CMake append objects from different CMakeLists.txt into one library
Asked Answered
N

2

5

I would like to create a single library from objects from multiple sub-directories, each one containing their own CMakeLists.txt with OBJECT library trick to have multiple targets with different compile options.

Here are the files:

project_dir
|--- subdir1
|    |--- src1.c
|    |--- CMakeLists.txt
|--- subdir2
|    |--- src2.c
|    |--- CMakeLists.txt
|--- CMakeLists.txt

Contents of all CMakeLists.txt

// project_dir/CMakeLists.txt
// what to put here? to make one single library (mainLib)


// project_dir/subdir1/CMakeLists.txt
add_library(lib11 OBJECT src1.c)
set_target_properties(lib11 PROPERTIES COMPILE_FLAGS "some-flags11")

add_library(lib12 OBJECT src1.c)
set_target_properties(lib12 PROPERTIES COMPILE_FLAGS "some-flags12")
// here I would like to add lib11:objects and lib12:objects to mainLib
// how should it be done?

// project_dir/subdir2/CMakeLists.txt
// *** similar to subdir1 but with src2.c that creates lib21 and lib22
// here I would like to add lib21:objects and lib22:objects to mainLib
// how should it be done?

Can it be done platform independently? Thanks.

EDIT: Based on CMake: how create a single shared library from all static libraries of subprojects?, I can change the cmake files to the following, but still doesn't solve my problem.

// project_dir/subdir1/CMakeLists.txt
add_library(lib11 OBJECT src1.c)
set_target_properties(lib11 PROPERTIES COMPILE_FLAGS "some-flags11")

add_library(lib12 OBJECT src1.c)
set_target_properties(lib12 PROPERTIES COMPILE_FLAGS "some-flags12")

add_library(lib1 STATIC $<TARGET_OBJECTS:lib11> $<TARGET_OBJECTS:lib12>)

// Above add_library cannot be OBJECT which would fix my problem.
// how to append the lib1 (lib11 and lib12) to mainLib?

EDIT: Updating my post with attempt to chain interface libraries as suggested in the answer.

add_library(lib11 OBJECT test1/sub1/src1.c)
set_target_properties(lib11 PROPERTIES COMPILE_FLAGS "-DF1")

add_library(lib12 OBJECT test1/sub1/src1.c)
set_target_properties(lib12 PROPERTIES COMPILE_FLAGS "-DF2")

add_library(lib1 INTERFACE)
target_sources(lib1 INTERFACE $<TARGET_OBJECTS:lib11>)
target_sources(lib1 INTERFACE $<TARGET_OBJECTS:lib12>)

add_library(lib21 OBJECT test1/sub2/src2.c)
set_target_properties(lib21 PROPERTIES COMPILE_FLAGS "-DF1")

add_library(lib22 OBJECT test1/sub2/src2.c)
set_target_properties(lib22 PROPERTIES COMPILE_FLAGS "-DF2")

add_library(lib2 INTERFACE)
target_sources(lib2 INTERFACE $<TARGET_OBJECTS:lib21>)
target_sources(lib2 INTERFACE $<TARGET_OBJECTS:lib22>)

add_library(PARENT INTERFACE)

target_link_libraries(PARENT INTERFACE lib1)
target_link_libraries(PARENT INTERFACE lib2)

add_library(CORE OBJECT src.c)
add_library(GPARENT STATIC $<TARGET_OBJECTS:CORE>)

target_link_libraries(GPARENT INTERFACE PARENT)
Nosegay answered 13/3, 2018 at 21:12 Comment(3)
Thanks for replying. I already checked that link. I can use the same technique to combine different static library (OBJECT) within the subdirectory's CMakeLists.txt i.e. creating lib1 using lib11 and lib12 objects and similarly lib2. It doesn't seem like you can chain this technique, which would fix my problem. I don't want to add each individual object library (e.g. lib11, lib12, lib21, lib22) in my main CMakeLists.txt . Please explain if I am missing anything.Nosegay
Hm, so you want to compile the same OBJECT library with different flags, don't you? If yes, then you cannot - this would defeat the core purpose of OBJECT library as a set of precompiled object files. Just save list of source files into some variable, and use this list when create a main library with appropriate compiler flags.Bronwynbronx
Thanks again for replying. Yes. I need to compile the same source files in the sub-directories multiple times with different flags. What would be the best way to do that and have all the object files in the same library? In other words, if I do add_library(mainLib <some-source>) in the top-level CMakeLists.txt, how to add more object files (from subdirectories OBJECT libs) into this mainLib?Nosegay
B
8

Several OBJECT libraries can be incorporated into one INTERFACE library:

# Create lib1 OBJECT library with some sources, compile flags and so.
add_library(lib1 OBJECT ...)

# Create lib2 OBJECT library with some sources, compile flags and so.
add_library(lib2 OBJECT ...)

# Create INTERFACE library..
add_library(libs INTERFACE)
# .. which combines OBJECT libraries
target_sources(libs INTERFACE $<TARGET_OBJECTS:lib1> $<TARGET_OBJECTS:lib2>)

Resulted library can be used with standard target_link_libraries:

add_library(mainLib <some-source>)
target_link_libraries(mainLib libs)

It is possible to combine INTERFACE libraries futher:

# Create libs_another INTERFACE library like 'libs' one
add_library(libs_another INTERFACE)
..

# Combine 'libs' and 'libs_another' together
add_library(upper_level_libs INTERFACE)
target_link_libraries(upper_level_libs INTERFACE libs libs_another)

Resulted libraries' "chain" could be of any length.


While this way for combination of OBJECT libraries looks hacky, it is actually allowed by CMake documentation:

Although object libraries may not be named directly in calls to the target_link_libraries() command, they can be "linked" indirectly by using an Interface Library whose INTERFACE_SOURCES target property is set to name $<TARGET_OBJECTS:objlib>.

Bronwynbronx answered 15/3, 2018 at 8:8 Comment(9)
Perfect. This is what I was looking for. One related question: is it possible to chain it? I mean if I had another layer in the directory tree.Nosegay
Yes, it is possible to chain INTERFACE libraries futher. I have updated the answer.Bronwynbronx
Thanks again for your reply and the updated answer. I tried to combine the interface libraries as you suggested and then built an upper level lib using the combined interface library. The build process works, but for some reason, the upper-level lib doesn't contain any of the objects/symbols from the combined interface library. I also tried to add explicit dependencies to the interface library but still no change. Do I need to do anything else?Nosegay
Strange! I am also on Linux and using CMake 3.10.2 . Are you using a newer CMake?Nosegay
I have used CMake 3.4 or so. Could you show the code which doesn't work for you?Bronwynbronx
Hi Tsyvarev, at first I had each interface libraries built in a separate CMakeLists.txt . I also tried to combine all in one file but still didn't work. To keep it simple, I am putting the combined file here. Updating my main post.Nosegay
Sorry for long delay. I have checked your code, and it perfectly works for me. If I add an executable and link it with GPARENT, then it sees definitions of functions from all sources: test1/sub1/src1.c (compiled in two variants), test1/sub2/src2.c (compiled in two variants) and src.c.Bronwynbronx
Hi Tsyvarev, thanks for replying again. If you don't add an executable, does the library GPARENT have all 5 symbols? That's what I am trying to do. I am trying to build one big library from all these sub directories. No executable on my side.Nosegay
Command line for compile and link the library doesn't depend from whether someone uses this library or not. As the executable works, I assume the library is correct. OK, I will inspect the library tomorrow.Bronwynbronx
P
0

I just collect objects from all places using set with PARENT_SCOPE.

root CMakeLists.txt:

set(OBJECTS)

add_subdirectory(lib1)
add_subdirectory(lib2)

add_library(lib STATIC ${OBJECTS})

CMakeLists.txt in subdirectories:

add_subdirectory(lib11)

add_library(${PROJECT_NAME} OBJECT src1.c)
list(APPEND OBJECTS $<TARGET_OBJECTS:${PROJECT_NAME}>)
set(OBJECTS ${OBJECTS} PARENT_SCOPE)
Pneumectomy answered 28/5, 2019 at 10:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.