Куда идут файлы ресурсов в проекте Gradle, который строит модуль Java 9?

начиная с IDEA 2018.2.1, IDE запускает пакеты с подсветкой ошибок " не в графе модуля " из зависимостей, которые были модулированы. Я добавил module-info.java файл в мой проект и добавил реквизит requires заявления, но теперь у меня проблемы с доступом файлы ресурсов в моем .

(полный пример см. В разделе этот проект GitHub.)

когда я использовал ./gradlew run или ./gradlew installDist + в результате сценарий оболочки, я смог прочитайте файлы ресурсов, но когда я запустил app из IDE, я не был.

я подал вопрос с JetBrains, и я узнал, что идея заключалась в использовании модуля path, в то время как Gradle, по умолчанию, использовал classpath. Путем добавления следующий блок для my build.gradle, я смог получить Gradle к и ... невозможно прочитать файлы ресурсов.

run {
    inputs.property("moduleName", moduleName)
    doFirst {
        jvmArgs = [
                '--module-path', classpath.asPath,
                '--module', "$moduleName/$mainClassName"
        ]
        classpath = files()
    }
}

пробовал export - ing каталог ресурсов, который меня интересовал как "пакет", и во время компиляции получил сбой сборки с:

ошибка: пакет пуст или не существует: mydir

используя opens вместо exports получил ту же ошибку, хотя понижен на предупреждение.

я даже попытался передвинуть ресурс src/main/java, но это вызвало те же ошибки / предупреждения, а также привело к ресурсы не копируются в .

где ресурсы должен идти в Java 9, и как мне получить к ним доступ?


Примечание: я значительно отредактировал этот вопрос после продолжения исследуйте проблему. В первоначальном вопросе я также пытался выясните, как перечислить файлы в каталоге ресурсов, но в в ходе расследования я выяснил, что это был отвлекающий маневр. -- во-первых, потому что чтение каталогов ресурсов работает только тогда, когда ресурс считывается из file:/// URL (и, возможно, даже не затем), и во-вторых, потому что простые файлы тоже не работали, так что ясно, что проблема была с файлами ресурсов в целом, а не конкретно с справочники.


устранение:

Per Слоу-х, я добавил следующее в build.gradle:

// at compile time, put resources in same directories as classes
sourceSets {
  main.output.resourcesDir = main.java.outputDir
}

// at compile time, include resources in module
compileJava {
  inputs.property("moduleName", moduleName)
  doFirst {
    options.compilerArgs = [
      '--module-path', classpath.asPath,
      '--patch-module', "$moduleName=" 
        + files(sourceSets.main.resources.srcDirs).asPath,
      '--module-version', "$moduleVersion"
    ]
    classpath = files()
  }
}

// at run time, make Gradle use the module path
run {
  inputs.property("moduleName", moduleName)
  doFirst {
    jvmArgs = [
      '--module-path', classpath.asPath,
      '--module', "$moduleName/$mainClassName"
    ]
    classpath = files()
  }
}

Примечание: интересно, если я не продолжайте добавлять код Slaw, который делает run задача выполнить против банки, попытка прочитать каталог ресурсов InputStream в задаче запуска сейчас выдает IOException вместо предоставления списка файлов. (Против банки он просто получает пустой InputStream.)

1 ответов


в вашей щедрости вы запрашиваете официальную документацию о "правильном пути" для обработки ресурсов с помощью модулей Gradle и Jigsaw. Ответ, насколько я знаю, заключается в том, что нет "правильного пути", потому что Gradle еще (по состоянию на 4.10-rc-2) не имеет первоклассной поддержки модулей Jigsaw. Ближайший вы получите это Построение Модулей Java 9 документ.

, вы отметить речь идет о доступе к ресурсам из модуля (т. е. не из внешних модулей). Это не должно быть слишком сложно исправить с помощью простого build.gradle конфигурации.

по умолчанию Gradle разделяет выходные каталоги для классов и ресурсов. Это выглядит примерно так:

build/
|--classes/
|--resources/

при использовании run задание classpath значение sourceSets.main.runtimeClasspath. Это значение включает оба каталога, и это работает из-за того, как работает путь к классам. Вы можете думать об этом, как о classpath-это всего лишь один гигантский модуль.

