Makefile学习总结
相信做过rom开发的,尤其做过机型适配的,一定知道makefile的重要性。
这里我开了个章节,专门针对makefile做入门学习,学习的内容就是陈皓老师的 跟我一起写makefile.个人感觉作为入门还是比较经典的,当然更详细的可以看看 《GNU make 学习手册》
Makefile入门(一):概述
Makefile入门(二):MakeFile介绍
Makefile入门(三):书写规则
Makefile入门(四):书写命令
Makefile入门(五):使用变量
Makefile入门(六):使用条件判断
Makefile入门(七):使用函数
Makefile入门(八):make运行
Makefile入门(九):隐含规则
Makefile入门(十):使用make更新函数库文件
Android.mk入门
理解 Android Build 系统
针对十一篇的内容做一个简单的提炼:
一、概述
1.什么是makefile?
一个工程中的源文件不计其数,并且按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个 Shell脚本一样,其中也可以执行操作系统的命令。
我们可以简单的把makefile认为是一份定义了源文件间依赖关系、如何编译各个源文件并生成可执行文件的说明书。makefile关系到了整个工程的编译规则,makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
2.程序的编译和链接
首先要把源文件编译成中间代码文件,这个动作叫做编译(compile),
然后再把大量的中间代码文件合成执行文件,这个动作叫作链接(link)
make命令执行时,需要一个 makefile 文件,以告诉make命令如何去编译和链接程序。一个示例:我们的工程有8个c文件,和3个头文件,我们要写一个makefile来告诉make命令如何编译和链接这几个文件
编译链接规则:
1)如果这个工程没有编译过,那么我们的所有c文件都要编译并被链接。
2)如果这个工程的某几个c文件被修改,那么我们只编译被修改的c文件,并链接目标程序。
3)如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的c文件,并链接目标程序
二、makefile介绍
1.makefile规则
target ... : prerequisites ...
command
...
...
target可以是一个object file(目标文件),也可以是一个执行文件,还可以是一个标签(label)。对于标签这种特性,在后续的“伪目标”章节中会有叙述。
prerequisites就是,要生成那个target所需要的文件或是目标。
command也就是make需要执行的命令。(任意的shell命令)
另外command前使用tab
2.make是如何工作的
- make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
- 如果找到,它会找文件中的第一个目标文件(target),并把这个文件作为最终的目标文件。
- 如果目标文件不存在,或是目标文件所依赖的文件的修改时间要比目标文件新,那么,他就会执行后面所定义的命令来生成目标文件。
- 如果目标文件所依赖的文件也不存在,那么make会在当前文件中以此依赖的文件为目标寻找它的依赖性,如果找到则再根据那一个规则生成当前的依赖文件。(这有点像一个堆栈的过程)
这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,就不工作了。
3.makefile的工作方式:
- 读入所有的Makefile。
- 读入被include的其它Makefile。
- 初始化文件中的变量。
- 推导隐晦规则,并分析所有规则。
- 为所有的目标文件创建依赖关系链。
- 根据依赖关系,决定哪些目标要重新生成。
- 执行生成命令
三、书写规则与书写命令
一般来说,make会以UNIX的标准Shell,也就是/bin/sh来执行命令。
1.通配符
make支持三个通配符:“*”,“?”和“~”。这是和Unix的B-Shell是相同的。
2.伪目标
.PHONY: clean
clean:
rm *.o temp
“伪目标”并不是一个文件,只是一个标签,由于“伪目标”不是文件,所以make无法生成它的依赖关系和决定它是否要执行。我们只有通过显示地指明这个“目标”才能让其生效。为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”。伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为“默认目标”,只要将其放在第一个。一个示例就是,如果你的 Makefile需要一口气生成若干个可执行文件,但你只想简单地敲一个make完事,并且,所有的目标文件都写在一个Makefile中,那么你可以使 用“伪目标”这个特性。
3.显示命令
当我们用“@”字符在命令行前,那么,这个命令将不被make显示出来,最具代表性的例子是,我们用这个功能来像屏幕显示一些信息。如:
@echo 正在编译XXX模块......
当make执行时,会输出“正在编译XXX模块......”字串,但不会输出命令,如果没有“@”,那么,make将输出:
echo 正在编译XXX模块......
正在编译XXX模块......
4.命令执行
当依赖目标新于目标时,也就是当规则的目标需要被更新时,make会一条一条的执行其后的命令。需要注意的是,如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令
例如:
exec:
cd /home/hchen; pwd
会先执行cd 再执行pwd
四. 使用变量
1.变量的定义与使用
举例:
edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
“\”是换行的意思
.o文件字符串被重复了两次,可以使用变量来定义:
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
使用变量:$(objects)
2.变量的赋值方式
= 是最基本的赋值
:= 是覆盖之前的值
?= 是如果没有被赋值过就赋予等号后面的值
+= 是添加等号后面的值
"=":递归赋值,即赋值后并不马上生效,等到使用时才真正的赋值,此时通过递归找出当前的值,所有在使用是很有可并不是开始赋的值,所有使用时有应
":=":直接赋值,这就是我们常规的那种赋值方式,一赋值马上有效。在没赋值是为空字符。前面的变量不能使用后面的变量,只能使用前面已定义好的变量
"?=":仅仅在变量还没赋值的情况下才有效,所有一般用在第一次赋值。
"+=": 在变量后加上字符
五、条件判断
条件表达式的语法为:
<conditional-directive>
<text-if-true>
endif
以及:
<conditional-directive>
<text-if-true>
else
<text-if-false>
endif
关键字分为两对:每一对都是相反的意思,所以挑一个讲就行
ifeq 、ifneq
ifdef 、ifndef
1.ifeq <variable-name>比较参数的值是否相等
ifeq (<arg1>, <arg2>)
ifeq '<arg1>' '<arg2>'
ifeq "<arg1>" "<arg2>"
ifeq "<arg1>" '<arg2>'
ifeq '<arg1>' "<arg2>"
比较参数“arg1”和“arg2”的值是否相同。当然,参数中我们还可以使用make的函数。如:
ifeq ((strip(foo)),)
<text-if-empty>
endif
2.ifdef <variable-name> 如果变量<variable-name>的值非空,那到表达式为真。否则,表达式为假
示例一:
bar =
foo = $(bar)
ifdef foo
frobozz = yes
else
frobozz = no
endif
示例二:
foo =
ifdef foo
frobozz = yes
else
frobozz = no
endif
六、使用函数
函数调用,很像变量的使用,也是以“”来标识的,其语法如下:(<function> <arguments>)
或是
{ } 这里,就是函数名,make支持的函数不多。为函数的参数,参数间以逗号“,”分隔,而函数名和参数之间以“空格”分隔。函数调用以“公式输入有误(subst a,b,公式输入有误(subst a,b,{x})”的形式。因为统一会更清楚,也会减少一些不必要的麻烦。
具体函数可以看函数篇,不在此总结了。