【Linux】项目自动化构建工具-make和Makefile 的使用和进度条的实现

news2024/11/27 4:02:03

文章目录

  • 一、什么是make/makefile
  • 二、如何编写makefile
  • 三、make 的工作原理
    • 1.make的使用
    • 2.make的依赖性
    • 3.项目清理
    • 4..PHONY伪目标
  • 四、Linux第一个小程序 -- 进度条
    • 1.\r&&\n
    • 2.行缓冲区概念
    • 3.进度条
      • process.h
      • process
      • test.c
      • makefile

一、什么是make/makefile

什么是makefile

在我们以后的工作环境中,一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作

Linux中提供了自动化构建工具–makefile来帮我们解决这个问题,makefifile带来的好处就是——“自动化编译”,makefile一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率

此外,会不会写makefifile,从一个侧面说明了一个人是否具备完成大型工程的能力

什么是make

make是一个命令工具,是一个解释makefifile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefifile都成为了一种在工程方面的编译方法

总结:make是一条命令,makefifile是一个文件,两个搭配使用,完成项目自动化构建

二、如何编写makefile

makefile的编写重要的是编写依赖关系和依赖方法,依赖关系是指一个文件依赖于另一个文件,即我们想到得到一个文件,在目录下我们必须先有的另外一个文件,依赖方法是指如何依赖文件来得到对应的目标文件

我们在编写makefile的时候需要注意一下几点:

1.makefile文件名必须是makefile/Makefile,不能是其他的文件名,否则make无法识别

2.依赖文件可以有多个,也可以没有

3.依赖方法必须以Tab键开头,不能是四个空格

我们以下面的例子来说明如何编写makefile文件

#include <stdio.h>
int main()
{
	printf("hello makefile\n");
    return 0;
}

makefile文件:

test.out:test.c  // 依赖关系
    gcc test.c -o test.out  // 依赖方法
.PHONY:clean //伪目标
clean:
	rm -f test.out

如上,在makefile文件中,test.out 依赖于test.c ,我们以冒号作为分隔符,依赖方法是gcc 编译执行,clear不依赖于任何文件,依赖方法为rm -f指令,其中,.PHONY修饰的clear表示其是一个伪目标,表示它总是被执行

三、make 的工作原理

1.make的使用

在Linux在,我们输入make命令之后,make会在当前目录下找名字叫“Makefifile”或“makefifile”的文件,如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“test.c”这个文件,并把这个文件作为最终的目标文件,如果找不到,就会打印提示信息

在我们上面的例子中,makefile中一共有两个目标文件,test.out和clear,如下,我们输入make它会默认执行第一个目标文件,此外,我们也可以指定多个目标文件来让它执行多个目标文件

1.没有编写makefile文件:

在这里插入图片描述

2.使用一个指令:

在这里插入图片描述

3.同时使用多个指令

在这里插入图片描述

2.make的依赖性

我们将上面例子的makefile修改成如下内容:

test.out:test.o
    gcc test.o -o test.out
test.o:test.s
    gcc -c test.s -o test.o
test.s:test.i
    gcc -S test.i -o test.s
test.i:test.c
    gcc -E test.c -o test.i
.PHONT:clean
clean:
	rm -f test.i test.o test.s test.out            

在这里插入图片描述

我们在输入make指令之后,make会在当前目录找名字叫“Makefifile”或“makefifile”的文件,如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“test.out”这个文件,并把这个文件作为最终的目标文件,如果test.out所依赖的test.o文件不存在,那么make会在当前文件中找目标为test.o文件的依赖性,如果

找到则再根据那一个规则生成test.o文件(l类似于栈–后进先出)

如果test.o依赖的文件也不存在,则继续执行该规则,知道找到存在依赖文件的目标文件,得到目标文件之后层次返回形成路径上的其他目标文件,或者最后被依赖的文件找不到,直接退出并报错

这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件

在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理

对于我们上面的例子,test.out依赖的文件test.o不存在,make会去寻找以test.o为目标文件的依赖关系,test.o依赖的test.s也不存在,make会去找以test.s为目标文件的依赖关系,然后test.s依赖test.i,test.i依赖test.c,test.c文件存在,此时,make就会根据test.i的依赖方法形成test.i,层层向上返回,依次形成test.s,test.o,test.out

