Gradleの依存関係解決で困ったとき、まず使っている基本タスク

## はじめに

Gradleで依存関係の解決を行っていると、こんな問題に直面することがあります。

* 依存関係の解決に失敗する
* 思ってるのと違うバージョンが取得されている
* ビルドスクリプトに書いたのと違うバージョンが取得されている


こういう問題が起こる背景には、「間接依存」や「バージョン競合」など、Gradleでの依存解決の仕組みが関係していることがあります。
この記事では、そういった依存関係のトラブルへ直面した時に使っているGradleの標準タスク `dependencies`と`dependencyInsight`について整理します。

Gradle公式の、関連するページも貼っておきます↓

https://docs.gradle.org/current/userguide/viewing_debugging_dependencies.html


## タスク概要

### dependencies

`dependencies`タスクは、依存関係の全体像を把握するためのタスクです。

```sh:sh
./gradlew dependencies
```

よく使うオプションとして、`--configuration`で「構成」を指定します。
たとえばコンパイル時に必要な依存関係を表示したければ`compileClasspath`を使う、実行時の依存関係が見たければ`runtimeClasspath`などです。
他にも `testCompileClasspath` や `testRuntimeClasspath` といったテスト用の構成もあります。

```sh:sh
./gradlew dependencies --configuration runtimeClasspath
```

タスクを実行すると、依存関係の親子関係がツリーで表示されるので、「どのライブラリがどのライブラリを引き込んでいるのか」などを把握することが出来ます。

ただ、大きなプロジェクトでは解決している依存関係も多いので、出力が膨大で把握するのが大変なときもあります。その際はgrepするなどして適宜絞り込みましょう。

```sh:sh
./gradlew dependencies --configuration runtimeClasspath | grep <ライブラリ名など>
```


### dependencyInsight

`dependencyInsight`は、特定の依存関係について詳細を把握するためのタスクです。

```sh: sh
./gradlew dependencyInsight --dependency <ライブラリ名>
```

指定されたライブラリの詳細、つまり「どこから来たのか」「なぜそのバージョンが選ばれたのか」などが出力されます。
ちなみにライブラリ名の指定は部分一致もOKなのですが、基本的には最初にマッチしたものを表示する仕様のようなので、できるだけ正確に（違うものがマッチしないように）指定しましょう。

なお`dependencyInsight`も、`--configuration`オプションで構成を指定できます。

```sh: sh
./gradlew dependencyInsight --dependency <ライブラリ名> --configuration compileClasspath
```

#### サンプル

例えば私の手元にあるプロジェクトで、`quarkus-hibernate-orm`について出力させると下記の出力が得られました。

```sh: sh
./gradlew dependencyInsight --dependency io.quarkus:quarkus-hibernate-orm

> Task :master-service:dependencyInsight
io.quarkus:quarkus-hibernate-orm:3.4.1
  Variant compile:
    | Attribute Name                     | Provided | Requested    |
    |------------------------------------|----------|--------------|
    | org.gradle.status                  | release  |              |
    | org.gradle.category                | library  | library      |
    | org.gradle.libraryelements         | jar      | classes      |
    | org.gradle.usage                   | java-api | java-api     |
    | org.gradle.dependency.bundling     |          | external     |
    | org.gradle.jvm.environment         |          | standard-jvm |
    | org.gradle.jvm.version             |          | 21           |
    | org.jetbrains.kotlin.platform.type |          | jvm          |
   Selection reasons:
      - By constraint
      - Forced

io.quarkus:quarkus-hibernate-orm:3.4.1
\--- io.quarkus:quarkus-bom:3.4.1
     \--- compileClasspath (requested io.quarkus:quarkus-bom:{strictly 3.4.1})

io.quarkus:quarkus-hibernate-orm -> 3.4.1
\--- compileClasspath
```

この出力で、たとえば`Selection reasons`を見れば、`By constraint` `Forced`と出ているので、それぞれ「制約があって選ばれた」「特定のバージョンに固定されている」のが分かります。

また、そのあとを見ていくと、`requested io.quarkus:quarkus-bom:{strictly 3.4.1}`とあるので、`quarkus-bom`から`quarkus-hibernate-orm`が引き込まれており、しかも`3.4.1`を`strictly`指定で要求していることが分かります。


## 依存関係の調査方法

簡単な例ですが、依存関係の解決について調べるときのよくある流れを。

1. まず構成ごとの全体像を出す

```sh: sh
./gradlew dependencies --configuration runtimeClasspath
```

1. `grep`で絞り込んでアタリをつける。ここでは`quarkus`で絞ってみる

```sh: sh
./gradlew dependencies --configuration runtimeClasspath | grep quarkus
```

1. 絞り込んだ出力からフルネームを取得して、`dependencyInsight`で依存関係について深堀りする

```sh: sh
./gradlew dependencyInsight --dependency io.quarkus:quarkus-hibernate-orm --configuration runtimeClasspath
```

この流れを、依存関係を追いかける時の基本セットみたいな感じで使っています。

`dependencyInsight`で、例えば依存関係の解決に失敗しているならその理由、思ってるのと違うバージョンが取得されているならそれが引き込まれている理由が分かるはずです。
あとはそれを手がかりに、依存関係の指定を見直すなど、具体的な改善のアクションに繋げられます。