Ohhnews

分类导航

$ cd ..
foojay原文

Jenesis 问世:你早已写好了构建脚本

#jenesis#java#构建工具#模块系统#ai编码代理

过去十年里,Java 发生了相当大的变化。这门语言获得了模块、记录类和虚拟线程,平台也改为每六个月发布一次。然而,我们构建 Java 应用的方式几乎没有改变。pom.xml 或 build.gradle.kts 仍然在重述代码已经声明的内容,比如项目名称、依赖项和主类。除此之外,每超出默认行为一步,就要增加一块配置,通常还要再加一个插件,直到构建变成第二个需要与第一个程序一起维护的程序。而且,在编译一行代码之前,wrapper 还要先下载构建工具本身——就 Gradle 而言,这需要 152 MB。本文介绍 Jenesis,这是我为从不同角度解决这一问题而编写的构建工具。

一个似乎停滞的领域

Maven 4 即将到来,经过多年的工作,它的发布将值得欢迎。然而,它并没有解决我上面描述的那些弱点。构建仍然是项目的第二份描述,需要手工与代码保持同步,而且每次构建仍然会运行完整的测试套件。即使对于 Java 模块系统,Maven 4 也为测试新增了一个自己的文件,名为 module-info-patch.maven,它使用的语法只有 Maven 才能理解。这个文件与 Java 平台已经提供的模块描述符并列存在。

近年来大多数替代构建工具用 Java、Kotlin 或其他语言取代 XML,但底层模型保持不变。这种“Maven,但用 Java 写”的方式写起来更方便,但它仍然是一个描述第一个程序的独立程序。

我以前也遇到过类似的情况。当我开始开发 Byte Buddy 时,Java 已经提供了好几个用于在运行时生成代码的库,再添加一个 API 更友好的库几乎不值得。相反,Byte Buddy 以不同的方式看待这个问题:它允许用普通 Java 类型来定义类,并委托给普通方法,而不需要任何字节码知识。对于构建,我在 Jenesis 中尝试类似的事情,不同之处在于,项目根本不需要构建文件。

为什么反馈循环比以往更重要

我预计,语言和编程平台将越来越多地以其将变更转化为结果的速度,以及其适应能力来衡量。编码智能体是这一发展的重要组成部分。智能体会一轮又一轮地编辑、构建并检查结果,而且它常常在虚拟机或其他云环境中这样做,这些环境从全新的检出开始。因此,平台需要在这些工作流的背景下演进,其中也包括它的构建工具。

在这方面,Java 处于有利位置。现代 JVM 执行代码的速度不亚于原生编译语言,同一份字节码可以在任何平台上运行,编译一次变更只需片刻。然而,构建却跟不上。一台全新的机器首先需要下载构建工具及其插件,而且每一轮都会重复此前已经完成的工作。Jenesis 试图让构建 Java 应用变得轻而易举,以至于只需要 JDK 25 或更新版本以及一份检出。

从代码中读取构建

Jenesis 从代码中推导构建。借助 Java 模块系统,每个模块都已经在描述符中声明了其名称和依赖要求,而该描述符由 javac 编译并校验。Jenesis 将这个 module-info.java 视为构建文件,而描述符无法表达的元数据则添加到其 Javadoc 中。例如,考虑以下模块:

/**
 * @jenesis.release 25
 * @jenesis.main demo.app.Main
 */
module demo.app {
    requires org.slf4j;
    requires com.fasterxml.jackson.databind;
    requires info.picocli;
    requires static org.jspecify;
}

这个描述符已经描述了一个完整的项目。在启动项目时,人们通常希望使用每个依赖项的最新稳定版本,因此 Jenesis 可以自行确定这些版本。运行 java build/jenesis/Make.java pin 会将每个版本连同已解析构件的校验和写入描述符。

Jenesis 本身就是 2.3 MB 的纯 Java 源代码,它被提交到项目的 build/jenesis/ 文件夹中,并由 JDK 启动,不需要下载任何 wrapper 或发行包:

curl -fsSL https://get.jenesis.build | bash
java build/jenesis/Make.java

试用 Jenesis 不需要将项目模块化。由于 Maven Central 上的每个构件都在 POM 中描述其依赖项,Jenesis 无论如何都需要一个 POM 解析器,因此它同样可以读取项目的 pom.xml。将 Jenesis 安装到现有项目中是试用它最快的方式。

缓存与隔离

每个构建步骤都以其输入的哈希为键,因此 Jenesis 会跳过任何输入未发生变化的步骤,并且只运行能够触及已更改类的测试。这也使得在机器之间共享构建结果变得轻而易举,因为只需一个属性即可将缓存指向共享文件夹或缓存服务器。这样一来,全新的 CI 运行器或智能体的虚拟机就可以复用别处已经构建好的内容。

当智能体或人类克隆一个不熟悉的仓库来构建它、运行其测试或复现 bug 时,构建过程也会执行没有人审查过的代码。出于这个原因,Jenesis 可以在一个一次性容器中运行每次构建以及它启动的每个程序,该容器无法访问主机的 home 目录或环境。一旦在用户自己的配置中启用,这就会应用于每次构建,而项目无法选择退出。因此,AI 智能体和人类都可以安全地使用未知的 Java 项目,而无需采取任何额外的预防措施。

Jenesis 是如何诞生的

