Rich von Lehe
Rich von Lehe

Reputation: 1542

Using --whole-archive linker option with CMake and libraries with other library dependencies

I've got a project that used to be a giant set of source files that all got compiled and then linked as one executable. As a first step in making the project more modular, I am breaking up the build into several smaller chunks and making them static libraries. There's a hierarchy, so Exe1 will link against static libs Lib2A and Lib2B. Lib2A will depend on static Lib3A, lib3B, lib3C, etc. The numbers here show their layer in the hierarchy.

The problem is that I need to use --whole-archive when linking or else some symbols from the underlying libraries are not found.

When I add the below for the linking of Exe1:

target_link_libraries(Exe1 -Wl,--whole-archive Lib2A Lib2B -Wl,--no-whole-archive)

I end up with an actual link stage command like:

g++ -o Exe1 -Wl,--whole-archive libLib2A.a libLib2B.a -Wl,--no-whole-archive libLib3A.a libLib3B.a libLib3C.a

Inevitably, symbols from some of the layer 3 static libraries get lost and I get missing symbol errors.

I expected that because Lib2A has Lib3* libraries as dependencies, that they would also be "inside" the --whole-archive part of the linker command, but they show up outside.

I've tried many different combinations (e.g. putting the --whole-archive stuff at lower layers), but haven't come across an approach that works using CMake. What am I doing wrong?

Thanks

Upvotes: 24

Views: 26421

Answers (5)

Frederik
Frederik

Reputation: 719

CMake 3.24 adds an official way to do this using the LINK_LIBRARY:WHOLE_ARCHIVE generator expression:

add_library(lib1 STATIC ...)
add_library(lib2 ...)
target_link_libraries(lib2 PRIVATE "$<LINK_LIBRARY:WHOLE_ARCHIVE,lib1>")

That being said, using object libraries is usually an easier solution. That way the list of compiled object files from the library you are linking is simply used as-is in the final linker command, instead of first creating a static library file.

Upvotes: 14

3CxEZiVlQ
3CxEZiVlQ

Reputation: 38538

If you need to use the linker option --whole-archive, then you definably should use object libraries:

    # Lib2A/CMakeLists.txt
    add_library(Lib2A OBJECT ${Lib2A_SRC})
    # Lib2B/CMakeLists.txt
    add_library(Lib2B OBJECT ${Lib2B_SRC})

It is portable and does not require use the linker option --whole-archive.

Upvotes: 6

driedler
driedler

Reputation: 4190

The following worked for me. Consider two libraries:

  • my_platform
  • my_clib

We want the whole archive of my_clib, and my_platform links to it.

add_library(my_platform INTERFACE) # this could also be a regular library

add_library(my_clib STATIC)
target_sources(my_clib
PRIVATE 
    gcc_newlib_nano.c 
    gcc_newlib_nano_cpp.cc 
)

# Link my_clib and any other libs
target_link_libraries(my_platform
INTERFACE
    my_clib
)
# Ensure the whole archive is linked
target_link_options(my_platform
INTERFACE
-Wl,--whole-archive ${CMAKE_CURRENT_BINARY_DIR}/libmy_clib.a -Wl,--no-whole-archive
)

Upvotes: 4

Brandon Braun
Brandon Braun

Reputation: 326

As an alternative to the above answer, I needed to get something quick and dirty to see if the effort to add whole archive target flags (or convert the code base to object libraries...) was the right solution. By following the CMake Source Code for the default link command, I modified my project's command to:

set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_CXX_COMPILER>  <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> -Wl,--start-group -Wl,--whole-archive <LINK_LIBRARIES> -Wl,--no-whole-archive -Wl,--end-group")

It worked a treat! While not the greatest solution, it will at least get some results quickly.

Upvotes: 1

Rich von Lehe
Rich von Lehe

Reputation: 1542

For 3.12 and newer versions of CMake, I would use object libraries.

The workaround I found for versions earlier than that was to create an intermediate static library that used some property magic to place all linkage dependencies inside the --whole-archive section. For me, the top-level static library was called 'source'. It contained actually nothing itself, but had linkage dependencies on a bunch of other static libraries. I created 'source-combined' as follows:

add_library(source-combined STATIC "")
set_target_properties(source-combined PROPERTIES LINKER_LANGUAGE CXX)

target_link_libraries(source-combined PUBLIC
  -Wl,--whole-archive
  $<TARGET_PROPERTY:source,INTERFACE_LINK_LIBRARIES>
  -Wl,--no-whole-archive
)

Now when I create an executable or a shared library by linking against this souce-combined library, I get the --whole-archive and --no-whole-archive as bookends around the entire set of static libraries that were the link dependencies of 'source'. It took forever to stumble across this technique, so I'm sharing it.

Upvotes: 30

Related Questions