使用Git Submodule减少Gradle构建配置模板代码

前言

最近为了学习和了解KMP,先后创建了TODO-LISTE-Wallet两个项目。在笔者每次创建新的项目之后,都要copy一个名为build-logic的文件夹到新的项目上去,虽然偶尔会根据情况进行一些修改,其实基本上是没有多大改动的,于是在想,如果通过submodule进行管理是不是会更方便些?

介绍

nowinandroid这个项目是一个非常值得去看去学习的,项目不大,阅读代码也不需要花太多时间。
nowinandroid项目中,对modularizationarchitecture有详细的介绍文档。而build-logic目的就在于实现modularization时,提供使用和配置的方便性。我们知道,多模块项目中,每个模块都有一个gradle配置文件,而构建配置的复杂性将影响着模块的同步时间和维护成本。另外,如果我们不整合管理,模块的增加将带来持续的维护成本。而约定插件可以将这些可重用和可组合的构建配置提取成kotlin代码中管理。在对应模块配置的时候apply对应的plugin id即可。

修改

1. 删除不确定依赖关系

nowinandroid中,build-logic作为一个单个项目的约定插件管理模块,其内部存在对外部其他模块的依赖。但如果想将其作为一个submodule,对外部这些不确定的依赖自然需要删除,所以在architecture提到的各层相关模块依赖关系需要从中删除,保留基本的构建配置项即可。

2. 增加构建配置项支持

nowinandroid中,build-logic中已有jacoco,app,feature,library,hilt等常用可组合构建配置插件,但是项目不同,构建配置需求也会不一样。就比如,在KMP(Android/iOS)项目中,在app,feature,library上的构建配置是不一样的,并且di上koinhilt有更好的可用性。所以增加构建配置项的支持,能大大增加submodule的适配可行性。

3. 加上catalog的管理并保留可覆盖和可扩展性

在可配置项中,比如hiltkoinjacoco等,需要使用到依赖库的版本等信息,所以catalog的管理是需要的。笔者在仓库中增加了默认的版本库,并提供一些常用的依赖库参考使用。而且当使用者使用自己catalog并将名字设置为libs即可覆盖使用。

使用

Step1 使用submodule

git submodule add https://github.com/BreakZero/build-logic

Step2 配置必须catalog

  • Option 1 直接使用build-logic目录下的default.versions.toml
    在project目录下的settings.gradle.kts中,将下面的代码copy到dependencyResolutionManagement块中
    versionCatalogs {
        create("libs") {
            from(files("./build-logic/default.versions.toml"))
        }
    }
  • Option 2 使用自己的catalog文件
  1. 在项目gradle目录下,创建一个名为libs.versions.toml的文件
  2. build-logic目录下的default.versions.toml内容全部copy到你的catalog下,再自行添加其他的。(catalogs目录下有参考)

Step3 进行gradle同步操作并使用

通过ide操作同步gradle进行初始构建,在同步成功后,便可以直接使用plugin替换相关模块的gradle配置项
如:

plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.kotlin-android) apply false

    id("easy.android.application")
    id("easy.android.application.compose")
} 
dependencies {
    implementation(libs.core.ktx)
    implementation(libs.androidx.compose.activity)
} 

经过使用plugin之后,gradle配置文件内容将大大减少,基本如下内容所示,只需对dependencies做好管理即可。

@Suppress("DSL_SCOPE_VIOLATION")
plugins {
    alias(easy.plugins.android.feature.koin)
    alias(easy.plugins.android.library.jacoco)
}

android {
    namespace = "com.easy.wallet.home"
}

dependencies {
    implementation(project(":Wallet-Android:design-system"))
    implementation(project(":Wallet-Android:core"))
    implementation(project(":platform:datastore"))
    implementation(project(":platform:shared"))
    implementation(project(":platform:model"))

    implementation(libs.androidx.paging)
    implementation(libs.androidx.paging.compose)
}

更多可以查看E-Wallet下的配置文件比较。

项目地址

build-logic

总结

  1. 目前这个submodule也是通过在两个项目使用的过程中一步一步慢慢整理出来的,对于一些相对特殊的情况并不会被考虑到
  2. 虽然submodule确实减少了一部分工作,但是同时也丢失了一部分方便性,例如前文提到的architecture中各层相关模块的依赖性,因为层与层之间的依赖确定性基本不会改变,所以这块如果在kotlin代码中也会减少很多维护成本的。
  3. 虽如#2所说,但是在multi-repo项目中,这种使用方式笔者还是比较推荐的。

不过在这方面仁者见仁,智者见智吧。 Have a good day!!!