Makefiles
基本规则
[!note] 依赖图 任何的文件被修改了,文件将会被重新编译 make会自动选择那些受影响的源文件重新编译,不受影响的源文件则不需要重新编译 - 需要更新的情况: - 目标没有生成 - 某个条件需要更新 - 某个条件的修改时间比目标晚 - 在一条规则被执行之前,规则的条件可能处于一下三种状态之一: - 需要更新:能够找到以该条件作为目标的规则,并且该规则中的目标需要更新 - 不需要更新:能够找到以该规则为目标的规则,但是该规则中目标不需要更新;或者不能找到以该条件为目标的规则,并且该条件已经生成 - 错误:不能找到以该条件为目标的规则,并且该条件没有生成 - 执行规则A的步骤: 1. 检查它的每个条件P 2. 再检查完A的每个条件过后,检查其目标T,如果属于一下情况之一,则执行其命令列表 1. 文件T不存在 2. 文件T存在,但是某个条件的修改时间比它晚 3. 某个条件P已被更新
- 如果在命令行中指定一个目标,则更新这个目标
- 如果不指定目标则更新Makefile中的第一条规则的目标
- 第一条规则的目标则称为缺省目标
[!note] 规则 只要执行了命令列表,则认为更新了目标,及时目标并没有产生文件 比如clean目标,并不依赖于任何条件,并且执行它的命令列表不会生成claen这个文件
[!note] @与-字符 -
@
:不显示命令本身而只显示其结果 -@echo "cleanning project"
:直接显示cleanning project
--
:即使命令出错,make也会执行后续的命令 --rm main *.o*
:即使报错,也会继续执行后续的指令
内置函数
字符串替换
$(parsubst pattern, replacement, text)
:在于pattern
匹配的text
中查找空格分隔的单词,并将其替换为replacement
foreach
$(foreach var, list, text)
:将一个单词列表转换为另一个列表var
被设置为list中每个单词text
是针对每一个单词的展开
if
- if检查第一个参数是否为非空
true
:运行第二个参数false
:运行第三个参数makefile foo := $(if this-is-not-empty,then!,else!) empty := bar := $(if $(empty),then!,else!) all: @echo $(foo) @echo $(bar)
函数调用
shell函数
其他规则
隐式规则
- 隐含规则的列表:
- 编译C程序:
$(CC) -c $(CPPFLAGS) $(CFLAGS)
- 编译C++程序:
$(CXX) -c $(CPPFLAGS) $(CXXFLAGS)
- 连接单个目标文件:
$(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)
- 编译C程序:
- 隐式规则使用的变量
CC
:编译C程序的程序;默认为cc
CXX
:编译C++程序的程序;默认为gcc
CFLAGS
:提供给C编译器的额外标志CXXFLAGS
:提供给C++编译器的额外标志CPPFLAGS
:提供给C预处理器的额外标志LDFLAGS
:当编译器应该调用链接器时,提供的额外标志
静态模式规则
- 静态模式规则是在Makefile中减少编写量的另一种方式:
- 本质:给定
target
与target-pattern
匹配
[!bug] 痛苦的调试经历 在链接测试程序时,确保
——Learn C The Hard Hard Way : ex32liblcthw.a
静态库的链接命令在测试程序源文件tests/list_tests.c
的后面,例如:cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG tests/list_tests.c build/liblcthw.a -o tests/list_tests
对Makefile的修改:
自动化变量
- 自动化变量:把模式中所定义的一系列文件自动的取出
- 包括:
$@
:规则中的目标文件集$%
:仅当目标是函数库文件中,表示规则中的目标成员名$<
:依赖目标中的第一个目标名字$?
:所有比目标新的依赖目标集合$^
:所有依赖目标的集合,去重$+
:所有依赖目标的集合,但是不去重$*
:
[!tip] 只输出命令但不执行
make -nB
,可以查看make
过程中都运行了什么命令,以此来理解一些变量的值