makefile总览

makefile 中,目标文件(target)包含:执行文件 edit 和中间目标文件(*.o),依赖文件(prerequisites)就是冒号后面的那些 .c 文件和 .h 文件。每一个 .o 文件都有一组依赖文件,而这些 .o 文件又是执行文件 edit 的依赖文件。依赖关系的实质上就是说明了目标文件是由哪些文件生成的,换言之,目标文件是哪些文件更新的。
在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个 Tab 键作为开头。记住,make 并不管命令是怎么工作的,他只管执行所定义的命令。make 会比较 targets 文件和 prerequisites 文件的修改日期,如果 prerequisites 文件的日期要比 targets 文件的日期要新,或者 target 不存在的话,那么,make 就会执行后续定义的命令。
这里要说明一点的是,clean 不是一个文件,它只不过是一个动作名字,有点像 C 语言中的 lable 一样,其冒号后什么也没有,那么,make 就不会自动去找文件的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令,就要在 make 命令后明显得指出这个lable 的名字。这样的方法非常有用,我们可以在一个 makefile 中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。

make 是如何工作的

在默认的方式下,也就是我们只输入 make 命令。那么,
1、make 会在当前目录下找名字叫“Makefile”或“makefile”的文件。
2、如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个文件,并把这个文件作为最终的目标文件。
3、 如果 edit 文件不存在, 或是 edit 所依赖的后面的 .o 文件的文件修改时间要比 edit这个文件新,那么,他就会执行后面所定义的命令来生成 edit 这个文件。
4、如果 edit 所依赖的.o 文件也存在,那么 make 会在当前文件中找目标为.o 文件的依赖性,如果找到则再根据那一个规则生成.o 文件。 (这有点像一个堆栈的过程)
5、 当然,你的 C 文件和 H 文件是存在的啦, 于是 make 会生成 .o 文件, 然后再用 .o 文件生命 make 的终极任务,也就是执行文件 edit 了。

makefile 中使用变量

在上面的例子中,先让我们看看 edit 的规则:
edit : main.o kbd.o command.o display.o
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o
insert.o search.o files.o utils.o
我们可以看到[.o]文件的字符串被重复了两次, 如果我们的工程需要加入一个新的[.o]文件,那么我们需要在两个地方加(应该是三个地方,还有一个地方在 clean 中)。当然,我们的 makefile 并不复杂,所以在两个地方加也不累,但如果 makefile 变得复杂,那么我们就有可能会忘掉一个需要加入的地方,而导致编译失败。所以,为了 makefile 的易维护,在 makefile 中我们可以使用变量。makefile 的变量也就是一个字符串,理解成 C 语言中的宏可能会更好。
比如,我们声明一个变量,叫 objects, OBJECTS, objs, OBJS, obj, 或是 OBJ,反正不管什么啦,只要能够表示 obj 文件就行了。我们在 makefile 一开始就这样定义:
objects = main.o kbd.o command.o display.o
insert.o search.o files.o utils.o
于是,我们就可以很方便地在我们的 makefile 中以“$(objects)”的方式来使用这个变量了,于是我们的改良版 makefile 就变成下面这个样子:

objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)

在规则中使用通配符

如果我们想定义一系列比较类似的文件,我们很自然地就想起使用通配符。make 支持
三各通配符: “”,“?”和“[…]”。 这是和 Unix 的 B-Shell 是相同的。 波浪号(“~”)
字符在文件名中也有比较特殊的用途。如果是“~/test”,这就表示当前用户的$HOME 目录
下的 test 目录。而“~hchen/test”则表示用户 hchen 的宿主目录下的 test 目录。(这些都
是 Unix 下的小知识了,make 也支持)而在 Windows 或是 MS-DOS 下,用户没有宿主目录,
那么波浪号所指的目录则根据环境变量“HOME”而定。
通配符代替了你一系列的文件,如“
.c”表示所以后缀为 c 的文件。一个需要我们注
意的是,如果我们的文件名中有通配符,如:“”,那么可以用转义字符“\”,如“*”
来表示真实的“
”字符,而不是任意长度的字符串。
好吧,还是先来看几个例子吧:
clean:
rm -f *.o
上面这个例子我不不多说了,这是操作系统 Shell 所支持的通配符。这是在命令中的通
配符。
print: .c
lpr -p ? t o u c h p r i n t 上 面 这 个 例 子 说 明 了 通 配 符 也 可 以 在 我 们 的 规 则 中 , 目 标 p r i n t 依 赖 于 所 有 的 [ . c ] 文 件 。 其 中 的 “ ? touch print 上面这个例子说明了通配符也可以在我们的规则中,目标 print 依赖于所有的[.c]文 件。其中的“ ?touchprintprint[.c]?”是一个自动化变量,我会在后面给你讲述。
objects = .o
上面这个例子,表示了,通符同样可以用在变量中。 并不是说[
.o]会展开, 不! objects
的值就是“
.o”。Makefile 中的变量其实就是 C/C++中的宏。如果你要让通配符在变量中
展开,也就是让 objects 的值是所有[.o]的文件名的集合,那么,你可以这样:
objects := $(wildcard *.o)
这种用法由关键字“wildcard”指出,关于 Makefile 的关键字,我们将在后面讨论。

