JEP 138:基于 Autoconf 的构建系统
概括
引入 autoconf(./configure
-style)构建设置,重构 Makefile 以删除递归,并利用JEP 139:增强 javac 以提高构建速度。
目标
我们试图实现的最高目标是:
- 从根本上提高构建速度
- 简化构建系统源代码(Makefile_等_)
- 简化开发人员的工作
- 获得准确且可重复的构建输出
- 简化构建机器配置(JPRT_等_)
我们将通过四个子项目来实现这些目标,这些项目或多或少紧密地交织在一起。
- 更新Makefile结构
- 使用autoconf(配置脚本)
- 添加并行 Java 编译支持
- 使 Java 构建增量
我们需要正确 理解现有的开发人员工作流程,以便能够最大限度地减少这一变化对每个人的影响。
该项目是改进 JDK 构建基础设施的更大努力的一部分。我们预计未来的步骤将密切关注该项目。这些步骤之间的区别有些随意,只是为了快速从改进 JDK 构建基础结构的第一个工作中受益。
非目标
由于我们将使用新的结构更新 Makefile,因此我们希望在未来解决的几个问题可能会因为更新而自行发挥作用。然而,我们在这个项目中并没有专门解决这些问题,我们不会测试它们,也不保证它们能够正常工作。 (但是,我们将尽力确保不会破坏任何有效的东西。)这些问题包括:
- 轻松移植到新平台
- 使得在没有网络连接的情况下进行JDK开发成为可能
- 为交叉编译提供适当的支持,包括在 64 位主机上编译 32 位二进制文件
- 改进警告的处理
我们也不会解决计划在未来步骤中解决的问题。 (但是,其中一些工作将为未来的改进奠定基础。)这些问题包括:
- 加速热点编译
- 升级编译器
- 支持IDE项目
- 重新考虑源丢弃机制
成功指标
构建简单性
鉴于所有先决条件均已具备,构建应通过以下方式完成:
- 从 Mercurial 存储库获 取源代码
./configure
make
构建速度
构建速度取决于硬件因素,并且改进会有所不同。我们的目标是在 8 路机器上的 Linux 上进行编译。这样的话,我们改进后构建JDK所花费的时间最多应该是当前时间的33%。 (通常这意味着从约 15 分钟缩短到约 5 分钟,或更短)。一个延伸目标是 JDK 的构建时间最多应为 20%(约 3 分钟)。
请注意,这仅适用于 JDK。它不包括构建 Hotspot,也不包括创建 Javadoc。
生成文件清理
JDK 中的所有小型 (<3 kB) 递归 Makefile(不包括 Hotspot)均应删除,并将功能收集到中央 Makefile 中。 (少量 Makefile 本身并不是目标,但是,将代码放在一个(或几个)位置有助于概览和理解。)
动机
构建完整的 JDK 不必要地缓慢。这给开发人员和构建系统带来了额外的负担。因此,开发人员只检查并构建部分源代码,因为整个产品的构建时间太长。
构建系统的当前实现有超过 350 个最小的递归 Makefile 分散在产品各处,因此很难对构建系统进行更改。当前的解决方案有时还需要更新 Makefiles 只是为了添加新的源文件或目录;不需要这样做。
今天,构建系统是通过使用多个环境变量来配置的。这与用于./configure
设置 构建系统的流行方法形成对比。除了熟悉之外,这比环境变量还有几个好处。检查配置的参数——拼写错误的参数会导致错误,而拼写错误的环境变量将被忽略。./configure --help
显示可用参数的列表,而几乎不可能获得影响当前构建系统的所有环境变量的完整列表。
描述
这些更改不会导致构建的产品发生任何变化;它们只影响内部开发过程。
更新Makefile结构
背景
将旧的 Makefile 更新为新的、简化的架构将是此处描述的所有其他工作的基础。
执行
当前每个目录一个文件的递归 makefile 风格将被删除。相反,makefile 将通过递归查看源代码目录来发现要编译的文件。不应编译的文件将被列为显式排除项。这将需要能够使用新的并行 javac 编译器。
多个子系统通用的代码将存储在新的顶级目录“common/make”中。设计思想是这些通用文件将提供一个具有辅助函数的库,以便每个子系统的 Makefile 可以尽可能简单、干净地编写。如果这些库允许提高每个子系统 Makefile 的简单性,我们将接受更高的代码复杂性。
由于 良好的编码实践不会由 Makefile 语法自动强制执行,因此我们将格外小心,以确保编写正确且可读的代码。
作为更新的一部分,我们将生成一份文档,描述我们发现有用并在重写过程中遵循的编码指南,以指导 Makefile 的未来更改。我们还将生成一个描述 Makefile 整体架构的文档。
除了构建生成的二进制文件或构建二进制文件的异常变体之外,Makefile 还执行其他操作。其中一些目标看起来很神秘并且不再使用。如果所有利益相关者都同意,那么我们不会将这些目标移植到新系统中。这是我们迄今为止正在考虑删除的功能列表:
- (目前为空)
新旧混合
可能可以保留旧的 Makefile 系统,与新重写的 Makefile 系统并行,因此我们在一段时间内有两种构建产品的方法(新的和旧的)。这并不是真正可取的,因为它有导致代码重复和普遍混乱的风险,并且会让我们错过删除旧东西的好处。然而,保留旧系统,或者有一种简单的方法来恢复旧系统,将有助于我们管理所涉及的风险。
过渡
大多数开发人员不会与实际的 makefile 进行太多交互,因此工作流程不会有任何大的变化。
以前,有时每当添加或删除源文件或目录时都需要更新 Makefile。这将不再需要,并且需要将其传达给所有开发人员。
想要更改实际 Makefile 的开发人员需要了解所使用的整体设计和编码原则。这将被记录下来,但需要传达这些文件 的存在。
使用autoconf(配置脚本)
背景
autoconf 背后的基本思想是,一个简单的界面将处理用户系统配置和 Makefile 要求之间的“粘合”问题。这个接口就是./configure
shell脚本。
因此,使用 autoconf 有两个方面:创建和使用./configure
脚本。配置脚本由 autoconf 工具根据configure.ac
(以及随附的帮助程序文件)中的源代码生成,该脚本是使用 M4 宏编写的。从该源代码configure
生成一个 shell 脚本。该脚本(即使已生成)已签入存储库。每当configure.ac
源代码发生更改时,configure
都需要重新生成脚本并在存储库中更新。要重新生成configure
,需要在系统上安装 autoconf 工具。
然而,典型的用户不需要这样做。既然configure
签到了,他/她只需要运行即可./configure
。为此,不需要 autoconf 工具。这会产生一个config.spec
采用 Makefile 语法的文件,该文件确定构建详细信息,并包含在 Makefile 中。
自动配置的实现
配置脚本有三个主要任务:
- 确定所有构建依赖项都存在。
- 分析平台之间的已知差异并确定哪些适用于当前 情况。
- 应用用户给出的参数来专门化构建。
尽管 autoconf 框架有助于完成所有这些任务,但它们都必须使用有关 OpenJDK 细节的知识进行显式编码。这意味着我们需要清楚我们实际拥有哪些构建依赖项、需要确定哪些差异以及用户可以通过哪些方式影响构建结果。
构建依赖关系之前已在 README 文件中进行了描述。
已知的差异之前已编码在 Makefile 中,或者已在“常识”中。
历史上,用户影响是通过使用环境变量来实现的,并且对这些变量的检查是在 Makefile 中进行的。
配置脚本可以像旧 Makefile 的“包装器”一样工作,并在 config.spec 中设置与 Makefile 所使用的相同的变量。在这种情况下,对于 Makefile 来说,变量来自配置脚本而不是用户几乎是透明的。然而,在许多情况下,更好的解决方案可能是输出更“干净”的变量,并重写 Makefile 的相应部分。
法律地位
作为使用 autoconf 的一部分,我们需要将 autoconf 中的三个文件包含在 JDK 8 源存储库中。这三个文件是pkg.m4
、config.guess
和config.sub
。已请求将这些文件包含在 OpenJDK 中的法律许可。我们认为这应该不是问题,因为 autoconf 许可证是明确编写的以支持此用例(基本上允许我们以任何我们喜欢的方式分发它们,只要它们用作配置脚本的一部分)。
过渡
当前构建 OpenJDK 的工作流程基本上是:
- 从存储库中检索源代码