Linux项目自动化构建工具--make/makefile(超级详细)

背景:

       一个大型工程中的源文件不计其数,它们按照不同的功能分别放到相对应的文件中去,当我们想去编译某些特定的文件的时候,那我们可以利用来书写 makefile 来制定编译规则。比如:什么文件需要先编译,什么文件需要后编译,什么文件需要重新编译等等一些功能我们都可以通过来书写 makefile 文档来完成相应的功能,这样我们的工作效率就大大提高了,而且相对而言比较省力,不用一行一行的去敲命令,可以更好的帮助我们完成工作。

理解:

       make 和 makefile
两个需要搭配使用,make 是命令工具,是一个解释 makefile 中指令的工具,而 makefile 它是一个集合了多个命令的文本文档。

makefile 的书写:

        首先利用 vi 打开 makefile 。(文档名必须为 makefile/Makefile
(注: 如果当前目录下没有makefile 文档,那么就相当于新建一个 makefile 文档)如下图:
在这里插入图片描述

在这里插入图片描述注:我这里是配置过的 VI 编辑器,正常情况下,可能没有这么好看,实用,但是不影响我们书写的东西。我们需要进入 vi 编辑器的插入模式(i)进行书写。

makefile 的书写规则:

目标对象:依赖对象
【tab】被执行的命令
(注:必须为 tab 键,不能用空格进行替换,语法不支持.)

例子:假如我们想要编译以下的代码。

#include <stdio.h>
#include <stdlib.h>

int main() {

 printf("hello makefile!\n");
 return 0;
 
}

上图是我们需要被编译的代码,下面是书写的 makefile 文档。

在这里插入图片描述
我们来仔细说一下这个例子,首先,我们知道 IDE 在 “编译” 我们的代码的时候会经过以下几个步骤:1.预处理。 2.编译。 3.汇编。4.链接。每经过一个步骤都会生成一个目标文件。我们之所以将它写的这么详细是因为我想说明一下 make 的执行过程。

make的原理:

1.当执行 make 命令时,它会在当前目录下寻找 makefile / Makefile文件。
2. 如果找到,它会找文件中的第一个目标文件,在上面的例子中,他会找到“ main ”这个文件,并把这个文件作为最终的目标文件,所做的一切都是朝它去。
3. 如果 main 文件不存在,或是 main 所依赖的后面 main.o 文件的文件修改时间要比 main 这个文件新(可以用 touch 测试),那么,他就会执行后面所定义的命令来生成 main 这个文件。这样做的目的是:所生成的文件是由最新的源文件生成的。
4. 如果 main 所依赖的 main.o 文件不存在,那么make会在当前文件中找目标为 main.o 文件的依赖性,如果找到,则再根据那一个规则生成 main.o 文件,然后依此类推的向下找。(这有点像一个堆栈的过程,递归进行查找目标对象所以依赖的项,并生成。)
5. 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
6. 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make 根本不理。
7. make 只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不存在,那它还是不工作。

具体过程如下:

在这里插入图片描述

make 执行之后会生成很多的中间文件,我们需要删除清理掉这些中间文件,我们应该怎么做呐?一个一个的删除吗? 我们的回答当然是 “ 不 ”,现在我们只是写了一个简单的程序,如果我们所写的程序比较大,每次生成编译一次,就要删除一次,中间所产生的文件可能有成千上万个,那么我们这样做的效率也就太慢了,费时费力,为了解决这个问题,我们有一个办法就是声明一个 “伪对象”

.PHONY:声明伪对象
        伪对象:不管目标对象是否存在,不管目标对象是否最新,每次都要重新执行生成规则
在这里插入图片描述
这样,每次不管怎么样都会执行 rm 指令。

最后再介绍一下 makefile 编写过程常用的技巧:
makefile 的变量的定义与使用:(这个有点像 C/C++ 中的宏定义与使用)

  1. 变量的定义与使用:
    在这里插入图片描述
    第 1,2,3 行分别定义了变量gc,he.c,he,并且使用了直接赋值的方式进行了赋值,第5,6行利用了 “ $( 变量名) ” 这样的形式使用了变量。

makefile 变量的四种赋值方式:

  1. 直接赋值 ====> " := "
  2. 递归赋值 ====> " = "
  3. 条件赋值 ====> " ?= "
  4. 追加赋值 ====> " += "

下面利用例子来说一下它们有什么区别:

  1. 直接赋值:
    在这里插入图片描述
    输出为:
    x => poo
    y => xx
  2. 递归赋值:
    在这里插入图片描述
    输出为:
    x => aa
    y => aabb
    由结果可知,我们直接赋值和递归赋值是不一样的。区别是:
    直接赋值:将当前所给的值直接展开赋值给变量,类似于 C/C++中的赋值。
    递归赋值:当某一个变量在的定义的时候。引用了其他变量的值,那么它就会去递归的展开寻找这个值,直到找到这个变量的最新定义才停止,找到之后,然后,再赋值给变量。
  3. 追加赋值:
    在这里插入图片描述
    输出为:
    x => poo p
    y => bbb
    追加赋值:由例子可知,为已经赋值的变量增加新值,如果,变量原先没有定义那么它相当于直接赋值。
  4. 条件赋值:
    在这里插入图片描述
    输出为:
    x => haha
    y => hello
    由此可知,如果变量没有被初始化,那么它的值就是条件赋值所提供的值,如果变量事先被初始化了,那么它的值就是初始化的值。(这一点有点像我们 C/C++ 的条件编译)

预定义变量
$@ : 目标对象
$^ :所有的依赖对象
$< : 依赖对象的第一个
wildcard(通配符): 获取指定目录下的文件名
patsubst : 字符串替换

使用例子:
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值