linux下自动构建工具make:makefile


什么是makefile?大多数人都应该是不太清楚的,因为现在人们基本都用着非常好的适合自己的IDE,而IDE为人们做了makefile该做的,从而导致大多数人并不了解makefile。如果是在Linux下编译程序,那么makefile是必须要掌握的!

很多linux新手经常是直接用gcc/g++命令来编译程序的,因为对于linux新手,他们的源文件数目少得可怜,他们仅仅用gcc/g++也是可以的,但通常一个项目是拥有非常多的源文件的,而这些源文件直接也有着复杂的关系(编译的先后顺序等),如果此时仍然用gcc/g++命令来直接编译的话,那将会是一个极其痛苦的事情。

make/makefile介绍

make是一条命令,makefile是一个文件。

执行make命令时,makefile会告诉make命令以什么样的规则来编译程序。

而makfile文件里的内容(也就是编译规则),是需要我们自己来写的!

来一个简单的例子:

image-20221120203333125

这里有四个文件:makefile、add.h、add.c、main.c

makefile里编写了编译add.c、main.c的规则

接下来,我们只需要执行一条命令make,即可完成makefile里的编译规则。

image-20221120203538207

现在我们再回想一下,如果不用make/makefile的话,我们是如何编译这两个源文件的呢?

我们应该是用gcc -o main main.c add.c 这一条命令来编译的。如果我们想要编译十次,我们就需要输入十次这样的命令,但如果有了make/makefile,我们只需要十次make。如果来一百个源文件,并且编译十次,我们直接用gcc会极其的麻烦,但是make仍然只需要十次就ok。

看到这里,想必大家已经认识到了make/makefile的重要性!

makefile的核心规则

target ...:prerequisites ...
	command
	...
	...

target是一个目标文件,prerequisites是生成target所需要的文件,commmand是make所需要执行的命令(shell命令)。

而这就是一种依赖关系:target依赖于prerequisites,而生成target的规则(命令)定义在command中。

值得注意的是:如果prerequisites中如果存在一个文件的时间要比target新,则command才能够执行。

看下面例子:

image-20221120205059317

当main已存在,并且main比main.c、add.c时间都要新的话,makefile中的command是无效的。

那么我们如何证实呢?

截屏2022-11-20 20.55.04

可以看到,main的modify时间是要比main.c、add.c的modify时间要新的,然后执行make的时候出现提示。

那么我们现在改一下main.c的modify时间(需要对main.c里的文本内容作修改),看看会发生什么。

截屏2022-11-20 21.00.16

我们打开文件,在里面添加了若干行空格,就改变了main.c的modify时间,导致main.c的modify时间比main的要新,所以后续执行make命令时能够执行成功。

这里有一个疑问,那就是要比较target和prerequisites的日期新旧,为什么只比较modify时间?

因为modify时间只有修改了文件里的内容时,才会跟着改变。

makefile的寻找规则

执行make时,会默认执行makefile里的第一个target。当第一个target成功生成(执行)之后,则停止下面的规则。如果当生成第一个target时发现所需的依赖文件不存在,则按从上到下的顺序依次执行规则直到找到依赖文件使得第一个target生成成功。

image-20221120220738353

直接执行make时,会为了makefile里的第一个target而从上往下寻找依赖文件。那么如果我们不想生成第一个target呢?假设我们这里只想生成add.o,该怎么办?这时我们需要make加上我们要生成(执行的)那个target。

image-20221120221217078

看上图,我们只想生成main.o,所以我们make main.o,而main.o的依赖文件是不存在的,所以向下寻找,直到生成main.o停止,所以也并没有执行makefile里面的add.o add.s add.i等操作。

makefile的伪目标

上面我们说过target和prerequisites会根据modify的时间新旧来决定是否执行command。

那么为什么要这样做呢?

这是因为,根据prerequisites而生成的target没必要再次编译生成,并且大型项目编译的时间是很长的,只有当prerequisites发生变化时target才需要再次编译生成最新的。

那么如果我们就是想要每次重新编译生成target而不受时间新旧的约束,这时该怎么做呢?我们可以给target前面修饰一个.PHONY,经过.PHONY修饰的名词就变为来伪目标,该目标下的command每次都可以执行而不受时间约束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云朵c

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值