Makefile文件语法

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Makefile⽂件语法
概述
本⽂将介绍Makefile种注释、回显、通配符、变量、循环判断、函数
注释
Makefile中只有单⾏注释,没有多⾏注释,注释以 # 开头。

以下Makefile注释⽚段节选⾃的Makefile
# Makefile for installing Lua
# See doc/readme.html for installation and customization instructions.
# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================
# Your platform. See PLATS for possible values.
PLAT= none
echoing(回显)
通常,make在执⾏命令⾏之前会把要执⾏的命令⾏进⾏输出。

我们称之为“回显”,就好像我们输⼊命令执⾏⼀样。

@
如果要执⾏的命令⾏以字符“@”开始,则make在执⾏时这个命令就不会被回显。

典型的⽤法是我们在使⽤“echo”命令输出⼀些信息时。

如:
@echo 开始编译XXX模块......
当make执⾏时,将输出“开始编译XXX模块......”这个信息。

如果在命令⾏之前没有字符“@”,那么,make的输出就是:
echo编译XXX模块......
编译XXX模块......
“-n”或“--just-print”
如果使⽤make的命令⾏参数“-n”或“--just-print”,那么make执⾏时只显⽰所要执⾏的命令,但不会真正的去执⾏这些命令。

只有在这种情况下make才会打印出所有make需要执⾏的命令,其中也包括了使⽤“@”字符开始的命令。

这个选项对于我们调试Makefile⾮常有⽤,使⽤这个选项我们可以按执⾏顺序打印出Makefile中所有需要执⾏的命令。

“-s”或“--slient”
make参数“-s”或“--slient”则是禁⽌所有执⾏命令的显⽰,就好像所有的命令⾏均使⽤“@”开始⼀样。

.SILENT
这个关键字的⾏为很像.PHONY,.PHONY标记的target可以理解成⼀个⽆条件执⾏的动作。

被.SILENT标价的target,为完成该target执⾏的所有command都是没有回显的。

.SILENT:clean
.PHONY:clean
clean:
rm -rf *.o
很显然这⾥⾯控制回显最灵活的⽅式就是@,推荐使⽤“@”来控制命令的回显。

通配符(Wildcard )
概述
Makefile中使⽤的通配符与Bash下⾯的filename wildcards⼀样,但似乎Makefile主要使⽤‘*’, ‘?’ 和 ‘[…]’
~字符也有特殊意义,~ 或者 ~/file name 代表home directory
~ 展开为 /home/you 你的家⽬录
~/bin 展开为 /home/you/bin 你的加⽬录下边的bin⽬录
~后⾯不接/则表⽰别⼈的家⽬录
~john 展开为 /home/john john的家⽬录
~john/bin 展开为 /home/john/bin john的家⽬录下边的bin⽬录
targets 和 prerequisites中的通配符,有make⼯具负责展开。

commands中的通配符则由shell负责展开。

除此之外的其他情况,要想展开通配符则需要显式调⽤wildcard函数。

由于*默认情况下具有通配符的特殊含义,如果需要按照普通字符理解他,则需要使⽤ \* 转义。

通配符举例
在commands中使⽤通配符
此时通配符展开⼯作由shell负责
clean:
rm -f *.o
在prerequisites 中使⽤通配符
此时通配符展开由make负责,在target中也可以使⽤通配符,依然是由amke负责展开。

print: *.c
lpr -p $?
touch print
这个例⼦除了表明通配符⽤法外,同时展⽰了empty target的⽤法。

empty target是phony target的变体,empty target中的target 可以存在,也可以不存在。

其特⾊是在commands最后会touch更新target。

在定义变量时使⽤通配符
objects = *.o
变量objects的值就是*.o,但是当你把objects的值⽤于target、prerequisite时,make⼯具会展开;⽤于commands时,shell会展开。

表⾯看上去,这似乎没啥问题,看如下代码
objects = *.o
foo : $(objects)
cc -o foo $(CFLAGS) $(objects)
objects的值就是*.o,单独看他就是⼀个名字古怪的⽂件。

但是⽤在target、prerequisite、commands时,make或shell会⾃动将其展开为有意义的具体xxx.o⽂件名,因此上⾯代码看上去没有问题。

但是如果,当前⽬录下并没有.o⽂件,target、
prerequisite、commands展开时也找不到.o⽂件,他就会找*.o这个⽂件,当然*.o⽂件也是没有的,于是会报"cannot figure out how to make *.o."错误。

解决这个陷阱的⽅法是使⽤wildcard函数
wildcard函数通常和patsubst函数⼀起使⽤,这是因为在函数内部通配符也不会⾃动展开
objects := $(patsubst %.c,%.o,$(wildcard *.c))
foo : $(objects)
cc -o foo $(objects)
变量
⾃定义变量与赋值符
Makefile 允许使⽤等号⾃定义变量。

var = Hello World
test:
@echo $(var)
上⾯代码中,变量 var等于 Hello World。

调⽤时,变量需要放在 $( ) 之中。

调⽤Shell变量,需要在美元符号前,再加⼀个美元符号,这是因为Make命令会对美元符号转义。

test:
@echo $$HOME
有时,变量的值可能指向另⼀个变量。

v1 = $(v2)
上⾯代码中,变量 v1 的值是另⼀个变量 v2。

