Linux下的Makefile与进度条程序

目录

Linux下的Makefile与进度条程序

Makefile与make

Makefile与make介绍

创建第一个Makefile并使用make

Makefile文件基本格式介绍

Makefile依赖方法执行过程

Makefile通用写法

进度条程序

实现效果

前置知识

回车(\r)与换行(\n)

输出缓冲区

实现进度条


Linux下的Makefile与进度条程序

Makefilemake

Makefilemake介绍

在Linux中,Makefile是一个文件,make是一个指令,当使用make指令时,该指令会在当前目录下找Makefile文件从而执行内部的内容

创建第一个Makefile并使用make

首先,在当前目录下创建一个Makefile文件(也可以写成makefile),例如:

接下来在同级目录下创建一个code.c文件

使用vim编辑器输入下面的内容:

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

保存code.c文件后退出当前vim,使用vim打开Makefile文件,输入下面的内容:

code:code.cgcc -o code code.c
.PHONY:clean
clean:rm -rf code
需要注意, gcc -o code code.crm -rf code前方是一个Tab键的大小,而不是4个或者8个空格

保存Makefile文件后退出当前vim,在当前目录下输入make指令即可在当前目录下创建code.c对应的可执行文件(具有可执行权限并且文件本身可执行)code,例如下图:

通过常规方式运行该可执行文件./code即可看到打印输出的内容:

接着使用make clean指令清理刚才生成的可执行文件code

Makefile文件基本格式介绍

以前面例子中的Makefile为例:

code:code.cgcc -o code code.c
.PHONY:clean
clean:rm -rf code
  • 第一行中的code:code.c代表依赖关系,code表示目标文件,code.c表示依赖文件列表中的文件,第二行的gcc -o code code.c代表依赖方法(指令)
  • 第三行中的.PHONY表示生成一个伪目标,clean表示伪目标的名字(可以类比变量名)
  • 第四行及第五行与第一行及第二行含义一致,表示依赖关系和依赖方法,而因为clean没有需要依赖的文件,所以clean:后没有任何依赖文件列表文件

依赖关系:表示两个文件之间构成的一定关系,比如父子关系

依赖方法:通过依赖方法可以执行的对应的指令

依赖文件列表:code.c所处的位置即为依赖文件列表,为了生成目标文件code而需要的文件称为依赖文件,依赖文件列表可以含有不止一个文件

注意:理论上来说,依赖文件列表中的 code.c在当前情况下可以不写,但是如果不写,在第一次执行 make指令后,不论之后 code.c是否修改,再执行 make指令都无法执行对应的依赖方法,因为 code文件已经存在,所以为了保证可以修改,需要加上 code.c

从上面的运行结果可以看出,每一次执行make时都会在控制台回显出对应的依赖方法,如果将编译指令改为echo "测试",则效果如下:

可以看到先回显了对应的依赖方法,再执行依赖方法,如果不希望出现这种情况,可以在执行的指令前加上@使指令不再回显,所以上面的Makefile可以修改为:

code:code.c@echo "测试"

运行结果如下:

所以原始的Makefile可以修改为:

code:code.c@echo "Start Compiling..."@gcc -o code code.c@echo "End Compiling..."
.PHONY:clean
clean:@echo "Cleaning code..."@rm -rf code@echo "End Cleaning..."
一个依赖集中可以有多个依赖方法

此时正常运行结果如下:

如果代码出现错误,则gcc会中断编译,所以此时运行结果如下:


使用.PHONY可以生成一个指定名字的伪目标,伪目标的作用是:清除依赖方法执行时进行的文件时间对比,下面是具体介绍:

首先,在Linux中可以使用stat+文件名查看文件当前的属性,对于code.c有:

执行结果中,主要关注三个部分:AccessModifyChange,这三个部分分别表示文件最近一次的访问时间、文件内容被修改的时间和文件属性被修改的时间

  • Access时间:一般不是特别精确,因为如果一个文件访问一次就需要更新一次访问时间,那么对于多个文件来说,这种操作的消耗对于CPU来说是很大的
  • Modify时间:Modify时间只表示文件内容被修改的时间,如果文件属性时间修改,则不影响Modify时间,但是需要注意,Modify时间一旦改变一般伴随着Change时间改变,因为修改文件内容有时会影响到文件的相关属性(例如文件大小等)
  • Change时间:Change时间只表示文件属性被修改的时间,修改文件属性时间不会影响Modify时间

接着,观察对于没有添加伪目标的Makefile第一部分依赖集,如果code文件已经存在,再一次进行make的效果:

code:code.c@echo "Start Compiling..."@gcc -o code code.c@echo "End Compiling..."

如果此时对code.c文件进行修改,那么执行结果会有所不同:

那么指令是如何知道文件是否被修改呢?就是通过前面提到的Modify时间和Change时间,过程如下图所示:

因为code.c创建的时间早于code.c编译的时间,所以开始时不存在code文件,所以第一次执行make指令时正常执行。

code.c文件未修改时,第二次执行make指令会发现code.cModify时间和Change时间依旧在make之前,因为第一次已经满足了code.c的两个时间在code文件的两个时间之前,所以gcc就不会再进行一次编译。

当修改code.c文件后,code.cModify时间和Change时间改变,导致code.c的两个时间在code文件的两个时间之后,此时gcc就可以正常执行,从而make指令不受影响

而如果再Makefile中为这一部分添加一个伪目标,则可以清除指令中文件时间的对比过程:

.PHONY:code
code:code.c@echo "Start Compiling..."@gcc -o code code.c@echo "End Compiling..."

此时无论执行多少次make指令,都不会出现make指令中gcc因为文件时间对比而导致执行结果不同:

make指令虽然结果完全相同,但是不代表依赖方法没有执行,即文件确实每一次都重新编译

执行完编译部分的make指令,想要执行删除code文件对应的make指令需要在make后加上clean,这个clean代表伪目标名,之所以前面直接使用make就可以执行编译指令,是因为make指令在读取Makefile文件时是从上至下顺序查找,而直接使用make,就会执行第一个依赖集对应的依赖方法,执行完毕后就不会再继续往下读;而对于删除code文件的指令来说,其所在位置时Makefile中的第二个依赖集,所以需要告诉make指令找哪一部分

所以,此处可以看出.PHONY的第二个作用就是声明一个伪目标,通过该伪目标帮助make指令快速定位需要执行的依赖集

如果细心可以发现,对于clean依赖集来说,不论是否有.PHONY都可以无限制执行rm -rf依赖方法,所以可以推断出rm -rf指令本身不会考虑文件的时间属性,但是为什么此处还需要加.PHONY?一方面是为了声明伪目标,另一方面是为了当前依赖集中的其他指令会有时间对比

Makefile依赖方法执行过程

前面学习到,当执行gcc -o code code.c实际上是分成了四步,即:

  1. code.c文件编译生成code.i文件
  2. code.i文件编译生成code.s文件
  3. code.s文件编译生成code.o文件
  4. code.o文件编译生成code可执行文件

将对应的指令写入Makefile中,代码如下:

code:code.ogcc -o code code.o
code.o:code.sgcc -c code.s -o code.o
code.s:code.igcc -S code.i -o code.s
code.i:code.cgcc -E code.c -o code.i.PHONY:clean
clean:@rm -rf code

根据make从上至下的运行顺序,首先执行gcc -o code code.o,但是,因为code.o不存在,并且code.o文件依赖于code.s文件,所以继续执行code.o:code.s对应的依赖方法,以此类推直到最后一条依赖方法gcc -E code.c -o code.i执行向上返回执行前面未执行的依赖方法。整个过程可以理解为在一个栈中操作:

假设此处执行的依赖方法同样进栈

所以执行的结果如下图所示:

实际上,在真正开发中,只需要用到两个部分,如下:

code:code.ogcc -o code code.o
code.o:code.cgcc -c code.c -o code.o

此时运行结果如下:

Makefile通用写法

在前面的Makefile中,每一个依赖方法都需要在前面的依赖关系部分的文件重新写一遍,为了简化过程,可以使用下面的写法:

