公司目前用bjam做build工具。bjam为boost项目所采用,对构建常规的C/C++工程比较方便,但是限制也很多,尤其是内建的脚本语言太晦涩,使得定制自己的构建规则很不方便。不过今天我想说的倒不是这些缺点,而是bjam 3.1.13在FreeBSD平台上的一个bug。
我们有3个文件,分别是
<1> main.c :
#include <stdio.h>
void foo_one(int);
int main()
{
foo_one(2007);
return 0;
}
<2> one.c :
#include <stdio.h>
void foo_two(int);
void foo_one(int a)
{
foo_two(a);
printf("in one lib:%d/n",a);
}
<3> two.c :
#include <stdio.h>
void foo_two(int a)
{
printf("in two lib:%d/n",a + 100);
}
现在把one.c和two.c分别build成libone.a和libtwo.a,然后用下面这个jamfile来构建可执行文件main :
project server : requirements
<warnings>on
<warnings-as-errors>on
<user-interface>console
: default-build release ;
exe main :
main.c
libone.a
libtwo.a ;
install all :
main
: <location>./ ;
因为main.c只引用了one.c里的foo_one,然后one.c引用了two.c里的foo_two,所以它们应该依照这个顺序出现在构建脚本中(如上例)。可是兄弟们,当你运指如飞敲入`bjam'和回车,满心欢喜地等待大功告成时,却发现这厮却撂挑子了,给出的理由是:
libone.a(one.o)(.text+0xd): In function `foo_one':
: undefined reference to `foo_two'
咦,foo_two怎么会找不到?不明明在libtwo里待着吗?按照Unix平台的链接规则(请参考这篇文章)链接器应该能把libtwo.a里的foo_two给揪出来,可为什么结果会这样呢?幸好bjam提供了debug相关选项能看清它所有的命令行调用。咱们也不必瞎猜了,赶紧运行`bjam -d2'来一窥究竟吧。
其它的输出信息就不必理会了,重要的是链接是如何调用的。在作者的机器上这条命令是这样的:
"g++" -o "main" "../../build/server_project/test/gcc-3.4.4/release/main.o" "libtwo.a" "libone.a" -Wl,--strip-all
对顺序敏感的读者一定发现了libtwo.a出现在libone.a之前,和它们在构建脚本中出现的顺序刚好相反,而这正是链接失败的元凶(还不明白的同学请仔细研读上面给出的参考文档)。
经试验,发现bjam在FreeBSD上用.a与.so构建目标时都会有顺序反转的问题,由于在unix平台上链接的成功依赖于链接顺序,所以这个bug很容易让不明就里的同学抓狂。
补充一点,windows平台上的bjam无此问题。但即使有,对使用Visual C++的用户也没有致命的影响,因为它的链接器对链接顺序并不敏感。