1.makefile核心与make工具概述
make 命令执行时,需要一个 Makefile 文件,以告诉 make 命令需要怎么样的去编译和
链接程序。makefile是一个路标,指引make这个工具怎么执行。
makefile这个路标的核心规则其实一条:
target:prerequisite
command
–target 是一个目标文件,可以是Object文件,也可以是可执行文件
– prerequisites是要生成target所依赖的文件或目标
–command是make需要执行的生成target的命令
这是一个文件的依赖关系,也就是说,要生成的target 这一个或多个的目标文件依赖于
prerequisites 中的文件,其生成规则定义在 command 中。
而关于make,只需了解以下几点:
1.它是一个工具
2.执行make,工具会在当前目录下找makefile或Makefile文件
3.如果找到,它会找文件中的第一个目标文件(target),并把这个文件作为最终的目标文件。
4, 如果 target文件不存在, 或是 target所依赖的后面的 .o 文件的文件修改时间要比 target
这个文件新,那么,他就会执行后面所定义的命令来生成 edit 这个文件。
make的执行步骤如下:
1、读入所有的 Makefile。
2、读入被 include 的其它 Makefile。
3、初始化文件中的变量。
4、推导隐晦规则,并分析所有规则。
5、为所有的目标文件创建依赖关系链。
6、根据依赖关系,决定哪些目标要重新生成。
7、执行生成命令。
依赖性是make 与makefile的核心,make找生成目标文件的依赖关系,并执行命令,生成目标文件。
而makefile要学的书写规则,书写命令,变量/语句/函数的使用等内容,说白了都是为上面的依赖性服务。
此外,make 的“隐晦规则”:
不需要这么写
main.o : main.c defs.h
cc -c main.c
make会自己推导main.o的目标文件有main.c,且命令里有cc -c main.c,所以只需按如下写法:
main.o : defs.h
2.makefile语法学习
makefile语法学习里主要包括五部分:显示规则,隐式规则,变量定义,文件指示,其它
2.1规则
这里的规则就是之前说的核心规则:
target:prerequisite
command
也就是依赖规则与目标生成方法。
格式如上,这里有几点说明:
-
command 是命令行,如果其不与“target:prerequisites”在一行,那么,必须以[Tab键]开头, 如果和 prerequisites 在一行, 那么可以用分号做为分隔。
-
命令太长可以用\隔开,一行没有字符数限制。make 会以 UNIX 的标准 Shell,也就是/bin/sh 来执行命令。
-
target 或者prerequisite可以包含多个文件,以空格隔开。
-
target和prerequisite可以使用通配符: * :如“*.c”表示所以后缀为 c 的文件。
注意依赖文件prerequisite的搜索路径,可以通过VPATH指定。
VPATH是make的一个特殊变量,不是关键字,可以这样使用:
VPATH = src:…/headers
不同路径目录用冒号:隔开
也可以用小写vpath指定,注意,vpath是make的一个关键字,但是它更为灵活,用法如下:
1、vpath
为符合模式的文件指定搜索目录。
2、vpath
清除符合模式的文件的搜索目录。
3、vpath
清除所有已被设置好了的文件搜索目录。
pattern指定了文件集,directories指定了路径。pattern需要包含“%”字符。“%”的意思是匹配零或若干字符,如“%.h”表示所有以“.h”结尾的文件。例如:
vpath %.c foo:bar
make的最终目标是生成第一个makefile文件的第一个target文件,那么对于clean种命令就无法执行,所以,makefile引入了伪目标的概念。
伪目标就是假的”目标文件“,用.PHONY表示,如
.PHONY: clean
clean:
rm *.o temp
就是把clean当作一个”目标文件“,执行make clean来”生成这一目标“,进而达到了执行清除目标文件的目的。
伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为“默认目标”,只要将其放在第一个。一个示例就是,如果你的 Makefile 需要一口气生成若干个可执行文件,但你只想简单地敲一个 make 完事,并且,所有的目标文件都写在一个 Makefile 中,那么你可以使用“伪目标”这个特性:
all : prog1 prog2 prog3
.PHONY : all
前面说过,多依赖文件,可以通过*表示,多依赖的路径可以在vpath中用%指定,而多目标文件,可以用$@表示用在命令中,如
bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) > $@
它相当于
bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput
即$@相当于bigoutput littleoutput的集合。
规则的另一种写法
<targets ...>: <target-pattern>: <prereq-patterns ...>
<commands>
如
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
目标从$object 中获取,“%.o”表明要所有以“.o”结尾的目标,将这些目标后缀”.o“去掉,而依赖模式“%.c”,表示为刚才去掉”.o”的名称后加“.c”的后缀,于是,我们的依赖目标就是“foo.c bar.c”。
这种写法成为静态模式。
2.2书写命令(command)
2.2.1命令显示
make中所有的命令都会在执行前被打印在命令行中
前面加@ 命令不会被make显示
而参数“-n”或“–just-print”则只打印不执行
参数“-s"或者”-slient"则这是全局不打印
2.2.2命令执行
命令执行时,如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令。
2.2.2命令出错
命令出错时会终止,忽略命令的出错,我们可以在 Makefile 的命令行前加一个减号“-”(在 Tab 键之后),标记为不管命令出不出错都认为是成功的。
2.3 书写变量
makefile中的变量定义,类似与c中的宏定义。变量会在使用它的地方精确地展开。
变量在声明时需要给予初值,而在使用时,需要给在变量名前加上“
”符号,但最好用小括号“()”或是大括号“
”把变量给包括起来。如果你要使用真实的“
”符号,但最好用小括号“()”或是大括号“{}”把变量给包括起来。如果你要使用真实的“
”符号,但最好用小括号“()”或是大括号“”把变量给包括起来。如果你要使用真实的“”字符,那么你需要用“$$”来表示。
变量可以使用在许多地方,如规则中的“目标”、“依赖”、“命令”以及新的变量中。
变量赋值
使用=赋值:变量是可以使用后面的变量来定义的。
使用:=赋值:前面的变量不能使用后面的变量,只能使用前面已定义好了的变量。
使用?=赋值:若没有定义则定义,否则什么也不做。
使用+=赋值:追加变量。如果变量之前没有定义过,那么, “+=”会自动变成“=”, 如果前面有变量定义, 那么“+=”会继承于前次操作的赋值符。如果前一次的是“:=”, 那么“+=”会以“:=”作为其赋值符
变量的高级用法
替换变量中的共有的部分,其格式是“
(
v
a
r
:
a
=
b
)
”或是“
(var:a=b)”或是“
(var:a=b)”或是“{var:a=b}”,其意思是,把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符。
或 bar := $(foo:%.o=%.c)
makefile中“局部变量与全局变量”
当 make 嵌套调用时(参见前面的“嵌套调用”章节),上层 Makefile 中定义的变量会以系统环境变量的方式传递到下层的Makefile 中。在下层mk中重新赋值会覆盖变量。除非使用-e参数。
可以为某个目标文件设置局部变量,它可以和“全局变量”同名,因为它的作用范围只在这条规则以及连带规则中,所以其值也只在作用范围内有效。如:
prog : CFLAGS = -g
prog : prog.o foo.o bar.o
$(CC) $(CFLAGS) prog.o foo.o bar.o
在这个示例中,不管全局的$(CFLAGS)的值是什么,在 prog 目标,以及其所引发的所有规则中(prog.o foo.o bar.o 的规则),$(CFLAGS)的值都是“-g”。
或这样
%.o : CFLAGS = -O
给所有.o结尾的目标文件的CFLAGS 值赋值-O.
2.3使用条件判断
命令格式:
<conditional-directive>
<text-if-true>
else
<text-if-false>
endif
else部分非必须。条件关键字有四个:ifeq/ifneq、ifdef/ifndef
2.4 使用函数
函数调用,很像变量的使用,也是以“$”来标识的,其语法如下:
$(<function> <arguments1>,<arguments2>…)
make支持的函数不多,大体如下:
2.5 其他规则说明
1.注释:
makefile中注释使用符号# ,若要使用#字符,可以用#转义
2.makefile的文件名
使用makefile或者Makefile或者xxx.mk,建议使用Makefile,因为第一个字母大写,更加显眼醒目
3.mk的文件引用
与C类似,mk中的文件引用使用的也是include关键字,比如:
include a.mk
与c一样,如果没有指定路径,mk寻找的路径默认为当前目录,如果当前目录没有找到,那么mk还会从以下目录寻找:
- makefile中有”-I“或”–include-dir“参数指定的路径
- 如果目录/include(一般是:/usr/local/bin 或/usr/include)存在的话,make 也会去找。
如果有文件没有找到的话,make 会生成一条警告信息,但不会马上出现致命错误。它会继续载入其它的文件,一旦完成 makefile 的读取,make 会再重试这些没有找到,或是不能读取的文件,如果还是不行,make 才会出现一条致命信息。
3 关键字总结
名称 | 含义 | 说明 |
---|---|---|
vpath | 指定依赖文件的搜索路径 |
4 特殊变量总结
名称 | 含义 | 说明 |
---|---|---|
VPATH | 依赖文件的包含路径 | 用:分隔不同文件 |
$@ | 当前规则中所有目标的集合 |
常用变量
语法定义的默认变量
变量 | 说明 |
---|---|
$@ | 规则的目标所对应的文件名 |
$< | 规则中的第一个相关文件名 |
$^ | 规则中所有相关文件的列表,以空格分割 |
$? | 规则中日期新于目标的所有相关文件的列表,以空格分割 |
$(@D) | 目标文件的目录部分 |
$(@F) | 目标文件的文件名部分 |
预定俗成需预定义的变量
待进一步完善…