makefile 目标

.PHONY的作用

PHONY 目标并非实际的文件名:只是在显式请求时执行命令的名字。

有两种理由需要使用PHONY 目标:

  • 避免和同名文件冲突
  • 改善性能。

如果编写一个规则,并不产生目标文件,则其命令在每次make 该目标时都执行。例如:

clean:
	rm *.o temp

因为"rm"命令并不产生"clean"文件,则每次执行"make clean"的时候,该命令都会执行。 如果目录中出现了"clean"文件,则规则失效了。 :没有依赖文件,文件"clean"始终是最新的,命令永远不会执行。 为避免这个问题,可使用".PHONY"指明该目标。如:

.PYONY: clean
clean:
	rm *.o temp

这样执行"make clean"会无视"clean"文件存在与否。

FORCE目标

当规则没有依赖关系也没有命令,而且其目标不是存在的文件名,make认为此规则运行时这个目标总是被更新。这意味着如果规则依赖于此目标,其命令总是被执行。

clean: FORCE 
     rm $(objects) 
FORCE:

例中目标’FORCE’满足这种特殊条件,这样依赖于它的目标’clean’被强制执行其命令。名字’FORCE’没有特殊含义,只不过通常这样用而已。

这种方式使用FORCE.PHONY : clean效果相同。使用.PHONY更加明确高效,担不是所有的’make’都支持;这样许多makefile中使用’FORCE’。

空目标

空目标(empty target)是phony目标的变种:用来执行显式请求的一个动作。

和phony目标不同的是:这个目标文件可以真实存在,担文件的内容无关紧要,通常是空的

空目标文件的目的是: 利用其最后修改时间来记录命令最近一次执行的时间,这是通过使用touch命令更新目标文件来达到的。

print: foo.c bar.c 
     lpr -p $? 
     touch print

利用这条规则,执行make print时如果自上次make print之后任一文件改变了,lpr命令会执行。

自动变量$?是为了只打印出那些变化了的文件。

内建的特殊目标

某些名字作为目标存在时有特殊含义。

  • .PHONY 该目标的依赖被认为是phony目标,处理这些目标时,命令无条件被执行,不管文件名是否 存在及其最后修改时间
  • .SUFFIXES 该目标的依赖被认为是一个后缀列表,在检查后缀规则时使用
  • .DEFAULT 该目标的规则被使用在没有规则(显式的或隐含的)的目标上。如果’DEFAULT’命令定 义了,则对所有不是规则目标的依赖文件都会执行该组命令
  • .PRECIOUS 该目标的依赖文件会受到特别对待:如果make被kill或命令的执行被中止,这些目标并不删除;而且如果该目标是中间文件,在不需要时不会被删除。可以将隐含规则的目标模式(如%.o) 做为.PRECIOUS的依赖文件,这样可以保存这些规则产生的中间文件。
  • .INTERMEDIATE 该目标的依赖文件被当作中间文件;如果该目标没有依赖文件,则makefile中所有的 目标文件均被认为是中间文件。
  • .IGNORE 在执行该目标的依赖规则的命令时,make会忽略错误,此规则本身的命令没有意义。如果该 规则没有依赖关系,表示忽略所有命令执行的错误,这种用法只是为了向后兼容;由于会影响到所 有的命令,所以不是特别有用,推荐使用其它更有选择性忽略错误的方法。
  • .SILENT 在执行该目标的依赖规则的命令时,make并不打印命令本身。该规则的命令没有意义。 在.SILIENT没有依赖关系时,表示执行makefile中的所有命令都不会打印,该规则只是为了向 后兼容提供的。
  • .EXPORT_ALL_VARIABLES 只是作为一个目标存在,指示make将所有变量输出到子进程中。 定义的隐含规则的后缀作为目标时,也认为它是特殊目标;两个后缀的连接也是一样,比 如.c.o。这些目标是后缀规则,一中定义隐式规则的过时方法(但仍然广泛使用)。后缀通常 以’.’开始,所以特殊目标也以’.’开始。

一个规则多个目标

