1、 Makefile
显式规则
隐晦规则
变量定义
文件指示
注释
命令要以Tab键开始
文件名使用Makefile
引用其他Makegile include xxx ;
include前不能是Tab,
会先寻找include 所指出的Makefile
拖延战术:
要使用再展开变量
顺序:
最终目标,其他目标都是这个目标连带出来的,
第一条规则中的目标将被确立为最终的目标
规则:
文件的依赖关系 如何生成文件 命令,目标文件
换行符 \
伪目标 .PHONY 伪目标同样也可成为依赖
rm命令前面加一个 小减号 - :在执行规则时也许删除某些文件时会出现问题,表示不要管问题继续做后面的事情。
clean:
放在文件的最后;开头是make的默认目标
书写规则包含:
1、依赖关系 2、生成目标的方法
通配符:* ? [...] ; *.c 用在规则,用在变量
转义字符: /*表示真正的*
~:~my/test则表示用户my的宿主目录下的test目录
关键字
文件搜寻
多目标
$@ $@表示模式%.d文件
静态模式
自动生成依赖性:自动找寻源文件中包含的头文件
编译器支持的 -M / GNU -MM
与Makefile联系:
把自动生成的依赖关系放在一个文件中
自动化变量
$< 依赖文件 $^
@ 不输出
make参数-s或--slient则是全面禁止命令的显示
make执行时,带入make参数-n或--just-print,那么其只是显示命令,但不会执行命令
书写命令:命令的开头必须以Tab键开头
命令执行:应该把这两条命令写在一行上,用分号分隔
SHELL
默认情况下使用UNIX的标准Shell /bin/sh来执行命令
命令出错 make会检测每个命令的返回码,命令退出码非零,那么make就会终止执行当前规则,将有可能终止所有规则的执行
忽略命令的出错:
Makefile的命令行前加一个减号-,Tab之后。
全局的办法:
make加上-i或是--ignore-errors参数,
make的参数的是-k或是--keep-going,
就终目该规则的执行,但继续执行其它规则
.IGNORE作为目标的,忽略所有错误
嵌套执行make:模块编译和分段编译
两个变量,
一个是SHELL,一个是MAKEFLAGS,总是要传递到下层Makefile中make -w或是--print-directory会在make的过程中输出一些信息,让你看到目前的工作目录-C参数来指定make下层Makefile时,-w会被自动打开的参数中有-s(--slient)或是--no-print-directory,那么,-w总是失效的。
定义命令包:
为这些相同的命令序列定义一个变量
变量
变量可以使用在目标,依赖目标,命令或是Makefile的其它部分中。
可以包含字符、数字,下划线(可以是数字开头)
不应该含有:、#、=或是空字符(空格、回车等)
声明时:给予初值
使用时:在变量名前加上$符号,用小括号( )或是大括号{ }把变量给包括起来。
真实的$字符,那么你需要用$$来表示。
变量中的变量:
变量是可以使用后面的变量来定义的
系统变量 MAKELEVEL
如果我们的make有一个嵌套执行的动作(参见前面的“嵌套使用make”),那么,这个变量会记录了我们的当前Makefile的调用层数
:=操作符
?=操作符
如果没有被定义过,那么变量的值就是操作符后面的哪个。
+=操作符
追加变量值
变量值的替换
$(var:a=b) 静态模式
把变量的值再当成变量
override 指示符
变量是通常make的命令行参数设置的,那么Makefile中对这个变量的赋值会被忽略。
多行变量
使用define关键字
环境变量
被覆盖,make指定了“-e”参数,那么,系统环境变量将覆盖Makefile中定义的变量
CFLAGS环境变量
目标变量
条件判断
ifeq ifneq ifdef ifndef
ifdef只是测试一个变量是否有值,其并不会把变量扩展到当前位置
使用函数
$标识 $(<function> <arguments>)
${<function> <arguments>}
函数和变量的括号最好一样
字符串处理函数
subst 字符串替换函数
patsubst 模式字符串替换函数,(通配符%,/%来表示真实含义的%字符)
strip 去空格函数
findstring 查找字符串函数
filter 过滤函数
filter-out 反过滤函数
sort 排序函数
word 取单词函数
wordlist 取单词串函数
words 单词个数统计函数
firstword 首单词函数
文件名操作函数
dir 取目录函数
nodir 取文件函数
suffix 取后缀函数
basename 取前缀函数
addsuffix 加后缀函数
addprefix 加前缀函数
join 连接函数
foreach 函数 用来做循环用的
if 函数
call 函数 唯一一个可以用来创建新的参数化的函数
origin 函数 告诉这个变量是哪里来的
shell 函数 和反引号`是相同的功能
$(error <text ...>) 控制make的函数
make 的运行
退出码
0 —— 成功执行。
1 —— 如果make运行时出现任何错误,其返回1。
2 —— 如果你使用了make的-q选项,并且make使得一些目标不需要更新,那么返回2。
指定Makefile
寻找GNUmakefile、makefile和Makefile
指定目标
在make命令后直接跟目标的名字就可以完成
环境变量MAKECMDGOALS
伪目标:
all
这个伪目标是所有目标的目标,其功能一般是编译所有的目标。
clean
这个伪目标功能是删除所有被make创建的文件。
install
这个伪目标功能是安装已编译好的程序,其实就是把目标执行文件拷贝到指定的目标中去。
print
这个伪目标的功能是例出改变过的源文件。
tar
这个伪目标功能是把源程序打包备份。也就是一个tar文件。
dist
这个伪目标功能是创建一个压缩文件,一般是把tar文件压成Z文件。或是gz文件。
TAGS
这个伪目标功能是更新所有的目标,以备完整地重编译使用。
check和test
这两个伪目标一般用来测试makefile的流程。
检查规则
不执行参数:
“-n”
“--just-print”
“--dry-run”
“--recon”
把目标文件的时间更新,但不更改目标文件
“-t”
“--touch”
找目标:
“-q”
question”
指定一个文件:
“-W <file>”
“--what-if=<file>”
“--assume-new=<file>”
“--new-file=<file>”
make的参数
忽略和其它版本make的兼容性:
“-b”
“-m”
认为所有的目标都需要更新(重编译)
“-B”
“--always-make”
指定读取makefile的目录。如果有多个-C参数,make的解释是后面的路径以前面的作为相对路径,并以最后的目录作为被指定目录
“—debug[=<options>]”
输出make的调试信息
a —— 也就是all,输出所有的调试信息。(会非常的多)
b —— 也就是basic,只输出简单的调试信息。即输出不需要重编译的目标。
v —— 也就是verbose,在b选项的级别之上。输出的信息包括哪个makefile被解析,不需要被重编译的依赖文件(或是依赖目标)等。
i —— 也就是implicit,输出所以的隐含规则。
j —— 也就是jobs,输出执行规则中命令的详细信息,如命令的PID、返回码等。
m —— 也就是makefile,输出make读取makefile,更新makefile,执行makefile的信息。
-d --debug
-e --environment-overrides 指明环境变量的值覆盖makefile中定义的变量的值
-f=<file> --file=<file> --makefile=<file> 指定需要执行的makefile
-h --help 显示帮助信息
-i --ignore-errors 在执行时忽略所有的错误
-I <dir> --include-dir=<dir> 指定一个被包含makefile的搜索目标。可以使用多个-I参数来指定多个目录。
-j [<jobsnum>] --jobs[=<jobsnum>] 同时运行命令的个数
-k --keep-going 出错也不停止运行。如果生成一个目标失败了,那么依赖于其上的目标就不会被执行了。
“-l <load>”
“--load-average[=<load]”
“—max-load[=<load>]” 指定make运行命令的负载。
“-n”
“--just-print”
“--dry-run”
“--recon” 仅输出执行过程中的命令序列,但并不执行。
“-o <file>”
“--old-file=<file>”
“--assume-old=<file>” 不重新生成的指定的<file>,即使这个目标的依赖文件新于它。
“-p”
“--print-data-base” 输出makefile中的所有数据,包括所有的规则和变量。
“-q”
“--question” 不运行命令,也不输出。仅仅是检查所指定的目标是否需要更新。如果是0则说明要更新,如果是2则说明有错误发生
“-r”
“--no-builtin-rules” 禁止make使用任何隐含规则。
“-R”
“--no-builtin-variabes” 禁止make使用任何作用于变量上的隐含规则。
“-s”
“--silent”
“--quiet” 在命令运行时不输出命令的输出。
“-S”
“--no-keep-going”
“--stop” 取消-k选项的作用。
“-t”
“--touch” 只是把目标的修改日期变成最新的,也就是阻止生成目标的命令运行
“-v”
“--version” make程序的版本、版权等关于make的信息
“-w”
“--print-directory” 输出运行makefile之前和之后的信息。这个参数对于跟踪嵌套式调用make时很有用
“--no-print-directory” 禁止-w选项。
“-W <file>”
“--what-if=<file>”
“--new-file=<file>”
“--assume-file=<file>” 假定目标<file>需要更新,如果和-n选项使用,那么这个参数会输出该目标更新时的运行动作。
如果没有-n那么就像运行UNIX的touch命令一样,使得<file>的修改时间为当前时间
“--warn-undefined-variables” 只要make发现有未定义的变量,那么就输出警告信息
隐含规则:
可以通过“模式规则”的方式写下自己的隐含规则。用“后缀规则”来定义隐含规则会有许多的限制
隐含规则库
也可以使用make的参数-r或--no-builtin-rules选项来取消所有的预设置的隐含规则
默认的后缀列表是:
.out, .a, .ln, .o, .c, .cc, .C, .p, .f, .F, .r, .y, .l, .s, .S, .mod, .sym, .def, .h, .info, .dvi, .tex, .texinfo, .texi, .txinfo, .w, .ch .web, .sh, .elc, .el
<n>.o的目标的依赖目标会自动推导为<n>.c
<n>.o 的目标的依赖目标会自动推导为<n>.s
隐含规则使用的变量 CC 命令相关的,CFLAGS参数相关的
关于命令的变量。
AR 函数库打包程序。默认命令是“ar”。
AS 汇编语言编译程序。默认命令是“as”。
CC C语言编译程序。默认命令是“cc”。
CXX C++语言编译程序。默认命令是“g++”。
CO 从 RCS文件中扩展文件程序。默认命令是“co”。
CPP C程序的预处理器(输出是标准输出设备)。默认命令是“$(CC) –E”。
FC Fortran 和 Ratfor 的编译器和预处理程序。默认命令是“f77”。
GET 从SCCS文件中扩展文件的程序。默认命令是“get”。
LEX Lex方法分析器程序(针对于C或Ratfor)。默认命令是“lex”。
PC Pascal语言编译程序。默认命令是“pc”。
YACC Yacc文法分析器(针对于C程序)。默认命令是“yacc”。
YACCR Yacc文法分析器(针对于Ratfor程序)。默认命令是“yacc –r”。
MAKEINFO 转换Texinfo源文件(.texi)到Info文件程序。默认命令是“makeinfo”。
TEX 从TeX源文件创建TeX DVI文件的程序。默认命令是“tex”。
TEXI2DVI 从Texinfo源文件创建军TeX DVI 文件的程序。默认命令是“texi2dvi”。
WEAVE 转换Web到TeX的程序。默认命令是“weave”。
CWEAVE 转换C Web 到 TeX的程序。默认命令是“cweave”。
TANGLE 转换Web到Pascal语言的程序。默认命令是“tangle”。
CTANGLE 转换C Web 到 C。默认命令是“ctangle”。
RM 删除文件命令。默认命令是“rm –f”。
关于命令参数的变量
下面的这些变量都是相关上面的命令的参数。如果没有指明其默认值,那么其默认值都是空。
ARFLAGS 函数库打包程序AR命令的参数。默认值是“rv”。
ASFLAGS 汇编语言编译器参数。(当明显地调用“.s”或“.S”文件时)。
CFLAGS C语言编译器参数。
CXXFLAGS C++语言编译器参数。
COFLAGS RCS命令参数。
CPPFLAGS C预处理器参数。( C 和 Fortran 编译器也会用到)。
FFLAGS Fortran语言编译器参数。
GFLAGS SCCS “get”程序参数。
LDFLAGS 链接器参数。(如:“ld”)
LFLAGS Lex文法分析器参数。
PFLAGS Pascal语言编译器参数。
RFLAGS Ratfor 程序的Fortran 编译器参数。
YFLAGS Yacc文法分析器参数。
隐含规则链:
中间目标:
和一般的目标有两个地方所不同:第一个不同是除非中间的目标不存在,才会引发中间规则。
第二个不同的是,只要目标成功产生,那么,产生最终目标过程中,所产生的中间目标文件会被以rm -f删除。
说明一个文件或是目标是中介目标,你可以使用伪目标.INTERMEDIATE来强制声明
阻止make自动删除中间目标,使用伪目标.SECONDARY”来强制声明。
禁止同一个目标出现两次或两次以上,可防止在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)
表示$@的目录部分(不以斜杠作为结尾),如果$@值是dir/foo.o,那么$(@D)就是dir,而如果$@中没有包含斜杠的话,其值就是.(当前目录)。
$(@F)
表示$@的文件部分,如果$@值是dir/foo.o,那么$(@F)就是foo.o,$(@F)相当于函数$(notdir $@)。
$(*D)
$(*F)
和上面所述的同理,也是取文件的目录部分和文件部分。对于上面的那个例子,$(*D)返回dir,而$(*F)返回foo
$(%D)
$(%F)
分别表示了函数包文件成员的目录部分和文件部分。这对于形同archive(member)形式的目标中的member中包含了不同的目录很有用。
$(<D)
$(<F)
分别表示依赖文件的目录部分和文件部分。
$(^D)
$(^F)
分别表示所有依赖文件的目录部分和文件部分。(无相同的)
$(+D)
$(+F)
分别表示所有依赖文件的目录部分和文件部分。(可以有相同的)
$(?D)
$(?F)
分别表示被更新的依赖文件的目录部分和文件部分。
模式的匹配:
茎
重载内建隐含规则
老式风格的"后缀规则"
“双后缀"和"单后缀”
伪目标.SUFFIXES,定义和删除
.SUFFIXES: # 删除默认的后缀
.SUFFIXES: .c .o .h # 定义自己的后缀
隐含规则搜索算法
$% 专属函数库文件的自动化变量
函数库文件的后缀规则
使用"后缀规则"和"隐含规则"来生成函数库打包文件
在进行函数库打包文件生成时,请小心使用make的并行机制(-j参数)
如果多个ar命令在同一时间运行在同一个函数库打包文件上,就很有可以损坏这个函数库文件。
2、读入所有的Makefile
第一阶段:
读入被include 的其他Makefile
初始化文件中的变量
推导隐晦规则,并分析所有规则
为所有的目标文件创建依赖关系链
第二阶段:
根据依赖关系,决定哪些目标要重新生成
执行生成命令