Reputation: 10750
Question: Is it possible to join multiple object libraries into a single larger object library?
Consider the following simple project. libA.cpp
is compiled into an object library LibA.Obj
, and libB.cpp
is compiled into an object library LibB.Obj
. I want to have an object library, LibAB.Obj
, that consists of both LibA.Obj
and LibB.Obj
, and I want to link against it to produce a single shared library.
MyProject
├── CMakeLists.txt
└── src
├── libA.cpp
└── libB.cpp
If I want to link two libraries, A and B, into a single unit, I can do that by declaring them as object libraries:
cmake_minimum_required(VERSION 3.21)
project(ObjectLibExample)
add_library(LibA.Obj OBJECT src/libA.cpp)
add_library(LibB.Obj OBJECT src/libB.cpp)
add_library(LibAB SHARED $<TARGET_OBJECTS:LibA.Obj> $<TARGET_OBJECTS:LibB.Obj>)
This works as expected, and everything builds.
For reasons I will soon explain, I would like to merge multiple object libraries together into a single unit:
cmake_minimum_required(VERSION 3.21)
project(ObjectLibExample)
add_library(LibA.Obj OBJECT src/libA.cpp)
add_library(LibB.Obj OBJECT src/libB.cpp)
# Using LibAB.Obj as a source should be equivilant to using both LibA.Obj and LibB.Obj
add_library(LibAB.Obj OBJECT $<TARGET_OBJECTS:LibA.Obj> $<TARGET_OBJECTS:LibB.Obj>)
add_library(LibAB SHARED $<TARGET_OBJECTS:LibAB.Obj>)
Here, LibAB.Obj
should be an object library collecting the sources of both LibA.Obj
and LibB.Obj
.
For some reason, CMake acts as though LibAB.Obj
provides no sources, and the build fails.
I am currently working on re-organizing an existing project to have a modular structure. It will have modules A, B, and C, each of which will have submodules.
You can imagine the directory structure looking like this:
MyProject
├── A
│ ├── A1
│ ├── A2
│ └── A3
├── B
│ ├── B1
│ ├── B2
│ └── B3
└── C
├── C1
├── C2
└── C3
Each submodule represents a library that forms a part of a greater whole, and the build system should link everything together into a single library at the end.
The reason for this structure is twofold:
For example, MyProject/A/A1
looks like this:
MyProject/A/A1
├── bench
├── CMakeLists.txt
├── examples
├── src
└── test
My initial plan was to have each module contain an object library linking all of it's respective submodules, with all modules being statically linked together at root to produce a single library, MyProject.so
.
Each submodule would also produce a library specific to that submodule (eg, MyProject/A/A1
would produce MyProject.A.A1.so
), against which tests, benchmark code, and examples would be linked. This would allow faster iteration, without needing to re-build the entire project.
Unfortunately, this initial approach failed, for the reasons discussed above.
Upvotes: 3
Views: 640
Reputation: 727
What worked for me was to link the transitive object libraries normally, and then recursively add their target objects using this function:
function(add_target_objects TARGET_NAME LIBRARY_NAME)
target_sources(${TARGET_NAME} PRIVATE $<TARGET_OBJECTS:${LIBRARY_NAME}>)
# recurse into library dependencies
get_target_property(libs ${LIBRARY_NAME} LINK_LIBRARIES)
if(libs)
foreach(lib ${libs})
add_target_objects(${TARGET_NAME} ${lib})
endforeach()
endif()
endfunction()
add_library(LibA.Obj OBJECT src/libA.cpp)
add_library(LibB.Obj OBJECT src/libB.cpp)
add_library(LibAB.Obj OBJECT)
target_link_libraries(LibAB.Obj PRIVATE LibA.Obj LibB.Obj)
add_library(LibAB SHARED)
add_target_objects(LibAB LibAB.Obj)
Upvotes: 0
Reputation: 82491
According to the documentation of add_library(OBJECT)
:
An object library compiles source files but does not archive or link their object files into a library.
[...]
Object libraries may contain only sources that compile, header files, and other files that would not affect linking of a normal library (e.g. .txt).
You haven't provided any sources that would compile, so a fatal error indicating that the object library isn't used in the intended way is expected.
A workaround possible starting cmake 3.12 would be to create an INTERFACE
library and linking the object libraries with INTERFACE
visibility.
add_library(LibAB INTERFACE)
target_link_libraries(LibAB.Obj INTERFACE LibA.Obj LibB.Obj)
add_library(LibAB SHARED)
target_link_libraries(LibAB PRIVATE LibAB.Obj)
This does prevent you from using $<TARGET_OBJECTS:LibAB.Obj>
though.
Upvotes: 0