Makefile总结

重构代码,用到Makefile,工作中写的并不多,只会改改,现在要全面的使用了,复习一下Makefile,作了些总结:


Makefile的规则

 

target ... : prerequisites ...

command

...

...

target也就是一个目标文件,可以是Object File,也可以是执行文件。

prerequisites就是,要生成那个target所需要的文件或是目标。

command也就是make需要执行的命令。(任意的Shell命令)。

 

这是一个文件的依赖关系,也就是说,target 这一个或多个的目标文件依赖于

prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites

中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。

这就是Makefile的规则。也就是Makefile 中最核心的内容。

依赖关系的实质上就是说明了目标文件是由哪些文件生成的,换言之,目标文件是哪些文件更新的。

在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个Tab键作为开头。记住,make并不管命令是怎么工作的,他只管执行所定义的命令。make会比较targets文件和prerequisites文件的修改日期,如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的话,那么,make就会执行后续定义的命令。

要说明一点的是,clean不是一个文件,它只不过是一个动作名字,有点像C语言

中的lable一样,其冒号后什么也没有,那么,make就不会自动去找文件的依赖性,

也就不会自动执行其后所定义的命令。要执行其后的命令,就要在make命令后明显得

指出这个lable的名字。这样的方法非常有用,我们可以在一个makefile中定义不用的

编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。

 

make是如何工作的

1、make会在当前目录下找名字叫“Makefile”或“makefile”的文件。

2、如果找到,它会找文件中的第一个目标文件(target)

          3、如果target文件不存在,或是target所依赖的后面的 .o 文件的文件修改时间要比target这

个文件新,那么,他就会执行后面所定义的命令来生成target这个文件。

4、如果target所依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依

性,如果找到则再根据那一个规则生成.o文件。(这有点像一个堆栈的过程)

5、当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o

件生命make的终极任务,也就是执行文件target了。

这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编

译出第一个目标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,

那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,

make根本不理。make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面

的文件还是不在,那么对不起,我就不工作啦。

像clean这种,没有被第一个目标文件直接或间接关联,那

么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令

——“make clean”,以此来清除所有的目标文件,以便重编译。

makefile中使用变量

makefile的变量也就是一个字符串,理解成C语言中的宏可能会更好。makefile中以“$(变量名)”的方式来使用变量

 

四、make自动推导

GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的命令;只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果make找到一个whatever.o,那么whatever.c,就会是whatever.o 的依赖文件。并且 cc –cwhatever.c 也会被推导出来。如:

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)

五、清空目标文件的规则

每个Makefile 中都应该写一个清空目标文件(.o和执行文件)的规则,这不仅便于重编译,也很利于保持文件的清洁。

一般的风格都是:

clean:

rm edit $(objects)

更稳健的做法是:

.PHONY : clean

clean :

-rm edit $(objects)

.PHONY意思表示clean是一个“伪目标”,。而在rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。当然,clean的规则不要放在文件的开头,不然,这就会变成make的默认目标,相信谁也不愿意这样。不成文的规矩是——“clean从来都是放在文件的最后”。

六、Makefile包含的内容

Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。

几点说明:

1、显式规则。显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书

写者明显指出,要生成的文件,文件的依赖文件,生成的命令。

2、文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像

C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像

C语言中的预编译#if一样;还有就是定义一个多行的命令。

3、注释。Makefile 中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这

个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符,可以用反斜框

进行转义,如:“\#”。

值得注意的是:在Makefile中的命令,必须要以[Tab]键开始。

七、引用其他Makefile文件

在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的

#include,被包含的文件会原模原样的放在当前文件的包含位置。include的语法是:

include <filename>

make命令开始时,会把找寻include所指出的其它Makefile,并把其内容安置在当前的

位置。就好像C/C++的#include指令一样。如果文件都没有指定绝对路径或是相对路径的

话,make会在当前目录下首先寻找,如果当前目录下没有找到,那么,make还会在

