《跟我一起学makefile》学习笔记

趁着有点空余的时间,学习下makefile;


1. .PHONY表示后面的接着的prerequisites为一个伪目标文件;

2.make有自动推到功能,make看到一个[.o]文件,就会自动的把[.c]文件加在依赖关系中;

3.命令前加一个小减号,如-rm,的意思是:也许某些文件出现问题,但不要管,继续做后面的事;

4.Makefile 里包含五个东西:显式规则、隐晦规则、变量定义、文件指示和注释;

5.文件指示包含三个部分:一,在一个Makefile中引用另外一个Makefile,就像C语言中的include一样;二,根据某些情况制定Makefile中的有效部分,就像C语言中的预编译#if一样;三,定义一个多行的命令;

6.Makefile中的命令,必须要以[Tab]键开始;

7.默认情况下,make命令会在当前目录下按顺序找寻文件名"makefile"、"Makefile";

8.可以使用关键字include关键字包含其他的Makefile进来,多个用空格隔开。make执行include,没有指定路径则从当前目录开始,找不着时,如有“-I”或者“--include-dir”的参数,make会在所指定的目录下寻找;

9.一个情况下不要定义环境变量MAKEFILES;

10.makefile的执行步骤:

a.读入所有的Makefile。

b.读入被include的其他的Makefile.

d.初始化文件中的变量。

e.推导隐晦规则,并分析所有规则。

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

g.根据依赖关系,决定那些目标要重新生成。

h.执行生成命令。

10.规则语法:

target:prerequisites

command

...

或者

target:prerequisites ; command

command

...

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

12.规则中使用的通配符:

a."*",表示任意长度的字符串;

b."?",

c."[...]",

d."~"表示当前用户$HOME目录下

13.文件搜索

a,通过给"VPATH"的赋值,如VPATH=src:../headers,这样再当前目录没有找着时就到所设定的目录查找。

b.使用关键字"vpath",可以指定不同的文件在不同的搜索目录中,使用的方法有三种:

一,vpath<pattern>  <directories>为符合模式<pattern>的文件指定搜索目录<directories>;

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

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

可以使用vpath的语句来指定不同的搜索策略,如:

vapth %.c foo   vapth %.cfoo:bar 

vapth % blishvapth %balish

vapth % bar

14. 伪目标不是一个文件而是一个标签;

15.多目标,可以使用自动化变量"$@"实现多个目标;

16.静态模式可以更加容易地定义多目标的规则,可以让我们的规则变得更加的有弹性和灵活;

语法:

<targets...>:<target-pattern>:<prereq-pattern...>

<commands>

...

targets定义了一系列的目标文件,可以有通配符。是目标的一个集合。

target-pattern是指明了targets的模式,也就是目标集模式。

prereq-patterns是目标的依赖模式,它对target-pattern形成的模式再进行一次依赖目标的定义。

17.自动生成依赖性

c/c++编译器都支持一个“-M”的选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系;但如果使用的是

GNU的c/c++编译器,就得使用“-MM”参数,否则,该参数会把一些标准的头文件也包括进来。如:gcc -M main.c,

依赖性,GUN组织建议把编译器为每个源文件的自动生成的依赖关系放到一个文件中,为每一个“name.c”的文件都生成

一个“name.d”的Makefile文件,[.d]文件中就存放[.c]文件的依赖关系;

18.显示命令

当我们用“@”字符在命令行前,那么这个命令将不被make显示出来。

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

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

19.命令的执行

如果要让上一条命令的结果应用在下一条命令时,应该两条命令写在同一行,并用分号分开

20.命令出错

命令前添加“-”可以避免命令出错就终止makefile执行

给make加上“-i”或“--ignore-errors”参数 ,Makefile中所有命令都会忽略错误。

给make加上“k”或“--keep-going”,Makefile中如果有某个规则中的命令出错了,那么就终止该规则的执行,但继续执行其他的规则。

21.嵌套执行make

每个目录都有一个makefile,那么在总控的makefile可以这样写:

subsystem:

cd xxdir && $(MAKE)

总控的makefile的变量可以传递到下级的makefile中(如果你显式声明),但是不会覆盖下层的Makefile中所定义的变量,除非制订了“-e”参数。

