首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【Linux】深入浅出 Linux 自动化构建:make 与 Makefile 的实用指南

【Linux】深入浅出 Linux 自动化构建:make 与 Makefile 的实用指南

作者头像
用户11872857
发布2025-12-17 16:03:10
发布2025-12-17 16:03:10
310
举报

前言

在 Linux 开发中,makeMakefile是提升项目构建效率的神兵利器。从单个文件的小程序到百万行代码的大型工程,掌握它们能让你从重复的编译命令中解放出来。本文将带你从零开始理解make、makefile 的工作原理,并通过实战案例掌握其应用技巧。

📚 Linux 入门篇

【 Linux 历史溯源与指令入门 】

【 Linux 指令进阶 】

【 Linux 权限管理 】

🔧 Linux 工具篇

【 yum + vim 】

【 sudo白名单配置 + GCC/G++ 】


目录

一、背景:为什么需要 make/Makefile?

二、核心概念:依赖关系与依赖方法

【案例】:一个简单的 C 项目

【问题】:文件名必须是makefile吗?

【问题】:如果打乱依赖关系的顺序会正常执行吗?

三、make 的工作原理:如何判断 “是否需要编译”?

【stat指令】

四、伪目标与项目清理

五、特殊符号

六、依赖关系缺失问题

七、如何在使用make后不显示依赖方法

八、make工作原理总结


一、背景:为什么需要 make/Makefile?

想象一下,你有一个由多个.c文件组成的项目,每次修改代码后都要手动执行gcc -c file1.cgcc -c file2.cgcc -o target file1.o file2.o…… 这样的重复劳动不仅低效,还容易出错。

make是一个命令工具Makefile是一个规则文件,二者配合实现了自动化编译:只需编写一次规则,后续执行make命令即可自动完成 “哪些文件需要编译、哪些需要重编译” 的判断,极大提升开发效率。

对于大型工程,会不会写 Makefile 甚至成为衡量工程师能力的一个侧面标准 —— 毕竟它能清晰管理 “文件依赖、编译顺序、清理逻辑” 等复杂场景。

二、核心概念:依赖关系与依赖方法

makefile 的核心是 “依赖关系”“依赖方法”:

  • 依赖关系:描述 “目标文件” 依赖于哪些 “源文件”。例如,可执行文件hello依赖于目标文件hello.ohello.o依赖于汇编文件hello.s,以此类推。
  • 依赖方法:描述如何从 “依赖文件” 生成 “目标文件”(通常是编译命令)。
【案例】:一个简单的 C 项目

我们以输出 “hello Makefile!” 的小程序为例,逐步构建 Makefile。

