linux入门---初识makefile和make

为什么会有makefile和make

首先大家这里得知道一点就是:make是一个指令,makefile是一个文件,一个大型的项目会存在着成百上千个源文件和头文件,这么多的源文件和头文件他们的功能,类型,模块都是不同的,那么这些文件他们就会被分布到不同的子目录下,然而不同的子目录里面的每个源文件都可能需要编译,那么谁和谁编译在一起,谁先编译谁后编译等问题都是mkfile来决定的。在vs下我们把头文件和源文件添加好,按下ctrl+F5就可以正常的运行,可是先编译的哪个文件,后编译的哪个文件呢?这些文件又是怎么连接到一起的呢?我们现在写的程序都只有一个可执行程序,等后来我们学的多了,有三,四个可执行程序那又该怎么办呢?或者我把我的功能模块分成不同的子程序,编译一个程序形成多个可执行程序,那这个在vs下就会变得很难做,所以要想实现复杂的编译就只能在linux中使用make和mkfile。make带来的最大好处就是自动化编译,先写mkfile文件再使用make来执行mkfile文件中的内容。

如何写一个简单的makefile

我们首先、创建一个文件名为mycode.c,然后在这个文件里面添加一些内容,比如说下面的代码:

  #include<stdio.h>
   int main()
   {
       int a=10;
       int b=20;
       printf("相加的结果为:%d\n",a+b);
       return 0;
   }                                                                                                                                                 

在这里插入图片描述
然后再创建一个名为makefile的文件,这里大家要注意以下文件夹的名字必须为makefile或者Makefile其他的就不行了:
在这里插入图片描述

然后我们就要使用vim打开makefile这个文件
在这里插入图片描述

在makefile这个文件的第一行得先写依赖关系和依赖方法,比如说生成的可执行程序叫mytest,那么mytest这个文件就是依赖mycode.c形成的,所以在第一行我们得把这个依赖关系写上去其形式为生成的文件:依赖的文件mytest是生成的文件,mycode.c就是依赖的文件,那么操作的结果就如下:
在这里插入图片描述

然后在第二行就得写这两个文件是如何依赖的,也就是我们所说的依赖方法,也就是mycode.c是通过哪个指令生成的mytest文件,这里大家要注意的一点就是第二行写方法的时候得用Tab键开头然后再跟上依赖关系,切记一定得是Tab键其他键都不行4个空格键也不行,这里的指令也就是平时在命令行上输入的gcc mycode.c -o mytest ,那么这个操作的结果就如下:
在这里插入图片描述

好写到这里我们就已经可以通过目前的makefile来执行一些操作,我们退出并保存这个文件来到命令行,当前是没有mytest这个文件的,但是当我们输入一个make指令之后就可以看到当前路径下多出来了mytest这个文件,并且这个文件还是可以执行的:
在这里插入图片描述
既然有文件的创建那么就会有对应文件的删除,删除指令我们一般命名为clean,这个操作不需要任何文件的依赖,所以在依赖关系这里我们就什么都不填:
在这里插入图片描述
然后在下一行用Tab键开头,继续写clean所需要的方法,也就是平时使用的删除指令:
在这里插入图片描述
而且对于删除指令来说,我们一般会将其设置为伪目标,设置的方法就是在依赖文件的上一行添加上这样的一段内容:PHONY:指令名称那么对应到文件里面就是这样的操作:
在这里插入图片描述
至于这个修饰有什么用,待会下面我们会详细的介绍,那么到这里makefile文件就写完了,这里大家得注意一点就是文件里面写的依赖关系其实也是一个声明指令的过程,比如说mytest:mycode.c这里就是告诉操作系有个指令的名字叫mytest,还告诉了操作系统这个指令依赖的文件是mycode.c,而下面的clean:则是告诉操作系统这里有个指令叫做clean但是他不需要依赖任何的文件,而我们在命令行中输入的make实际上操作系统就会在文件中从上往下的查找第一个指令,找到就开始执行执行完就停止,如果我们想执行这个文件中的其他指令的话我们就得在make的后面加上具体的名称,比如说上面我们将文件已经写好了,那么接下来在命令行中如果我们想要创建文件的话就可以输入make,如果我们想要删除这个文件的话就要输入make clean,比如说下面的操作:
在这里插入图片描述
那么看到这里想必大家应该知道了如何写一个makefile文件以及如何使用make指令,那么接下来我们来看看makefile文件的原理。