需要传递变量到下级makefile,可以声明:export<variable...>

不想变量传递到下级的,则声明:unexport <variable...>

如果要传递所有的变量,只要一个export就行了

在“嵌套执行”中比较有用的参数,“w”或是“--print-directory”会在make的过程中输出一些信息,让我们能看到目前的工作的目录

当使用“-C”参数来指定make下层Makefile时,“-w”会自动被打开;但如果参数中有“-s”或者“--no-print-directory”,那么“-w”总是失效。


22.定义命令包

如果Makefile中出现了一些相同命令序列,那么我们可以为这些相同的命令序列顶一个变量。定义这种命令序列的语法以“define”开始,以“endef”结束;

23.使用变量

变量名字可以包含字符、数字、下划线,不能含有":"#""="或是空字符,变量大小写敏感

24.变量中的变量

变量是可以使用后面的变量来定义的,但要避免递归定义。因此我们可以使用“:=”操作符来定义变量,但是这种形式的

的定义不能使用后面的后面的变量,只能使用前面的已定义好了的变量。系统变量“MAKELEVEL”记录我们当前Makefile的调用层数

还有一个操作符的“?=”,如果变量之前没有被定义过,那么变量就去当前的值,否则这条语句啥都不做;

25.变量的高级用法

(1)变量值的替换,可以替换掉变量中的共有的部分,格式是:“$(var:a=b)”或是“${var:a=b}”,其的意思是将变量“var”中所有以“a”字符串

“结尾”的“a”替换成“b”字符串,这里的“结尾”是指“空格”或是“结束符”

(2)把变量的值再当成变量

26.追加变量值

我们可以使用“+=”操作付给变量追加值;

如果变量之前没有定义过,“+=”会自动变成“=”,如果前面有变量定义,“+=”会继承于前次操作的赋值符。如果前一次是“:=”,“+=”会以“:=”作为其赋值符

27.override 指示符

如果有变量是通常make的命令行参数设置的,那么Makefile中对这个变量的赋值会被忽略。但想要强制在Makefile

中设置这列参数的值,那么可以使用“override”指示符,语法如下:

override  <variable> = <value>

override <variable> := <value> 

追加

override <variable> +=<more text>

28.多行变量

使用define关键字来设置变量值可以有换行,define指示符后面跟的是变量的名字,二重起一行定义变量的值,以endef关键字结束。

29.环境变量

make运行时系统变量可以在make开始运行时被载入打扫Makefile文件中,但是如果makefile中已定义这个变量或这个变量

由make命令行带入,那么这个环境变量的值将被覆盖。(如make指定了“-e”参数的,系统的环境变量将覆盖Makefile中的定义的变量)

30.目标变量

为目标设置局部变量称为“Target-specific Variable”,作用的范围只爱这条规则以及连带规则中,

其语法是:

<target...>:<variable-assignment>

<target ...>: overide<variable-assignment>

31.模式变量

在GNU的make中,支持模式变量,可以给定一种“模式”,可以把变量定义在符合这种模式的所有目标上,如给所有以[.o]结尾的目标定义目标变量:%.o:CFLAG=-O

32.使用条件判断

语法:

<conditional-directive>

<text-if-true>

endif

以及:

<conditional-directive>

<text-if-true>

else

<text-if-true>

endif

<conditional-directive>表示条件关键字,如ifeq、ifneq,对应的语法:

ifeq(<arg1>,<arg2>)

ifeq '<arg1>'  '<arg2>'

ifeq "<arg1>"  "<arg2>"

<conditional-directive>、else和endif开始不能用[Tab]键作为开始

ifdef 语法:

ifdef <variable-name>

如果变量<variable-name>的值非空,那表达式为真,否则为假。注意,ifdef只是测试一个变量是否有值,其并不会把变量扩展到当前位置

类似还有ifndef

最后是不要把自动化变量放入条件表达式中,因为自动化变量是在运行时才有的。

33.函数调用的语法

函数调用累世变量的使用,也是以“$”来标识,语法如下:

$(<function> <arguments>)或 ${<function> <arguments>}

