第六十五章. 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 仓库的模块,可以在Gradle(参见 第五十章, 依赖管理)或其他理解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” 插件在project上创建了一个名为 “publishing”的 PublishingExtension类型的扩展。这个扩展提供了两个容器,一个叫publications,一个叫repositories。“maven-publish”插件适用于MavenPublication publications 和 MavenArtifactRepository 仓库。
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.

发布对象描述了要被创建的发布内容的结构和配置。publications是通过任务发布到仓库中的,并且发布对象的配置明确决定了会发布哪些内容。一个项目的所有publications会在 PublishingExtension.getPublications() 容器中定义。每一个发布
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

向Maven仓库发布一个Gradle项目的最简单的方式是指定一个要发布的 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.

64.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”(以及artifact的“extension”和“classifer”),Gradle将处理任何有效的Unicode字符。
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 的“描述”元素。通过这个钩子,你可以修改 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)则是一个例外;不能使用“withXML”钩子来修改POM中的这些值。
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 子项目。一个例子是为您的library 分别发布一个单独的 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 将把每一个都发布到定义的仓库。如上文所述,每个发布的内容都必须给定一个唯一的标识。
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 的说法中,这叫做“安装”模块。对每一个在 publishing.publications 容器的MavenPublication ,“maven-publish” 插件自动为它们创建了一个 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 publish
:generatePomFileForMavenJavaPublication
:compileJava
:processResources UP-TO-DATE
:classes
:jar
:publishMavenJavaPublicationToMavenRepository
:publish

BUILD SUCCESSFUL

Total time: 1 secs

因此在这个例子中,你可以看到添加了一个 PublishToMavenLocal 任务,任务名字叫做“publishMavenJavaPublicationToMavenLocal”。这个任务被连线到publishToMavenLocal生命周期任务。执行gradle publishToMavenLocal会构建 POM 文件和所有要发布的构件,并将它们“installs”到本地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名称»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任务。确保当你尝试访问GenerateMavenPom任务时发布插件已经配置好的最简单的方式是,将访问的代码放在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.