终极参考:
GNU make 官方文档
Makefile学习
名词定义
编译
把高级语言书写的代码转换为机器可识别的机器指令。
编译高级语言后生成的指令虽然可被机器识别,但是还不能被执行。编译时,编译器检查高级语言的语法、函数与变量的声明是否 正确。只有所有的语法正确、相关变量定义正确编译器就可以编译出中间目标文件。
目标文件
目标文件在 Linux 中默认后缀为“.o”(如“foo.c”的目 标文件为“foo.o”)。
通常,一个高级语言的源文件都可对应一个目标文件。
链接
将多.o 文件,或者.o 文件和库文件链接成为可被操作系统执行的可执行程序。
链接器不检查函数所在的源文件,只检查所有.o 文件中的定义的符号。将.o 文件中使用的函数和其它.o 或 者库文件中的相关符号进行合并,最后生成一个可执行的程序。“ld”是 GNU 的链接器。
静态库
又称为文档文件(Archive File)。它是多个.o 文件的集合。Linux 中静态库文件的后缀为“.a”。
静态库中的各个成员(.o 文件)没有特殊的存在格式,仅仅是一个.o 文件的集合。使 用“ar”工具维护和管理静态库。
共享库
也是多个.o 文件的集合,但是这些.o 文件时有编译器按照一种特殊的方式生成。
对象模块的各个成员的地址(变量引用和函数调用)都是相对地址。因此在程序运行时,可动态加载库 文件和执行共享的模块(多个程序可以共享使用库中的某一个模块)。
Makefile
make 在执行时,需要一个命名为 Makefile 的特殊文件。这个文件告诉 make 需要做什么(完成什么任务),该怎么做。告诉 make以何种方式编译源代码,链接程序,清除编译过程文件等。
Makefile规则介绍
Makefile 描述规则:
TARGET... : PREREQUISITES...
COMMAND #命令行必须以[Tab]字符开始
...
...
TARGET
: 规则的目标。通常是程序中间或者最后需要生成的文件名。可以是.o文件、也可以是 最后的可执行程序的文件名。另外,目标也可以是一个make执行的动作的名称,如目标“clean”, 成这样的目标是“伪目标”。参考3.6 Makefile伪目标 一节
PREREQUISITES
: 规则的依赖。生成规则目标所需要的文件名列表。通常一个目标依赖于一个 或者多个文件。
COMMAND
: 规则的命令行。是 make 程序所有执行的动作(任意的 shell 命令或者可在 shell 下执行的程序)。
一个规则可以有多个命令行,每一条命令占一行。注意:每一个命令行必须以[Tab]字符开始, [Tab]字符告诉 make 此行是一个命令行。make 按照命令完成相应的动作。这也是书写 Makefile 中容易产生,而且比较隐蔽的错误。
命令就是在任何一个目标的依赖文件发生变化后重建目标的动作描述。一个目标可以没有依赖 而只有动作(指定的命令)。比如 Makefile 中的目标“clean”,此目标没有依赖,只有命令。它所 指定的命令用来删除 make 过程产生的中间文件(清理工作)。
在 Makefile 中“规则”就是描述在什么情况下、如何重建规则的目标文件,通常规则中包括 了目标的依赖关系(目标的依赖文件)和重建目标的命令。make 执行重建目标的命令,来创建或 者重建规则的目标(此目标文件也可以是触发这个规则的上一个规则中的依赖文件)。规则包含了 目标和依赖的关系以及更新目标所要求的命令。
Makefile 中可以包含除规则以外的部分。一个最简单的 Makefile 可能只包含规则描述。规则 在有些 Makefile 中可能看起来非常复杂,但是无论规则的书写是多么的复杂,它都符合规则的基 本格式。
符号
=
最基本的赋值
make会将整个makefile展开后,再决定变量的值。也就是说,变量的值将会是整个makefile中最后被指定的值。如下:例子中,y的值将会是 xyz bar ,而不是 foo bar 。
x = foo
y = $(x) bar
x = xyz
:=
覆盖之前的值
表示变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值。相当于C语言的宏定义。
如下:y的值将会是 foo bar ,而不是 xyz bar 了。
x := foo
y := $(x) bar
x := xyz
?=
如果没有被赋值过就赋予等号后面的值
+=
添加等号后面的值
$/
makefile中路径中的斜杠表示。
\
分解较长的行。
一个较长的行可以用反斜线\
分解为多行,这样可以是Makefile清晰容易阅读。
注意:反斜线\
后不能有空格
例子:
CDEBUG = -g
CFLAGS = $(CDEBUG) -I. -I$(srcdir) $(DEFS) \
-DDEF_AR_FILE=\"$(DEF_AR_FILE)\" \
-DDEFBLOCKING=$(DEFBLOCKING)
LDFLAGS = -g
%
匹配一个或者多个字符
例子:
vpath %.h ../inc #“%.h”表示所有以“.h”结尾的文件
vpath %c ../src #“%.c”表示所有以“.c”结尾的文件
-
忽略命令的执行错误
-
忽略命令执行错误,若下面有命令会继续执行
@
避免显示出命令本身
makefile会把将要执行的命令行在命令执行前输出到屏幕上,使用@
可以避免显示出命令本身。如下代码:
@echo 正在编译…
echo 正在编译…
则$make
后显示
正在编译…
echo 正在编译…
[tab]键
所有命令行必须以[tab]字符开始,但并不是所有的以[tab]键出现行都是命令行。make程序会把出现在第一条规则之后的所有以[tab]字符开始的行都作为命令行来处理。
更多参考:
Makefile 中:= ?= += =的区别 - 专注it - 博客园
Makefile的特殊目标,伪目标
更多参考:《GNU make 手册 v3.80(中文版).pdf》–3.9 Makefile的特殊目标
.PHONY
目标.PHONY
的所有的依赖被作为伪目标。当使用make命令行指定此目标时,这个目标所在规则定义的命令、无论目标文件是否存在都会被无条件执行。
下面我们通过例子来说明特殊目标.PHONY
, 如:
在 Makefile 中实现:清除(当前目录下)在编译过程中生成的一些文件(temp 和*.o 文件)的规则:
一般我们这样写:
clean:
rm *.o temp
这样做有个问题:
-
假如磁盘上当前目录下存在一个名为“clean”文件时,“clean”所在规则的命令无法执行。
-
命令“rm”的执行错误也会导致make停止,不再往下执行。
改进版:
.PHONY: clean
clean:
-rm *.o temp
-
这样目标"clean"就是一个伪目标,无论当前目录下是否存在"clean"这个文件。我们输入
make clean
之后。rm
命令都会被执行。 -
在命令行之前使用
-
,意思是忽略命令“rm”的执行错误。 -
当一个目标被声明为伪目标后,make 在执行此规则时不会试图去查找隐含规则来创建这个目标。这样也提高了 make 的执行效率,同时我们也不用担心由于目标和文件名重名而使我们的期望失败。
-
在书写伪目标规则时,首先需要声明目标是一个伪目标,之后才是伪目标的规则定义。
.PHONY: xxx xxx: -rm *.o temp
-
.PHONY :clean
clean :
-rm *.o temp
.SILENT
出现在目标.SILENT
的依赖列表中的文件,make 在创建这些文件时,不打印出重建此文 件所执行的命令。同样,给目标.SILENT
指定命令行是没有意义的。
没有任何依赖文件的目标.SILENT
告诉make在执行过程中不打印任何执行的命令。现行 版本make支持目标.SILENT
的这种功能和用法是为了和旧版本的兼容。在当前版本中如果需 要禁命令执行过程的打印,可以使用make的命令行参数-s
或者--silent
。参考 8.7 make 的命令行选项 一节
.SUFFIXES
特殊目标“.SUFFIXES”的所有依赖指出了一系列在后缀规则中需要检查的后缀名(就是当前 make需要处理的后缀)。 参考 9.7 后缀规则 一节
.DEFAULT
Makefile 中,目标“.DEFAULT”所在规则定义的命令,被用在重建那些没有具体规则的目标 (明确规则和隐含规则)。就是说一个文件作为某个规则的依赖,但却不是另外一个规则的目标时。 Make 程序无法找到重建此文件的规则,此种情况时就执行“.DEFAULT”所指定的命令。
更多参考:
《GNU make 手册 v3.80(中文版).pdf》-- 3.6 Makefile伪目标
Makefile基本语法 - u013216061的博客 - CSDN博客
命令选项变量
Makefile中,所有命令都应该使用变量定义,在规则中使用此命令时,通过相应的变量的引用来实现命令的调用。
如:定义变量CC = gcc
,规则中可使用$(CC)
来引用gcc
。
在命令变量后面添加“FLAGS”来命名这个选项变量。
如:CFLAGS
是c编译器(命令变量为CC
)的命令行选项变量;LDFLAGS
是命令”ld“(命令变量LD
)的选项变量。
CFLAGS
表示用于 C 编译器的选项。指定头文件(.h文件)的路径。
如:CFLAGS=-I/usr/include -I/path/include。安装一个包时会在安装路径下建立一个include目录。
CXXFLAGS
表示用于 C++ 编译器的选项。gcc 等编译器会用到的一些优化参数,也可以在里面指定库文件的位置。
用法:LDFLAGS=-L/usr/lib -L/path/to/your/lib。每安装一个包都几乎一定的会在安装目录里建立一个lib目录。
LIBS
:告诉链接器要链接哪些库文件。
如LIBS = -lpthread -liconv
Makefile选项CFLAGS,LDFLAGS,LIBS - wanghetao - 博客园
关键字
override
如果一个变量的值需要在编译选项中指定或由系统传入,那么makefile中可以使用override关键字来设置,使这个变量的赋值被忽略
define
使用define关键字可以定义多行变量
define two-lines
echo foo
echo $(bar)
endef
export
将变量导出,以便于所有的子makefile都可以使用
如:
export DEBUG = -g
export OPTIMIZE = -Ospace
include
和C语言的#include
一样,将后面的文件展开到当前位置
自动变量
自动变量是make自动根据规则生成的,不需要用户显式的指出相应的文件或目标名称。以下就是七个最核心的自动变量:
$@
目标文件的文件名;
$%
仅当目标文件为归档成员文件(.lib 或者 .a)时,显示文件名,否则为空;
$<
依赖(prerequisite)列表里面的第一个文件名;
$?
所有在prerequisite列表里面比当前目标新的文件名,用空格隔开;
$^
所有在prerequisite列表中的文件,用空格隔开; 如果有重复的文件名(包含扩展名),会自动去除重复;
$+
与$^
相似,也是prerequisite列表中的文件名用空格隔开,不同的是这里包含了所有重复的文件名;
$*
显示目标文件的主干文件名,不包含后缀部分。
此外,上面的每个变量都带有两个不同的变种,用于适应不同种类的make。分别是在后面附加一个“D”或者“F”。例如,$(^D)
就是代表所有依赖文件的路径,$(<F)
表示依赖文件第一个的文件部分的值。
Makefile基本语法 - u013216061的博客 - CSDN博客
内置函数列表
字符串替换和分析 | 作用 | 解释 |
---|---|---|
$(subst from,to,text) | 字符串替换 | 把字符串 text 中所有的 from 字符串替换为 to 字符串 |
$(patsubst pattern,replacement,text) | 模式字符串替换 | 将字符串 text 中符合模式 pattern 的单词替换为 replacement |
$(strip text) | 去空格 | 去掉字符串 text 的前置和后置空白,并将 text 中多个连续的空白字符合并为一个空白字符 |
$(findstring find,text) | 字符串查找 | 在字符串 text 中查找 find 字符串 |
$(filter pattern…,text) | 字符串过滤 | 过滤掉字符串 text 中所有不符合模式 pattern 的单词,保留所有符合模式的单词 |
$(filter-out pattern…,text) | 字符串反过滤 | 过滤掉字符串 text 中所有符合模式 pattern 的单词,保留所有不符合此模式的单词 |
$(sort text) | 排序 | 将字符串 text 中的单词按照 ASCII 序进行升序排序,并去掉重复的单词 |
$(words text) | 统计单词个数 | 统计字符串 text 中单词的个数 |
$(word n,text) | 取单词 | 取字符串 text 中第 n 个单词 |
$(wordlist s,e,text) | 取单词串 | 从字符串 text 中取出从 s 开始到 e 结束之间的单词串 |
$(firstword text) | 取首单词 | 取字符串 text 中的第一个单词 |
$(lastword text) | 取末单词 | 取字符串 text 中的最后一个单词 |
文件名操作 | 作用 | 解释 |
$(dir names) | 取目录 | 从文件名列表 names 中取出各个文件名的目录部分,文件名的【目录部分】是指文件名中的最后一个斜线/ 之前的部分(包含斜线) |
$(notdir names) | 取文件名 | 从文件名列表 names 中取出各个文件名的非目录部分,文件名的【非目录部分】是指文件名中的最后一个斜线/ 之后的部分 |
$(suffix names) | 取后缀 | 从文件名列表 names 中取出各个文件名的后缀部分,文件名的【后缀部分】是文件名中最后一个点号. 之后的部分(包含点号) |
$(basename names) | 取前缀 | 从文件名列表 names 中取出各个文件名的前缀部分,文件名的【前缀部分】指的是文件名中最后一个点号. 之前的部分 |
$(addsuffix suffix,names) | 增加后缀 | 为文件名序列 names 中的每一个文件名添加后缀 suffix(在文件名之后添加) |
$(addprefix prefix,names) | 增加前缀 | 为文件名序列 names 中的每一个文件名添加前缀 prefix(在文件名之前添加) |
$(join list1,list2) | 单词连接 | 将字符串 list1 和字符串 list2 各单词进行连接。将 list2 的第一个单词追加到 list1 第一个单词后合并为一个单词,依次类推 |
$(wildcard pattern…) | 获取通配模式文件名 | 列出当前目录下所有符合通配符模式 pattern 的文件名。pattern 使用 shell 可识别的通配符,包括:? 、* 、[...] 、[!...] 、[^...] 、[c1-c2] , pattern 不支持 shell 的通配符 {string1,string2…}, pattern 可以使用多个通配符模式。多个通配符模式之间使用空格分隔。 |
$(realpath names) | 获取文件真实路径 | 获取文件名序列 names 中存在的文件和目录的真实路径,函数会判断文件和目录是否存在,如果不存在,则返回空。 |
$(abspath names) | 获取文件路径 | 获取文件名序列 names 中文件和目录的绝对路径。 |
条件函数 | 作用 | 解释 |
$(if condition, then-part[, else-part]) | if判断 | if函数可以包含“else”部分,或是不含。即if函数的参数可以是两个,也可以是三个。condition参数是if的表达式,如果其返回的为非空字符串,那么这个表达式就相当于返回真,于是,then-part会被计算,否则else-part会被计算。 |
$(and conditon1[,condition2][,condition3]…) | 与操作 | |
$(or conditon1[,condition2][,condition3]…) | 或操作 | |
$(not condition) | 非操作 | |
控制make函数 | 作用 | 解释 |
$(error text) | 错误消息 | 有致命的错误时,显示消息text |
$(warning text) | 警告消息 | 有警告消息时,显示消息text |
$(info text) | 打印消息 | 当text消息打印到标准输出 |
$(foreach variable,list,text) | 循环 | 用控制变量,重复利用一段脚本。把list中的单词逐一取出,放到variable中,再执行text中的表达式。 |
$(value variable) | 返回未展开的变量 | |
$(origin variable) | 查找变量在哪得到它的值 | |
$(flavor variable) | find out the flavor of variable | |
$(call variable,param[,param]…) | 创建新函数 | 创建新的参数化的函数 |
$(shell command) | 替换shell命令的输出 | |
$(eval param) | evaluate the arguments as makefile syntax |
更多参考: