C++ Ubuntu上的Boost单元测试动态链接

C++ Ubuntu上的Boost单元测试动态链接,c++,ubuntu,singleton,dynamic-linking,boost-unit-test-framework,C++,Ubuntu,Singleton,Dynamic Linking,Boost Unit Test Framework,我正在尝试使用Boost的单元测试框架构建一个单元测试。我想动态链接测试套件库和Boost提供的自动生成的测试模块。以下是我一直使用的基本结构: test_main.cpp: #define BOOST_TEST_DYN_LINK #define BOOST_TEST_MAIN #include <boost/test/unit_test.hpp> 在Ubuntu14.04上测试,所有可执行文件编译和链接都没有错误 “unittest”无法执行“test_lib”套件,声称安装失

我正在尝试使用Boost的单元测试框架构建一个单元测试。我想动态链接测试套件库和Boost提供的自动生成的测试模块。以下是我一直使用的基本结构:

test_main.cpp:

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN

#include <boost/test/unit_test.hpp>
在Ubuntu14.04上测试,所有可执行文件编译和链接都没有错误

“unittest”无法执行“test_lib”套件,声称安装失败,但“unittest2”和“unittest3”成功:

$./unittest
Test setup error: test tree is empty
$./unittest2
Running 1 test case...
*** No errors detected
$./unittest3
Running 1 test case...
*** No errors detected
现在头痛的是:所有unittest*都在Fedora 20上运行测试套件

在查看“unittest”的依赖项列表时,我确实看到“libcase.so”没有在Ubuntu版本中列出,而是在Fedora20版本中。我对依赖项进行了重新排序,为SO使用了绝对路径,并更改了Boost的版本(1.54和1.55)。什么都没用

有什么办法可以阻止“libcase.so”在Ubuntu 14.04上链接,而不是在Fedora 20上链接?我是否缺少一些神奇的编译器/链接器标志

更新:

Sehe的评论和回答有助于进一步缩小问题的范围。如果我正确理解Boost的动态链接UTF实现(至少从1.54/55开始),那么框架提供了一个测试用例管理器单例。每个测试用例在构建时都会自动注册到管理器中

我认为问题在于,无论出于何种原因,在将库链接到二进制文件的过程中,Ubuntu上的链接“优化”了管理器单例实例使用的静态全局变量。实际上,尽管共享同一个全局静态变量,但它不会链接两个单例实例。它将它们视为两个独立的实例

我按照中描述的步骤检查库和二进制文件。与他们的情况不同,-rdynamic选项不能解决我的问题

我做了更多的测试,发现这很有趣。如果您预加载libcase.so对象,unittest将在Ubuntu上工作。即使libcase.so没有出现在其ldd列表中。我觉得这是意料之中的,因为当unittest运行时,管理器的单例是“预加载”的,它将与之链接

$ LD_PRELOAD=/absolute/path/to/libcase.so ./unittest
Running 1 test case ...

我仍然不知道为什么Ubuntu不想像预期的那样链接,而Fedora就是这样。阅读(特别是“与Microsoft DLL的比较”部分)让我觉得Ubuntu遵循的是Windows链接模式。

问题在于
lib\u case.o
被优化了,因为没有对其中包含的任何内容的引用

如果所有引用都是从测试用例定义引用到unittest框架(用于自注册),但没有引用回来,那么编译并链接测试主轴就是“未使用”库

我可以在我的系统(Ubuntu 14)上复制它。下面是一个简单的黑客程序,演示如何通过强制引用(在本例中是强制引用一个名为
force\u reference\u this\u object\u file
的全局变量)来修复它

注释

  • 当然,您通常会在头文件中声明该全局
  • 您会发现您需要部署
    libcase.so
    或使用LD\u LIBRARY\u PATH将
    /
    包含在库路径中
lib_case.cpp

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

int force_reference_this_object_file = 42;

BOOST_AUTO_TEST_SUITE( test_lib )

BOOST_AUTO_TEST_CASE( test_lib_case ) {
    BOOST_ASSERT(true);
}
BOOST_AUTO_TEST_SUITE_END()
#定义BOOST\u TEST\u DYN\u链接
#包括
int force_reference_this_object_file=42;
BOOST\u AUTO\u TEST\u套件(TEST\u lib)
BOOST\u AUTO\u TEST\u案例(TEST\u lib\u案例){
BOOST_断言(true);
}
BOOST\u AUTO\u TEST\u SUITE\u END()
test_main.cpp

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN

#include <boost/test/unit_test.hpp>

extern int force_reference_this_object_file;

