📝

Now in Androidの読み解き - ビルドプラグイン

2022/08/04に公開

はじめに

キャッチアップがてら、以下リポジトリを読んでいて気になって調べたことを残していきます。
https://github.com/android/nowinandroid

その前にこのリポジトリは何者?

KotlinとJetpackComposeで完全に構築されたAndroidアプリ。Androidの設計と開発のベストプラクティスに従っており、開発者にとって役立つリファレンスとなることを目的としている。とのこと。
※現在開発の初期段階にありPlayStoreではまだ利用できない。 2022年8月現在

気になったこと

まずプロジェクトを見るときはbuild.gradleをみて、使用ライブラリやモジュールの関連を確認するようにしてます。で、早速気になったところがありました。

// app/build.gradel.kts
plugins {
    id("nowinandroid.android.application")
    id("nowinandroid.android.application.compose")
    id("nowinandroid.android.application.jacoco")
    kotlin("kapt")
    id("jacoco")
    id("dagger.hilt.android.plugin")
    id("nowinandroid.spotless")
    id("nowinandroid.firebase-perf")
}

id("nowinandroid.xxx.yyy")

まず、ここが気になりました。どのように動いて何をしているのか追ってみることにしました。

プロジェクトの構成はプロジェクトルート配下のgradleとつくファイルのを見ていけば、何かしらわかるので見ていきました。settings.gradle.ktsにそれっぽい記述あり

settings.gradle.kts

pluginManagement {
    includeBuild("build-logic") // この部分は何をしているか
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}

build-logicを取りこんでいるので、その部分を追っていきます。

build-logic/settings.gradle.kts

dependencyResolutionManagement {
    ...
    versionCatalogs {
        create("libs") {
            from(files("../gradle/libs.versions.toml"))
        }
    }
}

include(":convention")  // こちらを追う

build-logic/convention/build.gradle.kts

...
gradlePlugin {
    plugins {
        register("androidApplicationCompose") {
            id = "nowinandroid.android.application.compose"
            implementationClass = "AndroidApplicationComposeConventionPlugin"
        }
        register("androidApplication") {
            id = "nowinandroid.android.application"
            implementationClass = "AndroidApplicationConventionPlugin"
        }
        ...
    }
}

app/build.gradel.ktsで記述されていたidと同じ値が見つかりました。
id("nowinandroid.android.application")はどのような挙動をするのかというと、implementationClassに設定しているクラスを見てみます。
build-logic/convention/src/main/kotlin配下にありました。

AndroidApplicationConventionPlugin

class AndroidApplicationConventionPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        with(target) {
            with(pluginManager) {
                apply("com.android.application")
                apply("org.jetbrains.kotlin.android")
            }

            extensions.configure<BaseAppModuleExtension> {
                configureKotlinAndroid(this)
                defaultConfig.targetSdk = 32
            }
        }
    }

}
internal fun Project.configureKotlinAndroid(
    commonExtension: CommonExtension<*, *, *, *>,
) {
    commonExtension.apply {
        compileSdk = 32

        defaultConfig {
            minSdk = 21
        }

        compileOptions {
            sourceCompatibility = JavaVersion.VERSION_1_8
            targetCompatibility = JavaVersion.VERSION_1_8
            isCoreLibraryDesugaringEnabled = true
        }

        kotlinOptions {
            // Treat all Kotlin warnings as errors (disabled by default)
            allWarningsAsErrors = properties["warningsAsErrors"] as? Boolean ?: false

            freeCompilerArgs = freeCompilerArgs + listOf(
                "-opt-in=kotlin.RequiresOptIn",
                // Enable experimental coroutines APIs, including Flow
                "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
                "-opt-in=kotlinx.coroutines.FlowPreview",
                "-opt-in=kotlin.Experimental",
                // Enable experimental kotlinx serialization APIs
                "-opt-in=kotlinx.serialization.ExperimentalSerializationApi"
            )

            // Set JVM target to 1.8
            jvmTarget = JavaVersion.VERSION_1_8.toString()
        }
    }
    // gradle/libs.versions.tomlを読み取っている 
    val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")

    dependencies {
        add("coreLibraryDesugaring", libs.findLibrary("android.desugarJdkLibs").get())
    }
}

見慣れた記述がでてきました。なんとなく見えてきたかと思いますが、以下の設定をすると、設定したbuild.gradle.ktsに上記内容がマージされるようです。

結局どうなる

plugins {
    id("nowinandroid.android.application")
}

↓ 以下の内容と同義

plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
}

android {
    compileSdk = 32
    defaultConfig {
        targetSdk = 32
        minSdk = 21
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
        isCoreLibraryDesugaringEnabled = true
    }
    kotlinOptions {
        allWarningsAsErrors = properties["warningsAsErrors"] as? Boolean ?: false
        freeCompilerArgs = freeCompilerArgs + listOf(
            "-opt-in=kotlin.RequiresOptIn",
            // Enable experimental coroutines APIs, including Flow
            "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
            "-opt-in=kotlinx.coroutines.FlowPreview",
            "-opt-in=kotlin.Experimental",
            // Enable experimental kotlinx serialization APIs
            "-opt-in=kotlinx.serialization.ExperimentalSerializationApi"
        )
        jvmTarget = JavaVersion.VERSION_1_8.toString()
    }
}

dependencies {
    implementation(libs.android.desugarJdkLibs)
}

再度、app/build.gradle.ktsを見ると、必要な記述が足りないのに気づくと思います。このように動作することでビルドの設定を構築しているようです。

感想

マルチモジュールの構成は主流になってきてると思いますし、各種設定やバージョンなどを合わせる方法は試行錯誤してましたので、このような書き方ができることを知れて勉強になりました。
ざっくりではありますが、流れが追えてよかったです。

Discussion