SCons 使用 Scons 改造现有项目

laofo · 2010年03月25日 · 11 次阅读

版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明 http://bigwhite.blogbus.com/logs/32790581.html

今天是冬至,也是入冬以来感觉最冷的一天,毫不夸张的说:你一张嘴,牙就冻上了。上午 LP 在家收拾卫生,我继续用 Scons 改造现有的项目。下午出去理发,头发长长了后,似乎会造成思维迟钝^_^。

试验性的用 Scons 改造现有的 project,过程中对 Scons 了解又多了一些。上篇文章对 Scons 的性能没有给出定论,经过对 Scons 的深入后,发现 Scons 在执行初始时的性能的确不够快,这是因为 Scons 启动后,会对全部 SConstruct 以及下面子目录中的 SConscript 进行分析,子目录越多 Sconscript 文件个数越多,性能也就越差。但是这种分析也有一个优点,就是能帮你提前发现你 SConscript 中的一些 “语义” 错误,比如如果你在编译两个基础库,一个叫 add,一个叫 sub,这个基础库源码分别分布在两个目录 add 和 sub 中,编译后将分别生成 libadd.a 和 libsub.a 的库文件,但是如果你马虎了,在编写 SConscript 时将 target 都写成了'add'或都写成了'sub',则 Scons 会在执行 gcc 之前就帮你找出这个"语义"错误,提示如下: /export/home1/tony_bai/xxlib>scons -f SC*t scons: Reading SConscript files ... scons: *** Multiple ways to build the same target were specified for: /export/home1/tony_bai/xxlib/lib/libsub.a (from ['/export/home1/tony_bai/xxlib/add/libsub.a'] and from ['libsub.a']) File "/export/home1/tony_bai/xxlib/sub/SConscript", line 3, in

Scons 脚本基本写的差不多了,编译也 ok 了,但是编译出来的可执行程序在执行时却出现了问题:提示找不到某.so 文件。而用项目"原配"的 Makefile 编译出来的可执行程序却执行的很好,没有类似问题,百思不得其解。将.so 文件所在目录放到"LD_LIBRARY_PATH"中,问题得以解决,但这更加深了对这一现象的质疑。起初我一直以为是 Scons 在编译选项上不规范造成的,而 Scons 使用 gcc -G -o xx.so xx.o 来编译也的确有值得的怀疑点,-G 选项是我从未见过的 gcc 编译选项,查了半天手册也没有对该参数的说明,遂放弃。上工具吧!先用 ldd 对编译出来的可执行文件进行分析,我们先来假设用 Scons 编译出来的可执行程序名字为 Bin-scons,用"原配"Make 编译出来的可执行程序名字为 Bin-make。ldd 将列出可执行文件中动态依赖的库的名字,并在本机定位出各个动态库的位置。对 Bin-scons 和 Bin-make 分别 ldd 的结果却让我大吃一惊,Bin-scons 的 ldd 结果很正常,xx.so 出现在 list 中,并且其位置为我刚刚加入到 LD_LIBRARY_PATH 中的那个目录;但是 Bin-make 的 ldd 结果中却不见了 xx.so 的踪影,这是怎么回事呢?回头翻看 Makefile,并且又执行了多遍 Make,项目的 Makefile 明明是构造了 xx.so,在生成 Bin-make 时链接了 xx.so,并且 Bin-make 中使用了 xx.so 中提供的接口。再次仔细对比 Make 和 Scons 编译.so 时的差别,这回发现了些许不同的地方,"原配"的 make 在编译.so 时,除了用了-shared -fPIC 之外,还用了"-c"选项,而从 Scons 日志中只能看到 gcc -G -o libxx.so xx.pic.o,显然 Scons 先控制 gcc 将 xx.c 编译为 xx.pic.o,再由 xx.pic.o 构成 libxx.so,而且我发现用 Scons 和 Make 编译出的.so 文件大小居然不同。显然"-c"对两个编译过程带来了影响。一般来说,我们在编译一个动态库时是不会使用"-c"的,这里先不论项目 Makefile 写的是否 ok,单说"-c"会给编译过程带来什么吧。打开 gcc 的"--verbose"开关,我们来试试使用和不使用"-c"gcc 都做了些什么。还是以 add.c 为例,将 add.c 编译为 libadd.so。

gcc -o libadd.so -shared -fPIC -c add.c --verbose 执行结果: Reading specs from /usr/local/lib/gcc-lib/sparc-sun-solaris2.9/3.2/specs Configured with: ../configure --with-as=/usr/ccs/bin/as --with-ld=/usr/ccs/bin/ld --disable-nls Thread model: posix gcc version 3.2 /usr/local/lib/gcc-lib/sparc-sun-solaris2.9/3.2/cc1 -lang-c -v -D_GNUC=3 -DGNUC_MINOR=2 -DGNUC_PATCHLEVEL=0 -DGXX_ABI_VERSION=102 -Dsparc -Dsun -Dunix -Dsvr4_ -D_SVR4 -DPRAGMA_REDEFINE_EXTNAME -Dsparc_ -D_sun_ -D_unix_ -D_svr4_ -D_SVR4 -DPRAGMA_REDEFINE_EXTNAME -Dsparc -Dsun -Dunix -Asystem=unix -Asystem=svr4 -DNO_INLINE_ -D_STDC_HOSTED=1 -DSIZE_TYPE=unsigned int -DPTRDIFF_TYPE=int -DWCHAR_TYPE=long int -DWINT_TYPE=long int -DGCC_NEW_VARARGS_ -Acpu=sparc -Amachine=sparc add.c -quiet -dumpbase add.c -version -fPIC -o /var/tmp//cca0mHxn.s GNU CPP version 3.2 (cpplib) (sparc ELF) GNU C version 3.2 (sparc-sun-solaris2.9) compiled by GNU C version 3.2. ignoring nonexistent directory "NONE/include" ignoring nonexistent directory "/usr/local/sparc-sun-solaris2.9/include" #include "..." search starts here: #include <...> search starts here: /usr/local/include /usr/local/lib/gcc-lib/sparc-sun-solaris2.9/3.2/include /usr/include End of search list. /usr/ccs/bin/as -V -Qy -s -K PIC -o libadd.so /var/tmp//cca0mHxn.s /usr/ccs/bin/as: Sun WorkShop 6 update 2 Compiler Common 6.2 Solaris_9_CBE 2001/04/02