我在 2024 年开始开发 Jenesis,并手工开发了它的核心。然而,在很长一段时间里,我看不到它有任何竞争之道。成熟的构建工具提供了丰富的插件生态系统,几乎可以满足任何需求,而凭我手头可用的时间,独自与之匹敌是遥不可及的。

不过,我确实有大量规划架构的草图,基于这些草图,编码智能体帮助我让 Jenesis 冲过了终点线。特别是,智能体承担了任何一位维护者都没有时间完成的工作。它们用 Jenesis 构建了无数真实世界的项目,并总结遇到的 bug,这些报告向我显示了架构在哪里站不住脚,从而让我能够重新调整。这样一来,Jenesis 达到了一个成熟度水平;在这些工具能够接管这类繁重琐碎工作之前,开源项目不可能在如此短的时间内达到这种水平。今天,我认为 Jenesis 与其他开源方案一样成熟。

更多特性

除了本文描述的内容之外,Jenesis 还提供以下特性,每项特性都由 69 个演示 中的一个小型可运行项目展示:

  • 测试模块会命名它所测试的模块,Jenesis 会据此推断 JUnit 引擎(演示)。
  • 项目中的模块通过模块名相互引用,不需要根文件(演示)。
  • META-INF/versions/25/ 下的源文件无需进一步配置即可生成多版本 jar(演示)。
  • Kotlin、Scala 和 Groovy 与 Java 在同一个模块内编译(Kotlin、Scala、Groovy)。
  • XML、Protobuf 和 Avro 模式作为构建的一部分被编译为 Java(演示)。
  • 只运行能够触及已更改类的测试,并且构建结果可以跨机器共享(测试、缓存)。
  • 无需任何插件即可验证 SHA-256 固定值、OpenPGP 签名和 Sigstore 身份(固定、OpenPGP、Sigstore)。
  • 构建过程以及构建出的程序可以在无法访问主机机密的容器中运行(演示)。
  • 不同构建之间的 jar 字节级一致,并且包含 CycloneDX SBOM(可复现、SBOM)。
  • 同一库的两个版本可以共存于模块层中,而不是被 shaded 打包合并(演示)。
  • jlink 运行时、jpackage 镜像或容器镜像各自只需要一行配置(演示)。
  • Checkstyle、PMD 和 SpotBugs 通过其配置文件启用,同时也支持 Error Prone、JaCoCo 和 PIT(代码检查工具、Error Prone、覆盖率、PIT)。
  • 自定义构建步骤以小型 Java 模块编写,具备类型、IDE 支持和重构能力(演示)。## 模块并没有失败,失败的是构建工具

当 Java 模块系统发布时,许多项目得出结论:模块不值得这么麻烦。在我看来,这些麻烦大多是由那些把 module-info.java 当作事后补充的构建工具造成的。在 JavaZone 2026 上,我就这个主题,以及构建工具如果从模块描述符出发会是什么样子,做了一场演讲:

Jenesis 网站并排比较了 Jenesis、Maven、Gradle 和 Bazel 中的十四种常见构建,入门指南则带你完成第一次构建。源代码基于 Apache License 2.0 在 GitHub 上提供。本文介绍的是 0.15.3 版本,欢迎通过 GitHub issue 或发送电子邮件至 [电子邮件受保护] 提供你在自己项目中使用 Jenesis 的任何反馈。

分享此页面

发现错误,或有内容要补充?在 GitHub 上编辑此页面

[LOADING...]
作者:

Rafael Winterhalter

Rafael Winterhalter 是一位来自德国的全栈软件顾问,在挪威奥斯陆生活和工作,专攻 Java 平台。他是 Byte Buddy 的作者,这是一个 JVM 运行时代码生成器,曾获得 Duke's Choice 奖和 Groundbreaker……

相关文章

Gradle [LOADING...]
[LOADING...][LOADING...] 2 位作者 2025年11月3日 3,761 次浏览

Foojay Podcast #81:Maven 4 - Java 构建自动化的未来

Gradle
Gradle [LOADING...]
[LOADING...]Nicolas Frankel 2023年8月15日 8,380 次浏览

我对 Gradle 的最终看法(对比 Maven)

Gradle
Java 核心 [LOADING...]
[LOADING...]Christian Stein 2021年2月24日 4,708 次浏览

模块、模块,无处不在,尽在 Maven Central!

Java 核心
DevOps [LOADING...]
[LOADING...]Brian Vermeer 2023年3月17日 6,828 次浏览

如何使用 Maven 和 Gradle 在 Java 中创建 SBOM

DevOps

参与讨论

Gradle [LOADING...]
[LOADING...][LOADING...] 2 位作者 2025年11月3日 3,761 次浏览

Foojay Podcast #81:Maven 4 - Java 构建自动化的未来

Gradle
Gradle [LOADING...]
[LOADING...]Nicolas Frankel 2023年8月15日 8,380 次浏览

我对 Gradle 的最终看法(对比 Maven)

Gradle
Java 核心 [LOADING...]
[LOADING...]Christian Stein 2021年2月24日 4,708 次浏览

模块、模块,无处不在,尽在 Maven Central!

Java 核心
DevOps [LOADING...]
[LOADING...]Brian Vermeer 2023年3月17日 6,828 次浏览

如何使用 Maven 和 Gradle 在 Java 中创建 SBOM

DevOps