makefile简介
1.0 介绍
为了解决编译一个工程非常繁琐这个问题,前人就给我们发明了编译辅助工具 make 工具,它的编译思路是非常简单的,它会在编译之前先比较哪个文件的时间发生了改变,如果说这个文件它修改的时间要晚于编译生成的文件,那么它就会按照要求重新构建这些文件,而不是说再浪费时间重新构建其他的文件了。
make 工具是编译辅助工具,用来解决使用命令编译工程非常繁琐的问题。
Makefile 就是描述了整个工程编译连接等规则的文件。
我们在终端输入完 make 命令之后,调用 make工具,make 就会在当前目录按照文件名就会找 makefile 文件,Makefile 的命名必须是 makefile 或Makefile ,m 大写小写都是可以的。
2.0 语法
目标…... : 依赖文件集合……
命令 1
命令 2
……
如:
main : main.o input.o calcu.o
gcc -o main main.o input.o calcu.o
这条规则的目标是 main,main.o、input.o 和 calcu.o 是生成 main 的依赖文件,如果要更新目标 main,就必须先更新它的所有依赖文件,如果依赖文件中的任何一个有更新,那么目标也必须更新,“更新”就是执行一遍规则中的命令列表。
命令列表中的每条命令必须以 TAB 键开始,不能使用空格!
make 命令会为 Makefile 中的每个以 TAB 开始的命令创建一个 Shell 进程去执行。
Makefile 中可以写注释,注释开头要用符号“#”。
clean:
rm *.o
rm main
可通过make clean执行其下的命令。
make 的执行流程:
make 命令会在当前目录下查找以 Makefile或makefile命名的文件。
当找到 Makefile 文件以后就会按照 Makefile 中定义的规则去编译生成最终的目标文件。
当发现目标文件不存在,或者目标所依赖的文件比目标文件新(也就是最后修改时间比目标文件晚)的话,就会执行后面的命令来更新目标。
3.0 变量
objects = main.o input.o calcu.o
main: $(objects)
gcc -o main $(objects)
其中将objects 赋值为"main.o input.o calcu.o", 后续通过$(变量名)来引用。
4.0 赋值
4.1 “=”
“最后赋值”,指借助另外一个变量,可以将变量的真实值推到后面去定义。也就是变量的真实值取决于它所引用的变量的最后一次有效值。如:
name = zzk
curname = $(name)
name = zuozhongkai
print:
@echo curname: $(curname)
# 结果为输出 curname: zuozhongkai
“echo”前面加了个“@”符号,因为 Make 在执行的过程中会自动输出命令执行过程,在命令前面加上“@”的话就不会输出命令执行过程。
4.2 “:=”
立即赋值,这是因为赋值符“:=”不会使用后面定义的变量,只能使用前面已经定义好的。如4.1中若使用:=则输出 curname: zzk。
4.3 “?=”
curname ?= zuozhongkai
如果变量 curname 前面没有被赋值,那么此变量就是“zuozhongkai”,如果前面已经赋过值了,那么就使用前面赋的值。
4.4 “+=”
追加。如:
objects = main.o inpiut.o
objects += calcu.o
一开始变量 objects 的值为“main.o input.o”,后面我们给他追加了一个“calcu.o”,因此变量 objects 变成了“main.o input.o calcu.o”,这个就是变量的追加。
5.0 模式匹配
5.1 %
%通配符匹配,表示长度任意的非空字符串。
5.2 自动变量匹配
自动化变量 | 描述 |
---|---|
$@ | 规则中的目标集合,在模式规则中,如果有多个目标的话,“$@”表示匹配模式中定义的目标集合。 |
$% | 当目标是函数库的时候表示规则中的目标成员名,如果目标不是函数库文件,那么其值为空 |
$< | 依赖文件集合中的第一个文件,如果依赖文件是以模式(即“%”)定义的,那么“$<”就是符合模式的一系列的文件集合。 |
$? | 所有比目标新的依赖目标集合,以空格分开。 |
$> | 所有依赖文件的集合,使用空格分开,如果在依赖文件中有多个重复的文件,"$^”会去除重复的依赖文件,值保留一份。 |
$+ | 和“$^”类似,但是当依赖文件存在重复的话不会去除重复的依赖文件。 |
$* | 这个变量表示目标模式中"%"及其之前的部分,如果目标是 test/a.test.c,目标模式为 a.%.c,那么“$*”就是 test/a.test。 |
如:
1 objects = main.o input.o calcu.o
2 main: $(objects)
3 gcc -o main $(objects)
4
5 %.o : %.c
6 gcc -c $<
7
8 clean:
9 rm *.o
10 rm main
等价于
1 main: main.o input.o calcu.o
2 gcc -o main main.o input.o calcu.o
3 main.o: main.c
4 gcc -c main.c
5 input.o: input.c
6 gcc -c input.c
7 calcu.o: calcu.c
8 gcc -c calcu.c
9
10 clean:
11 rm *.o
12 rm main
5.3 伪目标
Makefile 有一种特殊的目标——伪目标,一般的目标名都是要生成的文件,而伪目标不代表真正的目标名,在执行 make 命令的时候通过指定这个伪目标来执行其所在规则的定义的命令。
使用伪目标主要是为了避免 Makefile 中定义的执行命令的目标和工作目录下的实际文件出现名字冲突,有时候我们需要编写一个规则用来执行一些命令,但是这个规则不是用来创建文件的。
使用伪目标时要防止伪目标的名称与当前目录下文件名称重复,否则会因为目标下没有依赖文件,所以目标被认为是最新的,因此后面的 命令也就不会执行。
声明伪目标的方式如下:
.PHONY : clean
具体场景如下:
1 objects = main.o input.o calcu.o
2 main: $(objects)
3 gcc -o main $(objects)
4
5 .PHONY : clean
6
7 %.o : %.c
8 gcc -c $<
9
10 clean:
11 rm *.o
12 rm main
上述代码第 5 行声明 clean 为伪目标,声明 clean 为伪目标以后不管当前目录下是否存在名为“clean”的文件,输入“make clean”的话规则后面的 rm 命令都会执行。
5.4 条件判断
语法格式有两种:
<条件关键字>
<条件为真时执行的语句>
endif
以及:
<条件关键字>
<条件为真时执行的语句>
else
<条件为假时执行的语句>
endif
条件关键字有 4 个、可分为2组:ifeq、ifneq一组,ifdef 和 ifndef一组,前一组表示是否相等,后一组表示是否非空。
如:
ifeq (<参数 1>, <参数 2>)
# 用单双引号皆可
ifeq “<参数 1>”, “<参数 2>”
ifeq “<参数 1>”, ‘<参数 2>’
ifdef <变量名>
6.0 函数
6.1 wildcard
通配符“%”只能用在规则中,只有在规则中它才会展开,如果在变量定义和函数使用时,通配符不会自动展开,这个时候就要用到函数wildcard,使用方法如下:
$(wildcard PATTERN...)
# 如:
$(wildcard *.c)
# 上面的代码是用来获取当前目录下所有的.c文件,类似"%"
6.2 dir
用函数dir来获取目录。
此函数用来从文件名序列
$(dir </src/a.c>) #提取 /src
6.3 notdir
此函数用与从文件名序列
$(notdir </src/a.c>) #提取文件名“a.c”。
6.4 foreach函数
格式:$(foreach <var>, <list>, <text>)
功能:把参数<list>中的单词逐一取出放到参数<var>所指定的变量中,然后再执行<list>所包含的表达式。每一次<text>会返回一个字符串。
每次<text>都会返回一个字符串,循环的过程中,<text>中所包含的每个字符串会以空格隔开,最后当整个循环结束时,<text>所返回的每个字符串所组成的整个字符串将会是函数 foreach 函数的返回值。
如:
names := a b c d
files := $(foreach n, $(names), $(n).o)
上面的例子中,$(name)中的单词会被挨个取出,并存到变量“n”中,“$(n).o”每次根据“$(n)”计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以,$(files)的值是“a.o b.o c.o d.o”。
6.5 subst
字符串替换。形式如下:
$(subst <from>, <to>, <text>)
此函数的功能是将字符串<text>中的<from>内容替换为<to>,函数返回被替换以后的字符串,比如如下示例:
$(subst zzk, ZZK, my name is zzk)
把字符串“my name is zzk”中的“zzk”替换为“ZZK”,替换完成以后的字符串为“my nameis ZZK "
6.6 patsubst
此函数查找字符串<text>中的单词是否符合模式<pattern>,如果匹配就用<replacement>来替换掉,<pattern>可以使用通配符“%",表示任意长度的字符串,函数返回值就是替换后的字符串。如果<replacement>中也包涵“%”,那么<replacement>中的“%”将是<pattern>中的那个“%”所代表的字符串,比如:
$(patsubst %.c, %.o, a.c b.c c.c)
将字符串“a.cb.cc.c”中的所有符合“%.c”的字符串,替换为“%.o”,替换完成以后的字符串为“a.o b.o c.o”。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架