gcc -o libadd.so -shared -fPIC add.c --verbose 执行结果: Reading specs from /usr/local/lib/gcc-lib/sparc-sun-solaris2.9/3.2/specs Configured with: ../configure --with-as=/usr/ccs/bin/as --with-ld=/usr/ccs/bin/ld --disable-nls Thread model: posix gcc version 3.2 /usr/local/lib/gcc-lib/sparc-sun-solaris2.9/3.2/cc1 -lang-c -v -D_GNUC=3 -DGNUC_MINOR=2 -DGNUC_PATCHLEVEL=0 -DGXX_ABI_VERSION=102 -Dsparc -Dsun -Dunix -Dsvr4_ -D_SVR4 -DPRAGMA_REDEFINE_EXTNAME -Dsparc_ -D_sun_ -D_unix_ -D_svr4_ -D_SVR4 -DPRAGMA_REDEFINE_EXTNAME -Dsparc -Dsun -Dunix -Asystem=unix -Asystem=svr4 -DNO_INLINE_ -D_STDC_HOSTED=1 -DSIZE_TYPE=unsigned int -DPTRDIFF_TYPE=int -DWCHAR_TYPE=long int -DWINT_TYPE=long int -DGCC_NEW_VARARGS_ -Acpu=sparc -Amachine=sparc add.c -quiet -dumpbase add.c -version -fPIC -o /var/tmp//ccz128Nl.s GNU CPP version 3.2 (cpplib) (sparc ELF) GNU C version 3.2 (sparc-sun-solaris2.9) compiled by GNU C version 3.2. ignoring nonexistent directory "NONE/include" ignoring nonexistent directory "/usr/local/sparc-sun-solaris2.9/include" #include "..." search starts here: #include <...> search starts here: /usr/local/include /usr/local/lib/gcc-lib/sparc-sun-solaris2.9/3.2/include /usr/include End of search list.

/usr/ccs/bin/as -V -Qy -s -K PIC -o /var/tmp//ccoU5RTD.o /var/tmp//ccz128Nl.s /usr/ccs/bin/as: Sun WorkShop 6 update 2 Compiler Common 6.2 Solaris_9_CBE 2001/04/02 /usr/local/lib/gcc-lib/sparc-sun-solaris2.9/3.2/collect2 -V -G -dy -z text -Y P,/usr/ccs/lib:/usr/lib -Qy -o libadd.so /usr/local/lib/gcc-lib/sparc-sun- solaris2.9/3.2/crti.o /usr/ccs/lib/values-Xa.o /usr/local/lib/gcc-lib/sparc-sun-solaris2.9/3.2/crtbegin.o -L/usr/local/lib/gcc-lib/sparc-sun- solaris2.9/3.2 -L/usr/ccs/bin -L/usr/ccs/lib -L/usr/local/lib/gcc-lib/sparc-sun-solaris2.9/3.2/../../.. /var/tmp//ccoU5RTD.o -lgcc_s -lgcc_s /usr/local/lib/gcc-lib/sparc-sun-solaris2.9/3.2/crtend.o /usr/local/lib/gcc-lib/sparc-sun-solaris2.9/3.2/crtn.o ld: Software Generation Utilities - Solaris Link Editors: 5.9-1.276

对比这两次的执行结果,我们可以发现,使用了-c 的编译过程实际上不是一个完整的共享库 (动态库.so) 的构建过程,而只是一个带有"-shared, -fPIC"的目标文件 (.o) 的编译过程,缺少 gcc crt 目标文件的链接过程,只是目标文件被命名为 libadd.so 了。这恰恰能解释我们前面提到了两点疑问了。为什么 ldd Bin-make 时没有发现其依赖 xx.so 以及 Bin-make 执行时一切 ok,没有报 “找不到 xx.so”,这一切都是因为 xx.so 实际上是以.o 形式存在的一个文件,在构建 Bin-make 链接 xx.so 时,实际上做到是静态链接而不是动态链接,xx.so 中的接口代码都已经存在于 Bin- make 中了,所以 ldd 无法找到对 xx.so 的依赖,Bin-make 执行时也无需找到 xx.so 了。看来这是项目 Makefile 中的一个问题了,只是这个"问题"隐藏太久而未能被发现罢了。

从收音机中得知"冬至"这天应该吃饺子,晚上和 LP 煮了两包水饺,热腾腾的,吃得直打饱嗝^_^。

暂无回复。
需要 登录 后方可回复。