初识makefile
Makefile具有三个要素:命令、依赖、目标。
Makefile并不会关心命令是如何执行的,仅仅只是会去执行所有定义的命令,输入命令的一个集合。
同时一条命令中的目标可能是另一条命令中的依赖,因此形成了一层一层的依赖关系。
Makefile语法
- ⽬标放在‘:’的前⾯,其名字可以是由字⺟和下划线‘_’组成 。
- 执行时,用
make 目标
方式运行,当没有指明具体的⽬标是什么时,那么 make 以 Makefile ⽂件中定义的第⼀个⽬标作为这次运⾏的⽬标。 - 目标 all: Makefile文件默认只生成第一个目标文件即完成编译,但是我们可以通过all 指定所需要生成的目标文件。
- 注意
tab
键
变量
变量的使用可以提高makefile的可维护性。⼀个变量的定义很简单,就是⼀个名字(变量名)后⾯跟上⼀个等号,然后在等号的后⾯放这个变量所期望的值。对于变量的引⽤,则需要采⽤ ( 变量名 ) 或者 (变量名)或者 (变量名)或者{变量名}这种模式。
1.自动变量(☆☆☆☆☆)
对于每⼀个规则,⽬标和先决条件的名字会在规则的命令中多次出现,每⼀次出现都是⼀种麻烦,更为麻烦的是,如果改变了⽬标或是依赖的名,那得在命令中全部跟着改。有没有简化这种更改的⽅法呢?这我们需要⽤到 Makefile 中的⾃动变量,最常用包括:
- $@⽤于表示⼀个规则中的⽬标。当我们的⼀个规则中有多个⽬标时,$@所指的是其中任何造成命令被运行的目标。
- $^则表示的是规则中的所有先决(依赖)条件。
- $<表示的是规则中的第⼀个先决(依赖)条件。
.PHONY:all
all:first second third
@echo "\$$@ = $@"
@echo "\$$^ = $^"
@echo "\$$< = $<"
first second third:
运行结果
需要注意的是,在 Makefile 中‘ $ ’具有特殊的意思,因此,如果想采⽤ echo 输出‘$’,则必需⽤两个连着的‘$’。还有就是,$@对于 Shell 也有特殊的意思,我们需要在“$$@”之前再加⼀个脱字符‘\’。
修改simple的makefile
.PHONY: clean
CC = gcc
RM = rm
EXE = simple
OBJS = main.o foo.o
$(EXE): $(OBJS)
$(CC) -o $@ $^
main.o: main.c
$(CC) -o $@ -c $^
foo.o: foo.c
$(CC) -o $@ -c $^
clean:
$(RM) $(EXE) $(OBJS)
运行结果:
2.特殊变量
(1)MAKE变量
它表示的是make 命令名是什么。当我们需要在 Makefile 中调⽤另⼀个 Makefile 时需要⽤到这个变量,采⽤这种⽅式,有利于写⼀个容易移植的 Makefile。
.PHONY: clean
all: @echo "MAKE = $(MAKE)"
运行结果:
(2)MAKECMDGOALS变量
它表示的是当前⽤户所输⼊的 make ⽬标是什么。
.PHONY: all clean
all clean:
@echo "\$$@ = $@"
@echo "MAKECMDGOALS = $(MAKECMDGOALS)"
MAKECMDGOALS 指的是⽤户输⼊的⽬标,当我们只运⾏ make 命令时,虽然根据Makefile 的语法,第⼀个⽬标将成为缺省⽬标,即 all ⽬标,但 MAKECMDGOALS 仍然是空,⽽不是all,这⼀点需要注意。
3.变量赋值
1、"="是最普通的等号,在Makefile中容易搞错赋值等号,使用 “=”进行赋值,变量的值是整个Makefile中最后被指定的值。
VIR_A = A
VIR_B = $(VIR_A) B
VIR_A = AA
经过上面的赋值后,最后VIR_B的值是AA B,而不是A B,在make时,会把整个Makefile展开,来决定变量的值
2、“:=” 表示直接赋值,赋予当前位置的值。
VIR_A := A
VIR_B := $(VIR_A) B
VIR_A := AA
最后BIR_B的值是A B,即根据当前位置进行赋值。因此相当于“=”,“:=”才是真正意义上的直接赋值
3、“?=” 表示如果该变量没有被赋值,赋值予等号后面的值。
VIR ?= new_value
如果VIR在之前没有被赋值,那么VIR的值就为new_value。
VIR := old_value
VIR ?= new_value
这种情况下,VIR的值就是old_value
4、"+="和平时写代码的理解是一样的,表示将符号后面的值添加到前面的变量上
4.预定义变量
CC:c编译器的名称,默认值为cc。cpp c预编译器的名称默认值为$(CC) -E
CC = gcc
回显问题,Makefile中的命令都会被打印出来。如果不想打印命令部分 可以使用@去除回显
@echo "clean done!"
5.override指令
override 指令避免定义的变量被覆盖
.PHONY: all
override foo = x
all:
@echo "foo = $(foo)"
函数
1.wildcard函数(☆☆☆)
wildcard 是通配符函数,通过它可以得到我们所需的⽂件,这个函数类似我们在 Windows 或Linux 命令⾏中的“*”。其形式是:$(wildcard pattern)
.PHONY:all
SRC=$(wildcard *.c)
all:
@echo "SRC = $(SRC)"
2.patsubst函数(☆☆☆)
patsubst 函数是⽤来进⾏字符串替换的,其形式是:$(patsubst pattern, replacement, text)
.PHONY:all
mixed=foo.c bar.c main.o
objects:=$(patsubst %.c, %.o, $(mixed))
all:
@echo $(objects)
3.addprefix函数
addprefix 函数是⽤来在给字符串中的每个⼦串前加上⼀个前缀,其形式是:$(addprefix prefix, names…)
4.filter函数
filter 函数⽤于从⼀个字符串中,根据模式得到满⾜模式的字符串,其形式是:$(filter pattern…, text)
5.filter-out函数
filter-out 函数⽤于从⼀个字符串中根据模式滤除⼀部分字符串,其形式是:$(filter-out pattern…, text)
6.strip
strip 函数⽤于去除变量中的多余的空格,其形式是:$(strip string)
模式
.PHONY: clean
CC = gcc
RM = rm
EXE = simple
OBJS = main.o foo.o
$(EXE): $(OBJS)
$(CC) -o $@ $^
%.o: %.c //模式应用
$(CC) -o $@ -c $^
clean:
$(RM) $(EXE) $(OBJS)
模式类似于 Windows 操作系统中所使⽤的通配符,当然是⽤“%”⽽不是“*”。采⽤了模式以后,不论有多少个源⽂件要编译,都应⽤同⼀个模式规则。
其他
注释符
# 字符是注释符,makefile 把 # 字符后面的内容作为注释内容处理(shell、perl 脚本也是使用 # 字符作为注释符)。
如果某行的第一个非空字符为 #,则此行会被 make 解释为注释行(命令行除外,如果 Tab 字符之后使用 # 字符,则会被 make 解释为命令行)。
注释行的结尾如果存在反斜线(\),那么下一行也被作为注释行,可以进行多行注释。
建议在书写 makefile 时将注释作为一个独立的行,而不要和 makefile 的有效行放在同一行中书写。
make 有时候会把 # 字符之前的内容作为有效行的内容(如定义变量的时候)。
当在 makefile 中需要使用字符 # 时,可以使用 \ 加 #**(#)**来实现,表示将 # 字符作为一个普通字符而不是注释符。
清除
和all类似,编写用于清除编译生成的文件
假目标
假目标用于避免所定义的目标和已经存在的文件是重名的情况,使⽤**.PHONY** 关键字来定义
多用来定义 all , clean