makefile文件
转自//
https://seisman.github.io/how-to-write-makefile/introduction.html#
:=
前面的变量不能使用后面的变量,只能使用前面已定义好了的变量。
+=
操作符给变量追加值
?=
,先看示例:
FOO ?= bar
其含义是,如果FOO没有被定义过,那么变量FOO的值就是“bar”,如果FOO先前被定义过,那么这条语将 什么也不做.
多行变量
define two-lines echo foo echo $(bar) endef
替换变量中的共有的部分,其格式是 $(var:a=b)
或是 ${var:a=b}
,其意思是, 把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。
使用函数
$(subst <from>,<to>,<text>)
将text中的from替换成to
$(patsubst <pattern>,<replacement>,<text>)
将text中的pattern模式的替换成replacement
例子:$(patsubst %.o,%.c, a.c b.c) >a.o b.o
$(strip <string>)
将string前后的空格删除
findstring <find>, <in>
将in 中的find返回
filter <pattern...>,<test>
将test中的pattern模式过滤出来。可以同时过滤多个模式的字符穿。
filterout与filter作用相反,将不符合模式的字符串过滤出来
sort <list>
将list里面的字符串序列按asc排序输出
Word <n>,<test>
将test里的第n个单词返回。如果n大于test单词个数返回null
worldlist <start>,<end>,<test>
将test从start开始到end的函数返回
words <test>
统计test中的单词个数
firstWord <test>
返回test中的第一个单词
文件操作函数
dir <name ...>
将name中的目录部分返回
notdir <name ..>
将文件名返回
suffix <names ...>
将nsuffixames中所有的文件的后缀返回回来。形如.c .c .o .s 如果所有文件都没有后缀,就返回空
basename <names ..>
与suffix相反,返回删除后缀名的字符串
addsuffix <suffix>,<names ...>
给names 添加后缀名
addprefix <prefix>,<names ...>
给names里的每一项添加前缀
join <list1>,<list2>
逐个链接字符串。aaa bbb ,111 222 > aaa111 bbb222.个数不匹配项都添加到末尾。
foreach <var>,<list>,<test>
例子 names := a b c d
files := $(foreach n,$names,$(n).o)
将names里的每一个复制n,将$(n).o拼接成一个新的字符串返回给files
call <expression>,<parm1>,<param2>.....//自定义函数
expression中1,2...与 <parm1>,<param2>.....对应传参数,并返回expression返回值
shell
指明 后面接的是一条shell 命令
控制make 运行的函数
error
warning
make的运行
make的退出码
0 表示成功执行
1 错误
2 -q选项
make指定文件
make -f filename
make指定目标
例如 make clean 就只会执行 clean 伪目标
make all
make install //将编译好的目标文件拷贝到安装目录
make检查规则
-n 将 make执行序列的命令 打印出来。不执行命令 用于调试
-t 将目标的生成时间更新一下。
-q ???
-w <filename>查看filename文件相关的编译情况。 结合-n
make的参数
-b ,-m 忽略兼容性
-b 所有的目标都要重新编译。 相当于 执行了make clean
-c <dir> ,--directory <dir>指定读取makefile 的目录
-debug [ options] -a -b -v -i -j -m 输出调试信息
-e 使用同名环境变量的值 ,放弃makefile中的同名变量
-f 指定makefile
-h 获取help
-i 忽略错误
-I 指定头文件包含目录
-j 没理解
-k 如果一个 目标出错,相关依赖 放弃。继续编译其他的。可以一次性暴露更多编译问题,提高工作效率
-l
-n 见上文
-o <**.o> 不更新*.o
-p 输出所有的makefile中的数据
-q 检查目标是否需要更新。 0 需要。2 不需要更新
-r 不适用隐含规则
-s 将所有执行输出信息 导向/dev/null
-v 查看make 版本信息
隐含规则
1.*.o 的依赖自动推导为 *.c
$(cc) -c $(CPPFLAGS) $(CFLAGS)
2.链接OBJ文件的隐含规则
x : y.o z.o
> cc -c x.c -o x.o
>cc -c y.c -o y.o
>cc -c z.c -o z.o
>cc x.o y.o z.o -o x
命令变量
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文法分析器参数。
定义模式规则
你可以使用模式规则来定义一个隐含规则。一个模式规则就好像一个一般的规则,只是在规则中,目标的定义 需要有 %
字符。 %
的意思是表示一个或多个任意字符。在依赖目标中同样可以使用 %
, 只是依赖目标中的 %
的取值,取决于其目标。
模式规则示例
下面这个例子表示了,把所有的 .c
文件都编译成 .o
文件.
%.o : %.c $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
其中, $@
表示所有的目标的挨个值, $<
表示了所有依赖目标的挨个值。这些奇怪的变量我们 叫“自动化变量”,后面会详细讲述。
自动化变量
$@
: 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,$@
就是匹配于 目标中模式定义的集合。$%
: 仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是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所不能识别的,那么$*
就是空值。-
当你希望只对更新过的依赖文件进行操作时,
$?
在显式规则中很有用,例如,假设有一个函数库文件 叫lib
,其由其它几个object文件更新。那么把object文件打包的比较有效率的Makefile规则是:lib : foo.o bar.o lose.o win.o ar r lib $?
在上述所列出来的自动量变量中。四个变量(
$@
、$<
、$%
、$*
)在扩展时 只会有一个文件,而另三个的值是一个文件列表。这七个自动化变量还可以取得文件的目录名或是在当前 目录下的符合模式的文件名,只需要搭配上D
或F
字样。这是GNU make中老版本的特性, 在新版本中,我们使用函数dir
或notdir
就可以做到了。D
的含义就是Directory, 就是目录,F
的含义就是File,就是文件。下面是对于上面的七个变量分别加上
D
或是F
的含义:$(@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)
分别表示被更新的依赖文件的目录部分和文件部分。
最后想提醒一下的是,对于
$<
,为了避免产生不必要的麻烦,我们最好给$
后面的那个特定 字符都加上圆括号,比如,$(<)
就要比$<
要好一些。还得要注意的是,这些变量只使用在规则的命令中,而且一般都是“显式规则”和“静态模式规则” (参见前面“书写规则”一章)。其在隐含规则中并没有意义。