下面的几个目录下找:

1、如果make执行时,有“-I”或“--include-dir”参数,那么make就会在这个参数所指

定的目录下去寻找。

2、如果目录<prefix>/include(一般是:/usr/local/bin或/usr/include)存在的话,make也

会去找。

如果有文件没有找到的话,make会生成一条警告信息,但不会马上出现致命错误。它

会继续载入其它的文件,一旦完成makefile的读取,make会再重试这些没有找到,或

是不能读取的文件,如果还是不行,make才会出现一条致命信息。如果你想让make不

理那些无法读取的文件,而继续执行,你可以在include前加一个减号“-”。

八、make的工作方式

GNU的make工作时的执行步骤入下:(其它的make也是类似)

1、读入所有的Makefile。

2、读入被include的其它Makefile。

3、初始化文件中的变量。

4、推导隐晦规则,并分析所有规则。

5、为所有的目标文件创建依赖关系链。

6、根据依赖关系,决定哪些目标要重新生成。

7、执行生成命令。

一般来说,make会以UNIX的标准Shell,也就是/bin/sh来执行命令。

九、文件搜寻

在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并

存放在不同的目录中。所以,当make需要去找寻文件的依赖关系时,你可以在文件前

加上路径,但最好的方法是把一个路径告诉make,让make在自动去找。Makefile文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,

make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,

make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了

另一个设置文件搜索路径的方法是使用make的“vpath”关键字(注意,它是全小写

的),这不是变量,这是一个make的关键字,这和上面提到的那个VPATH变量很类

似,但是它更为灵活。它可以指定不同的文件在不同的搜索目录中。这是一个很灵活的

功能。它的使用方法有三种:

1、vpath <pattern> <directories>

为符合模式<pattern>的文件指定搜索目录<directories>。

2、vpath <pattern>

清除符合模式<pattern>的文件的搜索目录。

3、vpath

清除所有已被设置好了的文件搜索目录。

vapth使用方法中的<pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符,例

如,“%.h”表示所有以“.h”结尾的文件。<pattern>指定了要搜索的文件集,而

<directories>则指定了<pattern>的文件集的搜索的目录。例如:

vpath %.h ../headers

该语句表示,要求make在“../headers”目录下搜索所有以“.h”结尾的文件。(如果某文

件在当前目录没有找到的话),我们可以连续地使用vpath语句,以指定不同搜索策略。如果连续的vpath语句中出现了相同的<pattern>,或是被重复了的<pattern>,那么,make会按照vpath语句的先后

顺序来执行搜索。

十、伪目标

“伪目标”并不是一个文件,只是一个标签,由于“伪目标”不是文件,所以make无法生成它的依赖关系和决定它是否要执行。我们只有通过显示地指明这个“目标”才能让其生效。当然,“伪目标”的取名不能和文件名重名,不然其就失去了“伪目标”的意义了。

为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”。

只要有这个声明,不管是否有“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”这个目标为“伪

目标”。

十一、命令

每条规则中的命令和操作系统Shell的命令行是一致的。make会一按顺序一条一条的执

行命令,每条命令的开头必须以[Tab]键开头,除非,命令是紧跟在依赖规则后面的分

号后的。在命令行之间中的空格或是空行会被忽略,但是如果该空格或空行是以Tab键

开头的,那么make会认为其是一个空命令。

通常,make会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在

命令行前,那么,这个命令将不被make显示出来,最具代表性的例子是,我们用这个

功能来像屏幕显示一些信息。

如果make执行时,带入make参数“-n”或“--just-print”,那么其只是显示命令,但不

会执行命令,这个功能很有利于我们调试我们的Makefile,看看我们书写的命令是执

行起来是什么样子的或是什么顺序的。

而make参数“-s”或“--slient”则是全面禁止命令的显示。

命令执行:

当依赖目标新于目标时,也就是当规则的目标需要被更新时,make会一条一条的执行

其后的命令。需要注意的是,如果你要让上一条命令的结果应用在下一条命令时,你应

