那个制表符与五十年兼容性:Make 如何把周末决策变成软件世界的自然法则

一段周末代码和一个看似笨拙的制表符决定了构建工具的基本模型——目标、依赖、时间戳—Make 用极简抽象统治了五十年软件构建,同时将兼容性与历史包袱一并传承。


Make 工具五十年:一份万字发展史
第七章 那个著名的制表符:一个周末决定,五十年的代价
讲 Make 的历史,绕不开那件"Unix 历史上最糟糕的设计失误之一"。
7.1 规则本身
1978 年论文里就写明了:分开成行的命令必须以制表符开头。命令可以出现在"依赖行分号之后,或者紧跟依赖行、以制表符开头的行上"。
Feldman 后来解释了制表符的来源:Johnson 的 yacc 是新的,而生成文本模式识别代码的 Lex 更是崭新,他把 Make 当成学习它们的好借口。"在第一次尝试 Lex 把自己绕进去之后,我就用 newline-tab 这个模式简单地做了点东西。它work了,就留下来了。"
7.2 为什么没有改
真正的转折点在几周后。Feldman 回忆:
"然后过了几周,我的用户群体大约有十几个人,大多是朋友,我不想搞砸我已经有的用户基础。剩下的,很遗憾,就成了历史。"
2015 年 Michael Stillwell 就一个流行版本向他求证,Feldman 回复说那个故事"只对了一半"。他用了制表符是因为在第一版 Lex 里"处理某些其他模式有困难"。几周内十几个朋友在用 Make,"所以即便我知道'第一列的制表符'是个坏主意,我也不想打乱我的用户基础。于是我把灾难加在了几千万人头上。"
7.3 后果
问题陈述起来很简单:一个制表符和一串空格在屏幕上看可能一模一样,所以"makefile 的解释可能因不可见的空白差异而发生剧烈变化"。
GNU Make 至今仍强制执行它。其手册把命令称为 recipe,并说每一行 recipe"必须以制表符开头",还特意补充:"八个空格不算。"如果你用八个空格缩进,GNU Make 通常会停下来报错:"missing separator (did you mean TAB instead of 8 spaces?)"。
这个故事已经成为软件工程课的经典案例:一个周五晚上为了赶时间做出的设计决策,到周一早上就获得了自然法则般的力量——只要有人依赖它。 制表符熬过了 Lex 的相关性、熬过了贝尔实验室本身、熬过了每一次想用空格替代它的尝试。
第八章 隐线:芬克与设计哲学
Make 的持久,本质上是Unix 哲学的一次纯粹表达:做一件事,做到完整,与一切组合。
Feldman 自己总结过这种取舍——"make 知道文件、时间戳和 shell 命令。这就是可能出错事项的完整清单。" 对比一个现代构建系统,它知道你的语言、框架、注册表、锁文件、CI 提供商、云缓存,以及你为配置这一切而写的 TOML 模式——每个集成都是一种失败模式,每个依赖都是一项负债,每个"便利"都是对一个变化快于配置的世界的假设。
"Feldman 用一个周末写出了 make,因为这本就是一个周末问题。它能存活五十年,是因为他有纪律只解决那个问题,别的什么都不做。"
这句话或许是这份历史里最值得反复读的一句。
第九章 影响与传承:Make 的后代与对手
Make 建立起来的"依赖图 + 配方"模型,成为此后每一个构建系统都必须面对的地基:要么采纳它,要么反叛它。
• 采纳者:Apache Ant、Rake、MSBuild,以及被明确列为"受 Make 影响"的 Ant、Rake、MSBuild 等。
• 反叛者:Ninja 对速度的专注、Bazel 对封闭性(hermeticity)的追求。
一个有趣的对照是 CMake:它约 150 万行 C 和 C++ 代码,其存在的全部理由就是生成 Makefile。 一个 150 万行的工具,只为给 1976 年那个 2500 行的程序生产输入文件。这既是背书,也可以说是别的东西。
而 Make 本身仍在最核心的地方运行:
• Linux 内核的 kbuild 系统建立在 GNU Make 之上,从源码构建 Linux 本质上就是一次极大的递归 make 调用。
• GCC 需要 GNU Make 才能构建。
• BSD 操作系统用 BSD make 构建整个基础系统。
• 一个 1976 年周末写出的构建系统,编译运行着 Netflix、WhatsApp 生产环境以及互联网根域名服务器上的操作系统。
第十章 荣光与人物:Stuart Feldman
Feldman 拥有普林斯顿大学天体物理科学学士学位和 MIT 应用数学博士学位。他约在 1973 年加入贝尔实验室计算科学研究部,与 Ken Thompson、Dennis Ritchie、Doug McIlroy 等 Unix 先驱共事。
离开贝尔实验室后,他先后在 Bellcore、IBM、Google 担任高级研究与工程职务,曾任 Google 工程副总裁,并在 2006 至 2008 年担任 ACM 主席。
2003 年,他因 Make 获得 ACM 软件系统奖。 他还在软件工程课堂上,把制表符当作教学案例来讲。
"这些资历没有一项是理解他所造之物的必要条件。他所造之物,才是理解这些资历的必要条件。"
第十一章 2026:五十年后,Make 仍在说明什么
Make 是有资格被认真研究的一份警示录,与它受到的赞美一样。它留下的经典教训包括:
• 强制制表符——一个代价惨重的历史包袱;
• 递归 make 的微妙之处;
• 基于时间戳的过期判断可能被骗过;
• 可移植性与 GNU 扩展之间的张力。
这些无一不是语言与工具设计课的经典教材。
但最震撼的结论仍然是:一个 1976 年写就的程序,至今仍坐在世界如何编译软件的中心,核心思想几乎未变——这是"把基础抽象做对"的最有力证明之一。
用三行来概括这五十年,也许再合适不过:
目标。依赖。时间戳。三个概念,五十年,同一个算法。
附:一页时间线速查
时间 事件
1976 年 4 月 Stuart Feldman 在贝尔实验室完成 Make 早期版本(基线 2.1,4/24/76)
1977 年 4 月 某版本进入 PWB(Programmer's Workbench)
1978 年 8 月 版本 2.54,源码含 54 条修订记录
1979 年 4 月 论文《Make — A Program for Maintaining Computer Programs》发表于《Software: Practice and Experience》
1979 年 随 Version 7 Unix 发布(版本 2.58,约 2500 行 C)
1986 年 Sun DevPro Make 随 SunOS-3.2 出现
1988 年 GNU Make 首次出现(Stallman 与 McGrath)
1992 年 核心行为纳入 POSIX.2(IEEE Std 1003.2)
1997 年 Paul D. Smith 接手 GNU Make 长期维护
2003 年 Feldman 获 ACM 软件系统奖
2006 年 12 月 Sun DevPro Make 开源(Solaris 开源努力的一部分)
2009 年 OpenOffice 构建系统重写为使用 GNU Make
2010 年 GNU Make 3.82 引入 .RECIPEPREFIX
2013 年 10 月 GNU Make 4.0:Guile 脚本、output-sync、可加载对象
2022 年 GNU Make 4.4:MAKEFLAGS 与 jobserver 改进
2023 年 2 月 GNU Make 4.4.1,当前稳定版

一句话总结:Make 的历史是一部"最小可行抽象如何统治世界"的历史——1976 年的一个周末,为了解决"文件没重新编译"这一件事,Feldman 写下了目标、依赖、时间戳三个概念;五十年后,全世界最大的软件系统,仍在用同一个算法决定谁该被重新编译。