не могу найти jpackage через ToolProvider
короткая версия: я пытаюсь вызвать jpackage из задачи gradle, но ToolProvider возвращает null (или, лучше, неудачный вариант). Это относится к 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 доступен через API ToolProvider (java.util.spi.ToolProvider) под именем "jpackage".)
И еще мой (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, поскольку он, кажется, вызывает Gradle с JDK11, он сам поставляется в комплекте, но, по крайней мере, IntelliJ может скомпилировать сам скрипт сборки (поскольку проверка версии находится в блоке doLast, а не непосредственно в регистр-блок). В качестве альтернативы вы можете изменить JDK, который IntelliJ использует для вызова Gradle, прокрутите вниз до комментария Slaw под его ответом, чтобы узнать, как это сделать.
Кстати: я почти уверен, что Gradle версии 6.3 является жестким требованием для того, чтобы это работало, поскольку это первая версия Gradle, совместимая с Java 14.
1 ответ
Проблема связана с JEP 11: Модули инкубатора. В этом JEP говорится:
Модуль инкубатора обозначается значком
jdk.incubator.
префикс в имени модуля, независимо от того, экспортирует ли модуль инкубационный API или содержит инкубационный инструмент.
В Java 14 и, вероятно, в следующих нескольких выпусках инструмент jpackage содержится в модуле с именем jdk.incubator.jpackage
. По префиксу имени мы видим, что модуль является модулем инкубатора. В том же JEP позже говорится:
Модули инкубатора являются частью образа среды выполнения JDK, создаваемого стандартной сборкой JDK. Однако по умолчанию модули инкубатора не разрешаются для приложений на пути к классам. Более того, по умолчанию модули инкубатора не участвуют в привязке служб для приложений в пути к классу или пути модуля [курсив добавлен].
Приложения на пути к классам должны использовать
--add-modules
параметр командной строки, чтобы запросить разрешение модуля инкубатора. Приложения, разработанные как модули, могут указыватьrequires
илиrequires transitive
зависимости [sic] от модуля инкубатора. (Некоторые модули инкубатора, например те, которые предлагают инструменты командной строки, могут отказаться от экспорта каких-либо пакетов и вместо этого предоставлять реализации служб, так что инструменты могут быть доступны программно [sic]. Обычно не рекомендуется требовать модуль, который не экспортирует пакеты, но в этом случае это необходимо для разрешения модуля инкубатора и участия его поставщиков услуг в привязке услуг.)
Поскольку модули инкубатора не участвуют в привязке сервисов, jdk.incubator.jpackage
модуль не разрешается во время выполнения. К сожалению, это означает, что инструмент нельзя найти черезToolProvider
если вы тоже:
- Запустите процесс Java с помощью
--add-modules jdk.incubator.jpackage
, - Или сделайте свой код модульным и включите
requires jdk.incubator.jpackage;
в файле информации о модуле.
Поскольку вы пытаетесь вызвать jpackage из задачи Gradle, я полагаю, что второй вариант невозможен. Однако первый вариант кажется жизнеспособным; при выполнении 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 */)
}
}