Makefile执行过程
- 读取阶段
- 读取Makefile文件的所有内容
- 根据Makefile的内容再程序中建立起变量
- 在程序中构建起显示规则和隐式规则
- 建立目标和依赖之间的依赖图
- 目标更新阶段
- 用第一阶段构建起来的数据确定哪个目标需要更新然后执行对应的更新方法
- 变量和函数的展开如果发生再第一阶段就称作立即展开,否则称为延迟展开
Makefile 的规则
target: prerequisites |
target
target 可以是一个 object file (目标文件,eg: main.o),也可以是一个可执行文件(executable file, eg: hello, ./hello execute the target file),还可以是一个标签(label, eg: clean)。
指定最终目标(默认是第一个目标) :
.DEFAULT_GOAL = your-customized-target(你的自定义总目标)
如果某个目标(target) 跟最终的目标以及过程中的目标文件没有任何依赖关系的话就不会自动执行,必须手动执行 eg: 以下
Makefile
文件执行make
命令时,test.o 和 clean 都不会自动执行,因为他们跟最终的target
无关,必须手动make clean
,make test.o
才会执行相应的recipe
。target: hello.o main.o
g++ -o target hello.o main.o
hello.o: hello.cpp
g++ -c hello.cpp
main.o: main.cpp hello.h
g++ -c main.cpp
test.o: test.cpp
g++ -c test.cpp
clean:
rm *.o target
伪目标:没有对应依赖文件的目标
# 1. 正常可以不用将clean 加入到伪目标中,但是当该文件夹下存在和目标同名的文件时就会冲突,当前路径下有个文件叫做 clean,执行 make clean 就不能正常执行,会提示 clean is up to date |
prerequisites(依赖)
- 生成改
target
所依赖的文件(没有依赖项就不写)。 - 依赖类型: target: normal prequisites | order-only prequisites,make 会根据你的依赖文件(normal prequisites) 是否更改过来判断目标文件是否需要重新执行 recipe, 不会检查 order-only prequisites。
recipe(执行的方法,本质就是shell命令)
- 该
target
需要执行的命令(可以是任意的 shell 命令)。
make 会自动执行 shell 命令 |
Makefile 中使用变量(相当于C语言宏定义)
引用变量切记要用括号 () {} 都可以
objects = main.o kbd.o command.o dispaly.o \# 反斜杠是连接不同行的内容 |
自动得到所有的依赖文件
g++ -MM main.cpp |
多行变量
define shells |
取消变量定义
undefine shells |
变量覆盖
# 在makefile objs 的定义是 |
系统中的环境变量可以直接使用
变量替换引用
objs = main.o hello.o test.o |
绑定目标的变量
# 全局的变量 整个 makefile 文件都能访问 |
自动变量
$@ # 本目标的目标名 target |
二次展开
多目标与多规则
独立多目标
组合多目标
组合多目标调用一次方法将更新所有目标,独立多目标每个目标的更新需要单独调用一次更新方法
block.o input.o scene.o &: block.cpp input.cpp common.h
g++ -c block.cpp
g++ -c input.cpp
g++ -c scene.cpp所有目标的更新方法都写道其中,每次更新只会调用一次
静态模式
静态模式就是用
%
进行文件匹配来推导出相应的依赖(只能在一定程度上解决文件依赖问题,头文件依赖任然解决不了)$(objs): %.o : %.cpp
g++ -c $(@:%.o=%.cpp)
特殊目标
.ONESHELL |
指定依赖搜索路径
# upper case VPATH 大写的 VPATH 是变量 |
条件判断
# ifdef 判断一个变量是否已经定义 |
函数
字符串处理函数
# 函数调用时相当于使用变量 |
文件名处理函数
files=src/main.cpp hello.cpp |
TO DO
条件函数
file函数
foreach函数
call函数
value函数
origin函数
flavor函数
eval函数
shell函数
let函数
信息提示和控制函数
显示规则和隐式规则
警告:Makefile 的自动推导还是少用,保不齐出什么 bug 你连调试都不知道怎么调试当项目比较复杂时可能会有以下问题
- 依赖关系不明确:如果文件之间的依赖关系没有明确指定,make 可能无法正确判断哪些文件需要更新。
- 文件名约定:如果文件名不符合常见的约定,make 可能无法正确识别。
- 复杂的构建规则:当项目的构建规则非常复杂时,自动推导可能会出现错误。
C语言编译
.c -> .o
$(CC) $(CPPFLAGS) $(CFLAGS) -c |
C++编译
.cc .cpp .C -> .o
$(CC) $(CPPFLAGS) $(CFLAGS) -c |
链接
由 .o 文件链接到可执行文件
$(CC) $(LDFLAGS) *.o $(LOADLIBES) $(LDLIBS) |
同一项目有多个Makefile文件
包含其他文件
使用include 指令可以读入其他 Makefile 文件的内容,效果就如同在 include 的位置上用对应的文件内容替换一样
include mkf1 mkf2 |
嵌套Makefile
|
可以通过 export
指令向子项目的 Makefile
传递变量
export var # 传递变量 var |
总结
Makefile 实质就是一系列的 shell 命令,只要对 shell 命令熟悉就很好写 Makefile
后续学习过程,多阅读大型项目的 Makefile 代码
redis: https://github.com/redis/redis
ffmpeg
aubio
libav
OpenH264
TinyVM
TinyXML2