makefile的原理

makefile存在的意义是为了帮助我们构建项目,也就是能够正常顺利的做一件事情,而做成一件事情就要有依赖关系和依赖方法,比如说我找我爸要钱就要这件事就要表明依赖关系和依赖方法,有一天我打电话找我爸要钱我跟他说我是你儿子就不说话了,你觉得我能要到钱吗?很明显是不能的因为我爸知道你是我的儿子但是他不知道儿子打电话给我的目的是什么?所以要不到钱如果我换个说法这么说:给我点钱。大家觉得可以吗?答案肯定是不行的这里只有意图没有依赖关系,爸爸只知道有人找他要钱但是他不知道谁找他要钱所以也是不可以的,而上面的gcc mycode.c -o mytest 就是依赖方法,mytest:mycode.c就是依赖关系,我们要想做成一件事就得将自己的依赖和关系依赖方法说清楚,我们找爸爸要钱就得说明我是你的儿子请你转我点钱,我们要想让makefile帮我们完成一些事情就得告诉makefile我们的依赖关系和依赖方法,那么这就是makefile的原理。

伪目标是什么?

我们上面提到了一个概念叫做伪目标,那什么是伪目标呢?他有什么用呢?我们来看看下面的操作,一开始是没有生成可执行的程序的:
在这里插入图片描述
我们输入一个make指令就可以看到这里生成了一个可执行程序并且可以正常执行的:
在这里插入图片描述
然后我们将这里的被依赖文件的内容进行一下修改:
在这里插入图片描述
修改成这样就可以让输出的结果变成40,然后我们再往命令行中输入一个make就可以看到这里又执行了一遍依赖方法:
在这里插入图片描述
并且结果变成了40,可以当我们再输入make指令的时候屏幕中会出现这么一句话:
在这里插入图片描述
那这是为啥呢?我们再修改一下被依赖文件的内容
在这里插入图片描述
再在命令行中输入make指令就会发现他执行力依赖方法,并且生成的可执行程序的结果为50:
在这里插入图片描述
那这是不是就说明了一个现象:一个文件被执行了一次生成了可执行程序,如果你没有对这个文件的内容进行修改的话,你想让操作系统再执行一次的话是不允许的,他会告诉你生成的可执行程序已经是最新版本了无需再执行一次,那大家有没有想过操作系统为什么要这么做呢?原因很简单,一个文件生成可执行程序是需要消耗时间和资源的我们这里写的内容简单消耗的时间少资源也少,所以生成一个可执行程序不需要什么代价,那如果一个文件生成一个可执行程序需要的时间是半个小时呢?如果一个文件没有被修改的话那为什么还要不停的生成同一个可执行程序呢?不是有现成的吗?所以对同一个文件执行多次make的时候系统会告诉我们这已经是最新文件了无需再次执行make指令,这是一种现象,那我们能不能改变这个现象呢?答案是可以的,我们使用伪目标来修饰一下这个指令就可以达到不管我们是否修改文件内容,只要我们输入对应的指令就可以执行的目的,伪目标修饰的类型就是在依赖关系的前面加上:.PHONY:文件名这么一句话,这样mytest指令就被为目标修饰了:
在这里插入图片描述
这样就算我们不停的输入make指令也不会告诉我们这已经是最新的文件:
在这里插入图片描述
那么这就是伪目标的功能希望大家可以理解。

如何实现伪目标

