我在使用automake的项目中遇到了一个非常奇怪的链接错误。从手册中我所做的看起来非常简单,所以我真的很想知道我做错了什么......
我的项目有三个文件夹:
每个库都使用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> >)'
起初我虽然是因为一些静态符号,但我也遇到了非静态符号的问题。
我检查了生成的libs,它似乎正确地包含了符号:
~/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
但是我在某个地方搞砸了,所以欢迎任何想法!
由于
PH
在文件夹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这样的选项, - 整个存档但它没有帮助(加上我不知道如何将它们添加到Automake生成的行......)
答案 0 :(得分:4)
这很可能是一个库排序问题 - 库越“普遍”,它应该在最后的链接行中发生。具体来说,GNU ld完全一次读取符号库,然后丢弃库中的所有其他符号,然后再转到下一个库指令。有各种解决方案(参见'ld'的手册页),但最简单的方法是重新排序Makefile.am中的行,以便在客户端和引擎库之后放置libube-common.a 。
请注意,Darwin ld 不具有此行为,默认情况下会保留所有库符号(在链接期间可能会占用更多内存)。