【Linux】第九站:make和makefile

简介: 【Linux】第九站:make和makefile


一、 Linux项目自动化构建工具make/Makefile

1.make/makefile工作现象

make/makefile

make是一条指令

makefile是一个当前目录下的文件

他们的用法如下

这样的话,当我们直接输入make的时候自动执行下面的这条语句

如果我们还想要清理的话,我们可以这样做

这样的话,就可以将我们从繁杂的命令中解脱了

在上面的makefile文件中,我们也可以是Makefile文件。

test.exe依赖于test.c

而下面的方法就是依赖方法

下面的clean不依赖于任何方法

2.依赖关系与依赖方法

什么是依赖关系和依赖方法呢?

在下面的例子中

test依赖于test.c生成,下面的一行就是依赖的方法,即生成的具体细节

我们可以将其具体的写的详细一些

如下所示

当我们make的时候

它的执行逻辑是这样的,先去执行第一个依赖关系,但是第一个依赖关系所需要的被依赖的文件不存在,我们继续去找看这个被依赖的文件能否被生成,如此递归下去。最终我们在被依赖的位test.c的时候刚好找到了,停了下来,然后开始不断的往回执行

所以就有了上面的效果

而且即便我们将Makefile里面的内容给乱序了,它依然按照它的标准去找

不影响最终结果

这个过程其实就是makefile的自动化推导

如果我们缺少了其中的一个依赖关系,那么则会直接报错

3.如何清理

上面的依赖关系可以使得我们减少繁琐的命令。仅需一个make即可

那么清理其实也是比较麻烦的一件事情,我们能否去完成呢?当然是可以的

如下就完成了清理工作

这个clean是不需要依赖关系,只需要一个方法即可

4.为什么这里我们需要带上clean

现在的问题是为什么我们执行清理的时候要带上clean呢?而前面那个不需要呢?

其实这个make是默认执行第一个依赖的。而前面连续调用多个依赖是因为要像栈一样的链式调用

我们也可以自己手动去调用它这个第一个依赖

如果我们将clean改为了第一个依赖,那么就会默认执行clean

不过我们还是建议将生成可执行程序的那个依赖放在第一个

5.连续的make

我们现在将我们的makefile文件变为下面的样子

然后当我们连续的make的时候,会显示如下

  • 这是为什么呢?

这其实是因为我们的make编译完成之后,如果源代码没有被改变过,那么就不会再次编译了,因为根本没有必要

这里的目的就是为了提高编译效率

  • 那么这里是怎么做到呢?

这里一定是源文件形成可执行,先有源文件,才有可执行,一般而言,源文件的最近修改时间比可执行文件要老的

而如果我们更改了源文件,历史上曾经还有可执行,那么源文件的最近修改时间,一定要比可执行程序要新

所以只需要比较,可执行程序的最近修改时间和源文件的最近修改时间,如果.exe新于.c源文件,不需要重新编译;.exe老于.c源文件,需要重新编译

一般而言,.exe == .c的时间是不可能的。

那么如何证明前面所说的

在linux中有一条命令stat

它可以访问文件的一些时间

这里有三个时间

Access : 最近访问时间

比如cat,vim都会去访问。这些都会更改这个时间

Modify : 最近的对文件内容修改的时间

文件 = 文件内容 + 文件属性

change : 最近修改文件属性时间

这三个时间我们也称为ACM时间

这三个时间很有可能是同时修改的

比如当对文件内容修改后,由于访问了,所以Access时间也被修改了,而内容修改必然伴随着文件大小的改变,所以最终文件属性也要被修改,所以最终三个时间都被修改了

比如下面,我们进去修改了一下,就会导致全部时间被修改了

如下是我们修改文件的属性

上面的一切都符合我们的预期

不过在有的linux系统上,我们的access时间不会被修改。

这是因为Access时间更新太频繁了。需要写到磁盘上,由于频繁的访问外设会使得效率大大降低。所以现在的一些linux系统会根据modify和change的修改次数去进行修改,以此减少开销。可以理解为里面有一个计数器的存在,变相的提高效率

