1. 概要

Gradle 6.0リリースには、ビルドをより効率的かつ堅牢にするのに役立ついくつかの新機能が含まれています。 これらの機能には、改善された依存関係管理、モジュールメタデータ発行、タスク構成の回避、およびJDK13のサポートが含まれます。

このチュートリアルでは、Gradle6.0で利用できる新機能を紹介します。 ビルドファイルの例では、GradleのKotlinDSLを使用します。

2. 依存関係管理の改善

近年のリリースごとに、Gradleはプロジェクトが依存関係を管理する方法を段階的に改善しています。 これらの依存関係の改善は、Gradle6.0で最高潮に達します。 現在安定している依存関係管理の改善点を確認しましょう。

2.1. APIと実装の分離

java-library プラグインは、再利用可能なJavaライブラリを作成するのに役立ちます。 プラグインは、ライブラリのパブリックAPIの一部である依存関係を、実装の詳細である依存関係から分離することを推奨します。 この分離により、ユーザーがライブラリのパブリックAPIの一部ではない型を誤って参照することがないため、ビルドがより安定します。

java-libraryプラグインとそのapiおよび実装構成はGradle3.4で導入されました。 このプラグインはGradle6.0の新機能ではありませんが、プラグインが提供する拡張された依存関係管理機能は、Gradle6.0で実現された包括的な依存関係管理の一部です。

2.2. リッチバージョン

プロジェクトの依存関係グラフには、同じ依存関係の複数のバージョンが含まれていることがよくあります。 これが発生した場合、Gradleはプロジェクトが最終的に使用する依存関係のバージョンを選択する必要があります。

Gradle 6.0では、豊富なバージョン情報を依存関係に追加できます。 豊富なバージョン情報は、依存関係の競合を解決するときにGradleが可能な限り最良の選択を行うのに役立ちます。

たとえば、Guavaに依存するプロジェクトについて考えてみます。 さらに、このプロジェクトがバージョン10.0以降安定しているGuava APIのみを使用していることがわかっているにもかかわらず、Guavaバージョン28.1-jreを使用しているとします。

require 宣言を使用して、このプロジェクトで10.0以降の任意のバージョンのGuavaを使用できることをGradleに通知できます。また、 Preferred 宣言を使用して、Gradleに28.1-jreを使用する必要があることを通知します。他の制約がそれを妨げることはありません。 since 宣言は、この豊富なバージョン情報を説明するメモを追加します。

implementation("com.google.guava:guava") {
    version {
        require("10.0")
        prefer("28.1-jre")
        because("Uses APIs introduced in 10.0. Tested with 28.1-jre")
    }
}

これは、ビルドをより安定させるのにどのように役立ちますか? このプロジェクトが、Guavaバージョン16.0を使用する必要がある依存関係fooにも依存しているとします。 foo プロジェクトのビルドファイルは、その依存関係を次のように宣言します。

dependencies {
    implementation("com.google.guava:guava:16.0")
}

fooプロジェクトはGuava16.0に依存しており、プロジェクトはGuavaバージョン28.1-jreと foo の両方に依存しているため、競合が発生します。 Gradleのデフォルトの動作は、最新バージョンを選択することです。 ただし、この場合、最新バージョンを選択するのは間違った選択です。 、 なぜなら foo バージョン16.0を使用する必要があります。

Gradle 6.0より前は、ユーザーは自分で競合を解決する必要がありました。 Gradle 6.0では、プロジェクトで10.0までのGuavaバージョンを使用する可能性があることをGradleに通知できるため、Gradleはこの競合を正しく解決し、バージョン16.0を選択します。

requireおよびprefer宣言に加えて、strictlyおよびreject宣言を使用できます。 strictly 宣言は、プロジェクトで使用する必要のある依存関係のバージョン範囲を記述します。 reject 宣言は、プロジェクトと互換性のない依存関係のバージョンを記述します。

