java - 无法通过 ToolProvider 找到 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 问题)。
长版:我正在关注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 版本。
解决方案
该问题与JEP 11: Incubator Modules有关。该 JEP 声明:
孵化器模块
jdk.incubator.
由其模块名称中的前缀标识,无论模块是导出孵化 API 还是包含孵化工具。
在 Java 14 中,可能在接下来的几个版本中,jpackage 工具包含在一个名为jdk.incubator.jpackage
. 从名称的前缀中,我们可以看出该模块是一个孵化器模块。同一个 JEP 后来指出:
孵化器模块是标准 JDK 构建生成的 JDK 运行时映像的一部分。但是,默认情况下,孵化器模块不会为类路径上的应用程序解析。此外,默认情况下,孵化器模块不参与类路径或模块路径上的应用程序的服务绑定[强调添加]。
类路径上的应用程序必须使用
--add-modules
命令行选项来请求解析孵化器模块。作为模块开发的应用程序可以直接指定requires
或requires transitive
依赖于孵化器模块。(一些孵化器模块,例如那些提供命令行工具的模块,可能会放弃导出任何包,而是提供服务实现,以便可以以编程方式访问工具 [原文如此]。通常不建议要求一个不导出包的模块,但在这种情况下,为了解析孵化器模块,并让其服务提供者参与服务绑定,这是必要的。)
由于孵化器模块不参与服务绑定,因此该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
您不必每次都在命令行中键入它。查看此 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 */)
}
}
推荐阅读
- python - 高效点积密集到稀疏
- amazon-web-services - S3 中的对象锁定是否有助于防止对象被覆盖?
- python - Azure Functions Python blobTrigger 如何修复“Microsoft.Azure.WebJobs.Extensions.Storage:对象引用未设置为对象的实例。”?
- javascript - 数组中的第 K 个最大元素
- php - PHP REST API XML请求在传递状态导出ID时返回空响应它正在工作
- magnolia - n.integration.contentconnector.JcrContentConnector:获取片段异常的 JCR id 失败
- php - 通过覆盖实体对 CollectionType 中的 EntityType 进行分组
- android - 为什么推送通知图标变成白色而不是使用 FCM 的应用程序图标 android?
- javafx - Javafx 中多个 TextField 的一个 ChangeListener
- mysql - 有什么方法可以将我的所有表格日期转换为 varchar 而不是使用 ALTER?