Gradle 模块元数据简介

目录

简介

Gradle 模块元数据在 Gradle 5.3 中达到 1.0 版本,在此我们将解释为什么您应该像我们一样兴奋!

创建 Gradle 模块元数据是为了解决多年来困扰依赖管理的许多问题,特别是但不限于 Java 生态系统。它尤其重要,因为 POM 文件(或 Ivy 文件)根本不够丰富,无法描述当今软件的现实情况,在当今软件中,您可能需要区分用于不同平台的二进制文件,或者在有多个可用实现时选择 API 的特定实现。

我们将在本文后面的内容中描述更多示例。一些问题可能有解决方法,但这些解决方法是临时的甚至是容易出错的。例如,您是否意识到以下问题:为不同的 Java 版本使用分类器、排除以避免特定的记录器绑定,或者仅仅因为您需要覆盖特定版本而添加一级依赖项?

Gradle 模块元数据 1.0 是对这些问题的解答,也是朝着改进我们整个行业的依赖管理迈出的第一步。

它在实践中允许什么? #

当您的类路径上同时存在 guava-jdk5guava-jdk8,并且您的应用程序仅因条目的幸运排序而工作时,您是否曾经诅咒过?您是否曾经遇到过不同的 SLF4J 绑定,并且仅在运行时才注意到?那是因为这些库具有不同的变体,而现有元数据格式无法正确描述这些变体。构建工具甚至应该如何理解 jdk8 JAR、sources JAR 甚至 all JAR 之间的区别?Gradle 模块元数据旨在以一种消费者可以表达更精确需求的方式解释这种差异。例如,消费者可以专门要求他们可以在 JDK 8 中使用的东西。在 SLF4J 的情况下,构建工具将识别出 Log4J 绑定与 java.util.logging 绑定是互斥的。

整个想法是支持变体感知的依赖管理,这基于组件变体的描述(例如,主二进制文件、源包、特定于平台的二进制文件等等)及其相应的依赖项。

我们的一些合作伙伴已经使用 Gradle 元数据数月了。例如,Kotlin native 正在使用 Gradle 模块元数据来表示将 Kotlin 项目编译为不同架构时可以获得的不同的二进制文件。Google 正在使用变体感知的依赖管理,但缺少一个“外部模型”来支持它。Gradle 模块元数据就是那个外部模型,它将允许 Android 归档文件 (AAR) 的正确发布。

这些只是一些示例,但通过利用 Gradle 元数据,可以解决这些问题以及更多问题。随着越来越多的库作者采用 Gradle 模块元数据,我们的行业作为一个整体将解决更多问题。

Gradle 模块元数据如何影响您 #

Gradle 模块元数据 1.0 为所有 Gradle 用户启用细粒度的依赖项解析。从 Gradle 5.3 开始,如果您是消费者,并且您使用的库已发布 Gradle 元数据,则 Gradle 将自动使用发布到 Maven 或 Ivy 仓库的任何 Gradle 元数据。

但是,Gradle 5.3 不会默认自动发布它 —— 这将在 6.0 版本中实现。您今天可以发布 Gradle 模块元数据,但您必须通过使用 Maven Publish 或 Ivy Publish 插件并向您的 settings 脚本添加以下行来选择加入其发布

settings.gradle(.kts) #

enableFeaturePreview("GRADLE_METADATA")

Gradle 模块元数据如何影响 Maven 或 Ant+Ivy 构建? #

Maven 和 Ivy 使用者没有任何变化:如果您已选择加入发布 Gradle 模块元数据,则相应的文件将与 POM 文件(如果您发布到 Maven 仓库)或 Ivy 文件(如果您发布到 Ivy 仓库)一起发布。请注意,从构建组件到 Maven 和 Ivy 元数据的映射是有损的:例如,您不知道用于构建某些内容的 Java 版本,因此消费者不可能事先知道它们是否兼容。另一个示例是当您使用 Gradle 特定功能(如 rich versions)时。我们会尽力将它们映射到 Maven 或 Ivy 中的概念,但由于其元数据格式的限制,信息仍会在过程中丢失。