文件搜寻

在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存
放在不同的目录中。所以,当 make 需要去找寻文件的依赖关系时,你可以在文件前加上路
径,但最好的方法是把一个路径告诉 make,让 make 在自动去找。
Makefile 文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,
make 只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make
就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。
VPATH = src:…/headers
上面的的定义指定两个目录,“src”和“…/headers”,make 会按照这个顺序进行搜
索。目录由“冒号”分隔。 (当然,当前目录永远是最高优先搜索的地方)
另一个设置文件搜索路径的方法是使用 make 的“vpath”关键字(注意,它是全小写
的),这不是变量,这是一个 make 的关键字,这和上面提到的那个 VPATH 变量很类似,但是
它更为灵活。它可以指定不同的文件在不同的搜索目录中。这是一个很灵活的功能。它的使
用方法有三种:
1、vpath
为符合模式的文件指定搜索目录。
2、vpath
清除符合模式的文件的搜索目录。
3、vpath
清除所有已被设置好了的文件搜索目录。
vapth 使用方法中的需要包含“%”字符。“%”的意思是匹配零或若干字符,
例如,“%.h”表示所有以“.h”结尾的文件。指定了要搜索的文件集,而
则指定了的文件集的搜索的目录。例如:
vpath %.h …/headers
该语句表示,要求 make 在“…/headers”目录下搜索所有以“.h”结尾的文件。(如果
某文件在当前目录没有找到的话)
我们可以连续地使用 vpath 语句,以指定不同搜索策略。如果连续的 vpath 语句中出现
了相同的,或是被重复了的,那么,make 会按照 vpath 语句的先后顺
序来执行搜索。如:
vpath %.c foo
vpath % blish
vpath %.c bar
其表示“.c”结尾的文件,先在“foo”目录,然后是“blish”,最后是“bar”目录。
vpath %.c foo:bar
vpath % blish
而上面的语句则表示“.c”结尾的文件,先在“foo”目录,然后是“bar”目录,最后
才是“blish”目录。

伪目标

