Makefile
- makefile 文件的基本格式
target: dependencies steps to build target with dependencies
-
目标、源文件、生成方式(shell)
-
例c多文件编译
.PHONY: all clean depen depen=clean main.o libmulti_div.so libadd_minus.a all: $(depen) sudo cp libmulti_div.so /usr/lib gcc -o main4 main.o -L./ -ladd_minus -lmulti_div libadd_minus.a: gcc -c add_minus.c ar rc libadd_minus.a add_minus.o libmulti_div.so: gcc multi_div.c -fPIC -shared -o libmulti_div.so main.o: patch -p2 < v3.0.patch gcc -c main.c clean: -rm $(depen) add_minus.o main4 main.c.\*
基本概念¶
依赖的执行顺序¶
- GNU make 会按默认的优先级查找当前文件夹的文件,查找的优先级为: 「GNUmakefile」> 「makefile」> 「Makefile」。(三种不同名称的 makefile)
- 手动指定
make -f name
- 手动指定
- 使用时间戳来判断文件是否需要更新是一种高效的方式,避免不必要的重新编译。
- 依赖文件不存在:如果
make
检测到一个目标的依赖文件不存在,它会查找Makefile
中定义的规则来创建这个依赖文件。 - 依赖文件的时间戳比目标文件新:如果依赖文件存在且其修改时间比目标文件的修改时间要新,
make
会认为目标文件已经过时。因此,make
会执行更新目标文件所需要的命令。这通常涉及重新编译源代码或执行其他更新目标文件所需的步骤。 - 依赖文件的时间戳比目标文件老:如果所有依赖文件的修改时间都比目标文件的修改时间要旧,
make
会判断目标文件是最新的,没有必要重新构建。因此,它会忽略对应于这些依赖文件的命令。
- 依赖文件不存在:如果
-
make 按照从左到右的顺序检查依赖
main:main.o testa testb
-
目标重建的规则:
- 目标是否为伪目标,若是则需要执行重建,否则参考第 2 条
- 目标是否存在,若存在,则参考第 3 条,若不存在则需要执行重建
- 目标文件是否比其全部依赖项都新,若比其任一依赖项文件要旧则需要执行重建
-
依赖的分类
- 常规依赖:告诉
make
目标重建所需的依赖文件以及何时需要更新目标。 - order-only 依赖:告诉
make
目标重建所需的依赖文件,这些依赖文件的更新不会导致目标被重建。order-only 依赖以管道符号「|」开始,管道符号左边为常规依赖项,管道符号右边为 order-only 依赖项。 - 如
aim_1:test_1 |test_2 test_3
- 常规依赖:告诉
自动推导规¶
- 对于 xxx. o 类型的目标会默认使用命令 cc -c xxx. c -o xxx. o 进行编译。
- 对于 xxx 类型的目标会默认使用命令 cc xxx. o -o xxx 进行编译。
- 即使没有 makefile 文件,自动推导也会进行
最终目标¶
- 一般情况下 makefile 的最终目标是第一条规则的目标。
- 当目标名以符号「.」开头且其后跟的不是斜线符号「/」(即不被解析为目录符号)时,此目标无法作为最终目标。如
.PHONY
- 需要手动选择执行
make -f phony_make .PHONY
- 需要手动选择执行
- 模式规则中的目标不会被作为最终目标
%:
可以匹配任何未显示定义的目标,是一种模式规则,不会被作为最终目目标
- 环境变量指定的文件会被
make
首先读入,但其中的目标无法作为最终目标。 - 关键字
include
所引用进 makefile 中的规则,它的目标会被作为最终目标。
基本概念¶
-
注意缩进必须使用制表符不能用空格代替
-
忽略错误:添加
-
作为前缀-rm $(depen)
,即使命令执行出现错误也继续完成 makefile -
执行 make file:
make targetname
main: main.o gcc -o main main.o main.o: main.c gcc -c main.c
- 可以使用
make main.o
或make main
-
省略 name 时默认以第一条规则作为目标
-
在命令前添加
@
不会把命令本身输出,如@cd
include¶
-
make
读入 makefile 时遇见include
指示符会暂停读入当前文件,转而读入include
指定的文件,之后才继续读入当前文件。 -
makefile 中可以使用
include
指令来包含另一个文件。(如引入变量、依赖的构建方式) include ./makefile_dir/inc_a
-
当 include 指示符包含的文件不包含绝对路径,且在当前路径下也无法寻找到时,make 会按以下优先级寻找文件:
-I
指定的目录make -I ./makefile_dir/
/usr/gnu/include
/usr/local/include
/usr/include
- 指定 makefile 的 include 路径
-
makefile 的执行分为两个阶段,一阶段是读入所有 makefile 文件,
include
导入的文件以及环境变量指定的文件,同名变量会被 include 的覆盖
环境变量¶
- 功能上与 include 类似,允许读取环境变量中的 makefile 文件
- 如
export MAKEFILES=./makefile_dir/c_inc
(在 bash 中执行)
重载¶
- makefile 重载另一个 makefile 时,不允许有规则名重名。对于两个文件中同名的规则,
make
后读入的规则会重写先读入的规则 - 对于存在重名方法时不要直接 include 防止覆盖
AAim: make -f AMake intro BAim: make -f BMake intro
变量¶
-
变量的定义
depen=main.o testa testb clean
- 使用
$(depen)
- 使用
-
声明为目标(即名称代表要执行的命令、阶段而不是实际的文件),类似声明变量名
.PHONY: all clean depen
-
输入时提供参数
make -f eq.mk a=6
,在 makefile 内直接使用${a}
特殊变量¶
$@
:当前执行目标的名称$<
:第一个依赖文件名$^
:所有的依赖文件名,以空格分隔$?
:比目标新的依赖文件名,以空格分隔$*
:目标模式中%
匹配的部分
通配符¶
- 在规则的目标或依赖中使用%而不是*
aim_%: test_%
函数方法¶
$(wildcard *.c)
:这个wildcard
函数扩展为当前目录下所有匹配规则的文件名的列表(以.c
结尾的文件名列表)$(patsubst %.c,%.o,$(wildcard *.c))
:patsubst
函数用于模式替换。它的作用是将第三个参数(这里是通过wildcard
得到的文件列表)中匹配第一个参数模式(%.c
)的部分替换为第二个参数指定的模式(%.o
)。obj = $(patsubst %.c,%.o,$(wildcard *.c))
流程控制¶
- 注意每一条指令都是相互独立的(并不是在相同的一个 shell 环境)
- 如果想要连续执行代码(如结合 cd),那么要将代码写在同一行
@pwd; cd .. ; pwd
- 可以使用\分行
@echo "cmd1 process \ id is :" $$$$; \ echo "cmd2 process id is :" $$$$
- 可以使用\分行
条件判断语句¶
- 判断关键字
-
格式
ifeq (ARG1, ARG2) ifeq 'ARG1' 'ARG2' ifeq "ARG1" "ARG2" ifeq "ARG1" 'ARG2' ifeq 'ARG1' "ARG2"
-
条件语句
CONDITIONAL-DIRECTIVE TEXT-IF-TRUE endif CONDITIONAL-DIRECTIVE TEXT-IF-TRUE else TEXT-IF-FALSE endif