TARGET=code
SRC=code.o$(TARGET):$(SRC)$(CC) -o $@ $<
%.o:%.c$(CC) -c $< -o $@.PHONY:clean
clean:@rm -rf $(TARGET) $(SRC)

上面的代码中,首先创建了两个变量分别代表生成的目标文件code以及第一个依赖集中的依赖文件列表中的文件,在依赖方法中使用了两个自动变量(一般建议大写),分别是$@$<

Makefile中,$@表示生成的目标文件,$<表示从依赖文件列表中取出一个文件,对应的还有$^表示依赖文件列表中的所有文件

而对于gcc来说,在Makefile中可以使用内置变量CC(表示C编译器的名字)代替

如果涉及到多个文件编译,则在SRC%.c处使用空格分隔每一个文件


至此,一个基本的Makefile文件编写语法就这么多,如果需要更详细了解Makefile文件,请移步至此->(待更新)

进度条程序

实现效果

前置知识

回车(\r)与换行(\n)

在C语言或者其他高级语言中,换行(\n)表示回到下一行的开始处,实际上换行的效果并不如此

回车(\r):回到当前光标所在行的开始

换行(\n):前往光标所在行的下一行,但是光标是平行向下移动

输出缓冲区

观察下面程序运行的结果:

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

如果在Linux终端运行该程序,可以看到程序先打印了hello linux,然后等待了2秒才显示prompt提示

这里的 sleep函数不是Windows下的 Sleep函数,但是效果基本一致

将上面的程序修改为下面的程序,再观察效果:

#include <stdio.h>
#include <unistd.h>int main()
{printf("hello linux");sleep(2);return 0;
}

可以看到程序先等待了2秒,然后才打印hello linux

C语言程序默认从上往下顺序执行代码,所以不可能是先执行了sleep(2)才执行printf("hello linux");,出现这种现象的原因就是因为缓冲区的存在,程序在输出时并不会直接将内容输出到显示器上,而是先输出到输出缓冲区,再通过刷新/结束缓冲区将内容打印到屏幕上,而之所以在有\n时会显示再等待就是因为\n刷新了缓冲区,导致内容打印到了屏幕上

如果使用将\n替换为\r则同样会先等待再打印,因为\r也不具备刷新缓冲区的效果,如果在当前情况下想刷新缓冲区但又不想使用\n,则可以使用fflush()函数,传递参数为标准输出stdout,代码如下:

#include <stdio.h>
#include <unistd.h>int main()
{printf("hello linux");fflush(stdout);sleep(2);return 0;
}

实现进度条

思路:首先创建3个文件,分别是测试文件main.c、头文件process.h和实现文件process.c。对于进度条,实际上就是先打印原数组内容,再填充数组然后刷新缓冲区,对于百分比,只需要使用循环变量控制即可,对于最右侧闪烁的符号,实际上就是四个动画帧符号,每一次循环加载一个动画帧即可,但是为了循环加载,需要使下标在指定范围内循环,可以考虑循环队列(数组版)下标轮回的思路,所以基本代码如下:

// 实现文件
#include "process.h"void process()
{char bar[NUM] = {0};// 进度条数组int count = 0;const char *label = "|/-\\";//控制进度条最右侧的闪烁符号(| 顺时针旋转)size_t len = strlen(label);while(count <= 100) {// [%-100s]预留100个字符的位置,每一次打印数组bar中的字符,因为初始化为0,所以数组中全是'\0',当遇到第一个'\0'就停止,加上-表示从右往左(默认从左往右)打印// 使用count控制进度条百分比,百分号%需要额外转义// label闪烁符号,使用count%4使下标循环在0-3,思路可以联想循环队列数组版控制下标轮回的方式printf("[%-100s][%d%%][%c]\r", bar, count, label[count%len]);fflush(stdout); // 先刷新缓冲区,打印出停留在缓冲区的内容bar[count++] = STYLE; // 向数组中添加字符usleep(20000);// 睡眠时间单位为微秒,1秒 = 1000毫秒=1000000微秒}printf("\n");
}// 头文件
#include <stdio.h>
#include <unistd.h>
#include <string.h>#define NUM 101 // 定义进度条字符的个数
#define STYLE '#' // 定义进度条的样式void process();// 测试文件
#include "process.h"int main()
{process();return 0;
}

