[译]Android和KMP项目的Gradle速查表 - 插件篇

Gradle可能会让人感到困惑和难以理解。当你创建一个新的Android项目时,系统会生成一些Gradle文件就不管了。开发者经常会从GitHub或Stack Overflow上复制粘贴代码,而并不真正理解其中的原理。

本系列将解释Gradle构建中常见代码的含义,并作为你将来可以参考的速查表。除了这篇文章外,我还创建了一个GitHub Gist,其中涵盖了典型的Gradle项目设置,并包含一个markdown速查表。如果你想跟随一个现有项目学习,我推荐使用这个多平台compose模板KaMP Kit,它是KMM项目的一个很好的起点。

在本文中,我们将探讨Gradle如何工作,深入了解Gradle插件,并为你留下一些方便的速查卡片以供参考。

Gradle构建实际发生了什么

在我们开始讨论插件之前,让我们先了解构建过程中发生的事情:

  1. 根据gradle-wrapper.properties下载Gradle包装器
  2. 构建检测settings.gradle文件并进行评估,以确定哪些项目参与
  3. 为每个项目创建项目实例,并评估它们的build.gradle脚本
  4. 为请求的任务创建任务图
  5. 按照依赖关系的顺序安排和执行每个选定的任务

这里需要注意的主要是构建运行的顺序。它首先检查settings.gradle,然后遍历settings中定义的每个子模块,并运行它们的build.gradle文件。然后如前所述,按照依赖关系的顺序执行任务。实际顺序可能很复杂,但简单来说,以KMP为例,我们可以说:

  • settings.gradle运行每个子模块的build.gradle
    • settings.gradlebuild.gradle解析插件
    • build.gradle检查要运行的代码块(如androidkotlin)并运行它们
  • 这些代码块解析dependencies

我喜欢这样思考这个顺序:

settings.gradle -> build.gradle -> plugins -> blocks -> dependencies

因此,考虑到这一点,我们将首先介绍插件,然后是常见的kotlinandroid代码块,接着是依赖项,最后回到一些关于settings.gradle的说明。Gradle来源

Note

Gradle脚本文件可以根据所选的DSL有两种不同的扩展名:.gradle用于Groovy DSL,.gradle.kts用于Kotlin DSL。本博文中这两个扩展名可以互换使用,因为它讨论的是Gradle的概念而不是语法。

Note

在Gradle中,有很多方法可以做同一件事。这篇文章不一定会讨论位置的最佳实践,但会给出一些关于如何在多模块项目中组织定义的建议。

Note

这不仅是给你的速查表,也是给我的。我仍在学习,所以如果我有没有涵盖的内容,或者有不正确的内容,请告诉我。

定义

在我们开始之前,这里有一些我在文章中使用的可能不清楚的术语:

  • Level:在本文中,当我说"level"时,我指的是模块层级。例如,root或top level指的是根目录的build.gradle和模块。
  • DSL:领域特定语言。例如:Kotlin DSLPlugins DSLGradle DSL

插件概述

插件DSL

// yourModule/build.gradle.kts
plugins {
    kotlin("multiplatform")     // 这是你添加插件的地方
}
// settings.gradle.kts (root)
pluginManagement {              // 配置项目中使用的插件的地方
    repositories {              // 列出你想从哪里获取插件
        google()
        mavenCentral()          // 中央Maven仓库(稍后会详细介绍maven)
        gradlePluginPortal()    // 默认Gradle插件门户 (https://plugins.gradle.org/)
    }
    plugins {                   // 列出你将使用的插件
        kotlin("multiplatform")
            .version("1.8.10")    // 插件必须在某个层级包含版本定义
            .apply(false)       // 你可以选择不应用插件,以防你不想在某个特定层级使用该插件
    }
}

传统插件应用方式

buildscript {
  repositories { // 列出你想从哪里获取插件
    maven { url = uri("https://plugins.gradle.org/m2/") }
  }
  dependencies {
    classpath("do.re.mi.exam:ple:1.8.20") // 这是你想添加的插件的依赖
  }
}
apply(plugin = "org.jetbrains.kotlin.multiplatform") // 这里是你定义插件的地方