это не работает, однако, при использовании modulepath, потому что технически файлы внутри resources не принадлежат к модулю, который находится внутри classes. Это означает, что нам нужен способ сообщить системе модулей включить resources внутри модуля внутри classes. К счастью, есть --patch-module. Этот параметр имеет следующий формат:

--patch-module <module>=<path>

здесь <path> is <file>(;<file>)* (Я предполагаю, что ; разделитель платформы зависимый.)

чтобы позволить вашему модулю получить доступ к собственным ресурсам, просто настройте свой run задача так:

run {
    input.property('moduleName', moduleName)
    doFirst {
        jvmArgs = [
                '--module-path', classpath.asPath,
                '--patch-module', "$moduleName=" + files(sourceSets.main.output.resourcesDir).asPath,
                '--module', "$moduleName/$mainClassName"
        ]
        classpath = files()
    }
}

вот как я это делал, и до сих пор все получалось довольно хорошо.


но как вы разрешаете внешним модулям получать доступ к ресурсам из вашего модуля при запуске приложения из Gradle? Это становится немного более запутанным.

если вы хотите разрешить внешним модулям доступ к ресурсам модуль должен opens пакет ресурса по крайней мере для модуля чтения (это относится только к капсулированные ресурсы). Однако, как вы обнаружили, это приводит к предупреждениям о компиляции и ошибкам во время выполнения.

  1. предупреждение компиляции, потому что вы пытаетесь opens пакет, который не существует в соответствии с системой модуля.
    • это следует ожидать, так как каталог ресурсов не включен, когда компиляция по умолчанию (при условии пакета только для ресурсов).
  2. ошибка выполнения заключается в том, что система модулей не может найти пакет, который вы объявили упоминалось выше. Я предполагаю, что система модуля делает некоторую проверку целостности перед применением заплатка.

Примечание: под "только для ресурсов" я имею в виду пакеты, у которых нет .java/.class файлы.

чтобы исправить предупреждение компиляции, вам просто нужно использовать --patch-module снова внутри compileJava задач. На этот раз вы будете использовать исходные каталоги ресурсов, а не выходной каталог.

compileJava {
    inputs.property('moduleName', moduleName)
    doFirst {
        options.compilerArgs = [
                '--module-path', classpath.asPath,
                '--patch-module', "$moduleName=" + files(sourceSets.main.resources.srcDirs).asPath,
                '--module-version', "$version"
        ]
    }
}

ошибки времени выполнения есть несколько вариантов. Первый вариант - "объединить" выходные данные ресурсов каталог с выходным каталогом для классов. Используя эту опцию, вы можете удалить --patch-module С run конфигурация задачи.

sourceSets {
    main.output.resourcesDir = main.java.outputDir
}

jar {
    // I've had bad experiences when "merging" output directories
    // where the jar task ends up creating duplicate entries in the JAR.
    // Use this option to combat that.
    duplicateStrategy = DuplicatesStrategy.EXCLUDE
}

второй вариант-настроить run задача выполнить файл JAR, а не разнесенный каталог(ы). Это работает, потому что, как и первый вариант, он объединяет классы и ресурсы в одном месте, и поэтому ресурсы являются частью модуля.

run {
    dependsOn += jar
    inputs.property('moduleName', moduleName)
    doFirst {
        // add JAR file and runtime classpath. The runtime classpath includes the output of the source set
        // so we remove that as well (to avoid having two of the same module on the modulepath)
        def modulepath = files(jar.archivePath) + (sourceSets.main.runtimeClasspath - sourceSets.main.output)
        jvmArgs = [
                '--module-path', modulepath.asPath,
                '--module', "$moduleName/$mainClassName"
        ]
        classpath = files()
    }
}

оба варианта можно также использовать внутри место --patch-module опция для первой части этого ответа (доступ к ресурсам из того же модуля).


в качестве бонуса, вот как я добавляю --main-class атрибут моих модульных банок:

jar {
    inputs.property('mainClassName', mainClassName)
    doLast {
        exec {
            executable = 'jar'
            args = [
                    '--update', '--file', "$archivePath",
                    '--main-class', "$mainClassName"
            ]
        }
    }
}

это позволяет использовать java -m module.name, а не java -m module.name/some.package.Main. Кроме того, если run задача настроена на выполнение банки, которую вы можете изменить:

'--module', "$moduleName/$mainClassName"

в:

'--module', "$moduleName"

P. S. Если есть лучший способ сделать пожалуйста, дайте мне знать.