一条有多个目标的规则和写多条规则,每条一个目标作用是等同的。同样的命令应用于所有目标,但其效用会因将实际目标以$@代替而不同。规则中所有目标的依赖关系是一样的。这在两种情况下有用:

  • 只有依赖关系,不需要命令。例如:kbd.o command.o files.o: command.h
  • 所有的目标同样的命令。命令不需要完全相同,因为在命令中可以使用$@
    bigoutput littleoutput : text.g 
    	generate text.g -$(subst output,,$@) > $@ 
     
    bigoutput : text.g 
    	generate text.g -big > bigoutput 
    littleoutput : text.g 
    	generate text.g -little > littleoutput 
    等同。
    

这里假设程序generate产生两种输出:一种使用-big选项,一种使用-little选项。如果想象使 用$@变化命令那样来变化依赖关系,不能通过多目标的普通规则实现,但是可以通过模式规则来实现。

一个目标多条规则

一个文件可以是多条规则的目标,所有规则的依赖关系被合并。如果目标比任一个依赖文件旧,命令被执行。

一个文件只能有一组命令执行。如果多个规则对于同一个文件都给出了命令,make使用最后一组并打印错误信息(特殊情况:如果文件名以.开始,并不打印错误信息,这一点是为了和其它make兼容)。没有任何理由需要将makefile写成这样,这是make给出错误信息的理由。

一条只有依赖关系的附加规则可以一次给出许多文件的附加依赖文件。例如’objects’变量表示系统中编译器的所有输出.,说明当’config.h’更改时所有文件必须重做的简单方法如下:

objects = foo.o bar.o 
foo.o : defs.h 
bar.o : defs.h test.h 
$(objects) : config.h

不用改变实际目标文件生成的规则,这条规则可以在需要增删附加的依赖关系时插入或提出。另一个诀窍是附加的依赖关系可以用变量表示,在make执行时,可以给变量赋值:

extradeps=$(objects) : $(extradeps)

当命令make extradeps=foo.h执行时会认为foo.h是每个目标文件的依赖文件,但简单的’make’命令不是这样。

静态模式规则

静态模式规则(static pattern rules)可以指定多个目标,并且使用目标名字来建议依赖文件的名字;比普通多目标规则更通用因为不需要依赖关系是相同的:依赖关系必须类似但不需要相同。

语法

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

说明:

  • TARGETS列表指出规则应用的目标,可以包含通配符,于普通规则的目标相同。
  • TARGET-PATTERN和DEP-PATTERNS来表明目标的依赖关系如何计算:匹配TARGET-PATTERN的目标从名字中抽出一部分,叫做词干(stem),词干被替换到DEP-PATTERNS来形成依赖文件名。

每个模式通常包含一个%字符。当TARGET-PATTERN匹配一个目标时,%字符可以匹配目标名中的任何部分;这部分即是词干,模式的其余部分必须完全匹配。 例如: foo.o匹配%.o,’foo’是词干;目 标 ’foo.c’ 和’foo.out’并不匹配这个模式。

目标的依赖文件名通过将DEP-PATTERNS中的%替换为词干形成:如果依赖模式为%.c,在替换词干’foo’可以得到’foo.c’。依赖模式中不包含’%’也是合法的,此依赖文件对所有的目标均有效。

如果需要在模式规则中使用’%’字符,必须在其前面加’’字符,如果’%’前的’’字符是有实际意义的,必须在其前面加’’,其它的’’不必如此处理。如the\%weird\%pattern在有效的’%’前是the%weird,其后是’pattern’。最后的’’保持原样是因为其并不影响’%’字符。

以下例子从相应的’.c’文件编译’foo.o’和’bar.o’:

objects = foo.o bar.o 
$(objects): %.o: %.c 
     $(CC) -c $(CFLAGS) $< -o $@

每个目标必须匹配目标模式,对于不匹配的目标会给出警告。如果列表中只有部分文件匹配模式,可以使用filter函数移去不匹配的文件名:

files = foo.elc bar.o lose.o 
$(filter %.o,$(files)): %.o: %.c 
     $(CC) -c $(CFLAGS) $< -o $@ 
$(filter %.elc,$(files)): %.elc: %.el 
     emacs -f batch-byte-compile $<

例子中$(filter %.o,$(files)) 结果是bar.o lose.o

$(filter %.elc,$(files)) 的结果是foo.elc

以下例子说明$*的使用:

bigoutput littleoutput : %output : text.g 
     generate text.g -$* > $@

命令generate执行时,$*扩展为词干’big’或’little’。

