双冒号规则
双冒号规则就是使用::代替普通规则:得到的规则。当同一个文件作为多个规则的目标时,双冒号规则的处理和普通规则的处理过程完全不同(双冒号规则允许在多个规则中为同一个目标指定不同的重建目标的命令)
在Make中,一个目标可以出现在多个规则中,但这些规则必须是同一种规则,要么都是普通规则,要么都是双冒号规则,不允许同一个目标同时出现在两种不同的规则中。
冒号规则和普通规则的处理的不同:
- 双冒号规则中当依赖文件比目标更新时,规则将会被执行。对于没有依赖只有命令行的双冒号规则,当引用此目标时,规则的目标将会被无条件执行。普通规则中,当规则的目标文件存在时,此规则的命令永远不会被执行(目标文件永远是最新的)
- 当同一个文件作为多个双冒号规则的目标时,这些不同的规则会被独立地处理,而不是像普通规则那样那样合并所有依赖到一个目标文件。对这些规则的处理就像多个不同的普通规则,多个双冒号规则中的每一个依赖被改编后,make只执行此规则定义的命令,而其他的以这个文件作为目标的双冒号规则不会被执行。
例:
Newprog::foo.c$(CC) $(CFLAGS)$< -o $@
Newprog::bar.c$(CC) $(CFLAGS)$< -o $@
如果foo.c文件被修改,执行make以后目标Newprog将根据foo.c文件被重建;如果bar.c被修改,那么目标Newprog将根据bar.c被重建。如果以上为普通规则,make会报错。
当同一个目标出现在多个双冒号规则中,规则的执行顺序和普通规则的执行顺序一样。
GUN make的双冒号规则给用户提供了一种根据依赖的更新情况而执行不同的命令来重建同一目标的机制。双冒号规则使用较少,一般双冒号规则都需要定义命令,如果没有定义命令,在执行规则时,将其目标自动查找隐含规则。
自动产生依赖
现在的C编译器提供了通过查找源文件中的#include来自动产生以来的功能,GCC支持用-M来实现此功能,自动寻找源文件中包含的头文件并生成依赖关系。当不需要依赖关系中标准库文件时,使用-MM参数。
使用GCC自动产生依赖关系时,所产生的规则明确指明了目标时main.o文件。main.o的中间过程文件在使用完成后不会被删除。
在新版的make中,推荐的方式是为每一个源文件产生一个描述其依赖关系的Makefile文件。对于一个源文件NAME.c,对应的这个Makefile文件为NAME.d。NAME.d中描述了文件NAME.o所有依赖的所有头文件。采用这方式时,只有源文件再修改之后才会重新使用命令生成新的依赖关系描述文件。
可以使用如下的模式规则来自动生产每一个.c文件对应的.d文件
%.d:%.c$(CC) -M $(CPPFLAGS) $<>$@.$$$$;\sed 's,\($*\)\.o[:]*,\1.o $@:,g'<$@.$$$$>$@;\
$@.$$$$rm -f
此规则的含义是所有的.d文件依赖于同名的.c文件
第一行:使用C编译器自动生成依赖文件$<的头文件的依赖关系,并输出成为一个临时文件。$$$$表示当前进程号;$(CC)为GUN的C的C编译工具。
第二行: sed处理第二行已产生的临时文件,并生成此规则的目标文件。
第三行:删除临时文件
书写命令
在Linux系统,可能使用不同的Shell,但make的命令默认是被/bin/sh(Linux标准Shell)解释执行的
显示命令
如果规则的命令行以字符串以字符@开始,那么这个命令将不被make显示出来。
命令执行
命令可以每一行写一句。如果希望上一条结果的命令作用于下一条命令,要把多行写在一起要用分号;隔开
命令出错
-字符加在命令之前,命令执行失败,make忽略此命令执行失败
嵌套执行make
export传递变量到下级Makefile
unexport不让某些变量传递到下级Makefile
定义命令包
如果Makefile中出现一些相同命令序列,那么可以为这些相同的命令序列定一个变量。以deifne开始,以ended结束
变量的基本操作
在Makefile中定义的变量与C/C++中的宏一样,代表一个文本字符串,在Makefile中执行会自动原样展开在所使用的地方,不同点是Makefile剋有改变它的值。在Makefile中,变量可以用在目标、依赖目标、命令或是其他部分中。
变量名可以是字符、数字、下划线(可以数字开头),但不能有:#=或空格符(空格回车等),变量大小写敏感
变量的基础
变量在声明时需要给予初值,需要使用的时候要在变量名前加上$符号,最好使用小括号或大括号把变量包括起来(可以不加)。要用$字符使用$$来表示。
变量中的变量
在定义变量的值时,可以使用其他变量来构建变量的值,在Makefile中有两种方式来用变量定义变量的值。
赋值使用=操作符
?=操作符的含义是如果没有定义过,那么就把值赋给变量;如果变量被定义过,那么就什么都就不做
变量高级用法
介绍两种高级使用方法:
- 变量值的替换,可以替换变量中的共有部分,其格式是$(var:a=b)或${var:a=b},意思是把变量中所有以a字符串"结尾"(空格或结束符)的a替换成b字符串
foo:=a.o b.o c.o
bar:=$(foo:.o=.c)
另一种变量替换的技术是以静态模式定义的。例
foo:=a.o b.o c.o
bar:=$(foo:%.o=%.c)
- 把变量的值再当成变量
x=y
y=z
a:=$($(x))
追加变量值
+=操作符为变量追加值
如果变量之前没有定义过,那么+=会自动变成=;如果之前定义过,那么+=会继承于前次操作的赋值符;如果前一次是:=,那么+=会以:=作其赋值符
overrride指示符
如有变量是通过make的命令行参数设置的,那么Makefile中对这个变量的赋值会被忽略。如有想在Makefile中设置这类参数的值,可以使用override指示符。
override <变量名>=<值>
override <变量名>:=<值>
override <变量名>+=<值>
对多行变量定义可以用define指示符,overrride指示符同样可以使用
多行变量
define关键字。使用define关键字设置变量的值可以有换行。定义是以endef关键字结束。
环境变量
make运行时的系统环境变量可以在make开始运行时被载入到Makefile文件中,如果Makefile已定义了这个变量,或这个变量由make命令行带入,那么系统的环境变量值会被覆盖。(make -e参数)
如果在环境变量中设置了CFLAGS环境变量,就可以在所有的Makefile中使用这个变量。make嵌套调用,上层定义的变量会以系统环境变量传递到下层,默认情况下,只有通过命令行设置的变量才会被传递,而定义在文件中的变量要向下传递需要使用export关键字来声明。
目标变量
可以为某个目标设置局部变量,作用范围只在这条规则及其连带规则中。语法结构如下
<作用目标...>:<变量分配>
<作用目标...>:override<变量分配>
模式变量
在GUN 的make中,还支持模式变量。变量可以定义在某个目标上。模式变量的好处就是可以给定一种模式,把变量定义在符合这种模式的所有目标上。
make的模式一般都含有一个%
例:
%.o:CFLAGS=-O
#语法
<作用目标...>:<变量分配>
<作用目标...>:override<变量分配>
override同样是针对系统环境传入的变量或者make命令行指定的变量。
条件判断
根据不同情况选择不同的执行分支。条件表达式可以是比较变量的值,或比较变量和常量的值。
示例
判断变量$(CC)是否是gcc,如果是则使用GUN函数编译目标
libs_for_gcc=-lgnu
normal_libs=
foo:$(objects)
ifeq($(CC),gcc)$(CC) -o foo $(objects) $(libs_for_gcc)
else$(CC) -o foo $(objects) $(normal_libs)
endif
目标foo根据变量$(CC)的值选取不同的函数库来编译程序
关键字:ifeq\else\endif\ifnep\ifdef\ifndef
语法
条件表达式的语法
ifeq(条件1,条件2)#<条件关键字><条件为真时执行的语句>
#else#<条件为假时执行的语句>
endif
条件关键字有4种
ifeq(参数1,参数2)
ifeq'参数1','参数2'
ifeq"参数1","参数2"
ifeq"参数1",'参数2'
ifeq'参数1',"参数2"
ifneq关键字,与ifeq类似,但是相反,如果参数1和参数2的值不同才为真。
ifdef关键字
ifdef <变量名>
如果变量名的取值不为空,表达式为真;否则假。
ifndef关键字,与ifdef关键字类似,但相反。