参数间以逗号分隔开

34.字符串处理函数

1)subst 字符串替换函数

$(subst <from>,<to>,<text>)

将字符串<text>中的<from>字符串替换成<to>;函数返回替换后的字符串

2) patsubst 模式字符串替换函数

$(patsubst <pattern>,<replacement>,<text>)

查找<text>中的单词(单词以空格、tab或回车 换行分隔)是否符合模式<pattern>,如果匹配的话,则以<replacement>替换;函数的返回被替换过后的字符串;

3)strip 去掉空格的函数

$(strip<string>) 

去掉<string>字符串中开头和结尾的空字符,返回被去掉空格的字符串值;

4)findstring 查找字符串函数

$(findstring <find>,<in>)

在字符串<in>中查找<find>字符串,如果找到,那么返回<find>,否则返回空字符串。

5)filter 过滤函数

$(filter <pattern...>,<text>)

以pattern模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词,可以有多个模式。返回符合模式<pattern>的字符串。

6)filter-out 反过滤函数

$(filter-out <pattern...>,<text>)

以<pattern>模式过滤<text>字符串中的单词,去除符合<pattern>的单词,可以有多个模式;返回不符合模式<pattern>的字符串;

7)sort 排序函数

$(sort<list>)

给字符串<list>中的单词排序(升序);返回排序后的字符串;注意该函数会去掉<list>中的相同的单词

8)word 取单词函数

$(word <n>,<text>)

取字符串<text>中第<n>个单词(从1开始);返回取得的单词;如果n大于字符串的单词数,则返回空字符串;

9)wordlist 取单词串函数

$(wordlist <s>,<e>,<text>)

从字符串<text>中取从<s>开始到<e>的单词串;返回从<s>到<e>的单词串;如果<s>比字符串的单词数要大,则返回空值;如果

<e>大于字符串的单词数,则返回从<s>开始,到<text>结束的单词串;

10)words 单词个数统计函数

$(words <text>)

统计<text>中的字符串中的单词个数;返回统计出的单词个数;

技巧:如果我们要取<text>中的最后一个单词,可以这样:$(word $(words <text>),<text>);

11)firstword 首单词函数

$(firstword <text>)

取字符串<text>中的第一个单词;返回字符串<text>的第一个单词。

35.文件名操作函数

1)dir 取目录函数

$(dir <names...>)

从文件名序列<names>中取出目录部分。目录部分是指最后一个反斜杠("/")之前的部分,如果没有反斜杠,那么返回“/”

返回文件名序列的目录部分

2) notdir 取出文件函数

$(notdir <names...>)

从文件名序列<names>中取出非目录部分,指的是最后一个反斜杠("/")之后的部分;返回文件名字;

3)suffix 取后缀函数

$(suffix <names...>)

从文件名字序列中取出各个文件的后缀;返回各个文件的后缀。如果没有文件后缀则返回空字符串

4)basename 取前缀的函数

$(basename <names...>)

从文件名字中取出文件名字的前缀;返回各个文件的前缀,如果没有前缀,则返回空字符串;

5) addsuffix 加后缀函数

$(addsuffix <suffix>,<names...>)

将后缀<suffix>加到<names>中的每个单词后面;返回加过后缀的文件名序列;

6) addprefix 加前缀函数

$(addprefix <prefix>,<names...>)

将前缀<prefix>加到<names>的每个单词的后面;返回加过前缀的文件名序列;

7) join 连接函数

$(join <list1>,<list2>)

将<list2>中的单词对应地加到<list1>的单词后面。如果<list1>的单词个数要比<list2>的多,<list1>中多出来的单词将保持原样;如果<list2>的单词个数要比<list1>

多,<list2>多出来的单词将被赋值到<list2>中,即添加到<list1>的末尾

36.foreach函数

语法:

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

把参数<list>中的单词逐一取出放到参数<var>所指定的变量中,然后再执行<text>所包含的表达式。每一次<text>会返回一个字符串,每次返回的字符串以空格分开;最后循环结束时,<text>所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值

因此,<var>最好是一个变量名,<list>可以使一个表达式,而<text>中一般会使用<var>整个参数来依次枚举<list>中的单词;<var>参数是一个临时的局部变量。

