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:
Use
find_program()
to have CMake locate the MSVClib.exe
tool on your system. If you runcmake
from the Visual Studio Command Prompt,find_program
can locatelib.exe
automatically, without using the optionalPATHS
argument to tell it where to look.Use CMake's
add_custom_target()
command to calllib.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:
- https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#object-libraries
- https://cmake.org/cmake/help/latest/command/target_link_libraries.html#linking-object-libraries
- 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
Restore the State of Std::Cout After Manipulating It
Overload a C++ Function According to the Return Value
How to Read a File Line by Line or a Whole Text File At Once
How Does Array[100] = {0} Set the Entire Array to 0
Should the Trailing Return Type Syntax Style Become the Default For New C++11 Programs
Why Does Int Pointer '++' Increment by 4 Rather Than 1
The Procedure Entry Point _Gxx_Personality_V0 Could Not Be Located
Scope Resolution Operator Without a Scope
Fixed-Size Floating Point Types
Is Floating-Point Addition and Multiplication Associative
Serializing a Class Which Contains a Std::String
Difference Between Conversion Specifiers %I and %D in Formatted Io Functions (*Printf/*Scanf)
Opengl - Index Buffers Difficulties
Propagating 'Typedef' from Based to Derived Class For 'Template'
How to Convert a Double into a String in C++