第六十五章. Maven 发布(新)

Chapter 65. Maven Publishing (new)

本章内容描述了一个新的 孵化中的 功能,是由“maven-publish”插件提供的 maven 发布支持。最终这个新的发布支持将取代通过Upload任务发布的方式。
This chapter describes the new incubating Maven publishing support provided by the “maven-publish” plugin. Eventually this new publishing support will replace publishing via the Upload task.

如果你正在查找有关使用 Upload 任务的原始的 Maven 发布支持的文档,请查阅《第五十一章,发布工件》。
If you are looking for documentation on the original Maven publishing support using the Upload task please see Chapter 51, Publishing artifacts.

本章介绍如何将构建工件发布到 Apache Maven 仓库。发布到 Maven 仓库的模块可以被 Maven、Gradle(参见《第 50 章,依赖管理》)和其他理解 Maven 仓库格式的工具使用。
This chapter describes how to publish build artifacts to an Apache Maven Repository. A module published to a Maven repository can be consumed by Maven, Gradle (see Chapter 50, Dependency Management) and other tools that understand the Maven repository format.

65.1. “maven-publish”插件

65.1. The “maven-publish” Plugin

使用Maven格式发布的功能,是由 “maven-publish” 插件提供的。
The ability to publish in the Maven format is provided by the “maven-publish” plugin.

publishing”项目在“publishing”项目上创建了一个PublishingExtension类型的扩展。这个扩展提供了两个容器,一个叫publications,一个叫repositories。“maven-publish”插件适用于MavenPublication publications 和 IvyArtifactRepository repositories。
The “publishing” plugin creates an extension on the project named “publishing” of type PublishingExtension. This extension provides a container of named publications and a container of named repositories. The “maven-publish” plugin works with MavenPublication publications and MavenArtifactRepository repositories.

示例 65.1. 应用“maven-publish”插件 - Example 65.1. Applying the 'maven-publish' plugin

build.gradle

apply plugin: 'maven-publish'

应用“maven-publish”插件将执行以下操作:
Applying the “maven-publish” plugin does the following:

65.2. 发布

65.2. Publications

如果你不熟悉项目工件和配置,你应该阅读介绍这些概念的《第五十一章,发布工件》。本章还介绍了使用另一种不同的机制的“发布工件 ”。这里描述的发布功能最终会取代那一功能。
If you are not familiar with project artifacts and configurations, you should read the Chapter 51, Publishing artifacts that introduces these concepts. This chapter also describes “publishing artifacts” using a different mechanism than what is described in this chapter. The publishing functionality described here will eventually supersede that functionality.

Publication对象描述了要创建的发布内容的结构和配置。Publications是通过任务发布到仓库的,发布对象的配置明确地决定了要发布的内容。一个项目的所有publications都会在PublishingExtension.getPublications()容器中定义,每一个publication在项目中都具有唯一名称。
Publication objects describe the structure/configuration of a publication to be created. Publications are published to repositories via tasks, and the configuration of the publication object determines exactly what is published. All of the publications of a project are defined in the PublishingExtension.getPublications() container. Each publication has a unique name within the project.

要使“maven-publish”插件起作用,必须将一个 MavenPublication 添加到publications集合里。这个publication决定了实际要发布的工件,以及在关联的POM文件中包含的详细信息。通过添加组件,自定义工件以及修改生成的POM文件,可以配置一个publication。
For the “maven-publish” plugin to have any effect, a MavenPublication must be added to the set of publications. This publication determines which artifacts are actually published as well as the details included in the associated POM file. A publication can be configured by adding components, customizing artifacts, and by modifying the generated POM file directly.

65.2.1. 发布软件组件

65.2.1. Publishing a Software Component

将 Gradle 项目发布到 Maven 库的最简单方法是指定一个要发布的 SoftwareComponent。目前可用于publication的组件有:
The simplest way to publish a Gradle project to a Maven repository is to specify a SoftwareComponent to publish. The components presently available for publication are:

表65.1. 软件组件 - Table 65.1. Software Components

名称
Name
提供者
Provided By
工件
Artifacts
依赖
Dependencies
java 第二十三章. Java 插件
Chapter 23, The Java Plugin
生成的 jar 文件
Generated jar file
“runtime”配置的依赖
Dependencies from 'runtime' configuration
web 第二十六章. War 插件
Chapter 26, The War Plugin
生成的 war 文件
Generated war file
没有依赖
No dependencies

在以下示例中,工件和运行时依赖都来自由 Java Plugin 添加的 “java” 组件。
In the following example, artifacts and runtime dependencies are taken from the `java` component, which is added by the Java Plugin.

