我刚开始接触源码编译时,跑去问周围一位经验丰富的工程师:为什么明明有现成的安装包,还要费劲从源码自己编一遍?对方只回了一句话:因为你要的功能,二进制包里没有。
这句话我是在自己维护一个小工具时才真正听懂的。那个工具叫 DemoX,一个用 C 写的命令行小部件,官方分发二进制时默认关掉了好几个扩展模块,而我刚好需要其中一个。翻完文档,唯一的出路就是源码编译与定制。整趟走下来我才意识到,源码编译不是什么高深学问,它本质上就是一种“把别人的半成品材料,按你的施工图重新加工”的工程能力。
这篇内容适合谁看?一类是长期依赖系统包管理器、对 configure 和 Makefile 一知半解的开发者;另一类是要在受限部署环境里做定制构建的运维或交付工程师。我会用自己编译 DemoX 的完整过程作主线,把环境准备、参数定制、模块裁剪、补丁维护、失败排查、安装回滚这些环节一一展开,尽量说人话,少讲空理论。
1. 为什么放着现成包不用,非要自己编译——被低估的三个真实场景
1.1 二进制包缺失的编译开关,才是定制的真正起点
官方发布的二进制安装包,本质上是别人预定好的“标准配置”。这就像你去饭店点了一份套餐,主菜固定、配菜固定,你没法单点某个特定搭配。但源码在你手里之后,那头牛和那筐菜都是你的,你想切哪块、想放什么调料,完全自己说了算。
以 DemoX 为例。官方二进制分发版默认不启用扩展日志模块,理由是“大多数人用不上”。可我这边需要记录每次请求的完整元数据,没有这个模块,后续所有分析都断档。包管理器里翻了又翻,根本没得选。最直接的办法,就是下载源码包,在 configure 阶段用--enable-extended-log打开这个开关,然后重新编译。
这类情况非常多。很多软件在分发时为了兼容性、体积或授权考虑,会默认关闭一部分模块:图形界面、可选协议支持、调试符号、静态库版本,等等。这些“被关掉的角落”通常在源码包的configure --help输出里列得清清楚楚,只是大多数人从来不打开看一眼。
1.2 版本滞后与修复节奏,自己编才能跟得上
第二个场景更现实:官方发布的二进制版本,往往滞后于源码版本的迭代节奏。尤其是当你依赖某个新功能,或者需要立刻跟进一个安全修复时,发行版仓库里的二进制包可能还是三个月前的旧版本。
安全修复经常是先出源码补丁,再出测试构建,最后才轮到各发行版仓库同步。中间的窗口期,如果你只能等安装包,那你就是干等;如果你会源码编译,拿到补丁当天就能完成定制构建并部署到测试环境。我维护一个内部服务时,依赖的一个底层库爆出了一个问题,官方仓库的二进制包迟迟没更新,我选择了把修复补丁手动打到源码里重新编译部署。这个过程并不轻松,但当天就解决了问题,比干等安装包踏实得多。
1.3 裁剪体积与可控运行环境,编译成了唯一选项
还有一个容易被忽略但实际很常见的场景:部署环境的硬件和系统受限。我遇到过一台生产机器,CPU 是老型号,官方二进制包却是在新指令集下编译的,装上去直接非法指令崩溃。那种情况,包管理器帮不了你,只有用源码编译时指定兼容的指令集参数,才跑得起来。
反过来也一样。现在很多瘦客户端、容器镜像、嵌入式场景对体积和启动速度极其敏感。官方二进制包为了照顾大多数用户,往往会带上各种模块和动态依赖,又大又慢。自己从源码编译时,可以把用不到的模块全部去掉,编译出一个小而快的专用版本。这种“按需裁剪”别的办法替代不了。
所以总结下来,源码编译与定制解决的核心问题就是三个:功能开关缺失、版本节奏不可控、运行环境定制化。只要其中一条戳中你,你就该把源码编译这件事认真学起来了。
2. 开编前我必做的四件事——解压、读文档、看依赖和预估时间
2.1 先检查工具链,别让编译器版本成为第一个拦路虎
拿到源码包的第一件事,不是急着解压执行 configure,而是先确认当前环境里编译器、构建工具能不能满足项目要求。很多源码包在 README 或 INSTALL 文件里会写清楚最低版本要求,比如“需要 C99 编译器”“建议 GNU Make 3.81 以上”“需要 CMake 3.10 以上”。
我一般会在干净的终端下跑几条命令,先探底:
gcc --version make --version cmake --version如果项目还用到 autoconf 自动生成脚本,还需要确认autoconf和automake是否安装。对于大部分用 C/C++ 写的开源软件,编译器版本太旧会导致语法不兼容;版本太新又可能触发一些以前未发现的告警被当作错误处理。所以,务必先读项目文档里对构建系统的说明,再动手跑脚本。
有些朋友喜欢直接在系统里把所有开发工具包都装一遍,这个思路方向对,但很浪费。更靠谱的做法是,先看项目文档,再缺什么补什么。你不需要一台机器上同时存在十种编译器版本,够用就好。
2.2 依赖清单要提前列好,别等 configure 半路报错
源码包通常不是一个完全自包含的孤岛,它依赖很多外部库。这些依赖在文档里一般会列出,常见形式是“需要 xxx 库开发版”。所谓开发版,就是你不仅要能运行这个库,还要有它的头文件和编译期符号。这个区分很多人搞混。
我一般会做一个简单的依赖分类,大概这样:
- 基础运行库,比如某些通用数据结构的实现库;
- 工具库,比如日志、参数解析、网络通信相关模块;
- 可选扩展,比如某种数据格式支持、某种协议支持。
以 DemoX 为例,它需要libdept这个虚构库的基础运行版本,还需要libdept-dev开发包来提供头文件。用系统包管理器安装时,名字往往长得差不多,但一个带-dev一个不带。缺开发包最典型的现象就是 configure 阶段报出“找不到某个头文件”或“找不到某个函数定义”。
configure脚本本身就会做依赖检查,但如果你能提前把大部分依赖准备好,后面就顺畅很多。我的习惯是花五分钟翻一下 INSTALL 文件里的依赖清单,再顺手跑一下包管理器的查询命令,确认哪些已装、哪些缺失,一次性装齐,而不是等 configure 报一个错装一个包。
2.3 configure --help 的优先级,永远高于网上的各种教程
网上一搜“某某源码编译教程”,出来的结果鱼龙混杂。有些是不同版本的老教程,参数早就不一样了;有些是作者自己环境的特例,搬到你的机器上根本不适用。最权威、最贴合当前版本的说明,永远是源码包里自带的那份帮助输出。
configure --help这个命令,是我每拿到一个新源码包必跑的。它会把当前版本支持的安装路径参数、模块开关、外部依赖指定方式全部列出来,信息量极大。我通常是先跑一遍,把输出保存下来:
./configure --help | tee configure-help.txt然后打开这个文件,按自己的需求搜索相关参数。这样做的好处是,你永远是在“这个版本实际提供的选项”里做选择,而不是凭记忆套用别的版本的经验。跑完帮助输出再读一遍 README 中的快速开始部分,基本就能确定编译命令长什么样了。
2.4 磁盘、内存与并行度,三个容易被忽略的数字
源码编译真正开始之后,有人会惊讶地发现磁盘空间蹭蹭往下掉。这太正常了。源码包可能只有几十 MB,但展开后、编译中间产物建立起来,体积会膨胀数倍。尤其是 C/C++ 项目,每一个源文件对应一个.o中间文件,这些文件全部落在磁盘上。我建议至少预留源码包 3 到 5 倍大小的可用空间。
内存同样重要。很多人以为编译只吃 CPU,其实最后链接阶段非常吃内存,链接器要把所有目标文件、符号表集中处理,内存不够时进程直接被内核杀掉。如果机器内存不大,并行度不要拉满,建议先用两个任务试跑,确认资源富余再上调。
常见的错误姿势是一上来就make -j$(nproc),把 CPU 线程数全部填满。第一次编译时,依赖关系还没验证完,某个编译任务失败会引发连锁反应,日志特别难读。我个人的习惯是:第一轮先make -j2,把整个流程跑通;确认没有错之后,再做一次清理,重新用make -j$(nproc)并行编译。这样看似多花了一点时间,但排错上手快得多。
3. configure/cmake里那些值得反复读的参数——藏在help输出里的定制入口
3.1 configure 不是魔法,它只是帮你“体检 + 排菜”
很多第一次接触源码编译的人,看到./configure这行命令就觉得神秘,总觉得它像某种黑盒魔法。其实它做的事情非常直白:检查当前系统里有哪些编译器、头文件、库,检查各种功能的支持情况,然后根据你的参数,生成一份适合当前环境的 Makefile 和配置文件头。
用生活化一点的话讲,configure 就像一个餐厅后厨在正式做菜之前做的“体检与排菜”:先看看食材有没有(依赖库)、锅能不能热(编译器)、菜单要按什么规格定(参数)。它本身不产出最终的可执行文件,真正的烹饪动作发生在后面的make阶段。
所以,configure 阶段输出的每一行检查信息都值得扫一眼。如果它明确告诉你“找不到 xxx 头文件”,这就说明某个依赖没就绪,后面make大概率也会失败。与其等编译到一半才崩,不如在 configure 阶段就把缺口补齐。
3.2 高频参数拆解:prefix、enable-disable 与 with-without
configure 家族的参数虽然多,但高频的就那么几类。我整理了一个常用速查表:
| 参数 | 作用 | 我常用的写法 |
|---|---|---|
--prefix | 设定安装根目录 | --prefix=/opt/demox |
--sysconfdir | 配置文件目录 | --sysconfdir=/etc/demox |
--enable-xxx | 启用某个功能模块 | --enable-extended-log |
--disable-xxx | 关闭某个功能模块 | --disable-doc-gen |
--with-xxx | 链接外部依赖库 | --with-libdept |
--without-xxx | 明确跳过某依赖 | --without-gui |
--prefix是我最看重的一个参数。它决定了编译出来的东西最终装到哪里。默认情况下很多项目会装到/usr/local,但如果你同时维护多个版本,就会遇到文件互相覆盖的麻烦。我会给每个版本指定独立的目录,比如/opt/demox-1.0、/opt/demox-1.1,再通过软链接管理当前生效版本。后面第 6 章讲回滚方案时你会看到这套做法的好处。
--enable和--disable是两个方向相反的开关,一个负责加,一个负责减。--with和--without则更像“指定外部依赖的绑定方式”。实际跑起来时它们常常混着用,比如 DemoX 我最后用了这样一套命令:
./configure \ --prefix=/opt/demox \ --sysconfdir=/etc/demox \ --enable-extended-log \ --disable-doc-gen \ --with-libdept这套参数表达得很清楚:装到独立目录、配置文件放在/etc/demox、开启扩展日志、关掉文档生成、链接上 libdept 库。每一个参数背后都有明确目的,而不是默认无脑回车。
3.3 环境变量的定制力量:CFLAGS、LDFLAGS 和运行时路径
除了命令行参数,configure 还会读取一系列环境变量来定制编译行为。这里面最常用的是三个:
CFLAGS:给 C 编译器传的编译选项,比如优化级别、调试级别、额外宏定义;CPPFLAGS:给 C 预处理器传的选项,主要用于指定额外头文件搜索路径;LDFLAGS:给链接器传的选项,用于指定额外库路径、链接器行为。
用法很简单,就在命令前加上变量赋值:
CFLAGS="-O2 -fstack-protector-strong -D_FORTIFY_SOURCE=2" \ LDFLAGS="-Wl,-rpath,/opt/demox/lib -Wl,-z,relro,-z,now" \ ./configure --prefix=/opt/demox很多人刚接触时会忽略-Wl,-rpath的含义。简单说,它是在编译时把“程序运行时去哪里找动态库”的路径写进最终产物里。如果你把程序装在一个非系统默认目录里,又不设置这个路径,运行时会提示找不到某个.so文件。这个问题在源码编译定制场景里特别容易踩,因为你自己指定的安装路径往往不在系统的默认搜索路径里。
3.4 除了 configure 家族,也要认识 cmake 和 meson
不是所有项目都用 configure 脚本。现在很多新项目选择 CMake 或者 Meson 作为构建系统。它们的定制入口长得不一样,但思路类似:你仍然是告诉构建系统“装到哪里、开什么功能、用什么依赖”。
CMake 项目常用的是-D参数,例如:
cmake -S . -B build \ -DCMAKE_INSTALL_PREFIX=/opt/demox \ -DBUILD_SHARED_LIBS=OFF \ -DENABLE_EXTENDED_LOG=ONMeson 项目习惯在meson setup阶段传入选项:
meson setup build --prefix=/opt/demox -Denable_extended_log=true遇到这类项目时,我的建议还是一样:先跑一遍帮助输出。CMake 项目一般用cmake -LH列出可配选项,Meson 项目用meson configure查看默认值。先搞清楚这个项目提供了哪些开关,再去定制,事半功倍。
4. 定制不只是加功能——裁剪、优化与私有补丁的管理
4.1 裁剪模块的三条原则:能关则关、能小则小、能静态则静态
很多人一听到定制,第一反应是“加功能”,但实际交付场景里,裁剪往往比增加更重要。一个软件包装进生产环境或容器镜像,每多一个模块,就意味着多一份攻击暴露面、多几 MB 体积、多一段启动时间。
我在裁剪 DemoX 时遵循了三条原则:
第一,不用的功能一律关掉。比如文档生成模块、交互式演示模块、图形界面前端,全都不需要,直接用--disable-xxx关掉。这里的排查思路是:先列出当前环境还需要什么能力,再对照--help输出看哪些模块对应这些能力,其余全部排除。
第二,能不动态就不动态,能静态就静态。静态链接的好处是产物自包含,部署到别的机器上不用担心动态库缺失。当然,静态链接也有代价,体积大、更新库时要重新编译,需要在具体场景里权衡。我的习惯是:对于部署目标不固定或环境受限的组件,倾向静态;对于长期运行且依赖更新频繁的服务,倾向动态。
第三,裁剪后立刻验证功能不缺失。这条最容易被人忽略。裁完编完,需要把核心功能完整跑一遍,确认关掉的东西确实不影响主线业务。我有一次自信满满地裁掉了一个“副模块”,结果那个模块承担着异步任务调度能力,只是在文档里写得不够明显。所以,裁剪一时爽,验证不能忘。
4.2 优化构建的实用选项,让产物更适合生产环境
编译阶段一次性加入合适的优化参数,对最终产物影响很大。以下是我常用的组合:
| 选项 | 作用 | 什么时候用 |
|---|---|---|
-O2 | 常规性能优化,保持较好的编译速度和可调试性 | 默认推荐 |
-O3 | 更激进的性能优化 | CPU 密集运算场景 |
-march=native | 按当前机器指令集优化 | 只部署在同一台机器 |
-fstack-protector-strong | 栈溢出保护 | 面向网络的程序 |
-D_FORTIFY_SOURCE=2 | 增强运行时安全检查 | 面向网络的程序 |
很多人看到-march=native就喜欢往命令里加,因为它理论上能让程序跑得更快。但我要泼一盆冷水:这个参数是拿“当前编译机的指令集”作为优化基准的,一旦产物被放到一台更老的 CPU 机器上,可能直接出现非法指令。除非你非常确信产物只在本机运行,否则别在生产构建里用它。
编译器选项之外,还有一个值得养成习惯的做法:安装时去符号表。符号表主要用于调试,但生产环境一般不需要,而且它会让二进制体积明显变大。多数项目提供make install-strip或在安装阶段自动调用 strip,建议生产构建都走这一步。体积变小,逆向分析的摩擦也更大,算是一举两得。
4.3 私有补丁的维护:版本漂移、冲突解决与重复应用
源码编译定制,很多时候不只是传几个参数那么简单。你可能要改源代码本身,加一段日志、修一个边界条件、对接内部系统。这时你就有了“私有补丁”。
我对私有补丁的维护流程是这样的:从原始源码版本拉一个干净的副本,建一条独立分支或独立目录,在上面做修改,然后用 git 导出 diff:
git diff > ../patches/demox-custom.patch这个补丁文件必须妥善保存,最好在文件头部注释清楚:适用于哪个版本、解决了什么问题、作者和日期。因为源码一旦升级到新版,原来的补丁大概率不能直接套用。
升级时的工作流大概是:
cd demox-new-version git apply ../patches/demox-custom.patch如果补丁因为版本差异产生冲突,git apply会告诉你冲突位置。这时候需要手动核对新旧代码的上下文,把修改重新落上去。这个过程有人觉得很烦,但它恰恰提醒你:源码在进化,你的定制也要跟着进化。补丁的维护成本,正是定制带来的真实成本。
我踩过一次比较深的坑:补丁文件没有标注适用版本,半年后升级时记不清它基于哪个版本生成,结果反复尝试,浪费了整整一天。从那之后,每个补丁文件头部都会写明适用版本和改动范围。
4.4 每次编译都要留“构建配方”,别靠记忆复现
源码编译这件事,最大的敌人是“时间”和“记忆”。三个月后,当你需要重新构建一个定制版本时,你大概率会忘记当时用了哪些 configure 参数、设了哪些环境变量、打了哪些补丁。
我的解决方案很简单:编译时顺手把完整命令记录到一个文本文件里。我习惯在源码目录旁边建一个build/目录,里面放build-cmd.txt,把当时执行的命令原样保存下来:
cat > build/build-cmd.txt << 'EOF' # 编译时间: 2024-xx-xx # 源码版本: DemoX 1.0 # 补丁文件: ../patches/demox-custom.patch CFLAGS="-O2 -fstack-protector-strong" \ ./configure --prefix=/opt/demox --enable-extended-log make -j4 make install-strip EOF这样做的价值,在几个月后甚至几年后会充分体现。你不需要重新回忆和推理,直接照方抓药,就能生成一个与上次几乎一致的构建。所谓“可复现的构建”,就是从这种小事开始的。
5. 编译失败后怎么定位——我的排查链路和保存的救命日志
5.1 错误分三类,先判断方向再动手
编译失败时,新手最常见的反应是盯着满屏的英文错误发呆,然后整段复制去搜索引擎碰运气。这既低效又容易误导。我更习惯先把错误归个类,明确方向后再动手。
| 错误类型 | 典型表现 | 常见原因 | 我的排查方向 |
|---|---|---|---|
| 环境类 | 找不到头文件、找不到库 | 缺开发包、路径不对 | 先定位是哪个文件缺失,再装对应包或加路径 |
| 代码类 | 语法错误、类型不匹配 | 编译器版本差异、源码有 bug | 搜索错误文本,查官方提交记录 |
| 资源类 | 编译进程被杀、磁盘报错 | 内存不足、磁盘空间耗尽 | 看系统日志、查看可用内存和磁盘 |
这个分类看起来朴素,但非常管用。环境类错误基本是“缺东西”,补上就好;代码类错误往往需要查上下文;资源类错误则跟代码无关,纯粹是你的机器撑不住了。方向不对,再多努力都是白费。
5.2 从日志尾部往上看,是读懂编译日志的关键习惯
编译日志动辄上千行,从头逐行读,读到一半眼睛已经花了。正确的姿势是:把日志保存下来,先搜错误行,从错误出现的起点位置往上找真正的第一个错误。
我的做法是:
make 2>&1 | tee build.log grep -n "error:" build.log | tail -30error:关键字后面的内容往往才是关键。但要注意,编译器经常会因为第一个错误导致后续连锁报错,所以如果你看到一堆错误,先回到最上面那个错误去处理,而不是在中间某个看似更“复杂”的错误上纠结。
编译日志还有一个价值:它是很好的排查记录。很多问题在搜索引擎里搜不到完全一样的,但把日志里的关键错误片段提取出来去搜,往往能命中官方 issue 或社区讨论。保存日志这个习惯,等于给自己留了排查素材。
5.3 几个我真实踩过的编译错误实例
缺头文件是最常见的。比如源码里写#include <dept.h>,configure 时也通过了,但编译到某个文件时突然报找不到。这种情况多半是某个子模块依赖了额外的开发包,而这个依赖没有被 configure 的检查覆盖到。解决办法就是找到那个头文件属于哪个包,安装对应的-dev包,再重新编译。千万别想着手动去复制头文件,那会埋下更大的坑。
链接阶段的报错也很典型,特征是大量undefined reference to 'xxx'。这里经常出问题的不是“缺链接库”,而是“链接顺序不对”。在传统链接器里,依赖方要放在被依赖方的前面,顺序反了符号就找不回来。如果你在用较新工具链,可能对顺序不敏感;但遇到老式链接器,这个坑很酸爽。我的解决办法是,把代码来自的目标文件放在前面,库文件放在后面,必要时多写几遍库名。
内存不足导致的失败则是另一种体验。日志中间会出现类似cc1: out of memory的字样,或者编译进程直接消失,最终 make 报错退出。这种情况跟代码没什么关系,是你的编译并行任务太多,内存扛不住了。降一下make -j的并行度,或者临时加点 swap,通常就能解决。磁盘满则是另一个常见资源问题,特征更直接:No space left on device。看到这样的提示,第一个念头是清出空间,而不是去检查代码。
5.4 make distclean 和干净构建目录,救了我很多次
源码编译过程中,最让人难受的挫败感来自“明明改了配置,行为却没变”。很多时候这是因为 Makefile 和中间文件已经根据旧的配置生成了,你再改 configure 参数重新执行,某些旧的.o文件还会被复用,导致结果混乱。
这时候make distclean就有用了。它会清除 configure 生成的文件、Makefile、目标文件以及各种编译中间产物,让源码目录回到“刚解压出来”的干净状态。之后再重新跑 configure,所有配置都会从零开始生效。
我自己的习惯是,源码目录始终保留一个干净副本。一旦遇到难以定位的诡异问题,直接从干净副本复制出一份来重新操作,彻底抛开之前的残留状态。这个习惯帮我省了很多排查时间,也让我敢于大胆尝试各种参数组合,反正搞坏了换一份就是。
6. 收尾工作:安装、验证与回滚习惯
6.1 先试装到临时目录,别直接把产物扔进系统目录
编译成功后,最忌讳的事情就是直接make install,把产物一股脑装进/usr/local或系统默认路径。因为一旦装了,想再干净地撤回来就没那么容易了。
我每次编译完都会把安装目标指到一个临时目录里先行试装。具体做法有两种:
一种是在 configure 阶段就把--prefix指向一个独立目录,比如/opt/demox-stage,安装后直接到这个目录下验证,验证通过再整体挪到正式目录。
另一种是保留正式路径的配置,但在执行安装时用临时根目录:
make install DESTDIR=/tmp/demox-root这样会把所有文件都装到/tmp/demox-root下面,目录结构与你最终想要的一致。检查无误后,再把/tmp/demox-root/opt/demox迁移到正式路径。这套流程能让你“先试用、后生效”,对正在运行的服务来说特别重要。
6.2 验证内容不止是“能运行”三个字
很多人试运行一下觉得能跑,就认为万事大吉。真正负责的做法是,做一个标准化验证清单,逐项确认。
我的验证清单包括:
- 执行
./demox --version,确认版本号符合预期; - 执行
ldd /opt/demox/bin/demox,检查动态库链接是否按预期指向你的目录; - 对二进制算一个
sha256sum,记录产物摘要,后续回滚时比对; - 检查配置文件是否从
--sysconfdir指定的目录读取; - 把核心功能走一遍,确认裁剪和补丁没有破坏主线行为。
前四项花不到五分钟,但能挡住绝大多数因为路径、链接、配置目录不对引起的“假成功”。尤其是ldd这一步,很多自编译的程序出现运行时报错,问题就出在动态库路径没对,运行时会找到旧版本的系统库,行为与你的定制版本完全不符。
6.3 回滚方案:旧版本目录、软链接与切换命令
源码编译定制一次的成本不低,所以一旦新版本出问题,你要有快速切回旧版本的能力,而不是重新编译一遍旧代码。
我的做法是“版本目录 + 软链接”的搭配。旧版本装在/opt/demox-1.0,新版本装在/opt/demox-1.1,然后用一个软链/opt/demox指向当前生效版本:
ln -sfn /opt/demox-1.1 /opt/demox切换回旧版本时,只需要把软链指回去:
ln -sfn /opt/demox-1.0 /opt/demox系统服务和调用方统一使用/opt/demox/bin/demox这个路径访问,它们不会感知到版本切换,只要软链变了,生效版本就变了。这套方案特别适合需要频繁定制和迭代的内部工具,既灵活又安全。如果你用 systemd 管理服务,切换后记得重启服务让进程重新加载路径。
6.4 把定制产物和记录归档,给未来的自己留一条后路
最后一步,也是很多人最不愿意做的事:归档。编译完成、验证通过、部署上线,事情看起来结束了,但如果没有任何记录,三个月后同一个源码包、同一批参数,你未必能原样复现。
我在每个定制版本交付时,都会打一个归档目录,里面放四样东西:
- 当前版本的源码副本(或明确记录源码包的下载地址和校验值);
- 构建命令记录文件;
- 所有私有补丁文件;
- 二进制产物的校验摘要和验证记录。
这类归档平时看觉得多余,但真正遇到“这个版本是什么时候编的”“这个二进制为什么和源码对不上”这种问题时,它就是救命稻草。我做内部工具这几年,靠这份归档解决过不少棘手的回溯问题,省下的时间远超当初归档花费的几分钟。
源码编译与定制,说到底就是一套“把不确定变成确定”的流程:确定环境、确定参数、确定模块、确定验证结果。把这套流程养成习惯之后,你会发现很多看似麻烦的问题,根本不需要求人帮忙,自己拿着源码就能解决。