How to obtain IMPORTED_LOCATION according to the "current" configuration? - cmake

In my project I use a dynamic library engine.dll.
engine dll is compiled once for debug configuration and once for release configuration.
Here is how I declare the imported target:
add_library(engine SHARED IMPORTED GLOBAL)
set_target_properties(
engine
PROPERTIES
IMPORTED_LOCATION_DEBUG
"${CMAKE_CURRENT_SOURCE_DIR}/bin_jit/Debug/engine.dll"
IMPORTED_LOCATION_RELEASE
"${CMAKE_CURRENT_SOURCE_DIR}/bin_jit/Release/engine.dll"
IMPORTED_LOCATION_RELWITHDEBINFO
"${CMAKE_CURRENT_SOURCE_DIR}/bin_jit/Release/engine.dll"
)
I would like to copy engine.dll to the relevant output directory. for this purpose, I want to query its IMPORTED_LOCATION property according to the build config.
get_target_property(
dynamic_library_target_imported_location # out value
engine
IMPORTED_LOCATION
)
But this fails. so how can I have dynamic_library_target_imported_location contain the correct location according to the configuration?

EDIT2: If you still persist on not using generator expressions which I highly recommend as they are safe and always behave in a well defined matter. Then you should instead of IMPORTED_LOCATION use the property LOCATION. However you should try to obtain this property at the latest time possible as you shouldn't do anything that might change the imported targets location.
EDIT:
Based on your comment I guess I should clarify what the generator expression does:
$<IF:$<CONFIG:Release,RelWithDebInfo>:Release,Debug>
If you build with Release or RelWithDebInfo config the generator expression will expand to:
${CMAKE_CURRENT_SOURCE_DIR}/bin_jit/Release/engine.dll
If you build with anything other than the configs above mentioned then:
${CMAKE_CURRENT_SOURCE_DIR}/bin_jit/Debug/engine.dll
NOTE: This is exactly what you wanted to achieve with the IMPORTED_LOCATION.
Since you already know the location of the imported library beforehand, there is no need to set/get the property. You can just use the generator expression to do exactly what you want.
For installing targets I would recommend: file(GET_RUNTIME_DEPENDENCIES ...) It is not recommended outside installs as they mention in the documentation.
If by output directory you mean build directory then why not just straight up copy it via a post-build command (or without) using generator expressions? e.g.
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/bin_jit/$<IF:$<CONFIG:Release,RelWithDebInfo>:Release,Debug>/engine.dll
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/engine.dll)

Related

Can I manage QuantLib (nicely) with CMake and vcpkg?

