CMake dependencies and Dll copies - c++

I did wrote some CMake code to transitively get all library dependencies of a given executable target and copy dlls next to it (yes Windows World)
Then I got an external library that does not support CMake. That have a single implib lets call it a.lib with an a.dll, this library also depends on another dll without any available implib (b.dll)
So I wrote a aConfig.cmake like this:
set(a_FOUND TRUE)
add_library(a SHARED IMPORTED)
set_target_properties(a PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
IMPORTED_LOCATION "${a_DIR}/bin/a.dll"
IMPORTED_IMPLIB "${a_DIR}/lib/a.lib"
)
add_library(b SHARED IMPORTED)
set_target_properties(b PROPERTIES
IMPORTED_LOCATION "${a_DIR}/bin/b.dll"
)
# to express a dependency without requiring implib at link time
target_link_library(a PRIVATE b)
I end up with this (expected) error
IMPORTED library can only be used with the INTERFACE keyword of
target_link_libraries
Then I modified b to become an INTERFACE (It feels strange here):
add_library(b INTERFACE IMPORTED)
set_target_properties(b PROPERTIES
IMPORTED_LOCATION "${a_DIR}/bin/b.dll"
)
target_link_library(a INTERFACE b)
with CMake 3.18 my transitive loop does not want to ask for an IMPORTED_LOCATION on an INTERFACE
INTERFACE_LIBRARY targets may only have whitelisted properties. The
property "IMPORTED_LOCATION" is not allowed.
but with CMake 3.19 it works and do exactly what I am expecting. So I read the Release Note and except this statement :
Interface Libraries may now have source files added via add_library() or target_sources(). Those with sources will be generated as part of the build system.
nothing seems related to this.
Since I found the CMake 3.18 error, I feel that what I am doing is wrong but I cannot find a better way to express this runtime dependency.
Does anyone have a better idea on how to express this dependency or does it feels right like this ?

In CMake 3.19 and after you could create b target for a dll without a lib in a single line:
add_library(b INTERFACE "${a_DIR}/bin/b.dll")
So when you link a with b, a will get a dependency from the file ${a_DIR}/bin/b.dll.
This is what CMake 3.19 introduces: a dependency of INTERFACE library from the file.
For implement that dependency in CMake pre-3.19, one need to create additional custom target:
add_custom_target(b_files DEPENDS "${a_DIR}/bin/b.dll")
add_library(b INTERFACE)
add_dependencies(b b_files)

Related

CMake: Absolute lib-pathnames given to target_link_library( PRIVATE ) are exported, appear in generated INTERFACE_LINK_LIBRARIES. How do I?