3.项目清理

工程是需要被清理的,在makefile中,我们使用clean作为项目清理的目标文件,由于项目清理不需要依赖于其他文件,所以clean 也不需要依赖关系

像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令——“make clean”,以此来清除所有的目标文件,以便重编译

在这里插入图片描述

但是一般我们这种clean的目标文件,我们将它设置为伪目标,用 .PHONY 修饰,伪目标的特性是,总是被执行的

4…PHONY伪目标

当我们对同一个源文件多次make的时候,我们就会发现第一次程序正常编译,但是第二次之后它就不会再进行编译了,而是提示“make :‘test.out’ is up to date.":

在这里插入图片描述

但是如果我们将源文件进行修改再进行make的时候,第一次正常进行编译,第二次之后情况还是和上述情况一样

在这里插入图片描述

实际上,Linux是为了防止我们对已经编译好的并且没有经过修改的文件进行重复编译,从而造成时间的浪费,有人可能会说,我编译一个程序最多几秒中,浪费这一点时间问题不大,但是我们在工作的时候,编译一个程序可能需要几十分钟甚至更久,这时对已经编译好的文件不再进行编译,就会减少很多的时间。对于上面我们的代码,如果test.c已经编译得到了test.out,并且我们对test.c文件没有进行修改,那么我们再次make的时候make就不再执行,即不进行重复编译。

那么make是如何判断源程序不需要重新编译呢?也就是如何判断源文件没有被修改呢?

make会通过比较源文件和目标文件的时间戳来判断是否需要重新编译。如果源文件的时间戳比目标文件的时间戳早,说明源文件没有被修改过,此时make就不需要重新编译。否则,make会重新编译源文件生成新的目标文件

具体来说,make会根据每个源文件生成一个对应的目标文件,然后比较它们的时间戳。如果目标文件不存在,或者其时间戳比源文件早,说明源文件已经被修改过,此时make就需要重新编译。否则,make就认为源文件没有被修改过,不需要重新编译

需要注意的是,如果源文件的修改只是微小的变化,例如修改了注释或空格等,此时make可能无法正确判断是否需要重新编译。为了避免这种情况,可以使用一些工具来生成文件的摘要或哈希值,并将其与目标文件的哈希值进行比较,这样可以更加准确地判断是否需要重新编译

在Linux中,文件一共有三种时间:

1.访问时间(Access):当我们查看文件内容后该时间改变,比如cat,vim,less等等

2.修改时间(Modify):当我们修改文件的内容后该时间改变

3.改变时间(Change):当我们修改文件的属性或者权限的时候该时间改变,比如vim/nano(文件大小改变),chmod/chown/chgrp(文件的权限改变)

但是实际上,我们访问文件的内容不一定会改变文件的访问时间,其原因如下:

1.在Linux下,访问文件内容的操作十分频繁,而修改文件的访问时间需要对文件进行IO操作,如果我们每次访问都修改文件的访问时间,会增大系统的负担

2.一个文件是否能被读取是由文件的权限决定的,而既然文件是可读的,那么说明文件的拥有者/所属组是并不在意我们对文件进行读取,所以也没有必要每次都修改文件的访问时间

3.有些文件系统可能会使用一些优化策略,例如延迟写入(delayed write)或写时复制(copy-on-write),这些策略可能会导致文件的访问时间不及时更新。此外,在某些情况下,文件的访问时间可能会被关闭或禁用

4.为了提高系统性能,Linux内核使用了一些优化策略来减少对文件访问时间戳的更新。例如,内核会将文件的访问时间戳的更新延迟到一定时间后再更新,或者只在文件被关闭时才更新访问时间戳。这样可以减少对文件系统的读写操作,提高系统性能

5.在某些情况下,文件系统可能会禁用访问时间戳的更新,例如使用noatime或nodiratime选项挂载文件系统时,访问时间戳不会被更新。这些选项可以提高文件系统的性能,但会牺牲文件访问时间戳的准确性

访问文件内容不一定会改变文件的访问时间戳,这取决于文件系统的优化策略和挂载选项。但是,在大多数情况下,访问文件内容会导致访问时间戳的更新。

综上所述,Linux下并不会每次访问文件的内容都会更新文件的访问时间,而是积累到一定的访问次数或者积累一段时间才更新