该使用分号分隔这两条命令。比如你的第一条命令是cd命令,你希望第二条命令得在

cd之后的基础上运行,那么你就不能把这两条命令写在两行上,而应该把这两条命令

写在一行上,用分号分隔。

命令出错:

每当命令运行完后,make会检测每个命令的返回码,如果命令返回成功,那么make

会执行下一条命令,当规则中所有的命令成功返回后,这个规则就算是成功完成了。如

果一个规则中的某个命令出错了(命令退出码非零),那么make就会终止执行当前规

则,这将有可能终止所有规则的执行。有些时候,命令的出错并不表示就是错误的。例如mkdir命令,我们一定需要建立一个目录,如果目录不存在,那么mkdir就成功执行,万事大吉,如果目录存在,那么就

出错了。我们之所以使用mkdir的意思就是一定要有这样的一个目录,于是我们就不希

望mkdir出错而终止规则的运行。为了做到这一点,忽略命令的出错,我们可以在Makefile的命令行前加一个减号“-”(在Tab键之后),标记为不管命令出不出错都认为是成功的。

还有一个全局的办法是,给make加上“-i”或是“--ignore-errors”参数,那么,Makefile

中所有命令都会忽略错误。而如果一个规则是以“.IGNORE”作为目标的,那么这个规

则中的所有命令将会忽略错误。这些是不同级别的防止命令出错的方法;

还有一个要提一下的make的参数的是“-k”或是“--keep-going”,这个参数的意思是,

如果某规则中的命令出错了,那么就终目该规则的执行,但继续执行其它规则。

十二、嵌套执行make

在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,

我们可以在每个目录中都书写一个该目录的Makefile,这有利于让我们的Makefile 变

得更加地简洁,而不至于把所有的东西全部写在一个Makefile中,这样会很难维护我

们的Makefile,这个技术对于我们模块编译和分段编译有着非常大的好处。

我们有一个子目录叫subdir,这个目录下有个Makefile文件,来指明了这个目

录下文件的编译规则。那么我们总控的Makefile可以这样:

subsystem:

cd subdir && $(MAKE)

在“嵌套执行”中比较有用的参数,“-w”或是“--print-directory”会在make

的过程中输出一些信息,让你看到目前的工作目录。比如,如果我们的下级make目录

是“/home/hchen/gnu/make”,如果我们使用“make -w”来执行,那么当进入该目录时,

我们会看到:

make: Entering directory `/home/test/gnu/make'.

而在完成下层make后离开目录时,我们会看到:

make: Leaving directory `/home/test/gnu/make'

当你使用“-C”参数来指定make下层Makefile 时,“-w”会被自动打开的。如果参数中

有“-s”(“--slient”)或是“--no-print-directory”,那么,“-w”总是失效的。

 

十三、变量的使用

在Makefile中的定义的变量,就像是C/C++语言中的宏一样,他代表了一个文本字串,

在Makefile 中执行的时候其会自动原模原样地展开在所使用的地方。其与C/C++所不同

的是,你可以在Makefile中改变其值。在Makefile中,变量可以使用在“目标”,“依

赖目标”,“命令”或是Makefile 的其它部分中。

变量的命名字可以包含字符、数字,下划线(可以是数字开头),但不应该含有“:”、

“#”、“=”或是空字符(空格、回车等)。变量是大小写敏感的,“foo”、“Foo”和

“FOO”是三个不同的变量名。

有一些变量是很奇怪字串,如“$<”、“$@”等,这些是自动化变量。

变量在声明时需要给予初值,而在使用时,需要给在变量名前加上“$”符号,但最好

用小括号“()”或是大括号“{}”把变量给包括起来。如果你要使用真实的“$”字符,

那么你需要用“$$”来表示。

变量高级用法:

1.      变量值的替换

我们可以替换变量中的共有的部分,其格式是“$(var:a=b)”或是“${var:a=b}”,其意

