7 深入学习规则的目标与依赖

185 阅读5分钟

本次将介绍强制目标、多规则目标、多目标规则、静态模式、双冒号规则的使用。

知识点

  • 强制目标的使用
  • 多规则目标的使用
  • 多目标规则的使用
  • 静态模式的使用
  • 双冒号规则的使用

本章实验涉及到的代码文件位于 /home/project/make_example-master/chapter6 目录下,请在 Terminal 中通过 cd 命令切换至该目录后再进行实验学习。

chapter6 文件夹结构如下:

├── force #强制目标的使用测试
│   ├── makefile
│   └── normal.mk
├── multi #多规则目标和多目标规则的使用测试
│   ├── aim.mk
│   └── makefile
├── rule #双冒号规则使用测试
│   ├── err.mk
│   └── makefile
└── static #静态规则使用测试
    └── makefile

强制目标的使用

为了验证强制目标的功能,我们先使用一个普通目标测试其被 make 执行的状况。 测试使用到的源代码的 force/normal.mk 内容如下:

#this is a makefor for foce aim test

aim:dep_a
    date >> aim

dep_a:
    touch dep_a

最终目标 aim 依赖于 dep_a 文件,dep_a 文件会在执行时被 touch 指令更新时间戳。

进入 force 目录并连续执行两次此 makefile 文件:

cd force;make -f normal.mk;make -f normal.mk

Terminal 的输出结果如下图:

图片.png

可见终极目标 aim 只会重建一次,之后因为依赖文件没有更新,aim 目标也不会再被重建。


如果希望最终目标每次都能够被重建,

  • 一种方法是将其声明为伪目标
  • 另一种方法是使用强制目标。 强制目标是指没有依赖或者命令行的目标,此目标每次被执行时都会被更新到,因此依赖于此目标的目标也会被强制更新。

源代码的 force/makefile 中有一个强制目标的示例,内容如下:

#this is a makefor for foce aim test

aim:FORCE
    date >> aim

FORCE:

从内容中可以看出 FORCE 是一个强制目标,同时搜索目录下不存在同名的 FORCE 文件,因此 FORCE 目标每次被执行时都会无条件更新,导致 aim 也会被强制更新。

现在直接执行三次 make,查看 aim 文件内容与 Terminal 的输出结果。

make;make;make;cat aim

Terminal 的输出结果如下图:

图片.png

从输出结果中可以看出 aim 目标被连续重建三次,aim 文件中第一个时间是使用 normal.mk 时加入的,后面三次时间是使用 makefile 加入的。


多规则目标的使用

多规则目标是指一个目标可以对应到多个规则\color{red}{一个目标可以对应到多个规则},每条规则有不同的依赖。 make 对多规则目标的处理方式如下:

  1. 合并依赖文件列表
  2. 不断重写目标的重建规则,由新的规则直接覆盖旧的规则。

通过提供的 multi/makefile 文件,可以对多规则目标进行测试,文件内容如下:

#this is a makefile for multi-command test

.PHONY: multi prepare

multi:dep_a dep_b
    @echo $^ "[cmd1]"

multi:dep_c dep_d
    @echo $^ "[cmd2]"

multi:prepare
    @echo $^ "[cmd3]"

multi:
    @echo $^ "[cmd4]"

prepare:
    @touch dep_a dep_b dep_c dep_d

从文件内容上可以看到 multi 目标对应了四条规则,依赖文件,执行的指令都不一样。先观察 make 会怎样处理这个文件,切换目录并执行 make 命令:

cd ../multi;make

Terminal 的输出结果如下:

makefile:9: warning: overriding commands for target `multi'
makefile:6: warning: ignoring old commands for target `multi'
makefile:12: warning: overriding commands for target `multi'
makefile:9: warning: ignoring old commands for target `multi'
makefile:15: warning: overriding commands for target `multi'
makefile:12: warning: ignoring old commands for target `multi'
prepare dep_a dep_b dep_c dep_d [cmd4]

可以看见 multi 规则指令一直在被重写,并忽略之前的指令。

最后使用cmd4 来重建 multi 目标,其它指令都没有得到执行。最后依赖文件列表值得思考。

注意:使用 make 4.1 和 3.8 产生的依赖项摆放顺序不一样,感兴趣的同学可以自己测试。 make 4.1 的排列是按照 makefile 规则的顺序从后到前,从左到右。而 make3.81 则是最后的依赖项在前,其它依赖项从前往后合并。


多目标规则的使用

makefile的规则书写格式允许一个规则对应多个目标\color{blue}{makefile 的规则书写格式允许一个规则对应多个目标} 规则的依赖关系和命令对所有目标有效。\color{red}{规则的依赖关系和命令对所有目标有效。}

我们使用 aim.mk 来测试多目标规则,aim.mk 内容如下:

#this is a makefile for multi-aim test

.PHONY:clean

