MakeFile
作者:解琛
时间:2020 年 4 月 1 日
- MakeFile
- 一、Hello World!
- 二、变量
- 三、变量定义方式
- 四、变量的操作方式
- 五、特殊的变量
- 六 规则
- 七、条件判断
- 八、函数
-
- 8.1 文本处理函数
-
- 8.1.1 $(subst FROM,TO,TEXT)
- 8.1.2 **$(patsubst PATTERN,REPLACEMENT,TEXT)**
- 8.1.3 **$(strip STRING)**
- 8.1.4 **$(findstring FIND, STRING)**
- 8.1.5 **$(filter PATTERN,TEXT)**
- 8.1.6 $(filter-out PATTERN,TEXT)
- 8.1.7 $(sort LIST)
- 8.1.8 $(word N,TEXT)
- 8.1.9 $(wordlist START,END,TEXT)
- 8.1.10 $(words TEXT)
- 8.1.11 $(firstword TEXT)
- 8.2 文件名处理函数
-
- 8.2.1 $(dir NAMES)
- 8.2.2 $(notdir NAMES)
- 8.2.3 $(suffix NAMES)
- 8.2.4 $(basename NAMES)
- 8.2.5 $(addsuffix SUFFIX,NAMES)
- 8.2.6 $(addprefix PREFIX,NAMES)
- 8.2.7 $(wildcard PATTERN)
- 8.2.8 $(foreach VAR,LIST,TEXT)
- 8.2.9 $(if CONDITION,THEN-PART,ELSE-PART)
- 8.2.10 $(call VAR,ARGS,...)
- 8.2.11 $(origin VAR)
- 8.2.12 $(shell COMMANDS)
- 九、Make 选项
- 十、工程应用进阶
- 附录:参考文献
GNU makeGNU make(工程管理器 make 在不同环境有很多版本分支,比如 Qt 下的 qmake,windows 下的 nmake 等,下面提到的 make 指的是 LINUX 下的 GNU make)。
而 Makefile,是 make 的配置文件,用来配置运行 make 的时候的一些相关细节,比如指定编译选项,指定编译环境等等。一般而言,一个工程项目不管是简单还是复杂,每一个源代码子目录都会有一个 Makefile 来管理,然后一般有个所谓的顶层 Makefile 来统一管理所有的子目录 Makefile。
一、Hello World!
funny:
echo "Hello World!"
之后,我们使用make来进行编译。
xiechen@xiechen-Ubuntu:~/文档/apc/1.serialFile/build$ mv MakeFile makefile
xiechen@xiechen-Ubuntu:~/文档/apc/1.serialFile/build$ make
echo "just for fun"
just for fun
xiechen@xiechen-Ubuntu:~/文档/apc/1.serialFile/build$
在这个最简单的 Makefile 只有两行,包含了其最核心的语法:第 1 行的 funny 被称之为目标,因为它后面有一个冒号,冒号后面是这个目标的依赖列表,这个例子中 funny的依赖列表为空。
紧跟着第 2 行的行首是一个制表符(即 Tab 键),这个制表符很重要,不能写成空格,更不能省略,其后紧跟着一个 SHELL 语句(事实上就因为有了那个制表符,make 才知道后面是一个 SHELL 命令)。
这个目标,以及其后的依赖列表(可以没有),以及其下的 SHELL 命令(可以没有),统称为一套规则。
二、变量
2.1 基本的变量
在Makefile中变量的特征有以下几点:
- 变量和函数的展开(除规则的命令行以外),是在make读取Makefile文件时进行的,这里的变量包括了使用“=”定义和使用指示符“define”定义的变量。
- 变量可以用来代表一个文件名列表、编译选项列表、程序运行的选项参数列表、搜索源文件的目录列表、编译输出的目录列表和所有我们能够想到的事物。
- 变量名不能包括“:”、“#”、“=”、前置空白和尾空白的任何字符串。需要注意的是,尽管在GNU make中没有对变量的命名有其它的限制,但定义一个包含除字母、数字和下划线以外的变量的做法也是不可取的,因为除字母、数字和下划线以外的其它字符可能会在以后的make版本中被赋予特殊含义,并且这样命名的变量对于一些SHELL来说不能作为环境变量使用。
- 变量名是大小写敏感的。变量“foo”、“Foo”和“FOO”指的是三个不同的变量。Makefile传统做法是变量名是全采用大写的方式。推荐的做法是在对于内部定义的一般变量(例如:目标文件列表objects)使用小写方式,而对于一些参数列表(例如:编译选项CFLAGS)采用大写方式,这并不是要求的。但需要强调一点:对于一个工程,所有Makefile中的变量命名应保持一种风格,否则会显得你是一个蹩脚的开发者(就像代码的变量命名风格一样),随时有被鄙视的危险。
- 另外有一些变量名只包含了一个或者很少的几个特殊的字符(符号)。称它们为自动化变量。像“<”、“@”、“?”、“*”、“@D”、“%F”、“^D”等等,后面会详述之。
- 变量的引用跟SHELL脚本类似,使用美元符号和圆括号,比如有个变量叫A,那么对他的引用则是 ( A ) , 有 个 自 动 化 变 量 叫 @ , 则 对 他 的 引 用 是 (A),有个自动化变量叫@,则对他的引用是 (A),有个自动化变量叫@,则对他的引用是(@),有个系统变量是CC则对其引用的格式是 ( C C ) 。 对 于 前 面 两 个 变 量 而 言 , 他 们 都 是 单 字 符 变 量 , 因 此 对 他 们 引 用 的 括 号 可 以 省 略 , 写 成 (CC)。对于前面两个变量而言,他们都是单字符变量,因此对他们引用的括号可以省略,写成 (CC)。对于前面两个变量而言,他们都是单字符变量,因此对他们引用的括号可以省略,写成A和$@。
来写一个简单的程序,熟悉一下这些基本变量的使用方法。
xiechen@xiechen-Ubuntu:~/文档/apc/1.serialFile/build$ cat makefile -n
1 A = xiechen
2 B = I love China
3 C = handsome $(A)
4
5 all:
6 @echo $(A)
7 @echo $(B)
8 @echo $(C)
xiechen@xiechen-Ubuntu:~/文档/apc/1.serialFile/build$ make
xiechen
I love China
handsome xiechen
2.2 系统预定义变量
CFLAGS、CC、MAKE、SHELL 等等,这些变量已经有了系统预定义好的值,当然我们可以根据需要重新给他们赋值,例如 CC 的默认值是 gcc,当我们需要使用 c 编译器的时候可以直接使用它。
这样做的好处是:在不同平台中,c 编译器的名称也许会发生变化,如果我们的 Makefile使用了 100 处 c 编译器的名字,那么换一个平台我们只需要重新给预定义变量 CC 赋值一次即可,而不需要修改 100 处不同的地方。比如我们换到 ARM 开发平台中,只需要重新给 CC 赋值为 arm-linux-gnu-gcc。
xiechen@xiechen-Ubuntu:~/文档/apc/1.serialFile/build$ cat makefile -n
1 jerome:
2 @echo $(CFLAGS)
3 @echo $(CC)
4 @echo $(MAKE)
5 @echo $(SHELL)
xiechen@xiechen-Ubuntu:~/文档/apc/1.serialFile/build$ make
cc
make
/bin/sh
2.3 自动化变量
<、@、?、#等等,这些特殊的变量之所以称为自动化变量,是因为他们的值会“自动地”发生变化,考虑普通的变量,只要你不给他重新赋值,那么他的值是永久不变的,比如上面的 CC,只要不对他重新赋值,CC 永远都等于 arm-linux-gnu-gcc。但是自动化变量的值是不固定的,你不能说@的值等于几,但是他的含义的固定的:@代表了其所在规则的目标的完整名称。
三、变量定义方式
3.1 递归定义
A = I love $(B)
B = China
此处,在变量 B 出现之前,变量 A 的定义包含了对变量 B 的引用,由于 A 的定义方式是所谓的“递归”定义方式,因此当出现$(B)时会对全文件进行搜索,找到 B 的值并代进A 中,结果变量 A 的值是 “I love China”。
3.2 直接定义
B = China
A := I love $(B)
此处,定义 A 时用的是所谓的“直接”定义方式,说白了就是如果其定义里出现有对其他变量的引用的话,只会其前面的语句进行搜寻(不包含自己所在的那一行),而不是搜寻整个文件。
3.3 条件定义
A = apple
A ?= I love China
此处对 A 进行了两次定义,其中第二次是条件定义,其含义是:如果 A 在此之前没有定义,则定义为“I love China”,否则维持原有的值。
3.4 多行命令定义
define commands
echo “thank you!”
echo “you are welcome.”
endef
此处定义了一个包含多行命令的变量commands,我们利用它的这个特点实现一个完整命令包的定义。注意其语法格式:以define开头,以endef结束,所要定义的变量名必须在指示符“define”的同一行之后,指示符define所在行的下一行开始一直到“end”所在行的上一行之间的若干行,是变量的值。这种方式定义的所谓命令包,可以理解为编程语言中的函数。
四、变量的操作方式
4.1 追加变量的值
A = apple
A += tree
这样,变量A的值就是apple tree。
4.2 修改变量的值
A = srt.c string.c tcl.c
B = $(A:%.c=%.o)
这样,变量B的值就变成了 srt.o string.o tcl.o。例子中$(A:%.c=%.o)的意思是:将变量A中所有以.c作为后缀的单词,替换为以.o作为后缀。其实这种变量的替换功能是内嵌函数patsubst的简单版本,使用patsubst也可以实现这个替换的功能:
A = srt.c string.c tcl.c
B = $(patsubst %.c, %.o, $(A)
4.3 override一个变量
override CFLAGS += -Wall
在执行make时,通常可以在命令行中携带一个变量的定义,如果这个变量跟Makefile中出现的某一变量重名,那么命令行变量的定义将会覆盖Makefile中的变量。就是说,对于一个在Makefile中使用常规方式(使用“=”、“:=”或者“define”)定义的变量,我们可以在执行make时通过命令行方式重新指定这个变量的值命令行指定的值将替代出现在Makefile中此变量的值。
4.4 导出变量
export CFLAGS = -Wall -g
在Makefile中导出一个变量的作用是:使得该变量可以传递给子Makefile。在缺省的情况下,除了两个特殊的变量”SHELL”、”MAKEFLAGS”、不为空的”MAKEFILES”以及在执行make之前就已经存在的环境变量之外,其他变量不会被传递给子Makefile。
对于默认就会被传递给子Makefile的变量,可以使用unexport来阻止他们的传递。
unexport MAKEFLAGS
五、特殊的变量
5.1 VPATH
这个特殊的变量用以指定Makefile中文件的备用搜寻路径:当Make