37.if函数

if函数类似ifeq函数,语法:

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

38. call 函数

该函数是唯一一个可以用来创建的参数化的函数。语法:

$(call<expression>,<parm1>,<parm2>,<parm3>...)

当make执行整个函数时,<expression>参数中的变量,如$(1),$(2)等,会被参数<parm1>,

<parm2>等依次替换;而<expression>的返回值就是call函数的返回值

39.origin函数

该函数可以告诉你这个变量是哪里来的,语法:

$(origin <variable>)

<variable>是变量名,不应该是引用,所以最好在<variable>中不要使用"$"字符,origin函数返回值说明这个变量的

说明这个变量的“出生情况”,其的返回值如下:

 1)"undefined",如果变量从来没有定义过,origin函数返回这个值;

 2)"default"  如果<variable>是一个默认定义的,比如"CC"这个变量;"environment"

3)"file"  如果变量被定义在makefile中;

4) “command line” 如果变量是被命令行定义的

5)"override" 如果变量是被override指示符重新定义的

6)"automatic" 如果变量是一个命令运行中的自动化变量

40.shell 函数

该函数的参数应该是操作系统Shell的命令,和反引号"`"是相同的功能。即,shell函数把执行操作系统命令后的

输出作为函数返回;注意这个函数对系统的性能是有害的,避免大量使用。

41.控制make的函数

make提供一些函数来控制make的运行:

1) error

$(error <text...>)

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

,所以你可以把其定义在某一个变量中,并在后续的脚本中使用这个变量也是可以的。

2)warning 

$(warning <text...>)

跟error函数类似,但它不会让make退出,只是输出一段警告信息并且make继续往下执行


make的运行

42.make的退出码

0--表示成功执行;

1--表示make运行时出现任何错误

2--如果使用了make的"-q"选项,并且make使得一些目标不需要更新


43.指定Makefile

GUN make寻找默认的Makefile的规则是在当前的目录下一次顺序找三个文件--“GUNmakefile”、"makefile"和"Makefile",

一旦找着就开始读取这个文件并执行,如果想要指定执行某个makefile,可以使用make的"-f"或是“--file”参数;

44.指定目标

一般的情况下,make的最终目标是makefile中的第一个目标,而其他目标一般是由这个目标连带出来。任何在makefile中的目标都可以被指定终极目标,但是除了以"-"打头,或是包含了"="的目标。有一个make的环境变量叫"MAKECMDGOALS",这个变量中会存放你所指定的终极目标的列表,如果没有指定目标这个变量为空值

标准的makefile书写规则:

1)"all"

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

2)"clean"

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

3)"install"

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

4)"print"

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

5)"tar"

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

6)"dist"

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

7)"TAGS"

更新所有的目标,以备完整地重编译使用;

8)"check"和"test"

用来测试makefile的流程的

45.make参数

1)

“-b”、"-m"

忽略和其他版本make的兼容性

2)

“-B”、"--always-make"

认为所有的目标都需要更新

3)

“-C<dir>”、"--directory=<dir>"

指定读取makefile的目录,如果有多个"-C"参数,make的解释是后面的路径以前面的作为相对路径,并以最后的目录作为指定的目录

4)

"--debug[=<options>]"

输出make的调试信息,option的取值:

a--all,输出所有的调试信息;

b--base 只输出简单的调试信息;

v--verbose,在b选项的级别之上,输出包括哪个makefile被解析,不需要被重编译的依赖文件等;

i--implicit ,输出所有的隐含规则;

j--jobs,输出执行规则中命令的详细信息,如命令的PID,返回码等

m--makefile 输出make读取makefile,更新makefile,执行makefile的信息

5)"-d"

相当于"--debug=a";

6)"-e"、"--environment-overrides"

指明环境变量的值覆盖makefile中定义的变量的值

7)“-f<file>”、"--file=<file>"、"--makefile=<file>"

指定需要执行的makefile

8)"-h"、"--help"

显示帮助信息

9)"-i"、"--ignore-errors"

在执行时忽略所有的错误

