java - 无法通过 ToolProvider 找到 jpackage

标签 java kotlin gradle java-14 jpackage

短版:我正在尝试从 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 问题)。

长版:我正在关注 talk on jpackage ,在 12:12 显示从构建工具调用 jpackage 的代码。 (official jpackage page 还提到:除了命令行界面,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");
    }
}

解决方案: (结合斯劳的回答)
由于 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 block 中,而不是直接在寄存器 block )。或者,您可以更改 IntelliJ 用来调用 Gradle 的 JDK,向下滚动到 Slaw 在他的答案下的评论,看看如何。

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

最佳答案

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

An incubator module is identified by the jdk.incubator. prefix in its module name, whether the module exports an incubating API or contains an incubating tool.


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

Incubator modules are part of the JDK run-time image produced by the standard JDK build. However, by default, incubator modules are not resolved for applications on the class path. Furthermore, by default, incubator modules do not participate in service binding for applications on the class path or the module path [emphasis added].

Applications on the class path must use the --add-modules command-line option to request that an incubator module be resolved. Applications developed as modules can specify requires or requires transitive dependences [sic] upon an incubator module directly. (Some incubator modules, such as those offering command-line tools, may forego exporting any packages, and instead provide service implementations so that tools can be accessed programatically [sic]. It is usually not recommended to require a module that exports no packages, but it is necessary in this case in order to resolve the incubator module, and have its service providers participate in service binding.)


由于孵化器模块不参与服务绑定(bind),jdk.incubator.jpackage模块在运行时未解析。不幸的是,这意味着无法通过 ToolProvider 找到该工具。除非您:
  • 使用 --add-modules jdk.incubator.jpackage 启动 Java 进程,
  • 或者使您的代码模块化并包含 requires jdk.incubator.jpackage;模块信息文件中的指令。

  • 由于您试图从 Gradle 任务中调用 jpackage,我认为选项二是不可行的。然而,第一种选择似乎是可行的。执行 Gradle 时,您可以指定适当的 org.gradle.jvmargs系统属性。例如:
    ./gradlew "-Dorg.gradle.jvmargs=--add-modules=jdk.incubator.jpackage" buildInstaller
    
    您不必每次都在命令行中键入它。查看 this Gradle documentation查看您可以在哪里定义该系统属性。可能也可以通过您的 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 */)
        }
    }
    

    关于java - 无法通过 ToolProvider 找到 jpackage,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61307077/

    相关文章:

    Gradle 依赖管理 : latest. 集成 vs. latest.release

    java - Weblogic 12.2.1.3 错误 : Could not find or load main class com. oracle.cie.wizard.WizardController

    java - 用特殊字符替换 & 符号的正则表达式模式

    java - Idea 不断将 Kotlin 的编译目标从 1.8 切换到 1.6

    android - 协程限制?

    groovy - 如何获取 Gradle 子项目的拓扑排序列表?

    java - Hibernate 标准 aPI 多选

    java - 将 Jade4J 配置为 Spring 中的默认模板引擎

    java - 需要在Java类中使用此方法( Kotlin )

    android - 如何在gradle中为任务设置绝对路径