Androidでマルチモジュールを作ると、build.gradle.ktsの記述を共通化したくなる。業務のプロジェクトでbuild-logicを使って共通化していたものの、はっきり理解できていないところがあったので、now in androidやDroidkaigi 2023のリポジトリを眺めたりしながら、個人のリポジトリを作って探索した。(publicリポジトリなので自由にみていってください)
基本的な使い方はここでは言及せず、気付きについてメモを残す。
buildSrcのやり方と、convention plugin(build-logic)を使うやり方
convention pluginは、特定プロジェクト用途のpluginのこと。
どの方法で柔軟にAndroidのGradleのビルドロジックをまとめるか #Android - Qiitaにもあるが、buildSrcを使うやり方もある。buildSrcのやり方は、buildSrcディレクトリ配下に共通設定を書く、convention pluginのやり方は自分でディレクトリを作成して共通設定を書くやり方だと思う(間違ってたらご指摘ください)。convention pluginのやり方だと任意にディレクトリの名前を決められる。now in androidやdroidkaigiを参考にbuild-logicという名前にしている。convention pluginを使う際は、ルートのsettings.gradle.ktsで、includeBuild()
を使って認識させる。
buildSrcはgradleを使うと自動で認識される。デメリットとしてはビルドのホットパスになるので、ビルド時間に関わってくるようだ。あまり深堀りできてはいないが、buildSrcは小規模プロジェクトで、build-logicは大規模プロジェクトに有利そう。
自分はconvention pluginのほうがむしろ馴染みがあるので、特に理由がない場合はconvention pluginを使っていきたい。
build-logic内の構成
最終的に、以下の様になった。
build-logic
├──build.gradle.kts
├──settings.gradle.kts
├──gradle.properties
└──src/main/kotlin
└── PublicPlaygroundapplicationPlugin.kt
droidkaigiでは、src/main/kotlinの下にio/github/droidkaigi/confsched2023/conventionみたいに続く。now in androidでは、build-logicにconventionモジュールがある作りになっている。
build-logicが大きくなるようであれば、ディレクトリを切ることを検討してもよいが、自分の場合はとりあえずこのままで良いと思った。
now in androidでもdroidkaigiでも、build-logicのbuild.gradle.ktsでタスクグループを設定している。自分は今はやっていない。グルーピングされるのでタスクを実行するときにわかりやすくなるのだと思う。
group = "com.google.samples.apps.nowinandroid.buildlogic"
build-logicでバージョンカタログを使う
build-logic内のsettings.gradle.ktsでバージョンカタログの記述をすると、バージョンカタログの機能を使えるようになる。filesの引数のディレクトリのありかは、settings.gradle.ktsからの相対的な位置となる。
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
build-logic内ではbuild tool等の関数を利用してconvention pluginを記述したくなる。そのために必要なライブラリを、libs.versions.tomlの[libraries]として登録する。libs.versions.tomlには[libraries]と[plugins]の項目があり、librariesとして使う必要がある。
[libraries]
android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }
now in androidもdroidkaigiも必要なものを[libraries]に書いてbuild-logicで利用している点は同じだが、droidkaigiの方は[bundles]としてpluginを束ねて書くやり方をしている。こちらのほうが、読みやすい。
// build.gradle.kts(:build-logic)
dependencies {
implementation(libs.bundles.plugins)
// 略
}
// libs.versions.toml
[bundles]
plugins = [
"androidGradlePlugin",
"kotlinGradlePlugin",
"hiltGradlePlugin",
"composeJbGradlePlugin",
"roborazziGradlePlugin",
"kspGradlePlugin",
"firebaseGradlePlugin",
"ktorfitGradlePlugin",
"kotlinxSerializationGradlePlugin",
"completeKotlinPlugin",
"detektGradlePlugin",
"ossLicensesPlugin",
"koverPlugin",
"firebaseCrashlyticsGradlePlugin"
]
build cleanのタスクを作る
build cleanのタスクをカスタムで作っておけば、サブプロジェクト配下のbuildディレクトリをすべて削除することができるので便利。
Type-safe project accessors
なにもしないと、サブモジュールで別のサブモジュールの依存を書くときには、implementation(project":module:sub")
みたいにする必要がある。このやり方だと補完も働かないし、typoする恐れもある。
settings.gradle.ktsで、enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
とすれば、type safeにすることができる。
// settings.gradle.kts
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
// build.gradle.kts
dependencies {
// これが
implementation(project(":module:path"))
// こう
implementation(projects.module.path)
}
自分はルートのsettings.gradle.ktsにも、build-logicのsettings.gradle.ktsにも設定を入れている。
kotlin option
kotlin optionの記述は、build-logicのbuild.gradle.ktsに書いておけば良さそう。
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = "17"
}
}
tips、はまりどころ
gradleを使う頻度は通常の実装コードと比べると、エラーからとりうる対策を導き出しづらい。まずはサンプルを忠実に、丁寧に写経していきたい。書き漏れがあるとエラーになるわけだが、どこを書き漏らしたかエラーを見ても分かりづらいので、丁寧に書いていこう。typoには普段より気をつけよう。
たとえば自分がミスしたところでいうと、build-logic内のsetteings.gradle.kts
をsettings.gradle.kt
にtypoしてしまった。これではsettings.gradle.ktに書いたversion catalogの設定を反映できず、libs.xx
を使おうとしても補完がきかず、動作もできなかった。
またApplicationPluginを作ったときに、com.android.application
のプラグインだけを入れて、org.jetbrains.kotlin.android
を入れ忘れてしまった。このときのエラーが、manifestからMainActivityを見つけられません、みたいなものであり、解決策がわからず、時間を使ってしまっていた。
また、build cleanやAndroid studioの再起動はどうしてもわからなかったときにやると治る可能性がある。キャッシュが残っていたり、indexが効かなくなっていたときにまずやってみよう。