Makefile 小记
Linux 中使用最广的构建工具是 make,而 make 会读取 Makefile 文件中的配置信息来完成构建,一个简单的 Makefile 文件如下所示:
1 | editor : main.o text.o |
具体而言,冒号前面为 target,即要生成的文件;冒号后面为 dependencies,即被依赖的文件;每一个 target:dependencies 对的下一行为要执行的命令(注意要以 Tab 键起首)。当 make 不带参数时,默认执行第一个 target。target 也可以是要求 make 要完成的动作,执行这种 target 后并不能得到和 target 同名的文件,因此,也称做 phony target(即伪 target),如第 7 行所示。dependencies 也可以为空,如常用的 target 为 clean
时,就没有依赖,只有命令,一般用于清理工作。
当输入 make
或 make editor
,即可开始构建。若 editor
这个 target 文件不存在,或者 main.o
、text.o
这两个依赖文件被修改,都会导致 make 调用其下的命令 gcc -o editor main.o text.o
;接下来,由于引用到 main.o
和 text.o
,make 会检查 main.o
的依赖 main.c
、def.h
有无更新,如果有,则执行其下的命令 gcc -c main.c
;同理,也适用于 text.o
。当输入 make install
,make 会检查 install
的依赖 editor
是否是最新,如果是,则执行其下的命令 mv editor /usr/local
。
注释
Makefile 中以 #
开头的均为注释。
回声
正常情况下,make 会打印每条命令,然后再执行,这就叫做回声。在命令的前面加上 @
,就可以关闭回声。由于在构建过程中,需要了解当前在执行哪条命令,所以通常只在注释和纯显示的 echo
命令前面加上 @
。
内置目标名
内置目标名指示了如果某些名称作为 target(目标名)出现,则具有特殊含义,常用的如下所示:
.PHONY
:明确声明伪目标.SUFFIXES
:消除默认后缀规则.DELETE_ON_ERROR
:如果遇到错误(或 make 中断)则删除目标文件
更多的内置目标名可以参考 make 官方手册。
变量
Makefile 中也可以使用变量,如下所示:
1 | TXT = Hello World |
这类似于 C 语言中的宏,按照传统,变量名一般大写,使用变量时要放在 $()
之中。
有时,变量的值可能指向另一个变量,比如:V1 = $(V2)
,这时会出现一个问题,V1
的值到底在定义时扩展(静态扩展),还是在运行时扩展(动态扩展)?如果 V2
的值是动态变化的,这两种扩展方式的结果可能会差异很大。为了解决类似问题,Makefile 一共提供了四个赋值运算符,如下所示:
1 | # 在运行时扩展,允许递归扩展。 |
内置变量
Makefile 提供了一系列的内置变量,常见的如下所示:
$(CC)
:指向当前使用的编译器
更多的内置变量可以参考 make 官方手册。
自动变量
判断
使用条件判断,可以让 make 根据运行时的不同情况选择不同的执行分支。如下所示:
1 | ifeq ($(CC),gcc) |
上面代码判断当前编译器是否为 gcc,然后指定不同的库文件。其中 ifeq
比较参数 arg1
和 arg2
是否相同,类似的,ifneq
比较参数 arg1
和 arg2
是否不相同。
除此之外,还有 ifdef
判断变量是否被定义,ifndef
判断变量是否没有被定义。
函数
Makefile 中还内置了许多函数,可供调用,格式如下:
1 | $(function arguments) |
常用的函数有:
$(shell)
:用来执行 shell 命令$(wildcard)
:用来在 Makefile 中,替换 Bash 的通配符。$(patsubst)
:用于模式匹配的替换,语法为$(patsubst <pattern>,<replacement>,<text>)
$(filter)
:$(addsuffix)
:$(addprefix)
:$(if)
:$(foreach)
:$(call)
:唯一一个可以用来创建新的参数化的函数,语法为$(call <expression>,<parm1>,<parm2>,...,<parmn>)
,值得注意的是,call 函数在处理参数时,第 2 个及其之后的参数中的空格会被保留,因而在向 call 函数提供参数时,最安全的做法是去除所有多余的空格,避免造成一些奇怪的效果。
相关示例如下:
1 | # shell函数用法 |
更多的内置函数可以参考 make 官方手册
引用其它的 Makefile
在 Makefile 中可以使用 include
关键字把别的 Makefile 包含进来,这很像 C 语言的 #include
,被包含的文件会原模原样的放在当前文件的包含位置。include
的语法是:include <filename>
,其中被包含的 Makefile 文件通常以 .mk
结尾。