How to "install" (copy) header files of transitively linked libraries with CMake - build

I have 3 libraries: A, B, C.
C links against B. B links against A (C will "link" against B as well).
target_link_libraries(B A)
target_link_libraries(C B)
Each of these libraries has been set with a PUBLIC_HEADER property, which contains the path of the respective header files. For instance:
set_target_properties(A PROPERTIES PUBLIC_HEADER "A.h")
set_target_properties(B PROPERTIES PUBLIC_HEADER "B.h")
set_target_properties(C PROPERTIES PUBLIC_HEADER "C.h")
When I compile and install the library C in the "build" directory (with make && make install), I want both the output library file and all the "linked" header files to be there.
To this aim, I used the following directive:
install(TARGETS C
RUNTIME DESTINATION build
LIBRARY DESTINATION build
ARCHIVE DESTINATION build
PUBLIC_HEADER DESTINATION build)
However, only C.h file (and the library) gets copied into the build directory.
How can library C inherit the PUBLIC_HEADER(s) of the A/B linked libraries?

Related

CMake dependencies and Dll copies

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)

How to build a static library from two directories?

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.

Using a Find*.cmake file with a header only library

I am creating a header only library (Library A) that includes another library (Library B) using its FindB.cmake file.
I include the FindB.cmake file by having it in a cmake directory and doing:
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
find_package(B REQUIRED)
target_link_libraries(A INTERFACE B::B)
The tests on Library A all work fine so B is included successfully in A. To make B available to projects that want to include A, I have in my AConfig.cmake.in :
include(CMakeFindDependencyMacro)
find_dependency(B REQUIRED)
But when I try to make an application (App C) that uses Library A (with find_package(A REQUIRED)), I get an error that cmake cannot find the FindB.cmake file. The error goes away if I put the FindB.cmake file in the App C project and include it like I did with Library A, but I don't want people using Library A to need the FindB.cmake file.
Is there a way to make all of the location information from FindB.cmake carry over from Library A to App C without needing to include the actual FindB.cmake file?
#Tsyvarev had the answer.
I added this to install the FindB.cmake:
install(FILES "${PROJECT_SOURCE_DIR}/cmake/FindB.cmake"
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/A/cmake
)
Then in my AConfig.cmake.in, I edited it with the following:
set(CMAKE_MODULE_PATH #CMAKE_INSTALL_DATAROOTDIR#/A/cmake #CMAKE_MODULE_PATH#)
find_dependency(B REQUIRED)
I can now add Library A to App C with find_package(A REQUIRED) and everything transfers over.
Thanks again #Tsyvarev

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.

CMake can't link external libraries

I have a C++ project that contains source files. For an external project, there are some folders I need to search for included libraries:
/home/data/lib/wisenet
/home/data/lib/wise_log
/home/data/lib/wise_rs_device
/home/data/lib/json
/home/data/lib/wise_versioning
What must I write to include these external libraries in CMake? These folders contain only interface resources (h files and .a libraries).
I tried to include these directories like this:
include_directories(
/home/data/lib/wisenet
/home/data/lib/wise_log
... etc
)
And I don't understand how to correctly add the lib files such as libwise_rs_device.a.
Include directories only are for... well, include paths for your code. It won't link libraries.
The correct way to use external library is using imported libraries:
add_library(wise_rs_device STATIC IMPORTED GLOBAL)
set_target_properties(wise_rs_device PROPERTIES
IMPORTED_LOCATION "path/to/static/library"
INTERFACE_INCLUDE_DIRECTORIES "path/to/headers/of/wise_rs_device"
)
Then, you can simply link the imported target to yours:
# will link to the static library and add include directories.
target_link_libraries(your_executable PRIVATE wise_rs_device)