1. 概要

開発中に、使用するよりも多くの依存関係を追加してしまうことがあります。

このクイックチュートリアルでは、Gradle Nebula Lint プラグインを使用して、このような問題を特定して修正する方法を説明します。

2. セットアップと構成

この例では、マルチモジュールのGradle5セットアップを使用しています。

このプラグインはGroovyベースのビルドファイルでのみ機能します。

ルートプロジェクトビルドファイルで設定しましょう。

plugins {
    id "nebula.lint" version "16.9.0"
}

description = "Gradle 5 root project"

allprojects {
    apply plugin :"java"
    apply plugin :"nebula.lint"
    gradleLint {
        rules=['unused-dependency']
    }
    group = "com.baeldung"
    version = "0.0.1"
    sourceCompatibility = "1.8"
    targetCompatibility = "1.8"

    repositories {
        jcenter()
    }
}

当面は、マルチプロジェクトビルドに対してのみこの方法で構成できます。 これは、各モジュールに個別に適用できないことを意味します。

次に、モジュールの依存関係を構成しましょう。

description = "Gradle Unused Dependencies example"

dependencies {
    implementation('com.google.guava:guava:29.0-jre')
    testImplementation('junit:junit:4.12')
}

次に、モジュールソースに簡単なメインクラスを追加しましょう。

public class UnusedDependencies {

    public static void main(String[] args) {
        System.out.println("Hello world");
    }
}

これを少し後で構築し、プラグインがどのように機能するかを確認します。

3. 検出シナリオとレポート

プラグインは出力jarを検索して、依存関係が使用されているかどうかを検出します。

ただし、いくつかの条件に応じて、異なる結果が得られる可能性があります

次のセクションでは、より興味深いケースについて説明します。

3.1. 未使用の依存関係

セットアップが完了したので、基本的なユースケースを見てみましょう。 未使用の依存関係に関心があります。

lintGradleタスクを実行してみましょう。

$ ./gradlew lintGradle

> Task :lintGradle FAILED
# failure output omitted

warning   unused-dependency                  this dependency is unused and can be removed
unused-dependencies/build.gradle:6
implementation('com.google.guava:guava:29.0-jre')

✖ 1 problem (0 errors, 1 warning)

To apply fixes automatically, run fixGradleLint, review, and commit the changes.
# some more failure output

何が起きたのか見てみましょう。 compileClasspath 構成には、未使用の依存関係( guava )があります。

プラグインが示唆するようにfixGradleLintタスクを実行すると、依存関係はbuild.gradleから自動的に削除されます。

ただし、代わりに依存関係のあるダミーロジックを使用しましょう。

public static void main(String[] args) {
    System.out.println("Hello world");
    useGuava();
}

private static void useGuava() {
    List<String> list = ImmutableList.of("Baledung", "is", "cool");
    System.out.println(list.stream().collect(Collectors.joining(" ")));
}

再実行しても、エラーは発生しません。

$ ./gradlew lintGradle

BUILD SUCCESSFUL in 559ms
3 actionable tasks: 1 executed, 2 up-to-date

3.2. 推移的な依存関係の使用

別の依存関係を含めましょう:

dependencies {
    implementation('com.google.guava:guava:29.0-jre')
    implementation('org.apache.httpcomponents:httpclient:4.5.12')
    testImplementation('junit:junit:4.12')
}

今回は、推移的な依存関係から何かを使用しましょう。

public static void main(String[] args) {
    System.out.println("Hello world");
    useGuava();
    useHttpCore();
}

// other methods

private static void useHttpCore() {
    SSLContextBuilder.create();
}

しばらく様子を見てみましょう:

$ ./gradlew lintGradle

> Task :lintGradle FAILED
# failure output omitted 

warning   unused-dependency                  one or more classes in org.apache.httpcomponents:httpcore:4.4.13 
are required by your code directly (no auto-fix available)
warning   unused-dependency                  this dependency is unused and can be removed 
unused-dependencies/build.gradle:8
implementation('org.apache.httpcomponents:httpclient:4.5.12')

✖ 2 problems (0 errors, 2 warnings)

2つのエラーが発生します。 最初のエラーは、httpcoreを直接参照する必要があることを大まかに示しています。

サンプルのSSLContextBuilderは実際にはその一部です。

2番目のエラーは、httpclient。から何も使用していないことを示しています

推移的な依存関係を使用する場合、プラグインはそれを直接的な依存関係にするように指示します