在这里插入图片描述

而make则是根据可执行程序的修改时间(Modify time)与源文件的修改时间进行对比来判断是否需要重新进行编译

在这里插入图片描述

在这里插入图片描述

make判断源文件是否需要重新编译只与源文件的修改时间有关,与源文件的内容改动无关,我们可以通过touch命令来进行验证,(touch file:如果file已经存在,则更新file的所有时间)

在这里插入图片描述

我们在了解make是如何判断是否需要重新执行依赖方法形成目标文件之后,.PHONY的原理和作用就显而易见了:被.PHONY修饰的目标文件不根据文件的修改时间来判断是否需要重新执行,从而达到总是被执行的效果

在这里插入图片描述

我们也同样可以使用.PHONY来修饰test.out,使得test.out每次都被重新编译

在这里插入图片描述

四、Linux第一个小程序 – 进度条

1.\r&&\n

对于\n相信大家一定会熟悉,因为在C语言中我们经常使用/n来进行换行,但是实际上我们C语言的‘\n’是’\r’+’\n’

‘\r’:回车,即将光标移动到当前行首

‘\n’:换行,即将光标移动到下一行

所以我们C语言的’\n’的作用是回车+换行,而不仅仅是换行

2.行缓冲区概念

我们知道,我们从键盘输入的字符以及向显示器输出的内容,并不是直接的读入或者输出,而是会被存放到输入缓冲区和输出缓冲区中,待缓冲区刷新时数据才会被读入或输出

而行缓冲是缓冲区的一种,在行缓冲下,当输入和输出中遇到换行符时,才执行真正的I/O操作,即我们输入的字符会先存放到缓冲区,等按下回车键时才进行真正的I/O操作。我们可以使用两份不同的代码来验证上诉结论:

在这里插入图片描述

在这里插入图片描述

我们可以看到,test1.c的数据printf后并没有直接打印显示到终端上,而是等待程序结束后缓冲区刷新后才显示,而test2.c中的数据由于’\n’可以刷新行缓冲,所以直接打印到了终端

3.进度条

此时,我们就可以运用回车换行的特性以及我们所学了C语言知识来编写一个进度条小程序了

process.h

#pragma once
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#define NUM 101
//#define STYLE '#'
#define S_NUM 5                                
// 函数声明              
extern void ProcessOn();

process

#include "process.h"
 // 进度条字符数组
    const char style[S_NUM]={'#','$','*','>','-'};
// 函数的定义 
void ProcessOn()
{
    int cnt=0;
    // 定义字符数组,用于存储进度条显示的字符
    char bar[NUM];
    memset(bar,'\0',sizeof(bar));
    // 旋转光标
    const char* lable = "|\\-/";
    // 循环101次
    while(cnt<=100)
    {
        printf("[%-100s][%d%%][%c]\r",bar,cnt,lable[cnt%4];)
        // printf("\033[42;34m[%-100s][%d%%][%c]\033[0m\r",bar,cnt,lable[cnt%4]);
        // 缓冲区刷新
         fflush(stdout);
        // bar[cnt++] = STYLE;                                                                                                                                              
        bar[cnt++] = style[N];         
        usleep(50000);                                                             
    }                                                                                     
   printf("\n");                                     
 }

test.c

#include "process.h"
int main()
{
    ProcessOn();                                                                             return 0;
}

makefile

ProcessOn:process.c test.c
    gcc process.c test.c -o ProcessOn -DN=0                                          .PHONT:clean
clean:
    rm -f ProcessOn

在process.c中,我们每次打印数据之后使用’\r’让光标回到行首,然后刷新缓冲区,再增加bar数组里面的标识字符,这样就可以使我们下一次打印数据时可以直接覆盖之前的数据,并且会增加一个字符的内容,就有就可以实现进度条的效果

同时,为了使进度条更加的真实,我们还增加了一个显示进度的百分比和一个旋转光标,每次打印打印完成之后休眠0.05秒,使得进度条在5秒中之后打印完毕,这样就使得进度条就有了一个加载进度以及一个旋转的符号

usleep函数

usleep函数的一个休眠函数,传入参数的单位是纳秒,而sleep传入参数的单位是秒

在这里插入图片描述

