не могу найти 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.


person StephanS    schedule 19.04.2020    source источник


Ответы (1)


Проблема связана с JEP 11: модули инкубатора. В этом JEP говорится:

Модуль инкубатора идентифицируется по префиксу jdk.incubator. в его имени, независимо от того, экспортирует ли модуль API инкубации или содержит инструмент инкубации.

В Java 14 и, вероятно, в следующих нескольких выпусках инструмент jpackage содержится в модуле с именем _ 2_. По префиксу имени мы видим, что модуль является модулем инкубатора. В том же JEP позже говорится:

Модули инкубатора являются частью образа среды выполнения JDK, создаваемого стандартной сборкой JDK. Однако по умолчанию модули инкубатора не разрешены для приложений на пути к классам. Кроме того, по умолчанию модули инкубатора не участвуют в связывании служб для приложений на пути к классу или пути модуля [курсив добавлен].

Приложения в пути к классам должны использовать параметр командной строки --add-modules для запроса разрешения модуля инкубатора. Приложения, разработанные как модули, могут напрямую указывать requires или requires transitive зависимости [sic] от модуля инкубатора. (Некоторые модули инкубатора, например те, которые предлагают инструменты командной строки, могут отказаться от экспорта каких-либо пакетов и вместо этого предоставлять реализации служб, так что инструменты могут быть доступны программно [sic]. Обычно не рекомендуется требовать модуль, который не экспортирует пакеты, но в этом случае он необходим для разрешения модуля инкубатора и для того, чтобы его поставщики услуг участвовали в привязке услуг.)

Поскольку модули инкубатора не участвуют в привязке службы, модуль jdk.incubator.jpackage не разрешается во время выполнения. К сожалению, это означает, что инструмент не может быть найден через ToolProvider, если только вы:

  1. Запустите процесс Java с --add-modules jdk.incubator.jpackage,
  2. Или сделайте свой код модульным и включите директиву 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 */)
    }
}
person Slaw    schedule 19.04.2020
comment
спасибо, теперь работает! логика аргументов довольно задействована, поэтому я все равно вызвал JPackage.buildInstaller, но этот код теперь вызывает Runtime.getRuntime (). exec (cmdAndArgs) (Проблема с вашим подходом заключалась в том, что IntelliJ, похоже, вызывает gradle с Java11, хотя проект настроен на использование Java14. Итак, мой IntelliJ не смог понять скрипт сборки, так как тест версии выполняется во время компиляции. Хотя я переместил его в блок doLast, пока я выполняю задачу gradle из командная строка, я в порядке.) - person StephanS; 20.04.2020
comment
Что касается IntelliJ, использующего неправильный JDK с Gradle, перейдите в Настройки → Сборка, выполнение, развертывание → Инструменты сборки → Gradle и убедитесь, что Gradle JVM настроен на использование SDK проекта. - person Slaw; 20.04.2020