makefile的规则
规则的三要素
本文主要介绍makefile的编写。
1 一个简单的makefile示例
创建一个makefile文件,并添加如下内容
app:main.c add.c sub.c mul.c
gcc main.c add.c sub.c mul.c -o app
当前文件夹中的问价
add.c head.h main.c makefile mul.c sub.c
这种编译方式,每次改动一个文件都要整体重新编译,效率太低。通过.o来编译打包,再次编译时,可以实现只编译改动部分。
2 根据.o来编译
app:main.o add.o sub.o mul.o
gcc main.o add.o sub.o mul.o -o app
main.o:main.c
gcc -c main.c
add.o:add.c
gcc -c add.c
mul.o:mul.c
gcc -c mul.c
sub.o:sub.c
gcc -c sub.c
这里的执行顺序是,首先执行第一行,发现.o文件不存在时,再执行如下命令先编译生成.o文件
main.o:main.c
gcc -c main.c
其它.o以此类推。到最后再执行如下这行命令
gcc main.o add.o sub.o mul.o -o app
执行make
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_makeFile# make
gcc -c add.c
gcc -c sub.c
gcc -c mul.c
gcc main.o add.o sub.o mul.o -o app
下面验证下只修改一个文件会不会全部重新编译
1 先看下所有.o文件最后修改的时间
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_makeFile# ls -l *.o
-rw-r--r-- 1 root root 1240 Jan 27 12:52 add.o
-rw-r--r-- 1 root root 2032 Jan 27 12:51 main.o
-rw-r--r-- 1 root root 1232 Jan 27 12:52 mul.o
-rw-r--r-- 1 root root 1232 Jan 27 12:52 sub.o
2 修改add.c文件,然后重新编译
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_makeFile# make
gcc -c add.c
gcc main.o add.o sub.o mul.o -o app
可以看到只是重新编译了.c文件
看生成.o文件的时间也可以验证。
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_makeFile# ls -l *.o
-rw-r--r-- 1 root root 1240 Jan 27 13:15 add.o
-rw-r--r-- 1 root root 2032 Jan 27 12:51 main.o
-rw-r--r-- 1 root root 1232 Jan 27 12:52 mul.o
-rw-r--r-- 1 root root 1232 Jan 27 12:52 sub.o
3 提取makefile中的变量
首先看下之前的makefile
app:main.o add.o sub.o mul.o
gcc main.o add.o sub.o mul.o -o app
main.o:main.c
gcc -c main.c
add.o:add.c
gcc -c add.c
mul.o:mul.c
gcc -c mul.c
sub.o:sub.c
gcc -c sub.c
再来看提取变量后的makefile
obj=main.o add.o sub.o mul.o
target=app
$(target):$(obj)
gcc $(obj) -o $(target)
%.o:%.c
gcc -c $< -o $@
下面解释下替换过程
1 首先将要编译的.o文件提取为变量
obj=main.o add.o sub.o mul.o
2 将打包的包名app提取为target变量
target=app
3 把之前的表达式用变量来表示
app:main.o add.o sub.o mul.o ---> $(target):$(obj)
gcc main.o add.o sub.o mul.o -o app ---> gcc $(obj) -o $(target)
4 %.o:%.c — 含义
main.o add.o sub.o mul.o
每次查找main.o时,将会用main.o来匹配%.o
也就是说当查找main.o时,%.o:%.c就会匹配main.o:main.c
main.o:main.c ---> %.o:%.c
5 $ < ---- $@ — $^ — 含义
makefile中的自动变量
$<: 规则中的第一个依赖
$@: 规则中的目标
$^: 规则中的所有依赖
只能在规则的命令中使用
gcc -c main.c --- gcc -c $< -o $@
6 拓展:makefile中自己维护的变量
CC == gcc
CPPFLAGS == -I
CFLAGS -Wall -g -c
LDFLAGS -L -l
把gcc替换为CC
:4,6s/gcc/$(CC)
obj=main.o add.o sub.o mul.o
target=app
$(target):$(obj)
$(CC) $(obj) -o $(target)
%.o:%.c
$(CC) -c $< -o $@
4 makefile中的函数
获取当前文件下的所有.c文件
src=$(wildcard ./*.c)
pathsubst是匹配替换函数,会把所有的.c替换成.o。
.c的来源是根据src获取的。
ojb=$(patsubst ./%.c, ./%.o, $(src))
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_makeFile# cat makefile
target=app
src=$(wildcard ./*.c)
obj=$(patsubst ./%.c, ./%.o, $(src))
$(target):$(obj)
$(CC) $(obj) -o $(target)
%.o:%.c
$(CC) -c $< -o $@
5 删除.o
每次重新编译都会提示app已经是最新的
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_makeFile# make
make: 'app' is up to date.
可以在makefile中添加如下命令,使每次编译都把对应的.o和app程序删除。
clean:
rm $(obj) $(target)
整体makefile
target=app
src=$(wildcard ./*.c)
obj=$(patsubst ./%.c, ./%.o, $(src))
$(target):$(obj)
$(CC) $(obj) -o $(target)
%.o:%.c
$(CC) -c $< -o $@
clean:
rm $(obj) $(target)
make之前先执行make clean命令。此时.o和app都被删除了。
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_makeFile# ls
add.c add.o app head.h main.c main.o makefile mul.c mul.o sub.c sub.o
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_makeFile# make clean
rm ./mul.o ./add.o ./main.o ./sub.o app
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_makeFile# ls
add.c head.h main.c makefile mul.c sub.c
删除完成后,在执行make clean,会提示文件找不到
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_makeFile# make clean
rm ./mul.o ./main.o ./add.o ./sub.o app
rm: cannot remove './mul.o': No such file or directory
rm: cannot remove './main.o': No such file or directory
rm: cannot remove './add.o': No such file or directory
rm: cannot remove './sub.o': No such file or directory
rm: cannot remove 'app': No such file or directory
makefile:9: recipe for target 'clean' failed
make: *** [clean] Error 1
添加-f,强制删除
clean:
rm $(obj) $(target) -f
此时再执行make clean 就不会提示找不到了
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_makeFile# make clean
rm ./mul.o ./main.o ./add.o ./sub.o app -f
但是如果当前文件有一个clean文件,执行make clean会遇到冲突
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_makeFile# touch clean
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_makeFile# ls
add.c clean head.h main.c makefile mul.c sub.c
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_makeFile# make clean
make: 'clean' is up to date.
此时可以加一个伪目标.PHONY:clean,就不会有此问题
.PHONY:clean
clean:
rm $(obj) $(target) -f
6 命令执行失败时继续向下执行
当一条命令执行失败时,可以添加“ - ”,使命令继续向下执行
例如在clean命令中添加一个新建文件的命令
clean:
mkdir /test
rm $(obj) $(target) -f
执行make clean后,会提示执行失败
james@iZ2ze9ftqv2b7zbety6qd8Z:/root/learn_makeFile$ make clean
mkdir /test
mkdir: cannot create directory ‘/test’: Permission denied
makefile:10: recipe for target 'clean' failed
make: *** [clean] Error 1
此时添加“ - ”,使命令执行失败后,继续向下执行
james@iZ2ze9ftqv2b7zbety6qd8Z:/root/learn_makeFile$ make clean
mkdir /test
mkdir: cannot create directory ‘/test’: Permission denied
makefile:10: recipe for target 'clean' failed
make: [clean] Error 1 (ignored)
rm ./mul.o ./main.o ./add.o ./sub.o app -f