I was looking at generated PackageConfig.cmake files for external libraries I am using and I often saw things like:
set_property(TARGET Package::lib APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG)
set_target_properties(Package::lib PROPERTIES
IMPORTED_LOCATION_NOCONFIG "${CMAKE_CURRENT_LIST_DIR}/liblib.so.1.1.1"
IMPORTED_SONAME_NOCONFIG "liblib.so.1"
)
What exactly is this value used for?
Related
I want to have an interface library which propagates CXX_COMPILER_LAUNCHER to another target.
As I understood the documentation (see below) something like this should work but it doesn't.
add_library(lib INTERFACE)
set_property(TARGET lib INTERFACE_CXX_COMPILER_LAUNCHER "echo")
add_executable(main)
target_link_library(main
INTERFACE lib)
but...
get_property(_VAR TARGET main PROPERTY CXX_COMPILER_LAUNCHER)
message(FATAL_ERROR "compiler launcher: ${_VAR})
What am I doing wrong?
cmake: compatible-interface-properties
cmake: transitive-usage-requirements
EDIT:
put together a minimal example and added Tsyvarevs hint. Still no success.
cmake_minimum_required(VERSION 3.18)
project(lib)
add_library(lib INTERFACE)
set_target_properties(lib PROPERTIES
INTERFACE_CXX_COMPILER_LAUNCHER "echo 'Hello, World'"
INTERFACE_C_COMPILER_LAUNCHER "echo 'Hello, World'"
)
set_property(TARGET lib APPEND PROPERTY COMPATIBLE_INTERFACE_STRING CXX_COMPILER_LAUNCHER)
set_property(TARGET lib APPEND PROPERTY COMPATIBLE_INTERFACE_STRING C_COMPILER_LAUNCHER)
add_library(main SHARED)
target_link_libraries(main
INTERFACE
lib
)
get_target_property(_VarMain main CXX_COMPILER_LAUNCHER)
get_target_property(_VarLib lib INTERFACE_CXX_COMPILER_LAUNCHER)
message(WARNING "varLib: ${_VarLib}")
message(FATAL_ERROR "varMain: ${_VarMain}")
Only a property which INTERFACE_* counterpart is listed as known property is propagated out-of-the-box.
For tell CMake to propagate other properties, you need to add those properties into one of COMPATIBLE_INTERFACE_* list-like properties. Selection of the list determines how the final value of the consumer's property is selected from its initial value (if it is explicitly set) and propagated values:
COMPATIBLE_INTERFACE_BOOL requires that all values should be equal as boolean. (E.g. TRUE and ON are compatible values).
COMPATIBLE_INTERFACE_STRING requires that all values should be equal as string. (E.g. "FOO" and "foo" are incompatible values).
COMPATIBLE_INTERFACE_NUMBER_MAX selects maximum from the numeric values.
COMPATIBLE_INTERFACE_NUMBER_MIN selects minimum from the numeric values.
The other important thing: properties are propagated only at the end of configuration process. That is, the propagated value cannot be extracted via get_property command. Instead, generator expressions should be used.
While CMake is aware about the property CXX_COMPILER_LAUNCHER, it doesn't know its INTERFACE-prefixed variant INTERFACE_CXX_COMPILER_LAUNCHER.
Since the property has string semantic, you could add it to the COMPATIBLE_INTERFACE_STRING list:
set_property(TARGET lib APPEND PROPERTY COMPATIBLE_INTERFACE_STRING CXX_COMPILER_LAUNCHER)
After that the property will be automatically propagated to the consumers of your library.
For observe the result of the propagation, you could add custom target with $<TARGET_PROPERTY> generator expression:
add_custom_target(print_prop ALL
COMMAND echo "Property for main:" $<TARGET_PROPERTY:main,CXX_COMPILER_LAUNCHER>
)
so building the project (using make) will give:
Property for main: echo
I'm trying to build prusaslicer dependencies over superslicer (is not related to the question) but I'm getting an error in the FindTBB.cmake file where it says that
set_target_properties called with incorrect number of arguments.
The following code is from where if complains about the format:
set_target_properties(TBB::tbb PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS}"
INTERFACE_LINK_LIBRARIES "Threads::Threads;${CMAKE_DL_LIBS}"
INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS}
IMPORTED_LOCATION ${TBB_LIBRARIES})
What can I do to fix this?
set_target_properties requires the value of the property to be exactly 1 parameter, so the TBB_INCLUDE_DIRS or TBB_LIBRARIES not containing exactly one element (which given the use of plural is likely) does break things. The values need to be quoted in order to ensure the snippet works regardless of the number of elements in those (list?) variables.
set_target_properties(TBB::tbb PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS}"
INTERFACE_LINK_LIBRARIES "Threads::Threads;${CMAKE_DL_LIBS}"
INTERFACE_INCLUDE_DIRECTORIES "${TBB_INCLUDE_DIRS}"
IMPORTED_LOCATION "${TBB_LIBRARIES}")
The alternative would be to use the set_property command instead, which does allow you to specify multiple values.
set_property(TARGET TBB::tbb PROPERTY INTERFACE_COMPILE_DEFINITIONS ${TBB_DEFINITIONS})
set_property(TARGET TBB::tbb PROPERTY INTERFACE_LINK_LIBRARIES Threads::Threads ${CMAKE_DL_LIBS})
set_property(TARGET TBB::tbb PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS})
set_property(TARGET TBB::tbb PROPERTY IMPORTED_LOCATION ${TBB_LIBRARIES})
Unfortunately since this seems to be a third party library there's probably little you can to other than modifying your installation and reporting this issue to the vendor and hoping for a fix in a future update.
Btw: There's some additional weirdness going on in this snippet: The use of plural in TBB_LIBRARIES could indicate that multiple values could be specified, but the IMPORTED_LOCATION property is supposed to be set to a single path to a binary, see the documentation of the target property.
Perhaps there could be a hack for dealing with the issue that could allow you to build your project regardless of the issues mentioned: Make sure to import TBB::tbb first and as the only component of the package; If the library does not automatically add additional libraries to the TBB_LIBRARIES variable this could result in the variable containing a single value. I would't rely on this being the case in future releases though...
Is there a proper and clean way to add dependencies to an IMPORTED target in CMake (v3.17)?
I'm using CommonAPI-DBus, which does install a CommonAPI-DBusConfig.cmake file but doesn't define "useful" targets, so I want to wrap it with my own target. My preferred name is CommonAPI::DBus (because my ideal setup would have DBus as one of many components in the CommonAPI namespace), and because of the :: it has to be an imported target, fine.
CommonAPI-DBus depends on DBus-1, which isn't implicitly imported anywhere, so I want my target to also bring in DBus-1 (which I can get with find_package(DBus-1)), and that's where I run into problems.
I see two options:
1) Copy the dbus-1 properties to my target:
The DBus-1 package creates a dbus-1 target, so I can set up my target like:
if(NOT TARGET CommonAPI::DBus)
add_library(CommonAPI::DBus INTERFACE IMPORTED)
set_target_properties(CommonAPI::DBus PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${COMMONAPI_DBUS_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES "${CommonAPI_DBus_LIBRARY}"
)
foreach(var IN ITEMS INTERFACE_INCLUDE_DIRECTORIES INTERFACE_LINK_LIBRARIES INTERFACE_COMPILE_DEFINITIONS)
get_target_property(tmp dbus-1 ${var})
if(tmp)
message("Coping ${var} from dbus-1: ${tmp}")
set_property(TARGET CommonAPI::DBus APPEND PROPERTY ${var} ";${tmp}")
endif()
endforeach()
# Get the dbus-1 library
get_target_property(tmp dbus-1 IMPORTED_LOCATION)
set_property(TARGET CommonAPI::DBus APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${tmp})
unset(tmp)
endif()
But that's pretty ugly, and a lot of code to maintain
2) Ignore the dbus-1 target and use the exported variables
find_package(dbus-1) does export DBus1_INCLUDE_DIR and DBus1_LIBRARY, etc. I could simply use these properties, however this feels like an "old" way of using CMake. In my opinion the main advantage of CMake is the ability to define targets which dictate own interface and encapsulate their setup logic, rather than downstream artifacts using private variables from its setup script.
I suppose what I want is an INTERFACE target that uses :: to make it clear that I'm using a namespace.
I would like to compile a library as shared or static based on other variable eg:
add_library(MyLibrary $<$<IF:${BUILD_SHARED_LIBS},SHARED,STATIC> ${SOURCES})
For clarity I expect this to be equivalent with the following:
if(BUILD_SHARED_LIBS)
add_library(MyLibrary SHARED ${SOURCES})
elseif()
add_library(MyLibrary STATIC ${SOURCES})
endif()
AFAIK, you cannot do that with generator expressions (no way to query that aspect according to doc), since BUILD_SHARED_LIBS is there for exactly that reason; to allow you to select its value during configuration (using the -D command line option). This will work only if you do not set the library type explicitly as in your code, but like this
add_library(MyLibrary ${SOURCES})
Actually, that is the recommended practice. If you need to influence its value in association with some other condition, you can override it with the usual if()/else() logic, making sure that you print at least an informative message() for the user.
However, an even better approach would be to push those decisions to the user (via options) and check for the illegal combinations, issuing a message(FATAL_ERROR). Even if that condition is determined automatically, this is still a tactic that has merit.
In CMake, assuming one is just setting one property, is there any difference between
set_target_properties(target PROPERTIES prop value)
and
set_property(TARGET target PROPERTY prop value)
?
Cf.
https://cmake.org/cmake/help/v3.0/command/set_property.html
https://cmake.org/cmake/help/v3.0/command/set_target_properties.html
which imply there is no difference but aren't that clear.
Consider set_target_properties() as a specialized form of set_property().
Advantages of ...
set_target_properties(...) is a convenience function because it allows to set multiple properties of multiple targets.
For example:
add_executable(a ...)
add_executable(b ...)
set_target_properties(
a
b
PROPERTIES
LINKER_LANGUAGE CXX
FOLDER "Executable"
)
set_property(TARGET ...) can APPEND to a list- or APPEND_STRING to a string-based property of targets.
For example:
add_executable(a ...)
set_property(
TARGET a
APPEND PROPERTY
INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}"
)
References
How to change the name of the output binary to not be a.out with CMake?
target_include_directories prior to 2.8.12?
Appending compiler flags to a file with CMake
The difference is that with set_property, you get to define the scope. You actually have more options with the set_property other than just specifying a target, such as specifying source files in a list to have a certain property.
For example:
set_property(SOURCE src1.cpp src2.cpp PROPERTY SKIP_AUTOMOC ...)
This will add the SKIP_AUTOMOC property to source files listed. (This is for Qt, where Moc'ing of objects occurs automatically and sometimes you don't want that).
Contrast with set_target_properties where you must specify a Target and the property and it's value.
set_target_properties(target PROPERTIES CXX_STANDARD 11 ...)
Hopefully this helps!
Note that you also have respective set_*_properties functions for some of the other types of properties: set_source_files_properties, set_directory_properties and set_tests_properties. Notably absent are setters for install and global properties.
The reason for that is that these functions predate the general set_property call, which was only introduced with CMake 2.6, together with a general overhaul of the property system to what it is today.
These days, people tend to prefer the generic set_property, as it is the more modern function and provides a few additional features. It also offers a more consistent syntax than the old functions (eg. set_directory_properties not allowing to specify the directory as a parameter, set_source_files vs set_directory, etc.).
There is not a strong technical reason for preferring set_property, but I would consider it slightly better style than using the old, specific functions.