这条规则会告诉 make —— 如果希望编译目标 all —— 首先要确保 hello、goodbye 和 fibonacci 都是最新的。然后,就什么也不做了。下面并没有提供指令。在这条规则完成之后,并不会创建名为 all 的文件。这个目标是一种假目标。在某些 make 变种中使用的技术术语称之为 “伪目标”。
伪目标是为了组织结构的目的而设计的,这在编写一个清晰的 makefile 时是种非常不错的技术。举例来说,我们可能会经常看到下面的规则:
build: clean all install |
这指定了编译过程执行的操作顺序。
系统还定义了几个特殊的目标,它们对 make 可以产生一些特别的影响,提供一种可配置的机制。具体的目标集对于每个实现来说都是不同的;其中最通用的一个是 .SUFFIXES 目标,它使用的源是一系列模式,添加在可识别的文件后缀列表中。这些特殊目标并不会用作通用规则来把编译作为 makefile 中默认的第一条目标。
有些版本的 make 允许将特殊源与给定目标的依赖性一起指定,例如 .IGNORE,它说明从编译这个目标所使用的命令中生成的错误都应该忽略,仿佛它们前面都有一个短线一样。这些标记的可移植性并不好,但是对于理解 makefile 来说却是必须的。
在 make 中有一些隐式规则用来根据文件名后缀执行通用转换。举例来说,如果现在没有 makefile,可以创建一个名为 “hello.c” 的文件,并运行 make hello 命令:
$ make hello
cc -O2 -o hello hello.c
|
大型程序使用的 makefile 可能会简单地指定自己需要的对象模块清单(hello.o、world.o 等),然后为怎么样将 .c 文件转换成 .o 文件提供一条规则:
.c.o: cc $(CFLAGS) -c $< |
实际上,大部分 make 工具都有一个早已内嵌到系统中的与此类似的规则;如果请求 make 来编译 file.o,而且现在已经有 file.c 文件了,那么它就可以正确地完成编译过程。术语 "$<" 是一个特殊的预定义的 make 变量,代表某条规则的 “源”。这使我们可以使用一些 make 变量。
通用规则取决于 “后缀” 的声明,它然后会被识别为文件扩展名,而不是文件名的一部分。
make 程序使用了一些变量来简化通用值的重用。最常见的值可能是 CFLAGS。有关 make 变量有一些东西应该澄清一下。它们不一定必须是环境变量。如果所给出的名字没有对应的 make 变量,那么 make 就会去检查环境变量;然而,这并意味着 make 变量会被导出为环境变量。优先规则非常神秘;通常,它们的顺序从高到低依次为:
因此,一个变量只有在没有在任何 makefile 或命令行中指定时,才会使用环境变量的设置(注意:父进程 makefile 变量有时候会传递下来,但不总会这样。正如可能已经猜测到的一样,这些规则在各个 make 工具中会有所不同)。
人们在使用 make 时常常碰到的一个问题是变量被变量名的一部分替换掉了:举例来说,$CFLAGS 就被替换成了 “FLAGS”。因此要引用一个 make 变量,就请将它的名字放到括号中:$(CFLAGS)。否则,所得到的将是 $C,后面加上一个 FLAGS。
很多变量都有一些特殊的意义,这是正在使用它们的规则的一种功能。最常见的用法有:
$< —— 用来构建目标所使用的源文件$* —— 目标名中基本的部分(不包含扩展名或目录)$@ —— 目标的完整名虽然 Berkeley make 没有使用这些变量,但是它们(到现在)都是可移植的。至少,是部分可移植的;其确切定义在不同的 make 实现中可能会有所不同。使用这些变量编写的任何复杂规则都可能到某个特定的实现就不能用了。
有时候可能还需要执行一些 make 中没法移植的内容。由于 make 是通过 shell 来运行所有操作的,因此常见的解决方案是编写一个内嵌的 shell 脚本来实现。下面是怎么样实现的过程。
首先,要知道 shell 脚本传统上来讲是在多行中编写的,它们可以使用分号来分割语句,从而将整个脚本压缩成一行。其次,要注意这样做可读性不好。解决方案是一种折衷:使用常见的缩进格式来编写脚本,但是在每行后面都加上一个 “;\” 符号。这在语法上使用分号结束了一个 shell 命令,但却会把一个 make 命令的文本部分一次传递给 shell。举例来说,下面的代码就可能会在某个最上层的 makefile 中出现:
all: for i in $(ALLDIRS) ; \ do ( cd $$i ; $(MAKE) all ) ; \ done |
其中给出了需要注意的 3 件事情。首先是分号和反斜线的用法。其次是 make 变量的用法