Gradle文档

什么是插件

插件为你的Gradle构建添加功能。它们为项目添加新的任务、域对象和特性。更多信息在这里

示例:添加kotlin("multiplatform")允许你添加包含sourcesets和targets的kotlin代码块,并添加诸如build iosArm64Binaries之类的任务

如何添加插件?

向模块添加插件

让我们从顶层开始,从模块的build.gradle开始。首先,我们添加插件。

// build.gradle
plugins {
    id("com.android.library")
    kotlin("multiplatform")
        .version("1.8.10")  // 可选的\*
        .apply(false)     // 可选的
}

插件在plugins代码块中添加,可以添加到build.gradlesettings.gradle中的pluginManagement代码块中(我们稍后会讲到)。子模块继承插件,所以如果你想在子模块之间共享插件,可以在根模块中定义。

有两种方式包含插件:

  • id("id"):通过id导入插件,传入插件的组ID和名称
  • kotlin("id"):Kotlin是一个简便的写法,表示我们使用"org.jetbrains.kotlin"组ID。这相当于调用id("org.jetbrains.kotlin.id")

Version:你必须在项目中为插件添加版本。如果你与子模块共享插件,可以在根模块中定义版本,否则在使用的模块中设置版本。版本会被子模块继承,但如果需要,你也可以在子模块中覆盖版本。

Apply:你可以使用apply选择是否应用插件。这在你想在根项目中全局定义插件,但不想在根模块中实际使用它的情况下很有用。声明此插件的子模块将自动应用它。

KMP注意事项:在模块中定义的插件会应用到所有sourcesets。例如,如果你包含了id("org.jetbrains.kotlin.id")插件,它会尝试将该插件应用到你的iosMain sourceset并导致错误。

管理插件

其次,我们定义它来自哪里。

// settings.gradle
pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}

你需要定义插件来自哪里,否则Gradle不知道你在说什么。默认情况下,Gradle在gradlePluginPortal检查插件。如果你使用的插件在另一个仓库中,你可以在pluginManagement代码块中定义它。pluginManagement代码块是你设置插件来源仓库的地方。此外,你也可以在这里设置插件,使用上面提到的相同语法。我们将在未来的文章中讨论maven,但现在只需要知道mavenCentral是一个用于插件和依赖项的流行仓库。

传统方式

buildscript {
  repositories { 
    mavenCentral()
  }
  dependencies {
    classpath(
        "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.20"
    )
  }
}
apply(plugin = "org.jetbrains.kotlin.multiplatform")

传统方式有点不同。我们使用buildScript而不是pluginManagementRepositories是相同的,但注意这个版本中有一个Repositories代码块。这是你定义classpaths的地方,它们是插件的依赖项。

个人建议

定义插件有很多不同的方式,但截至2023年5月,这是我的建议:

  • 遵循android指南
  • settings.gradle中使用pluginManagement定义所有插件信息,包括版本,但设置apply为false
  • 然后在子模块中包含插件,但不包含版本

速查卡片

这里是一些关于Gradle插件的快速参考代码

代码块

完整覆盖

  • plugins:定义你想使用的插件的地方(更多信息)
  • repositories:告诉Gradle在哪里查找项目的代码块(更多信息)
  • pluginManagement:定义项目插件的地方,包含pluginsrepositories代码块(更多信息)
  • BuildScript:(插件的传统方式)定义添加插件的repositories和dependencies。也可以用来添加在项目中使用的变量,如版本(更多信息)

插件

  • id("id"):用组ID和名称添加插件(例如:"com.android.library")(更多信息)
  • kotlin("id"):Kotlin表示我们使用"org.jetbrains.kotlin"组ID
  • version("version"):设置插件版本,在某个模块层级是必需的(更多信息)
  • apply(boolean):是否应用插件。如果你想在根模块中定义插件,但只在某些特定子模块中使用它,这很有用(更多信息)

传统方式

  • classpath:你想添加的插件的依赖,classpathbuildscript代码块中使用
  • apply("id"):添加插件的传统方式

仓库