静态模式规则和隐式规则

静态模式规则和隐式规则在作为模式规则是具有很多共同点,都有目标模式和构造依赖文件名的模式,不同之处在于make决定何时应用规则的方法。

隐式规则可应用于匹配其模式的任何目标,但只限于没有指定命令的目标,如果有多条可应用的隐式规则,只有一条被使用,取决于规则的顺序。反之,静态模式规则适用于规则中明确目标列表,不适用于其它目标且总是适用于指定的每个目标。如果有两条冲突的规则,且都有命令,这是一个错误。

静态模式规则比隐式规则优越之处如下:

  • 可为一些不能按句法分类,但可以显式列出的文件重载隐式规则
  • 不能判定目录中的精确内容,一些无关的文件可能导致make适用错误的隐式规则;最终结果可能依赖 于隐式规则的次序。适用静态模式规则时,这种不确定性是不存在的:规则适用于明确指定的目标。

双冒号规则

双冒号规则(Double-colon rules)的目标后是::而不是:,当一个目标出现在多条规则中时,其处理和普通规则的处理不同。

当一个目标出现在多条规则中时,所有规则必须是相同类型的:都是普通的或者都是双冒号的。

  • 如果是双冒号,规则之间相互独立;
  • 如果目标需要更新,则规则的命令被执行;结果可能是没有执行,或者执行了其中一些,或者所有的规则都执行了。

同一目标的双冒号规则事实是完全孤立的,每条规则被被单独处理,就像不同目标的规则一样;规则按照在makefile中出现的次序被处理,此类规则真正有意义的是那些于命令执行次序无关的。

这种规则有时比较晦涩不是特别有用;它提供了一种机制:通过不同依赖文件的更新来对目标进行不同的处理,这种情形很罕见。每个这种规则应当提供命令,如果没有,适用的隐式规则将使用。

自动生成依赖关系

在makefile中,许多规则都是一些目标文件依赖于一些头文件。例如:main.c 通过#include使用defs.h,这样规则:

main.o: defs.h 

告诉make在defs.h变化时更新main.o。在程序比较大时,需要写许多这样的规则;而且当每次增删#include时,必须小心的更新makefile。许多现代的编译器可以帮你写这些规则,通常这是通过编译器的-M选项,例如命令:

cc –M main.c
输出以下内容: 
main.o : main.c defs.h

这样就不必写这些规则,有编译器代劳了。 注意这样的依赖关系中提及main.o,不会被隐式规则认为是中间文件,这意味这make在使用过它之后不会将其删除。使用老的’make’程序时,习惯做法是使用make depend命令利用编译器的功能产生依赖关系,该命令会产生一个depend文件包含所有自动产生的依赖关系,然后在makefile中使用include将其读入。

使用GNU的make时,重新生成makefile的功能使得这种做法变得过时:从不需要显式请求更新依赖关系,因为它总是重新生成任何过时的makefile。

自动依赖关系生成推荐的做法是对每个源文件做一个makefile。对每个源文件NAME.c,有一个makefile NAME.d,其中列出了目标文件NAME.o依赖的所有文件,这样在源文件更新时,需要扫描来产生新的依赖关系。例子是一个从NAME.c产生依赖关系文件NAME.d的模式规则:

%.d: %.c 
     $(SHELL) -ec '$(CC) -M $(CPPFLAGS) $< | sed '''s/($*).o[ :]*/1 $@/g''' > $@'

-e选项是当$(CC)命令失败时(exit状态非0),shell立刻退出。通常shell的返回值是管道中最后一条命令(sed)的返回值,这样make不会注意到编译器出错。

使用GNU的C编译器时(gcc),可以用 -MM选项来代替-M选项,这样省略系统头文件的依赖关系。sed命令的目的是将

main.o : main.c defs.h 
转换为 
main.o main.d : main.c defs.h

这样使得每个’.d’文件依赖于’.o’文件相应源文件和头文件,make则可以在原文间或头文件变化时更新依赖关系文件。如果定义了生成’.d’文件的规则,可以使用include指令来读入所有的文件:

sources = foo.c bar.c 
include $(sources:.c=.d)

例中使用替换变量来将源文件列表foo.c bar.c转换为依赖关系文件的列表。因为’.d’文件和其它文件一样,不需要更多工作,make会在需要时重新生成它们。