示例 65.2. 为一个java组件添加一个MavenPublication - Example 65.2. Adding a MavenPublication for a java component

build.gradle

publications {
        mavenJava(MavenPublication) {
            from components.java
        }
    }

65.2.2. 发布自定义工件

65.2.2. Publishing custom artifacts

我们还可以显式地配置要被包含在publication的工件。这些工件通常是以原始文件,或者是 AbstractArchiveTask的实例(如Jar, Zip)的方式来提供。
It is also possible to explicitly configure artifacts to be included in the publication. Artifacts are commonly supplied as raw files, or as instances of AbstractArchiveTask (e.g. Jar, Zip).

对于每个自定义工件,可以指定用于发布的extensionclassifier值。请注意,已发布的工件只有一个可以具有空的classifier,其他所有工件必须有一个唯一的classifier/extension组合。
For each custom artifact, it is possible to specify the extension and classifier values to use for publication. Note that only one of the published artifacts can have an empty classifier, and all other artifacts must have a unique classifier/extension combination.

如下所示配置自定义工件︰
Configure custom artifacts as follows:

示例65.3. 向MavenPublication 添加其他的工件 - Example 65.3. Adding additional artifact to a MavenPublication

build.gradle

task sourceJar(type: Jar) {
    from sourceSets.main.allJava
}

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java

            artifact sourceJar {
                classifier "sources"
            }
        }
    }
}

关于如何自定义工件的更详细的文档,请参阅MavenPublication
See MavenPublication for more detailed documentation on how artifacts can be customized.

65.2.3. 生成的POM中的标识值

65.2.3. Identity values in the generated POM

生成的 POM 文件的属性中,包含了从以下项目属性中获得的标识值:
The attributes of the generated POM file will contain identity values derived from the following project properties:

重写默认标识值很简单︰只需要配置MavenPublication时指定groupIdartifactIdversion的属性。
Overriding the default identity values is easy: simply specify the groupId, artifactId or version attributes when configuring the MavenPublication.

示例 65.4. 自定义发布标识 - Example 65.4. customizing the publication identity

build.gradle

publishing {
        publications {
            maven(MavenPublication) {
                groupId 'org.gradle.sample'
                artifactId 'project1-sample'
                version '1.1'

                from components.java
            }
        }
    }

某些仓库将无法处理所有受支持的字符。例如,当发布到Windows上的文件系统支持的仓库时,就不能将“:” 字符用作标识符。

Maven 将“groupId”和“artifactId”限制为有限的字符集([A-Za-z0-9_\\-.]+),并且 Gradle 强制执行该限制。对于“version”(以及工件的“extension”和“classifier”),只要是有效的Unicode字符,Gradle 都会处理。
Maven restricts 'groupId' and 'artifactId' to a limited character set ([A-Za-z0-9_\\-.]+) and Gradle enforces this restriction. For 'version' (as well as artifact 'extension' and 'classifier'), Gradle will handle any valid Unicode character.

唯一明确禁止使用的 Unicode 值是“”\”、“/”以及所有的 ISO 控制字符。提供的值会在发布的早期阶段进行验证。
The only Unicode values that are explicitly prohibited are '\', '/' and any ISO control character. Supplied values are validated early in publication.

65.2.4. 修改生成的 POM

65.2.4. Modifying the generated POM

有时候,从项目信息生成的 POM 文件需要在发布前进行调整。“maven-publish”插件提供了一个钩子以允许这样的修改。
At times, the POM file generated from the project information will need to be tweaked before publishing. The “maven-publish” plugin provides a hook to allow such modification.

示例 65.5. 修改 POM 文件 - Example 65.5. Modifying the POM file

build.gradle

publications {
        mavenCustom(MavenPublication) {
            pom.withXml {
                asNode().appendNode('description', 'A demonstration of maven POM customization')
            }
        }
    }

在这个例子中我们为生成的 POM 添加了一个“description”元素。通过这个钩子,你可以修改 POM 的任何方面的内容。例如,你可以把一个依赖的版本范围修改为用于生成构建的实际版本。
In this example we are adding a 'description' element for the generated POM. With this hook, you can modify any aspect of the POM. For example, you could replace the version range for a dependency with the actual version used to produce the build.

相关的 API 参考文档,请参阅MavenPom.withXml()
See MavenPom.withXml() for the relevant API reference documentation.

如果需要,几乎可以修改所创建POM的任何方面的内容。这意味着,你也可能把一个 POM 修改成无效的 Maven Pom,因此在使用这一功能时必须小心。
It is possible to modify virtually any aspect of the created POM should you need to. This means that it is also possible to modify the POM in such a way that it is no longer a valid Maven Pom, so care must be taken when using this feature.

