【Linux】Makefile的使用

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 $@ 

?

实际尝试

Snipaste_2022-03-29_16-09-32.png

这里我们将头文件,.c.o文件放在不同的目录下,编写makefile

4个.c文件:

Snipaste_2022-03-29_16-08-35.png

头文件:

Snipaste_2022-03-29_16-10-33.png

接下来是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

看一下运行的结果:

Snipaste_2022-03-29_23-49-58.png

?

?我是旺仔,和你一起成长


版权声明:本文为m0_61965582原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。