I would like to use vcpkg to manage my packages. I would also like to use CMake to manage my builds. Is it possible to include quantlib library without resorting to hard coded links? Something like find_package(quantlib)?
EDIT: Things I've tried to far (disclaimer - I'm new to this. It feels like I'm groping in the dark. This section makes my ignorance a little too transparent)
First, this is a clean build and I do not have backward comparability constraints. My aim is to create something that I can use between my Linux and Windows environments. I am using VSCode to edit my code and CMake.
My reference here was the Boost library. The way I used that, successfully (and header only at this point), was
find_package(Boost ${BOOST_VERSION} REQUIRED)
if (Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
else()
MESSAGE("BOOST not found")
endif()
To date I am making progress with something along the following lines (I'm not 100% sure this works yet but I believe I will get it over the line):
set(QUANTLIB_DIR "E:/SD/Programming/C++/vcpkg/installed/x64-windows"
CACHE PATH "The path to the quantlib directory")
include_directories(${QUANTLIB_DIR})
add_library(quantlib STATIC IMPORTED)
set_target_properties(quantlib PROPERTIES IMPORTED_LOCATION ${QUANTLIB_DIR}/lib/QuantLib-vc141-x64-mt.lib)
add_library(quantlibDebug STATIC IMPORTED)
set_target_properties(quantlibDebug PROPERTIES IMPORTED_LOCATION ${QUANTLIB_DIR}/debug/lib/QuantLib-vc141-x64-mt-gd.lib)
If this is the way I need to proceed (system dependent, build dependent) I will need to have sections like this for each different environments. I was hoping that using a package manager with CMake would allow me to use system agnostic variables in my build. Am I naive?
I am using AutoVcpkg to integrate vcpkg into CMake, which safes you from managing dependency directories manually. Basically you start off by defining the vcpkg installation directory. I am using the VCPKG_ROOT environment variable as a default value (however, you could provide something else using a build variable, in case you have multiple vcpkg installations):
INCLUDE (AutoVcpkg)
SET (AUTO_VCPKG_ROOT $ENV{VCPKG_ROOT})
Next, you can use the VCPKG_INSTALL macro to install the library you want:
VCPKG_INSTALL(quantlib)
FIND_PACKAGE(quantlib REQUIRED)
TARGET_LINK_LIBRARIES(MyProject PUBLIC quantlib)

Using target_link_libraries on specific configurations (MinSizeRel, Release, etc.) instead of general ones (Optimized or debug)?

From what I see from the target_link_libraries documentation, you can set the keyword debug, optimized or general before a library to define for what configuration to link it to. The problem is that I would like to have even more control and be able to specify a different library for Release, Debug, MinSizeRel and RelWithDebInfo. Is there a way to do this?
Command target_link_libraries accepts generator expressions, using which you may express almost any configuration-dependent logic.
Actually, specifying for target_link_libraries
debug libA
is effectively the same as using following generator expression:
$<$<CONFIG:Debug>:libA>
(More precise, it effectively the same with default setting of DEBUG_CONFIGURATIONS variable.)
If you want to link different flavors of the same library dependent on configuration, then the better way is using an IMPORTED library target combined with IMPORTED_LOCATION_<CONFIG> properties setting:
add_library(libA STATIC IMPORTED)
set_target_properties(libA PROPERTIES
IMPORTED_CONFIGURATIONS "Debug;Release"
IMPORTED_LOCATION_DEBUG "/foo/bar/libA_debug.a"
IMPORTED_LOCATION_RELEASE "/foo/bar/libA_release.a"
)
After that, plain
target_link_libraries(myExe PUBLIC libA)
will link with /foo/bar/libA_debug.a in 'Debug' configuration and with /foo/bar/libA_release.a in 'Release' configuration.

How can I create a target for an _existing_ library?

In CMake, we can add_library(mylib file1.cpp file2.cpp) and have a mylib.a in the library path get built. We can also target_include_directories(mylib INTERFACE some/directory), which effects targets depending on mylib.
But what if I have a library to begin with, which I will not be compiling. How can I add a target relating to it? That what I add as a dependency, will cause the .a file to be linked against, and for which I can set target_include_directories() ?
Note: I'm asking about CMake 3.x.
CMake provide an alternate signature for libraries that are already compiled:
add_library(
mynamespace::mylib
STATIC # or it could be SHARED
IMPORTED
)
See the official CMake documentation for more details.
with that you'll be able to add properties to the target doing so
set_target_properties(
mynamespace::mylib
PROPERTIES
IMPORTED_LOCATION "path/to/libmylib.a"
)
Little precision here, if you're using a Windows DLL, you must pass the DLL file's path in IMPORTED_LOCATION and set another property IMPORTED_IMPLIB that points to the .lib file, (not very handy).
Note that there is also a equivalent properties per configuration (Debug, and Release), that will need another property to be set (IMPORTED_CONFIGURATION), e.g. IMPORTED_LOCATION_DEBUG.
See also here and here in the documentation.
To avoid missing include files you can also precise the include directory using INTERFACE_INCLUDE_DIRECTORY property
set_target_properties(
mynamespace::mylib
PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "path/to/mylib/include"
)
With this, upon link declaration using target_link_libraries(), CMake will read information of the imported target and will add include directories implicitly.
Official documentation reference.

Add dependency to the CMake-generated build-system itself

In short: I know how to add dependencies to targets, in a CMake-generated build system. But I would like to add dependencies to the generated build-system itself.
Longer question: In the CMake-generated build process of cgal, we would like CMake to automatically re-run the configuration step, when certain files are modified. Unneeded details are hidden below:
As a matter of fact, we generate using CMake the build system for the CGAL libraries/examples/demos, but also at the same time the build system for our Doxygen-generated documentation. The Doxyfile is generated from multiple files.
When the CMake generator is "Makefile", there is a special target in the Makefile, that is named rebuild_cache, but that target (at the Makefile level) is not a CMake-target. And anyway, I look for a solution that is cross-platform, that is: usable with all CMake generators. I have the impression that what I want is not yet doable with CMake. Can you please confirm, so that I can fill a documented feature-request?
Since CMake 3.0, you can add such a file to the directory property CMAKE_CONFIGURE_DEPENDS. This property holds a list of files; if any of them changes, CMake will trigger re-configuration.
Here is a small example. Assuming your Doxyfile is generated from Doxyfile.in.1 and Doxyfile.in.2 in the current source directory, the property could be used like this:
set_property(
DIRECTORY
APPEND
PROPERTY CMAKE_CONFIGURE_DEPENDS
Doxyfile.in.1
Doxyfile.in.2
)
If you're using CMake 2.x, the property CMAKE_CONFIGURE_DEPENDS is not available, but you can use the following trick:
Pass the files through configure_file(), even if you just COPYONLY them someplace and don't use the resulting copies. configure_file() introduces precisely the buildsystem dependency you're looking for.
This works, but it adds the overhead of copying the file.
(Note: This trick was also the original content of this answer, since I was not aware of CMAKE_CONFIGURE_DEPENDS at time of answering).

Use RENAME in INSTALL(TARGETS ...)

I'm fairly new to CMake so please be gentle.
I have a two targets which both need to be called internal for further use on runtime. Now, when I call
set_target_properties(target1 PROPERTIES OUTPUT_NAME internal)
install(TARGETS target1 DESTINATION some/where/target1dir)
set_target_properties(target2 PROPERTIES OUTPUT_NAME internal)
install(TARGETS target2 DESTINATION some/where/target2dir)
one of the two targets will be overridden by the other one when invoking cmake, so when executing nmake in the build folder, the same file is copied to some/where/target1 and some/where/target2.
I considered using the RENAME option to change the temporary name of the built file to an arbitrary one, but this option is not allowed when using the TARGETS keyword.
Do you know how to solve this? Thank you!
I worked around this by adding a CMake file to the source directory which renames the files, configuring that file with the target's output name and the rename name, then adding a target property POST_INSTALL_SCRIPT which holds the path to the configured cmake file.
Due to a lack of knowledge about variables available to determine certain directories (such as the location of the devel folder) there is still much stuff inside that is hardcoded and the whole workaround seems like overkill and is ugly, so, if YOU know a better strategy, PLEASE tell me :)