Linking error with cmake

2019-02-23 09:31发布

问题:

I'm trying to understand why I get a linking error while compiling a project generated with CMake.

The CMakeFiles.txt builds a static library for each folder of the project and then link all of them together in the following way:

# root CMakeLists.txt

add_subdirectory(subfolder1)
add_subdirectory(subfolder2)
add_subdirectory(...)

add_executable(target ${SOURCES})

set(LIBRARIES
  LIB_FOO
  LIB_BAR
  ...
)

target_link_libraries(target
  ${LIBRARIES}
)

then in each subfolder I have a simple CMakeLists.txt like

file(GLOB FOO_SOURCE *.cpp)
add_library(LIB_FOO ${FOO_SOURCE})

Now this works and compiles everything fine but I get an undefined reference while linking, so I tried to investigate if everything was available at the end and it looks like so. The actual error is the following:

libLIB_WORLD.a(World.cpp.o): In function `World::generate(WorldGenOptions)':
World.cpp:(.text+0x803): undefined reference to `MapGenerator::MapGenerator(BlockMap*)'
World.cpp:(.text+0x837): undefined reference to `MapGenerator::generate(bool, WorldGenOptions)'

Now, MapGenerator.cpp is part of LIB_MAP, so I checked if the file exists and contains the symbols:

:~$ nm libLIB_MAP.a | grep generate
....
00000000000044dc T _ZN12MapGenerator8generateEb15WorldGenOptions

:~$ nm CMakeFiles/LIB_MAP.dir/MapGenerator.cpp.o | grep generate
....
00000000000044dc T _ZN12MapGenerator8generateEb15WorldGenOptions

So the symbol is present, at this point I checked if it was correctly linked by ld:

:~$ make VERBOSE=1
/usr/bin/g++  ... libLIB_MAP.a libLIB_WORLD.a ...

So it is actually present in linking phase together with the other library that is not able to find the symbol.

Is there something trivial I'm missing? I'm quite new to CMake so I'm out of ideas.

回答1:

This is a problem of a library dependency that was not modeled correctly in CMake.

Your LIB_WORLD references methods from LIB_MAP. This dependency is not modeled in your CMake scripts. As both of those are static libraries, they will still build fine on their own. (Remember that static libraries are essentially a bunch of object files packed together, but they never pass through the linker.)

However, as soon as you link them to an executable or a shared library, you will get the problem. Even though your executable links against both LIB_WORLD and LIB_MAP, it does so in the wrong order. So when the linker is trying to resolve the missing symbols for LIB_WORLD, it does not know yet about the symbols exported by LIB_MAP, hence the error message you experienced.

The proper fix is to introduce the dependency on LIB_WORLD:

add_library(LIB_WORLD [...])
target_link_libraries(LIB_WORLD LIB_MAP)

Now whenever you link something against LIB_WORLD you will always also link against LIB_MAP and CMake will take care that the order is right. (In particular, if your executable does not make direct use of methods from LIB_MAP you might want to remove it from its target_link_libraries altogether.)

As an additional benefit, it now allows you to build LIB_WORLD as a shared library, which would have failed with a linker error before.