Makefile有三个非常有用的变量。分别是$@,$^,$<代表的意义分别是:
$@--目标文件,$^--所有的依赖文件,$<--第一个依赖文件。
同时使用动态/静态库
GCC默认的链接库形式是动态的;如果要采用静态连接需要添加static参数,但是会导致整个GCC连接都是用静态连接方式。
这是可以使用-Wl,-Bstatic XXX -Wl,-Bdynamic XXX来指定需要静态连接和动态链接的库。
比如:-Wl,-Bstatic -lsqlite -Wl,-Bdynamic -lm。
libsqlite会被静态连接,而libm则会被动态连接。注意:-Wl,-Bstatic和-Wl,-Bdynamic的逗号后面不能有空格。
-L放在-Wl,-Bstatic还是-Wl,-Bdynamic之后,不会影响结果。
-Bstatic 还有三个写法: -dn和-non_shared 和-static
-Bdynamic 还有两个写法:-dy 和-call_shared
“--startgroup foo.o bar.o -Wl,--endgroup”表示一组。
编译静态动态库
编译动态库需要,指定一些编译选项:
-shared 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件。
-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
-L:表示要连接的库在当前目录中。
-I:指定头文件include查找目录。
-l:指定要使用的库名称,在静态库和动态库同名条件下,优先使用动态库。
-Wl,-soname :指定soname。
CFLAGS += -O0 -g -fPIC $(CC) -shared -Wl,-soname,$@ -o $@ $^ |
-O设置一共有五种:-O0、-O1、-O2、-O3和-Os。
除了-O0以外,每一个-O设置都会多启用几个选项,请查阅gcc手册的优化选项章节,以便了解每个-O等级启用了哪些选项及它们有何作用。
让我们来逐一考察各个优化等级:
-O0:这个等级(字母“O”后面跟个零)关闭所有优化选项,也是CFLAGS或CXXFLAGS中没有设置-O等级时的默认等级。这样就不会优化代码,这通常不是我们想要的。
-O1:这是最基本的优化等级。编译器会在不花费太多编译时间的同时试图生成更快更小的代码。这些优化是非常基础的,但一般这些任务肯定能顺利完成。
-O2:-O1的进阶。这是推荐的优化等级,除非你有特殊的需求。-O2会比-O1启用多一些标记。设置了-O2后,编译器会试图提高代码性能而不会增大体积和大量占用的编译时间。
-O3:这是最高最危险的优化等级。用这个选项会延长编译代码的时间,并且在使用gcc4.x的系统里不应全局启用。自从3.x版本以来gcc的行为已经有了极大地改变。在3.x,-O3生成的代码也只是比-O2快一点点而已,而gcc4.x中还未必更快。用-O3来编译所有的软件包将产生更大体积更耗内存的二进制文件,大大增加编译失败的机会或不可预知的程序行为(包括错误)。这样做将得不偿失,记住过犹不及。在gcc 4.x.中使用-O3是不推荐的。
-Os:这个等级用来优化代码尺寸。其中启用了-O2中不会增加磁盘空间占用的代码生成选项。这对于磁盘空间极其紧张或者CPU缓存较小的机器非常有用。但也可能产生些许问题,因此软件树中的大部分ebuild都过滤掉这个等级的优化。使用-Os是不推荐的。
下面是一个采用不同优化选项的lib和bin文件大小,及其执行耗时的对比。
可以看出bin文件增加不太多的情况下,执行速度得到了明显的改善。
libxxxx.ain | -O0 | -O3 |
-O0 | 5740K/542K 0.475 seconds | 5740K/552K 0.474 seconds |
-O3 | 7397K/725K 0.176 seconds | 7397K/733K 0.174 seconds |
Makefile选项CFLAGS,LDFLAGS,LIBS
CFLAGS 表示用于 C 编译器的选项,
CXXFLAGS 表示用于 C++ 编译器的选项。
这两个变量实际上涵盖了编译和汇编两个步骤。
CFLAGS: 指定头文件(.h文件)的路径,如:CFLAGS=-I/usr/include -I/path/include。同样地,安装一个包时会在安装路径下建立一个include目录,当安装过程中出现问题时,试着把以前安装的包的include目录加入到该变量中来。
LDFLAGS:gcc 等编译器会用到的一些优化参数,也可以在里面指定库文件的位置。用法:LDFLAGS=-L/usr/lib -L/path/to/your/lib。每安装一个包都几乎一定的会在安装目录里建立一个lib目录。如果明明安装了某个包,而安装另一个包时,它愣是说找不到,可以抒那个包的lib路径加入的LDFALGS中试一下。
LIBS:告诉链接器要链接哪些库文件,如LIBS = -lpthread -liconv
简单地说,LDFLAGS是告诉链接器从哪里寻找库文件,而LIBS是告诉链接器要链接哪些库文件。不过使用时链接阶段这两个参数都会加上,所以你即使将这两个的值互换,也没有问题。
有时候LDFLAGS指定-L虽然能让链接器找到库进行链接,但是运行时链接器却找不到这个库,如果要让软件运行时库文件的路径也得到扩展,那么我们需要增加这两个库给"-Wl,R":
LDFLAGS = -L/var/xxx/lib -L/opt/mysql/lib -Wl,R/var/xxx/lib -Wl,R/opt/mysql/lib
如果在执行./configure以前设置环境变量export LDFLAGS="-L/var/xxx/lib -L/opt/mysql/lib -Wl,R/var/xxx/lib -Wl,R/opt/mysql/lib" ,注意设置环境变量等号两边不可以有空格,而且要加上引号(shell的用法)。那么执行configure以后,Makefile将会设置这个选项,链接时会有这个参数,编译出来的可执行程序的库文件搜索路径就得到扩展了。
elf2flt
LDFLAGS += -Wl,-elf2flt=-s32768 |
elf[exective linked file]: 一种为Linux系统所采用的通用文件格式,支持动态链接和重定位。 flat:扁平格式。elf文件有很大的头文件,flat格式对文件头和一些段信息做了简化,可执行程序小,适于嵌入式系统。
elf2flt就是将elf格式转换为flt格式。
在编译器链接的时候一般就可以使用“-elf2flt”选项直接编译出flt格式的结果。
连接器是将所有编译器和汇编器的输出文件连接成一个二进制映象的工具。二进制映象有很多种不同格式,而Flat、AOUT、COFF、PE和ELF是最常见的几种。你是否还记得,我们在选择的连接器是LD,它有很多功能。现在存在很多不同版本的LD,它们都能将产生你需要的二进制映象格式。但无论你选用哪种格式,在输出文件中都会出现三个区域,Text(或者Code),Data和BSS区。Text(或者Code)区是只读的代码区。Data区是可读可写的数据区,举例来说,你在程序定义了一个变量并给它赋值5,那么这个“5”就被存储在Data区。而BSS区则是可读可写且没有初始化的数据区。它存储着未赋任何值的数组。注意,BSS区是一个虚拟的区域它不存在于二进制映像中,但当二进制映像被加载后,它就存在于内存中了。