In a large CMake/C++ project our major library target Foo is being added. It has many statically linked dependencies that are provided as absolute-paths to target_link_libraries(). Now I'm writing the CMake to export this library once built, including the generated CMake so a library consumer can simply use CMake find_package(). However the generated FooTargets.cmake is embeding the absolute-paths of the link-libraries instead of just the library names.
Additionally, I've already created an add_library(BarCommon INTERFACE), to wrap up the referenced 3rd-party static libs separately and that seems to be working and the client-application consumed it. That accounts for 90% of the libraries also mentioned by FOO. So really FOO's exported INTERFACE_LINK_LIBRARIES should be about 3 file names instead of 40 absolute-pathnames.
I'm confused, why is the target_link_libraries(FOO PRIVATE ${libs} is defining the exported 'INTERFACE_LINK_LIBRARIES' property?
I would like that generated INTERFACE_LINK_LIBRARIES property to be only library filenames, and optimally have finer control over which filenames end up in it. How do I control this?
Code:
Inside a large CMake Macro for generating targets we have this:
-
I call it to create library/target FOO.
_TARGET_ARGS_LIB_DEPENDS will resolve to ABSOLUTE FILE PATHNAMES for about 40 statically linked library dependencies.
add_library(${TargetName} STATIC ${AllSources} ${_TARGET_ARGS_OBJ_FILES})
<snip>
target_link_libraries(${TargetName}
PRIVATE
$<${GCC}:-Wl,--start-group>
${_TARGET_ARGS_LIB_DEPENDS}
$<${GCC}:-Wl,--end-group>
)
where that longish function returns I have just added these blocks:
target_link_directories(FOO
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
$<INSTALL_INTERFACE:foo/lib>
)
target_include_directories(FOO
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:foo/include>
)
install(TARGETS Foo
EXPORT
FooTargets
DESTINATION
foo/lib
)
There is some more 'install' boilerplate at the end of my CMakeLists.txt
After I run CMAKE, build the project and run the 'INSTALL' target CMake generated from Visual Studio I can examine generated file: local_install\Release\Windows\Foo\lib\cmake\Foo\FooTargets.cmake shows:
set_target_properties(Nexidia.Workbench.StaticLib.StaticLib PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/Foo/include"
INTERFACE_LINK_DIRECTORIES "${_IMPORT_PREFIX}/Foo/lib"
INTERFACE_LINK_LIBRARIES "\$<LINK_ONLY:\$<0:-Wl,--start-group>>;C:/full/path/to/libA.lib;C:/full/path/to/libB.lib \$<LINK_ONLY:\$<0:-Wl,--end-group>>"
)
(with the full paths to about 40 libraries).
PS. I'm reading Professional CMake: A Practical Guide, so if someone could cite the chapter/sub-section that answers this it might help.
This is because your library is static and so it cannot be correctly linked to consumers without including its dependencies on consumers' link lines. The same thing happens with the shared dependencies of shared libraries. Static dependencies of shared libraries are fully "absorbed" into the shared library and so they don't appear.
CMake is aware of all this, and so to respect the other semantics of PRIVATE, it inserts the $<LINK_ONLY:...> generator expression. This ensures that the libraries are used for linking only and that any associated INTERFACE_* properties are not propagated.
In other words, CMake is behaving correctly here.
You should resolve this by creating and linking to targets for libB and friends that you can either export as part of your main build or import into both your main build and via find_dependency in your distributed package, along with the libraries themselves, of course.
The best advice I can give you is to always link to targets in CMake, never to paths or raw library flags.

Cmake Top level project has two dependent projects, one child is also dependent on the other child