已发布模块的标识符(groupId,artifac,version)则是一个例外;这些值不能在POM中使用“withXML”钩子修改。
The identifier (groupId, artifactId, version) of the published module is an exception; these values cannot be modified in the POM using the `withXML` hook.

65.2.5. 发布多个模块

65.2.5. Publishing multiple modules

有时候,不需要创建单独的 Gradle 子项目就能从 Gradle 构建中发布多个模块会很有用。一个例子是为你的库发布一个单独的 API 和实现 jar。使用 Gradle 的话很简单:
Sometimes it's useful to publish multiple modules from your Gradle build, without creating a separate Gradle subproject. An example is publishing a separate API and implementation jar for your library. With Gradle this is simple:

示例 65.6. 从单项目中发布多个模块 - Example 65.6. Publishing multiple modules from a single project

build.gradle

task apiJar(type: Jar) {
        baseName "publishing-api"
        from sourceSets.main.output
        exclude '**/impl/**'
    }

    publishing {
        publications {
            impl(MavenPublication) {
                groupId 'org.gradle.sample.impl'
                artifactId 'project2-impl'
                version '2.3'

                from components.java
            }
            api(MavenPublication) {
                groupId 'org.gradle.sample'
                artifactId 'project2-api'
                version '2'

                artifact apiJar
            }
        }
    }

如果一个项目定义了多个发布,那么 Gradle 会将每个发布都发布到定义的仓库中。每个发布都必须x给定如上所述的唯一标识。
If a project defines multiple publications then Gradle will publish each of these to the defined repositories. Each publication must be given a unique identity as described above.

65.3. 仓库

65.3. Repositories

要发布的内容会被发布到仓库中。要发布到的仓库由 PublishingExtension.getRepositories() 容器定义。
Publications are published to repositories. The repositories to publish to are defined by the PublishingExtension.getRepositories() container.

示例 65.7. 声明要发布到的仓库 - Example 65.7. Declaring repositories to publish to

build.gradle

repositories {
        maven {
            url "$buildDir/repo" // change to point to your repo, e.g. http://my.org/repo
        }
    }

用于声明要发布的仓库的 DSL 与用于声明查找依赖的仓库的DSL一样,都是使用 RepositoryHandler。但是,在 Maven 发布的场景中,只有 MavenArtifactRepository 仓库才可以用于发布。
The DSL used to declare repositories for publication is the same DSL that is used to declare repositories to consume dependencies from, RepositoryHandler. However, in the context of Maven publication only MavenArtifactRepository repositories can be used for publication.

65.4. 执行发布

65.4. Performing a publish

对每一个在 publishing.publicationspublishing.repositories 容器中分别组合的MavenPublicationMavenArtifactRepository,“maven-publish” 插件自动为它们创建了一个 PublishToMavenRepository 任务。
The “maven-publish” plugin automatically creates a PublishToMavenRepository task for each MavenPublication and MavenArtifactRepository combination in the publishing.publications and publishing.repositories containers respectively.

这个创建的任务使用“publish«PUBLICATION 名称»PublicationTo«REPOSITORY 名称»Repository”的模式来命名。
The created task is named using the pattern "publish«NAME OF PUBLICATION»PublicationTo«NAME OF REPOSITORY»Repository".

示例 65.8. 将项目发布到 Maven 仓库 - Example 65.8. Publishing a project to a Maven repository

build.gradle

apply plugin: 'java'
apply plugin: 'maven-publish'

group = 'org.gradle.sample'
version = '1.0'

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java
        }
    }
    repositories {
        maven {
            url "$buildDir/repo" // change to point to your repo, e.g. http://my.org/repo
        }
    }
}

gradle publish的输出结果
Output of gradle publish

> gradle publish
:generatePomFileForMavenJavaPublication
:compileJava
:processResources UP-TO-DATE
:classes
:jar
:publishMavenJavaPublicationToMavenRepository
:publish

BUILD SUCCESSFUL

Total time: 1 secs

因此,在这个例子中,添加了一个 PublishToMavenRepository 任务,命名为“ publishMavenJavaPublicationToMavenRepository”,这个任务会被连接到publish生命周期任务中。执行 gradle publish会构建 POM 文件和所有要发布的工件,并将它们传到仓库。
So in this example a single PublishToMavenRepository task is be added, named 'publishMavenJavaPublicationToMavenRepository'. This task is wired into the publish lifecycle task. Executing gradle publish builds the POM file and all of the artifacts to be published, and transfers them to the repository.

65.5. 发布到Maven本地库

65.5. Publishing to Maven Local

