11.1.8 Kotlin Gradle 插件中的二进制兼容性验证

原文链接: https://kotlinlang.org/docs/gradle-binary-compatibility-validation.html

11.1.8 Kotlin Gradle 插件中的二进制兼容性验证

实验性 - 通用

二进制兼容性验证帮助库作者确保用户在升级到新版本时不会破坏自己的代码。这不仅对于提供顺畅的升级体验很重要,也有助于与用户建立长期信任,并鼓励他们持续采用该库。

提示: 二进制兼容意味着某个库两个版本编译后的字节码可以互换运行,而无需重新编译。

Kotlin Gradle 插件内置了二进制兼容性验证支持。该插件会根据当前代码生成应用二进制接口(ABI)转储,并将其与之前的转储进行比较,以突出显示差异。你可以审阅这些变更,找出任何可能破坏二进制兼容性的修改并采取行动。

如何启用

要启用二进制兼容性验证,请在 build.gradle.kts 文件中添加一个 abiValidation {} 块。如果你没有自定义配置,也可以改用 abiValidation() 函数:

Kotlin

1
2
3
4
kotlin {
    @OptIn(org.jetbrains.kotlin.gradle.dsl.abi.ExperimentalAbiValidation::class)
    abiValidation()
}

Groovy

1
2
3
kotlin {
    abiValidation()
}

KGP 会创建所需的 Gradle 任务。如果你的项目有多个模块都需要检查二进制兼容性,请分别配置每个模块。

检查二进制兼容性问题

在修改代码后,要检查是否存在可能破坏二进制兼容性的问题,请在 IntelliJ IDEA 中运行 checkKotlinAbi Gradle 任务,或在项目目录中运行以下命令:

1
./gradlew checkKotlinAbi

该任务会比较 ABI 转储,并把检测到的任何差异作为错误打印出来。请仔细检查输出,判断是否需要修改代码以保持二进制兼容性。

默认情况下,当你的项目中启用了二进制兼容性验证并且你运行 check 任务时,Gradle 也会运行 checkKotlinAbi 任务。

更新参考 ABI 转储

要更新 Gradle 用于检查你最新改动的参考 ABI 转储,请在 IntelliJ IDEA 中运行 updateKotlinAbi 任务,或在项目目录中运行以下命令:

1
./gradlew updateKotlinAbi

只有当你确信自己的改动与之前的版本保持二进制兼容时,才应更新参考转储。

配置过滤器

你可以定义过滤器来控制 ABI 转储中包含哪些类、属性和函数。使用 filters {} 块,分别通过 excluded {} 和 included {} 块添加排除规则和包含规则。

只有当某个声明不匹配任何排除规则时,Gradle 才会把它包含在 ABI 转储中。当定义了包含规则时,该声明必须匹配其中一条规则,或者至少有一个成员匹配。

规则可以基于:

  • 类、属性或函数的完全限定名(byNames)。
  • 具有 BINARY 或 RUNTIME 保留策略的注解名称(annotatedWith)。

提示: 在名称规则中可以使用通配符 **、* 和 ?:* ** 匹配零个或多个字符,包括句点。* * 匹配零个或多个字符,不包括句点。用它来指定单个类名。* ? 恰好匹配一个字符。

例如:

Kotlin

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
kotlin {
    @OptIn(org.jetbrains.kotlin.gradle.dsl.abi.ExperimentalAbiValidation::class)
    abiValidation {
        filters {
            excluded {
                byNames.add("**.InternalUtils")
                annotatedWith.add("com.example.annotations.InternalApi")
            }

            included {
                byNames.add("com.example.api.**")
                annotatedWith.add("com.example.annotations.PublicApi")
            }
        }
    }
}

Groovy

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
kotlin {
    abiValidation {
        filters {
            excluded {
                byNames.add("**.InternalUtils")
                annotatedWith.add("com.example.annotations.InternalApi")
            }

            included {
                byNames.add("com.example.api.**")
                annotatedWith.add("com.example.annotations.PublicApi")
            }
        }
    }
}

这个示例:

  • 排除:
  • InternalUtils 类。
  • 使用 @InternalApi 注解的声明。
  • 包含:
  • com.example.api 包中的所有内容。
  • 使用 @PublicApi 注解的声明。

要进一步了解过滤,请参阅 Kotlin Gradle 插件 API 参考。

对不支持的目标禁止推断出的变更

在多平台项目中,如果你的宿主系统无法编译所有目标,Kotlin Gradle 插件会尝试根据可用的目标推断 ABI 变更。这有助于避免在之后切换到支持更多目标的宿主时出现误报失败。

要禁用这一行为,请在 build.gradle.kts 文件中添加以下内容:

Kotlin

1
2
3
4
5
6
kotlin {
    @OptIn(org.jetbrains.kotlin.gradle.dsl.abi.ExperimentalAbiValidation::class)
    abiValidation {
        keepLocallyUnsupportedTargets.set(false)
    }
}

Groovy

1
2
3
4
5
kotlin {
    abiValidation {
        keepLocallyUnsupportedTargets = false
    }
}

如果某个目标不受支持且推断被禁用,checkKotlinAbi 任务会失败,因为它无法生成完整的 ABI 转储。如果你宁愿让任务失败,也不愿冒漏掉二进制不兼容变更的风险,这一行为可能很有用。

包含来自 maven-publish 插件的发布产物

默认情况下,二进制兼容性验证使用 Kotlin 编译输出生成 ABI 转储。因此,生成的 ABI 转储可能无法反映最终发布的产物。例如,当你使用 maven-publish 插件时,重定位等后处理步骤可能会在编译之后修改产物。

为确保 ABI 转储准确反映 maven-publish 插件发布的产物,请在 build.gradle.kts 文件中添加以下内容:

Kotlin

1
2
3
4
5
6
kotlin {
    @OptIn(org.jetbrains.kotlin.gradle.dsl.abi.ExperimentalAbiValidation::class)
    abiValidation {
        binariesSource.set(MAVEN_PUBLICATIONS)
    }
}

Groovy

1
2
3
4
5
kotlin {
    abiValidation {
        binariesSource = MAVEN_PUBLICATIONS
    }
}

警告: 由于 Kotlin/Android 项目以及带 Android 目标的多平台项目不发布 JAR 文件,此功能不适用于它们。