======MakeFile====== //Notes & usage// ---- ====Installation==== * 只能在 linux 环境下运行 * Windows 可以使用 [[https://www.mingw-w64.org/getting-started/msys2/|msYS2]] # ubuntu sudo apt install build-essential # check current version make -v ====Basics==== ''Makefile'' 由若干规则(//Rule//)组成。每条规则包含: * 目标文件(//Target//) * 依赖文件(//Prerequisites//) * 生成目标文件的命令 格式为: Target File: Prerequisites1, Prerequisites2.... # 生成命令以 Tab 开头 complie commands... 比如我们要基于 ''a.txt'' 和 ''b.txt'' 生成文件 ''m.txt'': m.txt: a.txt, b.txt cat a.txt, b.txt > m.txt ===多层级 makefile=== 假设 ''m.txt'' 是中间件,用于上一级的 ''x.txt'' 生成,那么 makefile 可以写成: # makefile 会默认执行第一条规则 # 查找依赖的时候会递归向下找寻对应规则 x.txt: m.txt c.txt cat m.txt c.txt > x.txt m.txt: a.txt b.txt cat a.txt b.txt > m.txt 可以看到此时 ''m.txt'' 并未生成。这种情况下,''make'' 会向下查找是否有可以支持 ''m.txt'' 生成的规则。 ==增量编译== make 生成的文件会基于依赖文件的最后修改时间来决定是否重新生成。如果当前生成文件使用的是最新的依赖文件,那么 make 会自动跳过生成,并提供对应输入: # 增量编译 $ make make: 'x.txt' is up to date. ===伪目标=== 从上面的例子中可以看到,make 只有在两种情况下才有新文件的生成动作: * 目标文件不存在 * 目标文件**过期** 假设我们希望的不是生成文件,而是批量执行一系列命令,那么上述要求就会导致问题。比如我们想批量删除之前例子中的生成文件。一般思路是写一个文件 ''clean'',并把命令都写进去: clean: rm-f m.txt rm-f x.txt 当我们运行的时候,由于 ''clean'' 作为目标,本身没有任何依赖文件,因此 makefile 会认为该目标文件是 up to date 的状态;所以不会有任何命令的执行。\\ \\ 这种情况的解决方式是,不要以任何形式的文件作为命令的载体,而是以 ''.PHONY'' 关键词开头,将 ''clean'' 对应的命令全写到 makefile 里面去。''.PHONY'' 关键字会让 makefile 将目标 ''clean'' 视为**伪目标**(//PHONY Target//)。这种情况下的 ''clean'' 会被视作一个动作;不管有没有这个名字的文件,都会执行这个**动作**: x.txt: m.txt c.txt cat m.txt c.txt >x.txt m.txt: a.txt b.txt cat a.txt b.txt >m.txt # define clean "action" .PHONY: clean clean: rm -f m.txt rm -f x.txt 运行时,需要单独的使用该目标: make clean 这样的伪目标还有很多,比如: make build # 编译程序 make run # 运行程序 make clean # 删除中间产物 make # 默认执行 all → build