相关概念
注释符:#
伪目标 (.PHONY)
clean:
rm *.o
上述示例中的clean
就是一个伪目标,常用于清除生成的文件,以达到完整重编译的左右。
这里并不会生成clean
文件。
“伪目标”并不是一个文件,而只是一个标签;由于“伪目标”不是文件,所以make无法生成它的依赖关系和决定它是否要执行;我们需要显示地指明这个“伪目标”才能让其生效。
此外,伪目标的取名不能和文件名一致,否则就失去了“伪目标”的意义。为了避免与文件重名的情况,可以使用一个特殊的标记.PHONY
来显示地指明一个目标为“伪目标”,而不用管是否有重名的文件存在。
.PHONY: clean
clean:
rm *.o
上述例子表明:不管是否存在clean
文件,只要输入make clean
,就可以运行clean
这个伪目标。
目标可以成为伪目标的依赖
伪目标一般没有依赖的文件。但也可以指定依赖的文件。Makefile中的第一个目标会被作为“默认目标”,我们可以将伪目标放置在第一位,以作为“默认目标”。一个应用示例就是,如果你的Makefile一次执行若干个可执行文件,但你仅需要输入一次make命令即可,并且所有的目标文件都写在一个Makefile中,那么可以使用“伪目标”的特性:
all: obj1 obj2 obj3
.PHONY: all
obj1: obj1.c utils.o
cc -o obj1 obj1.o utils.o
obj2: obj2.c utils.o
cc -o obj2 obj2.o utils.o
obj3: obj3.c common.o
cc -o obj3 obj3.o common.o
上述的例子,我们使用.PHONY: all
声明了一个伪目标all
,其依赖于其他三个目标。由于默认目标具有总是被执行的特性,同时由于all
又是一个伪目标。伪目标只是一个标签不会生成文件all
。而其他三个目标的规则总是被执行,也即可达到生成多个目标的目的。
伪目标可以成为伪目标的依赖
.PHONY: cleanAll cleanObj cleanTemp
cleanAll: cleanObj cleanTemp
rm *.exe
cleanObj:
rm *.o
cleanTemp:
rm *.temp
其中, make cleanAll
将清除所有的文件(*.exe *.o *.temp
),make cleanObj
仅清除*.o
,make cleanTemp
仅清除*.temp
。这样可以使用不同的命令来达到清除不同种类的文件的目的。
静态模式
使用变量
常见的内置变量Variables
CC:C 编译器的名称。
CFLAGS:C 编译器的选项。
LD:链接器的名称。
LDFLAGS:链接器的选项。
AR:静态库的归档工具。
RM:删除文件的命令。
MAKE:Make 工具的名称。
MAKEFLAGS:Make 工具的选项。
CURDIR:当前工作目录的路径。
SHELL:Shell 程序的名称。
MAKEFILE_LIST:包含当前 Makefile 和所有被包含的 Makefile 的列表。
$(CC):C 编译器的名称。
$(CXX):C++ 编译器的名称。
$(AR):静态库归档工具的名称。
$(LD):链接器的名称。
$(AS):汇编器的名称。
$(FC):Fortran 编译器的名称。
$(RM):删除文件的命令。
$(MAKE):Make 工具的名称。
$(MAKECMDGOALS):被调用的目标列表。
$(MAKEFILE_LIST):包含当前 Makefile 的文件列表。
$(SHELL):Shell 解释器的名称。
$(CURDIR):当前工作目录的绝对路径。
$(PWD):当前工作目录的绝对路径。
$(ARFLAGS):静态库归档工具的选项。
$(LDFLAGS):链接器的选项。
$(ASFLAGS):汇编器的选项。
$(CPPFLAGS):预处理器的选项。
$(CFLAGS):C 编译器的选项。
$(CXXFLAGS):C++ 编译器的选项。
$(FFLAGS):Fortran 编译器的选项。
$(LFLAGS):链接器的选项。
AR:静态库归档程序的名称。
CC:C 编译器的名称。
CXX:C++ 编译器的名称。
CFLAGS:C 编译器的选项。
CXXFLAGS:C++ 编译器的选项。
CPPFLAGS:预处理器选项。
LD:链接器的名称。
LDFLAGS:链接器的选项。
LIBS:要链接的库。
RM:删除文件的命令。
MAKE:Make 工具的名称。
MAKEFLAGS:Make 工具的选项。
MAKEFILE_LIST
CC
CFLAGS
常见的内置函数
开头的
$
表示要执行一个Makefile的函数。
$(patsubst pattern,replacement,text):替换模式字符串。
$(wildcard pattern):匹配指定模式的文件列表。
$(foreach var,list,text):迭代列表中的每个元素,并将文本应用于每个元素。
$(shell command):执行命令并返回结果。
$(notdir names):从文件名路径中提取文件名部分。
$(basename names):从文件名路径中提取基本名称部分。
$(dir names):从文件名路径中提取目录部分。
$(addsuffix suffix,names):将后缀添加到每个名称。
$(addprefix prefix,names):将前缀添加到每个名称。
$(wildcard pattern):匹配指定模式的文件列表。
$(shell command):执行命令并返回结果。
$(foreach var,list,text):迭代列表中的每个元素,并将文本应用于每个元素。
$(if condition,then-part[,else-part]):根据条件选择不同的文本。
$(call variable,param,…):调用变量,并传递参数。
$(subst from,to,text):将文本中的字符串替换为另一个字符串。
$(patsubst pattern,replacement,text):替换模式字符串。
$(strip string):移除字符串中的前导和尾随空格。
$(findstring find,in):在字符串中查找子字符串。
$(filter pattern…,text):根据模式过滤文本中的元素。
$(dir names):从文件名路径中提取目录部分。
$(notdir names):从文件名路径中提取文件名部分。
$(suffix names):提取文件名路径中的后缀。
$(basename names):提取文件名路径中的基本名称。
$(addsuffix suffix,names):为文件名路径添加后缀。
$(addprefix prefix,names):为文件名路径添加前缀。
$(wildcard pattern):匹配指定模式的文件列表。
$(shell command):执行命令并返回结果。
$(patsubst pattern,replacement,text):替换模式字符串。
$(subst from,to,text):将文本中的字符串替换为另一个字符串。
$(foreach var,list,text):迭代列表中的每个元素,并将文本应用于每个元素。
$(if condition,then-part,else-part):根据条件选择返回不同的部分。
$(filter pattern…,text):筛选出符合模式的文本。
$(addprefix prefix,name…):为名称添加前缀。
$(addsuffix suffix,name…):为名称添加后缀。
$(notdir names):从文件名路径中提取文件名部分。
$(dir names):从文件名路径中提取目录部分。
$(shell command):执行命令并返回结果。
$(error message):输出错误消息并停止构建过程。
$(warning message):输出警告消息。
$(info message):输出一般消息。
wildcard: 扩展通配符
在Makefile规则中,通配符会被自动展开
。但在变量的定义和函数引用
时,通配符将失效
。这种情况下如果需要通配符有效,就需要使用函数wildcard
,它的用法是:$(wildcard PATTERN...)
。在Makefile中,它被展开为已经存在的、使用空格分开的、匹配此模式的所有文件列表。如果不存在任何符合此模式的文件,函数会忽略模式字符并返回空。
一般我们可以使用$(wildcard *.c)
来获取工作目录下的所有的.c文件列表
。复杂一些用法;可以使用$(patsubst %.c,%.o,$(wildcard *.c))
,首先使用“wildcard”函数获取工作目录下的.c文件列表;之后将列表中所有文件名的后缀.c替换为.o。这样我们就可以得到在当前目录可生成的.o文件列表。
因此在一个目录下可以使用如下内容的Makefile来将工作目录下的所有的.c文件进行编译并最后连接成为一个可执行文件:
objects := $(patsubst %.c,%.o,$(wildcard *.c))
foo : $(objects)
cc -o foo $(objects)
其中,%
是指匹配零或若干字符(如果需要引用%
,使用转义字符\
)。这里使用了make
的隐含规则来编译.c
的源文件,使用一个特殊符号:=
对变量进行赋值。
测试用例
在当前TEST
目录下,创建两个.c文件(testA.c
和testB.c
),以及一个目录subdir
;然后再目录subdir
下,创建两个.c文件(testC.c
和testD.c
)。整体结构如下图所示:
紧接着,在TEST
目录下,创建makefile
文件,其内容如下:
SRCS = $(wildcard *.c ./subdir/*.c)
DIR = $(notdir $(SRCS))
OBJ = $(patsubst %.c, %.o, $(DIR))
all:
@echo $(SRCS)
@echo $(DIR)
@echo $(OBJ)
其中,wildcard
可扩展通配符;notdir
可去除路径;patsubst
可替换通配符。
输出结果为:
testA.c testB.c ./subdir/testC.c ./subdir/testD.c
testA.c testB.c testC.c testD.c
testA.o testB.o testC.o testD.o
解释:
第一行:wildcard
把指定目录./
和./subdir/
下的所有后缀为.c的文件全部展开;
第二行:notdir
将展开的文件去除路径信息,比如将./subdir/testC.c ./subdir/testD.c
分别替换为testC.c testD.c
;
第三行:patsubst
将$(DIR)
中的后缀为.c的变量全部替换为.o。
wildcard
(patsubst pattern,replacement,text)
foreach
(foreach var,list,text)