对应的Makefile如下:

TARGET=process
SRC=process.c main.c$(TARGET):$(SRC)$(CC) $^ -o $@.PHONY:clean
clean:rm -rf $(TARGET)

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

15、Django Admin添加自定义字段功能

修改模型类HeroAdmin admin.register(Hero) class HeroAdmin(admin.ModelAdmin):change_list_template "entities/heroes_changelist.html"... # 此处原代码不动&#xff0c;只增加此前后代码def get_urls(self):urls super().get_urls()my_urls [path(immort…

溜狗牵绳行为检测-目标检测数据集(包括VOC格式、YOLO格式)

溜狗牵绳行为检测-目标检测数据集&#xff08;包括VOC格式、YOLO格式&#xff09; 数据集&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1CwLEAKcdlh9hbcBNh_Awdw?pwdiu6b 提取码&#xff1a;iu6b数据集信息介绍&#xff1a; 共有 1980 张图像和一一对应的标注文件…

7.统一网关-Gateway

文章目录 1.统一网关介绍2.网关开发3.predicate4.Route Predicate Factories(路由断言工厂)4.1Path 路由断言工厂4.2.Method 路由断言工厂4.3 Header 路由断言工厂4.4 Query 路由断言工厂4.5 Host 路由断言工厂4.6 After 路由断言工厂4.7 Before 路由断言工厂4.8 Between 路由断…

超声波测距模块HC-SR04(基于STM32F103C8T6HAL库)

超声波测距模块参考资料 1.电路连接及引脚配置 触发信号PA3只需要输出10us的高电平&#xff0c;所以直接设置成 普通的GPIO端口即可&#xff1b;回响信号使用外部中断&#xff0c;上升沿信号产生外部中断&#xff0c;打开定时器&#xff0c;下降沿再产生一次中断&#xff0c;读…

国内外大模型汇总(包括科大星火、文心一言、通义千问、智普清言、华为大模型)

国内外大模型汇总 1. 科大讯飞星火认知大模型 主要特点&#xff1a; 多语言能力&#xff1a;以中文为核心&#xff0c;同时支持多语言处理&#xff0c;能够进行跨语种的语言理解和生成。 广泛的任务能力&#xff1a;具备内容生成、语言理解、知识问答、推理、数学计算、代码…

强化网络安全:通过802.1X协议保障远程接入设备安全认证

随着远程办公和移动设备的普及&#xff0c;企业网络面临着前所未有的安全挑战。为了确保网络的安全性&#xff0c;同时提供无缝的用户体验&#xff0c;我们的 ASP 身份认证平台引入了先进的 802.1X 认证协议&#xff0c;确保只有经过认证的设备才能接入您的网络。本文档将详细介…

Kafka【六】Linux下安装Kafka(Zookeeper)集群

Kafka从早期的消息传输系统转型为开源分布式事件流处理平台系统&#xff0c;所以很多核心组件&#xff0c;核心操作都是基于分布式多节点的。本文这里采用三台虚拟机模拟真实物理主机搭建Zookeeper集群和kafka集群。 VMware可以使用户在一台计算机上同时运行多个操作系统&…

使用AI写WebSocket知识是一种怎么样的体验?

一、WebSocket基础知识 1. WebSocket概念 1.1 为什么会出现WebSocket 一般的Http请求我们只有主动去请求接口&#xff0c;才能获取到服务器的数据。例如前后端分离的开发场景&#xff0c;自嘲为切图仔的前端大佬找你要一个配置信息的接口&#xff0c;我们后端开发三下两下开…

Windows下的Redis启动报错Redis service failed to start

报错原因&#xff1a;Redis服务没有找到log文件 解决方案&#xff1a; 1、在Redis目录下打开redis.windows-service.conf文件 2、找到logfile存放目录&#xff0c;一般默认为Logs/redis_log.txt 3、在Redis目录创建Logs文件夹&#xff0c;在Logs文件夹下创建redis_log.txt文件…

