在前一篇文章讲解了Makefile的一些概念和原理,接下来说说Makefile的一些知识点。
make与make clean
生成目标文件规则(make命令):
执行make命令则会根据当前目录的Makefile文件定义的规则生成对应的目标文件。
如果Makefile为其他名字,比如makefile.linux,则需要使用make的参数(-f or --file)执行对应的Makefile文件,例如:
代码语言:javascript复制make -f makefile.linux
清空目标文件的规则(make clean命令):
每个Makefile中都应该写一个清空目标文件( .o
和目标文件等)的规则,这不仅便于重编译,也很 利于保持文件的清洁。也许在写Makefile的时候,都要养成这样一个习惯,一般的风格都是:
clean:
rm $(obj) *.o
更为稳健的做法是(原因:如果当前目录存在clean文件,该命令会执行失败),解决办法:增加伪目标:.PHONY:clean:
代码语言:javascript复制.PHONY:clean
clean:
rm $(obj) *.o
注意:
clean
的规则不要放在文件 的开头,不然,会变成make的默认目标,一般:clean从来都是放 在文件的最后。
书写规则
显示规则(@字符):
当用@字符在命令前面时,那么执行这条命令的时候,这条命令不会显示出来。对比: 命令前加@字符例子:
代码语言:javascript复制rice@rice:~/rice_file/mkfile$ cat Makefile
exec:
@echo "rice makefile"
rice@rice:~/rice_file/mkfile$ make
rice makefile
命令前不加@字符例子:
代码语言:javascript复制rice@rice:~/rice_file/mkfile$ cat Makefile
exec:
echo "rice makefile"
rice@rice:~/rice_file/mkfile$ make
echo "rice makefile"
rice makefile
注意:
make命令参数 -s
或 --silent
或 --quiet
则是全面禁止命令的显示
命令执行规则:
当依赖目标新于目标时,make会一条一条的执行其后的命令。其中,加入要让上一条命令的结果应用在下一条命令时,应使用分号分隔这两条命令,并且不能将两条命令写在同一行。
例子1:
Makefile:
代码语言:javascript复制exec:
@cd /home/rice
@pwd
结果:
代码语言:javascript复制/home/rice/rice_file/mkfile
例子2:
Makefile:
代码语言:javascript复制exec:
@cd /home/rice;pwd
结果:
代码语言:javascript复制/home/rice
从上面两个例子可以证明命令依赖的规则。
命令出错规则(-符号):
当命令运行完,make会检测每个命令的返回码,如果返回成功,那make会执行下一条命令,当规所有的命令成功返回后,make执行完成。如果一个规则中的某个命令出错了(命令退出码 非零),那么make就会终止执行当前规则,这将有可能终止所有规则的执行。
有时命令的出错并不表示错误。例如mkdir命令,建立一个目录,如果目录不存 在,则mkdir不会出现错误。如果目录已存在,那么将产生错误。从例子说明,mkdir的出错并没有对其他命令产生影响,因为我只要目录存,所以mkdir出错不应该终止命令规则的运行。
为了解决上述问题,只需要在Makefile的命令行前加一个符号-,即使命令执行出错,也依然继续执行后续的命令。
代码语言:javascript复制.PHONY:clean
clean:
-rm $(obj) *.o
变量
变量的定义
Makefile也支持变量定义,变量的定义也让的我们的Makefile更加简化,可复用。
变量的定义:一般采用大写字母,赋值方式像C语言的赋值方式一样。
代码语言:javascript复制DIR = ./src/
变量取值:使用括号将变量括起来再加美元符。
代码语言:javascript复制FOO = $(DIR)
Makefile除了使用'='进行赋值,还有其他赋值方式,比如':='和'?=',接下来我们来对比一下这几种的区别:
赋值符'=':
代码语言:javascript复制PARA = RICE
CURPARA = $(PARA)
PARA = rice
print:
@echo $(CURPATA)
结果为:
代码语言:javascript复制rice
变量CURPARA的值并不是“RICE”。其值为PARA最后一次赋值的值。说明,赋值符“=”,可以借助另外一个变量,可以将变量的真实值推到后面去定义。也就是变量的真实值取决于它所引用的变量的最后一次有效值。
赋值符':=':
代码语言:javascript复制PARA = RICE
CURPARA := $(PARA)
PARA = rice
print:
@echo $(CURPATA)
结果为:
代码语言:javascript复制RICE
变量CURPARA的值为“RICE”。“=”和“:=”的区别就在这里,“:=”只取第一次被赋值的值。
赋值符'?=':
代码语言:javascript复制PARA = RICE
PARA ?= rice
print:
@echo $(PATA)
结果为:
代码语言:javascript复制RICE
如果上面例子第一行去掉,结果为:
代码语言:javascript复制rice
说明,如果变量PARA 前面没有被赋值,那么此变量就是“rice”,如果前面已经赋值过,那么就使用前面赋的值。
赋值符' =':
Makefile中的变量是字符串,有时候我们需要给前面已经定义好的变量添加一些字符串进去,此时就要使用到符号" =":
代码语言:javascript复制OBJ = main1.o main2.o
OBJ = main3.o
OBJ的值为:”main1.o,main2.o,main3.o“。说明“ =”用作与变量的追加。
系统自带变量:
系统自定义了一些变量,通常都是大学,比如CC,PWD,CLFAG等等,有些有默认值,有些没有,比如以下几种,如下
- CPPFLAGS:预处理器需要的选项,如:-l
- CFLAGS:编译的时候使用的参数-Wall -g -c
- LDFLAGS:链接库使用的选项-L -l
其中:默认值可以被修改,比如CC默认值是cc,但可以修改为gcc:CC=gcc
自动变量:
Makefile的语法提供一些自动变量,这些变量可以让我们更加快速的完成Makefile的编写,其中自动变量只能在规则中的命令使用,常用的自动变量如下:
- $@:规则中的目标
- $<:规则中的第一个依赖文件
- $^:规则中的所有依赖文件
CC = gcc
OBJ = main.o add.o
output: $(OBJ)
$(CC) -o $@ $^
main.o: main.c
$(CC) -c $<
add.o: add.c
$(CC) -c $<
.PHONY:clean
clean:
@rm $(OBJ) output
模式规则
模式规则实在目标及依赖中使用%来匹配对应的文件,我们依旧使用上面的例子,采用模式规则格式,如下:
代码语言:javascript复制CC = gcc
OBJ = main.o add.o
output: $(OBJ)
$(CC) -o $@ $^
%.o: %.c
$(CC) -c $<
.PHONY:clean
clean:
@rm $(OBJ) output
其中:
main.o由main.c生成
add.o 由add.c生成
函数
Makefile提供了大量的函数,其中我们经常使用的函数主要有两个(wildcard,patsubst)。
wildcard函数:用于查找指定目录下指定类型的文件,函数参数:目录 文件类型,使用方法:
代码语言:javascript复制SRC = $(wildcard ./src/*.c)
print:
@echo $(SRC)
结果:
代码语言:javascript复制./src/add.c ./src/main.c
表示:找到目录./src下所有后缀为.c的文件,并赋值给变量SRC。命令执行完,SRC变量的值:./src/add.c ./src/main.c
patsubst函数:用于匹配替换。函数参数:原模式 目标模式 文件列表,使用方法:
代码语言:javascript复制SRC = $(wildcard ./src/*.c)
OBJ = $(patsubst %.c, %.o, $(SRC))
print:
@echo $(OBJ)
结果:
代码语言:javascript复制./src/add.o ./src/main.o
表示:把变量中所有后缀为.c的文件替换为.o。命令执行完,OBJ变量的值:./src/add.o ./src/main.o