最后我们了为了丰富进度条字符的样式,我们把进度条字符设置为一个字符数组,我们可以根据我们想要的样式来进行选择,选择时调整N的值即可

此外,我们还可以给输出字符加上颜色。控制颜色的基本格式为:

printf("\033[字背景颜色;字体颜色m字符串\033[0m");
// 比如下面的例子
printf("\033[47;31mhello world\033[5m");

这里有一篇相关的文章,大家有兴趣的可以看一看C语言控制台下printf设置文字颜色和背景色以及实现简单的文字选择菜单

注意:在printf函数中,%具有特殊的意义,所以我们需要输入%%对其进行转义,同样,在lable数组中,字符’\‘也是特殊字符,我们需要输入’\\’

最终我们实现的进度条演示如下:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/590694.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【c语言习题】使用链表解决约瑟夫问题

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c语言系列专栏&#xff1a;c语言之路重点知识整合 &#x…

在 Shell 脚本中使用 `exec` 命令的方法和用途

在 Shell 脚本中&#xff0c;exec 是一个非常有用的命令&#xff0c;用于执行命令或替换当前进程。它允许您在脚本中启动新的进程&#xff0c;并将控制权完全转移到新进程。本文将详细介绍在 Shell 脚本中使用 exec 命令的方法和用途。 什么是 Exec 命令&#xff1f; exec 是一…

python基础----02-----字面量、变量、数据类型及其转换、标识符以及字符串、数据输入(input语句)

一 字面量 1 字面量 字面量&#xff1a;在代码中&#xff0c;被写下来的的固定的值称之为字面量。类似C/C的字符串常量。 1.1 常见的python值类型 Python中常见的值类型&#xff1a; 实际上在C/C字面量和这里的类型还是有区别的&#xff0c;体现在内存存储中&#xff0c;字…

JavaScript正则表达式

1.介绍 2.语法 3.元字符 4.修饰符 目标&#xff1a;学习正则表达式概念及语法&#xff0c;编写简单的正则表达式实现字符的查找或检测。 一、介绍 1.什么是正则表达式 正则表达式&#xff08;Regular Expression&#xff09;是用于匹配字符串中字符组合的模式。&#xff08;…

设计模式之~原型模式

定义&#xff1a;用原型实例指导创建对象的种类&#xff0c;并且通过拷贝这些原型创建新的对象。原型模式其实就是从一个对象再创建另外一个可定制的对象&#xff0c;而且不需知道任何创建的细节。 优点&#xff1a; 一般在初始化的信息不发生变化的情况下&#xff0c;克隆是最…

触摸屏驱动的问题,在C站搜文章一下午解决不了,最后ChatGpt解决了

目录 一、遇到问题二、在C站搜索文章去解决问题的收获三、用 ChatGpt 去解决的收获四、总结 一、遇到问题 现实问题&#xff1a; 有一个基于Linux4.19内核开发了&#xff0c;在海思SS528芯片运行的系统&#xff0c;用鼠标可以正常使用。 现在要求使用一块公司开发的 多点触控屏…

ES+Redis+MySQL,这个高可用架构设计

一、背景 会员系统是一种基础系统&#xff0c;跟公司所有业务线的下单主流程密切相关。如果会员系统出故障&#xff0c;会导致用户无法下单&#xff0c;影响范围是全公司所有业务线。所以&#xff0c;会员系统必须保证高性能、高可用&#xff0c;提供稳定、高效的基础服务。 …

window10安装vim编辑器

我们在做git操作的时候&#xff0c;很多文字编辑工作会默认打开 Vim 编辑器来进行操作。 Vim 是一个高度可配置的文本编辑器&#xff0c;旨在让创建和更改任何类型的文本变得非常高效。大多数 UNIX 系统和 Apple OS X 都将它作为vi包含在内&#xff0c;用惯了Linux中的Vim编辑器…

机器学习实战7-基于机器学习算法预测相亲成功率

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下机器学习实战7-基于机器学习算法预测相亲成功率&#xff0c;随着社会的发展&#xff0c;大家都忙于事业&#xff0c;对自己的终身大事就耽搁了&#xff0c;相亲是一种传统的寻找伴侣的方式&#xff0c;随着时代的发…

谷歌训了28个15亿参数模型,说明数据对大模型训练的影响

