Specifying target_link_libraries when building shared object in cmake - cmake

My project creates a shared object A that 'depends' should reference other shared objects B, C , D and E. After building the project and checking the build with the utility ldd I see no references to the shared objects B, C , D and E. However when I use the directive target_link_libraries(A , B , C , D , E ) in my build the references to the shared objects appear in A.so. My question is two fold:
Is it correct to use target_link_libraries in this way?
If so, why is this use of target_link_libraries correct considering that I'm building a shared object where linking is at runtime.
Example:
My Frobnigator project depends on a ContinuumTransfunctioner and Transmogrifier shared objects that have already been built. My question is about whether the line target_link_libraries(Frobnigator ${Libraries}) is necessary.
cmake_minimum_required(VERSION 3.0)
set(Libraries
ContinuumTransfunctioner
Transmogrifier
)
set(SourceFiles
Wrapper.cpp
Logger.cpp
)
add_library(Frobnigator SHARED ${SourceFiles})
add_library(FrobnigatorStatic STATIC ${SourceFiles})
set_target_properties(FrobnigatorStatic PROPERTIES OUTPUT_NAME Frobnigator)
#target_link_libraries(Frobnigator ${Libraries}) # Do I need this?

Yes, you need to use target_link_libraries even when create SHARED library.
While some symbol resolution is performed at runtime (loading time), there some things which should be performed at build time (linking).
The main thing is ... providing a list of libraries, which should be loading with your library. This list is "embedded" into the library file. There is no other way for dynamic loader to know, which other libraries should be loaded with your one.
Among other things performed at link time is:
Checking that all symbols needed for your library are actually available in other libraries.

Related

Why the use of generator expression to link object library?

I understand the purpose of object libraries, but why does linking it to a target need to involve a generator expression?
add_library(myObjects OBJECT a.cpp b.cpp)
add_library(mainLib ${other_srcs} $<TARGET_OBJECTS:myObjects>) # Why not "PRIVATE myObjects" just like other files and targets?
From my understanding, generator expressions are useful for evaluating things available only during generation phase. Why would it be the case that object libraries, which are simply collection of object files, and their location not be known during the configuration phase?
In addition to what #Alex Reinking has already said, the location of the object file may itself depend on the configuration, e.g. with the "Ninja Multi-Config" generator the object files of a target t are located in <build-dir>/t/CMakeFiles/t.dir/<config>/<path-of-source-file-relative-to-dir-in-which-target-is-defined>/<src-file-name>.o
So you're exactly in the situation that you only know the location at generation time, hence the need for a generator expression.
add_library(mainLib ${other_srcs} $<TARGET_OBJECTS:myObjects>) # Why not "myObjects" just like other files and targets?
So:
add_library(mainLib ${other_srcs} myObjects)
Because that would try to find a file named myObjects and compile it. The file has no extensions, so cmake will exit because the file does not exists and he doesn't know how to compile it anyway.
If you meant:
add_library(mainLib ${other_srcs})
target_link_libraries(mainLib PUBLIC myObjects)
The link_libraries dependency on object libraries did not work in older cmake when OBJECT libraries were introduced.
The documentation on object libraries explicitly mentions the form add_library(... $<TARGET_OBJECTS:...) and notes that since cmkae 3.12 target_link_libraries may be used with object libraries.
Why would it be the case that object libraries, which are simply collection of object files, and their location not be known during the configuration phase?
Simple: the target_sources command may be used to add sources (and therefore objects) to a target at any point. If a variable were expanded immediately, one would miss objects that were added later.
In multi-config builds, it is also unknown at configuration time whether you refer to the Debug or Release objects.
Imagine that add_library(target a.cpp b.cpp) created a variable named target_OBJECTS containing /path/to/a.o;/path/to/b.o. Then the following sequence demonstrates the problem:
add_library(target a.cpp b.cpp)
add_executable(main main.cpp ${target_OBJECTS}) # (1)
# ... later ...
target_sources(target PRIVATE c.cpp)
# ... later ...
add_executable(utility util.cpp ${target_OBJECTS}) # (2)
Now how would (2) know about /path/to/c.o? You could have target_sources update that variable, but now a single object library has dependents that see different subsets of it. That's horribly confusing. Moreover, (1) wouldn't see c.o, which is maybe what's desired in this example, but that's not always true. Maybe your code is just organized to define custom commands to create generated sources later.
Making this imperative, rather than declarative like $<TARGET_OBJECTS:target>, imposes structure on CMake code akin to requiring targets in a Makefile to be topologically sorted. It also changes the meaning from "the object files for this target" to "the object files for this target at this point in CMake execution time", which is strictly more complicated to reason about while not offering any expressive advantage.
If you replace this code with the CMake 3.12+ object library linking support, it looks even more vexing:
add_library(target a.cpp b.cpp)
add_executable(main main.cpp)
target_link_libraries(main PRIVATE target) # (1)
# ... later ...
target_sources(target PRIVATE c.cpp)
# ... later ...
add_executable(utility util.cpp)
target_link_libraries(utility PRIVATE target) # (2)
Now, looking at either call to target_link_libraries(... target), one would reasonably expect the same objects to be added.

Can I get the original target name from an alias target?

If I create an alias target in CMake like
add_library(my::foo ALIAS my_foo)
is there any way to query the name of the underlying target name from the alias target?
My use case:
A shared C++ codebase with several independent modules. The root folder of this codebase contains a CMakeList.txt to be added via add_subdirectory to the project using it. According to our convention e.g. my_foo will always be located in a subfolder named my_foo. Furthermore, my_foo will be exported as alias target my::foo and used as such in the project. Note that my_foo is always an INTERFACE target, so I cannot set any custom properties on it.
We use conan to manage third party library dependencies. All modules that have such dependencies contain a conanfile.txt
For convenience I want to write a function (located in the shared codebase's root CMakeList) that takes a list of module targets the project wants to use and scans all of them for conanfiles and sets up the dependencies for those used. I want to be able to pass my::foo as argument to that function but derive my_foo from that argument inside the function to get the corresponding folder name to scan for the conanfile
Any other suggestions that solve the problem according to my use case are welcome as well!
An alias target has a special property, where it store name of the original target: ALIASED_TARGET.
get_target_property(my_foo_original my::foo ALIASED_TARGET)
message(STATUS "Alias my::foo refers to the target ${my_foo_original}")

Should my CMake imported targets have dependencies?

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.

CMake: Is there a difference between set_property(TARGET ...) and set_target_properties?

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.

Link two dependent projects with CMake

Let I have two projects A & B. A has a structure: src, tests, vendor(3d party libs). I want to put the project B with the same structure into A's src. A and B are dependent (A uses files from B; B uses files from A and from A's vendor). I want to be able to run tests for A and B separately. Is there a way to do it with CMake?
As mentioned by ComicSansMS in the comments, you want to split this into three projects: A, B and C. Thew new project C contains the stuff that is used by both A and B. Without knowing more specifics, it's hard to suggest the particular action you should take to split up the projects.
As far as CMake building the resulting three-part project, yes, it's fairly simple:
add_library(C c.cpp c2.cpp)
add_library(A a.cpp a1.cpp)
target_link_libraries(A C)
add_library(B b.cpp b2.cpp)
target_link_libraries(B C)
add_executable(test test.cpp)
target_link_libraries(test A B)