Reputation: 857
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
Upvotes: 9
Views: 10244
Reputation: 857
Tsyvarev's answer described well-enough what I was actually doing (rather than what I thought I was doing) to get me to look up the right things to answer the root question that I had ("Why does foo_runtime work but bar_runtime doesn't, when both link against static libraries linked against static libraries?")
From the CMake documentation for target_link_libraries:
Library dependencies are transitive by default with this signature. When this target is linked into another target then the libraries linked to this target will appear on the link line for the other target too.
target_link_libraries does not cause linking in the static libraries foo_subproject_1
and foo_subproject_2
(static libraries don't invoke the linker). What is does is makes the list of required libraries available for anything that tries to link with foo_subproject_1
or foo_subproject_2
So, effectively, my foo_runtime
and foo_lib
target_link_libraries command is, as far as CMake is concerned:
target_link_libraries(foo_runtime foo_subproject_1 some_library some_other_library foo_subproject_2 some_library some_other_library)
target_link_libraries(foo_lib foo_subproject_1 some_library some_other_library foo_subproject_2 some_library some_other_library)
foo_lib
, being static, doesn't invoke the linker. foo_runtime
, being an executable, does.
bar
is a totally different project, so doesn't get to take advantage of the transitive dependencies (and linking fails, because I'm missing all the symbols from the lower-level libraries).
This behaviour of target_link_libraries wasn't expected, as such the behaviour of the project as a whole was somewhat misleading (since it looked like I was bundling a bunch of libraries at the subproject level, and then bundling those at the upper level. In reality, I had just told the upper level what all the libraries it needed were).
To summerise:
Q "Why does foo_runtime work but bar_runtime doesn't, when both link against static libraries linked against static libraries?"
A "foo_runtime is not linking against static libraries linked against static libraries. foo_runtime is linked against more static libraries than you originally thought it was linking against.
bar_runtime doesn't work because your foo_lib is basically empty"
Upvotes: 2
Reputation: 65870
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!
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.
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.
Upvotes: 10