file_1 file_2 file_3:depen_1 depen_2
    @echo "this is a multi-aim rule for " $@
    touch file_1 file_2 file_3

depen_1:
    touch depen_1

depen_2:
    touch depen_2

clean:
    $(RM) depen_* file_*

从内容上可以看到 file_1file_2file_3 共用一条规则,并共同依赖于 depen_1depen_2 两个文件。规则中可以使用自动化变量$@ 来区分具体执行哪一条规则。

现在分别执行三条规则并观察输出结果。

cd ../multi/
make -f aim.mk file_1;make -f aim.mk file_2;make -f aim.mk file_3

Terminal 的输出结果如下图:

图片.png

说明处理符合预期,$@ 能够识别到当前的执行目标。另一点需要说明的地方是多目标规则同样能支持最终目标,而多目标中的第一个目标就是终极目标\color{red}{多目标中的第一个目标就是终极目标}。执行 make 验证一下:

make -f aim.mk clean;make -f aim.mk

Terminal 的输出结果如下图:

图片.png

多目标中的第一个目标就是终极目标\color{blue}{多目标中的第一个目标就是终极目标} ,这里是file_1 file_2 file_3:depen_1 depen_2 file1


静态模式的使用

多目标规则可以利用自动化变量来区分不同目标并做出相应的处理,但所有目标的依赖文件必须相同。如果某种情况下我们希望多目标规则中,不同目标能对应不同的依赖文件,可以使用静态模式规则来实现。

静态模式规则的基本语法如下:

TARGETS...:TARGET-PATTERN:PREREQ-PATTERNS...
COMMANDS
...

TARGETS 表示目标文件列表,RARGET-PATTERN 为目标模式,它提取出与 TARGETS 中相匹配的部分作为「茎」,替换 PREREQ-PATTERNS 中相应的部分来产生依赖文件。这样不同的目标就可以通过模式匹配来依赖不同的文件。

chapter6/static/ 目录下的 makefile 文件用于验证静态模式规则,文件内容如下:

#this is a makefile for static mode

.PHONY:clean aim_1 aim_2 aim_3

aim_1 aim_2 aim_3:aim_%:depen_% # 这行将aim_%(1,2,3)的依赖对象产生为depen_%这个变量
    @echo "target:"$@ " depen:" $^

depen_%:
    touch $@

clean:
    $(RM) aim_* depen*

关于# Makefile中的@,@, ^, <,< , ?, %, +, $*

aim_1aim_2aim_3 是规则目标,根据静态模式的语法可以看出他们分别依赖于depen_1depen_2depen_3 文件。这三个依赖文件也使用了模式匹配规则,通过 touch 命令生成或更新自己。分别执行三个目标,命令如下:

cd ../static/
make aim_1;make aim_2;make aim_3

Terminal 的输出结果如下图:

图片.png

可见它们分别依赖于各自特有的依赖文件。


双冒号规则的使用

双冒号规则使用“::”代替普通规则中的“:”。当一个文件作为多个双冒号规则的目标时,这些不用的规则会被独立处理,分别执行,而不是像普通规则一样被新的处理命令覆盖。

chapter6/rule/ 目录下的 makefile 文件可以用来测试双冒号规则,内容如下:

#this is a test makefile

.PHONY:clean

aim::depen_a
    @echo $@ " : " $^ " [cmd1]"

aim::depen_b depen_c
    @echo $@ " : " $^ " [cmd2]"

aim::depen_d
    @echo $@ " : " $^ " [cmd3]"

aim::
    @echo $@ " : " $^ " [cmd4]"

depen_%:
    touch $@

clean:
    $(RM) depen_*

从文件内容上来看 aim 是多个双冒号规则的目标,这些规则的依赖和命令都不一样。现在来测试 aim 的重建过程,执行 make 命令:

cd ../rule/
make

Terminal 的输出结果如下图:

可见四个规则都被执行到,并且每一条规则都是独立执行,依赖项并不像普通多规则目标那样发生合并。


一个目标可以出现在多个规则中,但这些规则类型必须相同\color{red}{规则类型必须相同}要么都是普通规则,要么都是双冒号规则。

err.mk 演示了普通规则和双冒号规则混合使用的情况,它将 makefile 中的第四条双冒号规则修改为普通规则,内容如下:

#this is a test makefile

.PHONY:clean

aim::depen_a
	@echo $@ " : " $^ " [cmd1]"

aim::depen_b depen_c
	@echo $@ " : " $^ " [cmd2]"

aim::depen_d
	@echo $@ " : " $^ " [cmd3]"

#这块改成普通模式:
aim:
	@echo $@ " : " $^ " [cmd4]"

depen_%:
	touch $@

clean:
	$(RM) depen_*

执行下面的命令并观察输出结果。

make -f err.mk

Terminal 的输出结果如下图:

从结果上可以知道,make 确实是不允许不同类型的规则混合使用.


本章节测试了强制目标、多规则目标、多目标规则、静态模式、双冒号规则的使用方法。