第一章 概述
1. 首先区别make 与 Makefile
2. make的整个过程包括编译和链接
输入源文件,经过编译,生成中间文件,即.o文件
输入中间文件,即.o文件,经过链接,生成exe文件或静态、动态库。
3. Makefile文件是被解释执行的,可以调用操作系统的一些函数。
第二章 Makefile介绍
1. 最基本规则
target ... : prerequisites ...
command
其中target可以是.o(编译过程输入),exe(连接过程输出),标签即伪目标
prerequisits:所需文件
command:相关操作
2. target为伪目标是非常常用的技术
clean :
rm edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
clean不是一个文件,它只不过是一个动作名字
其冒号后什么也没有,那么,make就不会自动去找文件的依赖性
3. make的默认工作流程,即只输入make的工作流程(区别输入make clean等)
1) 当前目录查找Makefile或makefile
2) 查找Makefile文件的第一个目标文件,即确定了该Makefile的唯一任务,只要完成该任务即可退出,其它都是浮云。
3) 反复生成目标文件、查找依赖文件、生成依赖文件这个类似栈的过程,直到第一个目标出现,即生成。
4) 最后,很显然,2中的clean并非第一个target,所以需要显示执行,如make clean
4. 变量的声明与使用
声明:Variant = value
使用:$( Variant)
5. make的自动规则
例如:main.o:main.c main.h
make在遇到.o,会自动依赖同名的.c文件,所以上句可简写成
main.o:main.h
6. 另类风格
make支持a.o b.o c.o:abc.h,但不建议
7. 一般来说,make会以UNIX的标准Shell,也就是/bin/sh来执行命令
第三章 Makefie总述
1. make默认寻找当前目录的makefile或Makefile,当然你也可以自己指定
make –f(--f) Make.Other
2. 引用其它的Makefile
语法:include <filename> 其中filename支持包含绝对路径或相对路径
3. include 的搜索过程
1) 制定了相对路径或绝对路径,直接取
2) 否则
i. 当前目录
ii. make 选项 –I或—include-dir所指的参数
4. MAKEFILES参数,作用类似include,只是它包含进的文件中的target不起作用。
第四章 书写规则
1. target : prerequisites
command
…
1) 多个target以空格隔开
2) 如果prerequisites与target不在同一行出现,需要以TAB为行首
3) Command支持shell中的命令,毕竟是由/bin/sh来执行命令
2. 在文件名中使用通配符:* ? [0-9]
波浪号~使用:“~/test”,这就表示当前用户的$HOME目录。
“~zhangsan/test”则表示用户zhangsan的宿主目录下的test目录
3. 文件搜索功能
就像C语言中的#include使用的搜索目录功能
1) Makefile的特殊变量VPATH
VPATH = src:../header
冒号用于多个目录的分隔符。
2) 关键字vpath的使用
i. vpath <pattern> <directories>
为符合模式<pattern>的文件指定搜索目录<directories>。
ii. vpath <pattern>
清除符合模式<pattern>的文件的搜索目录。
iii. vpath
清除所有已被设置好了的文件搜索目录。
iv. 举例:vpath %.c foo:bar 冒号用于多个目录的分隔符
vpath % blish
4. 伪目标
1) clean:
rm –rf *.o
clean就是伪目标,但clean不能当前Makefile所在目录中的其它文件重名,为了避免这种顾忌,可以使用.PHONY声明伪目标
.PHONY clean
clean:
rm –rf *.o
2) 通常一个Makefile只能生成一个目标
i. 伪目标应用案例一:生成多个目标
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
ii. 类似实现子函数的伪目标应用案例
.PHONY: cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff
5. 多目标
一个Makefile只能有一个目标,即第一个出现的目标,与即将要说的多目标有冲突吗?
可以理解为没有冲突,因为第一个出现的目标,可以于其它目标并列存在,即target中用空格隔开目标。
自动化变量“$@”通常是为多目标服务的。
6. 静态模式
它是相对接下来要讲到的自动生成依赖性而言的,它侧重于手动编写,多用于多目标。
<targets ...>: <target-pattern>: <prereq-patterns ...>
<commands>
....
targets定义了一系列的目标文件,可以有通配符。是目标的一个集合。
target-parrtern是指明了targets的模式,也就是的目标集模式。
prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义。
7. <targets ...>: <target-pattern>: <prereq-patterns ...>
<commands>
....
targets定义了一系列的目标文件,可以有通配符。是目标的一个集合。
target-parrtern是指明了targets的模式,也就是的目标集模式。
prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义。
objects = foo.o bar.o foo.c bar.c
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
%.o即过滤objects中后缀为.o的目标
%.c的%指的,上一级模式匹配(即%.o)中%符号所匹配到的字符串,即foo与bar
8. 自动生成依赖性
1) 如果我们的main.c中有一句“#include "defs.h"”,那么我们的依赖关系应该是:
main.o : main.c defs.h
再如果在main.c中又需要引入”consts.h”,那么我们又需要修改Makefile
main.o : main.c defs.h consts.h
2) 大部分C/C++编译器,支持-M选项,自动搜索.c文件中依赖的头文件
GNU的C/C++编译器,你得用“-MM”参数,因为“-M”参数会把一些标准库的头文件也包含进来
3) 问题来了,编译器是编译器,Makefile是Makefile,Makefile如何利用编译器生成的依赖关系呢?
编译器为每一个源文件生成相应的依赖关系临时文件,该文件属于Makefile文件,然后主Makefile包含这个Makefile即可获得依赖关系。
第五章 书写命令
1. 命令显示
1) 命令的开头必须以[Tab]键开头
2) make通常会在屏幕上显示当前执行的命令行。
但在命令前使用“@”字符,那么这个命令将不被显示出来。
@echo 正在编译XXX模块......
3) make参数“-n”或“--just-print”,只显示命令,但不执行命令,常用于调试Makefile,查看命令执行过程与顺序。
4) make参数“-s”或“--slient”则是全面禁止命令的显示
2. 命令执行
1) 如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令。
2) exec:
cd /home/hchen
pwd
假设当前目录是根目录:/
结束输出根目录:/
3) exec:
cd /home/ zhangsan; pwd
假设当前目录是根目录:/
结束输出根目录:/home/zhangsan
3. 命令出错
1) 命令运行完后,make会检测每个命令的返回码
如果成功,继续执行
如果失败,可能终止执行
2) 可在Makefile的命令前加一个减号“-”(在Tab键之后),忽略返回码的判断
3) make加上“-i”或是“--ignore-errors”参数
如果一个规则是以“.IGNORE”作为目标的
make的参数的是“-k”或是“--keep-going”
以上具体请参阅Page 27
4. 嵌套执行make
1) 在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile
2) 例如,我们有一个子目录叫subdir,这个目录下有个Makefile文件,来指明了这个目录下文件的编译规则。那么我们总控的Makefile可以这样书写:
subsystem:
cd subdir && $(MAKE)
其等价于:
subsystem:
$(MAKE) -C subdir
这两个例子的意思都是先进入“subdir”目录,然后执行make命令。
这里为什么使用$(MAKE),而不是make命令呢?因为make通常需要一些参数,所以定义成一个变量比较利于维护。
3) 嵌套make中最关心的问题通常是参数的传递
与C语言类似,内层可以访问外层变量,如果变量名相同,则隐藏或者说替换。
4) 上层Makefile如何向下层Makefile传递参数呢?
export <variable ...>
export variable = value
export variable := value
export variable += value
取消参数传递使用:unexport <variable ...>
5) 需要注意的是,有两个变量,一个是SHELL,一个是MAKEFLAGS,这两个变量不管你是否export,其总是要传递到下层Makefile中,特别是MAKEFILES变量,其中包含了make的参数信息,如果我们执行“总控Makefile”时有make参数或是在上层Makefile中定义了这个变量,那么MAKEFILES变量将会是这些参数,并会传递到下层Makefile中,这是一个系统级的环境变量。
6) 如果你不想往下层传递参数,你可以:
subsystem:
cd subdir && $(MAKE) MAKEFLAGS=
7) “嵌套执行”中比较有用的make参数,“-w”或是“--print-directory”
使用“make -w”来执行,那么当进入该目录时,我们会看到:
make: Entering directory `/home/hchen/gnu/make'.
而在完成下层make后离开目录时,我们会看到:
make: Leaving directory `/home/hchen/gnu/make
当你使用“-C”参数来指定make下层Makefile时,“-w”会被自动打开的
5. 定义命令包
1) define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef
2) “run-yacc”是这个命令包的名字,其不要和Makefile中的变量重名
3) foo.c : foo.y
$(run-yacc)
第六章 使用变量
在Makefile中的定义的变量,就像是C/C++语言中的宏一样, 与C/C++所不同的是,你可以在Makefile中改变其值。
1. 变量在声明时需要给予初值,使用时加“$”符号,但最好用小括号“()”或是大括号“{}”把变量给包括起来,给变量加上括号完全是为了更加安全地使用这个变量。如果你要使用真实的“$”字符,那么你需要用“$$”来表示。
2. 变量中的变量
1) 使用“=”的变量
foo = $(bar)
bar = Hello
特点:变量不一定非要是已定义好的值
在宏替换过程中,遇到变量依次展开,这样的话就支持递归展开。
注意:递归通常发生在取值的时候,即使用$符号时才会发生
所以区别y = z与y = $(z),然后x = y。
2) 使用“:=”的变量
x := foo
y := $(x) bar
x := later
最终x值为later,y值为foo bar
y := $(x) bar
x := foo
最终x值为foo,y值为bar,因为x未定,即为空
特点:前面的变量不能使用后面的变量
3) 定义空格变量技术
nullstring :=
space := $(nullstring) # end of the line
nullstring是一个Empty变量,其中什么也没有,而我们的space的值是一个空格。
4) 使用“?=”的变量
FOO ?= bar
如果定义了FOO,FOO的值就是“bar”,否则什么也不做
3. 变量的高级用法
1) 第一种是变量值的替换
例如:y := a.o b.o,变量y的值由a.o b.o变成了a.c b.c。
注意:y的值通常是不变的,通常是将变化后的值放入(临时)变量中
foo := a.o b.o c.o
bar := $(foo:.o=.c)
意思是: 以“o”字串“结尾替换成以“c”字串结尾的
最终bar值为a.c b.c c.c
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
使用通配符的方式,可参考静态模式
2) 第二种高级用法是——“把变量的值再当成变量”
first_second = Hello
a = first
b = second
all = $($a_$b)
最终all的值就是“Hello”。
4. 追加变量值 -- “+=”操作符
1) 举例:objects = main.o foo.o bar.o utils.o
objects += another.o
2) 如果变量之前没有定义过,那么,“+=”会自动变成“=”
3) 如果前面有变量定义,那么“+=”会继承于前次操作的赋值符 ***
5. override指示符
如果有变量是通常make的命令行参数设置的,那么Makefile中对这个变量的赋值会被忽略。
如果你想在Makefile中设置这类参数的值,那么,你可以使用“override”指示符。其语法是:
override <variable> = <value>
override <variable> := <value>
当然,你还可以追加:
override <variable> += <more text>
还有多行变量
override define foo
bar
endef
6. 多行变量
define two-lines
echo foo
echo $(bar)
endef
其工作方式和“=”操作符一样
因为命令需要以[Tab]键开头,所以如果你用define定义的命令变量中没有以[Tab]键开头,那么make就不会把其认为是命令
7. 环境变量
make运行时的系统环境变量可以在make开始运行时被载入到Makefile文件中,但是 如果Makefile中已定义了这个变量,或是这个变量由make命令行带入,那么系统的环境变量的值将被覆盖。(如果make指定了“-e”参数,那么,系统环境变量将覆盖Makefile中定义的变量)
8. 目标变量
之前定义过通常的全局变量,它的作用域通常是整个文件。
目标变量,即它的作用域是生成该目标的整个过程中。
语法:<target ...> : <variable-assignment>
<target ...> : overide <variable-assignment>
<variable-assignment>可以是各种赋值表达式,如“=”、“:=”、“+=”或是“?=”
9. 模式变量
模式变量时基于目标变量之上的功能。
即它为目标变量应用于多个目标成为了可能。
<pattern ...> : <variable-assignment>
<pattern ...> : override <variable-assignment>
第七章 使用条件判断
1. 语法:
#ifeq、#ifneq、#ifdef、#ifndef
#else
#endif
2. 特别注意的是,make是在读取Makefile时就计算条件表达式的值,并根据条件表达式的值来选择语句,所以,你最好不要把自动化变量(如“$@”等)放入条件表达式中,因为自动化变量是在运行时才有的。
第八章 使用函数
1. 函数调用,很像变量的使用,也是以“$”来标识的,其语法如下
$(<function> <arguments>) 或是 ${<function> <arguments>}
<function>就是函数名,make支持的函数不多。<arguments>是函数的参数,参数间以逗号“,”分隔,而函数名和参数之间以“空格”分隔。
2. 字符串处理函数
1) $(subst <from>,<to>,<text>)
名称:字符串替换函数——subst。
功能:把字串<text>中的<from>字符串替换成<to>。
返回:函数返回被替换过后的字符串。
2) $(patsubst <pattern>,<replacement>,<text>)
名称:模式字符串替换函数——patsubst。
功能:查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式<pattern>,如果匹配的话,则以<replacement>替换。这里,<pattern>可以包括通配符“%”,表示任意长度的字串。如果<replacement>中也包含“%”,那么,<replacement>中的这个“%”将是<pattern>中的那个“%”所代表的字串。(可以用“\”来转义,以“\%”来表示真实含义的“%”字符)
返回:函数返回被替换过后的字符串。
3) $(strip <string>)
名称:去空格函数——strip。
功能:去掉<string>字串中开头和结尾的空字符。 中间空格不处理。
返回:返回被去掉空格的字符串值。
4) $(findstring <find>,<in>)
名称:查找字符串函数——findstring。
功能:在字串<in>中查找<find>字串。
返回:如果找到,那么返回<find>,否则返回空字符串。
5) $(filter <pattern...>,<text>)
名称:过滤函数——filter。 功能:以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词。可以有多个模式。
返回:返回符合模式<pattern>的字串。
6) $(filter-out <pattern...>,<text>)
与5)功能相反
7) $(sort <list>)
名称:排序函数——sort。
功能:给字符串<list>中的单词排序(升序)。
返回:返回排序后的字符串。
8) $(word <n>,<text>)
名称:取单词函数——word。
功能:取字符串<text>中第<n>个单词。(从一开始)
返回:返回字符串<text>中第<n>个单词。如果<n>比<text>中的单词数要大,那么返回空字符串。
9) $(wordlist <s>,<e>,<text>)
名称:取单词串函数——wordlist。
功能:从字符串<text>中取从<s>开始到<e>的单词串。<s>和<e>是一个数字。
返回:返回字符串<text>中从<s>到<e>的单词字串。如果<s>比<text>中的单词数要大,那么返回空字符串。如果<e>大于<text>的单词数,那么返回从<s>开始,到<text>结束的单词串。
10) $(words <text>)
名称:单词个数统计函数——words。
功能:统计<text>中字符串中的单词个数。
返回:返回<text>中的单词数。
11) $(firstword <text>)
名称:首单词函数——firstword。
功能:取字符串<text>中的第一个单词。
返回:返回字符串<text>的第一个单词。
3. 文件名操作函数
1) $(dir <names...>)
名称:取目录函数——dir。
功能:从文件名序列<names>中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。
返回:返回文件名序列<names>的目录部分
2) $(notdir <names...>)
名称:取文件函数——notdir。
功能:从文件名序列<names>中取出非目录部分。非目录部分是指最后一个反斜杠(“ /”)之后的部分。
返回:返回文件名序列<names>的非目录部分。
3) $(suffix <names...>)
名称:取后缀函数——suffix。
功能:从文件名序列<names>中取出各个文件名的后缀。
返回:返回文件名序列<names>的后缀序列,如果文件没有后缀,则返回空字串
4) $(basename <names...>)
名称:取前缀函数——basename。
功能:从文件名序列<names>中取出各个文件名的前缀部分。
返回:返回文件名序列<names>的前缀序列,如果文件没有前缀,则返回空字串。
5) $(addsuffix <suffix>,<names...>)
名称:加后缀函数——addsuffix。
功能:把后缀<suffix>加到<names>中的每个单词后面。
返回:返回加过后缀的文件名序列。
6) $(addprefix <prefix>,<names...>)
名称:加前缀函数——addprefix。
功能:把前缀<prefix>加到<names>中的每个单词后面。
返回:返回加过前缀的文件名序列。
7) $(join <list1>,<list2>)
名称:连接函数——join。
功能:把<list2>中的单词对应地加到<list1>的单词后面。如果<list1>的单词个数要比<list2>的多,那么,<list1>中的多出来的单词将保持原样。如果<list2>的单词个数要比<list1>多,那么,<list2>多出来的单词将被复制到<list1>中。
返回:返回连接过后的字符串。
4. foreach
$(foreach <var>,<list>,<text>)
把参数<list>中的单词逐一取出放到参数<var>所指定的变量中,然后再执行<text>所包含的表达式。
names := a b c d
files := $(foreach n,$(names),$(n).o)
$(files)的值是“a.o b.o c.o d.o”
5. if
$(if <condition>,<then-part>)
$(if <condition>,<then-part>,<else-part>)
<condition>参数是if的表达式,如果其返回的为非空字符串,才返回真
6. call
$(call <expression>,<parm1>,<parm2>,<parm3>...)
当make执行这个函数时,<expression>参数中的变量,如$(1),$(2),$(3)等,会被参数<parm1>,<parm2>,<parm3>依次取代。
7. origin
$(origin <variable>)
1) 返回undefined
2) 返回default
可能指环境变量、CC、MAKEFLAGS等
3) 返回command line
执行make命令时输入的变量
4) 返回override
重定义的变量
5) 返回automatic
命令行运行中的自动化变量
8. shell函数
它和反引号“`”是相同的功能
contents := $(shell cat foo)
files := $(shell echo *.c)
9. 控制make的函数
1) error
$(error <text ...>) 产生一个致命的错误,<text ...>是错误信息。
将中止make命令
2) $(warning <text ...>)同error函数,只是它并不会让make退出
第九章 make的运行
1. make的退出码
返回0表示成功,返回1表示失败,还有返回2的与-q选项有关
2. 指定Makefile
执行make时,make会依次搜索“GNUmakefile”、“makefile”和“Makefile”
如何指定makefile的文件呢?
make –f hchen.mk
3. 指定目标
1) 例如常见的make clean
2) 任何在makefile中的目标都可以被指定成终极目标,但是除了以“-”打头,或是包含了“=”的目标,因为有这些字符的目标,会被解析成命令行参数或是变量。
3) make的环境变量叫“MAKECMDGOALS”,这个变量中会存放你所指定的终极目标的列表,如果在命令行上,你没有指定目标,那么,这个变量是空值。
4. 常用目标名
1) all
编译所有目标
2) clean
删除make创建的所有文件
3) install
编译所有目标后,将可执行文件拷贝到xxx/bin下
4) print
列出改变过的源文件
5) tar
将源文件打包
6) dist
将tar打包后的文件压缩成gz文件
7) TAGS
更新所有目标,以备完整编译。
8) check与test
一般用来测试makefile的流程
5. 检查、调试Makefile
1) ”n” 、“--just-print” 、“--dry-run” 、“--recon”
不执行参数,这些参数只是打印命令,不管目标是否更新
2) “-t” 、“--touch”
这个参数的意思就是把目标文件的时间更新,但不更改目标文件。
3) “-q” 、“--question”
找目标,如果目标存在,什么也不输出,也不会执行编译,如果目标不存在,则打印一条出错信息。
4) “-W <file>” 、“--what-if=<file>” 、“--assume-new=<file>” 、“--new-file=<file>”
指定一个文件(源文件或依赖文件),执行与该文件相关的命令,通常与“-n”参数一同使用。
6. make参数
1) “-b” 、“-m”
这两个参数的作用是忽略和其它版本make的兼容性。
2) “-B” 、“--always-make”
更新目标(重编译)
3) “-C <dir>” 、“--directory=<dir>”
指定读取makefile的目录。如果有多个“-C”参数,make的解释是后面的路径以前面的作为相对路径,并以最后的目录作为被指定目录
4) “—debug[=<options>]”
输出make的调试信息。
i. a —— all,输出所有的调试信息。(会非常的多)
ii. b —— basic,简单的调试信息。即输出不需要重编译的目标。
iii. v —— verbose,在b选项的级别之上。输出的信息包括哪个makefile被解析,不需要被重编译的依赖文件(或是依赖目标)等。
iv. i —— implicit,输出所以的隐含规则。
v. j —— 也就是jobs,输出执行规则中命令的详细信息,如命令的PID、返回码等。
vi. 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 <file>” 、“--old-file=<file>” 、“--assume-old=<file>”
不重新生成指定的<file>,即使这个目标的依赖文件新于它。
16) “-p” 、“--print-data-base”
输出makefile中的所有数据,包括所有的规则和变量
例:想输出信息而不想执行makefile,你可以使用“make -qp”
17) “-q” 、“--question”
仅检查所指定的目标是否需要更新。如果是0则说明要更新,如果是2则说明有错误发生。
18) “-r” 、“--no-builtin-rules”
禁止make使用任何隐含规则。
19) “-R” 、“--no-builtin-variabes”
禁止make使用任何作用于变量上的隐含规则。
20) “-s” 、“--silent” 、“--quiet”
在命令运行时不输出命令的输出。
21) “-S” 、“--no-keep-going” 、“--stop”
取消“-k”选项的作用。
22) “-t” 、“--touch”
只是把目标的修改日期变成最新的,阻止生成目标的命令运行。
23) “-v” 、“--version”
输出make程序的版本、版权等关于make的信息。
24) “-w” 、“--print-directory”
踪嵌套式调用make,输出进入、退出目录信息。
25) “--no-print-directory”
禁止“-w”选项。
26) “-W <file>” 、“--what-if=<file>” 、“--new-file=<file>” 、“--assume-file=<file>”
指定一个文件(源文件或依赖文件),执行与该文件相关的命令,通常与“-n”参数一同使用。
27) --warn-undefined-variables”
只要make发现有未定义的变量,那么就输出警告信息
第十章 隐含规则
1. 隐藏规则是什么呢?
Makefile由目标、依赖关系和命令组成,那么隐藏规则主要指的是依赖关系的自动生成,命令的自动生成。
--warn-undefined-variables”
只要make发现有未定义的变量,那么就输出警告信息
2. 即使是我们指定了“-r”参数,某些隐含规则还是会生效,因为有许多的隐含规则都是使用了“后缀规则”来定义的
3. 隐含规则一览
1) 编译C的隐含规则
i. 自动生成依赖关系
“<n>.o”的目标的依赖目标会自动推导为“<n>.c”
ii. 自动生成命令
“$(CC) –c $(CPPFLAGS) $(CFLAGS)”
2) 编译C++的隐含规则
i. 自动生成依赖关系
“<n>.o”的目标的依赖目标会自动推导为“<n>.cc”或是“<n>.C”,建议.cc
ii. 自动生成命令
“$(CXX) –c $(CPPFLAGS) $(CFLAGS)”
3) 汇编与汇编预处理的隐含规则
i. 自动生成依赖关系
“<n>.o” 的目标的依赖目标会自动推导为“<n>.s”, 默认使用编译品“as”
“<n>.s” 的目标的依赖目标会自动推导为“<n>.S”,默认使用”cpp”
ii. 自动生成命令
“$(AS) $(ASFLAGS)”,
4) 连接object的隐含规则
i. 自动生成依赖关系
“<n>”目标依赖于“<n>.o”
ii. “$(CC) $(LDFLAGS) <n>.o $(LOADLIBES) $(LDLIBS)”
默认使用C编译的连接程序
iii. 补充
x依赖于x.o y.o
x : y.o 可以省略x.o,但不能省略y.o
5) 从C程序创建Lint库的隐含规则
i. 自动生成的依赖关系
“<n>.ln” (lint生成的文件)的依赖文件被自动推导为“n.c”
ii. 自动生成的命令
“$(LINT) $(LINTFALGS) $(CPPFLAGS) -i”
6) 除C、C++外的语言,请参考书籍。
4. 隐含规则使用的变量
1) make的“-R”或“--no–builtin-variables”参数来取消你所定义的变量对隐含规则的作用。
2) 首先变量分两种:命令相关变量和参数相关变量
3) 命令相关变量
i. C语言编译程序,默认命令是“cc
ii. C程序的预处理器(输出是标准输出设备)。默认命令是“$(CC) –E”
iii. C++语言编译程序,默认命令是“g++”
iv. 汇编语言编译程序,默认命令是“as”
v. 函数库打包程序,默认命令是“ar”
vi. 删除文件命令。默认命令是“rm –f”。
4) 参数相关的变量
i. C语言编译器参CFLAGS
ii. C预处理器参数CPPFLAGS。
iii. C++语言编译器参数CXXFLAGS
iv. 汇编语言编译器参数ASFLAGS (当明显地调用“.s”或“.S”文件时)
v. 函数库打包程序AR命令的参数。默认值是“rv”,ARFLAGS
vi. 链接器参数LDFLAGS (如:“ld”)
5. 隐含规则链
1) 说明一个文件或是目标是中介目标,你可以使用伪目标“.INTERMEDIATE”来强制声明。
2) 阻止make自动删除中间目标,可以使用伪目标“.SECONDARY”来强制声明
6. 定义模式规则
1) 在Makefile中一提到模式,就少不了%。
"%"的展开是有讲究的,变量和函数的展开发生在make载入Makefile时,而模式规则中的"%"则发生在运行时。
2) 模式可应用于目标、依赖文件。
那么命令呢?命令通常需要利用自动化变量,后面提到。
3) 如果"%"定义在目标中,那么,目标中的"%"的值决定了依赖目标中的"%"的值。
7. 自动化变量
1) 自动化变量的出现就是为Makefile的模式服务的
2) $@: 目标集
3) $%: 目标的成员集,目标为函数库时有效
目标是"foo.a (bar.o)",那么,"$%"就是"bar.o","$@"就是"foo.a"。
4) $<: 依赖集的第一个成员。如果依赖集是模式定义的(即%),则依次代表每
一个成员
5) $?: 比目标新的依赖集,以空格分隔。注:是所有,不是依次。
6) $^: 去重复成员的依赖集,以空格分隔。注:是所有,不是依次。
7) $+: 不去重复成员的依赖集,以空格分隔。注:是所有,不是依次。
8) $*: 表示模式中"%"及其之前的部分的目标集。
目标是"dir/a.foo.b",目标的模式是"a.%.b","$*"的值就是
"dir/a.foo",构造有关联的文件名是比较有用。
9) 以上搭配上"D"或"F"字样使用,可实现是否显示目录的功能。
例如:$(@D) 、$(@F),即dir/foo.o,是否显示dir/的功能
8. Makefile的一个非常强大的功能就是模式,那模式具体如何匹配呢?
1) 我们把"%"所匹配的内容叫做"茎"
2) 通常先去掉目录部分,最后再加回去。
9. 重载内建隐含规则
例如:main.o 自动依赖main.c时使用的C语言隐含规则
可通过如下重载
%.o : %.c
$(CC) -c $(CPPFLAGS) $(CFLAGS) -D$(date)
10. 过时的后缀规则
将会被模式规则取代,这不做介绍
第十一章 使用make更新函数库
1. 在Unix下,一般是由命令"ar"来完成打包工作。
2. 目标:archive(member)
例如:abclib(a.o b.o c.o): a.o b.o c.o
ar cr abclib a.o b.o c.o
3. "$%",这是专属函数库文件的自动化变量
4. 在进行函数库打包文件生成时,请小心使用make的并行机制("-j"参数)。该参数是并行参数。
目前在更新函数库时不建议使用-j参数,可能有问题。