夕小瑶科技说 原创 作者 | Python 随着ChatGPT的爆火&#xff0c;大规模语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;如日中天。然而&#xff0c;由于相关数据细节并不公开&#xff0c;相关研究大多都是盲人摸象式探索&#xff0c;缺乏系统的经验指导…

关于打卡小程序可能会遇到的部分问题【华为云Astro低代码体验季】

关于打卡小程序可能会遇到的部分问题【华为云Astro低代码体验季】 前提一、可能遇到的问题二、 改进 前提 已经注册华为云账号且浏览过 &#xff1a;华为云Astro制作打卡小程序&#xff0c;在此基础上可能会遇到的问题 一、可能遇到的问题 当创建完成应用后&#xff0c;如果…

设计模式之~外观模式

定义&#xff1a; 为子系统中的一组接口提供一个一致的界面&#xff0c;此模式定义了一个高层接口&#xff0c;这个接口使得这一子系统更加容易使用。 结构图&#xff1a; 区分中介模式&#xff1a; 门面模式对外提供一个接口 中介模式对内提供一个接口 优点&#xff1a; 松耦…

【算法题解】32. 验证二叉搜索树的递归解法

这是一道 中等难度 的题 https://leetcode.cn/problems/validate-binary-search-tree/ 题目 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含…

【瑞萨RA_FSP】DMAC/DTC编程实战

文章目录 一、DMAC存储器到存储器传输二、DTC外部中断触发传输 一、DMAC存储器到存储器传输 1. FSP配置 打开该工程的 FSP 配置界面。然后按如图步骤加入 DMAC。 加入 DMAC 后如下图所示。 单击上图中新添加的 r_dmac 框&#xff0c;然后在左下角的“属性”窗口配置 DMAC 模…

一分钟学一个 Linux 命令 - cd

前言 大家好&#xff0c;我是 god23bin。欢迎来到这个系列&#xff0c;每天只需一分钟&#xff0c;记住一个 Linux 命令不成问题。今天让我们从 cd 命令开始&#xff0c;掌握在 Linux 系统中切换目录的技巧。 什么是 cd 命令&#xff1f; cd 命令来自这么一个词语&#xff0…

liftOver 不同版本基因组文件相互转化

大家好&#xff0c;我是邓飞。前一段时间有小伙伴在星球提问&#xff1a;想将不同版本的SNP数据合并&#xff0c;不想重新call snp&#xff0c;想把绵羊的V2和V4版本的数据合并&#xff0c;具体来说&#xff0c;是V2转为V4然后与V4合并。 我建议用liftOver软件进行处理&#xf…

SAP-MM-采购折扣知识与会计处理

采购折扣&#xff0c;按折扣的方式和性质可以分为商业折扣和现金折扣两种方式。 商业折扣是指购货方批量进货时&#xff0c;要求销货方按零售价格打一个折扣而少支付的货款金额。企业之间的商品购销活动中&#xff0c;商业折扣是一种较常见的交易现象。 现金折扣则是指在赊购条…

数列DP进阶

目录 一&#xff0c;斜率优化 1&#xff0c;斜率优化原理 2&#xff0c;凸包和斜率计算 3&#xff0c;实战 黑暗爆炸 - 4709 柠檬 二&#xff0c;else 力扣 644. 子数组最大平均数 II&#xff08;最大子段和二分&#xff09; ​力扣 646. 最长数对链 力扣 1235. 规划兼…

浅科普一下计算机发展史阶段及那些不为人知的重要里程碑

目录 〇、前言 一、计算机发展历史阶段 二、计算机发展史中重要的里程碑 三、计算机对人类社会发展的重要性 四、计算机的应用领域 五、常见计算机辅助技术 六、总结 〇、前言 计算机的诞生无疑对人类社会的发展起着至关重要的巨大作用。计算机发明者名叫约翰冯诺依曼&a…

SAP-MM-发票-采购运费

采购运费是采购业务中一种特殊的定价&#xff0c;在SAP系统中&#xff0c;交货成本和其相近&#xff0c;是指在货物交付过程中发生的运输成本&#xff0c;只要有货物交付&#xff0c;就会有运费&#xff0c;而运费或者由采购方承担&#xff0c;或者由销售方承担&#xff0c;国内…