思是,把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾

”意思是“空格”或是“结束符”。

foo := a.o b.oc.o

bar :=$(foo:.o=.c)

这个示例中,我们先定义了一个“$(foo)”变量,而第二行的意思是把“$(foo)”中所有

以“.o”字串“结尾”全部替换成“.c”,所以我们的“$(bar)”的值就是“a.c b.c c.c”。

另外一种变量替换的技术是以“静态模式”定义的,如:

foo := a.o b.oc.o

bar :=$(foo:%.o=%.c)

这依赖于被替换字串中的有相同的模式,模式中必须包含一个“%”字符,这个例子同

样让$(bar)变量的值为“a.c b.c c.c”。

2.把变量的值再当成变量.

x = y

y = z

a := $($(x))

在这个例子中,$(x)的值是“y”,所以$($(x))就是$(y),于是$(a)的值就是“z”。(注意

是“x=y”,而不是“x=$(y)”)。

3. 追加变量值

我们可以使用“+=”操作符给变量追加值,如果变量之前没有定义过,那么,“+=”会自动变成“=”,如果前面有变量定义,那么“+=”会继承于前次操作的赋值符。如果前一次的是“:=”,那么“+=”会以“:=”作为其赋值符,如:

variable :=value

variable += more

等价于:

variable :=value

variable :=$(variable) more

十四、使用条件判断

         使用条件判断,可以让make根据运行时的不同情况选择不同的执行分支。条件表达式

可以是比较变量的值,或是比较变量和常量的值。

条件表达式的语法为:

<conditional-directive>

<text-if-true>

endif

以及:

<conditional-directive>

<text-if-true>

else

<text-if-false>

endif

其中<conditional-directive>表示条件关键字,如“ifeq”。这个关键字有四个。

第一个是“ifeq”

ifeq (<arg1>, <arg2> )

ifeq '<arg1>' '<arg2>'

ifeq "<arg1>""<arg2>"

ifeq "<arg1>"'<arg2>'

ifeq '<arg1>'"<arg2>"

比较参数“arg1”和“arg2”的值是否相同。当然,参数中我们还可以使用make的函数。

如:

ifeq ($(strip $(foo)),)

<text-if-empty>

endif

这个示例中使用了“strip”函数,如果这个函数的返回值是空(Empty),那么<text-if-empty>就生效。

第二个条件关键字是“ifneq”。语法是:

ifneq (<arg1>, <arg2> )

ifneq '<arg1>' '<arg2>'

ifneq "<arg1>""<arg2>"

ifneq "<arg1>"'<arg2>'

ifneq '<arg1>'"<arg2>"

其比较参数“arg1”和“arg2”的值是否相同,如果不同,则为真。和“ifeq”类似。

第三个条件关键字是“ifdef”。语法是:

ifdef <variable-name>

如果变量<variable-name>的值非空,那到表达式为真。否则,表达式为假。当然,

<variable-name>同样可以是一个函数的返回值。注意,ifdef只是测试一个变量是否有值,

其并不会把变量扩展到当前位置。

第四个条件关键字是“ifndef”。其语法是:

ifndef <variable-name>

这个和“ifdef”是相反的意思。

十五、使用函数

在Makefile中可以使用函数来处理变量,从而让我们的命令或是规则更为的灵活和具

有智能。make所支持的函数也不算很多,不过已经足够我们的操作了。函数调用后,函

数的返回值可以当做变量来使用。

1、函数的调用语法

函数调用,很像变量的使用,也是

$(<function> <arguments> )

或是

${<function> <arguments>}

<function>就是函数名,make支持的函数不多。<arguments>是函数的参数,参数

间以逗号“,”分隔,而函数名和参数之间以“空格”分隔。函数调用以“$”开头,以圆

括号或花括号把函数名和参数括起。感觉很像一个变量,是不是?函数中的参数可以使

用变量,为了风格的统一,函数和变量的括号最好一样,如使用“$(subst a,b,$(x))”这