プロジェクトがGuava29で削除されることがわかっているAPIに依存している場合は、 strictly 宣言を使用して、Gradleが28を超えるバージョンのGuavaを使用しないようにします。 同様に、Guava 27.0にプロジェクトに問題を引き起こすバグがあることがわかっている場合は、rejectを使用してそれを除外します。

implementation("com.google.guava:guava") {
    version {
        strictly("[10.0, 28[")
        prefer("28.1-jre")
        reject("27.0")
        because("""
            Uses APIs introduced in 10.0 but removed in 29. Tested with 28.1-jre.
            Known issues with 27.0
        """)
    }
}

2.3. プラットフォーム

java-platform プラグインを使用すると、プロジェクト間で一連の依存関係制約を再利用できます。 プラットフォームの作成者は、バージョンがプラットフォームによって制御される密結合の依存関係のセットを宣言します。

プラットフォームに依存するプロジェクトは、プラットフォームによって制御される依存関係のバージョンを指定する必要はありません。 Mavenユーザーは、これがMaven親POMのdependentencyManagement機能に似ていることに気付くでしょう。

プラットフォームは、マルチプロジェクトビルドで特に役立ちます。 マルチプロジェクトビルドの各プロジェクトは同じ外部依存関係を使用する可能性があり、それらの依存関係のバージョンが同期しないようにする必要があります。

新しいプラットフォームを作成して、マルチプロジェクトビルドがプロジェクト間で同じバージョンのApacheHTTPクライアントを使用するようにします。 まず、 java-platformプラグインを使用するプロジェクトhttpclient-platform、を作成します。

plugins {
    `java-platform`
}

次に、このプラットフォームに含まれる依存関係の制約を宣言します。 この例では、プロジェクトで使用するApacheHTTPコンポーネントのバージョンを選択します。

dependencies {
    constraints {
        api("org.apache.httpcomponents:fluent-hc:4.5.10")
        api("org.apache.httpcomponents:httpclient:4.5.10")
    }
}

最後に、ApacheHTTPクライアントFluentAPIを使用するperson-rest-clientプロジェクトを追加しましょう。 ここでは、 platform メソッドを使用して、httpclient-platformプロジェクトへの依存関係を追加しています。 org.apache.httpcomponents:fluent-hcへの依存関係も追加します。 httpclient-platform が使用するバージョンを決定するため、この依存関係にはバージョンが含まれていません。

plugins {
    `java-library`
}

dependencies {
    api(platform(project(":httpclient-platform")))
    implementation("org.apache.httpcomponents:fluent-hc")
}

java-platform プラグインは、ビルド内の依存関係の不整合による実行時の予期しない予期しない事態を回避するのに役立ちます。

2.4. テストフィクスチャ

Gradle 6.0より前は、プロジェクト間でテストフィクスチャを共有したいビルド作成者は、それらのフィクスチャを別のライブラリプロジェクトに抽出していました。 これで、ビルドの作成者は、java-test-fixturesプラグインを使用してプロジェクトからテストフィクスチャを公開できます。

抽象化を定義し、その抽象化によって期待されるコントラクトを検証するテストフィクスチャを公開するライブラリを構築しましょう。

この例では、抽象化はフィボナッチ数列ジェネレーターであり、テストフィクスチャはJUnit5テストミックスインです。 フィボナッチ数列ジェネレーターの実装者は、テストミックスインを使用して、シーケンスジェネレーターが正しく実装されていることを確認できます。

まず、抽象化とテストフィクスチャ用の新しいプロジェクトfibonacci-spiを作成しましょう。 このプロジェクトには、java-libraryおよびjava-test-fixturesプラグインが必要です。

plugins {
    `java-library`
    `java-test-fixtures`
}

次に、JUnit5の依存関係をテストフィクスチャに追加しましょう。 java-libraryプラグインがapiおよびimplementation構成を定義するのと同様に、java-test-fixturesプラグインがを定義します。 testFixturesApiおよびtestFixturesImplementation構成:

dependencies {
    testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.1")
    testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.8.1")
}

依存関係を設定したら、 java-test-fixturesプラグインによって作成されたsrc/ testFixtures /javaソースセットにJUnit5テストミックスインを追加しましょう。 このテストミックスインは、FibonacciSequenceGenerator抽象化のコントラクトを検証します。

public interface FibonacciSequenceGeneratorFixture {

    FibonacciSequenceGenerator provide();

    @Test
    default void whenSequenceIndexIsNegative_thenThrows() {
        FibonacciSequenceGenerator generator = provide();
        assertThrows(IllegalArgumentException.class, () -> generator.generate(-1));
    }

    @Test
    default void whenGivenIndex_thenGeneratesFibonacciNumber() {
        FibonacciSequenceGenerator generator = provide();
        int[] sequence = { 0, 1, 1, 2, 3, 5, 8 };
        for (int i = 0; i < sequence.length; i++) {
            assertEquals(sequence[i], generator.generate(i));
        }
    }
}

このテストフィクスチャを他のプロジェクトと共有するために必要なのはこれだけです。

それでは、このテストフィクスチャを再利用する新しいプロジェクトfibonacci-recursiveを作成しましょう。 このプロジェクトは、dependenciesブロックのtestFixtures メソッドを使用して、fibonacci-spiプロジェクトからのテストフィクスチャへの依存関係を宣言します。

dependencies {
    api(project(":fibonacci-spi"))
    
    testImplementation(testFixtures(project(":fibonacci-spi")))
    testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1")
    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1")
}

最後に、 fibonacci-spi プロジェクトで定義されたテストミックスインを使用して、再帰的フィボナッチ数列ジェネレーターの新しいテストを作成できます。

class RecursiveFibonacciUnitTest implements FibonacciSequenceGeneratorFixture {
    @Override
    public FibonacciSequenceGenerator provide() {
        return new RecursiveFibonacci();
    }
}

Gradle 6.0 java-test-fixtures プラグインにより、ビルド作成者はプロジェクト間でテストフィクスチャを共有できる柔軟性が得られます

3. Gradleモジュールのメタデータ発行

従来、Gradleプロジェクトは、ビルドアーティファクトをIvyまたはMavenリポジトリに公開していました。 これには、それぞれivy.xmlまたはpom.xmlメタデータファイルの生成が含まれます。

ivy.xmlおよびpom.xmlモデルは、この記事で説明した豊富な依存関係情報を格納できません。 これは、ライブラリをMavenまたはIvyリポジトリに公開するときに、ダウンストリームプロジェクトがこの豊富な依存関係情報の恩恵を受けないことを意味します。

Gradle 6.0は、Gradleモジュールメタデータ仕様を導入することでこのギャップに対処しています。 Gradleモジュールメタデータ仕様は、Gradle6.0で導入されたすべての拡張モジュール依存関係メタデータの保存をサポートするJSON形式です。

プロジェクトは、従来のivy.xmlおよびpom.xmlメタデータファイルに加えて、このメタデータファイルをビルドしてIvyおよびMavenリポジトリに公開できます。 この下位互換性により、Gradle 6.0プロジェクトは、レガシーツールを壊すことなく存在する場合、このモジュールメタデータを利用できます。

Gradleモジュールメタデータファイルを公開するには、プロジェクトで新しいMaven公開プラグインまたはIvy公開プラグインを使用する必要があります。 Gradle 6.0以降、これらのプラグインはデフォルトでGradleモジュールメタデータファイルを公開します。 これらのプラグインは、レガシーパブリッシングシステムに代わるものです。

3.1. GradleモジュールメタデータをMavenに公開する

GradleモジュールメタデータをMavenに公開するようにビルドを構成しましょう。 まず、ビルドファイルにmaven-publishを含めます。

plugins {
    `java-library`
    `maven-publish`
}

