从信息论的角度考虑这个问题,编译器的三阶段编译的第一阶段不会产生编译器。它产生了一个需要实验验证的假设。一个好的编译器分发包的标志是,它将生成开箱即用且无需系统管理员或编译器开发人员进一步工作的分发版本的工作编译器,并具有该版本编译器的所需功能.
实现这一目标并不简单。考虑目标环境中的变量。
- 目标操作系统品牌
- 操作系统版本
- 操作系统设置
- Shell 环境变量
- 可包含的标头
- 可用于链接的库
- 传递给构建过程的设置
- 目标处理单元的架构
- 处理单元数
- 总线架构
- 执行模型的其他特征
- 编译器开发者可能犯的错误
- 构建编译器的人可能会犯的错误
在 GNU 编译器工具集和许多 tarball 发行版中,“configure”程序试图生成一个构建配置,以尽可能多地适应这些配置。 configure 没有错误或警告的完成并不能保证编译器将正常工作。此外,对于这个问题,更重要的是,构建的完成也不能保证。
新构建的编译器可能适用于 HelloWorld.c,但不适用于名为“智能星际控制和采集系统”的多项目、多存储库软件集合中的一千个源文件集合。
第二阶段和第三阶段是检查至少一些编译器功能的合理尝试,因为编译器源代码本身很方便,并且对刚刚构建的假设工作编译器有相当多的要求。
重要的是要了解第一阶段的结果和第二阶段的结果不会匹配。它们的可执行文件和其他构建工件是来自两个不同编译器的结果。第一阶段的结果是使用在“PATH”变量中列出的目录之一中找到的任何构建系统编译的,以编译 C 和 C++ 源代码。第二阶段的结果是使用假设工作的新编译器编译的。有趣的概率考虑是这样的:
如果使用第一阶段的结果再次编译编译器的结果与使用第二阶段的结果第三次编译编译器的结果完全相同,那么至少对于编译器源代码所需的功能来说,两者都可能是正确的。
最后一句话可能需要重读十几遍。它其实是一个简单的想法,但是动词compile和名词编译器的冗余可以打结,需要几分钟才能解开并能够重新打结。源、目标和执行的动作具有相同的语言根,不止一次,而是三次。
截至 2020 年 5 月 25 日,编译器的构建说明说明了相反的情况,这更容易理解,但只是轶事,没有深入了解三个阶段很重要的原因。
如果 stage2 和 stage3 比较失败,这通常表明 stage2 编译器编译的 GCC 不正确,因此是一个潜在的严重错误,您应该调查并报告。
如果我们从可靠性评估、测试优先、极限编程、6-Sigma 或全面质量管理的角度考虑 C/C++ 开发,C/C++ 开发环境中的哪个组件必须比编译器更可靠?不多。甚至自早期以来 GNU 编译器包一直在使用的编译器的三阶段引导也是一个合理但并非详尽的测试。这就是为什么包中有额外的测试。
从持续集成的角度来看,在编译和部署新编译器之前和之后,应该对即将使用新编译器的人开发的整个软件进行测试。这是确保新编译器不会破坏构建的最便捷方式。
在三个可靠性检查点之间,大多数人都感到满意。
- 确保编译器始终如一地编译自己
- 编译器开发人员在其发行版中添加的其他测试
- 开发者或系统管理员源代码域未被升级破坏
在数学方面,实际上不可能用地球上可用的硅和碳对编译器进行详尽的测试。 C++ 语言抽象中的递归界限(除其他外)是无限的,因此测试源代码的每个排列所需的芯片或时间位置实际上并不存在。在碳方面,没有任何人可以腾出必要的时间来充分研究源代码,以保证编译器源代码不会以某种方式施加一些有限的限制。”
三个级别的检查,其中只有一个是三阶段引导过程,对我们大多数人来说可能就足够了。
三阶段编译的另一个好处是新编译器使用新编译器进行编译,新编译器在速度或资源消耗方面可能更好,可能两者兼而有之。