所感箱

所感を書きためていきます

マルチモジュールもスタンダードになりつつあるので、バージョンカタログを整理してみた

モチベーション

筆者はAndroid開発を主戦場で、日々Gradleを使って依存関係の管理をします。 昨今はマルチモジュールもスタンダードになってきており、依存関係の管理に工夫が必要になっています。そんな依存関係の管理の方法として、Gradleからバージョンカタログという機能が提供されており、利用する機会が増えてきそうなので、今後のためにも整理してみます。

簡単な解説

Gradle 7.0からfeature previewとして導入され、Grade 7.6にて正式導入された機能で、端的に書くと、マルチモジュールで依存関係とプラグインを簡単に一括管理する方法です。これだけでも便利な機能だと思えますが、もう少し踏み込んでメリットを整理してみます。

バージョンカタログのメリット

docs.gradle.org

gradleのサイトにあるメリットを直訳すると以下の通り。

  • Gradle はカタログごとにタイプセーフなアクセサーを生成するため、IDE でオートコンプリートを使用して依存関係を簡単に追加できます。
  • 各カタログは、ビルドのすべてのプロジェクトに表示されます。これは、依存関係のバージョンを宣言し、そのバージョンへの変更がすべてのサブプロジェクトに適用されることを確認するための中心的な場所です。
  • カタログでは、一般的に一緒に使用される「依存関係のグループ」である依存関係バンドルを宣言できます。
  • カタログでは、依存関係のグループと名前を実際のバージョンから分離し、代わりにバージョン参照を使用できるため、複数の依存関係間でバージョン宣言を共有できます。

それぞれ簡単に整理してみる。

IDEでのオートコンプリート

特定のスクリプトファイルの拡張プロパティで、バージョン数値を管理し参照する方法もあります、オートコンプリートには対応しておらず、別ファイルを確認する必要があります。それを、以下の画像のように、IDEがフォローしてくれるようになる機能です。ちなみに宣言に移動などもサポートしています。

なお、筆者は最初オートコンプリートが機能しませんでしたが、IDEのアップデートをしたところ無事に機能しました。

依存関係のバージョン宣言とサブプロジェクトへの適用

依存関係の宣言を集約する機能です。例えば、以下のように、settings.gradleに依存関係の定義を書いておくと他の全てのモジュールから参照できるようになります。

// [settings.gradle]
dependencyResolutionManagement {
    versionCatalogs {
        libs {
            library('room-runtime', 'androidx.room:room-runtime:2.5.2')
            library('room-ktx', 'androidx.room:room-ktx:2.5.2')
            library('room-rxjava3', 'androidx.room:room-rxjava3:2.5.2')
        }
    }
}

// [app/build.gradle]
dependencies {
    implementation libs.room.runtime
    implementation libs.room.ktx
    implementation libs.room.rxjava3
}

// [hoge/build.gradle]
dependencies {
    implementation libs.room.runtime
    implementation libs.room.ktx
    implementation libs.room.rxjava3
}

versionCatalogsの定義により、エイリアスとしてアクセサーが提供され、参照側ではバージョンの指定は不要となります。エイリアス- or _ or . を区切り文字として、マッピングが行われ、例えば、room-runtimeという定義であれば、room.runtimeとして展開されます。

なお、settings.gradleに記述する以外に、tomlファイルに定義することが可能で、 gradle/libs.versions.tomlのパスにファイルを配置すると、デフォルトで参照してくれます。

# ./gradle/libs.versions.toml

[versions]

[libraries]
room-runtime = { group = "androidx.room", name = "room-runtime", version = "2.5.2" }
room-ktx = { group = "androidx.room", name = "room-ktx", version = "2.5.2" }
room-rxjava3 = { group = "androidx.room", name = "room-rxjava3", version = "2.5.2" }

[bundles]

複数の依存関係間でバージョン宣言の共有

複数のモジュールで、共通のバージョンニングルールが適用されているライブラリを、一つの宣言で管理できるようになる機能です。

[versions]
# room用のバージョンを宣言
room = "2.5.2"

[libraries]
# それぞれ、version.refにversion.roomを指定
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
room-rxjava3 = { group = "androidx.room", name = "room-rxjava3", version.ref = "room" }

[bundles]

Android開発では、共通バージョンニングのライブラリも多いので、宣言的にルールをつけられると管理の手間を下げられると思います。なお、Renovateもバージョンカタログに対応しており、tomlファイルに対してのPRも生成してくれるようです。

renovate/docs/usage/java.md at main · renovatebot/renovate · GitHub

依存関係バンドルの宣言

最後は、いくつかのライブラリをまとめて利用するケースに対応する機能です。 例えば、room-runtime, room-ktx, room-rxjava3を一緒に利用している場合は以下のようになります

# ./gradle/libs.versions.toml

[versions]
room = "2.5.2"

[libraries]
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
room-rxjava3 = { group = "androidx.room", name = "room-rxjava3", version.ref = "room" }

[bundles]
# room-runtime, room-ktx, room-rxjava3をバンドル宣言
room = ["room-runtime", "room-ktx", "room-rxjava3"]

// [app/build.gradle]
dependencies {
    # room-runtime, room-ktx, room-rxjava3が全て参照される
    implementation libs.bundle.room
}

必ず一緒に利用するものを宣言的に纏まることで、MECEに管理していくことができそうです。

所感

Gradleの公式の機能だけあって、使わない手はないと感じました。 Androidデベロッパーサイトでも移行ガイドが提供されているので、安心して利用できます。 移行過程で、依存ライブラリの要不要も整理していくと、お掃除も目指せそうですね。

ビルドをバージョン カタログに移行する  |  Android デベロッパー  |  Android Developers