伪目标的实现依靠的是文件的时间,我们创建的文件一般都会有三个时间,使用stat指令就可以看到这个三个时间的数据:
在这里插入图片描述
这三个时间分别为:Access time,Modify time,Change time,当我们修改文件的内容时会将Modify time进行更新,当我们修改文件的属性时会更新Change time,比如说下面的操作:
在这里插入图片描述
我们更改了一下文件的内容可以看到这里的三个时间都发生了修改,之所以会这样是因为文件的内容也属于文件属性的一种,所以我们修改内容的同时也修改文件的属性,我们更改一下文件的权限这样就不会更改Modify time,比如说下面的操作:
在这里插入图片描述
我们就可以看到Modify time没有被修改但是Change time被修改了,那么这里就是两个时间所代表的含义,当然这里还有第三个时间Access time,在老的操作系统内核代码中只要你访问了文件就会修改Access time,但是我们在操作文件的时候多半还是访问文件少数是修改文件,因为访问文件不一定会修改但是修改文件一定会访问文件,而一访问文件就会修改Accsee time,虽然这是一个时间,但是这个时间也是文件属性的一种,当一个文件的属性被修改了 必定会影响其他文件,因为有些文件会以这个文件的属性作为数据,如果底层的数据被修改了那么这个文件的属性也会被修改,就好比蝴蝶效应,所以这个access time我们不要太了解。好这里我们知道了当修改一个文件内容的时候会修改这个文件的时间,而我们生成的文件的时间一定是比依赖文件的时间晚的,如果我们修改了被依赖文件的内容的话就会将该文件的时间更新一下,这时一旦更新那么被依赖文件的时间就会比生成的文件的时间要晚,而我们每次执行这个指令的时候他就会比较一下这两个文件的时间如果依赖文件的时间比生成文件的时间要早的话,他就会告诉你此时的文件已经是最新版本无需再次生成,如果没有生成文件,或者生成文件的时间要比被依赖文件的时间要晚的话,我们再使用相应的指令就会执行对应的依赖方法。而我们这里的伪目标就是告诉操作系统,在执行这个指令的时候不要看时间了尽管执行就完事了,所以这就是为什么伪目标修饰的指令可以不停被执行的原因,那么这里我们可以根据下面的操作来验证这一规律,既然他是根据文件的时间来判断是否要执行依赖方法,那么这里我们可以先make一下然后通过touch指令来更新一下源文件的时间,这样再使用make的时候就不会提醒我们当前的文件已经是最新版本了,比如说下面的图片:

在这里插入图片描述
结果跟我们预想的是一样的,那么这里希望大家能够理解这里的内容。

makefile推导规则

我们之前说过一个可执行程序的形成会经历多个步骤,比如说mytest可执行程序是依赖mycode.o文件形成的:
在这里插入图片描述

而mytest.o是依赖mycode.s文件形成的
在这里插入图片描述

而mytest.s文件是由mytest.i文件形成的
在这里插入图片描述

而mytest.i文件是由mytest.c文件形成的
在这里插入图片描述

我们在命令行中打印一下makefile中的内容,就可以看到此时的顺序是这样的:
在这里插入图片描述
但是我们输入以下make指令却发现此时的执行依赖方法的顺序是这样的:
在这里插入图片描述
那这是为什么呢?原因很简单,在当前的目录中只存在mycode.c这个文件按照我们之前的讲法make指令会执行第一个指令也就是mytest,但是这个指令依赖的文件是mycode.o,但是这个文件在该路径下不存在啊
在这里插入图片描述
所以我们的操作系统就会往下找,然后就发现这个mycode.o文件是依赖mycode.s文件形成的,但是mycode.s文件在当前目录下也没有啊,所以编译器就会继续在makefile文件里面查找,发现这个mycode.s文件是由mycode.i文件形成的,所以就会去找mycode.i文件的形成方法,继续往下找就发现mycode.i文件是由mycode.c文件形成,并且依赖关系和文件都有,所以编译器执行的第一个方法就是

gcc -E mycode.c -o mycode.i

这个依赖方法执行完之后就有了mycode.i文件,这个文件形成了就可以根据依赖方法形成mycode.s文件,再根据mycode.s文件形成mycode.o文件,然后再依赖mycode.o文件形成mytest可执行程序,所以这里执行的依赖方法就完全反则来了,希望大家能够理解。

  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

叶超凡

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

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

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

打赏作者

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

抵扣说明:

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

余额充值