10)“-I<dir>”、"--include-dir=<dir>"

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

11)"-j[<jobsnum>]"、"--jobs[=<jobsnum>]"

指定同时运行命令的个数,如果没有这个参数,make运行命令时能运行多少就运行多少;如有多个"-j",那么仅最后一个"-j"才是有效的;

12)"-k"、"--keep-going"

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

13)"l<load>"、"--load-average[=<load]"、"--max-load[=<load]"

指定make运行命令的负载

14)"-n"、"--just-print"、"--dry-run"、"recon"

仅输出执行过程中的命令序列,但不执行.

15)"-o"、"--old-file=<file>"、"--assume-old=<file>"

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

16)"-p"、"--print-data-base"

输出makefile中的所有数据,包括所有的规则和变量。这个输出的信息量比较大,可以根据需要来输出对应的信息:

如果只是想输出信息而不想执行makefile,可以使用"make -qp"命令;如果想查看执行makefile前的预设变量和规则,可以使用"make -p -f /dev/null"

参数输出的信息会包含这你的makefile文件的文件名和行号

17)"-q"或"--question"

不运行命令,也不输出。仅仅是检查所指定的目标是否需要更新。如果是0则说明要更新,如果是2则说明有错误发生;

18)"-r"、"--no-builtin-rules"

禁止make使用任何隐含规则

19)“-R”、“--no-builtin-variables”

禁止make使用任何作用于变量上的隐含规则

20)"-s"、""、“--quilet”

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

21)"-S"、"--no-keep-going"、"--stop"

取消"-k"选项的作用,因为有些时候,make的选项是从环境变量"MAKEFLAGS"中继承下来的。所以你可以在命令行中使用这个参数来让环境变量中的"-k"选项失效

22)"-t"、"--touch"

只是把目标文件的时间最新;

23)"-v"、"--version"

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

23)"-w"、"--print-directory"

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

24)"-W<file>"、"--what-if=<file>"、"--assume-new=<file>"、"--new-file=<file>"

假定目标<file>需要更新,如果和"-n"选项使用,那么这个参数会输出该目标更新时的运行动作。如果没有"-n",就使得<file>的修改时间为当前时间

25)"--warn-undefined-variables"

只要make发现有未定义的变量,那么就输出警告信息


隐含规则

44.编译C程序的隐含规则

"<n>.o"的目标的依赖目标会自动推导为"<n>.c",并且生成命令是"$(CC) -c $(CPPFLAGS) $(CFLAGS)"

45.编译C++程序的隐含规则

"<n>.o"的目标的依赖目标会自动推导为"<n>.cc"或是"<n>.C",并且其生成命令是"$(CXX) -c $(CPPFLAGS) $(CFLAGS)";

46.汇编和汇编预处理的隐含规则

"<n>.o"的目标的依赖目标会自动推导为"<n>.s",默认使用编译项"as",并且其生成命令是:"$(AS) $(ASFLAGS)"."<n>.s"的目标的依赖目标会自动推导为"<n>.S",默认使用

C预编译器"cpp",并且其生成命令是:“$(AS) $(ASFLAGS)”;

47.链接Object文件的隐含规则

"<n>"目标依赖于"<n>.o",通过运行C的编译器来运行链接程序生成(一般是"ld"),其生成命令是:“$(CC) $(LDFLAGS) <n>.o $(LOADLIBES) $(LDLIBS)”。这个规则对于只有一个源文件的工程有效,同时也对多个Object文件(有不同源文件生成)的也有效

隐含规则使用的变量

48.关于命令的变量

1)AR

函数库打包程序。默认命令是"ar".

2)AS

汇编语言编译程序。默认命令是"as";

3)CC

C语言编译程序。默认命令是"cc"

4)CXX

C++ 语言编译程序,默认命令是"g++";

5)CO

从RCS文件中扩展文件程序。默认命令是"co"

6)CPP

c程序的预处理器(输出是标准输出设备)。默认命令是"$(CC)"

7)FC

Fortran和Ratfor 的编译器和预处理程序。默认命令是"f77"

8)GET

从SCCS文件中扩展文件的程序,默认命令是"get";

9)LEX