如果我们就想要修改,那么我们可以使用touch,touch后面如果跟的是一个不存在的文件名,就会创建一个文件,如果是已经存在的,会将该文件的所有时间全部更新

我们也可以定向的只改变一部分的

这里因为时间也是文件的属性,所以Access会改变后,change也会改变

所以现在我们知道了如何访问时间

不过问题还是之前的要比较两个的时间,他们其实比较的就是Modify时间,即文件内容的最近修改时间

将这些时间转化为时间戳,然后比较时间戳的大小即可

我们可以看到,明显.c文件要老于.exe文件,所以无法再次编译

如果我们使用touch命令强行修改test.c的时间,那么就可以再次编译了

所以上面的过程已经足以证明

make会根据源文件和目标文件的新旧,判定是否需要重新执行依赖关系进行编译

所以make命令并不总是执行编译的!

但是如果我们非要它每次都想要执行,不要管什么时间了,我们可以在makefile文件加上这句话

代表对于test这个依赖也不要管什么时间的问题了,每次都要执行

这个.PHNOY就是伪目标修饰

不过我们这个一般不建议放在编译时候,而是在清理的时候去修饰

修饰以后,这个clean就变成了伪目标,代表每次都执行

6.特殊符号

在makefile中有两个特殊符号

$@指的是冒号左边的那部分

$^指的是冒号右边的那部分

所以我们的这个编译可以改为上面的写法了

不过我们也会发现我们上面的使用make的操作会使得这些命令回显出来,如果我们不想要回显出来,我们可以加上@

二、Linux下实现一个简单的进度条

1.回车换行

回车和换行其实是两个概念

比如说在我们写作文的时候

当我们将一行写完了,如果我们是从第二行的开头写起,这其实叫做回车换行。

如果我们是直接这一行的正下方写起来,这就是换行

所以回车换行是两个动作

即将光标挪到下方是换行,将光标挪到开头这是回车

只不过我们c语言的\n一个就直接代表了回车换行,如果我们拆开用的话,就有他们各自的含义了,这也解释了为什么我们显示器在打印的时候,命名是换行但是确实在新一行的最开头了

即如果我们只想回车的话,那就是\r,如果是回车换行就是\n

2.缓冲区

我们先看如下代码

注意,sleep这个函数的头文件是unistd.h,这个可以在man手册中查找到

这是我们的运行结果,具体的现象是这样的,先打印出hello world,然后停顿两秒钟,然后再显示我们下面的命令行

如果我们将这个代码改为这样子

那么会先执行1还是2呢?

首先肯定是先执行1,因为这是c语言的特性,顺序执行

但是下面是我们的现象,这个现象是先停顿两秒钟,然后hello world和命令行同时出现

那么这是为什么呢?

在我们sleep期间,“hello world”在哪里呢?它一定是被保存起来了

这里其实保存在了缓冲区

这个缓冲区就是由C语言维护的一段内存

这里其实就是因为没有刷因缓冲区才导致的

在C语言中会默认打开标准输入、标准输出(显示器、stdout)、标准错误,三个流

如果我们想要刷新缓冲区,那么就可以刷新输出流即可

下面这个函数可以刷新

所以我们可以将代码改成这样

这样的话,hello world就会立刻出来,然后个等待两秒后,显示命令行

3.倒计时的实现

如果我们的代码是这样的,那么最终的效果是,一次性将987654321全部输出,这是因为,没有刷新缓冲区

但是我们显然不可以直接加上\n,因为这样虽然会刷新缓冲区,但是也换行了。没有倒计时是会换行的

如果我们的代码是这样的

那么最终的效果是这样的,我们也知道这样也是不行的,因为倒计时应该是覆盖原来的位置的

所以我们应该将倒计时写成这样的

这个\r代表回车,即将光标移动到当前行的最开头。而我们加上%-2d的原因是因为10是一个两位数。如果不这样做就会出现10,90,80,70…这种数据,因为它只会覆盖一个数据,我们显示屏打印的只是一个字符一个字符的打印的。10是俩个字符,我们后面只能覆盖一个字符

