Using Boost.Build and Boost.Test - linker errors

2019-08-08 03:00发布

问题:

I'm stuck trying to figure out why my tests won't link. I'm using Boost.Build and Boost.Test from Boost 1.59 on Ubuntu. I built and installed the boost libraries from source.

I am specifying <library>/boost//unit_test_framework as part of the unit test's usage requirements. My understanding is that this should set all of the linker requirements in order to link with Boost.Test.

Unfortunately, I get a heap of linker errors for undefined references to Boost.Test components even though the link command is specifying -lboost_unit_test_framework after the test.o file.

I have a very simple example that exhibits the described behaviour:

In Jamroot:

if $(BOOST_ROOT)
{
    use-project /boost : $(BOOST_ROOT) ;
}

import boost ;
boost.use-project ;

using testing ;

unit-test test
    : test.cpp
    : <library>/boost//unit_test_framework
    ;

In test.cpp:

#define BOOST_TEST_MODULE test
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE (pass) {

}

Running b2 -d+2 gives me the compilation and linker commands that are used.

$ b2 -a -d+2
gcc.compile.c++ bin/gcc-4.9.2/debug/test.o

    "g++"  -ftemplate-depth-128 -O0 -fno-inline -Wall -g -std=c++11 -fPIC  -DBOOST_ALL_NO_LIB -DBOOST_TEST_DYN_LINK   -c -o "bin/gcc-4.9.2/debug/test.o" "test.cpp"

gcc.link bin/gcc-4.9.2/debug/test

    "g++"    -o "bin/gcc-4.9.2/debug/test" -Wl,--start-group "bin/gcc-4.9.2/debug/test.o"  -Wl,-Bstatic  -Wl,-Bdynamic -lboost_unit_test_framework -Wl,--end-group -g 

bin/gcc-4.9.2/debug/test.o: In function `__static_initialization_and_destruction_0(int, int)':
/home/anthony/Development/example/test.cpp:4: undefined reference to `boost::unit_test::ut_detail::auto_test_unit_registrar::auto_test_unit_registrar(boost::unit_test::test_case*, boost::unit_test::decorator::collector&, unsigned long)'
bin/gcc-4.9.2/debug/test.o: In function `boost::unit_test::make_test_case(boost::function<void ()> const&, boost::unit_test::basic_cstring<char const>, boost::unit_test::basic_cstring<char const>, unsigned long)':
/usr/local/include/boost/test/tree/test_unit.hpp:249: undefined reference to `boost::unit_test::test_case::test_case(boost::unit_test::basic_cstring<char const>, boost::unit_test::basic_cstring<char const>, unsigned long, boost::function<void ()> const&)'
collect2: error: ld returned 1 exit status
...skipped <pbin/gcc-4.9.2/debug>test.passed for lack of <pbin/gcc-4.9.2/debug>test...
...failed updating 1 target...

回答1:

even though the link command is specifying -lboost_unit_test_framework after the test.o

Note that with the use of

-Wl,--start-group ...test.o  -lboost_unit_test_framework -Wl,--end-group

the order of test.o and the library doesn't matter: because of --{start,end}-group, they are considered to be part of the same "bookshelf".

It must be that the libboost_unit_test.{a,so} does not provide the symbols that you need. Perhaps it was built with a different version of g++? (This answer may be relevant.)

Your first step should be to figure out the mangled name of the symbol(s) you are missing: add -Wl,--no-demangle to your link line.

Then you can verify that you are in fact missing the required symbol(s):

nm -A libboost_unit_test.* | grep missing_mangled_symbol

Finally, you'll need to understand why you are missing that symbol. Is there a similar but slightly different symbol defined? This command may give some clues:

nm -AC libboost_unit_test.* | grep 'boost::unit_test::test_case::test_case'

Update:

I just discovered that not only the version of g++ matters here, but also the compilation flags. Consider:

// foo.cc
#include <string>
void foo(const std::string &) { }

// t.cc
#include <string>
void foo(const std::string &);
int main() { std::string s; foo(s); }

Using recent GCC trunk:

g++ -c t.cc foo.cc
g++ t.o foo.o  # success

g++ -D_GLIBCXX_USE_CXX11_ABI=0 t.cc foo.o
/tmp/ccKqZVvh.o: In function `main':
t.cc:(.text+0x1d): undefined reference to `foo(std::string const&)'
collect2: error: ld returned 1 exit status