Makefile
一个工程中的源文件不计数,其按类型、功能、 模块分别放在若干个目录中,makefile 定义了一系列的规则来指定,哪些文件需要先编译, 哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile 就像一个 Shell 脚本一样,其中也可以执行操作系统的命令。 makefile 带来的好 处就是——“自动化编译”,一旦写好,只需要一个 make 命令,整个工程完全自动编译, 极大的提高了软件开发的效率。make 是一个命令工具,是一个解释 makefile 中指令的命令 工具。
规则
格式:
TARGET:PREREQUISITES
<tab键>RECIPE
<tab键>RECIPE
<tab键>RECIPE
<tab键>RECIPE
.....
.PHONY:clean
clean:
<tab键>-rm *.o
TARGET 目标(文件名),make要做什么
你的makefile的最终目的是什么
PREREQUISITES:依赖,完成这个目标依赖的文件(也可以没有)
第一步看当前目标依赖的文件是否存在。
a.存在
按照RECIPE命令列表取完成目标
b.不存在
就搜索整个makefile,查找是否有以依赖文件为目标名的目标
如果有,则先递归的生成该目标....
如果没有,则报错...
RECIPE:生成该目标需要执行的命令
生成该目标需要执行的命令列表(可以是linux的shell命令)
.PHONY表示后边的都是伪目标
-rm 任何语句加上减号,就算语句错误也会继续执行后面的语句,如果想忽略全部错误,则可以使用make -i
make的用法
make + 目标名字
如果没有加目标,则默认完成makefile中第一个目标
把其他目标叫做“伪目标”
make运行的时候会自动的到当前目录找Makefile文件
make -C 指定目录
到指定的目录中去执行makefile
make会自动的把执行的命令打印到终端
如果不需要打印命令,则在命令行首加个@,就不会打印该行命令,如果需要全部不打印,则可以用make -s命令
make 是非常智能的,能够根据文件的修改时间,去自动的判断哪一些文件需要重新编译。
如果要指定特定的Makefile文件,则可以用make -f filename
一般默认使用make他会找当前目录的以"Makefile"为名的文件
在makefile文件中,注释是以#标注的,且只能进行行注释,如果需要打印#则需要用\进行转义,如\#
当依赖目标新于目标时,也就是当规则的目标需要被更新时,make 会一条一条的执行 其后的命令。需要注意的是,如果你要让上一条命令的结果应用在下一条命令时,你应该使 用分号分隔这两条命令。比如你的第一条命令是 cd 命令,你希望第二条命令得在 cd 之后的 基础上运行,那么你就不能把这两条命令写在两行上,而应该把这两条命令写在一行上,用 分号分隔。
make 的工作方式
1、读入所有的 Makefile。
2、读入被 include 的其它 Makefile。
3、初始化文件中的变量。
4、推导隐晦规则,并分析所有规则。
5、为所有的目标文件创建依赖关系链。
6、根据依赖关系,决定哪些目标要重新生成。
7、执行生成命令。
1-5 步为第一个阶段,6-7 为第二个阶段。第一个阶段中,如果定义的变量被使用了,那么, make 会把其展开在使用的位置。但 make 并不会完全马上展开,make 使用的是拖延战术,如 果变量出现在依赖关系的规则中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。
自动变量
系统已经帮我们定义好的变量,我们直接使用即可,已经有确定的值(特殊的含义)
$@ 当前完整的目标名字
main:
echo $@ //main
xxxxx:
echo $@ //xxxxx
$+ 当前所有的依赖文件,以空格隔开,可能包含重复的依赖文件
你写到哪一个目标下面,就表示哪一个目标的依赖文件列表
$^ 当前所有的依赖文件,以空格隔开,不重复的依赖文件
你写到哪一个目标下面,就表示哪一个目标的依赖文件列表
$< 第一个依赖的名字
main:a.c,b.c
gcc $<
$< <===> a.c
$?
所有时间戳比目标文件晚的依赖文件,并且以空格隔开
生成目标后,你又重新更改了文件
变量的用法
1 简单赋值 (:=) 简单的进行赋值操作
2 递归赋值 (=) 会向后展开
比如:foo = $(bar)
bar = $(ugh)
ugh = Huh?
打印出来foo的值为Huh?
3 追加赋值 (+=) 在变量当前值后边加上新的值 以空格隔开
4 条件赋值 (?=) 如果没有被定义,才会进行赋值操作
5 变量值的替换 $(var:a=b) 把变量“var”中所有以 “a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符”。
6 把变量的值再当成变量 比如:x = y
y = z
z = u
a := $($($(x))) 此时a的值为u
使用条件判断
libs_for_gcc = -lgnu
normal_libs =
foo: $(objects)
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else $(CC) -o foo $(objects) $(normal_libs)
endif
我们可以从上面的示例中看到三个关键字:ifeq、else 和 endif。ifeq 的意思表示条 件语句的开始,并指定一个条件表达式,表达式包含两个参数,以逗号分隔,表达式以圆括 号括起。else 表示条件表达式为假的情况。endif 表示一个条件语句的结束,任何一个条件 表达式都应该以 endif 结束。
1.ifeq 相同为真 不同为假
2 ifneq 相同为假 不同为真
3 ifdef 后加一个值 非空则真
4 ifndef 空为真
函数
函数调用,很像变量的使用,也是以“$”来标识的 $(<function> <arguments>)
字符串处理函数
1、字符串替换函数——subst
$(subst <from>,<to>,<text>)
功能:把字串<text>中的<from>字符串替换成<to>。
返回:函数返回被替换过后的字符串。
2、模式字符串替换函数——patsubst
$(patsubst <pattern>,<replacement>,<text>)
功能:查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符 合模式<pattern>,如果匹配的话,则以<replacement>替换。这里,<pattern>可以包括通 配符“%”,表示任意长度的字串。如果<replacement>中也包含“%”,那么,<replacement> 中的这个“%”将是<pattern>中的那个“%”所代表的字串。(可以用“\”来转义,以“\%” 来表示真实含义的“%”字符) 返回:函数返回被替换过后的字符串。
3、去空格函数——strip
$(strip <string>)
功能:去掉<string>字串中开头和结尾的空字符。
返回:返回被去掉空格的字符串值。
4、 查找字符串函数——findstring
$(findstring <find>,<in>)
功能:在字串<in>中查找<find>字串。
返回:如果找到,那么返回<find>,否则返回空字符串。
5、过滤函数——filter
$(filter <pattern...>,<text>)
功能:以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词。可以 有多个模式。
返回:返回符合模式<pattern>的字串。
示例: sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
cc $(filter %.c %.s,$(sources)) -o foo
$(filter %.c %.s,$(sources))
返回的值是“foo.c bar.c baz.s”。
6、反过滤函数——filter-out (与上函数相反)
7、排序函数——sort。
$(sort <list>)
功能:给字符串<list>中的单词排序(升序)。
返回:返回排序后的字符串。
示例:$(sort foo bar lose)返回“bar foo lose” 。
备注:sort 函数会去掉<list>中相同的单词。