所以最终,达到了我们的预期了

不过这段代码在windows上和我们linux上是有一些差别的

即那个数据没有被刷新出来,而是先等待两秒钟这个现象在windows上是做不出来的,只有在linux系统中才可以做出来

4. 进度条

我们先创建以下的文件

如下是每个文件的内容

我们将每个文件设置成这样的话,可以实现一个最简单的进度条

我们唯一需要注意的是在processBar.c文件中,因为我们要在当前行进行刷新,而我们是不可以使用换行的,只能用回车,所以我们得手动刷新,因为没有\n,就不会立即刷新,因为显示器模式是行刷新的

最终我们运行一下

不过我们会发现这个进度条有点慢,所以我们可以将他的时间给变快一些,所以我们可以使用这个函数

usleep,它是以微妙为单位的,即10^-6次方秒

所以我们可以让他设置为如下,就可以在10秒内跑完了

最终运行结果如下

不过上面的这个进度条显得有点挫,我们可以稍微改进一下

这样的话最终效果如下所示

不过我们还可以加上一个旋转的光标

这样就可以一直旋转了

我们还可以将我们的进度条改为带有箭头的形式

然后最后我们将我们的代码给整理一下

最终代码如下

运行结果如下

5.进度条改进

不过上面的其实不够模拟真实的场景,在真实的场景中应该是进度条程序只负责接收一个当前的比例,然后打印出界面即可。

而在外部有一个总任务量和当前完成任务量,随着下载任务的不断完成,当前完成的不断增加来形成的

如下是经过我们改良后程序

最终效果如下所示

最后为了更加使得我们的下载更真实,我们可以使用一个回调函数来解决

不过上面的程序还存在一定的问题

当我们进行多个下载任务的时候

如下所示

于是我们可以在添加一个函数

在main的这里记得结束之后清空一下数组即可

6.加上颜色

我们这个进度条看起来已经很不错了,但是还可以在优化,加上一些颜色

这样的话运行效果如下所示

相关文章
|
2月前
|
Linux C++
Linux c/c++之makefile的基础使用
Linux下C/C++项目中makefile的基本使用,包括基础、进阶和高级用法,以及如何创建和使用makefile来自动化编译过程。
18 0
Linux c/c++之makefile的基础使用
|
2月前
|
Linux 开发工具
【Linux快速入门(二)】Linux与ROS学习之编译基础(make编译)
【Linux快速入门(二)】Linux与ROS学习之编译基础(make编译)
|
7月前
|
小程序 Linux 开发工具
【Linux】Linux 开发工具(vim、gcc/g++、make/Makefile)+【小程序:进度条】-- 详解
【Linux】Linux 开发工具(vim、gcc/g++、make/Makefile)+【小程序:进度条】-- 详解
|
6月前
|
Linux 开发工具 数据安全/隐私保护
Linux基础——Linux开发工具(下)_make/makefile
Linux基础——Linux开发工具(下)_make/makefile
48 1
|
5月前
|
Java Linux C++
【Linux】Make和Makefile快速入门
【Linux】Make和Makefile快速入门
32 0
|
5月前
|
Java Linux 编译器
编写和使用Linux Makefile
编写和使用Linux Makefile
|
6月前
|
Linux
【make/Makefile】Linux下进度条的设计与实现
【make/Makefile】Linux下进度条的设计与实现
|
6月前
|
NoSQL 编译器 Linux
【Linux】--- Linux编译器-gcc/g++、调试器-gdb、项目自动化构建工具-make/Makefile 使用
【Linux】--- Linux编译器-gcc/g++、调试器-gdb、项目自动化构建工具-make/Makefile 使用
91 0
|
6月前
|
Linux Shell C++
linux C/C++ makefile
linux C/C++ makefile
|
Linux
LINUX进阶(其他篇)之make编译报错原因
LINUX进阶(其他篇)之make编译报错原因
366 0