次に、パブリケーションを構成します。 パブリケーションには、任意の数のアーティファクトを含めることができます。 java構成に関連付けられたアーティファクトを追加しましょう。

publishing {
    publications {
        register("mavenJava", MavenPublication::class) {
            from(components["java"])
        }
    }
}

maven-publish プラグインは、publishToMavenLocalタスクを追加します。 このタスクを使用して、GradleModuleMetadataパブリケーションをテストしてみましょう。

./gradlew publishToMavenLocal

次に、このアーティファクトのディレクトリをローカルのMavenリポジトリにリストします。

ls ~/.m2/repository/com/baeldung/gradle-6/1.0.0/
gradle-6-1.0.0.jar	gradle-6-1.0.0.module	gradle-6-1.0.0.pom

コンソール出力でわかるように、GradleはMavenPOMに加えてモジュールメタデータファイルを生成します。

4. 構成回避API

バージョン5.1以降、Gradleはプラグイン開発者に新しいインキュベーション構成回避APIを使用することを推奨しました。 これらのAPIは、ビルドが可能な場合に比較的遅いタスク構成手順を回避するのに役立ちます。 Gradleは、このパフォーマンスの向上をタスク構成の回避と呼んでいます。 Gradle 6.0は、このインキュベーションAPIを安定したものに昇格させます。

構成回避機能は主にプラグインの作成者に影響しますが、ビルドでカスタム構成タスク、またはプロパティを作成するビルド作成者も影響を受けます。 プラグインの作成者とビルドの作成者は同様に、新しいレイジー構成APIを使用してオブジェクトをプロバイダータイプでラップできるようになりました。これにより、Gradleは必要になるまでこれらのオブジェクトを「実現」することを回避します。

レイジーAPIを使用してカスタムタスクを追加しましょう。 まず、TaskContainer.registering拡張メソッドを使用してタスクを登録します。 registeringTaskProviderを返すため、 Task インスタンスの作成は、Gradleまたはビルド作成者が TaskProvider.get()[X180X ]。 最後に、Gradleが作成した後にタスクを構成するクロージャーを提供します。

val copyExtraLibs by tasks.registering(Copy::class) {
    from(extralibs)
    into(extraLibsDir)
}

Gradleのタスク構成の回避移行ガイドは、プラグインの作成者とビルドの作成者が新しいAPIに移行するのに役立ちます。 ビルド作成者の最も一般的な移行は次のとおりです。

  • tasks.createの代わりにtasks.register
  • tasks.getByNameの代わりにtasks.named
  • configuration.createの代わりにconfigurations.register
  • File(project.buildDir、 “foo”)の代わりに project.layout.buildDirectory.dir( “foo”)

5. JDK13サポート

Gradle 6.0では、JDK13を使用したプロジェクト構築のサポートが導入されています。 おなじみのsourceCompatibilityおよびtargetCompatibility設定でJava13を使用するようにJavaビルドを構成できます。

sourceCompatibility = JavaVersion.VERSION_13
targetCompatibility = JavaVersion.VERSION_13

RawStringLiteralsなどのJDK13の最もエキサイティングな言語機能の一部は、まだプレビューステータスです。 Javaビルドでタスクを構成して、これらのプレビュー機能を有効にしましょう。

tasks.compileJava {
    options.compilerArgs.add("--enable-preview")
}
tasks.test {
    jvmArgs.add("--enable-preview")
}
tasks.javadoc {
    val javadocOptions = options as CoreJavadocOptions
    javadocOptions.addStringOption("source", "13")
    javadocOptions.addBooleanOption("-enable-preview", true)
}

6. 結論

この記事では、Gradle6.0の新機能のいくつかについて説明しました。

強化された依存関係管理、Gradleモジュールメタデータの公開、タスク構成の回避、およびアーリーアダプターがJava13プレビュー言語機能を使用するようにビルドを構成する方法について説明しました。

いつものように、この記事のコードはGitHub上にあります。