Gradle Composite Build を用いて build.gradle.kts の設定を共通化する方法を解説します。
2023/02/22 Version Catalog をなるべく使うような構成となるように記事を更新しました
buildSrc よりも Composite Build がおすすめ
build.gradle.kts における設定の共通化はこれまでは buildSrc の利用が一般的でしたが、最近は buildSrc よりも Composite Build が推奨されることが多くなっています。
buildSrc は Gradle の構成全体に影響を与えるため、buildSrc の小さな変更がプロジェクト全体のリビルドに繋がる点などが問題であると言われています。
Gradle Plugin について
Composite Build で Gradle Plugin を実装することで設定の共通化を行う方針とします。
Gradle Plugin の作り方も参考となります。Gradle Plugin については以下の公式ドキュメントを参照してください。
前提
サンプルの解説にあたって以下の環境を想定します。いずれも必須というわけではありません。
- Gradle 8.0
- Composite Build はかなり古く、 Gradle 3.1 から対応しているようです
- build.gradle.kts (Kotlin DSL) 環境であること
- Version Catalog を使用していること
Composite Build の導入
全体構成
全体構成は以下のようになります。build-logic
モジュールが Composite Build です。
Version Catalog の設定
例として、以下のように Version Catalog を設定します。
[plugins]
の buildlogic-...
に Plugin Id を定義しています。Composite Build の Plugin はバージョンを持たないため unspecified
を設定します。 unspecified
以外の指定では Plugin のバージョン解決に失敗します。
gradle/libs.versions.toml
[versions]
gradle-android = "7.4.1"
gradle-android-compile-sdk = "33"
gradle-android-target-sdk = "33"
gradle-android-min-sdk = "16"
...
[libraries]
gradle-android = { module = "com.android.tools.build:gradle", version.ref = "gradle-android" }
...
[plugins]
android-library = { id = "com.android.library", version.ref = "gradle-android" }
// Plugin Id の定義
buildlogic-android-application = { id = "build-logic.android.application", version = "unspecified" }
buildlogic-android-library = { id = "build-logic.android.library", version = "unspecified" }
buildlogic-dependencygraph = { id = "build-logic.dependency-graph", version = "unspecified" }
build-logic モジュールの追加
build-logic
モジュールを追加し、settings.gradle.kts を以下のように設定します。
ここではモジュール名は build-logic
としていますが任意の名前で構いません。複数の Composite Build モジュールを配置することもできます。
Version Catalog を共通化するため、明示的に親となるプロジェクトのファイルを読み込ませています。
build-logic/settings.gradle.kts
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
rootProject.name = "build-logic"
build-logic
モジュールに Plugin の設定を追加します。
build-logic/build.gradle.kts
plugins {
`kotlin-dsl`
}
dependencies {
implementation(libs.gradle.android)
}
gradlePlugin {
plugins {
// 以下のプラグインの定義はサンプルです
// id は Version Catalog の定義から取り出して設定しています
register("android.application") {
id = libs.plugins.buildlogic.android.application.get().pluginId
// Version Catalog を使わない場合は以下のように id をそのまま設定します
//id = "build-logic.android.application"
implementationClass = "net.irgaly.buildlogic.AndroidApplicationPlugin"
}
register("android.library") {
id = libs.plugins.buildlogic.android.library.get().pluginId
implementationClass = "net.irgaly.buildlogic.AndroidLibraryPlugin"
}
register("dependency-graph") {
id = libs.plugins.buildlogic.dependencygraph.get().pluginId
implementationClass = "net.irgaly.buildlogic.ProjectDependencyGraphPlugin"
}
}
}
ここではサンプルのプラグインを記載していますが、任意のプラグインをいくつでも追加できます。用途に合わせて好きなだけプラグインを追加しましょう。
-
register("")
の名前は他と被らなければなんでも構いません。他の箇所から使うこともない名前です。 -
id
はプラグインを使用する module から指定するプラグイン ID 名を付けます。他のプラグイン ID と被らなければなんでも構いません。 -
implementationClass
はプラグインの実装クラスを指定します
Plugin 実装の追加
build-logic/build.gradle.kts
に定義したクラスを追加します。
たとえば「Android ライブラリモジュールに共通で適用したいプラグイン」 として AndroidLibraryPlugin
を作るとすると以下のようなものになります。
build-logic/src/main/kotlin/net/irgaly/buildlogic/AndroidLibraryPlugin.kt
package net.irgaly.buildlogic
import org.gradle.api.Plugin
import org.gradle.api.Project
class AndroidLibraryPlugin: Plugin<Project> {
override fun apply(target: Project) {
// ここで自由にプロジェクトの設定を適用する
// たとえば以下のように書く
with(target) {
with(pluginManager) {
// 共通のプラグインの適用
apply("com.android.library")
}
// Android モジュール向けの共通設定を適用 (内容は Extensions.kt に定義)
configureAndroid()
// Android ライブラリモジュール向けの共通設定を適用 (内容は Extensions.kt に定義)
configureAndroidLibrary()
}
}
}
サンプルとして共通設定のロジックを Extensions.kt に切り出しています。Extensions.kt は以下の通りです。
build-logic/src/main/kotlin/net/irgaly/buildlogic/Extensions.kt
package net.irgaly.buildlogic
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.LibraryExtension
import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalog
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.getByType
/**
* android build 共通設定を適用する
*/
fun Project.configureAndroid() {
extensions.configure<BaseExtension> {
// 任意の共通設定を実装
// サンプルとして VersionCatalog からバージョン指定を取り出す実装をしています
compileSdkVersion(libs.version("gradle-android-compile-sdk").toInt())
defaultConfig {
minSdk = libs.version("gradle-android-min-sdk").toInt()
targetSdk = libs.version("gradle-android-target-sdk").toInt()
}
}
}
/**
* android library 共通設定を適用する
*/
fun Project.configureAndroidLibrary() {
extensions.configure<LibraryExtension> {
// 任意の共通設定を実装
buildFeatures {
buildConfig = false
}
}
}
/**
* VersionCatalog の取得
*/
val Project.libs: VersionCatalog get() {
return extensions.getByType<VersionCatalogsExtension>().named("libs")
}
/**
* VersionCatalog version の取得
*/
fun VersionCatalog.version(name: String): String {
return findVersion(name).get().requiredVersion
}
build-logic モジュールを includeBuild() で取り込む
Composite Build モジュールをプラグインとして使えるようにします。
プロジェクト全体の settings.gradle.kts へ includeBuild()
設定を追加します。
settings.gradle.kts
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
}
rootProject.name = "gradle-composite-build"
include(":sample:android")
includeBuild("build-logic") // includeBuild で Composite Build を読み込む
rootProject でプラグインのバージョンを指定する
マルチモジュールでは rootProject の plugins ブロックでバージョンを指定します。apply false
で読み込むことで、Plugin を rootProject へ適用せずにバージョンを指定することができます。
rootProject でバージョンを指定しないと、他のモジュールで Plugin のバージョン不整合のエラーとなります。
build.gradle.kts
plugins {
// Plugin バージョンを指定するが、apply false により rootProject ではプラグインは実行しない
alias(libs.plugins.buildlogic.android.application) apply false
// Version Catalog を使わない場合は以下のようにバージョンを指定します
//id("build-logic.android.application") version "unspecified" apply false
alias(libs.plugins.buildlogic.android.library) apply false
// rootProject に適用するには apply false なしで読み込み
alias(libs.plugins.buildlogic.dependencygraph)
}
...
モジュールにプラグインを適用する
このサンプルでは AndroidLibraryPlugin を build-logic.android.library
として利用できるようにしました。
プラグインを適用するには、利用したいモジュールの build.gradle.kts で以下のように設定します。
sample/android/build.gradle.kts
plugins {
alias(libs.plugins.buildlogic.android.library)
// Version Catalog を使用しない場合は以下のように id 指定で適用します
//id("build-logic.android.library")
}
...
上記のようにプラグインを適用すると、このモジュールに対して AndroidLibraryPlugin.apply()
が実行されます。
Tips
プラグインの動作を変更できるようにしたい
Gradle Plugin は .apply()
に対してパラメーターを渡したりすることはできません。つまり、プラグインの適用 (.apply()
) そのものの動作を変更することはできません。
id("...")
でプラグインを読み込むとプラグインに含まれるクラスを利用できるようになります。パラメーターを渡して設定したいものがあれば、build.gradle.kts から必要なクラスを直接利用します。
id("...") apply false
とすると、プラグインの .apply()
を実行せずに Composite Build モジュール内のクラスを読み込むことも可能です。
import net.irgaly.buildlogic.configureAndroid // Composite Build モジュール内のクラスを参照できる
plugins {
// build-logic モジュールのクラスを読み込み、 .apply() も実行する
id("build-logic.android.library")
// build-logic モジュールのクラスを読み込むが、 .apply() は実行しない
//id("build-logic.android.library") apply false
}
configureAndroid(moduleName = "feature1") // パラメーターを渡して任意の機能の呼び出し
...
プラグインの適用 (.apply()
) の動作を変更することはできませんが、Gradle Plugin で追加した Task の設定をするためであれば Extension Objects を利用できます。詳細は以下のドキュメントの Making the plugin configurable
章を参照してください。
Gradle Plugin の実行順について
plugins ブロックでプラグインを適用することで対象プラグインのクラスなどを import などで参照することができるようになります。これは build.gradle.kts を実行するときに plugins ブロックを先行評価することで実現しています。
build.gradle.kts とプラグインの実行順は以下のとおりです。
- build.gradle.kts の plugins ブロックが実行される
- 読み込まれたプラグインの .apply() を実行する
- 1.~2. によってプラグインが読み込まれた状態で、build.gradle.kts の plugins ブロック以外を実行する
すべてのプラグインの .apply() が完了してから build.graldle.kts が評価されるため、プラグインからさらに他のプラグインを読み込ませることが可能となっています。
たとえば、以下のような build.gradle.kts と AndroidApplicationPlugin (build-logic.android.application
) を用意して Gradle プロジェクトを読み込ませると、「① → ② → ③-1 → ③-2」 の順に実行されます。
この build.gradle.kts を読み込ませたときのログは以下のようになります。
...
> Configure project :sample:android
---- B
---- C
---- H
---- I
---- J
---- A
---- D
---- E
---- F
---- G
...
参考
Composite Build を用いて設定を共通化する実装は Now in Android が参考となります。