1.有一些设定好的内部变量,它们根据每一个规则内容定义。三个 比较有用的变量是$@, $< 和 $^ (这些变量不需要括号括住)。 $@ 扩展成当前规则的目的文件名, $< 扩展成依靠列表中的第 一个依靠文件,而 $^ 扩展成整个依靠的列表(除掉了里面所有重 复的文件名)。利用这些变量,我们可以把上面的 makefile 写成:
=== makefile 开始 ===
OBJS = foo.o bar.o
CC = gcc
CFLAGS = -Wall -O -g
myprog : $(OBJS)
$(CC) $^ -o $@
foo.o : foo.c foo.h bar.h
$(CC) $(CFLAGS) -c $< -o $@
bar.o : bar.c bar.h
$(CC) $(CFLAGS) -c $< -o $@
2.在 UNIX 系统中,习惯使用 Makefile 作为 makfile 文件。如果要使用其他文件作为 makefile,则可利用类似下面的 make 命令选项指定 makefile 文件:
$ make -f Makefile.debug
3.而GNU make 除了支持后缀规则外还支持另一种类型的隐含规则--模式规则。这种规则更加通用,因为可以利用模式规则定义更加复杂的依赖性规则。模式规则看起来非常类似于正则规则,但在目标名称的前面多了一个 % 号,同时可用来定义目标和依赖文件之间的关系,例如下面的模式规则定义了如何将任意一个 file.c 文件转换为 file.o 文件:
%.c:%.o
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
4.假象目的 (Phony Targets)
假设你的一个项目最后需要产生两个可执行文件。你的主要目标 是产生两个可执行文件,但这两个文件是相互独立的——如果一 个文件需要重建,并不影响另一个。你可以使用“假象目的”来达到这种效果。一个假象目的跟一个正常的目的几乎是一样的, 只是这个目的文件是不存在的。因此, make 总是会假设它需要 被生成,当把它的依赖文件更新后,就会执行它的规则里的命令行。
如果在我们的 makefile 开始处输入:
all : exec1 exec2
其中 exec1 和 exec2 是我们做为目的的两个可执行文件。 make 把这个 'all' 做为它的主要目的,每次执行时都会尝试把 'all' 更新。但既然这行规则里没有哪个命令来作用在一个叫 'all' 的实际文件(事实上 all 并不会在磁碟上实际产生),所以这个规 则并不真的改变 'all' 的状态。可既然这个文件并不存在,所以 make 会尝试更新 all 规则,因此就检查它的依靠 exec1, exec2 是否需要更新,如果需要,就把它们更新,从而达到我们的目的。
假象目的也可以用来描述一组非预设的动作。例如,你想把所有由 make 产生的文件删除,你可以在 makefile 里设立这样一个规则:
veryclean :
rm *.o
rm myprog
前提是没有其它的规则依靠这个 'veryclean' 目的,它将永远不会被执行。但是,如果你明确的使用命令 'make veryclean' , make 会把这个目的做为它的主要目标,执行那些 rm 命令。
如果你的磁碟上存在一个叫 veryclean 文件,会发生什么事?这时因为在这个规则里没有任何依靠文件,所以这个目的文件一定是最新的了(所有的依靠文件都已经是最新的了),所以既使用户明确命令 make 重新产生它,也不会有任何事情发生。解决方法是标明所有的假象目的(用.PHONY),这就告诉 make 不用检查它们 是否存在于磁碟上,也不用查找任何隐含规则,直接假设指定的目 的需要被更新。在 makefile 里加入下面这行包含上面规则的规则:
.PHONY : veryclean
就可以了。注意,这是一个特殊的 make 规则,make 知道 .PHONY 是一个特殊目的,当然你可以在它的依靠里加入你想用的任何假象 目的,而 make 知道它们都是假象目的。
5.函数 (Functions)
makefile 里的函数跟它的变量很相似——使用的时候,你用一个 $ 符号跟开括号,函数名,空格后跟一列由逗号分隔的参数,最后 用关括号结束。例如,在 GNU Make 里有一个叫 'wildcard' 的函 数,它有一个参数,功能是展开成一列所有符合由其参数描述的文 件名,文件间以空格间隔。你可以像下面所示使用这个命令:
SOURCES = $(wildcard *.c)
这行会产生一个所有以 '.c' 结尾的文件的列表,然后存入变量 SOURCES 里。当然你不需要一定要把结果存入一个变量。
另一个有用的函数是 patsubst ( patten substitude, 匹配替 换的缩写)函数。它需要3个参数——第一个是一个需要匹配的 式样,第二个表示用什么来替换它,第三个是一个需要被处理的 由空格分隔的字列。例如,处理那个经过上面定义后的变量,
OBJS = $(patsubst %.c,%.o,$(SOURCES))
这行将处理所有在 SOURCES 字列中的字(一列文件名),如果它的 结尾是 '.c' ,就用 '.o' 把 '.c' 取代。注意这里的 % 符号将匹 配一个或多个字符,而它每次所匹配的字串叫做一个‘柄’(stem) 。 在第二个参数里, % 被解读成用第一参数所匹配的那个柄。
6.将长行用\分开便于阅读,这和使用一个长行的作用是一样的
edit : main.o kbd.o command.o display.o \
insert.o serach.o files.o utils.o
7简化命令
为每个文件写出编译命令不是必要的,因为make可以自己来做;以’.c’文件更新’.o’文件有一个隐含的规则,使用’cc -c’命令。Make将利用’cc –c main.c –o main.o’来将main.c编译为main.o,因此在生成目标文件的规则中,可以省略命令。
当’.c’文件以这样的方式使用时,将自动加入到依赖关系中;由是在省略命令的前提下,可以将’.c’文件从依赖关系中省略。以下是简化过的makefile:
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
.PHONY : clean
clean :
-rm edit $(objects)
8.包含
'include'指令告诉make暂停处理余下的内容,读入其它makefile。语法如下:
include FILENAMES …
这一行起始可以有空格,但TAB字符不允许。如果文件名包含变量或函数,这些将被扩展
9wildcard函数
通配符自动在规则中进行。但是在变量赋值的和函数的参数中通配符不会扩展,如果在这些情况下需要通配符扩展,必须使用'wildcard'函数。语法如下:
$(wildcard PATTERN...)
这个在makefile任何地方出现的字符串,会被匹配任何一个文件名格式的以空格隔开的现有文件列表替换。如果没有任何文件匹配一个模式,这个模式从'wildcard'的输出中忽略,注意,这和上述的通配符的处理是不一样的。
'wildcard'函数的一个功能是找出目录中所有的'.c'文件:
$(wildcard *.c)
可以通过替换后缀'.c'为'.o'从C文件列表得到目标文件的列表:
$(patsubst %.c,%.o,$(wildcard *.c))
这样,上节中的makefile改写为:
objects := $(patsubst %.c,%.o,$(wildcard *.c))
foo : $(objects)
cc -o foo $(objects)
这个makefile利用了编译C程序的隐含规则,所以不需要对编译写出显式的规则。(':='是'='的一个变体)
注意:'PATTERN'是大小写敏感的。
10.内建的特殊目标
某些名字作为目标存在时有特殊含义。
★.PHONY该目标的依赖被认为是phony目标,处理这些目标时,命令无条件被执行,不管文件名是否存在及其最后修改时间
★.SUFFIXES该目标的依赖被认为是一个后缀列表,在检查后缀规则时使用
★.DEFAULT该目标的规则被使用在没有规则(显式的或隐含的)的目标上。如果’DEFAULT’命令定义了,则对所有不是规则目标的依赖文件都会执行该组命令
★.PRECIOUS该目标的依赖文件会受到特别对待:如果make被kill或命令的执行被中止,这些目标并不删除;而且如果该目标是中间文件,在不需要时不会被删除。可以将隐含规则的目标模式(如%.o)做为’.PRECIOUS’的依赖文件,这样可以保存这些规则产生的中间文件。
★.INTERMEDIATE该目标的依赖文件被当作中间文件;如果该目标没有依赖文件,则makefile中所有的目标文件均被认为是中间文件。
★.IGNORE在执行该目标的依赖规则的命令时,make会忽略错误,此规则本身的命令没有意义。如果该规则没有依赖关系,表示忽略所有命令执行的错误,这种用法只是为了向后兼容;由于会影响到所有的命令,所以不是特别有用,推荐使用其它更有选择性忽略错误的方法。
★.SILENT在执行该目标的依赖规则的命令时,make并不打印命令本身。该规则的命令没有意义。在’.SILIENT’没有依赖关系时,表示执行makefile中的所有命令都不会打印,该规则只是为了向后兼容提供的。
★.EXPORT_ALL_VARIABLES只是作为一个目标存在,指示make将所有变量输出到子进程中。
定义的隐含规则的后缀作为目标时,也认为它是特殊目标;两个后缀的连接也是一样,比如’.c.o’。这些目标是后缀规则,一中定义隐式规则的过时方法(但仍然广泛使用)。后缀通常以’.’开始,所以特殊目标也以’.’开始。
11.另类风格的makefile
即然我们的make可以自动推导命令,那么我看到那堆[.o]和[.h]的依赖就有点不爽,那么多的重复的[.h],能不能把其收拢起来,好吧,没有问题,这个对于make来说很容易,谁叫它提供了自动推导命令和文件的功能呢?来看看最新风格的makefile吧。
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h
.PHONY : clean
clean :
rm edit $(objects)
这种风格,让我们的makefile变得很简单,但我们的文件依赖关系就显得有点凌乱了。鱼和熊掌不可兼得。还看你的喜好了。我是不喜欢这种风格的,一是文件的依赖关系看不清楚,二是如果文件一多,要加入几个新的.o文件,那就理不清楚了。
12.回显
通常make打印出要执行的命令,称之为回显,这和亲自敲命令的现象是一样的。当行之前有字符时,命令不再回显,字符在传递给shell前丢弃。典型的用法是只对打印命令有效,比如’echo’命令:
@echo About to make distribution files
当make使用’-n’或’—just-print’选项时,显示要发生的一切,但不执行命令。只有在这种情况下,即使命令以开始,命令行仍然显示出来。这个选项对查看make实际要执行的动作很有用。
‘-s’或’—silent’选项阻止make所有回显,就象所有命令以开始一样;一条没有依赖关系的’.SILENT’规则有相同的作用,但是更加灵活。
阅读(1369) | 评论(0) | 转发(0) |