依存関係ツリーを見てみましょう。

$ ./gradlew unused-dependencies:dependencies --configuration compileClasspath

> Task :unused-dependencies:dependencies

------------------------------------------------------------
Project :unused-dependencies - Gradle Unused Dependencies example
------------------------------------------------------------

compileClasspath - Compile classpath for source set 'main'.
+--- com.google.guava:guava:29.0-jre
|    +--- com.google.guava:failureaccess:1.0.1
|    +--- com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|    +--- com.google.code.findbugs:jsr305:3.0.2
|    +--- org.checkerframework:checker-qual:2.11.1
|    +--- com.google.errorprone:error_prone_annotations:2.3.4
|    \--- com.google.j2objc:j2objc-annotations:1.3
\--- org.apache.httpcomponents:httpclient:4.5.12
     +--- org.apache.httpcomponents:httpcore:4.4.13
     +--- commons-logging:commons-logging:1.2
     \--- commons-codec:commons-codec:1.11

この場合、httpcorehttpclientによって取り込まれていることがわかります。

3.3. リフレクションでの依存関係の使用

リフレクションを使用する場合はどうですか?

この例を少し強化してみましょう。

public static void main(String[] args) {
    System.out.println("Hello world");
    useGuava();
    useHttpCore();
    useHttpClientWithReflection();
}

// other methods

private static void useHttpClientWithReflection() {
    try {
        Class<?> httpBuilder = Class.forName("org.apache.http.impl.client.HttpClientBuilder");
        Method create = httpBuilder.getMethod("create", null);
        create.invoke(httpBuilder, null);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

次に、Gradleタスクを再実行しましょう。

$ ./gradlew lintGradle

> Task :lintGradle FAILED
# failure output omitted

warning   unused-dependency                  one or more classes in org.apache.httpcomponents:httpcore:4.4.13 
are required by your code directly (no auto-fix available)

warning   unused-dependency                  this dependency is unused and can be removed
unused-dependencies/build.gradle:9
implementation('org.apache.httpcomponents:httpclient:4.5.12')

✖ 2 problems (0 errors, 2 warnings)

どうしたの? 依存関係(httpclient)から HttpClientBuilder を使用しましたが、それでもエラーが発生しました。

リフレクション付きのライブラリを使用する場合、プラグインはその使用法を検出しません

その結果、同じ2つのエラーが表示されます。

一般に、runtimeOnlyなどの依存関係を構成する必要があります。

3.4. レポートの生成

大規模なプロジェクトの場合、端末で返されるエラーの数は処理が困難になります。

代わりにレポートを提供するようにプラグインを構成しましょう。

allprojects {
    apply plugin :"java"
    apply plugin :"nebula.lint"
    gradleLint {
        rules=['unused-dependency']
        reportFormat = 'text'
    }
    // other  details omitted
}

generateGradleLintReportタスクを実行して、ビルド出力を確認しましょう。

$ ./gradlew generateGradleLintReport
# task output omitted

$ cat unused-dependencies/build/reports/gradleLint/unused-dependencies.txt

CodeNarc Report - Jun 20, 2020, 3:25:28 PM

Summary: TotalFiles=1 FilesWithViolations=1 P1=0 P2=3 P3=0

File: /home/user/tutorials/gradle-5/unused-dependencies/build.gradle
    Violation: Rule=unused-dependency P=2 Line=null Msg=[one or more classes in org.apache.httpcomponents:httpcore:4.4.13 
                                                         are required by your code directly]
    Violation: Rule=unused-dependency P=2 Line=9 Msg=[this dependency is unused and can be removed] 
                                                 Src=[implementation('org.apache.httpcomponents:httpclient:4.5.12')]
    Violation: Rule=unused-dependency P=2 Line=17 Msg=[this dependency is unused and can be removed] 
                                                  Src=[testImplementation('junit:junit:4.12')]

[CodeNarc (http://www.codenarc.org) v0.25.2]

これで、testCompileClasspath構成の未使用の依存関係が検出されます。

残念ながら、これはプラグインの動作に一貫性がありません。 その結果、3つのエラーが発生します。

4. 結論

このチュートリアルでは、Gradleビルドで未使用の依存関係を見つける方法を説明しました。

まず、一般的な設定について説明しました。 その後、さまざまな依存関係とその使用法で報告されたエラーを調査しました。

最後に、テキストベースのレポートを生成する方法を見ました。

いつものように、完全なコードサンプルはGitHubで見つけることができます。