Lex方法分析器程序。默认命令是"lex"

10)PC

Pascal语言编译程序。默认命令是"pc"

11)YACC

Yacc文法分析器(针对C程序),默认命令是"yacc "

12)YACCR

Yacc文法分析器(针对Ratfor程序),默认命令是"yacc-r"

13)MAKEINFO

转换Texinfo源文件(.texi)到Info文件程序。默认命令”makeinfo“

14)TEX

从Tex原文件创建TexDVI文件的程序,默认是命令是:”tex“

15)TEXI2DVI

从Texinfo原文件创建TexDVI文件的程序,默认是命令是:”texi2dvi

16)WEAVE

转换Web到TeX的程序。默认命令是"weave"

17)WEAVE

转换C Web到TeX的程序。默认命令是"cweave"


18)TANGLE

转换Web到Pascal语言的程序。默认命令是:"tangle"

19)CTANGLE

转换 C Web到C 。默认命令是:"ctangle";

20)RM

删除文件命令。默认命令是:"rm -f";

49.关于命令参数的变量

如果没有指定默认值,则取空

1)ARFLAGS

函数库打包程序AR命令的参数。默认值是"rv".

2)ASFLAGS

汇编语言编译器参数(当明显地调用".s"或".S"文件时)

3)CFLAGS

C语言编译器参数

4)CXXFLAGS

C++语言编译器参数

5)COFLAGS

RCS命令参数

6)CPPFLAGS

C预处理参数

7)FFLAGS

Fortran语言编译器参数。

8)GFLAGS

SCCS "get"程序参数

9)LDFLAGS

链接器参数

10)LFLAGS

Lex 文法分析器参数

11)PFLAGS

Pascal 语言编译器参数

12)RFLAGS

Ratfor 程序的Fortran编译器参数。

13)YFLAGS

Yacc文法分析其参数

50)隐含规则链

1)隐含规则链是为生成最终目标文件的中间所推导一系列的规则,隐含规则所生成的中间目标最终会被以"rm -f" 删除。

2)一个被makefile指定目标或是依赖目标的文件不能被当作中介,但可以使用伪目标".INTERMEDIATE"来强制声明一个文件或是目标是中介目标

3)也可以阻止make自动删除中间目标,需要用".SECONDARY"来强制声明。还可以把目标以模式的方式来指定(如:%.o)成伪目标".PRECIOUS"的依赖目标,以保存被隐含规则所生成的中间文件。

4)在"隐含规则链"中,禁止同一个目标出现两次或两次以上,来防止make自动推导时出现无限递归的情况;

5)Make会优化一些特殊的隐含规则,而不生成中间文件。

51)定义模式规则

模式规则中,至少在规则的目标定义中要包含"%",否则就是要一般的规则。规则"%"的展开发生在变量和函数的展开后,变量和函数的展开发生在make

载入Makefile时,而模式规则中的"%"则发生在运行时。

52)自动化变量

目标和依赖文件都是一系列的文件,在每次的对模式规则的解析时,都会是不同的目标和依赖文件

把模式中所定义的一系列的文件自动地挨个取出,直至所有的符合模式的文件都取完。

1)$@

表示规则中的目标文件集;

2)$%

仅当目标是函数库文件中,表示规则中的目标成员名

3)$<

依赖目标中的第一个目标名字,如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。

4)$?

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

5)$^

所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复,这个变量会去除重复的依赖目标,只保留一份。

6)$+

所有依赖目标的集合。只是它不去除重复的依赖目标。

7)$*

表示目标模式中"%"及其之前的部分,如果目标没有模式的定义,那么"$*"不能被推到出,但是,如果目标文件的后缀是make所识别,那么"$*"是出来后缀的那一部分,如果不能识别,则"$*"是空值。兼容性问题,尽量避免使用。


以上7个自动化变量还可以取得文件的目录名或是在当前目录下的符合模式的文件名

8)$(@D)

表示“$@”的目录部分(不以斜杠作为结尾),如果"$@"中没有包含斜杠的话,其值就是"."

9)(@F)

表示"$@"的文件部分,相当于函数"$(notdir $@)"

10)$(*D)、$(*F)

