1.实例分析
target...:prerequisites...
commmand
note:command must start with <tab>
1.1 make会自己查找makefile或makefile文件夹,并把文件中找到的第一个目标作为最终目标.
1.2 使用变量
var=xxx
$(var)
1.3 makefile会自动推导依赖关系
看到.o文件就会自动把.c文件加到依赖关系中
1.5 清空目标文件的规则
eg1:
clean:
rm edit $(objects)
eg2:
.PHONY : clean
clean:
-rm edit $(objects)
2. makefile文件概述
.显示规则.
.隐式规则.让makefile自动推导依赖关系
.变量定义.
.文件指示. include 文件名
.注释. # 在变量定义后加#注释会把#前的空格等作为变量的定义
3.make书写规则
3.2 规则中使用通配符 * ? [...]
可以用转义字符\* 来表示真正*
~ 表home目录或宿主目录
自动化变量:$@ 目标集; $< 依赖目标集; $? 第一个依赖目标集
object :=$(wildcard *.o) 就是说object是所用.o文件展开的结果
3.3 文件搜索
makefile自己寻找依赖文件和目标文件的路径默认是当前目录,可以用VPATH变量或vpath关键字添加搜索路径
3.3.1 VPATH 关键字
VPATH= src:../headers:$(topdir)/include
如上:路径间以分号隔开
3.3.2 vpath关键字
vpath pattern directories
为符合模式的文件指定搜索目录,模式中用到%通配符
vpath pattern
清除符合模式的搜索目录
vpath
清除所有的文件搜索目录
3.4 伪目标 .PHONY
不会生成目标文件,只执行命令
3.5 多目标 $@表目标集
3.6 静态模式
如:
$(objects): %.o: %.c
gcc -c $< -o $@
3.7 自动生成依赖性
GCC中的 -M 选项可以自动搜索源文件中包含的头文件并生成依赖关系
(gcc -M会把标准头文件包括进来,用-MM就不会)
GNU组织建议把编译器为每一个源文件的自动生成的依赖关系放到一个文件中,为每一个“name.c”的文件
都生成一个“name.d”的Makefile文件,[.d]文件中就存放对应[.c]文件的依赖关系。
这里,我们给出了一个模式规则来产生[.d]文件:
%.d: %.c
@set -e; rm -f $@; \
$(CC) -M $(CPPFLAGS) $< > ; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < > $@; \
rm -f
这个规则的意思是,所有的[.d]文件依赖于[.c]文件,“rm -f $@”的意思是删除所有的目标,也就是[.d]文件,
第二行的意思是,为每个依赖文件“$<”,也就是[.c]文件生成依赖文件,“$@”表示模式“%.d”文件,如果有
一个C文件是name.c,那么“%”就是“name”,“$$$$”意为一个随机编号,第二行生成的文件有可能是
“name.d.12345”,第三行使用sed命令做了一个替换,关于sed命令的用法请参看相关的使用文档。第四行就是
删除临时文件。总而言之,这个模式要做的事就是在编译器生成的依赖关系中加入[.d]文件的依赖,即把依赖关系:
main.o : main.c defs.h
转成:
main.o main.d : main.c defs.h
于是,我们的[.d]文件也会自动更新了,并会自动生成了,当然,你还可以在这个[.d]文件中加入的不只是依
赖关系,包括生成的命令也可一并加入,让每个[.d]文件都包含一个完赖的规则。一旦我们完成这个工作,接
下来,我们就要把这些自动生成的规则放进我们的主Makefile中。我们可以使用Makefile的“include”命令,
来引入别的Makefile文件(前面讲过),例如:
sources = foo.c bar.c
include $(sources:.c=.d)
4.使用命令
4.1 显示命令
make会把其要执行的命令行在命令执行前输出到屏幕上。
当我们用“@”字符在命令行前,那么,这个命令将不被make显示出来
如:
@echo 正在编译XXX模块...... 显示:正在编译XXX模块.....
echo 正在编译XXX模块...... 显示:echo 正在编译XXX模块......
make参数“-n”或“--just-print”,那么其只是显示命令,但不会执行命令
make参数“-s”或“--slient”则是全面禁止命令的显示
4.2 执行命令
如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令(管道作用)
4.3 命令出错
.命令前加-忽略出错
.,给make加上“-i”或是“--ignore-errors”参数,那么,Makefile中所有命令都会忽略错误
.一个规则是以“.IGNORE”作为目标的,那么这个规则中的所有命令将会忽略错误
.make的参数的是“-k”或是“--keep-going”,这个参数的意思是,如果某规则中的命令出错了,
那么就终止该规则的执行,但继续执行其它规则
4.4 嵌套执行make
subsystem:
cd subdir && $(MAKE)
其等价于:
subsystem:
$(MAKE) -C subdir
定义$(MAKE)宏变量的意思是,也许我们的make需要一些参数,所以定义成一个变量比较利于维护。
这两个例子的意思都是先进入“subdir”目录,然后执行make命令
export <variable ...>
unexport <variable ...>
如果你要传递所有的变量,那么,只要一个export就行了。后面什么也不用跟,表示传递所有的变量
系统级的环境变量SHELL和MAKEFLAGS,管你是否export,其总是要传递到下层Makefile中.
还有一个在“嵌套执行”中比较有用的参数,“-w”或是“--print-directory”会在make的过程
中输出一些信息,让你看到目前的工作目录
当你使用“-C”参数来指定make下层Makefile时,“-w”会被自动打开的。
如果参数中有“-s”(“--slient”)或是“--no-print-directory”,那么,“-w”总是失效的。
4.5 定义命令包
如:
define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef
5.使用变量
5.1 变量基础
变量大小写敏感
$(var),如果要使用$用$$来表示
5.2 变量赋值
. = 右侧的变量可以定义在文件的任何地方
. :=变量不能使用后面的变量,只能使用前面定义好的变量
. ?=变量没有被定义过就定义否则什么都不做
5.3 变量高级用法
. 变量的替换
.bar := $(foo:.o=.c) 把“$(foo)”中所有以“.o”字串“结尾”全部替换成“.c”
.静态模式定义的变量替换
bar := $(foo:%.o=%.c)
.把变量的值再当成变量 (嵌套)
5.4 追加变量值 +=
objects = main.o foo.o bar.o utils.o
objects += another.o
5.5 override 指示符
如果有变量是通常make的命令行参数设置的,那么Makefile中对这个变量的赋值会被忽略。如果
你想在Makefile中设置这类参数的值,那么,你可以使用“override”指示符。其语法是:
override <variable> = <value>
override <variable> := <value>
当然,你还可以追加:
override <variable> += <more text>
对于多行的变量定义,我们用define指示符,在define指示符前,也同样可以使用ovveride指示符,如:
override define foo
bar
endef
5.6 多行变量
define two-lines
echo foo
echo $(bar)
endef
5.7 环境变量
make运行时的系统环境变量可以在make开始运行时被载入到Makefile文件中,但是如果Makefile中
已定义了这个变量,或是这个变量由make命令行带入,那么系统的环境变量的值将被覆盖。(如果make
指定了“-e”参数,那么,系统环境变量将覆盖Makefile中定义的变量)
当make嵌套调用时(参见前面的“嵌套调用”章节),上层Makefile中定义的变量会以系统环境变量
的方式传递到下层的Makefile中。当然,默认情况下,只有通过命令行设置的变量会被传递。而定义
在文件中的变量,如果要向下层Makefile传递,则需要使用exprot关键字来声明。(参见前面章节)
5.8 目标变量
Makefile中定义的变量都是“全局变量”,在整个文件,我们都可以访问这些变量
自动化变量就属于“规则型变量”,这种变量的值依赖于规则的目标和依赖目标的定义
为某个目标设置局部变量,这种变量被称为“Target-specific Variable”,作用范围只在这条规则以及连带规则中
其语法是:
<target ...> : <variable-assignment>
<target ...> : overide <variable-assignment>
如:
prog : CFLAGS = -g
prog : prog.o foo.o bar.o
$(CC) $(CFLAGS) prog.o foo.o bar.o
prog.o : prog.c
$(CC) $(CFLAGS) prog.c
foo.o : foo.c
$(CC) $(CFLAGS) foo.c
bar.o : bar.c
$(CC) $(CFLAGS) bar.c
5.9 模式变量
模式变量(Pattern-specific Variable),把变量定义在符合这种模式的所有目标上
<pattern ...> : <variable-assignment
<pattern ...> : override <variable-assignment>
如
%.o : CFLAGS = -o
6 使用条件判断
6.1 示例
ifeq ($(CC), gcc)
...
else
...
endif
6.2 语法
ifeq, ifneq, ifdef, ifndef
7. 使用函数
7.1 语法
$(<function> <arguments>)
${<function> <arguments>}
参数间以逗号“,”分隔,而函数名和参数之间以“空格”分隔。
函数调用以“$”开头,以圆括号或花括号把函数名和参数括起
7.2 字符串处理函数
$(subst <from>,<to>,<text>)
名称:字符串替换函数——subst。
功能:把字串<text>中的<from>字符串替换成<to>。
返回:函数返回被替换过后的字符串。
$(patsubst <pattern>,<replacement>,<text>)
名称:模式字符串替换函数——patsubst。
功能:查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合
模式<pattern>,如果匹配的话,则以<replacement>替换。这里,<pattern>可以包括
通配符“%”,表示任意长度的字串。如果<replacement>中也包含“%”,那么,
<replacement>中的这个“%”将是<pattern>中的那个“%”所代表的字串。
(可以用“\”来转义,以“\%”来表示真实含义的“%”字符)
返回:函数返回被替换过后的字符串。
$(strip <string>)
名称:去空格函数——strip。
功能:去掉<string>字串中开头和结尾的空字符。
返回:返回被去掉空格的字符串值。
$(findstring <find>,<in>)
名称:查找字符串函数——findstring。
功能:在字串<in>中查找<find>字串。
返回:如果找到,那么返回<find>,否则返回空字符串。
$(filter <pattern...>,<text>)
名称:过滤函数——filter。
功能:以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词。
可以有多个模式。
返回:返回符合模式<pattern>的字串。
$(filter-out <pattern...>,<text>)
名称:反过滤函数——filter-out。
功能:以<pattern>模式过滤<text>字符串中的单词,去除符合模式<pattern>的单词。
可以有多个模式。
返回:返回不符合模式<pattern>的字串
$(sort <list>)
名称:排序函数——sort。
功能:给字符串<list>中的单词排序(升序)。
返回:返回排序后的字符串。
示例:$(sort foo bar lose)返回“bar foo lose” 。
备注:sort函数会去掉<list>中相同的单词。
$(word <n>,<text>)
名称:取单词函数——word。
功能:取字符串<text>中第<n>个单词。(从一开始)
返回:返回字符串<text>中第<n>个单词。如果<n>比<text>中的单词数要大,那么返回空字符串。
示例:$(word 2, foo bar baz)返回值是“bar”。
$(wordlist <s>,<e>,<text>)
名称:取单词串函数——wordlist。
功能:从字符串<text>中取从<s>开始到<e>的单词串。<s>和<e>是一个数字。
返回:返回字符串<text>中从<s>到<e>的单词字串。如果<s>比<text>中的单词数要大,那么返回空字符串。如果<e>大于<text>的单词数,那么返回从<s>开始,到<text>结束的单词串。
示例: $(wordlist 2, 3, foo bar baz)返回值是“bar baz”。
$(words <text>)
名称:单词个数统计函数——words。
功能:统计<text>中字符串中的单词个数。
返回:返回<text>中的单词数。
示例:$(words, foo bar baz)返回值是“3”。
备注:如果我们要取<text>中最后的一个单词,我们可以这样:$(word $(words <text>),<text>)。
$(firstword <text>)
名称:首单词函数——firstword。
功能:取字符串<text>中的第一个单词。
返回:返回字符串<text>的第一个单词。
示例:$(firstword foo bar)返回值是“foo”。
备注:这个函数可以用word函数来实现:$(word 1,<text>)。
7.3 文件名操作函数
$(dir <names...>)
名称:取目录函数——dir。
功能:从文件名序列<names>中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。
返回:返回文件名序列<names>的目录部分。
示例: $(dir src/foo.c hacks)返回值是“src/ ./”。
$(notdir <names...>)
名称:取文件函数——notdir。
功能:从文件名序列<names>中取出非目录部分。非目录部分是指最后一个反斜杠(“/”)之后的部分。
返回:返回文件名序列<names>的非目录部分。
示例: $(notdir src/foo.c hacks)返回值是“foo.c hacks”。
$(suffix <names...>)
名称:取后缀函数——suffix。
功能:从文件名序列<names>中取出各个文件名的后缀。
返回:返回文件名序列<names>的后缀序列,如果文件没有后缀,则返回空字串。
示例:$(suffix src/foo.c src-1.0/bar.c hacks)返回值是“.c .c”。
$(basename <names...>)
名称:取前缀函数——basename。
功能:从文件名序列<names>中取出各个文件名的前缀部分。
返回:返回文件名序列<names>的前缀序列,如果文件没有前缀,则返回空字串。
示例:$(basename src/foo.c src-1.0/bar.c hacks)返回值是“src/foo src-1.0/bar hacks”。
$(addsuffix <suffix>,<names...>)
名称:加后缀函数——addsuffix。
功能:把后缀<suffix>加到<names>中的每个单词后面。
返回:返回加过后缀的文件名序列。
示例:$(addsuffix .c,foo bar)返回值是“foo.c bar.c”。
$(addprefix <prefix>,<names...>)
名称:加前缀函数——addprefix。
功能:把前缀<prefix>加到<names>中的每个单词后面。
返回:返回加过前缀的文件名序列。
示例:$(addprefix src/,foo bar)返回值是“src/foo src/bar”。
$(join <list1>,<list2>)
名称:连接函数——join。
功能:把<list2>中的单词对应地加到<list1>的单词后面。如果<list1>的单词个数要比<list2>的多,
那么,<list1>中的多出来的单词将保持原样。如果<list2>的单词个数要比<list1>多,那么,
<list2>多出来的单词将被复制到<list2>中。
返回:返回连接过后的字符串。
示例:$(join aaa bbb , 111 222 333)返回值是“aaa111 bbb222 333”。
7.4 foreach 函数
$(foreach <var>,<list>,<text>)
把参数<list>中的单词逐一取出放到参数<var>所指定的变量中,然后再执行<text>所包含的表达式。
每一次<text>会返回一个字符串,循环过程中,<text>的所返回的每个字符串会以空格分隔,最后当整个循环结束时,
<text>所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值
7.5 if函数
$(if <condition>,<then-part>)
$(if <condition>,<then-part>,<else-part>)
if函数的返回值是,如果<condition>为真(非空字符串),那个<then-part>会是整个函数的返回值,
如果<condition>为假(空字符串),那么<else-part>会是整个函数的返回值,此时如果<else-part>
没有被定义,那么,整个函数返回空字串。
7.6 call函数
$(call <expression>,<parm1>,<parm2>,<parm3>...)
当make执行这个函数时,<expression>参数中的变量,如$(1),$(2),$(3)等,
会被参数<parm1>,<parm2>,<parm3>依次取代。而<expression>的返回值就是call函数的返回值
7.7 orign 函数
origin函数不像其它的函数,他并不操作变量的值,他只是告诉你你的这个变量是哪里来的?其语法是:
$(origin <variable>)
注意,<variable>是变量的名字,不应该是引用。所以你最好不要在<variable>中使用“$”字符。
Origin函数会以其返回值来告诉你这个变量的“出生情况”,下面,是origin函数的返回值:
“undefined”
如果<variable>从来没有定义过,origin函数返回这个值“undefined”。
“default”
如果<variable>是一个默认的定义,比如“CC”这个变量,这种变量我们将在后面讲述。
“environment”
如果<variable>是一个环境变量,并且当Makefile被执行时,“-e”参数没有被打开。
“file”
如果<variable>这个变量被定义在Makefile中。
“command line”
如果<variable>这个变量是被命令行定义的。
“override”
如果<variable>是被override指示符重新定义的。
“automatic”
如果<variable>是一个命令运行中的自动化变量。关于自动化变量将在后面讲述。
7.8 shell 函数
contents := $(shell cat foo)
7.9 make控制函数
$(error <text ...>) 产生一个致命的错误,<text ...>是错误信息
$(warning <text ...>) 这个函数很像error函数,只是它并不会让make退出,
只是输出一段警告信息,而make继续执行。