格式:
目标:依赖文件的集合
命令1
命令2
……
注意:命令以 TAB 开始,不能使用空格
例如:
main:main.o a.o b.o
gcc main.o a.o b.o -o main
main.o:main.c
gcc -c main.c
a.o:a.c
gcc -c a.c
b.o:b.c
gcc -c b.c
clean:
rm *.o
rm main
1) -f 和 --file 参数:指定 Makefile 文件,如 make -f Makefile.sdk 或 make --file Makefile.sdk
2) @ :添加到命令行前,此命令不回显
3) #:行注释
4)- :添加到最前面,忽略此行可能发生的错误,继续执行
6) 环境变量 MAKEFILES:(不推荐使用)若存在此变量,make 会先把其值包含到 Makefile 中再执行。其中的 目标 不起作用,发现错误,不理会
8) $ :打开 Makefile 中定义的变量
9) $$ :打开 Makefile 中的 shell 变量
10) $(MAKE):调用 make 命令
-C 指定子目录,如 $(MAKE) -C XXX
export 和 unexport ,向子 makefile 传递变量 和 不向子 makefile 传递变量,特别说明,SHELL 和 MAKEFLAGS 两变量默认传递
11) $(CURDIR):当前目录
文件路径
若未指定路径,make 只会搜索当前目录;若指定了路径,make 依然会优先搜寻当前目录,在未找到的情况下,再到指定目录下寻找。另外在大工程中文件很多,通常会做分类,因此需要告知路径
Makefile 书写中的特殊变量 VPATH 定义了路径,VPATH = 目录:目录 ,中间由冒号 : 分隔,指定了两个目录,make 会按序搜索
Makefile 书写中的关键字 vpath 可以指定不同文件的不同路径,其中 pattern 需要用模式规则 % 将这些文件定性为文件集,使用方法有 3 种:
1)vpath pattern directories
为符合模式 pattern 的文件指定搜索路径 directories ,若连续使用且出现重复 pattern ,则按顺序执行,都会执行
如 vpath %.h ../headers
2)vpath pattern
清除符合模式 pattern 的文件的搜索路径
3)vpath
清除所有已设置的搜索路径
Makefile 书写中会 include 包含其他 Makefile 文件,如 include foo.make *.mk $(bar) ,在执行时需告知被包含文件的路径。在 make 命令后增加参数 -I 和 --include-dir ,指定所包含的文件的位置,如 make -I /user/local/bin 或 make --include-dir /user/include
此外,make 也可自动寻找源文件所包含的头文件,并生成一个依赖关系
gcc -M main.c
等价于
main.o:main.c defs.h \
/user/include/stdio.h \
/user/lib/gcc-lib/i486-suse-linux/include/stddef.h
另外如果使用的是 GNU 的 C/C++ 编译器时,当参数为 -M = 标准库头文件+被包含头文件,当参数为 -MM = 被包含头文件
赋值符
1. =
变量的实际值等于以此法最后一次赋的值,且在全文中该变量一直为实际值
name = abc
temp = $(name)
name = def
printf:
@echo $(temp)
此时输出 temp 的值为 def ,而不是 abc
2. :=
此方法是赋予变量实际值,且不会被 = 改变
name := abc
name = def
printf:
@echo $(name)
此时输出 name 的值为 abc ,而不是 def
3. ?=
如果变量之前未赋值,则赋值,否则使用之前的赋值
name := abc
name ?= def
printf:
@echo $(name)
此时输出 name 的值为 abc
4. +=
Makefile 中变量的值是字符串,此法为追加字符串
name := abc
name += def
printf:
@echo $(name)
此时输出 name 的值为 abc def,注意自动加了空格
通配符
make 支持 3 种通配符,* ? [...] ,此外还有波浪号 ~ ,它在文件名中有两种用途,一是表示当前用户 $HOME 目录,如 ~/test ,二是表示某用户的宿主目录,如 ~someone/test ,就是 someone 用户的宿主目录下的 test ,但在 Windows 或 MS-DOS 下用户没有宿主目录,那么此时 ~ 所指的目录就是环境变量 HOME 。这些都和 Unix 的 B-shell 相同
若文件名中有通配符,可以用转义字符 \ ,表示真实的符号,如 \*
通配符不会像 % 一样展开,比如 temp = *.o ,temp 的值并不是 a.o b.o c.o ,就是 *.o ,若想展开,需要函数 wildcard ,如 temp := $(wildcard *.o)
模式
%
表示任意长度的非空字符串,用于对文件名的匹配,用法类似通配符,表示符合匹配规则的所有文件的集合,即已展开
#初始 Makefile 可再简化为
object=main.o a.o b.o
main:$(object)
gcc $(object) -o main
%.o:%.c
#命令
clean:
rm *.o
rm main
自动变化量
它们表示一类文件的集合,并依次取出,即展开
只该在模式规则下的命令中使用,虽然非模式规则下依然可用,但有可能无法识别
自动变化量 | 描述 |
$@ |
目标,若目标有多个,则表示触发当前规则的那些目标; 若目标是以模式规则定义的(%X),则表示这一集合 |
$% |
若目标是函数库,表示为目标成员名,若不是函数库,其值为空 如:目标 = foo.a(bar.o) 则 $% = bar.o,$@ = foo.a |
$< |
依赖文件集合中的第一个文件(从左往右); 若依赖文件是以模式规则定义的(%X),则表示这一集合 |
$? | 所有比 目标文件 新的 依赖文件 的集合 |
$^ | 依赖文件集合,且去除重复文件 |
$+ | 依赖文件集合,不去重 |
$* |
若目标文件的后缀是 GNU make 所能识别的,则表示不加后缀的目标文件名,否则为空值 若目标是以模式规则定义的(%X),则表示 % 之前的部分 如:目标 = test/a.test.c,用 a.%.c 表示,则 $* = test/a.test 。是字符串 |
#初始 Makefile 再简化为
object=main.o a.o b.o
main:$(object)
gcc $(object) -o $@
%.o:%.c
gcc -c $<
clean:
rm *.o
rm main
伪目标
通过指定伪目标的方式,达到执行其下命令的目的。另外还能通过伪目标实现多目标编译。
为什么 make 的时候不执行 clean 下的命令?因为它没有被第一个目标直接或间接关联,Makefile 的执行是以第一个目标为最终目的,层层寻找依赖关系。想要执行需要指明,即 make clean
若当前目录下有同名文件,此时伪目标变性为目标,又因该目标没有依赖文件,所以会永远被认为是最新的,进而导致其下的命令不会执行。为了避免此问题,可以添加伪目标声明 .PHONY:clean ,此时不管有没有同名文件,都会认为是伪目标
object=main.o a.o b.o
main:$(object)
gcc $(object) -o $@
%.o:%.c
gcc -c $<
.PHONY:clean
clean:
rm *.o
rm main
多目标编译时,将伪目标定为最终目标,所有实际目标作为它的依赖文件集合,命令为空
all:a b c
.PHONY:all
a:a1.o a2.o
gcc a1.o a2.o -o a
b:b1.o b2.o
gcc b1.o b2.o -o b
c:c1.o c2.o
gcc c1.o c2.o -o c
伪目标作为依赖文件集合,可以实现清除不同种类文件的目的
.PHONY:cleanall cleanobj cleandiff
cleanall:cleanobj cleandiff
rm program
cleanobj:
rm *.o
cleandiff:
rm *.diff
静态模式
更好的定义多目标的执行规则
<targets ...>: <target-pattern>: <prereq-patterns ...>
<commands>
....
targets:多个目标的集合,一般都有通配符
target-pattern:按此规则对目标集合再分类的集合,应该有 %
prereq-patterns:依赖文件的集合,也是一个规则,替换目标后缀改为本规则后缀,应该有 %
例如:
objects = foo.o bar.o
all:$(objects)
$(objects):%.o:%.c
#命令
# 目标集合为 $(objects)
# 从中取符合 %.o 规则的,即后缀为 .o 的一批作为目标
# 取 %.o 目标集合的 % 所代表的字符串,其后加上 .c 后缀,即 foo.c bar.c
还可搭配 filter 函数使用
files = foo.elc bar.o lose.o
$(filter %.o,$(files)):%.o:%.c
#命令
$(filter %.elc,$(files)):%.elc:%.el
#命令
# 用 filter 函数过滤掉不符合规则的项,然后操作
判断
格式:
判断关键字
条件为真时执行的代码块
else
条件为假时执行的代码块
endif
判断关键字:
ifeq
判断是否相等
ifeq(参数1,参数2)
ifeq '参数1','参数2'
ifeq "参数1","参数2"
ifeq "参数1",'参数2'
ifeq '参数1',"参数2"
ifneq
判断是否不相等
ifneq(参数1,参数2)
ifneq '参数1','参数2'
ifneq "参数1","参数2"
ifneq "参数1",'参数2'
ifneq '参数1',"参数2"
ifdef
判断变量值是否非空
ifdef 变量
ifndef
判断变量值是否空
ifndef 变量
函数
Makefile 有已经定义好的函数,不支持自定义
$(函数名 参数1,参数2,...)
${函数名 参数1,参数2,...}
subst
实现字符串替换。将 text 中的 from 替换为 to,返回被替换后的字符串
$(subst from,to,text)
如:$(subst AAA,BBB,there is AAA)
patsubst
实现含有通配符 % 的字符串替换。先检查 text 中是否有和 pattern 匹配的字符串,若有就用 replacement 替换掉。其中 pattern 和 replacement 的 % 所代表的字符串相同,返回被替换后的字符串
$(patsubst pattern,replacement,text)
如:$(patsubst %.c,%.o,a.c b.c c.c d.c)
dir
提取参数中的目录部分作为返回值
$(dir names...)
如:$(dir src/a.c)
notdir
提取参数中的文件名作为返回值
$(notdir names...)
如:$(notdir src/a.c)
foreach
用作循环,把 list 字符串中的项逐一取出放入到 var 所指定的变量中,然后将 text 字符串返回,返回的字符串之间以空格隔开,循环结束时整体作为返回值返回
$(foreach <var>,<list>,<text>)
如:
name = a b c d
files = $(foreach n,$(name),$(n).o)
$(name)中的项逐一取出,存到变量 n 中,每次将 $(n).o 返回,最终 files = a.o b.o c.o d.o
wildcard
展开通配符,用在定义变量或函数使用中
$(wildcard PATTERN...)
如:files = $(foreach name,$(wildcard *.o),$(name))
origin
返回该变量的来源。若是在命令行定义的,其值为 command line
$(origin variable)
如:$(origin V)
filter
过滤 text 字符串中的项,仅保留符合 pattern 模式的项,返回过滤后的字符串
$(filter pattern,text)
如:$(filter 4.%,$(MAKE_VERSION))
firstword
取出 text 字符串中的第一个项并作为返回值返回
$(firstword text)
如:$(firstword x$(MAKEFLAGS))