这时会产⽣⼀个问题,v1 的值到底在定义时扩展(静态扩展),还是在运⾏时扩展(动态扩展)?如果 v2 的值是动态的,这两种扩展⽅式的结果可能会差异很⼤。

为了解决类似问题,Makefile⼀共提供了四个赋值运算符(=、:=、?=、+=),它们的区别请看。

VARIABLE = value
# 在执⾏时扩展,允许递归扩展。

VARIABLE := value
# 在定义时扩展。

VARIABLE ?= value
# 只有在该变量为空时才设置值。

VARIABLE += value
# 将值追加到变量的尾端。

内置变量(Implicit Variables)
Make命令提供⼀系列内置变量,(感觉上和gcc/g++的预定义宏差不多)⽐如,$(CC) 指向当前使⽤的编译器,$(MAKE) 指向当前使⽤的Make⼯具。

这主要是为了跨平台的兼容性,详细的内置变量清单见。

output:
$(CC) -o output input.c
⾃动变量(Automatic Variables)
Make命令还提供⼀些⾃动变量,它们的值与当前规则有关。

主要有以下⼏个。

(1)$@
$@指代当前⽬标,就是Make命令当前构建的那个⽬标。

⽐如,make foo的 $@ 就指代foo。

a.txt
b.txt:
touch $@
等同于下⾯的写法。

a.txt:
touch a.txt
b.txt:
touch b.txt
(2)$<
$< 指代第⼀个前置条件。

⽐如,规则为 t: p1 p2,那么$< 就指代p1。

a.txt:
b.txt
c.txt
cp $< $@
等同于下⾯的写法。

a.txt:
b.txt
c.txt
cp b.txt a.txt
(3)$?
$? 指代⽐⽬标更新的所有前置条件,之间以空格分隔。

⽐如,规则为 t: p1 p2,其中 p2 的时间戳⽐ t 新,$?就指代p2。

(4)$^
$^ 指代所有前置条件,之间以空格分隔。

⽐如,规则为 t: p1 p2,那么 $^ 就指代 p1 p2 。

(5)$*
$* 指代匹配符 % 匹配的部分,⽐如% 匹配 f1.txt 中的f1 ,$* 就表⽰ f1。

(6)$(@D) 和 $(@F)
$(@D) 和 $(@F) 分别指向 $@ 的⽬录名和⽂件名。

⽐如,$@是 src/input.c,那么$(@D) 的值为 src ,$(@F) 的值为 input.c。

(7)$(<D) 和 $(<F)
$(<D) 和 $(<F) 分别指向 $< 的⽬录名和⽂件名。

所有的⾃动变量清单,请看。

下⾯是⾃动变量的⼀个例⼦。

dest/%.txt: src/%.txt
@[ -d dest ] || mkdir dest
cp $< $@
上⾯代码将 src ⽬录下的 txt ⽂件,拷贝到 dest ⽬录下。

⾸先判断 dest ⽬录是否存在,如果不存在就新建,然后,$< 指代前置⽂件(src/%.txt), $@ 指代⽬标⽂件(dest/%.txt)。

判断和循环
循环和判断应⽤在commands处,⽽commands的解析则有shell负责。

因此Makefile的循环和判断其实也就是shell的循环和判断,其语法与shell完全⼀样。

ifeq ($(CC),gcc)
libs=$(libs_for_gcc)
else
libs=$(normal_libs)
endif
上⾯代码判断当前编译器是否 gcc ,然后指定不同的库⽂件。

LIST = one two three
all:
for i in $(LIST); do \
echo $$i; \
done
# 等同于
all:
for i in one two three; do \
echo $i; \
done
上⾯代码的运⾏结果。

one
two
three
函数
Makefile 还可以使⽤函数,格式如下。

$(function arguments)
# 或者
${function arguments}
Makefile提供了许多,可供调⽤。

下⾯是⼏个常⽤的内置函数。

(1)shell 函数
shell 函数⽤来执⾏ shell 命令
srcfiles := $(shell echo src/{00..99}.txt)
(2)wildcard 函数
wildcard 函数⽤来在 Makefile 中,替换 Bash 的通配符。

srcfiles := $(wildcard src/*.txt)
(3)subst 函数
subst 函数⽤来⽂本替换,格式如下。

$(subst from,to,text)
下⾯的例⼦将字符串"feet on the street"替换成"fEEt on the strEEt"。

$(subst ee,EE,feet on the street)
下⾯是⼀个稍微复杂的例⼦。

comma:= ,
empty:=
# space变量⽤两个空变量作为标识符,当中是⼀个空格
space:= $(empty) $(empty)
foo:= a b c
bar:= $(subst $(space),$(comma),$(foo))
# bar is now `a,b,c'.
(4)patsubst函数
patsubst 函数⽤于模式匹配的替换,格式如下。

$(patsubst pattern,replacement,text)
下⾯的例⼦将⽂件名"x.c.c bar.c",替换成"x.c.o bar.o"。

$(patsubst %.c,%.o,x.c.c bar.c)
(5)替换后缀名
替换后缀名函数的写法是:变量名 + 冒号 + 后缀名替换规则。

它实际上patsubst函数的⼀种简写形式。

min: $(OUTPUT:.js=.min.js)
上⾯代码的意思是,将变量OUTPUT中的后缀名 .js 全部替换成 .min.js 。

相关文档
最新文档