Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Background I have a project that uses other smaller projects. These projects themselves are made of other projects. A lot of this is legacy or has other managerial-fiat reasons for being arranged as is, so rolling everything into a single project is not an option. Some libraries are pre-compiled on remote shares.

I have 2 main subprojects that are giving me a headache:

  • Project Foo is an executable and library that links several static subprojects (foo_subproject_1, foo_subproject_n). These subprojects are further linked against static libraries in remote locations (some_lib, some_other_lib). Project Foo's executable compiles, links, and runs correctly

  • Project Bar is an executable that links several other projects, including libFoo. Linking fails with "undefined reference to " foo_subproject functions

As far as I can tell, the two projects are arranged similarly with their linkage instructions. Looking on SO, I discover that linking static libraries against static libraries shouldn't work, but then I'm confused as to how Project Foo is compiled successfully.

gcc and g++ 4.9.2 are the compilers (extern "C" problems of having some parts in C and some in C++ have already been checked for)


Question

I have misunderstood something about either or both how CMake add_subdirectory works, or about how the linker works. Can someone please explain how Project Foo works successfully, and Project Bar (doesn't) works as expected?

Update I looked closer at foo_lib.a and foo_runtime.

I should have determined that something was off to start with, because foo_runtime is nearly 100MB in size, and foo_lib is only 10KB.

nm reveals that foo_lib.a references a few dozen symbols, most of which are undefined. foo_runtime meanwhile references everything.

Equally confusing is that foo_subproject_1.a is similarly mostly undefined. Again, this is what I expect to see; but I don't understand how foo_runtime can be built from this?

I'm still unclear as to why some_library -> subproject -> foo_runtime is successful, but some_library -> subproject -> foo_lib -> bar isn't. At this stage of my investigations, I am expecting both commands to fail.


Project Foo is arranged (using CMake) thusly:

cmake_minimum_required(VERSION 2.6)
project(foo)

set(FOO_SRCS
    # source and headers for main foo project
)

# Project Foo libraries are subdirectories within this project
add_subdirectory(subs/foo_subproject_1)
add_subdirectory(subs/foo_subproject_2)

# Runtime executable
add_executable(foo_runtime main.c ${FOO_SRCS})
target_link_libraries(foo_runtime foo_subproject_1 foo_subproject_2)

# Library version (static library)
add_library(foo_lib STATIC ${FOO_SRCS})
target_link_libraries(foo_lib foo_subproject_1 foo_subproject_2)

Project Foo's subdirectories loosely have the following architecture:

cmake_minimum_required(VERSION 2.6)
project(foo_subproject_<n>)

set(FOO_SUBPROJECT_<N>_SRCS
    # source and headers for subproject
)

# foo_subproject's remote libraries are all static
add_library(some_lib STATIC IMPORTED)
set_target_properties(some_lib PROPERTIES IMPORTED_LOCATION /path/to/libsome_lib.a)

add_library(some_other_lib STATIC IMPORTED)
set_target_properties(some_other_lib PROPERTIES IMPORTED_LOCATION /path/to/libsome_other_lib.a)

include_directories(/paths/to/libs/include/)

# Static library for foo_subproject_N, links against static libs above
add_library(foo_subproject_<N> STATIC ${FOO_SUBPROJECT_<N>_SRCS})
target_link_libraries(foo_subproject_<N> some_library some_other_library)

Project Bar is arranged thusly:

cmake_minimum_required(VERSION 2.6)
project(bar)

set(BAR_SRCS
    # source and headers for main bar project
)

# Project Bar libraries are remote from Bar's perspective
add_library(foo_lib STATIC IMPORTED)
set_target_properties(foo_lib PROPERTIES IMPORTED_LOCATION /path/to/foo/libfoo_lib.a)

include_directories(/path/to/foo/include/)

# Runtime executable
add_executable(bar main.c ${BAR_SRCS} foo_lib)

Project Bar fails to link (compiles ok) with multiple errors of the form:

bar_frobulator.cpp:123: undefined reference to 'foo_subproject_1_init_frobulation'

where foo_subproject_1_init_frobulation lives in foo_subproject_1

like image 765
KidneyChris Avatar asked Aug 27 '18 11:08

KidneyChris


1 Answers

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.

like image 160
Tsyvarev Avatar answered Dec 02 '22 19:12

Tsyvarev