Makefile 编译生成可执行文件
概述
在 Linux(unix )环境下使用GNU 的make工具能够比较容易的构建一个属于你自己的工程,整个工程的编译只需要一个make命令就可以完成编译、连接以至于最后的执行。不过这需要我们投入一些时间去完成一个或者多个称之为Makefile 文件的编写。
所要完成的Makefile 文件描述了整个工程的编译、连接等规则。其中包括:工程中的哪些源文件需要编译以及如何编译、需要创建哪些库文件以及如何创建这些库文件、如何最后产生我们想要的可执行文件。
尽管看起来可能是很复杂的事情,但是为工程编写Makefile 的好处是能够使用一行命令来完成“自动化编译”,一旦提供一个(通常对于一个工程来说会是多个)正确的 Makefile。编译整个工程你所要做的事就是在shell 提示符下输入make命令。整个工程完全自动编译,极大提高了效率。
Makefile的基本格式
<target> : <prerequisites>
[tab] <commands>
其中
target :目标文件, 可以是 Object File, 也可以是可执行文件
prerequisites :生成 target 所需要的文件或者目标
command:make需要执行的命令 (任意的shell命令), Makefile中的命令必须以 [tab] 开头
示例解析
CC = gcc
CFLAGS = -g -O -Wall -I/home/tao/curl-7.71.1/_install/include -I/home/tao/WiringPi/wiringPi
LDFLAGS = -L/home/tao/curl-7.71.1/_install/lib -L/home/tao
LIBS = -lpthread -lwiringPi -lcurl
smartHousePro:bathroomLight.o bedroomLight.o kitchenLight.o livingroomLight.o swimmingpoolLight.o \
findDeviceByName.o fire.o faceDoor.o faceRecognition.o socket.o voicer.o main.o
$(CC) $^ $(LDFLAGS) -o $@ $(LIBS)
%.o:%.c
$(CC) $(CFLAGS) -c $^
clean:
rm -f *.o
第1~4行
自定义变量 和 $( )的使用
其中前4行是自定义变量
Makefile 允许使用等号自定义变量
调用时,变量需要放在 $( ) 之中,可以查看第7和第9行
//指定使用的编译器为gcc
CC = gcc
//指定头文件(.h文件)的路径
CFLAGS = -g -O -Wall -I/home/tao/curl-7.71.1/_install/include -I/home/tao/WiringPi/wiringPi
//指定库文件的位置
LDFLAGS = -L/home/tao/curl-7.71.1/_install/lib -L/home/tao
//告诉链接器要链接哪些库文件
LIBS = -lpthread -lwiringPi -lcurl
Makefile一共提供了四个赋值运算符 (=、:=、?=、+=)
VARIABLE = value
在执行时扩展,允许递归扩展。
VARIABLE := value
在定义时扩展。
VARIABLE ?= value
只有在该变量为空时才设置值。
VARIABLE += value
将值追加到变量的尾端。
第5~7行
smartHousePro:bathroomLight.o bedroomLight.o kitchenLight.o livingroomLight.o swimmingpoolLight.o \
findDeviceByName.o fire.o faceDoor.o faceRecognition.o socket.o voicer.o main.o
$(CC) $^ $(LDFLAGS) -o $@ $(LIBS)
注意:第二行必须由【Tab】键起首,不能用空格
反斜杠转义符号
在第5行出现了 \ 符号,使用的原因是第5行的内容太长了,不便于查看所写的内容
其中的作用是将第6行的内容承接在第5行的后面
自动变量 $^ 和 $@
第5~7行
smartHousePro:bathroomLight.o bedroomLight.o kitchenLight.o livingroomLight.o swimmingpoolLight.o \
findDeviceByName.o fire.o faceDoor.o faceRecognition.o socket.o voicer.o main.o
$(CC) $^ $(LDFLAGS) -o $@ $(LIBS)
$^
$^ 指代所有前置条件,之间以空格分隔。
比如,规则为 smartHousePro:bathroomLight.o bedroomLight.o kitchenLight.o,那么 $^ 就指代 bathroomLight.o bedroomLight.o kitchenLight.o
$@
$@指代当前目标,就是Make命令当前构建的那个目标。
比如,make smartHousePro 的 $@ 就指代 smartHousePro
第8、9行
%.o:%.c
$(CC) $(CFLAGS) -c $^
匹配符%
Make命令允许对文件名,进行类似正则运算的匹配,主要用到的匹配符是%
比如,假定当前目录下有 a.c 和 b.c 两个源码文件,需要将它们编译为对应的对象文件(.o文件)
%.o: %.c
等同于
a.o: a.c
b.o: b.c
使用匹配符%,可以将大量同类型的文件,只用一条规则就完成构建。
即第8、9行的作用是将所有的.c文件生成.o文件(对象文件)
第10、11行
clean:
rm -f *.o
上面代码的目标是clean,它不是文件名,而是一个操作的名字,属于"伪目标 ",作用是删除对象文件;其中 * 为通配符,即当执行 make clean 指令时,会将所有尾缀为.o的文件删除
CFLAGS、LDFLAGS和LIBS
CFLAGS
CFLAGS: 指定头文件(.h文件)的路径。如:CFLAGS=-I/usr/include -I/path/include
同样地,安装一个包时会在安装路径下建立一个include目录,当安装过程中出现问题时,试着把以前安装的包的include目录加入到该变量中来。
选项 说明
-c 用于把源码文件编译成 .o 对象文件,不进行链接过程
-o 用于连接生成可执行文件,在其后可以指定输出文件的名称
-g 用于在生成的目标可执行文件中,添加调试信息,可以使用GDB进行调试
-Idir 用于把新目录添加到include路径上,可以使用相对和绝对路径,“-I.”、“-I./include”、“-I/opt/include”
-Wall 生成常见的所有告警信息,且停止编译,具体是哪些告警信息,请参见GCC手册,一般用这个足矣!
-w 关闭所有告警信息
-O 表示编译优化选项,其后可跟优化等级0\1\2\3,默认是0,不优化
-fPIC 用于生成位置无关的代码
-v (在标准错误)显示执行编译阶段的命令,同时显示编译器驱动程序,预处理器,编译器的版本号
LDFLAGS
LDFLAGS:gcc 等编译器会用到的一些优化参数,也可以在里面指定库文件的位置。
用法:LDFLAGS=-L/usr/lib -L/path/to/your/lib。每安装一个包都几乎一定的会在安装目录里建立一个lib目录。如果明明安装了某个包,而安装另一个包时,它愣是说找不到,可以抒那个包的lib路径加入的LDFALGS中试一下。
LIBS
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