4

精简版:我正在尝试从 gradle 任务中调用 jpackage,但 ToolProvider 返回 null(或者更好的是失败的 Optional)。AdoptOpenJDK 14.0.0(sdkman 标识符 14.0.0.hs-adpt)和 Java.net(我认为那是 Oracle OpenJDK!?)14.0.1(sdkman 标识符 14.0.1-open)就是这种情况。我正在使用 Gradle 6.3(但这感觉不像是 gradle 问题)。

长版:我正在关注jpackage 上的这个演讲,在 12:12 显示了从构建工具调用 jpackage 的代码。(官方jpackage页面还提到:除了命令行界面,jpackage可以通过名为“jpackage”的ToolProvider API(java.util.spi.ToolProvider)访问。

仍然是我的(Kotlin)代码(位于 buildSrc/src/main/kotlin 中)

object JPackage {
  fun buildInstaller( 
    ...
  ): Int {
    val jpackageTool: ToolProvider = ToolProvider.findFirst("jpackage").orElseThrow {
      val javaVersion: String = System.getProperty("java.version")
      IllegalStateException(
        "jpackage not found (expected JDK version: 14 or above, detected: $javaVersion)"
      )
    }
    val arguments: Array<String> = ...
    return jpackageTool.run(System.out, System.err, *arguments)
  }
}

由新的 Gradle 任务调用

tasks {
  register("buildInstaller") {
    JPackage.buildInstaller(
      ...
    )
    dependsOn("build")
  }
}

未能说明

> Could not create task ':buildInstaller'.
> jpackage not found (expected JDK version: 14 or above, detected: 14.0.1)

我应该补充一点,从命令行调用 jpackage 没有问题。

更新:我证实这与 Kotlin 或 Gradle 无关。这个基本的 Java-14 程序产生了同样的异常:

public class Main {
    public static void main(String[] args) {
        java.util.spi.ToolProvider.findFirst("jpackage").orElseThrow(() -> {
            String javaVersion = System.getProperty("java.version");
            return new IllegalStateException("jpackage not found (expected JDK version: 14 or above, detected: " + javaVersion + ")");
        });
        System.out.println("success");
    }
}

解决方案:(结合 Slaw 的回答)由于 jpackage 处于“孵化”状态,因此对于我的非模块化应用程序来说不容易使用,我决定通过创建一个新进程来调用它:

object JPackage {
  fun buildInstaller( 
    ...
  ): Int {
    val arguments: Array<String> = ...
    return execJpackageViaRuntime(arguments)
  }

  private fun execJpackageViaRuntime(arguments: Array<String>): Int {
    val cmdAndArgs = ArrayList<String>(arguments.size+1).let {
      it.add("jpackage")
      it.addAll(arguments)
      it.toTypedArray()
    }
    return try {
      val process: Process = Runtime.getRuntime().exec(cmdAndArgs)
      process.waitFor(3L, TimeUnit.MINUTES)
      return process.exitValue()
    } catch (e: Exception) {
      1
    }
  }
}

我的任务定义如下所示:

tasks {
    register("buildInstaller") {
        dependsOn("build")

        doLast {
            if (JavaVersion.current() < JavaVersion.VERSION_14) {
                throw GradleException("Require Java 14+ to run 'jpackage' (currently ${JavaVersion.current()})")
            }
            JPackage.buildInstaller(
                ...
            )
        }
    }
}

我无法从 IntelliJ 中执行任务,因为它似乎使用它本身捆绑的 JDK11 调用 Gradle,但至少 IntelliJ 可以自己编译构建脚本(因为版本检查在 doLast 块中,而不是直接在寄存器块)。或者,您可以更改 IntelliJ 用来调用 Gradle 的 JDK,向下滚动到 Slaw 在他的答案下的评论,看看如何。

顺便说一句:我很确定Gradle 版本 6.3是这个工作的硬性要求,因为它是第一个与 Java 14 兼容的 Gradle 版本。

4

1 回答 1

3

该问题与JEP 11: Incubator Modules有关。该 JEP 声明:

孵化器模块jdk.incubator.由其模块名称中的前缀标识,无论模块是导出孵化 API 还是包含孵化工具。

在 Java 14 中,可能在接下来的几个版本中,jpackage 工具包含在一个名为jdk.incubator.jpackage. 从名称的前缀中,我们可以看出该模块是一个孵化器模块。同一个 JEP 后来指出:

孵化器模块是标准 JDK 构建生成的 JDK 运行时映像的一部分。但是,默认情况下,孵化器模块不会为类路径上的应用程序解析。此外,默认情况下,孵化器模块参与类路径或模块路径上的应用程序的服务绑定[强调添加]。

类路径上的应用程序必须使用--add-modules命令行选项来请求解析孵化器模块。作为模块开发的应用程序可以直接指定requiresrequires transitive依赖于孵化器模块。(一些孵化器模块,例如那些提供命令行工具的模块,可能会放弃导出任何包,而是提供服务实现,以便可以以编程方式访问工具 [原文如此]。通常不建议要求一个不导出包的模块,但在这种情况下,为了解析孵化器模块,并让其服务提供者参与服务绑定,这是必要的。)

由于孵化器模块不参与服务绑定,因此该jdk.incubator.jpackage模块不会在运行时解析。不幸的是,这意味着无法通过以下方式找到该工具,ToolProvider除非您:

  1. --add-modules jdk.incubator.jpackage使用,启动 Java 进程
  2. 或者使您的代码模块化并requires jdk.incubator.jpackage;在模块信息文件中包含一个指令。

由于您试图从 Gradle 任务中调用 jpackage,我认为选项二是不可行的。然而,第一种选择似乎是可行的。执行 Gradle 时,您可以指定适当的org.gradle.jvmargs系统属性。例如:

./gradlew "-Dorg.gradle.jvmargs=--add-modules=jdk.incubator.jpackage" buildInstaller

您不必每次都在命令行中键入它。查看此 Gradle 文档,了解您可以在哪里定义该系统属性。可能也可以通过您的 IDE 做某事,但不确定。

也就是说,也应该可以jpackage使用Exec任务作为另一个进程调用。例如:

tasks {
    val build by existing

    register<Exec>("buildInstaller") {
        if (JavaVersion.current() < JavaVersion.VERSION_14) {
            throw GradleException("Require Java 14+ to run 'jpackage'")
        }
        dependsOn(build)

        executable = "jpackage"
        args(/* your args */)
    }
}
于 2020-04-19T19:50:44.710 回答