Let's imagine we have two static libraries which are separate projects:
################################################
# Logger library ###############################
set(Logger_INCLUDE_DIRS Logger/include)
set(Logger_LIBRARIES Logger)
add_library(Logger STATIC
${PROJECT_HEADERS}
${PROJECT_RESOURCES}
${PROJECT_SOURCES}
${MISC}
)
target_include_directories(Logger
PUBLIC
include
)
################################################
# Utils library ################################
add_library(Utils STATIC
${PROJECT_HEADERS}
${PROJECT_RESOURCES}
${PROJECT_SOURCES}
${MISC}
)
Utils library depends on Logger library for logging
Which of these ways will be right to provide Logger sources for Utils library or they are both incorrect and I need to use another one
I have two options here, first one is to provide only *.h file, as far as understood it is enough for static library:
target_include_directories(Utils
PUBLIC
include
${Logger_INCLUDE_DIRS}
)
The other one is to use target_link_libraries, as far as I understood *.h files are linked too but in general we do not need to link one static library with another one and this is redundant:
target_link_libraries(Utils
${Logger_LIBRARIES}
)
Use target_link_libraries whenever one library/executable target uses another library target. Do not think about redudancy of linking static libraries.
In CMake, linking targets is much more than simple linking libraries. Large part of CMake is propagating libraries properties when linking. Just use that.
Also, if you will decide to transform your STATIC library into SHARED, using target_link_libraries provide no additional actions in such transformation.
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 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.
From the following example:
CMakeList.txt file:
include_directories(inc)
# Grab all the cpp and h files to be compile.
file(GLOB SOURCES
inc/*.h
inc/*.hpp
src/*.cpp
)
add_library(MyStaticLib STATIC ${SOURCES} )
target_link_libraries(MyStaticLib PUBLIC "${OPENCV_LIBS}/opencv_world410.lib" )
target_link_libraries(MyStaticLib PUBLIC "${OPENCV_LIBS}/opencv_world410d.lib" )
Does this create a single static library? I thought you could not link a static library to a static library in c++? Why does this work?
Also, what would be the best way to do this? For example, if I create an API that is a static Lib i.e., MyStaticLib and it’s dependent on opencv’s Static lib, what would be the best way to set this up in CMake?
In short
When target_link_libraries is applied to the static library, it won't affect on the resulted library file. But it affects on the target in the similar way, as it would affect on the target of the shared library.
So, you can use target_link_libraries both for static and shared libraries in the same manner.
In details
When applied to the static library, target_link_libraries doesn't have immediate effect on the creation of this library. That is, when static library will be created (as file), it won't store that linkage:
add_library(MyStaticLib STATIC ${SOURCES} )
target_link_libraries(MyStaticLib PUBLIC "${OPENCV_LIBS}/opencv_world410.lib")
# When file 'MyStaticLib.lib' will be created,
# it will NOT be linked with 'opencv_world410.lib'.
(As you probably know, a static library doesn't store any information about the linkage, a static library is just a collection of object files.)
But while the file with the static library doesn't store linkage information, the CMake target, which represents that static library, will be "aware of linkage". This can be useful in many cases:
If in the same project you create an executable or a shared library, and link it with the static library (using target_link_libraries), that executable will actually linked with the dependent library:
add_executable(myExe ...)
target_link_libraries(myExe PRIVATE MyStaticLib)
# When file 'myExe.exe' will be created, it WILL be linked with 'opencv_world410.lib'
If in the same project you create another static library, and link it with the static one, the another static library will be "aware of linkage" both with the initial static library and its dependency.
add_library(MyStaticLibAnother STATIC ..)
target_link_libraries(MyStaticLibAnother PUBLIC MyStaticLib)
# "As if" following line is executed
# target_link_libraries(MyStaticLibAnother PUBLIC "${OPENCV_LIBS}/opencv_world410.lib")
If you install your library target and export it using command install(TARGETS ... EXPORT ...), then the library can be found with find_package. And that find_package will create a static library target, which will be aware of linkage with all its dependencies:
# [In main project]
install(TARGETS MyStaticLib EXPORT MyProject)
install(EXPORT MyProject NAMESPACE MyProject::)
# [In another project]
find_package(MyProject REQUIRED)
# It creates the target MyProject::MyStaticLib which is
# "aware of linkage" with 'opencv_world410.lib'.
Additionally, if you link a static library with a target (not with a plain file), and that target contains compile definitions or other information, applicable to the compile stage, the static library will be compiled using that compile information.
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 :
I've tried to build a rather large shared library on Windows with cmake + ninja + msvc, that is composed of multiple static libs from subfolders. So a root CMakeLists.txt looks like:
project (sharedlib CXX)
include(${CMAKE_CURRENT_LIST_DIR}/staticlib1/CMakeLists.txt)
include(${CMAKE_CURRENT_LIST_DIR}/staticlib2/CMakeLists.txt)
add_library(sharedlib SHARED)
target_link_libraries(sharedlib
staticlib1
staticlib2
)
set_target_properties(wux PROPERTIES LINK_FLAGS "/WHOLEARCHIVE")
Where the CMakeLists.txt in sub-folders staticlib1 and staticlib2 both look something like:
add_library(staticlib1 STATIC)
target_sources(staticlib1 PUBLIC
${CMAKE_CURRENT_LIST_DIR}/sourceA.cpp
${CMAKE_CURRENT_LIST_DIR}/sourceB.cpp
)
target_include_directories(staticlib1 PUBLIC
${CMAKE_CURRENT_LIST_DIR}/inc
)
target_compile_options(staticlib1 PUBLIC
/flag1
/flag2
)
When I run cmake --build both staticlib1 and staticlib2 get built no problem. Cool. But when the linker tries to build sharedlib, the cmake-generated rsp file has:
CMakeFiles\sharedlib.dir\staticlib1\sourceA.cpp.obj
CMakeFiles\sharedlib.dir\staticlib1\sourceB.cpp.obj
CMakeFiles\sharedlib.dir\staticlib2\sourceC.cpp.obj
CMakeFiles\sharedlib.dir\staticlib2\sourceD.cpp.obj
staticlib1.lib
staticlib2.lib
So I get linker errors because symbols are defined twice. How do I get cmake to stop adding both the objects and the final libs to the linker rsp?
Technically I don't need the static libs after the fact. However, I cannot simply switch to using OBJECT libraries. The actual project has ~250 static libs, comprising ~3500 object files. The linker dies (out of memory) with just a fraction of the object files.
I must build up the static libs first and then link just them into the shared lib after the fact, to get around the memory limitations of the linker. Our current build scripts follow this pattern just fine, so I know it works. I just need cmake to follow the same pattern.
This is the effect of target_sources command: with PUBLIC keyword it adds sources both for the library (static) and for anyone who links that library.
You should instead use PRIVATE keyword, or, better, add sources in the add_library call itself:
add_library(staticlib1 STATIC
${CMAKE_CURRENT_LIST_DIR}/sourceA.cpp
${CMAKE_CURRENT_LIST_DIR}/sourceB.cpp
)