样的形式,而不是“$(substa,b,${x})”的形式。因为统一会更清楚,也会减少一些不必

要的麻烦。

2. 字符串处理函数、文件名操作函数、

具体查看相关手册

3. foreach 函数

oreach函数用来做循环用的,它的语法是:

$(foreach<var>,<list>,<text> )

这个函数的意思是,把参数<list>中的单词逐一取出放到参数<var>所指定的变量中,

然后再执行<text>所包含的表达式。每一次<text>会返回一个字符串,循环过程中,

<text>的所返回的每个字符串会以空格分隔,最后当整个循环结束时,<text>所返回的

每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。所以,<var>最好是一个变量名,<list>可以是一个表达式,而<text>中一般会使用

<var>这个参数来依次枚举<list>中的单词。注意,foreach中的<var>参数是一个临时的局部变量,foreach函数执行完后,参数<var>的变量将不在作用,其作用域只在foreach函数当中。

4. if 函数

if函数的语法是:

$(if <condition>,<then-part> )

或是

$(if<condition>,<then-part>,<else-part> )

if函数可以包含“else”部分,或是不含。即if函数的参数可以是两个,也可以是

三个。<condition>参数是if的表达式,如果其返回的为非空字符串,那么这个表达式就

相当于返回真,于是,<then-part>会被计算,否则<else-part>会被计算。而if函数的返回值是,如果<condition>为真(非空字符串),那个<then-part>会是整个函数的返回值,如果<condition>为假(空字符串),那么<else-part>会是整个函数的返回值,此时如果<else-part>没有被定义,那么,整个函数返回空字串。

<then-part>和<else-part>只会有一个被计算。

5. shell函数

shell函数也不像其它的函数。它的参数应该就是操作系统Shell的命令。它和

