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.
Related
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.
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})
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)
I have two folders with two different libraries.
LibBase
LibPublic
LibB includes some LibBase's headers.
I'd like to have LibPublic as a static library including "LibBase" in its .a file.
Each CMakeLists.txt is:
set(SRCLIB file.cpp)
add_library(${PROJECT_NAME} ${SRCLIB})
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
target_include_directories(...)
The top level CMakeLists.txt:
add_subdirectory(LibBase)
add_subdirectory(LibPublic)
How can I force CMake to include LibBase inside LibPublic so that I can only share libLibPublic.a?
LibBase is a proprietary library and LibPublic is the "public" library we share.
LibBase and LibPublic, both may be added using add_subdirectory() by other libraries or apps so that a single app executable or a single .a file can be provided. Each "library" should be compiled as just objects, static library or even dynamic library. I'd like them to be generic, and an upper CMakeLists.txt will decide what to do.
I tried with add_library(${PROJECT_NAME}-obj OBJECT ${SRCLIB}) but I get errors:
CMakeLists.txt:22 (target_include_directories):
Cannot specify include directories for target "LibPublic" which is not
built by this project.
Given the following file and some dummy cpp files:
cmake_minimum_required(VERSION 3.10)
project(foo)
add_library(Base STATIC base.cpp)
add_library(Public STATIC public.cpp)
target_sources(Public PRIVATE $<TARGET_OBJECTS:Base>)
I end up with a libPublic.a that contains functions from both libraries.
Note that this is a solution to your question, but maybe not a solution to your underlying requirement: static libraries are simple collections of functions and the functions in "Base" are plainly visible.
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.