namespace {
    struct Local
    {
        int& ref_;
        Local() : ref_(force_reference_this_object_file) {}
    };

    static Local hack_;
}
#定义BOOST\u TEST\u DYN\u链接
#定义BOOST_TEST_MAIN
#包括
外部强制引用此对象文件;
名称空间{
结构局部
{
int&ref;
Local():ref_(force_reference_this_object_file){}
};
静态本地黑客攻击;
}
生成文件

all: unittest unittest3

CPPFLAGS=-Wall -fPIC
LDFLAGS+=-L ~/WORK/pocpp/3rdparty/boost_1_58_0/stage/lib/

%.o: %.cpp
    g++ -c $(CPPFLAGS) $^ -o $@

libcase.so: lib_case.o
    g++ $(CPPFLAGS) -shared -Wl,-soname,$@ -o $@ $^

unittest: test_main.o | libcase.so
    #g++ $(CPPFLAGS) -o $@ $< $(LDFLAGS) -L. -lcase -lboost_unit_test_framework
    g++ $(CPPFLAGS) -o $@ $< $(LDFLAGS) ./libcase.so -lboost_unit_test_framework

unittest3: test_main.o lib_case.o
    g++ $(CPPFLAGS) -o $@ $^ $(LDFLAGS) -lboost_unit_test_framework
all:unittest unittest3
CPPFLAGS=-Wall-fPIC
LDFLAGS+=-L~/WORK/pocpp/3rdparty/boost\u 1\u 58\u 0/stage/lib/
%.o:%.cpp
g++-c$(CPPFLAGS)$^-o$@
libcase.so:lib_case.o
g++$(CPPFLAGS)-shared-Wl,-soname,$@-o$@$^
单元测试:test_main.o | libcase.so
#g++$(CPPFLAGS)-o$@$<$(LDFLAGS)-L.-lcase-lboost\U单元测试\U框架
g++$(cppfagas)-o$@$<$(LDFLAGS)。/libcase.so-lboost\u unit\u test\u框架
unittest3:test_main.o lib_case.o
g++$(CPPFLAGS)-o$@$^$(LDFLAGS)-lboost\U单元测试\U框架
明白了

Ubuntu在默认情况下似乎使用了
--as needed
链接器选项,而Fedora可能没有。关闭它会将libcase.so库添加到unittest所需的列表中。部署库(或使用LD_library_PATH)后,unittest现在可以工作了

unittest: libcase.so
        g++ -o unittest test_main.cpp -Wl,--no-as-needed -L. -lcase -lboost_unit_test_framework

我觉得这很简单…

显然Fedora上ld的默认版本或操作模式没有那么积极地优化未使用的对象。问题是使用了lib_case.o。所以Fedora没有优化它们是正确的。唉。我不知道你希望实现什么。我可以更改我的答案,但这对你没有帮助。事实上生命:链接器和工具链的行为变化很大(比这更大)跨平台。我还没有花足够的时间来弄清这件事是否存在缺陷。但是,如果他们的存储库中有47000个包是使用工具链构建的,那么你可以假设没有任何明显的缺陷。长话短说,你要么提交一个缺陷,要么使用它。无论如何,你现在必须使用它目标是能够动态加载单元测试模块。基本上,我想为我的软件的每个功能创建一个共享的单元测试库。然后,我可以创建一个二进制文件,动态链接所有共享库。随后,我可以为特定功能添加新的单元测试,重建功能库,部署它,然后简单地重新运行单元测试二进制文件,而无需重新编译单元测试二进制文件或任何其他功能的单元测试库。至少在理论上是这样。感谢黑客的攻击。我已经发现,两个
all: unittest unittest3

CPPFLAGS=-Wall -fPIC
LDFLAGS+=-L ~/WORK/pocpp/3rdparty/boost_1_58_0/stage/lib/

%.o: %.cpp
    g++ -c $(CPPFLAGS) $^ -o $@

libcase.so: lib_case.o
    g++ $(CPPFLAGS) -shared -Wl,-soname,$@ -o $@ $^

unittest: test_main.o | libcase.so
    #g++ $(CPPFLAGS) -o $@ $< $(LDFLAGS) -L. -lcase -lboost_unit_test_framework
    g++ $(CPPFLAGS) -o $@ $< $(LDFLAGS) ./libcase.so -lboost_unit_test_framework

unittest3: test_main.o lib_case.o
    g++ $(CPPFLAGS) -o $@ $^ $(LDFLAGS) -lboost_unit_test_framework
unittest: libcase.so
        g++ -o unittest test_main.cpp -Wl,--no-as-needed -L. -lcase -lboost_unit_test_framework