与上面所述的同理,也是取文件的目录部分和文件部分。

11)$(%D)、$(%F)

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

12)$(<D)、$(<F)

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

13)$(^D)、$(^F)

分别取所有依赖文件的目录部分和文件部分。

14)$(+D)、$(+F)

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

15)$(?D)、$(?F)

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


53)模式的匹配

一个目标的模式有一个前缀或是后缀的"%",传递的部分是“%”所匹配的内容

当一个模式匹配包含有斜杠的文件时,那么在进行模式匹配时,目录部分会首先别移开,然后进行匹配,成功后,在把目录加回去。

54)重载内建隐含规则

在定义目标的依赖后面重写command;

55)老式分格的"后缀规则"

有"双后缀"和"单后缀"

双后缀分别为依赖文件的后缀和目标文件后缀;后缀规则不允许任何的依赖文件。

让make知道一些特定的后缀,我们可以使用伪目标".SUFFIXES"来定义或是删除

删除默认的后缀: .SUFFIXES:

make的参数"-r"或是"-no-builtin-rules"也会使得默认的后缀列表为空,而变量"SUFFIXE"别用来定义默认的后缀列表,但不要改变变量"SUFFIXE"的值

56)隐含规则搜索算法

1.把T的目录部分分离出来,叫D,而剩余部分叫N;

2.创建所有匹配于T或是N的模式规则列表;

3.如果在模式规则列表中有匹配所有文件的模式,如"%",那么从列表中移除其他的模式;

4.移除列表中没有命令的规则;

5.对于第一个在列表中的模式规则:

1)推导其"茎"S,S应该是T或是N匹配于模式中"%"非空的部分;

2)计算依赖文件。把依赖文件中的"%"都替换成"茎"S。如果目标模式中没有包含斜框字符,而把D加在第一个依赖文件的开头。

3)测试是否所有的依赖文件都存在或是理当存在。(如果有一个文件被定义成另外一个规则的目标文件,或是一个显式规则的依赖文件,那么这个文件叫"理当存在")

4)如果所有依赖文件存在或者理当存在,或是就没有依赖文件。那么这条规则将被采用,退出该算法。

6.如果经过第五步,没有模式规则被找到,那么就做更进一步的搜索。对于存在列表中的第一个规则模式:

1)如果规则是终止规则,那就忽略它,继续下一条模式规则;

2)计算依赖文件;

3)测试多有的依赖文件是否存在或是理当存在;

4)对于不存在的依赖文件,递归调用这个算法查找他是否可以被隐含规则找到。

5)如果所有的依赖文件存在或是理当存在,或是根本没有依赖文件。那么这条规则被采用,退出该算法。

7.如果没有隐含规则可以使用,查看".DEFAULT"规则,如果有,采用,把".DEFAULT"的命令给T使用。

一旦规则被找到,就会执行其相当的命令,而此时,我们的自动化变量的值才会生成。


使用make更新函数库文件

57.函数库文件的成员

一个函数库文件由多个文件组成,可以通过以下格式指定函数库文件及其组成:

archive(menber);

这个不是命令,而一个目标和依赖的定义,这种用法基本上就是为了"ar"命令来服务的

58.函数库成员的隐含规则

当make搜索一个目标的隐含规则时,如果这个目标是"a(m)"形式的,其会把目标变成"(m)".如果我们的成员是"%.o"的模式定义,并且如果我们使用"make foo.a(bar.o)"

的形式调用Makefile时,隐含规则会去找"bar.o"的规则,如果没有地定义bar.o的规则,那么内建隐含规则生效,make会去找bar.c文件来生成bar.o;

59.函数库文件的后缀规则

可以使用"后缀规则"和"隐含规则"来生成函数库打包文件,如:

.c.a:

$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o

$(AR) r $@ $*.o

$(RM) $*.O

60)函数打包注意事项

在进行函数库打包文件生成时,小心使用make的并行机制("-j"参数)。如果多个ar命令在同一时间

运行在同一个函数库打包文件上,就很有可能损坏这个函数库文件,尽量不要使用"-j"这个参数






























你可能感兴趣的:(《跟我一起学makefile》学习笔记)