首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >通过ToolProvider找不到jpackage

通过ToolProvider找不到jpackage
EN

Stack Overflow用户
提问于 2020-04-19 15:38:15
回答 1查看 384关注 0票数 4

短版本:,我试图从一个gradle任务调用jpackage,但是ToolProvider返回null (或者更好的是一个失败的可选选项)。在AdoptOpenJDK 14.0.0 (sdkman标识符14.0.0.hs-adpt)和Java.net (我认为这是OpenJDK!?) 14.0.1 (sdkman标识符14.0.1-open)上都是这样。我使用的是Gradle 6.3 (但这不像一个gradle问题)。

long version: --我遵循这个谈jpackage,在12:12显示从构建工具调用jpackage的代码。( 正式的jpackage页面还提到:除了命令行接口之外,jpackage还可以通过名为“jpackage”的ToolProvider API (java.util.spi.ToolProvider)访问。)

还有我的(Kotlin)代码(位于buildSrc/src/main/kotlin)

代码语言:javascript
复制
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)
  }
}

由新的分级任务调用

代码语言:javascript
复制
tasks {
  register("buildInstaller") {
    JPackage.buildInstaller(
      ...
    )
    dependsOn("build")
  }
}

失败与声明

代码语言:javascript
复制
> Could not create task ':buildInstaller'.
> jpackage not found (expected JDK version: 14 or above, detected: 14.0.1)

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

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

代码语言:javascript
复制
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处于“孵化”状态,因此我决定通过创建一个新的过程来调用它,这对于我的非模块化应用程序来说并不容易获得:

代码语言:javascript
复制
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
    }
  }
}

我的任务定义如下:

代码语言:javascript
复制
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版本。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-04-19 19:50:44

这个问题与JEP 11:孵化器模块有关。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系统属性。例如:

代码语言:javascript
复制
./gradlew "-Dorg.gradle.jvmargs=--add-modules=jdk.incubator.jpackage" buildInstaller

您不必每次都在命令行中键入这些内容。查看这个分级文档,查看您还可以在哪里定义该系统属性。也许也可以通过IDE来做一些事情,尽管还不确定。

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

代码语言:javascript
复制
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 */)
    }
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61307077

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档