Okay, so I have a solution. First it's important to recognize that static libraries do not link other static libraries into the code. A combined library must be created, which on Linux can be done with ar. See Linking static libraries to other static libraries for more info there.

Consider two source files:

test1.c:

 int hi()
 {
   return 0;
 }

test2.c:

int bye()
{
  return 1;
}

The CMakeLists.txt file is to create two libraries and then create a combined library looks like:

project(test)

    add_library(lib1 STATIC test1.c)
    add_library(lib2 STATIC test2.c)

    add_custom_target(combined ALL
      COMMAND ${CMAKE_AR} rc libcombined.a $<TARGET_FILE:lib1> $<TARGET_FILE:lib2>)

The options to the ar command are platform-dependent in this case, although the CMAKE_AR variable is platform-independent. I will poke around to see if there is a more general way to do this, but this approach will work on systems that use ar.


Based on How do I set the options for CMAKE_AR?, it looks like the better way to do this would be:

add_custom_target(combined ALL
   COMMAND ${CMAKE_CXX_ARCHIVE_CREATE} libcombined.a $<TARGET_FILE:lib1> $<TARGET_FILE:lib2>)

This should be platform-independent, because this is the command structure used to create archives internally by CMake. Provided of course the only options you want to pass to your archive command are rc as these are hardwired into CMake for the ar command.

Answer from tpg2114 on Stack Overflow
Top answer
1 of 4
28

Okay, so I have a solution. First it's important to recognize that static libraries do not link other static libraries into the code. A combined library must be created, which on Linux can be done with ar. See Linking static libraries to other static libraries for more info there.

Consider two source files:

test1.c:

 int hi()
 {
   return 0;
 }

test2.c:

int bye()
{
  return 1;
}

The CMakeLists.txt file is to create two libraries and then create a combined library looks like:

project(test)

    add_library(lib1 STATIC test1.c)
    add_library(lib2 STATIC test2.c)

    add_custom_target(combined ALL
      COMMAND ${CMAKE_AR} rc libcombined.a $<TARGET_FILE:lib1> $<TARGET_FILE:lib2>)

The options to the ar command are platform-dependent in this case, although the CMAKE_AR variable is platform-independent. I will poke around to see if there is a more general way to do this, but this approach will work on systems that use ar.


Based on How do I set the options for CMAKE_AR?, it looks like the better way to do this would be:

add_custom_target(combined ALL
   COMMAND ${CMAKE_CXX_ARCHIVE_CREATE} libcombined.a $<TARGET_FILE:lib1> $<TARGET_FILE:lib2>)

This should be platform-independent, because this is the command structure used to create archives internally by CMake. Provided of course the only options you want to pass to your archive command are rc as these are hardwired into CMake for the ar command.

2 of 4
15

I'd like to enhance the other solutions by providing my CMakeLists.txt file that actually works also in terms of building dependencies.

Solution misusing CMake

cmake_minimum_required(VERSION 2.8)

add_library(lib1 test1.cpp)
add_library(lib2 test2.cpp)
include_directories(${CMAKE_CURRENT_DIR})
add_executable(mainexec main.cpp)
target_link_libraries(mainexec combinedLib)  # Important to place before add_custom_target

set(LIBNAME "combinedLib.lib")

add_custom_command(
    OUTPUT ${LIBNAME}
    COMMAND lib.exe /OUT:${LIBNAME} $<TARGET_FILE:lib1> $<TARGET_FILE:lib2>
    DEPENDS lib1 lib2
    COMMENT "Combining libs..."
)

add_custom_target(combinedLib
    DEPENDS ${LIBNAME}
)

Note that this solution works so far with Visual Studio but I guess it can be made multi-platform compliant. I can imagine that the following version might work for Unix-based platforms:

set(LIBNAME "libCombinedLib.a")

add_custom_command(
    OUTPUT ${LIBNAME}
    COMMAND ar -rcT ${LIBNAME} $<TARGET_FILE:lib1> $<TARGET_FILE:lib2>
    DEPENDS lib1 lib2
    COMMENT "Combining libs..."
)