最早先的一个例子中,我们提到过一个“clean”的目标,这是一个“伪目标”,
clean:
rm *.o temp
正像我们前面例子中的“clean”一样,即然我们生成了许多文件编译文件,我们也应
该提供一个清除它们的“目标”以备完整地重编译而用。 (以“make clean”来使用该目
标)
因为,我们并不生成“clean”这个文件。“伪目标”并不是一个文件,只是一个标签,
由于“伪目标”不是文件,所以 make 无法生成它的依赖关系和决定它是否要执行。我们只
有通过显示地指明这个“目标”才能让其生效。当然,“伪目标”的取名不能和文件名重名,
不然其就失去了“伪目标”的意义了。
当然,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显
示地指明一个目标是“伪目标”,向 make 说明,不管是否有这个文件,这个目标就是“伪
目标”。
.PHONY : clean
只要有这个声明,不管是否有“clean”文件,要运行“clean”这个目标,只有“make
clean”这样。于是整个过程可以这样写:
PHONY: clean
clean:
rm *.o temp
伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同
样可以作为“默认目标”,只要将其放在第一个。一个示例就是,如果你的 Makefile 需要
一口气生成若干个可执行文件,但你只想简单地敲一个 make 完事,并且,所有的目标文件
都写在一个 Makefile 中,那么你可以使用“伪目标”这个特性:
all : prog1 prog2 prog3
.PHONY : all
prog1 : prog1.o utils.o
cc -o prog1 prog1.o utils.o
prog2 : prog2.o
cc -o prog2 prog2.o
prog3 : prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o
我们知道,Makefile 中的第一个目标会被作为其默认目标。我们声明了一个“all”的
伪目标,其依赖于其它三个目标。由于伪目标的特性是,总是被执行的,所以其依赖的那三
个目标就总是不如“all”这个目标新。所以,其它三个目标的规则总是会被决议。也就达
到了我们一口气生成多个目标的目的。“.PHONY : all”声明了“all”这个目标为“伪目
标”。
随便提一句,从上面的例子我们可以看出,目标也可以成为依赖。所以,伪目标同样也
可成为依赖。看下面的例子:
.PHONY: cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff
“make clean”将清除所有要被清除的文件。“ cleanobj”和“cleandiff”这两个伪
目标有点像“子程序”的意思。我们可以输入“make cleanall”和“make cleanobj”和
“make cleandiff”命令来达到清除不同种类文件的目的。
伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同
样可以作为“默认目标”,只要将其放在第一个。一个示例就是,如果你的 Makefile 需要
一口气生成若干个可执行文件,但你只想简单地敲一个 make 完事,并且,所有的目标文件
都写在一个 Makefile 中,那么你可以使用“伪目标”这个特性:
all : prog1 prog2 prog3
.PHONY : all
prog1 : prog1.o utils.o
cc -o prog1 prog1.o utils.o
prog2 : prog2.o
cc -o prog2 prog2.o
prog3 : prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o
我们知道,Makefile 中的第一个目标会被作为其默认目标。我们声明了一个“all”的
伪目标,其依赖于其它三个目标。由于伪目标的特性是,总是被执行的,所以其依赖的那三
个目标就总是不如“all”这个目标新。所以,其它三个目标的规则总是会被决议。也就达
到了我们一口气生成多个目标的目的。“.PHONY : all”声明了“all”这个目标为“伪目
标”。
随便提一句,从上面的例子我们可以看出,目标也可以成为依赖。所以,伪目标同样也
可成为依赖。看下面的例子:
.PHONY: cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff
“make clean”将清除所有要被清除的文件。“ cleanobj”和“cleandiff”这两个伪
目标有点像“子程序”的意思。我们可以输入“make cleanall”和“make cleanobj”和
“make cleandiff”命令来达到清除不同种类文件的目的。

多目标

@ − − 目 标 文 件 , @--目标文件, @^–所有的依赖文件,$<–第一个依赖文件。

Makefile 的规则中的目标可以不止一个,其支持多目标,有可能我们的多个目标同时依赖
于一个文件,并且其生成的命令大体类似。于是我们就能把其合并起来。当然,多个目标的
生成规则的执行命令是同一个,这可能会可我们带来麻烦,不过好在我们的可以使用一个自
动化变量“ @ ” ( 关 于 自 动 化 变 量 , 将 在 后 面 讲 述 ) , 这 个 变 量 表 示 着 目 前 规 则 中 所 有 的 目 标 的 集 合 , 这 样 说 可 能 很 抽 象 , 还 是 看 一 个 例 子 吧 。 b i g o u t p u t l i t t l e o u t p u t : t e x t . g g e n e r a t e t e x t . g − @”(关于自动化变量,将在后面讲述),这个变量表示着目前规则中所有的目 标的集合,这样说可能很抽象,还是看一个例子吧。 bigoutput littleoutput : text.g generate text.g - @bigoutputlittleoutput:text.ggeneratetext.g(subst output,$@) > @ 上 述 规 则 等 价 于 : b i g o u t p u t : t e x t . g g e n e r a t e t e x t . g − b i g > b i g o u t p u t l i t t l e o u t p u t : t e x t . g g e n e r a t e t e x t . g − l i t t l e > l i t t l e o u t p u t 其 中 , − @ 上述规则等价于: bigoutput : text.g generate text.g -big > bigoutput littleoutput : text.g generate text.g -little > littleoutput 其中,- @bigoutput:text.ggeneratetext.gbig>bigoutputlittleoutput:text.ggeneratetext.glittle>littleoutput(subst output, @ ) 中 的 “ @)中的“ @)”表示执行一个 Makefile 的函数,函数名为 subst,
后面的为参数。关于函数,将在后面讲述。这里的这个函数是截取字符串的意思,“ @ ” 表 示 目 标 的 集 合 , 就 像 一 个 数 组 , “ @”表 示目标的集合,就像一个数组,“ @@”依次取出目标,并执于命令。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值