Makefile
它的好处就是自动化编译,极大地提高了开发的效率
make命令格式
make [ -f file] [ options ] [ targets ]
- make默认在工作目录下寻找名为GNUnmakefile,makefile,Makefile的文件作为makefile的输入文件
-f
可以指定以上名字以外的文件作为makefile的输入文件
options:
-v
:显示make工具的版本信息-w
:处理makefile之前和之后都显示一次工作路径-C 目录xx
: 读取makefile之前的工作路径 ,并改变工作路径至xx目录-n
:只打印要执行的命令但是不执行-s
:执行但是不显示执行的命令
?
我们学习 makefile 要掌握它的1个规则,2个函数,3个自动变量
接下来我们先来看一下1个规则
Makefile中的规则
一条规则里面有着基本的三要素:
目标文件:依赖文件列表
命令列表 #此前必须有一个Tab缩进
目标:
通常是要产生的文件名称,目标可以是可执行文件或者其他obj文件,也可以是一个动作的名称依赖文件:
用来输入从而产生目标的文件,一个目标通常有好几个依赖文件(可以没有)命令:
make执行的动作,一个规则可以包含几条命令(可以没有)
有多个命令时,每个命令占一行
?
下面来看一个例子:
test01:add.c mul.c test.c #目标文件以及依赖文件
gcc add.c mul.c -o test01 #命令列表,多文件连编
这样会有一个缺点:效率低,修改其中一个文件,所有文件都要重新编译,并且在预处理、编译、汇编、链接四步中,最耗时间的是编译
那么我们可以把编译分步去写:
test02:add.o mul.o test.o
gcc add.o mul.o test.o -o test02
add.o:add.c
gcc -c add.c
mul.o:mul.c
gcc -c mul.c
test.o:test.c
gcc -c test.c
这样我们如果修改了其中一个文件,那么只需要重新编译那个需要更新的文件可以了
?
Makefile中的变量
在其中的变量类似于c语言中的宏定义,使用该变量相当于内容替换
定义变量: 变量名 = 变量值
引用变量: $(变量名)
或 ${变量名}
接下来是Makefile中的三个自动变量:
$@
: 表示规则中的目标$^
表示所有依赖条件$<
表示对应规则的第一个依赖条件;如果将该变量应用到模式规则中,那么它可以将依赖列表中的依赖依次取出,套用模式规则
他们不可以单独使用,必须在一条规则的命令中才可以使用,因为自动变量也要知道在哪一条规则中,才能自动找到对应的目标与依赖条件
看一下它们的使用:
test.o:test.c
gcc -c $< -o $@
#在这一条规则中$<就是test.c , $@就是test.o
#也就是这条命令相当于:
#gcc test.c -o test.o
在后面将自动变量和模式规则配合使用会更加舒服?
?
Makefile中的函数
1) wildcard
这个函数可以用来批量获取文件
例如wildcard ./*.c
就是获取当前目录下的所有.c
文件
#获取当前目录下所有.c文件,并将其赋给变量SRC
SRC=$(wildcard ./*.c)
2) patsubst
这个函数有三个参数,这里用一个例子来说明
patsubst %.c,%.o,$(SRC)
就是将 SRC
(第三个参数) 中所有出现的.c
(第一个参数)替换成.o
(第二个参数)
以上说的替换仅仅是名字的替换,而不是说文件的转换改变,那么就这样换个换一下名字有什么作用呢?
这样替换了之后,我们可以直接用它表示在一条中我们想生成的所有.o
文件,就不用一个个文件敲上去了(稍稍解放劳动力)
?
模式规则
假如我现在目录下有test.c add.c mul.c
三个.c
文件
我想要编译它们生成.o
文件
那么我要在 makefile 中写:
test.o:test.c
gcc -c $< -o $@
add.o:add.c
gcc -c $< -o $@
mul.o:mul.c
gcc -c $< -o $@
这样看起来就有很多重复的代码
现在我们用模式规则来写
#模式匹配 将所有的.c都生成对应的.o
%.o:%.c
gcc -c $< -o $@
?
接着我们再结合之前的函数,假如我现在目录下有test.c add.c mul.c
三个.c
文件,我想要通过make生成最终的目标文件testTarget
#获取当前目录下所有.c文件,并将其赋给变量SRC
#这里SRC=test.c add.c mul.c
SRC=$(wildcard ./*.c)
#将SRC中所有出现的.c替换为.o,注意这里没有发生转换,只是文件名的替换
#这里OBJS=test.o add.o mul.o
OBJS=$(patsubst %.c,%.o,$(SRC))
TARGET=testTarget
ALL:$(TARGET) #告诉make这TARGET是你最终要生成的目标文件
#第一次到这的时候,在这条规则中作为依赖条件的OBJS还不存在
#那么make就会寻找有没有其他规则是用于生成这个依赖条件的
$(TARGET):$(OBJS)
gcc $(OBJS) -o $(TARGET)
#模式匹配 将所有的.c都生成对应的.o
#在这里,就将上一条规则所需要的依赖条件生成了
$(OBJS):%.o:%.c
gcc -c $< -o $@
#清理编译产生的中间obj文件以及目标文件
clean:
-rm -rf $(OBJS) $(TARGET)
?
Makefile中的伪目标
问题:在我们执行 make clean
时如果当前目录下有同名clean文件,会影响 make 的判断
伪目标声明.PHONY:
.PHONY:clean #声明clean为伪目标
伪目标声明后,makefile不会判断目标文件是否存在或者目标文件是否更新了
在makefile中有一些加于命令前的特殊符号:
-
: 此命令出问题,make也会继续执行后续命令
clean:
-rm -rf $(OBJS) $(TARGET)
例如这里如果在clean之前,其中一个本要被rm
删除的文件被提前删除了,那么这一条命令就出问题了
这时候我们不想它停下来,就在命令前加上-
@
:不显示命令本身,只显示结果
#模式匹配 将所有的.c都生成对应的.o
%.o:%.c
@gcc -c $< -o $@
?
实际尝试
这里我们将头文件,.c
, .o
文件放在不同的目录下,编写makefile
4个.c
文件:
头文件:
接下来是makefile:
SRC=$(wildcard ./src/*.c) #这里要指明路径,是./sec目录下的.c文件
OBJS=$(patsubst ./src/%.c, ./obj/%.o, $(SRC)) #同样的,这里注明.o文件的路径
head_path=./inc
Target=TargetFile.out
ALL:$(Target)
#最后的链接
$(Target):$(OBJS)
gcc $^ -o $@
#src目录下.c文件的编译
#注意头文件的展开是在预处理阶段
#所以要在这用-I指明头文件的路径
#这里的模式匹配注意文件的路径
#用src目录下的.c文件,生成./obj目录下在这个规则中我们想要的目标.o文件
$(OBJS):./obj/%.o:./src/%.c
gcc -c $< -o $@ -I $(head_path)
clean:
-rm -rf $(OBJS) $(Target)
.PHONY:clean ALL
看一下运行的结果:
?
?我是旺仔,和你一起成长