机器学习数学公式推导之降维

文章目录 降维线性降维-主成分分析 PCA损失函数SVD 与 PCoASVD 的基本形式SVD 的计算p-PCA 小结 P22 (系列五) 降维1-背景 本文参考 B站UP: shuhuai008 &#x1f339;&#x1f339; 降维 我们知道&#xff0c;解决过拟合的问题除了正则化和添加数据之外&#xff0c;降维就是最…

自己部门日均1000+告警?如何减少90%无效告警?

目录标题 一、告警的类别1.技术告警1.1基础设施告警1.2基本服务告警 2.业务告警3.监控大盘告警 二、为何需要告警治理&#xff1f;三、治理迫在眉睫1.1告警治理策略1.2核心监控告警点1.3避免告警反模式1.4告警规约制定1.5自动化处理 一、告警的类别 一般的告警分为以下几点&am…

连续信号的matlab表示

复习信号与系统以及matlab 在matlab中连续信号使用较小的采样间隔来表四 1.单位阶跃信号 阶跃信号:一个理想的单位阶跃信号在时间 t 0 之前值为0&#xff0c;在 t 0 及之后值突然变为常数 A&#xff08;通常取 A 1&#xff09; %matlab表示连续信号,是让信号的采样间隔很小…

数据类型转换

1. 基本数据类型转换 1.1 自动类型转换 1.2 注意 1.3 强制类型转换 2. String 与基本数据类型的转换 2.1 基本数据类型转 String public class StringConvert{public static void main(String []args){//基本数据类型-->Stringbyte a1;short b10;int c100;long d1000;…

什么是网络安全?

目录 网络安全定义 网络安全如何运作&#xff1f; 1.人 2.基础设施 3.漏洞 4.技术 网络安全的演变 未来十年的网络安全将会是什么样子&#xff1f; 网络安全为何对企业如此重要&#xff1f; 网络安全的类型 1.网络安全 2.应用程序安全 3.信息安全 4.运营安全 5.灾…

SAP与湃睿PLM系统集成案例

一、项目背景 浙江某家用电机有限公司, 该公司的产品涵盖洗衣机、‌空调、‌冰箱及厨房用具等家电电机的制造&#xff0c;‌具备年产4600万台电机的生产能力&#xff0c;‌是中国最大的家电电机生产基地之一。 为确保工艺路线信息在设计与生产执行层面的无缝传递&#xff0…

c语言——用一维数组输出杨辉三角形

一.代码 #include <stdio.h> int Num[100]; int Hang; int Lie; int a; int Flag; int main() {Lie 1;Hang 1;a 0;while (1) {//列1为1if (Lie 1) {Num[1] 1;Lie;}//数据存到数组里面while (Hang > Lie && Hang ! 2) { if (Hang!Lie) {Flag Num[Lie] …

端口安全老化细节

我们都知道port-security aging-time命令用来配置端口安全动态MAC地址的老化时间&#xff0c;但是后面还可以加上类型&#xff1a; [SW1-GigabitEthernet0/0/1]port-security aging-time 5 type absolute Absolute time 绝对老化 inactivity Inactivity time相对老化 …

原型模式prototype

此篇为学习笔记&#xff0c;原文链接 https://refactoringguru.cn/design-patterns/prototype 能够复制已有对象&#xff0c; 而又无需使代码依赖它们所属的类 所有的原型类都必须有一个通用的接口&#xff0c; 使得即使在对象所属的具体类未知的情况下也能复制对象。 原型对…

形态学运算合集

圆形结构元素 禹晶、肖创柏、廖庆敏《数字图像处理&#xff08;面向新工科的电工电子信息基础课程系列教材&#xff09;》 禹晶、肖创柏、廖庆敏《数字图像处理》资源二维码

Java项目: 基于SpringBoot+mybatis+maven+mysql图书馆管理系统(含源码+数据库+任务书+答辩PPT+毕业论文)

一、项目简介 本项目是一套基于SpringBootmybatismavenmysql图书馆管理系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操…