6

我在使用 automake 的项目中遇到了一个非常奇怪的链接错误。从手册来看,我所做的似乎很简单,所以我真的想知道我会做错什么......

我的项目有三个文件夹:

  • src/common,我在其中将一些 C++ 文件编译成一个 libube-common.a 静态库
  • src/engine,我在其中将一些文件编译成一个 libube-engine.a 静态库
  • src/client,其中......你猜对了, libue-client.a ,还有一个文件 ube.cpp 这是我的主要

每个库都使用 Makefile.am 编译,如下所示:

noinst_LIBRARIES=libube-common.a
libube_common_a_SOURCES=gettext.h lua_helper.hpp \
 silent_ostream.hpp \
 logging.hpp logging.cpp \
 logger_interface.hpp \
     ... etc ...
AM_CPPFLAGS=-DSRCDIR=\"${srcdir}\" \
 -DLUADIR=\"${luadir}\" \
 -Wall -Werror \
 -I$(srcdir)/../../include \
 $(LUA_INCLUDE) \
 $(BOOST_CPPFLAGS)

这导致各种对象使用如下行构建:

g++ -DHAVE_CONFIG_H -I. -I../../../../../src/common -I../..  -DSRCDIR=\"../../../../../src/common\" -DLUADIR=\"\" -Wall -Werror -I../../../../../src/common/../../include -I/usr/include/lua5.1 -I/usr/include   -g -O2 -MT logging.o -MD -MP -MF .deps/logging.Tpo -c -o logging.o ../../../../../src/common/logging.cpp

所有这些都放在图书馆里:

ar cru libube-common.a logging.o prefix_resource_resolver.o stat_file_checker.o 
ranlib libube-common.a

所有这些看起来都很好,我什至可以将一些小测试程序与库链接(在同一个 makefile 中)

然后,在我的主程序的 Makefile.am 中,我要求链接到本地​​库:

ube_LDADD=../common/libube-common.a \
      ../engine/libube-engine.a \
      libube-client.a \
          ... other libs ...

这就是我得到这样的错误的地方:

g++  -g -O2   -o ube ube.o ../common/libube-common.a ../engine/libube-engine.a libube-   client.a -L/usr/include/lua5.1/lib -llua5.1  -lm -ldl  -L/usr/lib -lSDL -lSDL_image -lpng -ltiff -ljpeg -lz -lSDL_ttf -lfreetype -lSDL_mixer -lSDL_mixer -lSDL_ttf -lSDL_image

libube-client.a(game_loop.o): In function `Logging::debug_ostream(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
(...) logging.hpp:54: undefined reference to `Logging::get_ostream(LogLevel::Level, std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
(...)logging.hpp:54: undefined reference to `Logging::get_ostream(LogLevel::Level, std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'

起初我虽然是因为一些静态符号,但我也遇到了非静态符号的问题。

我检查了生成的库,它似乎正确包含符号:

~/prj/ube/builds/linux/current/src/common$ nm -C libube-common.a  | grep logging.o -C 20

logging.o:
00000010 t global constructors keyed to _ZN7Logging15disable_loggingEv
00000000 V guard variable for Logging::get_instance()::s_local_instance
000000b0 T Logging::get_ostream(LogLevel::Level, std::string)
00000000 T Logging::disable_logging()
00000040 T Logging::is_category_enabled(LogLevel::Level, std::string&)
     U std::ios_base::Init::Init()
     U std::ios_base::Init::~Init()
00000000 b std::__ioinit
     U __cxa_atexit
     U __dso_handle
     U __gxx_personality_v0

唯一的解决方法是明确链接到我的 .o 文件(通过将它们添加到 ube_LDADD 行......但这有点违背使用库的想法!!)

我似乎一直在关注手册:http ://www.gnu.org/software/hello/manual/automake/Linking.html#Linking

但显然我在某个地方搞砸了,所以欢迎任何想法!

谢谢

酸碱度


编辑:图书馆本身似乎工作,这似乎是一个链接问题。我可以将我的测试用例程序与它们联系起来。这是我所做的:

在文件夹 src/common/tests 中,有一个名为 common-tests.cpp 的 main 运行单元测试;common-tests bin 与库 libube-common.a 链接(它只需要 lib 内的对象,因为这些是单元测试)

# There is one program that aggreatates all tests cases
check_PROGRAMS = common-tests
common_tests_SOURCES= tests/common_tests.cpp \
tests/prefix_resource_resolver_test.cpp \
tests/mock_file_checker.hpp \
tests/stat_file_checker_test.cpp

# The program needs to be compiled against the local lib 
common_tests_LDADD=libube-common.a -L$(top_srcdir)/lib -lgtest -lgmock -llua -ldl

# This means common-tests is run when using 'make check'.
TESTS = common-tests

运行 make check 时,测试程序是这样编译的:

g++  -g -O2   -o common-tests common_tests.o prefix_resource_resolver_test.o stat_file_checker_test.o libube-common.a -L../../../../../lib -lgtest -lgmock -llua -ldl -lSDL_mixer -lSDL_ttf -lSDL_image 

一切都很完美。我能看到的唯一区别是,在这种情况下,库就在要链接的可执行文件旁边……这真的有什么不同吗?

另外,我尝试使用 -Wl,--whole-archive 之类的选项,但没有帮助(另外我不知道如何将它们添加到 Automake 生成的行...)

4

1 回答 1

5

这很可能是一个库排序问题——一个库越“常见”,它应该越晚出现在最后的链接行中。具体来说,GNU ld 只读取库中的符号一次,然后从库中丢弃所有其他符号,然后继续执行下一条库指令。有多种解决方案(请参阅“ld”的手册页),但最简单的一种是重新排序 Makefile.am 中的行,将 libube-common.a 放在客户端和引擎库之后。

请注意,达尔文 ld 没有这种行为,它默认保留所有库符号(在链接期间可能会使用更多内存)。

于 2010-01-11T14:06:23.680 回答