当前位置:首页 » 《我的小黑屋》 » 正文

自动化构建与进度显示:全面解读 make 与 Makefile

11 人参与  2024年12月23日 14:00  分类 : 《我的小黑屋》  评论

点击全文阅读


文章目录

`Make``make` 的主要功能使用 `make` 工具 `Makefile` 的基本结构简单示例进阶示例 `Make` 和 `Makefile` 的优缺点倒计时与进度条程序

makemakefileLinux/Unix 开发环境中用于自动化构建的强大工具,尤其在多文件编译的项目中,用于管理文件之间的依赖关系和构建规则。通过定义编译规则,Make工具可以根据源文件的更新情况,自动决定哪些部分需要重新编译,从而提高开发效率。

Make

make 是一个自动化构建工具,通过解析 makefile 文件中的规则,管理项目的构建流程。它的主要功能是根据源文件的修改情况,自动更新目标文件,避免手动输入复杂的编译命令。

make 的主要功能

自动化构建:通过规则定义编译、链接等步骤,减少重复操作。增量编译:仅编译发生变化的文件,优化构建时间。灵活性:支持复杂的依赖关系和自定义任务。

使用 make 工具

执行 make 命令时,make 会自动查找当前目录下的 Makefilemakefile 文件,并根据文件中的规则执行第一条命令(其余的需要显示调用):

make

Makefile 的基本结构

Makefile 是定义构建规则的文件,其核心部分是规则描述。每一条规则的基本格式如下:

Target : DependenciesCommand
目标(Target):需要生成的文件或任务。依赖项(Dependencies):生成目标所需的文件或条件。命令(Command):实现目标的具体操作,需以Tab键开头。

简单示例

code:code.cgcc code.c -o code.PHONY:cleanclean:rm -f code

在这里插入图片描述
在这里插入图片描述

具体说明:

code 是需要生成的可执行文件,其生成需要依赖 code.c 文件,通过 gcc code.c -o code 生成。clean 的作用是清理生成的 code 文件,但是没有直接跟 code 关联,需要显示调用 make clean 来清除 code 以便重新编译。.PHONY 修饰 cleanclean 就会成为伪目标,伪目标是指不对应具体文件的目标,常用于定义清理任务或其他辅助操作,其特性就是总是能够被执行,原因在于 .PHONYmake 忽略源文件和可执行文件的 Modify 时间

补充:

文件的相关时间有3个,可以通过 start 来查看。
在这里插入图片描述

访问时间 (Access Time):表示文件内容最近一次被访问的时间。修改时间 (Modification Time):表示文件内容最近一次被修改的时间。状态更改时间 (Change Time):表示文件属性最近一次发生更改的时间。

补充:

执行 make 时,code 被编译成 code.c ,之后当文件内容没有发生更改时,是无法再次 make 的,这是通过比较 code.ccode 的修改时间 (Modification Time) 来做到的,当 code.c 的修改时间早于 code 的修改时间时就无法再次 make

在这里插入图片描述
.PHONY 的作用就是让 make 忽略源文件和可执行文件的 Modify 时间,哪怕文件没有修改就可以执行 make

进阶示例

makefile 支持变量的定义,便于规则复用和代码维护。变量的值可以在定义时赋值,也可以在命令行传入;也提供了一些自动变量,用于简化规则中的目标和依赖项引用。

# 定义变量CC = gccTARGET = mainOBJS = main.o utils.o# 默认目标$(TARGET): $(OBJS)$(CC) -o $@ $^# 自动生成目标文件%.o: %.c$(CC) -c $< -o $@ # 清理目标.PHONY: cleanclean:rm -f $(OBJS) $(TARGET)
\$(CC) :变量CC的值,其他变量同理。$@ :当前规则的目标文件名。$< :当前规则的第一个依赖项。$^ :当前规则的所有依赖项。%.o: %.c :所有的.o文件都可以通过对应的.c文件生成

MakeMakefile 的优缺点

优点

自动化构建:减少手动输入编译命令的重复劳动。高效增量编译:仅重新编译必要的文件。灵活性高:支持多种任务自动化,如清理、打包、测试等。易扩展:通过变量和规则,支持复杂项目管理。

缺点

语法简洁但不直观:规则书写需要严格遵守语法格式(如Tab开头)。大型项目维护难度较大:需要搭配其他工具(如CMake )简化管理。

倒计时与进度条程序

倒计时

