Android 源代码上手指南 - Makefile语法
上一篇我们将到android build system, 讲了envsetup.sh和lunch, 在继续研究build过程之前有必要先讲讲Makefile的语法. android使用
(不支持其他版本!), 因此有许多GNU的特定语法, 和一般的Makefile长得很不一样. (为了叙述方便, 本文中凡是提到1
GNUMake 3.81
的地方, 均指1
Makefile
.) Makefile的基本语法是这样的:1
GNU Makefile
1
2
TARGET: <DEPENDS>
<TAB> instructions
运行
命令时, 根据TARGET对应的DEPENDS来确定依赖关系, 根据修改时间来判断某个TARGET是否需要rebuild.如果需要, 则执行1
make TARGET
对应的instructions. 这就是Makefile的基本原理.1
TARGET
.PHONY targets
在Makefile中, 有时会遇到标明为
的target, 它表示这个target只是一个名字, 不是一个文件. 例如:1
.PHONY
这个名为
的target指示makefile去做一些删除临时文件的指令, 但如果你的目录下刚好有个名为1
clean
的文件, 又因为clean没有任何的dependencies, make会认为1
clean
这个文件是最新的, 不需要执行. 这时我们就要告诉make, 1
clean
这个target只是一个名称而已, 并不是一个文件:1
clean
multiple targets
当一行写有个多个target时, 相当于把这些target分开写, 并使用一样的commands. 例如:
等价于:
其中$@会被替换为target名称.
在
中, 有一些特定的语法在android中广泛使用, 下面一一介绍一下:1
GNUMake
define
在
中, 定义一个变量可以用1
Makefile
或者1
=
.
其中1
:=
和1
=
的区别在于, 1
:=
是1
=
, 它在每次被用到的时候都会被求值; 而1
recusively expanded variable
是1
:=
, 它在定义的时候就被展开. 例如:1
simply expanded variable
执行
会显示:1
make
1
2
3
4
echo var1: bar
var1: bar
echo var2: foo
var2: foo
除了
和1
=
, 还有一个1
:=
, 它只在变量没有被定义时起作用, 常用于设置默认值.1
?=
define 也可以用来定义一个变量, 但define更强的一点在于可以定义多行变量. 例如:
输出:
foo bar
其中
放在一个命令前, 表示不输出这一行执行的命令.
不过, 在Makefile中有一个1
@
函数, 它可以”调用”一个变量, 因此变量可以用来实现类似于宏展开的功能. 例如:1
call
会输出:
1
foo,bar
这里有个坑:
注意我没有输入空格, 如果我写成1
$(call concat,foo,bar)
, 那么在concat展开的时候, 参数1
$(call concat, foo, bar)
就会等于前面带一个空格的1
$1
, 1
_foo
就会等于前面带一个空格的1
$2
(我这里用下划线来表示空格作为警示). 这个在用的时候要格外小心!1
_bar
include
用于包含另一个Makefile, 这个在比较大规模的项目中比较有用, 有助于Makefile的重用和模块化. 例如你可以将一些常用的宏扩展和变量定义放在一个1
include
中, 然后从你的Makefile中include之.1
definitions.mk
当include一个文件而这个文件不存在时, Make会报错, 而在
之前加上1
include
表示文件不存在时不要报错. 这个常用来包含可选的文件.1
-
if
在make语法中有一些常用的条件语句, 类似于C语言中的
和1
#ifdef
等. 例如:1
#if
检测后面两个值是否相等. 类似的还有1
ifeq
, 1
ifneq
, 1
ifdef
检测一个变量是否被定义, 等等.1
ifndef
函数
makefile中内置了一些函数, 方便实现一些常用的功能. 调用一个函数的语法是:
下面列举一些常见函数:
字符串操作
- $(subst from, to, text) 字符串替换
- $(strip string) 去掉开头和末尾的空白. (还记得前面说的那个坑吗?)
- $(filter pattern…, text) 取出所有匹配的文本, 丢到所有不匹配的. 例如
取出1
$(filter %.c %.s,$(sources))
变量中所有以1
sources
和1
.c
结尾的文本.1
.s
- $(filter-out pattern…, _text) 和上面相反, 取出所有不匹配的.
- $(word n, text) 取出
中第1
text
项.1
n
文件名操作
- $(dir names…) 取出
中所有文件的目录部分.1
names
- $(notdir names…) 和上面相反, 取出非目录部分(纯文件名部分).
- $(add_prefix prefix, names…) 给文件加前缀
- $(wildcard pattern) 获取匹配通配符的文件名. 例如
返回所有1
objects := $(wildcard *.o)
结尾的文件.1
.o
foreach
$(foreach var, list, text) 将
作为形式参数, 1
var
作为列表, 扩展1
list
的内容. 相当于python中的:1
text
call
在前面提到了, 用于”展开”一个宏(变量)
eval
是个很重要的函数, 可以用来实现Makefile的模板. 比较关键的一点是需要知道传给1
eval
的参数被展开了两次, 首先是传给eval之前, 然后再被当作makefile的语句include到当前的地方. 这个我们之后会继续讲到, 先放一个例子在这里.1
eval
输出调试
这些语句被用于输出调试信息: (debug和研究时都非常有用!)
- $(warning text…)
- $(info text…)
- $(origin variable) 可以告诉你一个变量是从哪里来的, 是环境中定义的, 还是makefile中定义的, 还是命令行中定义的. 这个在调试的时候也很有用. 参考这里
运行shell语句
- $(shell command) 用于运行一个shell语句, 然后将输出返回. 这个也非常有用, 例如有了这个你就可以用你自己喜欢的工具例如perl之类来进行字符串操作了, 可以不用费力学习和记忆makefile中相应的函数的语法.