今天在Emacs编译代码过程中遇到了一个以前一直忽略的问题。
最近常用emacs中定义的compile command调用g++编译小demo,今天发现以前一段可以运行的代码在emace下编译无法运行,提示符号引用找不到,而所用的库和代码以前在slickedit下都完全正常。
其实问题出在编译命令的文件名顺序导致的链接依赖顺序不对,可气的是,这类问题以前还写过篇文章记录这个,但这么快就忘了。
c++编译成执行文件需要经过预处理、编译、链接几个阶段,分别对应不同的工具和命令参数,而通过g++可以用1条命令完成所有步骤。
虽然大部分gcc参数的顺序是无所谓的,但涉及到文件的参数必须按顺序摆放。
下面是那个编译失败的例子:
假设代码mutex.cpp中使用了boost::mutex,这使得生成执行文件时需要链接boost的thread库。
如果用下面命令编译:
g++ -IE:/workspace/c++/lib/boost_1_47_0 -LE:/workspace/c++/lib/boost_1_47_0/lib -lboost_thread-mgw45-mt-1_47 mutex.cpp -o mutex.exe
会出现如下一大堆错误:
E:\workspace\c++\boostDemo\thread>g++ -IE:/workspace/c++/lib/boost_1_47_0 -LE:/workspace/c++/lib/boost_1_47_0/lib -lboost_thread-mgw45-mt-1_
47 mutex.cpp -o mutex.exe
In file included from E:/workspace/c++/lib/boost_1_47_0/boost/thread/win32/thread_data.hpp:12:0,
from E:/workspace/c++/lib/boost_1_47_0/boost/thread/thread.hpp:15,
from E:/workspace/c++/lib/boost_1_47_0/boost/thread.hpp:13,
from mutex.cpp:1:
E:/workspace/c++/lib/boost_1_47_0/boost/thread/win32/thread_heap_alloc.hpp:59:40: warning: inline function 'void* boost::detail::allocate_ra
w_heap_memory(unsigned int)' declared as dllimport: attribute ignored
E:/workspace/c++/lib/boost_1_47_0/boost/thread/win32/thread_heap_alloc.hpp:69:39: warning: inline function 'void boost::detail::free_raw_hea
p_memory(void*)' declared as dllimport: attribute ignored
Info: resolving vtable for std::exception by linking to __imp___ZTVSt9exception (auto-import)
Info: resolving vtable for std::bad_exception by linking to __imp___ZTVSt13bad_exception (auto-import)
Info: resolving vtable for std::bad_alloc by linking to __imp___ZTVSt9bad_alloc (auto-import)
Info: resolving std::cout by linking to __imp___ZSt4cout (auto-import)
Info: resolving vtable for std::runtime_error by linking to __imp___ZTVSt13runtime_error (auto-import)
Info: resolving vtable for __cxxabiv1::__class_type_info by linking to __imp___ZTVN10__cxxabiv117__class_type_infoE (auto-import)
Info: resolving vtable for __cxxabiv1::__si_class_type_info by linking to __imp___ZTVN10__cxxabiv120__si_class_type_infoE (auto-import)
Info: resolving vtable for __cxxabiv1::__vmi_class_type_info by linking to __imp___ZTVN10__cxxabiv121__vmi_class_type_infoE (auto-import)
Info: resolving vtable for __cxxabiv1::__pointer_type_info by linking to __imp___ZTVN10__cxxabiv119__pointer_type_infoE (auto-import)
d:/tool/dev/c++/mingw/bin/../lib/gcc/mingw32/4.5.0/../../../../mingw32/bin/ld.exe: warning: auto-importing has been activated without --enab
le-auto-import specified on the command line.
This should work unless it involves constant data structures referencing symbols from auto-imported DLLs.
z:\temp\ccGMvhIY.o:mutex.cpp:(.text+0x145): undefined reference to `_imp___ZN5boost6thread4joinEv'
z:\temp\ccGMvhIY.o:mutex.cpp:(.text+0x152): undefined reference to `_imp___ZN5boost6thread4joinEv'
z:\temp\ccGMvhIY.o:mutex.cpp:(.text+0x15f): undefined reference to `_imp___ZN5boost6thread4joinEv'
z:\temp\ccGMvhIY.o:mutex.cpp:(.text+0x16c): undefined reference to `_imp___ZN5boost6threadD1Ev'
z:\temp\ccGMvhIY.o:mutex.cpp:(.text+0x184): undefined reference to `_imp___ZN5boost6threadD1Ev'
z:\temp\ccGMvhIY.o:mutex.cpp:(.text+0x191): undefined reference to `_imp___ZN5boost6threadD1Ev'
z:\temp\ccGMvhIY.o:mutex.cpp:(.text+0x1b5): undefined reference to `_imp___ZN5boost6threadD1Ev'
z:\temp\ccGMvhIY.o:mutex.cpp:(.text+0x1d5): undefined reference to `_imp___ZN5boost6threadD1Ev'
z:\temp\ccGMvhIY.o:mutex.cpp:(.text+0x1e6): more undefined references to `_imp___ZN5boost6threadD1Ev' follow
z:\temp\ccGMvhIY.o:mutex.cpp:(.text$_ZN5boost6threadC1IPFvPKcES3_EET_T0_[boost::thread::thread
char const*), char const*)]+0x3a): undefined reference to `_imp___ZN5boost6thread12start_threadEv'
z:\temp\ccGMvhIY.o:mutex.cpp:(.text$_ZN5boost6threadC1INS_3_bi6bind_tIvPFvPKcENS2_5list1INS2_5valueIS5_EEEEEEEET_NS_10disable_ifINS_14is_con
vertibleIRSD_NS_6detail13thread_move_tISD_EEEEPNS0_5dummyEE4typeE[boost::thread::thread t::_bi::list1 const*> > >, boost::disable_if r const*> > >&, boost::detail::thread_move_t > > > > >, boost::thread::dummy*>::type)]+0x2a): undefined reference to `_imp___ZN5boost6thread12start_threadEv' z:\temp\ccGMvhIY.o:mutex.cpp:(.text$_ZN5boost6threadC1INS_8functionIFvvEEEEET_NS_10disable_ifINS_14is_convertibleIRS5_NS_6detail13thread_mov e_tIS5_EEEEPNS0_5dummyEE4typeE[boost::thread::thread nvertible defined reference to `_imp___ZN5boost6thread12start_threadEv' collect2: ld returned 1 exit status 上面的错误实际表明了4个信息: 1.由于这里是使用的boost动态库,boost的thread动态库涉及的代码需要链接gcc所实现的标准库,所以在编译过程中提示这些符号是auto-import的。 2.代码编译可以通过。 3.链接库文件g++能找到(如果找不到库文件不会是这种出错信息,信息会是“no such file”) 4.当mutex.cpp生成的obj链接时出现了无法找到boost thread库文件符号的问题。 一时没想到这个问题的原因。 但以前用slickedit使用同样的程序代码和链接库却是没问题的,所以问题还应该在编译命令的细节上。 slickedit下的编译使用的是一套通用的make文件,基本思路就是目录下源文件统统生成obj,然后obj统统链接生成exe或者lib。 其make文件中包含了compile和link的命令定义: 毫无疑问,文本格式的code转化成exe执行文件,必然是先生成obj,而后链接,即使用g++一个命令这也不会有区别。 因此,注意到错误命令中cpp文件是出现在最后,所以问题原因应该是编译命令中cpp文件名的顺序决定了obj的顺序,而obj的顺序又决定了其依赖顺序。根据gcc的编译原则,如果A库依赖B库则B必须在命令行中放在A库后面(右面) .参考以前的一篇博客: 到此问题就比较明了了,由于mutex.cpp在编译命令中的位置处于-lboost_thread-mgw45-mt-1_47之后(更右边)而mutex.cpp又依赖 thread库,cpp的顺序决定了在链接时所生成的mutex.obj会优先于boost_thread-mgw45-mt之前进行符号解析和链接,过早的链接导致了链接失败。 在编译命令中越靠前(更左边)的obj或者lib会越晚完成链接,因此在写编译命令时应该保证被依赖的库都应该在命令行中处于依赖它的库的后面(更右边) 因此正确的命令是: g++ -IE:/workspace/c++/lib/boost_1_47_0 mutex.cpp -LE:/workspace/c++/lib/boost_1_47_0/lib -lboost_thread-mgw45-mt-1_47 -o mutex.exe 注意:-I放在的顺序是无所谓的,也就是说: g++ mutex.cpp -IE:/workspace/c++/lib/boost_1_47_0 -LE:/workspace/c++/lib/boost_1_47_0/lib -lboost_thread-mgw45-mt-1_47 -o mutex.exe 这个命令也是没问题的,因为-I的所有包含文件都会在预处理阶段统一一次性完成,而不是像链接一样一个个obj链接。 也可以用两阶段指令完成生成exe,比如: g++ -c mutex.cpp -IE:/workspace/c++/lib/boost_1_47_0 g++ -o mutex.exe mutex.o E:/workspace/c++/lib/boost_1_47_0/lib/libboost_thread-mgw45-mt-1_47.dll.a 注意如果这里的两个.o文件位置颠倒,就会出现和上面一样的错误了. g++ -o mutex.exe E:/workspace/c++/lib/boost_1_47_0/lib/libboost_thread-mgw45-mt-1_47.dll.a mutex.o 严格来说这个问题其实和Emacs没有关系,只是由于emacs在调用M-x compile时可以通过定制命令行执行compile command来一次性调用g++生成执行文件,这样更容易曝露出这个问题,因为cpp在命令中的的位置隐式决定了其obj的链接顺序。 如果compile command定义成make文件则此问题不容易产生,因为写make文件的时候都会注意依赖关系,这样自动生成的g++编译命令都是符合被依赖的文件放后面的条件。 COMPILE=g++ -c -o "$(OUTDIR)/$(*F).o" $(CFG_INC) $<
LINK=g++ -o "$(OUTFILE)" $(ALL_OBJ)
http://ppgunjack.iteye.com/blog/988542