build: build-logic 约定插件 (mcl.*)
This commit is contained in:
36
build-logic/convention/DESIGN_NOTES.md
Normal file
36
build-logic/convention/DESIGN_NOTES.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# DESIGN_NOTES — 约定插件
|
||||
|
||||
## 为什么存在
|
||||
|
||||
多模块项目里,`compileSdk`/`minSdk`/Java target、Compose、Hilt、Room、Lint、Spotless 等配置若在每个模块重复手写,必然漂移。约定插件把这些配置**集中编码**,模块只声明"我是什么类型"(`mcl.*` 插件),不关心"怎么配"。
|
||||
|
||||
这也是 harness-engineering 的"严谨性转移":把约束写进确定性规则,而不是依赖记忆。
|
||||
|
||||
## 插件映射
|
||||
|
||||
| 插件 id | 实现类 | 作用 |
|
||||
|---------|--------|------|
|
||||
| `mcl.kotlin.library` | `JvmLibraryConventionPlugin` | 纯 JVM(kotlin-jvm + jvmTarget 11 + spotless + junit) |
|
||||
| `mcl.android.library` | `AndroidLibraryConventionPlugin` | Android 库(application/library 插件的库版 + lint + spotless + compileSdk/minSdk) |
|
||||
| `mcl.android.library.compose` | `AndroidLibraryComposeConventionPlugin` | = library + Compose |
|
||||
| `mcl.android.application.compose` | `AndroidApplicationComposeConventionPlugin` | = application + Compose |
|
||||
| `mcl.android.feature` | `AndroidFeatureConventionPlugin` | = library.compose + hilt + 导航/lifecycle/共享模块依赖 |
|
||||
| `mcl.android.hilt` | `AndroidHiltConventionPlugin` | KSP + Hilt(hilt-android + hilt-compiler) |
|
||||
| `mcl.android.room` | `AndroidRoomConventionPlugin` | KSP + Room(runtime/ktx/compiler + schema 目录) |
|
||||
| `mcl.android.lint` | `AndroidLintConventionPlugin` | lint 配置(checkDependencies、disable GradleDependency) |
|
||||
| `mcl.root` | `RootPlugin` | 注册 `verifyArchitecture` |
|
||||
|
||||
## 关键实现要点
|
||||
|
||||
- **内置 Kotlin**:AGP 9 内置 Kotlin,因此**不**显式应用 `org.jetbrains.kotlin.android`;Kotlin 编译选项通过 `KotlinAndroidProjectExtension` 的 `compilerOptions` 配置(见 `KotlinAndroid.kt`)。
|
||||
- **compose 插件自包含**:`*.compose` 插件先 `apply(mcl.android.library/application)` 再叠加 Compose,所以模块只需声明 compose 插件一个。
|
||||
- **helper 函数**:`KotlinAndroid.kt`(SDK/Java/Kotlin)、`AndroidCompose.kt`(compose feature + BOM + tooling + material3)、`Spotless.kt`(ktlint,无 license header)拆成独立函数,插件只做编排。
|
||||
- **`libs` 访问器**:`ProjectExtensions.kt` 里的 `val Project.libs` 让插件能引用版本目录(`libs.findLibrary/findVersion`)。
|
||||
|
||||
## 如何新增
|
||||
|
||||
1. **新模块**:建 `build.gradle.kts`(选对插件 + namespace)+ 空 `AndroidManifest.xml`(Android 模块),在 `settings.gradle.kts` include,在 `RootPlugin.kt` 的 allow-list 加一行。
|
||||
2. **新插件**:在 `convention/build.gradle.kts` 的 `gradlePlugin { plugins { register(...) } }` 注册,并在 `gradle/libs.versions.toml` 的 `[plugins]` 里加一个 `mcl-*` 条目(无 version)。
|
||||
3. **改配置**:优先改 helper 函数,让所有适用模块一并生效。
|
||||
|
||||
> 注意:`mcl-android-application` 与 `mcl-android-library` 因有 `-compose` 变体(叶子也是前缀),在 `convention/build.gradle.kts` 里引用它们的 plugin id 时要用 `.asProvider().get().pluginId`,其余用 `.get().pluginId`。
|
||||
79
build-logic/convention/build.gradle.kts
Normal file
79
build-logic/convention/build.gradle.kts
Normal file
@@ -0,0 +1,79 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
group = "com.mingyuan.androidclient.buildlogic"
|
||||
|
||||
// build-logic targets the JDK used to run Gradle (17), unrelated to the device/runtime JDK.
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
jvmTarget = JvmTarget.JVM_17
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.android.gradlePlugin)
|
||||
compileOnly(libs.kotlin.gradlePlugin)
|
||||
compileOnly(libs.ksp.gradlePlugin)
|
||||
compileOnly(libs.room.gradlePlugin)
|
||||
compileOnly(libs.spotless.gradlePlugin)
|
||||
}
|
||||
|
||||
tasks {
|
||||
validatePlugins {
|
||||
enableStricterValidation = true
|
||||
failOnWarning = true
|
||||
}
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
register("androidApplication") {
|
||||
id = libs.plugins.mcl.android.application.asProvider().get().pluginId
|
||||
implementationClass = "AndroidApplicationConventionPlugin"
|
||||
}
|
||||
register("androidApplicationCompose") {
|
||||
id = libs.plugins.mcl.android.application.compose.get().pluginId
|
||||
implementationClass = "AndroidApplicationComposeConventionPlugin"
|
||||
}
|
||||
register("androidLibrary") {
|
||||
id = libs.plugins.mcl.android.library.asProvider().get().pluginId
|
||||
implementationClass = "AndroidLibraryConventionPlugin"
|
||||
}
|
||||
register("androidLibraryCompose") {
|
||||
id = libs.plugins.mcl.android.library.compose.get().pluginId
|
||||
implementationClass = "AndroidLibraryComposeConventionPlugin"
|
||||
}
|
||||
register("androidFeature") {
|
||||
id = libs.plugins.mcl.android.feature.get().pluginId
|
||||
implementationClass = "AndroidFeatureConventionPlugin"
|
||||
}
|
||||
register("androidHilt") {
|
||||
id = libs.plugins.mcl.android.hilt.get().pluginId
|
||||
implementationClass = "AndroidHiltConventionPlugin"
|
||||
}
|
||||
register("androidRoom") {
|
||||
id = libs.plugins.mcl.android.room.get().pluginId
|
||||
implementationClass = "AndroidRoomConventionPlugin"
|
||||
}
|
||||
register("androidLint") {
|
||||
id = libs.plugins.mcl.android.lint.get().pluginId
|
||||
implementationClass = "AndroidLintConventionPlugin"
|
||||
}
|
||||
register("kotlinLibrary") {
|
||||
id = libs.plugins.mcl.kotlin.library.get().pluginId
|
||||
implementationClass = "JvmLibraryConventionPlugin"
|
||||
}
|
||||
register("root") {
|
||||
id = libs.plugins.mcl.root.get().pluginId
|
||||
implementationClass = "RootPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
import com.mingyuan.androidclient.buildlogic.configureAndroidCompose
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
|
||||
/**
|
||||
* Compose for the application module. Applies [AndroidApplicationConventionPlugin] (compileSdk,
|
||||
* lint, formatting) and then adds Compose. Apply this single plugin for the app module.
|
||||
*/
|
||||
class AndroidApplicationComposeConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
apply(plugin = "mcl.android.application")
|
||||
apply(plugin = "org.jetbrains.kotlin.plugin.compose")
|
||||
|
||||
configureAndroidCompose(extensions.getByType<ApplicationExtension>())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
import com.mingyuan.androidclient.buildlogic.configureKotlinAndroid
|
||||
import com.mingyuan.androidclient.buildlogic.configureSpotless
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
|
||||
/**
|
||||
* Base convention for the application module. Applies the Android application plugin, Kotlin +
|
||||
* Android SDK defaults, lint, and deterministic formatting. Compose is added by
|
||||
* [AndroidApplicationComposeConventionPlugin].
|
||||
*/
|
||||
class AndroidApplicationConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
apply(plugin = "com.android.application")
|
||||
apply(plugin = "mcl.android.lint")
|
||||
|
||||
extensions.configure<ApplicationExtension> {
|
||||
configureKotlinAndroid(this)
|
||||
defaultConfig.targetSdk = 37
|
||||
defaultConfig.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
testOptions.animationsDisabled = true
|
||||
}
|
||||
|
||||
configureSpotless()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import com.mingyuan.androidclient.buildlogic.libs
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
|
||||
/**
|
||||
* Convention for a feature module: Compose + Hilt, plus the shared dependencies every feature
|
||||
* needs (shared UI, design system, domain models, and Navigation). Feature modules should apply
|
||||
* only this plugin (plus any feature-specific additions).
|
||||
*/
|
||||
class AndroidFeatureConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
apply(plugin = "mcl.android.library.compose")
|
||||
apply(plugin = "mcl.android.hilt")
|
||||
|
||||
dependencies {
|
||||
"implementation"(project(":core:ui"))
|
||||
"implementation"(project(":core:designsystem"))
|
||||
"implementation"(project(":core:model"))
|
||||
"implementation"(project(":core:domain"))
|
||||
"implementation"(libs.findLibrary("androidx-navigation-compose").get())
|
||||
"implementation"(libs.findLibrary("androidx-lifecycle-runtime-compose").get())
|
||||
"implementation"(libs.findLibrary("androidx-lifecycle-viewmodel-compose").get())
|
||||
"implementation"(libs.findLibrary("androidx-hilt-navigation-compose").get())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import com.mingyuan.androidclient.buildlogic.libs
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
|
||||
/**
|
||||
* Applies Hilt via KSP (not kapt) and wires the Hilt runtime + compiler.
|
||||
*/
|
||||
class AndroidHiltConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
with(pluginManager) {
|
||||
apply("com.google.devtools.ksp")
|
||||
apply("com.google.dagger.hilt.android")
|
||||
}
|
||||
dependencies {
|
||||
"implementation"(libs.findLibrary("hilt-android").get())
|
||||
"ksp"(libs.findLibrary("hilt-compiler").get())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import com.android.build.api.dsl.LibraryExtension
|
||||
import com.mingyuan.androidclient.buildlogic.configureAndroidCompose
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
|
||||
/**
|
||||
* Compose for an Android library module. Applies [AndroidLibraryConventionPlugin] (compileSdk,
|
||||
* lint, formatting) and then adds Compose. Apply this single plugin for Compose-enabled libraries.
|
||||
*/
|
||||
class AndroidLibraryComposeConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
apply(plugin = "mcl.android.library")
|
||||
apply(plugin = "org.jetbrains.kotlin.plugin.compose")
|
||||
|
||||
configureAndroidCompose(extensions.getByType<LibraryExtension>())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import com.android.build.api.dsl.LibraryExtension
|
||||
import com.mingyuan.androidclient.buildlogic.configureKotlinAndroid
|
||||
import com.mingyuan.androidclient.buildlogic.configureSpotless
|
||||
import com.mingyuan.androidclient.buildlogic.libs
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
|
||||
/**
|
||||
* Base convention for Android library modules. Applies the Android library plugin, Kotlin +
|
||||
* Android SDK defaults, lint, formatting, and shared test dependencies. Compose is added by
|
||||
* [AndroidLibraryComposeConventionPlugin].
|
||||
*/
|
||||
class AndroidLibraryConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
apply(plugin = "com.android.library")
|
||||
apply(plugin = "mcl.android.lint")
|
||||
|
||||
extensions.configure<LibraryExtension> {
|
||||
configureKotlinAndroid(this)
|
||||
defaultConfig.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
configureSpotless()
|
||||
|
||||
dependencies {
|
||||
"testImplementation"(libs.findLibrary("junit").get())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
import com.android.build.api.dsl.LibraryExtension
|
||||
import com.android.build.api.dsl.Lint
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
|
||||
/**
|
||||
* Project-wide Android Lint configuration, applied to application and library modules.
|
||||
*/
|
||||
class AndroidLintConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
when {
|
||||
pluginManager.hasPlugin("com.android.application") ->
|
||||
configure<ApplicationExtension> { lint(Lint::configure) }
|
||||
pluginManager.hasPlugin("com.android.library") ->
|
||||
configure<LibraryExtension> { lint(Lint::configure) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Lint.configure() {
|
||||
checkDependencies = true
|
||||
disable += "GradleDependency"
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import androidx.room.gradle.RoomExtension
|
||||
import com.google.devtools.ksp.gradle.KspExtension
|
||||
import com.mingyuan.androidclient.buildlogic.libs
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
|
||||
/**
|
||||
* Applies Room (via KSP) and wires runtime/ktx/compiler, generating Kotlin code and storing
|
||||
* database schemas for future auto-migrations.
|
||||
*/
|
||||
class AndroidRoomConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
apply(plugin = "androidx.room")
|
||||
apply(plugin = "com.google.devtools.ksp")
|
||||
|
||||
extensions.configure<KspExtension> {
|
||||
arg("room.generateKotlin", "true")
|
||||
}
|
||||
|
||||
extensions.configure<RoomExtension> {
|
||||
schemaDirectory("$projectDir/schemas")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
"implementation"(libs.findLibrary("androidx-room-runtime").get())
|
||||
"implementation"(libs.findLibrary("androidx-room-ktx").get())
|
||||
"ksp"(libs.findLibrary("androidx-room-compiler").get())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import com.mingyuan.androidclient.buildlogic.configureKotlinJvm
|
||||
import com.mingyuan.androidclient.buildlogic.configureSpotless
|
||||
import com.mingyuan.androidclient.buildlogic.libs
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
|
||||
/**
|
||||
* Convention for pure Kotlin/JVM modules (no Android dependency) such as :core:model and
|
||||
* :core:domain. Keeping these modules Android-free keeps them fast to test and forces domain
|
||||
* logic to stay framework-agnostic.
|
||||
*/
|
||||
class JvmLibraryConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
apply(plugin = "org.jetbrains.kotlin.jvm")
|
||||
configureKotlinJvm()
|
||||
configureSpotless()
|
||||
dependencies {
|
||||
"testImplementation"(libs.findLibrary("junit").get())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
89
build-logic/convention/src/main/kotlin/RootPlugin.kt
Normal file
89
build-logic/convention/src/main/kotlin/RootPlugin.kt
Normal file
@@ -0,0 +1,89 @@
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.register
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Root convention plugin. Registers the `verifyArchitecture` task, which enforces the module
|
||||
* dependency direction declared in `docs/architecture.md`.
|
||||
*
|
||||
* The allow-list lives in ONE place here; adding a module means adding a line to [allowList]
|
||||
* (and mirroring it in the architecture doc). The check reads each module's `build.gradle.kts`
|
||||
* as a file (config-cache safe) and rejects any `project(":...")` reference that is not allowed.
|
||||
*/
|
||||
class RootPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
val featureAllowed = setOf(
|
||||
":core:ui",
|
||||
":core:designsystem",
|
||||
":core:domain",
|
||||
":core:model",
|
||||
)
|
||||
|
||||
val allowList = mapOf(
|
||||
":app" to setOf(
|
||||
":core:common", ":core:model", ":core:domain", ":core:designsystem",
|
||||
":core:ui", ":core:data", ":core:network", ":core:database", ":core:datastore",
|
||||
":feature:auth", ":feature:checkin", ":feature:plan", ":feature:course",
|
||||
":feature:survey", ":feature:feedback", ":feature:profile",
|
||||
),
|
||||
":core:common" to emptySet(),
|
||||
":core:model" to emptySet(),
|
||||
":core:domain" to setOf(":core:common", ":core:model"),
|
||||
":core:designsystem" to setOf(":core:common"),
|
||||
":core:ui" to setOf(":core:designsystem", ":core:model"),
|
||||
":core:network" to setOf(":core:common", ":core:model"),
|
||||
":core:database" to setOf(":core:model"),
|
||||
":core:datastore" to setOf(":core:model"),
|
||||
":core:data" to setOf(
|
||||
":core:domain", ":core:model", ":core:network", ":core:database", ":core:datastore",
|
||||
),
|
||||
":feature:auth" to featureAllowed,
|
||||
":feature:checkin" to featureAllowed,
|
||||
":feature:plan" to featureAllowed,
|
||||
":feature:course" to featureAllowed,
|
||||
":feature:survey" to featureAllowed,
|
||||
":feature:feedback" to featureAllowed,
|
||||
":feature:profile" to featureAllowed,
|
||||
)
|
||||
|
||||
val rootDir = target.rootDir
|
||||
|
||||
target.tasks.register("verifyArchitecture") {
|
||||
group = "verification"
|
||||
description = "Enforce module dependency direction (see docs/architecture.md)"
|
||||
|
||||
doLast {
|
||||
val projectDependency = Regex("""project\("(:[^"]+)"\)""")
|
||||
val violations = mutableListOf<String>()
|
||||
|
||||
for ((modulePath, allowed) in allowList) {
|
||||
val buildFile = File(
|
||||
rootDir,
|
||||
"${modulePath.removePrefix(":").replace(":", "/")}/build.gradle.kts",
|
||||
)
|
||||
if (!buildFile.exists()) {
|
||||
violations += "$modulePath: missing build.gradle.kts"
|
||||
continue
|
||||
}
|
||||
val declared = projectDependency
|
||||
.findAll(buildFile.readText())
|
||||
.map { it.groupValues[1] }
|
||||
.toSet()
|
||||
for (dep in declared) {
|
||||
if (dep !in allowed) violations += "$modulePath -> $dep"
|
||||
}
|
||||
}
|
||||
|
||||
if (violations.isNotEmpty()) {
|
||||
throw GradleException(
|
||||
"Architecture violations (see docs/architecture.md):\n" +
|
||||
violations.joinToString("\n") { " - $it" },
|
||||
)
|
||||
}
|
||||
logger.lifecycle("Architecture check passed: no forbidden module dependencies.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.mingyuan.androidclient.buildlogic
|
||||
|
||||
import com.android.build.api.dsl.CommonExtension
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
|
||||
/**
|
||||
* Configure Compose-specific options for an Android module: enable the Compose build feature,
|
||||
* add the Compose BOM so all Compose artifacts share one version, and wire the preview/tooling
|
||||
* dependencies used by Android Studio.
|
||||
*/
|
||||
internal fun Project.configureAndroidCompose(commonExtension: CommonExtension) {
|
||||
commonExtension.apply {
|
||||
buildFeatures.apply {
|
||||
compose = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val bom = libs.findLibrary("androidx-compose-bom").get()
|
||||
"implementation"(platform(bom))
|
||||
"androidTestImplementation"(platform(bom))
|
||||
"implementation"(libs.findLibrary("androidx-compose-ui").get())
|
||||
"implementation"(libs.findLibrary("androidx-compose-ui-graphics").get())
|
||||
"implementation"(libs.findLibrary("androidx-compose-material3").get())
|
||||
"implementation"(libs.findLibrary("androidx-compose-ui-tooling-preview").get())
|
||||
"debugImplementation"(libs.findLibrary("androidx-compose-ui-tooling").get())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.mingyuan.androidclient.buildlogic
|
||||
|
||||
import com.android.build.api.dsl.CommonExtension
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.kotlin.dsl.assign
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinBaseExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
|
||||
|
||||
/**
|
||||
* Configure base Kotlin + Android options shared by every Android module.
|
||||
*
|
||||
* compileSdk / minSdk are the project-wide contract (see docs/architecture.md); Java/Kotlin
|
||||
* target stays at 11 so library modules and the app agree on bytecode level.
|
||||
*/
|
||||
internal fun Project.configureKotlinAndroid(commonExtension: CommonExtension) {
|
||||
commonExtension.apply {
|
||||
compileSdk = 37
|
||||
|
||||
defaultConfig.apply {
|
||||
minSdk = 28
|
||||
}
|
||||
|
||||
compileOptions.apply {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
configureKotlin<KotlinAndroidProjectExtension>()
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure base Kotlin options for pure JVM (non-Android) modules such as :core:model.
|
||||
*/
|
||||
internal fun Project.configureKotlinJvm() {
|
||||
extensions.configure<JavaPluginExtension> {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
configureKotlin<KotlinJvmProjectExtension>()
|
||||
}
|
||||
|
||||
private inline fun <reified T : KotlinBaseExtension> Project.configureKotlin() = configure<T> {
|
||||
// Treat Kotlin warnings as errors only when the `warningsAsErrors` Gradle property is set.
|
||||
val warningsAsErrors = providers.gradleProperty("warningsAsErrors").map {
|
||||
it.toBoolean()
|
||||
}.orElse(false)
|
||||
when (this) {
|
||||
is KotlinAndroidProjectExtension -> compilerOptions
|
||||
is KotlinJvmProjectExtension -> compilerOptions
|
||||
else -> TODO("Unsupported project extension $this ${T::class}")
|
||||
}.apply {
|
||||
jvmTarget = JvmTarget.JVM_11
|
||||
allWarningsAsErrors = warningsAsErrors
|
||||
freeCompilerArgs.add(
|
||||
// Enable experimental coroutines APIs, including Flow.
|
||||
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mingyuan.androidclient.buildlogic
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.VersionCatalog
|
||||
import org.gradle.api.artifacts.VersionCatalogsExtension
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
|
||||
/**
|
||||
* Access to the shared `libs` version catalog from within convention plugins.
|
||||
*
|
||||
* The catalog is declared in `gradle/libs.versions.toml` and wired into the included
|
||||
* build via `build-logic/settings.gradle.kts`, so build-logic and the main build share
|
||||
* one source of truth for versions.
|
||||
*/
|
||||
val Project.libs
|
||||
get(): VersionCatalog = extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.mingyuan.androidclient.buildlogic
|
||||
|
||||
import com.diffplug.gradle.spotless.SpotlessExtension
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
|
||||
/**
|
||||
* Enforce deterministic Kotlin formatting with ktlint (rules driven by `.editorconfig`).
|
||||
*
|
||||
* No license header is applied — this is a private project. Formatting only; run
|
||||
* `./gradlew spotlessCheck` (verify) or `./gradlew spotlessApply` (fix).
|
||||
*/
|
||||
internal fun Project.configureSpotless() {
|
||||
apply(plugin = "com.diffplug.spotless")
|
||||
extensions.configure<SpotlessExtension> {
|
||||
kotlin {
|
||||
target("src/**/*.kt")
|
||||
ktlint(libs.findVersion("ktlint").get().requiredVersion).editorConfigOverride(
|
||||
mapOf(
|
||||
"android" to "true",
|
||||
// 包名 com.mingyuan.android_client 含下划线,刻意保留(见 docs/decisions/0004)。
|
||||
"ktlint_standard_package-name" to "disabled",
|
||||
),
|
||||
)
|
||||
endWithNewline()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user