Linux:Makefile 自动化构建与进度条实战

本文为 Linux 开发工具专题的第四部分,详细讲解 Makefile 的编写规则、变量、自动变量、模式规则,以及如何编写一个功能完整的命令行进度条程序(涉及回车换行、缓冲区、刷新、格式化输出、旋转光标等知识)。通过理论与实践结合,帮助同学们掌握自动化构建和终端交互编程。

一、Makefile 入门与核心概念

1.1 什么是 Makefile?

  • make:一个命令,用于自动化编译。

  • Makefile:一个文件,定义了编译规则(依赖关系 + 依赖方法)。

1.2 第一个 Makefile 示例

假设只有一个源文件 myproc.c

复制代码
myproc: myproc.c
    gcc -o myproc myproc.c
  • myproc: myproc.c依赖关系 :目标文件 myproc 依赖于 myproc.c

  • gcc -o myproc myproc.c依赖方法 :如何从依赖生成目标(必须以 Tab 键开头,不能用空格)。

1.3 清理目标:clean 与伪目标 .PHONY

复制代码
.PHONY: clean
clean:
    rm -f myproc
  • .PHONY 修饰的目标称为伪目标,它不表示一个真实文件。

  • 伪目标的特点:总是执行其依赖方法,不受文件新旧影响。

为什么要有 .PHONY

因为 clean 不是要生成一个叫 clean 的文件,而是执行删除操作。如果不加 .PHONY,当目录下意外存在一个名为 clean 的文件时,make clean 会认为目标已存在且没有依赖更新,从而不执行删除。

1.4 make 如何判断是否需要重新编译?

make 通过比较目标文件依赖文件modify time(修改时间) 来决定是否执行依赖方法:

  • 如果依赖文件的修改时间比目标文件,则重新生成目标。

  • 如果目标文件不存在,则肯定执行。

文件时间查看命令stat 文件名

文件有三种时间:

  • Access:最近访问时间(读取)

  • Modify:最近修改时间(内容改变)------ make 主要依据这个

  • Change:最近改变时间(属性改变)

  • 内容改变modify和change都会改变,属性改变只有change会变

1.5 为什么通常不用 .PHONY 修饰编译目标?

如果给 myproc 加上 .PHONY,那么每次 make 都会重新编译,即使源代码没有修改。这在大型项目中会浪费大量时间(只改了一个文件却要全部重编)。所以只对 clean 等不生成真实文件的目标使用 .PHONY


二、Makefile 进阶:变量与自动变量

2.1 定义和使用变量

复制代码
CC = gcc
SRC = myproc.c
BIN = myproc

$(BIN): $(SRC)
    $(CC) -o $(BIN) $(SRC)
  • 变量引用:$(变量名)

  • 好处:修改一处,全局生效(例如更改编译器或目标名)

2.2 自动变量(简化依赖方法)

自动变量 含义
$@ 表示目标文件
$^ 表示所有依赖文件列表
$< 表示第一个依赖文件

示例:

复制代码
$(BIN): $(SRC)
    $(CC) -o $@ $^

2.3 模式规则:%.o: %.c

复制代码
%.o: %.c
    $(CC) -c $< -o $@
  • % 是Makefile下的通配符,在表示任意相同的字符串。

  • 这条规则表示:如何从任意 .c 文件生成同名的 .o 文件。

  • -c 选项表示只编译不链接。

2.4 自动获取源文件列表

方法一:使用 wildcard 函数

复制代码
SRC = $(wildcard *.c)

方法二:使用 shell 命令

复制代码
SRC = $(shell ls *.c)

2.5 生成依赖的 .o 文件列表

复制代码
OBJ = $(SRC:.c=.o)
  • 这是一个替换引用 语法:将 SRC 中所有 .c 后缀替换为 .o

2.6 一个通用的 Makefile 模板

  • 这个 Makefile 可以自动处理当前目录下任意数量的 .c 文件。

  • 先编译每个 .c.o,再链接所有 .o 生成可执行文件。


三、进度条程序编写