I have Project A at the top.
Project A requires library B and library C.
Library B also requires library C by itself.
So, in short. How can I link libraries B and C up to A by just linking B?
I have it working now by individually linking all the libraries, but I feel like there is redundancy I could get rid of.
This is part of the CMAKE for Project A at the top:
find_package(libB REQUIRED)
include_directories(${libB_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${libB_LIBRARY})
find_package(libC REQUIRED)
include_directories(${libC_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${libC_LIBRARY})
But also within libB I have this in its CMAKE:
find_package(libC REQUIRED)
include_directories(${libC_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${libC_LIBRARY})
I feel like there is a better way to do this, please let me know. Thank you for any help.
You can use target_include_directories (documentation) to specify include directories for your target instead of specifying them for all targets of the current directory using include_directories. Among other things, this gives the ability to provide visibility specifiers to your includes dirs, which control whether your include dirs will affect only current target (PRIVATE), only those targets which link to your target (INTERFACE) or both (PUBLIC). The same specifiers are used similarly for target_link_libraries.
So, you can use PUBLIC to avoid duplicating your lib C includes and libraries, and since it is the default specifier, you can omit it. Then, parts of your CMakeLists may look like this:
In project A CMakeLists.txt:
find_package(libB REQUIRED)
target_include_directories(${PROJECT_NAME} ${libB_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${libB_LIBRARY})
In lib B CMakeLists.txt:
find_package(libC REQUIRED)
target_include_directories(${PROJECT_NAME} ${libC_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${libC_LIBRARY})
If you, e.g., want only lib B to link to lib C, but leave public 'inheritance' of include directories, you could change the last line of lib B CMakeLists.txt excerpt:
target_link_libraries(${PROJECT_NAME} PRIVATE ${libC_LIBRARY})

CMake imported library include_directories before other ones

I have a cmake project with an IMPORTED library which has a include directory which must be used before the include directories of the executable.
I tried the following:
cmake_minimum_required(VERSION 3.20)
project(test_cmake)
set(CMAKE_CXX_STANDARD 98)
#Librarry
add_library(lib SHARED IMPORTED)
target_include_directories(lib SYSTEM BEFORE INTERFACE ./lib_inc/)
set_target_properties(lib PROPERTIES IMPORTED_IMPLIB "lib.lib")
add_executable(test_cmake main.cpp)
target_include_directories(test_cmake AFTER PRIVATE include)
target_link_libraries(test_cmake PRIVATE lib)
But at least with the generator "Visual Studio 9 2008" the directory "include" in used before "lib_inc". Does anybody know a way to modify the imported lib so that there include will be the first?
Does anybody know a way to modify the imported lib so that there include will be the first?
The answer is no. Compiler command lines are computed by walking the DAG of linked libraries and placing the merged interface properties in (de-duplicated) pre-order starting with the "top" target. So there is no interface property that can do exactly what you want.
I have a [...] library which has a include directory which must be used before the include directories of the executable.
Frankly, this is a really dubious requirement. The library cannot know to which executables it will be linked so it cannot be a requirement to override its linkers' (as in "linker" and "linkee", not ld) include paths. Similarly, if this library L were a dependency of a static library S, and an executable E linked to S, what should happen with the "before" include dirs of L? Should they override E's? If so, that would be a truly confusing behavior, whereby no target can ever trust its own headers to be used, ever.
So this is not only not available, it's pretty dubious that it ever should be available. This just isn't a coherent semantics for property propagation.

Allow mutual dependencies in Cmake

I am trying to migrate a huge base of code from a custom in-house toolchain to Cmake. This code is comprised of lots of shared libraries and some executable.
Some of these libraries are interdependent. Let's take as examples Lib1 and Lib2, where Lib1 uses symbols from Lib2, and Lib2 uses symbols from Lib1.
In order to to create the Lib1 DLL, we need exported symbols from Lib2, which in turn need exported symbols from Lib1. Currently we resolved this by linking in two phases, as described here : Mutual Imports
). This gives us the following steps :
Create lib1.lib and lib1.exp
Create lib2.lib and lib2.exp
Create lib1.DLL using lib1.obj and lib2.lib/lib2.exp
Create lib2.DLL using lib2.obj and lib1.lib/lib1.exp
I know that this is a code smell, but the code base is way too huge to try and correct all of these cycles. So I would like to get a similar result using Cmake. I did not find any way of doing this from the documentation, except using only custom commands which would completely defeat the purpose of this tool.
The legacy toolchain does the following calls :
# compile sources
cl lib1\SRC\lib1help.cpp ... /Fo"build\cl\lib1.obj"
cl lib2\SRC\lib2help.cpp ... /Fo"build\cl\lib2.obj"
# create lib + exp symbols
lib /DEF build\cl\lib1.obj /OUT:build\link1\lib1.lib
lib /DEF build\cl\lib2.obj /OUT:build\link1\lib2.lib
# create mutual dependant dll using exp
link build\cl\lib1.obj build\link1\lib2.lib /DLL /OUT:build\link2\lib1.dll
link build\cl\lib2.obj build\link1\lib1.lib /DLL /OUT:build\link2\lib2.dll
As of now I have the following CmakeLists.txt:
add_library(lib2 SHARED lib2/src/lib2help.cpp)
add_library(lib1 SHARED lib1/src/lib1help.cpp)
target_include_directories(lib1 PUBLIC lib1/include/)
target_compile_definitions(lib1 PRIVATE __LIB1)
target_link_libraries(lib1 PRIVATE lib2)
target_include_directories(lib2 PUBLIC lib2/include/)
target_compile_definitions(lib2 PRIVATE __LIB2)
target_link_libraries(lib2 PRIVATE lib1)
This gives me the following result :
[cmake] CMake Error: The inter-target dependency graph contains the following strongly connected component (cycle)
[cmake] "lib2" of type SHARED_LIBRARY
[cmake] depends on "lib1" (weak)
[cmake] "lib1" of type SHARED_LIBRARY
[cmake] depends on "lib2" (weak)
Do you guys know a way I could implement this using Cmake ?
For now I plan on only targeting windows systems, but a OS agnostic would be awesome !
Thanks
I was so fixated on making a SHARED library that I didn't explore the STATIC+SHARED way. Thanks to #arrowd and #tsyvaref for the suggestion.
I got it working using the following CmakeLists.txt
add_library(lib1_obj OBJECT src/lib1help.cpp)
target_include_directories(lib1_obj PUBLIC include/)
target_include_directories(lib1_obj PRIVATE $<TARGET_PROPERTY:lib2_obj,INCLUDE_DIRECTORIES>)
target_compile_definitions(lib1_obj PRIVATE __LIB1)
add_library(lib1_static STATIC $<TARGET_OBJECTS:lib1_obj>)
target_link_libraries(lib1_static PUBLIC lib2_static)
set_target_properties(lib1_static PROPERTIES STATIC_LIBRARY_OPTIONS "/DEF")
set_target_properties(lib1_static PROPERTIES OUTPUT_NAME lib1)
add_library(lib1_shared SHARED $<TARGET_OBJECTS:lib1_obj>)
target_link_libraries(lib1_shared PRIVATE lib1_static)
set_target_properties(lib1_shared PROPERTIES RUNTIME_OUTPUT_NAME lib1)
add_library(lib1 ALIAS lib1_shared)
The exact same file is used for lib2, except of course switching names.
I am not sure this is the best way, but I am happy enough for now.
Here is what's happening :
Compile sources into an OBJECT library
Link the mutually dependants libs as static, with the "/DEF" to create export file
Rename the static libs output to facilitate their usage for linker
Link the shared library using .obj, .lib, and .exp previsouly created
Rename the shared library dll to be used easier
Here we can see the content of each DLL using Dependency Walker :

CMake unneeded dependency

I have a project using 3 CMakeLists.txt:
CMakeLists.txt C is my executable and depends on
CMakeLists.txt B which is a static lib and that depends on
CMakeLists.txt A which is also a static lib and depends on an external lib
In CMakeLists.txt C I specify my dependency against B using using target_link_libraries() and I do the same for the dependency of B against A. In CMakeLists.txt A I specify the dependency against the external lib.
I would expect this to work but C actually complains at link-time and I can only get it to work by specifying a dependency in C against the external lib.
Note that the external lib is dynamic (a .so file).
This looks weird to me, no? Anyone understands what is going on?
Thanks,
Antoine.
That should work. I bet there's a bug in the CMakeLists.txt.
View real dependencies
Check it with:
cmake .. --graphviz=deps.dot
xdot deps.dot
It will show a pretty picture of the dependency tree that cmake sees.
If you don't have xdot, export it to a png:
dot -Tpng deps.dot -o deps.png
firefox deps.png
Library not found ?
Another possibility is that the external library can't actually be found. Use find_library rather than just putting the library name:
find_library(FAIL failingmadly)
if (NOT FAIL)
message(FATAL_ERROR "Couldn't find the failingmadly library")
endif()
target_link_libraries(my_lib_a ${FAIL})
Position independent code?
Another possibility when linking static libs with dependencies on shared libs is the PIC complaints. You could add this in cmake before compiling anything:
add_definitions(-fPIC)
Good luck.