Makefile教程
1 Makefile语法和规则
语法:
targets : prerequisites
command
...
或
targets : prerequisites ; command
command
其中:
-
target为生成目标, 一般使用文件名或伪目标作为target, 多个target用空格分开,可使用通配符。 除了指定要生成的target, 其他target若没有被依赖则不会执行。要生成的target通过make target命令指定, 不指定则第一个target为默认target。
-
prerequisite可以是target、文件, 若prerequisite有同名的target, 会先按规则执行同名target下的command。
-
command 如果在新行必须以
Tab
开头; 若一行有多条语句, 用分号;
分隔。(以Tab开头的都会被当做command, 所以Makefile其他语句前不能使用Tab, 排版应使用空格)command结果只对当前行有效(每行command处于不同进程下)。例子:
exec: cd /home/raina pwd # 没有切换路径, 仍是当前目录 exec: cd /home/raina; pwd # 成功切换到/home/raina
规则:
- 仅当target不存在或prerequisite中有一个文件比target新时才会执行下面的command。
- 没有prerequisite的target仅当target不存在时command才执行(注意自动推导可能会为target添加依赖)。
2 make运行
Makefile使用make
命令运行, 将Makefile文件名保存成Makefile或makefile, 然后在终端运行make
命令, 会寻找当前目录下的Makefile或makefile文件, 自动运行。
2.1 make的选项
make -n
# 仅显示命令, 不执行命令
make -s
# 禁止所有命令语句本身的显示
make -f xxx
# 指定makefile文件为xxx
make -t
# 更新目标文件时间
make -q
# 寻找目标是否存在, 存在不会输出, 不存在打印错误信息
make -B
# 所有目标都需要重新编译
make -c dir
# 指定读取Makefile的目录
make -e
# 环境变量将覆盖Makefile内定义的变量
make -I dir
# 添加头文件查找路径
make -r
# 禁止make任何隐含规则
make -R
# 禁止make使用任何作用于变量上的规则
2.2 make执行过程
Makfile文件内容:
test : main.o
gcc -o test main.o
main.o : main.c
gcc -c -o main.o main.c
- make会在当前目录下寻找“Makefile”或“makefile”文件
- 找到后会找到Makefile中第一个target,也就是上面例子中的
test
- test的依赖
main.o
有对应的生成规则, 转去main.o:main.c
- 如果main.o不存在, 或当前目录下的main.c比main.o新, 就会执行下面的
gcc -c -o main.o main.c
; 若main.o比main.c新, 下面给的command就不会执行 - 若执行了main.o下的命令, 说明main.o已经更新了, test下的命令就会执行; 若main.o没有重新生成, 则比较当前目录下的main.o和test, 若main.o更新则执行
gcc -o test main.o
, 否则不执行。
在make的过程中,如果没有依赖的生成规则, 路径下也没有该依赖文件,或者command执行出错,make会直接退出。
3 Makefile特殊字符
-
单行注释:
#
-
换行符:
\
-
转义字符:
\
(若要转义’\(‘符号, 需要用`\)$`表示) -
通配符: Makefile支持shell的
*
、?
、[...]
通配符; -
模式匹配符:
%
-
不显示命令:
@
, 执行的command语句默认会被显示到中断, 若不希望make执行过程中显示某command, 则在前面添加@
符号:all: @echo hhhhhh
4 变量
4.1 变量的定义和使用
定义:
var = value
变量在声明时必须赋初值, Makefile没有数据类型, 变量的定义类似与C/C++的宏, 但是可以改变其值。
例子:
# 若当前目录下有a.o、b.o、c.o三个文件
objs = *.o
# objs的值就是*.o, 而不是通配符展开后的结果
Makefile变量定义的位置没有要求, 可以使用后面定义的变量。
变量的命名规则:
- 变量名可以使用字符、数字、下划线, 可以以数字开头
- 变量名不能包含
:
#
=
空字符
- 变量名大小写敏感
**使用: **
$var
$(var)
${var}
若使用没有定义的变量, 其值将为空。
4.2 多行值的变量
若变量的值有多行, 使用define ... endef
来定义:
define var # var为变量名
val-part1 # var的值
val-part2
...
endef
4.3 命令行变量
在命令行定义变量:
make var=value
注意:
-
等号两边不能加空格;
-
若Makefile文件中有和命令行变量同名的变量, 在Makefile中改变该变量值的语句会被忽略(包括define的多行值的变量和目标变量);
-
在Makefile若要修改命令行变量的值, 需在前面添加override 提示符:
override var=value
4.4 自动化变量
自动化变量 | 说明 |
---|---|
$@ | 表示target |
$< | 表示第一个prerequisite |
$^ | 所有的prerequisite的集合, 空格分隔, 重复的prerequisite会合并 |
$+ | 所有的prerequisite, 空格分隔, 重复的prerequisite不合并 |
$? | 所有比target新的prerequisite的集合, 以空格分隔 |
4.5 模式变量
模式变量: 包含%
的变量。
使用:
类似与shell的*
通配符:
%.o : CFLAG = -o # 定义一个目标变量, 作用于以’.o‘结尾的target
Makefile静态模式规则:
%.o: %.c # %.c中%的值为target与%.o匹配到的%的值
gcc -c $< -o $@
OBJS = a.o b.o
all: $(OBJS) c.o
# 仅对OBJS内的元素有效, c.o不会匹配此规则
$(OBJS): %.o: %.c # a.o的依赖为a.c, b.o的依赖为b.c
gcc -c $< -o $@
4.6 目标变量
目标变量仅在作用的target范围内有效, 类似于局部变量。
定义目标变量只需在变量定义前加其作用的target和冒号:
main : CFLAGS = -o # 仅在main目标范围内有效
main.o : CFLAGS = -c -o # 仅在main.o目标范围内有效
main : main.o
gcc $(CFLAGS) main main.o # gcc -o main main.o
main.o : main.c
gcc $(CFLAGS) main.o main.c # gcc -c -o main main.o
4.7 内置变量
$(CURDIR)
: 当前所在路径
$(MAKE_VERSION)
: make的版本号
5 赋值运算符
=
基本的赋值, 若变量后面被重新赋值, 会被覆盖
x = 1
y = $(x)
all:
echo $(y) # 2
x = 2
:=
若右值含变量, 则使用当前位置该变量的值(而不是整个Makefile展开后的值)
x = 1
y := $(x)
all:
echo $(y) # 1
x = 2
+=
追加变量的值(会用空格分隔)
var = a
var += b
all:
echo $(var) # a b
6 伪目标
.PHONY
用于标记伪目标, 被标记为伪目标的target, 即使当前路径下target已经存在且为最新, 也会执行下面的command。即伪目标一经调用, 一定执行。
例子:
.PHONY: clean cleanobj # 标记clean和cleanobj为伪目标
clean: cleanobj
rm -f main
cleanobj:
rm -f *.o
常见伪目标:
- all : 编译所有目标
- clean: 删除所有被make创建的文件
- install: 安装已编译好的程序, 将目标执行文件拷贝到指定路径
- print: 列出改变过的源文件
- dist: 创建一个压缩文件, 一般是将tar压缩成gz文件
- TAGS: 更新所有目标, 以完整地重新编译使用
- check或test: 测试makefile流程
- tar: 将源程序打包备份
7 条件判断
判断已定义:
ifdef var-name # var-name为变量名, 不加$
... # 不能用Tab锁紧, 只能使用空格
else # else分支可以没有
...
endif
判断未定义:
ifndef var-name
...
else
...
endif
判断相等:
ifeq (arg1, arg2)
# 或 ifeq 'arg1' 'arg2'
# 或 ifeq "arg1" "arg2"
...
else
...
endif
判断不相等:
ifneq (arg1, arg2)
# 或 ifneq 'arg1' 'arg2'
# 或 ifneq "arg1" "arg2"
...
else
...
endif
注意: make 是在读取 Makefile 时就计算条件表达式的值, 并根据条件表达式的值来选择语句, 所以, 最好不要把自动化变量(如“$@”等)放入条件表达式中, 因为自动化变量是在运行时才有的。
8 函数的使用
使用方法:
$(func args)
或
${func args}
常用函数:
$(subset from, to, text)
# 功能: 把字串text中的from字符串替换成to
# 返回值: 被替换后的字符串
$(patsubst pattern, replacement, text)
# 功能: 把text中符合pattern的元素替换成replacement
# 返回值: 被替换后的字符串
$(dir paths)
# 功能: 取出每个path的所在目录(最后一个/及之前的内容, 没有/为当前目录)
# 多个path用空格分开
# 返回值: 返回所在目录, 当前目录返回./
$(notdir paths)
# 功能: 取出每个path的文件命名
# 返回值: 取到的文件名
此文原创禁止转载,转载文章请联系博主并注明来源和出处,谢谢!
作者: Raina_RLN https://www.cnblogs.com/raina/