3.1 预备知识:回车与换行

  • 换行(\n:光标移动到下一行。

  • 回车(\r:光标移动到当前行的开头。

在终端中,我们通常想要的效果是:在同一行不断更新进度,这就需要使用 \r 让光标回到行首,然后覆盖之前的内容。

3.2 缓冲区与刷新

复制代码
printf("hello");   // 没有 \n,数据暂存在缓冲区
sleep(3);          // 此时屏幕上什么都没有
// 程序退出时自动刷新缓冲区,才显示 hello
  • 标准输出(显示器)是行缓冲 模式:遇到 \n 或缓冲区满或程序结束才会刷新。

  • 如果想立即刷新,可以使用 fflush(stdout)

3.3 倒计时程序(理解 \rfflush

复制代码
#include <stdio.h>
#include <unistd.h>

int main()
{
    int i = 10;
    while (i >= 0) {
        printf("%2d\r", i);   // %2d 保证两位宽度,\r 回车覆盖
        fflush(stdout);
        sleep(1);
        i--;
    }
    printf("\n");
    return 0;
}
  • %2d:固定输出两位数字,例如 9,这样覆盖时不会留下残余字符。

  • fflush(stdout):立即把缓冲区内容输出到屏幕。

3.4 进度条的第一版(固定循环)

先创建好process.h、process.c、main.c、Makefile四个文件,

先编写Makefile

把预备工作做好后打开process,h

开始写核心代码部分process.c:

最后写main.c调用核心代码:

  • [%-100s]:左对齐,宽度 100,保证进度条区域固定。

  • 每次增加一个 #,回车覆盖上一行,形成动态效果。

但是这个进度条是无法使用的,这个无法和网上的下载联动起来导致网上还没下好可能进度条就已经跑完了。

3.5 进度条的第二版(结合业务:下载模拟)

将进度条封装成函数,根据 已完成量总量 动态计算进度百分比和 # 的个数。

复制代码
//头文件
复制代码
//源文件
  • static int pos:每次调用递增,模4得到光标字符,实现旋转效果。光标旋转与下载次数有关与下载速度无关。

  • 注意:printf 内不要加 \n,使用 \r 覆盖。

3.6 模拟下载的主程序

复制代码
  • 每次循环,current 增加一个较小的值,调用 FlushProcess更新进度条。

  • 最终进度条会从 0% 逐渐走到 100%。


四、本节课总结

4.1 Makefile 核心要点

概念 说明
依赖关系 目标文件依赖于哪些源文件
依赖方法 如何从依赖生成目标(Tab 开头)
伪目标 .PHONY 总是执行,不检查文件新旧
变量 CC = gcc$(CC) 引用
自动变量 $@(目标)、$^(所有依赖)、$<(第一个依赖)
模式规则 %.o: %.c
文件时间 make 根据 modify time 判断是否需要重新编译
wildcard 获取当前目录所有 .c 文件
替换引用 $(SRC:.c=.o) 生成 .o 列表

4.2 进度条核心要点

知识点 说明
\r\n \r 回车回到行首,\n 换行到下一行
缓冲区 行缓冲,fflush(stdout) 强制刷新
格式化宽度 %2d%-100s 固定宽度,避免覆盖残留
旋转光标 字符数组 -\|/ 循环显示,表示程序未卡死
进度计算 根据 current/total 算出百分比和 # 个数

4.3 最终进度条效果

复制代码
[###################                     ][45.0%][\]
  • 进度条、百分比、旋转光标三者同时更新,且在同一行。
相关推荐
PH = 72 小时前
Docker将jar包装为Image并运行
运维·docker·容器
发发就是发2 小时前
资源管理:I/O端口与内存映射
linux·服务器·驱动开发·单片机·嵌入式硬件·fpga开发
坤坤藤椒牛肉面2 小时前
linux中断:顶半部与底半部
linux·运维·服务器
辞旧 lekkk2 小时前
【Git】远程操作与标签管理
linux·git·学习·萌新
志栋智能2 小时前
超自动化安全:构建弹性安全架构的关键支撑
安全·自动化·安全架构
qq_452396232 小时前
【工程实战】第四篇:UI 自动化 —— Playwright 异步模式深度实战:告别 Selenium 的“脆”与“慢”
selenium·ui·自动化
web守墓人2 小时前
【linux】Mubuntu发布,将完整的ubuntu arm装进手机应用中
linux·arm开发·ubuntu
重生的黑客2 小时前
Linux 开发工具:Git 版本控制与 GDB 调试入门
linux·运维·git
敲上瘾2 小时前
Docker核心要点和指令速通
linux·运维·docker·容器