Note that these solutions somehow misuse CMake as it would complain about a target of type UTILITY (instead of STATIC or SHARED) if you place the target_link_libraries call after the add_custom_target declaration.

CMake target-declaration-compliant solution

To make it CMake compliant, you can replace the `target_link_libraries' call by

target_link_libraries(mainexec ${LIBNAME})
add_dependencies(mainexec combinedLib)

In my case it is not entirely satisfactory because mainexec has to know about combinedLib although it expects all dependencies to be handled by the target_link_libraries call.

Alternative solution with less coupling

Looking a bit further towards imported targets I eventually found a solution that solves my last problem:

cmake_minimum_required(VERSION 2.8)

add_library(lib1 test1.cpp)
add_library(lib2 test2.cpp)
include_directories(${CMAKE_CURRENT_DIR})
add_executable(mainexec main.cpp)

set(LIBNAME "combinedLib.lib")

add_custom_command(
    OUTPUT ${LIBNAME}
    COMMAND lib.exe /OUT:${LIBNAME} $<TARGET_FILE:lib1> $<TARGET_FILE:lib2>
    DEPENDS lib1 lib2
    COMMENT "Combining libs..."
)

add_custom_target(combinedLibGenerator
    DEPENDS ${LIBNAME}
)

add_library(combinedLib STATIC IMPORTED)
set_property(TARGET combinedLib PROPERTY IMPORTED_LOCATION ${LIBNAME})
add_dependencies(combinedLib combinedLibGenerator)

target_link_libraries(mainexec combinedLib)

If you intend to modularize the whole add GLOBAL after STATIC IMPORTED to make the imported target globally visible.

Portable CMake solution

With the current CMake versions CMake provides full support for transitive dependencies and interface libraries. An interface library can then "link" against other libraries and this interface library can, in turn, be "linked" against. Why quotation marks? While this works good, this actually doesn't create a physical, combined library but rather creates a kind of an alias to the set of "sub-libs". Still this was the solution we eventually needed, which is why I wanted to add it here.

add_library(combinedLib INTERFACE)
target_link_libraries(combinedLib INTERFACE lib1 lib2)

target_link_libraries(mainexec combinedLib)

That's it!

🌐
CMake Discourse
discourse.cmake.org β€Ί code
Static library dependencies - Code - CMake Discourse
September 24, 2023 - So, let’s say I have separate libraries A, B & C in separate project folders. C is a static library but it’s CMake is unimportant for this example. B’s CMakeLists.txt looks something like this: add_library(B STATIC b.cpp) target_link_libraries(B PRIVATE C) And A’s CMakeLists.txt looks something like this: add_library(A STATIC a.cpp) target_link_libraries(A PRIVATE B) Now, I know the dependency to C is passed along to A thru B – that makes sense!
Discussions

How can I include dependencies in a static library using CMake
What I want to achieve is to only need to link against my library and not its dependencies Do you mean you want to bundle SDL and GLEW into your static library? Or that when you do target_link_library(… MyLibrary) it should transitively try to link to SDL and GLEW? Could you share your whole CMake file? More on reddit.com
🌐 r/cpp_questions
12
6
October 25, 2024
How to build static library with bundled dependencies - CMake

Slightly tangential, but... CMake 2.8 is ancient. Is there a reason you're still using it?

Newer versions introduce much more sane defaults for many things. You really ought to increase your CMake version requirement, which enables those policies. You can also enable them one-by-one, but that's not a sane approach.

General advice:

  1. Use target_include_directories instead of include_directories

  2. Use target_link_directories instead of link_directories

  3. Note that CMAKE_CXX_STANDARD is only available since CMake 3.1.

More on reddit.com
🌐 r/cpp
8
4
July 28, 2017
static library with static library dependencies propagates its dependencies when exported as package
Hello, I have library which we will call libB with statically linked dependencies libE.a, libF.a and libG.a (prebuilt) in target_link_dependencies(libB PRIVATE libE.a libF.a libG.a PUBLIC m). I have defined different project progA which is using libB which is exported as B-config.cmake. More on discourse.cmake.org
🌐 discourse.cmake.org
0
0
February 14, 2023
What is a header-only library and how do you use it with CMake?
its just where all the code is in a header file. You just add its directory to the target include directories list More on reddit.com
🌐 r/cpp_questions
28
31
December 29, 2020
🌐
CMake
cmake.org β€Ί cmake β€Ί help β€Ί latest β€Ί command β€Ί target_link_libraries.html
target_link_libraries β€” CMake 4.3.0-rc3 Documentation
creates an interface library iface_obj3 that forwards the obj3 usage requirements and adds the obj3 object files to dependents' link lines. The code Β· add_executable(use_obj3 use_obj3.c) target_link_libraries(use_obj3 PRIVATE iface_obj3) compiles use_obj3.c with -DOBJ3 and links executable use_obj3 with object files from use_obj3.c and obj3.c. This also works transitively through a static library.
🌐
CMake
cmake.org β€Ί cmake β€Ί help β€Ί latest β€Ί command β€Ί add_library.html
add_library β€” CMake 4.3.0-rc3 Documentation
Add a library target called <name> to be built from the source files listed in the command invocation. The optional <type> specifies the type of library to be created: ... A Static Library: an archive of object files for use when linking other targets.
🌐
Reddit
reddit.com β€Ί r/cpp_questions β€Ί how can i include dependencies in a static library using cmake
r/cpp_questions on Reddit: How can I include dependencies in a static library using CMake
October 25, 2024 -

I'm working on a library to help me in creating new projects faster. The library is built on top of a few dependencies, like SDL2 and GLEW.

What I want to achieve is to only need to link againts my library and not its dependencies when creating new projects. I could do this when instead of using CMake, I edited the Librarian section of the VS project myself, but when I use CMake to generate the project files, the "Additional Dependencies" and "Additional Library Directories" sections of the project properties remain empty.

Some additional info:
- I'm using vcpkg to manage dependencies
- to link the libraries I use: target_link_libraries(${PROJECT_NAME} PUBLIC SDL2::SDL2 SDL2::SDL2main GLEW::GLEW)
- when I create an executable with add_executable and not a library with add_library everything works as expected.

I've just began learning about CMake a few days ago, so feel free to correct me if I'm doing something wrong.

🌐
Cristianadam
cristianadam.eu β€Ί 20190501 β€Ί bundling-together-static-libraries-with-cmake
Bundling together static libraries with CMake - Cristian Adam
May 1, 2019 - You will have to take care of the POSITION_INDEPENDENT_CODE CMake property, which is not set for static libraries. This solves it. Everything works. But what if you want to make the QNX case even faster? (by removing the shared library all together!) We could just build only the static library, ...
🌐
CMake
cmake.cmake.narkive.com β€Ί Qws1knal β€Ί adding-dependencies-for-static-libraries
[CMake] Adding dependencies for static libraries
Nominally target_link_libraries isn't used at all for static libs, but if it should be, I'd expect the same behavior as that for shared libraries, which is to add the dependency libs to "Additional Dependencies". It just seems that no one thought of implementing it for the VS generators in the static lib case. Post by Titus von Boxberg If you want the librarian to combine A and B into a single A+ than you might as well add a post build step of A to do so. Yup, true, I don't know yet how to do it though, I had hopes that CMake would come to the rescue...
🌐
Reddit
reddit.com β€Ί r/cpp β€Ί how to build static library with bundled dependencies - cmake
r/cpp on Reddit: How to build static library with bundled dependencies - CMake
July 28, 2017 -

Note: This is a repost of an earlier post of mine which I deleted because I realized I didn't write a proper title

Hi there,

I am currently using CMake to create a static library (on Ubuntu) which utilizes a few of the static libraries from OpenCV 4 ( core imgcodecs video highgui imgproc ). My intention is to be able to bundle all of the required OpenCV static libraries into my own library so that I can distribute it as one library. Additionally, I want for the user of my library to not have to install OpenCV 4 on their system (but do not mind if the user has to do simple installs using apt-get install). I know there are tools for bundling static libraries (such as using ar for linux).

However, where I really am having the issue is with all the dependencies of OpenCV (such as libjpeg, libpng, etc). I don't necessarily mind if these libraries are bundled with mine or linked dynamically as they are relatively easy to install (can be installed with sudo apt-get install, whereas opencv4 needs to be built from source).

What is the best way to go about doing this?

This is my current CMakeLists.txt

It is currently working, but that is because I am using find_package(OpenCV REQUIRED) (which defeats the purpose of what I am trying to do). When I remove that line, the linker complains about not being able to find the OpenCV dependencies.

cmake_minimum_required(VERSION 2.8)
project(myproject)

set(CMAKE_CXX_STANDARD 14)
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
link_directories(${CMAKE_CURRENT_LIST_DIR}/lib)

find_package(OpenMP REQUIRED)
find_package(OpenCV REQUIRED)

set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory(nlohmann_json)

list(APPEND LINKER_LIBS opencv_core opencv_highgui opencv_video opencv_imgcodecs libmxnet.so libncnn.a nlohmann_json::nlohmann_json)
file(GLOB SRC${CMAKE_CURRENT_LIST_DIR}/src/*.cpp${CMAKE_CURRENT_LIST_DIR}/main.cpp)

add_library(myproject ${SRC})
target_link_libraries(myproject ${LINKER_LIBS} ${OpenMP_CXX_FLAGS})

To elaborate on my question. I build my project which generates libmyproject.a. I then take this library and will eventually extract the symbols from the OpenCV libs (libopencv_core.a libopencv_highgui.a libopencv_imgcodecs.a libopencv_video.a) and add them to my lib (for the time being, I have not yet done this step, which is why in the below example I am linking libopencv_*). I then use my library in a new project, for which the CMakeLists.txt is shown below:

cmake_minimum_required(VERSION 2.8)
project(myproject-driver)

set(CMAKE_CXX_STANDARD 14)
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
link_directories(${CMAKE_CURRENT_LIST_DIR}/lib)
find_package(OpenMP REQUIRED)

add_executable(myproject-driver main.cpp)
target_link_libraries(myproject-driver myproject libncnn.a ${OpenMP_CXX_FLAGS} libmxnet.so libopencv_core.a  libopencv_highgui.a  libopencv_imgcodecs.a  libopencv_video.a)

Building this generates the following errors:

Linking CXX executable myproject-driver
/usr/bin/ld: /home/nchafni/Cyrus/myproject/lib/libopencv_imgcodecs.a(grfmt_jpeg.cpp.o): undefined reference to symbol 'jpeg_default_qtables@@LIBJPEG_8.0'
//usr/lib/x86_64-linux-gnu/libjpeg.so.8: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status

How can I fix this. Is there some CMake command which will link all these dependencies for me? Do I need to manually track down each dependency of those libopencv_* libs and link those manually?

Top answer
1 of 3
6

Slightly tangential, but... CMake 2.8 is ancient. Is there a reason you're still using it?

Newer versions introduce much more sane defaults for many things. You really ought to increase your CMake version requirement, which enables those policies. You can also enable them one-by-one, but that's not a sane approach.

General advice:

  1. Use target_include_directories instead of include_directories

  2. Use target_link_directories instead of link_directories

  3. Note that CMAKE_CXX_STANDARD is only available since CMake 3.1.

2 of 3
4

Mind if I ask how come? As in are your customers asking you for that? Or are you doing it more as a learning exercise?

```Additionally, I want for the user of my library to not have to install OpenCV 4 on their system (but do not mind if the user has to do simple installs using apt-get install)```

In my experience customers like installers and package managers so using a tool like CPack with DEB/RPM generators will automatically create and orchestrate the required dependencies for pretty much most targeted platforms.

To answer your question more directly I'm not sure that it is possible in the generic sense. This is likely for two reasons, dependency hell and license agreements. Below is my limited knowledge someone can correct me if I am completely spreading FUD or wrong.

Sure your library may depend on static libraries. But the dependencies that you are specifying will likely have dependencies on shared libs. And your dependencies dependencies will likely have dependencies on shared libs, and we can follow the rabbit whole down into dependency hell.

Now assuming that we have a simple project and dependency hell is not an issue and everything is statically linked all the way through. Making the decision to compile a library static or shared has dramatic implications on your customers because of licensing. If you have any code that has hard FOSS requirements it means that your library will plainly not be used in business or probably the general sense if it is STATIC because the license agreements on things like LGPLv3 and LGPLv2 make it near impossible to incorporate into a customers environment. Now you need to manage and audit closely all of the ENTIRE licenses used by both you, your dependencies, and your dependencies dependencies.

As a learning excercise on how libraries are put together though I think it sounds like a really fun project!

Find elsewhere
🌐
CMake
cmake.org β€Ί cmake β€Ί help β€Ί v3.2 β€Ί command β€Ί target_link_libraries.html
target_link_libraries β€” CMake 3.2.3 Documentation
The library dependency graph is normally acyclic (a DAG), but in the case of mutually-dependent STATIC libraries CMake allows the graph to contain cycles (strongly connected components). When another target links to one of the libraries, CMake repeats the entire connected component.
🌐
CMake Discourse
discourse.cmake.org β€Ί code
static library with static library dependencies propagates its dependencies when exported as package - Code - CMake Discourse
February 14, 2023 - Hello, I have library which we will call libB with statically linked dependencies libE.a, libF.a and libG.a (prebuilt) in target_link_dependencies(libB PRIVATE libE.a libF.a libG.a PUBLIC m). I have defined different project progA which is using libB which is exported as B-config.cmake.
🌐
CMake
cmake.org β€Ί cmake β€Ί help β€Ί latest β€Ί guide β€Ί tutorial β€Ί Selecting Static or Shared Libraries.html
Step 10: Selecting Static or Shared Libraries β€” CMake 4.3.0-rc3 Documentation
Step 10: Selecting Static or Shared Libraries Β· This page was once part of an older version of the CMake tutorial which last appeared in CMake 4.1. See the current tutorial version here. To see the older version, follow this link or select the drop-down in the page header. Step 9: Packaging an Installer Β· Step 11: Adding ...
🌐
CMake
cmake.org β€Ί cmake β€Ί help β€Ί v3.0 β€Ί command β€Ί target_link_libraries.html
target_link_libraries β€” CMake 3.0.2 Documentation
The library dependency graph is normally acyclic (a DAG), but in the case of mutually-dependent STATIC libraries CMake allows the graph to contain cycles (strongly connected components). When another target links to one of the libraries, CMake repeats the entire connected component.
🌐
GitHub
github.com β€Ί costa-victor β€Ί cmake-lib-dependencies
GitHub - costa-victor/cmake-lib-dependencies: This example project demonstrate how to create static libraries and manage dependencies between them.
someLibC is a static library, but it depends on someLibB, which already has a dependecy on the someLibA. someLibC/example demonstrate how to use the someLibC and its dependency someLibB and headerOnlyLib. App depends on the someLibA, someLibB and someLibC Β· NOTE: Tested on macOS/Ubuntu, this may not work on Windows Β· β”œβ”€β”€ App # Application example β”‚ β”œβ”€β”€ CMakeLists.txt β”‚ └── src β”œβ”€β”€ README.md β”œβ”€β”€ LICENSE β”œβ”€β”€ headerOnlyLib # Header only library β”‚ β”œβ”€β”€ CMakeLists.txt β”‚ β”œβ”€β”€ cmake β”‚ β”œβ”€β”€ example β”‚ └── include β”œβ”€β”€ someLibA
Author Β  costa-victor
🌐
GitLab
gitlab.kitware.com β€Ί cmake β€Ί cmake β€Ί #19611
Library tries to link to private static dependency (#19611) Β· Issues Β· CMake / CMake Β· GitLab
August 19, 2019 - I have three libraries, A, B & C. A depends on B, and B privately depends on C. All are statically linked. They are also all in...
🌐
CMake
cmake.org β€Ί pipermail β€Ί cmake β€Ί 2018-September β€Ί 068263.html
[CMake] Best way to combine generated static libraries into a single static library
September 21, 2018 - (problems at runtime with duplicated > symbols in > >> > protobufs) > >> > > >> > 3- a variation around 1 > >> > instead of defining OBJECT libs, define a variable holding all the > >> > sources for lib1, another for lib2, ... > >> > then just do add_library(mysharedlib STATIC ${SOURCES_FOR_lib1} > >> > ${SOURCES_FOR_lib2}) > >> > It works a little bit like 1) but does not have any of its > problems > >> > (target_link, add_dependencies, generators, ...) > >> > It has new problems of its own though : if your libs live in > different > >> > subfolders, the variables might not be visible from your > >> > add_library(mysharedlib...) call.
🌐
CGold
cgold.readthedocs.io β€Ί en β€Ί latest β€Ί tutorials β€Ί libraries β€Ί static-shared.html
3.11.3. Static + shared β€” CGold 0.1 documentation
> cd library-examples [library-examples]> rmdir _builds _install /S /Q [library-examples]> cmake -Hright-way -B_builds\static -G "Visual Studio 14 2015" -DCMAKE_INSTALL_PREFIX=οΏ½%\_install [library-examples]> cmake --build _builds\static --config Release --target install ...
🌐
Alexreinking
alexreinking.com β€Ί blog β€Ί building-a-dual-shared-and-static-library-with-cmake.html
Building a Dual Shared and Static Library with CMake
March 6, 2021 - Static and shared libraries are typically produced from the same set of sources, too, so new CMake users sometimes expect that a single call to add_library will provide whatever mix of types they want. However, this is fundamentally incompatible with CMake's model of linking, which admits no properties on the link itself.
🌐
CMake
cmake.org β€Ί cmake β€Ί help β€Ί latest β€Ί prop_tgt β€Ί INTERFACE_LINK_LIBRARIES_DIRECT.html
INTERFACE_LINK_LIBRARIES_DIRECT β€” CMake 4.3.0-rc3 Documentation
Note that without the INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE target property, Foo would be linked twice: once as a direct dependency of app, and once as a dependency of FooPlugin. In the above Example: Static Plugins, the app executable specifies that it links directly to Foo. In a real application, there might be an intermediate library: add_library(app_impl STATIC app_impl.cpp) target_link_libraries(app_impl PRIVATE Foo) add_executable(app main.cpp) target_link_libraries(app PRIVATE app_impl)
🌐
CMake
cmake.org β€Ί pipermail β€Ί cmake β€Ί 2016-August β€Ί 064054.html
[CMake] Private dependencies of static libraries exported as targets
October 2, 2016 - It links to a library `bar`, which ... try to do modern CMake and package their library using IMPORTED targets): # somewhere in BarConfig.cmake add_library(bar STATIC IMPORTED ...) set_target_properties(bar PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${_IMPORT_PREFIX}/include ...
🌐
CMake
cmake.org β€Ί pipermail β€Ί cmake β€Ί 2019-February β€Ί 069016.html
[CMake] Static libraries depending on libraries: only on headers/options/defines
February 16, 2019 - It > builds a number of different binaries from an even larger number of > static libraries, and these libraries depend on each other as well, in > that they need to include headers and, sometimes, -D options etc. > > I've used straightforward target_link_libraries() to declare the > relationship between these libraries; for example: > > add_library(foo STATIC ...)