Как сгенерировать, скомпилировать, создать jar-файл и зависеть от модуля Gradle

У меня есть проект Java Gradle, который использует API, указанный OpenAPI. Я использовал плагин org.openapi.generator, который генерирует исходники, а также полный модуль Gradle.

Я ожидаю, что есть способ определить шаги создания, компиляции, jar, чтобы другие модули зависели от сгенерированного модуля.

I.e.

# api/build.gradle:
plugins {
    id 'java'
    id "org.openapi.generator" version "5.0.0"
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation group: 'junit', name: 'junit', version: '4.12'
}

compileJava.dependsOn "openApiGenerate"

openApiGenerate {
    generatorName = "java"
    inputSpec = "$projectDir/src/main/openapi/spec.yaml".toString()
    outputDir = "$buildDir/generated"
    apiPackage = "com.example.api"
    invokerPackage = "com.example.api.invoker"
    modelPackage = "com.example.api.model"
    configOptions = [
            dateLibrary: "java8",
            library    : "native"
    ]
    groupId = "com.example"
    id = "api"
}

gradlew api:openApiGenerate генерирует (лишние файлы исключены):

api/build/generated/
├── build.gradle
├── pom.xml
├── settings.gradle
└── src
    ├── main/java/...
    └── test/java/...

Есть ли способ делегировать, включить или зависеть от этого сгенерированного модуля из других модулей в проекте? Сгенерированный модуль имеет надежную group:artifact:version координату.

Т.е. Я хотел бы иметь возможность указать com.example:api:1.0 в другом месте проекта.


Я прочитал https://docs.gradle.org/current/userguide/composite_builds.html, поскольку это было похоже на то, что я ожидал, но я новичок в Gradle, и это было немного слишком глубоко.

Я пробовал переопределить основной и тестовый исходные наборы в api/build.gradle, но мне не нравится копировать и вставлять зависимости из api/build/generated/build.gradle.

Я нашел https://docs.gradle.org/current/userguide/declaring_dependencies.html#sec:dependency-types, который включает в себя дразнящий пример, но падает, поскольку это зависимость только от источника.

dependencies {
    implementation files("$buildDir/classes") {
        builtBy 'compile'
    }
}

Я просмотрел этот пример, но как мне зависеть от проекта (api/build/generated/), который еще не существует?

dependencies {
    implementation project(':shared')
}

person Nick Breen    schedule 30.12.2020    source источник


Ответы (2)


Отличный вопрос! У меня нет точного ответа, но, надеюсь, следующее поможет.

Предлагаемый подход

Я бы держал сборки модулей, которые зависят от сгенерированного API, полностью отдельно от сборки, которая генерирует API. Единственной связью между такими сборками должно быть объявление зависимости. Это означает, что вам нужно будет вручную убедиться, что вы сначала создали проект создания API, а потом строить только зависимые проекты.

По умолчанию это будет означать также публикацию модуля API до создания зависимых проектов. Альтернативой этому параметру по умолчанию действительно могут быть составные сборки - например, чтобы позволить вам протестировать только что созданный API локально перед его публикацией. Однако перед созданием / запуском составной сборки вам придется вручную запускать API, генерирующий сборку каждый раз, когда изменяется документ OpenAPI.

Пример

Допустим, у вас есть проект A в зависимости от сгенерированного API. Его сборка Gradle будет содержать что-то вроде этого:

dependencies {
    implementation 'com.example:api:1.0'
}

Перед запуском сборки A вам нужно сначала запустить

  1. ./gradlew openApiGenerate из вашего api проекта.
  2. ./gradlew publish из каталога api/build/generated/.

Затем сборка A может получить опубликованную зависимость из репозитория публикации.

В качестве альтернативы вы можете отбросить шаг 2 локально и запустить сборку A с дополнительной опцией Gradle CLI:

./gradlew --include-build $path_to/api/build/generated/ …

Идея для меньшего количества ручной работы

Я много думал об этом, но так и не нашел полного решения - отсюда и мое несовершенное предложение выше. Позвольте мне все же резюмировать мою идею того, как это может работать.

person Chriki    schedule 30.12.2020
comment
добавив к вашему ответу, приведенный ниже код в файле сборки gradle может быть удобен: sourceSets{ main{ java.srcDirs+=path_to/api/build/generated/ } } - person Sunil Kumar; 30.12.2020
comment
Спасибо, Сунил, я считаю, что OP уже пробовал этот подход, но исключил его - цитата из вопроса: «Я пробовал переопределить основной и тестовый исходные наборы в api/build.gradle, но мне не нравится копировать и вставлять зависимости из api/build/generated/build.gradle . » - person Chriki; 30.12.2020

Чтобы расширить ответ @ Chriki тем, что я на самом деле использовал:

  1. Определите api/ как собственный проект с пустым api/settings.gradle файлом.

    Это говорит Gradle, что это автономный проект.

  2. Определите модуль api с помощью:

    # api/build.gradle
    plugins {
       id 'java'
       id "org.openapi.generator" version "5.0.0"
    }
    
    repositories {
       mavenCentral()
    }
    
    openApiGenerate {
       generatorName = "java"
       inputSpec = "$projectDir/src/main/openapi/specification.yaml"
       outputDir = "$buildDir/generated"
       apiPackage = "com.example.api"
       invokerPackage = "com.example.api.invoker"
       modelPackage = "com.example.api.model"
       configOptions = [
          dateLibrary: "java8",
          library    : "native"
       ]
       groupId = "com.example"
       id = "api"
       version = "1.0.0"
    }
    

    Обратите внимание, что group и idversion) явно определяют его координату maven.

  3. Включите сборку с заменой, чтобы иждивенцы могли просто использовать ее координату maven:

    # settings.gradle
    
    includeBuild('api/build/generated') {
        dependencySubstitution {
            substitute module('com.example:api') with project(':')
        }
    }
    

    ... и в каком-то другом модуле:

    # app/build.gradle
    
    dependencies {
       implementation group: 'com.example', name: 'api'
    }
    

    Основное преимущество этого перед ./gradlew --include-build api/build/generated в том, что [моя] IDE тоже все это «связывает».

  4. Создайте библиотеку API:

     ./gradlew --project-dir api/ openApiGenerate
    
  5. Скомпилируйте / запустите основной проект:

     ./gradlew build
     ./gradlew run
    
person Nick Breen    schedule 31.12.2020