1、LDFLAGS & LIBS
关于makefile的依赖规则以及目标生成的步骤,这篇先不介绍,这里主要关注我在看makefile时遇到的一堆CFLAGS与LDFLAGS定义进行说明。
费了牛劲,才查到GNUmake中文手册中关于CFLAGS与LDFLAGS的说明,他们都是是隐含规则的变量,且是一种命令参数变量;然后又到GCC手册中查找有哪些编译选项。
makefile内嵌隐含规则的命令中,所使用的变量都是预定义的变量。我们将这些变量称为“隐含变量”。
这些变量允许对它进行修改:在Makefile中、通过命令行参数或者设置系统环境变量的方式来对它进行重定义。
无论是用那种方式,只要make在运行时它的定义有效,make的隐含规则都会使用这些变量。
1.1 先瞅一眼
LDFLAGS是选项,LIBS是要链接的库。
结果都传给ld,前者告诉到哪里找?后者是找什么?
LDFLAGS = -L/var/xxx/lib -L/opt/mysql/lib LIBS = -lmysqlclient -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
PS:-Wl,R在GraphicsMagick环境下,用为-R, 也就是LDFLAGS = -L/var/xxx/lib -R/var/xxx/lib
1.2 详细看看
GCC编译选项CFLAGS参数
GCC链接选项LDFLAGS参数
CFLAGS = -g -Wall -I./include -I./include/tinyalsa -Wl,--whole-archive -lpthread -Wl,--no-whole-archive -lc LDFLAGS = -L./lib ALL: $(CC) $(CFLAGS) $(LDFLAGS) main.c gfifo.c queue.c usbmonitor.c socket_rcv_360_server.c ./lib/srs_librtmp.a ./lib/libcrypto.a ./lib/libssl.a ./lib/libtinyalsa.a -o media_record -static -ldl -lstdc++ -lm -lpthread clean: rm media_record *.raw *.mp4 *.wav -rf CFLAGS = -g -Wall -I./include -I./include/tinyalsa -Wl,--whole-archive -lpthread -Wl,--no-whole-archive -lc -g:生成调试信息 -Wall:输出全部的告警信息 -I./include:添加头文件搜索目录./include -I./include/tinyalsa:添加头文件搜索目录./include/tinyalsa -Wl,--whole-archive -lpthread:将libpthread.so及以后的库中的所有符号都链接进可执行目标文件 -Wl,--no-whole-archive:关闭之前的-Wl,--whole-archive -lpthread 选项 LDFLAGS = -L./lib,将makefile当前目录下的、lib文件夹添加到库文件搜索目录 $(CC):makefile隐含变量,默认选择gcc编译器 $(CFLAGS):引用之前定义的CFLAGS变量,即编译选项参数 $(LDFLAGS):引用之前定义的LDFLAGS变量,即链接参数 $(CC) $(CFLAGS) $(LDFLAGS) main.c gfifo.c queue.c usbmonitor.c socket_rcv_360_server.c ./lib/srs_librtmp.a ./lib/libcrypto.a ./lib/libssl.a ./lib/libtinyalsa.a -o media_record -static -ldl -lstdc++ -lm -lpthread 使用定的编译器、编译选项参数、链接选项参数,编译.c文件,并使用静态方式链接制定的库文件,以及编译器目录下的libdl.a、libstdc++.a、libm.a、libpthread.a库文件生成 media_record 可执行目标文件。
重复
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
CFLAGS 标志参数实例说明
CFLAGS = -g -O2 -Wall -Werror -Wno-unused
编译出现警告性错误unused-but-set-variable,变量定义但没有使用,解决方法:
增加 CFLAGS 或CPPFLAGS参数如下:
CPPFLAGS=" -Werror -Wno-unused-but-set-variable" || exit 1
“-I dir”
正如上表中所述,“-I dir”选项可以在头文件的搜索路径列表中添加dir目录。
由于Linux中头文件都默认放到了“/usr/include/”目录下,因此,当用户希望添加放置在其他位置的头文件时,就可以通过“-I dir”选项来指定,这样,Gcc就会到相应的位置查找对应的目录。
比如在“/root/workplace/Gcc”下有两个文件:
#include int main() { printf(“Hello!!\n”); return 0; } #include
这样,就可在Gcc命令行中加入“-I”选项:
[root@localhost Gcc] Gcc hello1.c –I /root/workplace/Gcc/ -o hello1
这样,Gcc就能够执行出正确结果。
在include语句中,“<>”表示在标准路径中搜索头文件,““””表示在本目录中搜索。故在上例中,可把hello1.c的“#include”改为“#include “my.h””,就不需要加上“-I”选项了。
“-L dir”
选项“-L dir”的功能与“-I dir”类似,能够在库文件的搜索路径列表中添加dir目录。例如有程序hello_sq.c需要用到目录“/root/workplace/Gcc/lib”下的一个动态库libsunq.so,则只需键入如下命令即可:
[root@localhost Gcc] Gcc hello_sq.c –L /root/workplace/Gcc/lib –lsunq –o hello_sq
需要注意的是,“-I dir”和“-L dir”都只是指定了路径,而没有指定文件,因此不能在路径中包含文件名。
另外值得详细解释一下的是“-l”选项,它指示Gcc去连接库文件libsunq.so。由于在Linux下的库文件命名时有一个规定:必须以lib三个字母开头。因此在用-l选项指定链接的库文件名时可以省去lib三个字母。也就是说Gcc在对”-lsunq”进行处理时,会自动去链接名为libsunq.so的文件。
(2)告警和出错选项
下面结合实例对这几个告警和出错选项进行简单的讲解。
如有以下程序段:
#include void main() { long long tmp = 1; printf(“This is a bad code!\n”); return 0; }
这是一个很糟糕的程序,读者可以考虑一下有哪些问题?
· “-ansi”
该选项强制Gcc生成标准语法所要求的告警信息,尽管这还并不能保证所有没有警告的程序都是符合ANSI C标准的。运行结果如下所示:
[root@localhost Gcc]# Gcc –ansi warning.c –o warning
warning.c: 在函数“main”中: warning.c:7 警告:在无返回值的函数中,“return”带返回值 warning.c:4 警告:“main”的返回类型不是“int”
可以看出,该选项并没有发现”long long”这个无效数据类型的错误。
· “-pedantic”
允许发出ANSI C标准所列的全部警告信息,同样也保证所有没有警告的程序都是符合ANSI C标准的。其运行结果如下所示:
[root@localhost Gcc]# Gcc –pedantic warning.c –o warning
warning.c: 在函数“main”中: warning.c:5 警告:ISO C90不支持“long long” warning.c:7 警告:在无返回值的函数中,“return”带返回值 warning.c:4 警告:“main”的返回类型不是“int”
可以看出,使用该选项查看出了”long long”这个无效数据类型的错误。
· “-Wall”
允许发出Gcc能够提供的所有有用的报警信息。该选项的运行结果如下所示:
[root@localhost Gcc]# Gcc –Wall warning.c –o warning
warning.c:4 警告:“main”的返回类型不是“int” warning.c: 在函数”main”中: warning.c:7 警告:在无返回值的函数中,”return”带返回值 warning.c:5 警告:未使用的变量“tmp”
使用“-Wall”选项找出了未使用的变量tmp,但它并没有找出无效数据类型的错误。
另外,Gcc还可以利用选项对单独的常见错误分别指定警告,有关具体选项的含义感兴趣的读者可以查看Gcc手册进行学习。
(3)优化选项
Gcc可以对代码进行优化,它通过编译选项“-On”来控制优化代码的生成,其中n是一个代表优化级别的整数。对于不同版本的Gcc来讲,n的取值范围及其对应的优化效果可能并不完全相同,比较典型的范围是从0变化到2或3。
不同的优化级别对应不同的优化处理工作:
- 如使用优化选项“-O”主要进行线程跳转(Thread Jump)和延迟退栈(Deferred Stack Pops)两种优化。
- 使用优化选项“-O2”除了完成所有“-O1”级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令调度等。
- 选项“-O3”则还包括循环展开和其他一些与处理器特性相关的优化工作。
虽然优化选项可以加速代码的运行速度,但对于调试而言将是一个很大的挑战。
因为代码在经过优化之后,原先在源程序中声明和使用的变量很可能不再使用,控制流也可能会突然跳转到意外的地方,循环语句也有可能因为循环展开而变得到处都有,所有这些对调试来讲都将是一场噩梦。所以笔者建议在调试的时候最好不使用任何优化选项,只有当程序在最终发行的时候才考虑对其进行优化。
(4)体系结构相关选项