#include <stdio.h>#include <unistd.h>int main(){    int i = 10;// 初始化计数器 i 为 10    while(i >= 0)// 当 i 大于等于 0 时,进入循环    {        printf("%-2d\r", i);     // 使用 %-2d 格式化输出 i,左对齐,保证占用 2 个字符的宽度,并且在输出后添加回车符 \r        fflush(stdout);         // 刷新输出缓冲区,强制将数据立即输出到屏幕上        i--;// i 每次递减 1        sleep(1);// 程序暂停 1 秒    }    return 0;}

代码解析:

%-2d:在 printf 中,%-2d 是用来打印整数的格式化字符串。%-2d 表示将数字左对齐并且占用至少 2 个字符的宽度。假设 i 是个位数时,输出会带有一个空格,如 1、 2 等。这样保证打印数字时,光标位置不会被打乱。、\r:回车符 \r 的作用是将光标移动到当前行的最前面。因此,每次打印一个新的数字后,光标会回到行首,覆盖之前打印的数字,达到每次更新显示数字的效果。fflush(stdout)printf 输出通常是缓冲输出,也就是说数据会先保存在缓冲区中,直到缓冲区满或者程序结束时才会输出。为了确保每次打印的数字能够立即显示在屏幕上,使用 fflush(stdout) 强制刷新缓冲区。sleep(1)sleep(1) 使得程序每次打印数字后会暂停 1 秒,形成倒计时的效果。

进度条

#define NUM 101#define STYLE '#'double total = 1024.0;      //总下载量(1024 MB)double speed = 1.0;         //下载速度 (每次增加 1MB)void process_v2(double cur){    char buffer[NUM];// 用于存储进度条的字符数组    memset(buffer, 0, sizeof(buffer));// 初始化数组,将所有元素置为 0    const char* lable = "|/-\\";// 动态指示器(显示的符号)    int len = strlen(lable);// `lable` 字符串的长度(4)    static int cnt = 0;// 静态变量,记录动态符号的位置    cnt %= len;// 每次更新时,cnt 取值为 0~3,用来循环显示 `|/-\`    int num = (int)cur * 100 / total;// 当前下载进度的百分比    int i = 0;// 根据当前进度填充进度条    for(;i < num;++i)        buffer[i] = STYLE;    double rate = cur / total;// 计算当前进度的比例    printf("[%-100s][%.1lf%%][%c]\r", buffer, rate * 100, lable[cnt]);// 输出进度条、百分比和动态符号    fflush(stdout);// 刷新输出缓冲区,确保立即显示    cnt++;// 更新动态符号的索引}void DownLoad(){    double cur = 0;         // 当前下载量初始化为 0    while(cur <= total)// 当下载量小于等于总下载量时,持续更新进度条    {        process_v2(cur);// 调用 process_v2 更新进度条        usleep(5000);// 暂停 5 毫秒,模拟下载过程        cur += speed; // 每次增加 1MB    }    printf("\ndownload %.2lfMB Done\n", cur);// 下载完成后输出提示信息}int main(){     DownLoad();// 启动下载过程    return 0;}

代码解析:

常量和变量定义 NUM 是定义的进度条长度(101 个字符)。STYLE 是进度条中用来表示已下载部分的符号(#)。total 表示总下载量(1024 MB)。speed 定义了每次增加的下载量(1 MB),表示每次下载的增量。 process_v2 函数 进度条缓冲区:buffer 数组用于存储进度条的显示状态,长度为 NUM(101)。动态符号:lable 字符串包含 4 个字符 |, /,-, \,通过变量 cnt 控制这些字符的循环显示,给人一种“加载中”的感觉。下载进度计算:num = (int)(cur * 100 / total) 计算当前下载量 cur 相对于总下载量 total 的百分比,然后用这个百分比来填充进度条。输出格式:通过 printf 输出格式化的进度条,%-100s 表示左对齐且占据 100 个字符的空间,%.1lf%% 显示进度百分比,[%c] 显示动态字符。刷新输出:fflush(stdout) 确保每次进度条输出后立即刷新,避免缓冲区延迟输出。 DownLoad 函数 下载过程:cur 是当前已下载的量,初始值为 0。while(cur <= total) 循环执行,直到下载量达到总量 total。进度更新:每次循环调用 process_v2(cur) 更新进度条,并暂停 5 毫秒(usleep(5000)),然后增加下载量。下载完成:当 cur 超过 total 后,跳出循环并打印下载完成信息。

该程序会动态显示一个进度条,模拟下载进程。屏幕输出会类似如下:

[##########################################----------------------------------------][50.0%][|][####################################################------------------------------][70.0%][-][###############################################################------------------][90.0%[\][##################################################################################][100.0%]\]download 1024.00MB Done

Have a good day?

See you next time, guys!?✨?

请添加图片描述


点击全文阅读


本文链接:http://zhangshiyu.com/post/205698.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1