反引号“`”是相同的功能。这就是说,shell函数把执行操作系统命令后的输出作为函数

返回。于是,我们可以用操作系统命令以及字符串处理命令awk,sed等等命令来生成

一个变量,

contents := $(shell cat foo)

files := $(shell echo *.c)

注意,这个函数会新生成一个Shell程序来执行命令,所以你要注意其运行性能,如果

你的Makefile 中有一些比较复杂的规则,并大量使用了这个函数,那么对于你的系统

性能是有害的。

6. 控制make的函数

make提供了一些函数来控制make的运行。通常,你需要检测一些运行Makefile时的运

行时信息,并且根据这些信息来决定,你是让make继续执行,还是停止。

$(error <text ...> )

产生一个致命的错误,<text...>是错误信息。注意,error函数不会在一被使用就会产生

错误信息,所以如果你把其定义在某个变量中,并在后续的脚本中使用这个变量,那

么也是可以的。

$(warning <text ...> )

这个函数很像error函数,只是它并不会让make退出,只是输出一段警告信息,而

make继续执行。

十六、make的运行

一般来说,最简单的就是直接在命令行下输入make命令,make命令会找当前目录的

makefile来执行,一切都是自动的。

make的退出码:

make命令执行后有三个退出码:

0 —— 表示成功执行。

1 —— 如果make运行时出现任何错误,其返回1。

2 —— 如果你使用了make的“-q”选项,并且make使得一些目标不需要更新,那么返

回2。

指定Makefile:

要使用make的“-f”或是“--file”参数(“--makefile”参数也行)

指定目标:

一般来说,make的最终目标是makefile中的第一个目标,而其它目标一般是由这个目

标连带出来的。这是make的默认行为。当然,一般来说,你的makefile中的第一个目标

是由许多个目标组成,你可以指示make,让其完成你所指定的目标。要达到这一目的

很简单,需在make命令后直接跟目标的名字就可以完成

任何在makefile中的目标都可以被指定成终极目标,但是除了以“-”打头,或是包含

了“=”的目标,因为有这些字符的目标,会被解析成命令行参数或是变量。甚至没有被

我们明确写出来的目标也可以成为make的终极目标,也就是说,只要make可以找到

其隐含规则推导规则,那么这个隐含目标同样可以被指定成终极目标。make可以指定所有makefile中的目标,那么也包括“伪目标”:

“all”

这个伪目标是所有目标的目标,其功能一般是编译所有的目标。

“clean”

这个伪目标功能是删除所有被make创建的文件。

“install”

这个伪目标功能是安装已编译好的程序,其实就是把目标执行文件拷贝到指定的目标

中去。

“print”

这个伪目标的功能是例出改变过的源文件。

“tar”

这个伪目标功能是把源程序打包备份。也就是一个tar文件。

“dist”

这个伪目标功能是创建一个压缩文件,一般是把tar文件压成Z文件。或是gz文件。

“TAGS”

这个伪目标功能是更新所有的目标,以备完整地重编译使用。

“check”和“test”

这两个伪目标一般用来测试makefile的流程。

 

十七、make的参数

常用:

“-q”

“--question”

这个参数的行为是找目标的意思,也就是说,如果目标存在,那么其什么也不会输出,

当然也不会执行编译,如果目标不存在,其会打印出一条出错信息。

“-B”

“--always-make”

认为所有的目标都需要更新(重编译)。

“-C <dir>”

“--directory=<dir>”

指定读取makefile的目录。如果有多个“-C”参数,make的解释是后面的路径以前面的

作为相对路径,并以最后的目录作为被指定目录。如:“make –C /home/test –C prog”

等价于“make –C /home /test/prog”。

“-e”

“--environment-overrides”

指明环境变量的值覆盖makef

“-f=<file>”

“--file=<file>”

“--makefile=<file>”

指定需要执行的makefile。

“-h”

“--help”

显示帮助信息。

“-i”

“--ignore-errors”

在执行时忽略所有的错误。

“-I <dir>”

“--include-dir=<dir>”

指定一个被包含makefile的搜索目标。可以使用多个“-I”参数来指定多个目录。

“-k”

“--keep-going”

出错也不停止运行。如果生成一个目标失败了,那么依赖于其上的目标就不会被执行了。

“-l <load>”

“--load-average[=<load]”

“—max-load[=<load>]”

指定make运行命令的负载。

“-o <file>”

“--old-file=<file>”

“--assume-old=<file>”

不重新生成的指定的<file>,即使这个目标的依赖文件新于它。

“-r”

“--no-builtin-rules”

禁止make使用任何隐含规则。

“-s”

“--silent”

“--quiet”

在命令运行时不输出命令的输出。

“-v”

“--version”

输出make程序的版本、版权等关于make的信息。

“-w”

“--print-directory”

输出运行makefile之前和之后的信息。这个参数对于跟踪嵌套式调用make时很有用。

十八、自动化变量

所谓自动化变量,就是这种变量会把模式中所定义的

一系列的文件自动地挨个取出,直至所有的符合模式的文件都取完了。这种自动化变量

只应出现在规则的命令中。

常用自动化变量:

$@

表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于

目标中模式定义的集合。

$%

仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a

(bar.o)",那么,"$%"就是"bar.o","$@"就是"foo.a"。如果目标不是函数库文件(Unix

下是[.a],Windows下是[.lib]),那么,其值为空。

$<

依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将

是符合模式的一系列的文件集。注意,其是一个一个取出来的。

$?

所有比目标新的依赖目标的集合。以空格分隔。

$^

所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量

会去除重复的依赖目标,只保留一份。

$+

这个变量很像"$^",也是所有依赖目标的集合。只是它不去除重复的依赖目标。

$*

这个变量表示目标模式中"%"及其之前的部分。如果目标是"dir/a.foo.b",并且目标的模

式是"a.%.b",那么,"$*"的值就是"dir/a.foo"。这个变量对于构造有关联的文件名是比

较有较。如果目标中没有模式的定义,那么"$*"也就不能被推导出,但是,如果目标文

件的后缀是make所识别的,那么"$*"就是除了后缀的那一部分。例如:如果目标

是"foo.c",因为".c"是make所能识别的后缀名,所以,"$*"的值就是"foo"。这个特性

是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用"$

*",除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那

么"$*"就是空值。

当你希望只对更新过的依赖文件进行操作时,"$?"在显式规则中很有用。

四个变量($@、$<、$%、$*)在扩展时只会有一个文

件,而另三个的值是一个文件列表。这七个自动化变量还可以取得文件的目录名或是在

当前目录下的符合模式的文件名,只需要搭配上"D"或"F"字样。"D"的含义就是

Directory,就是目录,"F"的含义就是File,就是文件。

$(@D)

表示"$@"的目录部分(不以斜杠作为结尾),如果"$@"值是"dir/foo.o",那

么"$(@D)"就是"dir",而如果"$@"中没有包含斜杠的话,其值就是"."(当前目录)。

$(@F)

表示"$@"的文件部分,如果"$@"值是"dir/foo.o",那么"$(@F)" 就

是"foo.o","$(@F)"相当于函数"$(notdir$@)"。

"$(%D)"

"$(%F)"

分别表示了函数包文件成员的目录部分和文件部分。

"$(<D)"

"$(<F)"

分别表示依赖文件的目录部分和文件部分。

"$(^D)"

"$(^F)"

分别表示所有依赖文件的目录部分和文件部分。(无相同的)

"$(+D)"

"$(+F)"

分别表示所有依赖文件的目录部分和文件部分。(可以有相同的)

"$(?D)"

"$(?F)"

分别表示被更新的依赖文件的目录部分和文件部分。

对于"$<",为了避免产生不必要的麻烦,我们最好给$后面的那个特定字符都加上圆括号,比如,"$(<)"就要比"$<"要好一些。

 

函数wildcard:

在规则中,通配符会被自动展开。但在变量的定义和函数引用时,通配符将失效。这种情况下如果需要通配符有效,就需要使用函数“wildcard”,它的用法是:$(wildcardPATTERN...) 。在Makefile 中,它被展开为已经存在的、使用空格分开的、匹配此模式的所有文件列表。如果不存在任何符合此模式的文件,函数会忽略模式字符并返回空。需要注意的是:这种情况下规则中通配符的展开和上一小节匹配通配符的区别。

一般我们可以使用“$(wildcard *.c)”来获取工作目录下的所有的.c 文件列表。复

杂一些用法;可以使用“$(patsubst%.c,%.o,$(wildcard *.c))”,首先使用“wildcard”

函数获取工作目录下的.c 文件列表;之后将列表中所有文件名的后缀.c 替换为.o 。这样

我们就可以得到在当前目录可生成的.o 文件列表。因此在一个目录下可以使用如下内

容的Makefile 来将工作目录下的所有的.c 文件进行编译并最后连接成为一个可执行文

件:

#sample Makefile

objects := $(patsubst %.c,%.o,$(wildcard *.c))

 

foo : $(objects)

cc -o foo $(objects)

函数patsubst:

模式替换函数 $(patsubst PATTERN,REPLACEMENT,TEXT)

搜索“TEXT”中以空格分开的单词,将否符合模式“TATTERN”替换

为“REPLACEMENT”。参数“PATTERN”中可以使用模式通配符“% ”,来代表一个单词中的若干字符。如果参数“REPLACEMENT”中也包含

一个“% ”,那么“REPLACEMENT”中的“% ”将是“TATTERN”中

的那个“% ”所代表的字符串。在“TATTERN”和“REPLACEMENT”

中,只有第一个“% ”被作为模式字符来处理,之后出现的不再作模式

字符(作为一个字符)。在参数中如果需要将第一个出现的“% ”作为字

符本身而不作为模式字符时,可使用反斜杠“\ ”进行转义处理(转义处

理的机制和使用静态模式的转义一致

返回值:替换后的新字符串。

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值