I am trying to build a unit test using Boost's unit test framework. I would like to dynamically link test suite libraries with the auto generated test module that Boost provides. Here is the basic construction I've been using:
test_main.cpp:
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
lib_case.cpp:
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_SUITE( test_lib )
BOOST_AUTO_TEST_CASE( test_lib_case ) {
BOOST_ASSERT(true);
}
BOOST_AUTO_TEST_SUITE_END()
Makefile:
all: unittest unittest2 unittest3
lib_case.o: lib_case.cpp
g++ -g -c -Wall -fPIC lib_case.cpp -o lib_case.o
libcase.so: lib_case.o
g++ -shared -Wl,-soname,libcase.so -o libcase.so lib_case.o
unittest: libcase.so
g++ -o unittest test_main.cpp -L. -lcase -lboost_unit_test_framework
unittest2: test_main.cpp lib_case.cpp
g++ -o unittest2 test_main.cpp lib_case.cpp -lboost_unit_test_framework
unittest3: lib_case.o
g++ -o unittest3 test_main.cpp lib_case.o -lboost_unit_test_framework
Testing on Ubuntu 14.04, all executables compile and link without error.
'unittest' fails to execute the 'test_lib' suite claiming that the setup failed, but 'unittest2' and 'unittest3' succeed:
$./unittest
Test setup error: test tree is empty
$./unittest2
Running 1 test case...
*** No errors detected
$./unittest3
Running 1 test case...
*** No errors detected
Now for the headache: All unittest* run the test suite on Fedora 20.
In looking at the dependency lists for 'unittest', I do see that 'libcase.so' is not listed in the Ubuntu version, but is in the Fedora 20 version. I have played around with re-ordering the dependencies, using absolute paths for the SO, and changing versions of Boost (1.54 and 1.55). Nothing worked.
Any ideas on what may preventing the 'libcase.so' from be linked on Ubuntu 14.04 but not on Fedora 20? Am I missing some magic compiler/linker flag?
Update:
Sehe's comment and answer have helped to narrow down the problem a bit more. If I'm understanding Boost's dynamically linked UTF implementation (at least as of 1.54/55) correctly, then the framework provides a test case manager singleton. Every test case will automagically register with the manager upon construction.
I think the problem is that, for whatever reason, linking on Ubuntu 'optimizes out' the static global variable used for the singleton instance of the manager during the linking of the library to the binary. In effect, it does not link the two singleton instances despite sharing the same global static variable. It treats them as two separate instances.
I followed the steps described in Multiple instances of singleton across shared libraries on Linux to inspect the library and binary files. Unlike in their case, the -rdynamic option does not solve my problem.
I did some more testing and found this interesting. If you preload libcase.so object, unittest works on Ubuntu. Even though the libcase.so does not appear in its ldd listing. I feel like this is expected because the singleton for the manager was 'preloaded' when unittest runs it will link with it.
$ LD_PRELOAD=/absolute/path/to/libcase.so ./unittest
Running 1 test case ...
Still have no idea why Ubuntu does not want to link as expected/intended, where Fedora does. Reading this tutorial (specifically the 'Comparison to the Microsoft DLL' section) makes me think Ubuntu is following a Windows linking pattern.
Got it!
Ubuntu seems to use the
--as-needed
linker option by default, where as Fedora may not. Turning it off will add libcase.so library to the needed list for unittest. After deploying the library (or using LD_LIBRARY_PATH) the unittest works now.Figures it was something simple ...