【步骤 1】:编写 C 代码(hello.c

代码语言:javascript
复制
#include <stdio.h>
int main()
{
    printf("hello Makefile!\n");
    return 0;
}

【步骤 2】:编写 Makefile 规则

一个完整的 Makefile 规则格式为:

代码语言:javascript
复制
目标文件: 依赖文件
    依赖方法(编译命令,注意开头必须是Tab缩进)

针对hello.c,我们可以编写多层依赖的 Makefile:

代码语言:javascript
复制
# 最终可执行文件hello,依赖于hello.o
hello: hello.o
    gcc hello.o -o hello

# 目标文件hello.o,依赖于hello.s
hello.o: hello.s
    gcc -c hello.s -o hello.o

# 汇编文件hello.s,依赖于hello.i
hello.s: hello.i
    gcc -S hello.i -o hello.s

# 预处理文件hello.i,依赖于hello.c
hello.i: hello.c
    gcc -E hello.c -o hello.i

【步骤 3】:执行 make命令

在终端中输入make时,它会仅以 Makefile 中第一个定义的目标为起点,沿着该目标的完整依赖链(例如hello→hello.o→hello.s→hello.i→hello.c)执行构建。

整个过程中,make只会处理这个 “第一个目标” 及其所有依赖项(包括直接依赖和间接依赖),其他未被该依赖链关联的目标(即使在 Makefile 中定义)也不会被自动执行。

同时,它会通过比较 “目标文件” 与 “依赖文件” 的修改时间实现 “按需编译”:只有当依赖文件更新过(修改时间更新),才会重新编译对应的目标,否则直接跳过,大幅提升效率。

【问题】:文件名必须是makefile吗?

make是命令,makefile是一个文件,当前目录下的文件

文件名不一定必须是 Makefile,但 make 命令有默认的文件名查找规则,常用的合法文件名有两种:

  1. Makefile(首字母大写)
  2. makefile(全小写)

这两个文件名是 make 命令的默认查找对象。当在终端执行 make 时,它会优先先在当前目录下寻找这两个文件,找到后按其中的规则执行构建。

【如果想用其他文件名怎么办?】

如果你的想将构建规则文件命名为其他名称(比如 mybuild),可以通过 make-f--file 参数指定文件名,例如:

代码语言:javascript
复制
make -f mybuild  # 告诉make使用mybuild文件作为规则文件

【为什么推荐用 Makefile?】

在实际开发中,更推荐使用 Makefile(首字母大写),原因是:

  • 它在目录中会更醒目(通常大写字母的文件会排在前面),方便开发者和其他开发者快速识别项目的构建规则文件。
  • 符合大多数开源项目的约定,增强代码的规范性和可读性。

总结:默认情况下,make 只认 Makefilemakefile,但通过 -f 参数可以指定任意文件名作为构建规则文件。


【问题】:如果打乱依赖关系的顺序会正常执行吗?

在 Makefile 中,规则的顺序不影响依赖关系的解析,因为 make基于 “依赖树” 的拓扑顺序来执行编译的,而非按照规则在文件中的书写顺序。

以提供的 Makefile 为例,即使将规则顺序打乱,比如:

代码语言:javascript
复制
# 汇编文件hello.s,依赖于hello.i
hello.s: hello.i
    gcc -S hello.i -o hello.s

# 最终可执行文件hello,依赖于hello.o
hello: hello.o
    gcc hello.o -o hello

# 预处理文件hello.i,依赖于hello.c
hello.i: hello.c
    gcc -E hello.c -o hello.i

# 目标文件hello.o,依赖于hello.s
hello.o: hello.s
    gcc -c hello.s -o hello.o

make 依然会自动梳理依赖关系的层级

  1. 要生成 hello,需要先有 hello.o
  2. 要生成 hello.o,需要先有 hello.s
  3. 要生成 hello.s,需要先有 hello.i
  4. 要生成 hello.i,需要先有 hello.c

最终会按照 hello.c → hello.i → hello.s → hello.o → hello正确顺序执行编译命令。

这是因为 make 的核心逻辑是 “先处理依赖项,再处理目标项”—— 它会递归地查找每个目标的依赖,直到找到最底层的源文件(如 hello.c),再从下往上依次执行编译。

因此,只要依赖关系的定义是正确的,规则在 Makefile 中的书写顺序可以任意调整make 都能正确识别并按依赖层级执行构建。

三、make 的工作原理:如何判断 “是否需要编译”?

make 的核心逻辑是比较 “目标文件” 和 “依赖文件” 的 “最近修改时间”

  • 如果 “依赖文件” 的修改时间晚于“目标文件”,说明依赖文件被更新过,需要重新编译生成目标文件。
  • 如果 “目标文件” 不存在,也会触发编译。

【stat指令】 语法:stat [选项] [文件] 功能:查看文件的详细元数据(包括 ACM 时间、大小、权限、inode 等) 常用选项:

  • -c %y [文件]:仅查看文件的内容修改时间(Modify)
  • -c %z [文件]:仅查看文件的属性变更时间(Change)
  • -c %x [文件]:仅查看文件的最近访问时间(Access)
  • 无选项:查看文件的完整元数据(含所有时间、权限、inode 等)

【示例1】:目标文件最近修改时间比依赖文件最近修改时间新

这个时候不会触发编译,因为test是最新的

【示例2】:目标文件最近修改时间比依赖文件最近修改时间旧

从图中可以看到重新进行编译了

【注意】:我们比较的是Modify时间,也就是文件内容最近修改的时间

四、伪目标与项目清理

在开发中,我们常需要 “清理编译生成的中间文件”,这就需要用到伪目标(用.PHONY修饰)。伪目标的特性是 “总是被执行”,不会受文件修改时间的影响。

clean规则为例:

代码语言:javascript
复制
.PHONY: clean
clean:
    rm -f hello.i hello.s hello.o hello

执行make clean时,无论这些文件是否存在,都会执行rm命令清理它们,方便我们重新编译项目。

五、特殊符号

在 Makefile 中,@ 和 ^ 是自动变量,用于简化命令书写,分别代表不同的含义:

  • $@:表示当前规则中的目标文件(即规则中 : 左边的文件名)。
  • $^:表示当前规则中的所有依赖文件(即规则中 : 右边的所有文件名,去重后的值)。

六、依赖关系缺失问题

缺少test.s生成方式:

make后说没有规则可制作目标“test.s”,该目标是“test.o”所需要的,这个时候就会编译失败

七、如何在使用make后不显示依赖方法

在所有依赖方法前加上@符号后,再次make就会发现依赖方式不回显了

八、make工作原理总结

make 是如何工作的 , 在默认的方式下,也就是我们只输入 make 命令。

  1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
  2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“hello”这个文件 ,并把这个文件作为最终的目标文件。
  3. 如果hello文件不存在,或是hello所依赖的后面的hello.o文件的文件修改时间要比hello这个文件新(可以用 touch 测试),那么,他就会执行后面所定义的命令来生成hello这个文件。
  4. 如果hello所依赖的hello.o文件不存在,那么make会在当前文件中找目标为hello.o文件的依赖性,如果找到则再根据那一个规则生成hello.o文件。(这有点像一个堆栈的过程)
  5. 当然,你的C文件和H文件是存在的啦,于是make会生成 hello.o 文件,然后再用 hello.o 文件声明make的终极任务,也就是执行文件hello了。
  6. 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
  7. 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。
  8. make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-12-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、背景:为什么需要 make/Makefile?
  • 二、核心概念:依赖关系与依赖方法
    • 【案例】:一个简单的 C 项目
    • 【问题】:文件名必须是makefile吗?
    • 【问题】:如果打乱依赖关系的顺序会正常执行吗?
  • 三、make 的工作原理:如何判断 “是否需要编译”?
  • 四、伪目标与项目清理
  • 五、特殊符号
  • 六、依赖关系缺失问题
  • 七、如何在使用make后不显示依赖方法
  • 八、make工作原理总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档