makefile
在 Makefile
中,预定义的变量(也称为内置变量)提供了对构建过程中的默认值和特殊值的访问。这些变量通常由 Make 自动设置,并且可以覆盖它们以改变 Make 的行为。下面是 Make 环境中常见的几个内置变量及其用途:
-
$@
目标文件名(依赖关系中的最后一个文件)。在规则的命令中,用$@
表示当前目标的名字。 -
$^
所有的依赖文件名列表,没有重复,并且排除了隐含目标。 -
$<
第一个依赖文件的名字(主依赖项)。 -
$*
用于匹配模式(Pattern)中的通配符部分。通常用于从目标文件名中提取出特定的部分,如不包含扩展名的文件名部分。 -
$?
所有比目标更新的依赖文件名列表,只包括那些比目标新的依赖文件。 -
$>
所有比依赖文件新的目标文件名列表。
除了这些特殊符号之外,还有一些预定义的变量代表了不同的路径和环境信息:
$AR
- 归档命令(用于创建和修改静态库文件)。$AS
- 汇编程序命令。$CC
- C 编译器命令。$CXX
- C++ 编译器命令。$CPP
- C 预处理器命令。$RANLIB
- 更新库索引文件的命令。$LD
- 链接程序命令。$LDFLAGS
- 传递给链接器的默认选项。$CFLAGS
- 默认的 C 编译选项。$CXXFLAGS
- 默认的 C++ 编译选项。$CPPFLAGS
- 默认的预处理选项。
此外,还有一些变量提供了有关当前 Makefile 文件的信息:
$MAKEFILE_LIST
- 包含当前所有被包含的 Makefile 名称的列表。$MAKEFILE
- 当前正在执行的 Makefile 的名字。$MAKEFILES
- 用空格分隔的所有 Makefile 名字列表。
最后,有一些变量涉及到文件名路径:
$CURDIR
- 当前目录。$VPATH
- 虚拟路径,它告诉 Make 到哪里去找源文件。
了解并正确使用这些内置变量可以使 Makefile
更加灵活和强大。当然,用户也可以定义自己的变量,并且可以覆盖这些预定义的变量来改变默认的行为。
MAKE
表示命令是什么
MAKECMDGOALS
表示参数是什么
.PHONY:all
all:
@echo "MAKE = $(MAKE)"
@echo "MAKECMDGOALS = $(MAKECMDGOALS)"
输出结果如下:
# make all
MAKE = make
MAKECMDGOALS = all
.PHONY
是一个特殊的伪目标,在 Makefile 中使用,主要用于指定一个或多个“伪目标”(phony targets)。作用是明确告诉 make,其后列出的目标名不是文件名,即使文件系统中存在同名的文件,make 也不应该认为它们已经“存在”或“最新”。这样,当你执行 make 时,make 总是会执行该伪目标对应的命令,而不会因为它在文件系统中找到了一个同名的文件就跳过执行。
@echo
中的@
可以使make
不打印当前执行的命令。
在 Makefile 中 $$
具有特殊的意思,因此,如果想采用 echo 输出 $
,则必需用两个连着的 $$
。还有就是,$@
对于 Shell 也有特殊的意思,我们需要在 $$@
之前再加一个脱字符 \
。
.PHONY:all
all:first second third
@echo "\$$@=$@"
@echo "$$^=$^"
@echo "$$<=$<"
first second third:
执行结果如下:
# make
$@=all
$^=first second third
$<=first
变量的类别
=
符号定义的变量,称之为递归扩展变量,会进行递归扫描
上面这种不能对变量自己赋值,会陷入死循环。如
a=$(a) b
这种
:=
符号定义的变量,称之为简单扩展变量,只会进行一次扫描和替换
?=
称之为条件赋值,当变量以前没有定义时,就定义它并将右边的值赋给它;如果一定定义了就不再改变它的值
看下面的例子
.PHONY:all
x=foo
y=$(x) b
x=later
xx:=foo
yy:=$(xx) b
xx:=later
foo=x
foo?=y
bar?=y
all:
@echo "x = $(y), xx = $(yy)"
@echo "foo = $(foo), bar = $(bar)"
输出结果如下:
# make
x = later b, xx = foo b
foo = x, bar = y
#也可以在命令行上对变量赋值
# make bar=x
x = later b, xx = foo b
foo = x, bar = x
#变量也可以来自系统的环境变量
# export bar=aa
# make
x = later b, xx = foo b
foo = x, bar = aa
高级变量引用功能
下面示例了Makefile的变量引用的一种高级功能
在赋值的同时完成后缀替换操作
.PHONY:all
foo = a.o b.o c.o
bar := $(foo:.o=.c)
all:
@echo "bar = $(bar)"
输出结果如下:
# make
bar = a.c b.c c.c
overrider指令
前面的例子看到参数可以通过命令行和环境变量赋值,如果不想Makefile
中定义的变量被命令行或者环境变量覆盖掉,就可以使用override
指令
.PHONY:all
override foo := x
all:
@echo "foo = $(foo)"
输出结果如下
# make foo=b
foo = x
# export foo=c
# make
foo = x
模式
Makefile
中经常存在多个规则用于构建目标文件。如果对于每一个目标文件都要写一个不同的规则来描述就太繁琐了。Makefile
中的模式就是用来解决这种问题的。
看下面的例子
main.c
extern void foo();
int main(){
foo();
return 0;
}
foo.c
#include <stdio.h>
void foo(){
printf("This is foo (). \n");
}
Makefile
.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)
上面的例子我们%.o:%.c
,就是一个模式规则。它匹配所有.c
文件并指定如何构建对应的.o
文件。会编译出main.o
和foo.o
。这里的%
相当于通配符。注意,这里的通配符是%
,而不是*
。
函数
函数常常用来简化项目的Makefile
看下面的例子
.PHONY:clean
CC = gcc
RM = rm
EXE = simple
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c,%.o,$(SRCS))
$(EXE):$(OBJS)
$(CC) -o $@ $^
%.o:%.c
$(CC) -o $@ -c $^
clean:
$(RM) $(EXE) $(OBJS)
wildcard
是通配符函数, 通过它可以过滤出我们需要的文件,这里的通配符是*
,其形式是$(wildcard pattern)
查找当前目录下所有.c
文件,并将这些文件的名称赋值给变量SRCS
。
patsubst
函数是用来进行字符串替换,其形式是$(patsubst pattern, replacement, text)
,这里的通配符是%
。上面的例子中将SRCS
变量中所有.c
文件的后缀替换为.o
,并将结果赋值给变量OBJS
addprefix
函数是用来在给字符串中的每个子串前加上一个前缀,其形式是
$(addprefix prefix, names...)
看下面的例子
.PHONY:all
without_dir=fooc bar.c main.o
with_dir:=$(addprefix objs/,$(without_dir))
all:
@echo $(with_dir)
执行结果如下:
# make
objs/fooc objs/bar.c objs/main.o
filter
函数用于从一个字符串中,根据模式得到满足模式的字符串,其形式是:
$(filter pattern..., text)
看下面的例子
.PHONY:all
sources=foo.c bar.c baz.s ugh.h
s:=$(filter %.c %.s, $(sources))
all:
@echo $(s)
执行结果如下:
# make
foo.c bar.c baz.s
filter-out
函数用于从一个字符串中根据模式过滤掉一部分字符串,其形式是
$(filter-out pattern..., text)
看下面例子
.PHONY:all
objects=main1.o foo.o main2.o bar.o
result=$(filter-out main%.o,$(objects))
all:
@echo $(result)
执行结果如下:
# make
foo.o bar.o
strip
函数用于去除变量中多余的空格,其形式是:
$(strip string)
看下面例子
.PHONY:all
original=foo.c bar.c
stripped:=$(strip $(original))
all:
@echo "original = $(original)"
@echo "stripped = $(stripped)"
执行结果如下:
# make
original = foo.c bar.c
stripped = foo.c bar.c