为了与本地 Maven 安装集成,有时将模块发布到本地 .m2 仓库中会很有用。在 Maven 用语中,这称为“安装”模块。“maven-publish”插件通过为publishing.publications容器中的每个MavenPublication任务,自动创建一个PublishToMavenLocal任务,使得它很容易实现。其中每一个任务都连接到publishToMavenLocal生命周期任务中。你不需要在“publishing.repositories”代码段中使用“mavenLocal”。
For integration with a local Maven installation, it is sometimes useful to publish the module into the local .m2 repository. In Maven parlance, this is referred to as 'installing' the module. The “maven-publish” plugin makes this easy to do by automatically creating a PublishToMavenLocal task for each MavenPublication in the publishing.publications container. Each of these tasks is wired into the publishToMavenLocal lifecycle task. You do not need to have `mavenLocal` in your `publishing.repositories` section.

这个创建的任务使用“publish«PUBLICATION 名称»PublicationToMavenLocal”的模式来命名。
The created task is named using the pattern "publish«NAME OF PUBLICATION»PublicationToMavenLocal".

示例 65.9. 发布项目到 Maven 本地仓库 - Example 65.9. Publish a project to the Maven local repository

gradle publishToMavenLocal的输出结果
Output of gradle publishToMavenLocal

> gradle publishToMavenLocal
:generatePomFileForMavenJavaPublication
:compileJava
:processResources UP-TO-DATE
:classes
:jar
:publishMavenJavaPublicationToMavenLocal
:publishToMavenLocal

BUILD SUCCESSFUL

Total time: 1 secs

因此,在这个示例中,你可以看到添加了一个名为“publishMavenJavaPublicationToMavenLocal”的PublisheToMavenLocal任务。这个任务连接到publishToMavenLocal生命周期任务中。执行 gradle publishToMavenLocal 会构建 POM 文件和所有要发布的工件,并将它们“安装”到本地 Maven 仓库中。
So in this example you can see that a single PublishToMavenLocal task is be added, named 'publishMavenJavaPublicationToMavenLocal'. This task is wired into the publishToMavenLocal lifecycle task. Executing gradle publishToMavenLocal builds the POM file and all of the artifacts to be published, and 'installs' them into the local Maven repository.

65.6. 生成POM文件而不发布

65.6. Generating the POM file without publishing

有时候我们会需要在实际上不发布的情况下,为模块生成一个 Maven POM 文件。由于POM生成是由单独的任务执行的,因此这样做很简单。
At times it is useful to generate a Maven POM file for a module without actually publishing. Since POM generation is performed by a separate task, it is very easy to do so.

生成 POM 文件的任务为 GenerateMavenPom 类型,并且根据发布的名称它被命名为:“generatePomFileFor«PUBLICATION 的名称»发布”。因此,在下面的示例中,如果发布名为“mavenCustom”,则任务将命名为“generatePomFileForMavenCustomPublication”。
The task for generating the POM file is of type GenerateMavenPom, and it is given a name based on the name of the publication: "generatePomFileFor«NAME OF PUBLICATION»Publication". So in the example below, where the publication is named "mavenCustom", the task will be named "generatePomFileForMavenCustomPublication".

示例 65.10. 生成POM文件而不发布 - Example 65.10. Generate a POM file without publishing

build.gradle

model {
    tasks.generatePomFileForMavenCustomPublication {
        destination = file("$buildDir/generated-pom.xml")
    }
}

gradle generatePomFileForMavenCustomPublication的输出结果
Output of gradle generatePomFileForMavenCustomPublication

> gradle generatePomFileForMavenCustomPublication
:generatePomFileForMavenCustomPublication

BUILD SUCCESSFUL

Total time: 1 secs

发布模型的所有细节仍然要在 POM 生成中考虑,包括compon,自定义artifacts,以及通过pom.withXml所做的任何修改。
All details of the publishing model are still considered in POM generation, including components`, custom artifacts, and any modifications made via pom.withXml.

maven-publish”插件利用了后期插件配置的一些实验性的支持,在发布扩展配置前,不会构造 GenerateMavenPom 任务。确保在你尝试访问 GenerateM 任务时,发布插件已经配置的最简单的方法是将访问放在 publishing 块中,如上面的例子中所示。
The “maven-publish” plugin leverages some experimental support for late plugin configuration, and any GenerateMavenPom tasks will not be constructed until the publishing extension is configured. The simplest way to ensure that the publishing plugin is configured when you attempt to access the GenerateMavenPom task is to place the access inside a publishing block, as the example above demonstrates.

这同样适用于尝试访问发布内容相关的任务(如 PublishToMavenRepository)。这些任务应该在 publishing 块中引用。
The same applies to any attempt to access publication-specific tasks like PublishToMavenRepository. These tasks should be referenced from within a publishing block.