Combining Several Static Libraries into One Using Cmake

How to bundle multiple static libraries into single library in CMake for windows

If you are using Visual Studio, you can take advantage of the Microsoft Library Manager (LIB.exe) to combine your static libraries into one. Your CMake could follow these steps:

  1. Use find_program() to have CMake locate the MSVC lib.exe tool on your system. If you run cmake from the Visual Studio Command Prompt, find_program can locate lib.exe automatically, without using the optional PATHS argument to tell it where to look.

  2. Use CMake's add_custom_target() command to call lib.exe using the syntax for merging libraries:

    lib.exe /OUT:alpha_lib.lib  a.lib b.lib c.lib d.lib e.lib

    You can use target-dependent generator expressions in the custom target command to have CMake resolve the locations of your built libraries. The custom target will create a Project in your Visual Studio solution that can be run separately to merge all of the built static libraries into one library.

Your CMake could look something like this:

# Create the static libraries (a, b, c, d, and e)
add_library(a STATIC ${a_SOURCES})
...
add_library(e STATIC ${e_SOURCES})

# Tell CMake to locate the lib.exe tool.
find_program(MSVC_LIB_TOOL lib.exe)

# If the tool was found, create the custom target.
if(MSVC_LIB_TOOL)
add_custom_target(CombineStaticLibraries
COMMAND ${MSVC_LIB_TOOL} /OUT:$<TARGET_FILE_DIR:a>/alpha_lib.lib
$<TARGET_FILE:a>
$<TARGET_FILE:b>
$<TARGET_FILE:c>
$<TARGET_FILE:d>
$<TARGET_FILE:e>
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
endif()

CMake linking static libraries in different subdirectories into one single static library

Here is a minimal example using object libraries to manage sharing object files between static (or shared!) libraries and how to link to them.

In level1/CMakeLists.txt:

add_library(level1_obj OBJECT level1.cpp level1.h)
target_include_directories(level1_obj PUBLIC "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>")

add_library(level1)
target_link_libraries(level1 PUBLIC level1_obj)

In level2/CMakeLists.txt

add_subdirectory(level1)

add_library(level2_obj OBJECT level2.cpp level2.h)
target_include_directories(level2_obj PUBLIC "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>")

add_library(level2)
target_link_libraries(level2 PUBLIC level1_obj level2_obj)

Notice that level2 is linked to level1_obj, not level1.

In main/CMakeLists.txt:

cmake_minimum_required(VERSION 3.21)
project(TestProject)

add_subdirectory(level2)

add_executable(app Main.cpp)
target_link_libraries(app PRIVATE level2)

One bug to be aware of is that Xcode does not like libraries without any real source files. If that's a concern, you can add an empty source file to your static library targets.

Please be sure to read the documentation on object libraries:

  1. https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#object-libraries
  2. https://cmake.org/cmake/help/latest/command/target_link_libraries.html#linking-object-libraries
  3. https://cmake.org/cmake/help/latest/command/add_library.html#object-libraries

Also worth noting is that unless you have some need to distribute your level2 library independently from level1, it would be better to keep their objects separate and require linking to both. Normal target linking in CMake handles this automatically via the transitive linking mechanism.

Static libraries linked against other static libraries with CMake - one works, one doesn't. Why?

Can someone please explain how Project Foo works successfully, and Project Bar (doesn't) works as expected?

In short: Creating STATIC library doesn't involve linking step!

Details

In the Foo project you have an executable foo_runtime, which "works" because it is linked with proper libraries (e.g. with library foo_subproject_1 which defines foo_subproject_1_init_frobulation symbol).

An executable bar from Bar project doesn't perform that linking, so it fails. The line

target_link_libraries(bar foo_lib)

links with foo_lib, but this library doesn't defines the needed symbol foo_subproject_1_init_frobulation.

Note, that the line

target_link_libraries(foo_lib foo_subproject_1 foo_subproject_2)

in the Foo project doesn't perform actual linking: in general, building a static library doesn't involve linking step.

Given line just propagates include directories (and other compile-features) from foo_subproject_* libraries to the foo_lib one.

How to make it work

Because static library foo_lib doesn't track its dependency, you need to link bar with a library, which knows that. E.g., make foo_lib shared, or combine foo_subproject_* libraries into archive library, as suggested by the referenced question How to combine several C/C++ libraries into one?.

Alternatively, you may build Foo subproject within Bar one and, instead of creation of IMPORTED foo_lib target, use "normal" foo_lib target, created within Foo project. In that case, line

target_link_libraries(bar foo_lib)

would mean for CMake to (actually) link bar with foo_subproject_* libraries, because those libraries are "linked" (in CMake sense) into foo_lib. Again, the last "linking" has a meaning only for CMake: the file foo_lib.a doesn't aware about needing of foo_subproject_* libraries.



Related Topics



Leave a reply



Submit