1. target files : depend files
书写规则建议的方式是:单目标,多依赖。就是说尽量要做到一个规则中只存在一
个目标文件,可有多个依赖文件。 尽量避免使用多目标,单依赖的方式.
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
2. Rules for cleaning the Directory
.PHONY : clean
clean :
-rm edit $(objects)
1. 通过“ .PHONY”特殊目标将“ clean”目标声明为伪
目标。避免当磁盘上存在一个名为“ clean”文件时,目标“ clean”所在规则的命令无
法执行(参考 4.6 Makefile伪目标 一节)。 2. 在命令行之前使用“ -”,意思是忽略命令
“ rm”的执行错误(参考 5.4 命令的错误 一节)。
3.执行make命令时:按照下列文件名读取文件并命名:GNUmakfile,makefile,Makefile.
如果make程序在工作目录下无法找到以上三个文件中的任何一个,它将不读取任
何其他文件作为解析对象,也可以通过多个“ -f”或者“ --file”选项来指定多个需要读取的 makefile 文件,
多个 makefile 文件将会被按照指定的顺序进行链接并被 make 解析执行。当通过“ -f”
或者“ --file”指定 make 读取 makefile 的文件时, make 就不再自动查找这三个标准命
名的 makefile 文件.
4.变量MAKEFILES.
它和使用“ include”的区别:
1. 环境变量指定的 makefile 文件中的“目标”不会被作为 make 执行的“终极目
标”。就是说,这些文件中所定义规则的目标, make 不会将其作为“终极目标”
来看待。如果在 make 的工作目录下没有一个名为“ Makefile”、“ makefile”或
者“ GNUmakefile” 的文件,make 同样会提示“ make: *** No targets specified
and no makefile found. Stop.”;而在 make 的工作目录下存在这样一个文件
(“ Makefile”、“ makefile”或者“ GNUmakefile”),那么 make 执行时的“终
极目标”就是当前目录下这个文件中所定义的“终极目标”。
2. 环境变量所定义的文件列表,在执行 make 时,如果不能找到其中某一个文件
(不存在或者无法创建)。 make 不会提示错误,也不退出。就是说环境变量
“ MAKEFILES”定义的包含文件是否存在不会导致 make 错误(这是比较隐蔽
的地方)。
3. make 在执行时,首先读取的是环境变量“ MAKEFILES”所指定的文件列表,
之后才是工作目录下的 makefile 文件,“ include”所指定的文件是在 make 发
现此关键字的时、暂停正在读取的文件而转去读取“ include”所指定的文件。
变量“ MAKEFILES”主要用在“ make” 的递归调用过程中的的通信 (参考5.6 make
的递归执行 一节)。实际应用中很少设置此变量。因为一旦设置了此变量,在多级make
调用时;由于每一级make都会读取“ MAKEFILES”变量所指定的文件,将导致执行出
现混乱(这可能不是你想看到的执行结果)。不过,我们可以使用此环境变量来指定一
个定义了通用“隐含规则”和变量的文件,比如设置默认搜索路径(可参考4.5 目录搜
索 一节);通过这种方式设置的“隐含规则”和定义的变量可以被任何make进程使用
(有点象C语言中的全局变量)。
也有人想让 login 程序自动的在自己的工作环境中设置此环境变量,编写的
Makefile 建立在此环境变量的基础上。此想法可以肯定地说不是一个好主意。规劝大家
千万不要这么干,否则你所编写的 Makefile 在其人的工作环境中肯定不能正常工作。
因为别人的工作环境中可能没有设置相同的环境变量“ MAKEFILES”。
推荐的做法实:在需要包含其它 makefile 文件时使用指示符“ include”来实现。
5.变量:MAKEFILE_LIST:
make 程序在读取多个 makefile 文件时,包括由环境变量“ MAKEFILES”指定、
命令行指、当前工作下的默认的以及使用指示符“ include”指定包含的,在对这些文
件 进 行 解 析 执 行 之 前 make 读 取 的 文 件 名 将 会 被 自 动 依 次 追 加 到 变 量
“ MAKEFILE_LIST”的定义域中.
6.变量:VARIABLES.
.VARIABLES”。它被展开以后是此引用点
之前、 makefile文件中所定义的所有全局变量列表
7.重载另一个makefile.如果另一个makefile和先前的makefile,存在相同的目标且规则不相同.
如果存在一个命名为“ Makefile”的 makefile 文件,其中描述目标“ foo”
的规则和其他的一些规,我们也可以书写一个内容如下命名为“ GNUmakefile”的文件。
#sample GNUmakefile
foo:
frobnicate > foo
%: force
@$(MAKE) -f Makefile $@
force: ;
执行命令“ make foo”, make 将使用工作目录下命名为“ GNUmakefile”的文件
并执行目标“ foo”所在的规则,创建目标“ foo”的命令是:“ frobnicate > foo”。如
果执行另外一个命令“ make bar”,因为在“ GUNmakefile”中没有此目标的更新规
则。 make 将使用“所有匹配模式”规则,执行命令“ $(MAKE) -f Makefile bar”。如
果文件“ Makefile”中存在此目标更新规则的定义,那么这个规则会被执行。此过程同
样适用于其它 “ GNUmakefile”中没有给出的目标更新规则。此方式的灵活之处在于:
如果在“ Makefile”文件中存在同样一一个目标“ foo”的重建规则,由于 make 执行时
首先读取文件“ GUNmakefile”并在其中能够找到目标“ foo”的重建规则,所以 make
就不会去执行这个“所有模式匹配规则”(上例中目标“ %”所在的规则)。这样就避
免了使用指示符“ include”包含一个 makefile 文件时所带来的目标规则的重复定义问
题。
此种方式,模式规则的模式只使用了单独的“ %”(我们称他为“所有模式匹配规
则”),它可以匹配任何一个目标;它的依赖是“ force”,保证了即使目标文件已经
存在也会执行这个规则(文件已存在时,需要根据它的依赖文件的修改情况决定是否需
要重建这个目标文件);“ force”规则中使用空命令是为了防止 make 程序试图寻找
一个规则去创建目标“ force”时,又使用了模式规则“ %: force”而陷入无限循环.
8.make如何解析makefile文件.
GUN make 的执行过程分为两个阶段。
第一阶段:读取所有的 makefile 文件(包括“ MAKIFILES”变量指定的、指示符
“ include”指定的、以及命令行选项“ -f(--file)”指定的 makefile 文件),内建所有的
变量、明确规则和隐含规则,并建立所有目标和依赖之间的依赖关系结构链表。
在第二阶段:根据第一阶段已经建立的依赖关系结构链表决定哪些目标需要更新,
并使用对应的规则来重建这些目标。
9.规则执行的时间:
(1) 目标文件已过期.
(2) 目标文件存在,但是规则的依赖文件中存在一个依赖的最后修改时间比目标的最后修改时间晚.
规则的中心思想:目标文件的内容有依赖文件决定,依赖文件的任何一处改动,将导致目前已经存在的目标文件的内容过期.
10.函数wildcard:
$(wildcard *.c), 获取工作目录下的所有.c文件列表.
$(patsubst %.c,%.o,$(wildcard *.c)),首先使用“ wildcard”
函数获取工作目录下的.c 文件列表;之后将列表中所有文件名的后缀.c 替换为.o。这样
我们就可以得到在当前目录可生成的.o 文件列表.
objects := $(patsubst %.c,%.o,$(wildcard *.c))
foo : $(objects)
cc -o foo $(objects)
11.VPATH,搜索路径.
VPATH = src:../headers. 指定两个路径,src,../headers ,注意:当前路径永远是第一搜索路径.
12: vpath ,不是一个变量,而是make的一个关键字.
vpath PATTERN DIRECTORIES vpath %.h ../heasders:src
为所有符合模式“ PATTERN”的文件指定搜索目录“ DIRECTORIES”。多个目
录使用空格或者冒号(:)分开。类似上一小节的“ VPATH”变量。
2、 vpath PATTERN
清除之前为符合模式“ PATTERN”的文件设置的搜索路径。
3、 vpath
清除所有已被设置的文件搜索路径。
vpath %.c foo
vpath % blish
vpath %.c bar
表示对所有的.c 文件, make 依次查找目录:“ foo”、 blish”、“ bar”。
而:
vpath %.c foo: bar
vpath % blish
对于所有的.c 文件 make 将依次查找目录:“ foo”、“ bar”、“ blish.
13.目录搜索的机制:
为了更清楚地描述此算法,我们使用一个例子来说明。存在一个目录“ prom”,
“ prom”的子目录“ src”下存在“ sum.c”和“ memcp.c”两个源文件。在“ prom”
目录下的 Makefile 部分内容如下:
LIBS = libtest.a
VPATH = src
libtest.a : sum.o memcp.o
$(AR) $(ARFLAGS) $@ $^
首先,如果在两个目录(“ prom”和“ src”)都不存在目标“ libtest.a”,执行 make
时将会在当前目录下创建目标文件“ libtest.a”。另外;如果“ src”目录下已经存在
“ libtest.a”,以下两种不同的执行结果:
1) 当它的两个依赖文件“ sum.c”和“ memcp.c”没有被更新的情况下我们执行
make,首先 make 程序会搜索到目录“ src”下的已经存在的目标“ libtest.a”。
由于目标“ libtest.a”的依赖文件没有发生变化,所以不会重建目标。并且目标
所在的目录不会发生变化。
2) 当我们修改了文件“ sum.c”或者“ memcp.c”以后执行 make。“ libtest.a”和
“ sum.o”或者“ memcp.o”文件将会被在当前目录下创建(目标完整路径名
被废弃),而不是在“ src”目录下更新这些已经存在的文件。此时在两个目录下
(“ prom”和“ src”)同时存在文件“ libtest.a”。但只有“ prom/libtest.a”是最
新的库文件。
当在上边的 Makefile 文件中使用“ GPATH”指定目录时,情况就不一样了。首先
看看怎么使用“ GPATH”,改变后的 Makefile 内容如下:
LIBS = libtest.a
GPATH = src
VPATH = src
LDFLAGS += -L ./. –ltest
…….
……
同样;当两个目录都不存在目标文件“ libtest.a”时,目标将会在当前目录(“ prom”
目录)下创建。如果“ src”目录下已经存在目标文件“ libtest.a”。当其依赖文件任何
一个被改变以后执行 make,目标“ libtest.a”将会被在“ src”目录下被更新(目标完
整路径名不会被废弃)
14.伪目标依赖伪目标:
PHONY: cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff
15.自动化变量:
16.静态规则模式:
objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
与下面模式相同:
foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o
17双冒号规则:
Newprog :: foo.c
$(CC) $(CFLAGS) $< -o $@
Newprog :: bar.c
$(CC) $(CFLAGS) $< -o $@
如果“ foo.c”文件被修改,执行 make 以后将根据“ foo.c”文件重建目标“ Newprog”。
而如果“ bar.c”被修改那么“ Newprog”将根据“ bar.c”被重建。回想一下,如果以
上两个规则为普通规时出现的情况是什么?( make 将会出错并提示错误信息)
当同一个目标出现在多个双冒号规则中时,规则的执行顺序和普通规则的执行顺序
一样,按照其在 Makefile 中的书写顺序执行。
GNU make 的双冒号规则给我们提供一种根据依赖的更新情况而执行不同的命令
来重建同一目标的机制。一般这种需要的情况很少,所以双冒号规则的使用比较罕见。
一般双冒号规则都需要定义命令,如果一个双冒号规则没有定义命令,在执行规则时将
为其目标自动查找隐含规则。
18."-" 忽略命令执行的错误
我们可以在命令之前加一个减号“ -”
(在[Tab]字符之后),来告诉 make 忽略此命令的执行失败。命令中的“ -”号会在 shell
解析并执行此命令之前被去掉, shell 所解释的只是纯粹的命令,“ -”字符是由 make
来处理的。例如对于“ clean”目标我们就可以这么写:
clean:
-rm *.o
其含义是:即使执行“ rm”删除文件失败, make 也继续执行。
一般 make 的“ -k”参数在实际应用中,主要用途是:当同时修改了工程中的多个
文件后,“ -k”参数可以帮助我们确认对那些文件的修改是正确的(可以被编译),那些
文件的修改是不正确的(不能正确编译)。例如我们修改了工程中的 20 个源文件,修
改完成之后使用带“ -k”参数的 make,它可以一次性找出修改的 20 个文件中哪些是
不能被编译。
在执行 make 时,如果使用命令行选项“ -i”或者“ —ignore-errors”, make 将忽
略所有规则中命令执行的错误。没有依赖的特殊目标“ .IGNORE”在 Makefile 中有同
样的效果。但是“ .IGNORE”的方式已经很少使用,因为它没有在命令行之前使用“ -”
的方式灵活
Makefile 中定义一个特殊的目标 “ .DELETE_ON_ERROR”。但是这个做法存在不兼容。
推荐的做法是:在 make 执行失败时,修改错误之后执行 make 之前,使用 “ make clean”
明确的删除第一次错误重建的所有目标.
待更>>>>>