请注意,Gradle 5.2 引入了发布插件发出的警告,当它知道映射对于其他构建工具来说是有损的或有问题的时。

Gradle 模块元数据是一个 JSON 文件,其扩展名为 .module。每个文件描述一个包含零个或多个变体的单个软件组件。以下是具有多个变体的“com.acme:client:1.0-SNAPSHOT”组件的示例元数据文件的内容

{
  "formatVersion": "1.0",
  "component": {
    "group": "com.acme",
    "module": "client",
    "version": "1.0-SNAPSHOT",
    "attributes": {
      "org.gradle.status": "integration"
    }
  },
  "createdBy": {
    "gradle": {
      "version": "5.3",
      "buildId": "4wqjtkcv2fbmjjsewyu66wbvfq"
    }
  },
  "variants": [
    {
      "name": "apiElements",
      "attributes": {
        "org.gradle.dependency.bundling": "external",
        "org.gradle.jvm.version": 11,
        "org.gradle.usage": "java-api-jars"
      },
      "dependencies": [
        {
          "group": "com.mycompany",
          "module": "core",
          "version": {
            "requires": "2.5"
          }
        }
      ],
      "files": [
        {
          "name": "client-1.0-SNAPSHOT.jar",
          "url": "client-1.0-SNAPSHOT.jar",
          "size": 539,
          "sha1": "1f94fe53d33babdc9de537bb3a0108dbc0e25e4b",
          "md5": "6364cdd9923e1eda9b328bc80f93969c"
        }
      ]
    },
    {
      "name": "runtimeElements",
      "attributes": {
        "org.gradle.dependency.bundling": "external",
        "org.gradle.jvm.version": 11,
        "org.gradle.usage": "java-runtime-jars"
      },
      "dependencies": [
        {
          "group": "org.apache.commons",
          "module": "commons-lang3",
          "version": {
            "requires": "3.8"
          }
        },
        {
          "group": "com.mycompany",
          "module": "core",
          "version": {
            "requires": "2.5"
          }
        }
      ],
      "files": [
        {
          "name": "client-1.0-SNAPSHOT.jar",
          "url": "client-1.0-SNAPSHOT.jar",
          "size": 539,
          "sha1": "1f94fe53d33babdc9de537bb3a0108dbc0e25e4b",
          "md5": "6364cdd9923e1eda9b328bc80f93969c"
        }
      ]
    },
    {
      "name": "shadowApiElements",
      "attributes": {
        "org.gradle.dependency.bundling": "shadowed",
        "org.gradle.usage": "java-api"
      },
      "files": [
        {
          "name": "client-1.0-SNAPSHOT-all.jar",
          "url": "client-1.0-SNAPSHOT-all.jar",
          "size": 601730,
          "sha1": "9b70e54ffdce0541701d8f855bf75e059857eb0c",
          "md5": "3499bb6d9ccf86283854a5550135ea4a"
        }
      ]
    },
    {
      "name": "shadowRuntimeElements",
      "attributes": {
        "org.gradle.dependency.bundling": "shadowed",
        "org.gradle.usage": "java-runtime-jars"
      },
      "files": [
        {
          "name": "client-1.0-SNAPSHOT-all.jar",
          "url": "client-1.0-SNAPSHOT-all.jar",
          "size": 601730,
          "sha1": "9b70e54ffdce0541701d8f855bf75e059857eb0c",
          "md5": "3499bb6d9ccf86283854a5550135ea4a"
        }
      ]
    }
  ]
}

此文件声明了 4 个变体,属性让构建工具知道它们的用途。特别是,您将在此处看到,通常每个“API”和“运行时”只有一种变体,但这里各有 2 种变体。原因是这个特定的组件声明了一个额外的变体,其中依赖项是阴影的(fat jar)。这让消费者有机会决定他们是想要作为单独 jar 的依赖项,还是只想要库的 fatjar 变体。

如果您对更多技术细节感兴趣,请参阅 Gradle 模块元数据规范 1.0

欢迎早期采用者,请随时提供您的反馈

讨论