Maven 发布插件提供了将构建工件发布到Apache Maven存储库的功能。发布到 Maven 存储库的模块可以由 Maven、Gradle(请参阅声明依赖项)和其他理解 Maven 存储库格式的工具使用。您可以在发布概述中了解发布的基础知识。

用法

要使用 Maven 发布插件,请在构建脚本中包含以下内容:

build.gradle.kts
plugins {
    `maven-publish`
}
build.gradle
plugins {
    id 'maven-publish'
}

Maven 发布插件在名为PublishingExtensionpublishing类型的项目上使用扩展。此扩展提供了命名出版物的容器和命名存储库的容器。 Maven 发布插件可与MavenPublication发布和MavenArtifactRepository存储库配合使用。

任务

generatePomFileForPubNamePublication生成MavenPom

为名为PubName 的发布创建一个 POM 文件,填充已知的元数据,例如项目名称、项目版本和依赖项。 POM 文件的默认位置是build/publications/$pubName/pom-default.xml

publishPubNamePublicationToRepoNameRepositoryPublishToMavenRepository

PubName发布发布到名为RepoName 的存储库。如果您的存储库定义没有显式名称,则 RepoName将为“Maven”。

publishPubNamePublicationToMavenLocalPublishToMavenLocal

将PubName发布连同发布的 POM 文件和其他元数据复制到本地 Maven 缓存(通常为<当前用户的主目录>/.m2/repository )。

publish

取决于:所有任务publishPubNamePublicationToRepoNameRepository

将所有定义的发布发布到所有定义的存储库的聚合任务。它包括将发布复制到本地 Maven 缓存。

publishToMavenLocal

取决于:所有任务publishPubNamePublicationToMavenLocal

将所有定义的发布复制到本地 Maven 缓存,包括其元数据(POM 文件等)。

刊物

该插件提供MavenPublication类型的发布。要了解如何定义和使用发布,请参阅基本发布部分。

您可以在 Maven 发布中配置四项主要内容:

您可以在完整的发布示例中看到所有这些操作。 API 文档MavenPublication有额外的代码示例。

生成的 POM 中的标识值

生成的 POM 文件的属性将包含从以下项目属性派生的标识值:

覆盖默认标识值很简单:只需在配置MavenPublicationgroupId时指定,artifactId或属性即可。version

build.gradle.kts
publishing {
    publications {
        create<MavenPublication>("maven") {
            groupId = "org.gradle.sample"
            artifactId = "library"
            version = "1.1"

            from(components["java"])
        }
    }
}
build.gradle
publishing {
    publications {
        maven(MavenPublication) {
            groupId = 'org.gradle.sample'
            artifactId = 'library'
            version = '1.1'

            from components.java
        }
    }
}
某些存储库将无法处理所有支持的字符。例如,:在发布到 Windows 上文件系统支持的存储库时,该字符不能用作标识符。

Maven 将groupId和限制artifactId为有限的字符集 ( [A-Za-z0-9_\\-.]+),而 Gradle 强制执行此限制。对于version(以及工件extensionclassifier属性),Gradle 将处理任何有效的 Unicode 字符。

唯一明确禁止的 Unicode 值是\,/和任何 ISO 控制字符。提供的值在发布之初就经过验证。

自定义生成的 POM

生成的POM文件可以在发布前进行定制。例如,将库发布到 Maven Central 时,您需要设置某些元数据。 Maven Publish Plugin 为此提供了 DSL。请参阅DSL 参考中的MavenPom ,获取可用属性和方法的完整文档。以下示例展示了如何使用最常见的示例:

build.gradle.kts
publishing {
    publications {
        create<MavenPublication>("mavenJava") {
            pom {
                name = "My Library"
                description = "A concise description of my library"
                url = "http://www.example.com/library"
                properties = mapOf(
                    "myProp" to "value",
                    "prop.with.dots" to "anotherValue"
                )
                licenses {
                    license {
                        name = "The Apache License, Version 2.0"
                        url = "http://www.apache.org/licenses/LICENSE-2.0.txt"
                    }
                }
                developers {
                    developer {
                        id = "johnd"
                        name = "John Doe"
                        email = "john.doe@example.com"
                    }
                }
                scm {
                    connection = "scm:git:git://example.com/my-library.git"
                    developerConnection = "scm:git:ssh://example.com/my-library.git"
                    url = "http://example.com/my-library/"
                }
            }
        }
    }
}
build.gradle
publishing {
    publications {
        mavenJava(MavenPublication) {
            pom {
                name = 'My Library'
                description = 'A concise description of my library'
                url = 'http://www.example.com/library'
                properties = [
                    myProp: "value",
                    "prop.with.dots": "anotherValue"
                ]
                licenses {
                    license {
                        name = 'The Apache License, Version 2.0'
                        url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                    }
                }
                developers {
                    developer {
                        id = 'johnd'
                        name = 'John Doe'
                        email = 'john.doe@example.com'
                    }
                }
                scm {
                    connection = 'scm:git:git://example.com/my-library.git'
                    developerConnection = 'scm:git:ssh://example.com/my-library.git'
                    url = 'http://example.com/my-library/'
                }
            }
        }
    }
}

自定义依赖版本

支持两种发布依赖关系的策略:

声明的版本(默认)

此策略发布由构建脚本作者使用块中的依赖项声明定义的版本dependencies。发布时不会考虑任何其他类型的处理,例如通过更改已解析版本的规则。

已解决的版本

该策略可能通过应用解决规则和自动冲突解决来发布在构建期间解决的版本。这样做的优点是发布的版本与测试发布的工件的版本相对应。

已解决版本的示例用例:

  • 项目使用动态版本作为依赖项,但更喜欢向其使用者公开给定版本的已解析版本。

  • 结合依赖锁定,您希望发布锁定的版本。

  • 一个项目利用了 Gradle 的丰富版本约束,它对 Maven 进行了有损转换。它不依赖转换,而是发布已解析的版本。

这是通过使用versionMappingDSL 方法来完成的,该方法允许配置VersionMappingStrategy

build.gradle.kts
publishing {
    publications {
        create<MavenPublication>("mavenJava") {
            versionMapping {
                usage("java-api") {
                    fromResolutionOf("runtimeClasspath")
                }
                usage("java-runtime") {
                    fromResolutionResult()
                }
            }
        }
    }
}
build.gradle
publishing {
    publications {
        mavenJava(MavenPublication) {
            versionMapping {
                usage('java-api') {
                    fromResolutionOf('runtimeClasspath')
                }
                usage('java-runtime') {
                    fromResolutionResult()
                }
            }
        }
    }
}

runtimeClasspath在上面的示例中,Gradle 将使用在 中声明的依赖项上解析的版本api,这些版本映射到compileMaven 的范围。 Gradle 还将使用runtimeClasspath在 中声明的依赖项上解析的版本implementation,这些版本映射到runtimeMaven 的范围。 fromResolutionResult()指示 Gradle 应该使用变体的默认类路径,并且runtimeClasspathjava-runtime.

存储库

该插件提供MavenArtifactRepository类型的存储库。要了解如何定义和使用存储库进行发布,请参阅基本发布部分。

这是定义发布存储库的简单示例:

build.gradle.kts
publishing {
    repositories {
        maven {
            // change to point to your repo, e.g. http://my.org/repo
            url = uri(layout.buildDirectory.dir("repo"))
        }
    }
}
build.gradle
publishing {
    repositories {
        maven {
            // change to point to your repo, e.g. http://my.org/repo
            url = layout.buildDirectory.dir('repo')
        }
    }
}

您需要配置的两个主要内容是存储库的:

  • 网址(必填)

  • 姓名(可选)

您可以定义多个存储库,只要它们在构建脚本中具有唯一的名称即可。您还可以声明一个(且仅有一个)没有名称的存储库。该存储库将采用隐式名称“Maven”。

您还可以配置连接到存储库所需的任何身份验证详细信息。有关更多详细信息,请参阅MavenArtifactRepository 。

快照和发布存储库

将快照和版本发布到不同的 Maven 存储库是一种常见的做法。实现此目的的一个简单方法是根据项目版本配置存储库 URL。以下示例对以“SNAPSHOT”结尾的版本使用一个 URL,对其余版本使用不同的 URL:

build.gradle.kts
publishing {
    repositories {
        maven {
            val releasesRepoUrl = layout.buildDirectory.dir("repos/releases")
            val snapshotsRepoUrl = layout.buildDirectory.dir("repos/snapshots")
            url = uri(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl)
        }
    }
}
build.gradle
publishing {
    repositories {
        maven {
            def releasesRepoUrl = layout.buildDirectory.dir('repos/releases')
            def snapshotsRepoUrl = layout.buildDirectory.dir('repos/snapshots')
            url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
        }
    }
}

同样,您可以使用项目或系统属性来决定要发布到哪个存储库。如果release设置了项目属性,例如当用户运行时gradle -Prelease publish,以下示例将使用发布存储库:

build.gradle.kts
publishing {
    repositories {
        maven {
            val releasesRepoUrl = layout.buildDirectory.dir("repos/releases")
            val snapshotsRepoUrl = layout.buildDirectory.dir("repos/snapshots")
            url = uri(if (project.hasProperty("release")) releasesRepoUrl else snapshotsRepoUrl)
        }
    }
}
build.gradle
publishing {
    repositories {
        maven {
            def releasesRepoUrl = layout.buildDirectory.dir('repos/releases')
            def snapshotsRepoUrl = layout.buildDirectory.dir('repos/snapshots')
            url = project.hasProperty('release') ? releasesRepoUrl : snapshotsRepoUrl
        }
    }
}

发布到 Maven 本地

为了与本地 Maven 安装集成,有时将模块及其 POM 文件和其他元数据发布到 Maven 本地存储库(通常位于<当前用户的主目录>/.m2/repository )中很有用。用 Maven 的话说,这称为“安装”模块。

Maven Publish Plugin 通过为容器中的每个MavenPublication自动创建PublishToMavenLocal任务,使这一切变得容易。任务名称遵循 的模式。这些任务中的每一个都连接到聚合任务中。您不需要在您的部分中。publishing.publicationspublishPubNamePublicationToMavenLocalpublishToMavenLocalmavenLocal()publishing.repositories

发布Maven搬迁信息

当项目更改其发布的工件的groupIdartifactId坐标)时,让用户知道在哪里可以找到新工件非常重要。 Maven 可以通过重定位功能来帮助解决这个问题。其工作方式是,项目在旧坐标下发布一个仅包含最小重定位 POM的附加工件;该 POM 文件指定了新工件的位置。然后,Maven 存储库浏览器和构建工具可以通知用户工件的坐标已更改。

为此,项目添加了一个额外的MavenPublication指定MavenPomRelocation

build.gradle.kts
publishing {
    publications {
        // ... artifact publications

        // Specify relocation POM
        create<MavenPublication>("relocation") {
            pom {
                // Old artifact coordinates
                groupId = "com.example"
                artifactId = "lib"
                version = "2.0.0"

                distributionManagement {
                    relocation {
                        // New artifact coordinates
                        groupId = "com.new-example"
                        artifactId = "lib"
                        version = "2.0.0"
                        message = "groupId has been changed"
                    }
                }
            }
        }
    }
}
build.gradle
publishing {
    publications {
        // ... artifact publications

        // Specify relocation POM
        relocation(MavenPublication) {
            pom {
                // Old artifact coordinates
                groupId = "com.example"
                artifactId = "lib"
                version = "2.0.0"

                distributionManagement {
                    relocation {
                        // New artifact coordinates
                        groupId = "com.new-example"
                        artifactId = "lib"
                        version = "2.0.0"
                        message = "groupId has been changed"
                    }
                }
            }
        }
    }
}

仅需要在 下指定发生变化的属性relocation,即artifactId和/或groupId。所有其他属性都是可选的。

当新工件具有不同版本时,指定version可能很有用,例如因为版本编号又从 1.0.0 开始。

自定义message可以解释工件坐标发生变化的原因。

应该为旧工件的下一个版本创建重定位 POM。例如,当 的工件坐标com.example:lib:1.0.0发生更改并且具有新坐标的工件继续版本编号并发布为 时com.new-example:lib:2.0.0,则重定位 POM 应指定重定位从com.example:lib:2.0.0com.new-example:lib:2.0.0

重定位 POM 只需发布一次,发布后应再次删除其构建文件配置。

请注意,重定位 POM 并不适合所有情况;当一个工件被分割成两个或多个单独的工件时,重定位 POM 可能没有帮助。

追溯发布搬迁信息

过go工件坐标发生变化且当时未发布任何重定位信息时,可以追溯发布重定位信息。

与上述相同的建议也适用。为了方便用户迁移,请务必注意version重定位 POM 中指定的内容。重定位 POM 应允许用户一步移动到新工件,然后允许他们在单独的步骤中更新到最新版本。例如,当 的坐标com.new-example:lib:5.0.0在 2.0.0 版本中发生更改时,理想情况下应发布重定位 POM,以将旧坐标com.example:lib:2.0.0重定位到com.new-example:lib:2.0.0。然后,用户可以从版本 2.0.0 切换com.example:libcom.new-example版本 2.0.0,然后单独更新到版本 5.0.0,逐步处理重大更改(如果有)。

搬迁信息追溯发布时,无需等待项目下次定期发布,可同时发布。如上所述,一旦发布了重定位 POM,就应该再次从构建文件中删除重定位信息。

避免重复的依赖关系

当仅工件的坐标发生变化,但工件内的类的包名称保持不变时,可能会发生依赖性冲突。项目可能(传递地)依赖于旧工件,但同时也依赖于新工件,新工件都包含相同的类,可能具有不兼容的更改。

为了检测此类冲突的重复依赖项,可以将功能作为Gradle 模块元数据的一部分发布。有关使用Java 库项目的示例,请参阅声明本地组件的附加功能

进行试运行

要在将重定位信息发布到远程存储库之前验证其是否按预期工作,可以先将其发布到本地 Maven 存储库。然后可以创建一个本地测试 Gradle 或 Maven 项目,其中包含重定位工件作为依赖项。

完整示例

以下示例演示如何签署和发布 Java 库,包括源代码、Javadoc 和自定义 POM:

build.gradle.kts
plugins {
    `java-library`
    `maven-publish`
    signing
}

group = "com.example"
version = "1.0"

java {
    withJavadocJar()
    withSourcesJar()
}

publishing {
    publications {
        create<MavenPublication>("mavenJava") {
            artifactId = "my-library"
            from(components["java"])
            versionMapping {
                usage("java-api") {
                    fromResolutionOf("runtimeClasspath")
                }
                usage("java-runtime") {
                    fromResolutionResult()
                }
            }
            pom {
                name = "My Library"
                description = "A concise description of my library"
                url = "http://www.example.com/library"
                properties = mapOf(
                    "myProp" to "value",
                    "prop.with.dots" to "anotherValue"
                )
                licenses {
                    license {
                        name = "The Apache License, Version 2.0"
                        url = "http://www.apache.org/licenses/LICENSE-2.0.txt"
                    }
                }
                developers {
                    developer {
                        id = "johnd"
                        name = "John Doe"
                        email = "john.doe@example.com"
                    }
                }
                scm {
                    connection = "scm:git:git://example.com/my-library.git"
                    developerConnection = "scm:git:ssh://example.com/my-library.git"
                    url = "http://example.com/my-library/"
                }
            }
        }
    }
    repositories {
        maven {
            // change URLs to point to your repos, e.g. http://my.org/repo
            val releasesRepoUrl = uri(layout.buildDirectory.dir("repos/releases"))
            val snapshotsRepoUrl = uri(layout.buildDirectory.dir("repos/snapshots"))
            url = if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl
        }
    }
}

signing {
    sign(publishing.publications["mavenJava"])
}

tasks.javadoc {
    if (JavaVersion.current().isJava9Compatible) {
        (options as StandardJavadocDocletOptions).addBooleanOption("html5", true)
    }
}
build.gradle
plugins {
    id 'java-library'
    id 'maven-publish'
    id 'signing'
}

group = 'com.example'
version = '1.0'

java {
    withJavadocJar()
    withSourcesJar()
}

publishing {
    publications {
        mavenJava(MavenPublication) {
            artifactId = 'my-library'
            from components.java
            versionMapping {
                usage('java-api') {
                    fromResolutionOf('runtimeClasspath')
                }
                usage('java-runtime') {
                    fromResolutionResult()
                }
            }
            pom {
                name = 'My Library'
                description = 'A concise description of my library'
                url = 'http://www.example.com/library'
                properties = [
                    myProp: "value",
                    "prop.with.dots": "anotherValue"
                ]
                licenses {
                    license {
                        name = 'The Apache License, Version 2.0'
                        url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                    }
                }
                developers {
                    developer {
                        id = 'johnd'
                        name = 'John Doe'
                        email = 'john.doe@example.com'
                    }
                }
                scm {
                    connection = 'scm:git:git://example.com/my-library.git'
                    developerConnection = 'scm:git:ssh://example.com/my-library.git'
                    url = 'http://example.com/my-library/'
                }
            }
        }
    }
    repositories {
        maven {
            // change URLs to point to your repos, e.g. http://my.org/repo
            def releasesRepoUrl = layout.buildDirectory.dir('repos/releases')
            def snapshotsRepoUrl = layout.buildDirectory.dir('repos/snapshots')
            url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
        }
    }
}

signing {
    sign publishing.publications.mavenJava
}


javadoc {
    if(JavaVersion.current().isJava9Compatible()) {
        options.addBooleanOption('html5', true)
    }
}

结果将发布以下工件:

  • POM:my-library-1.0.pom

  • Java 组件的主要 JAR 工件:my-library-1.0.jar

  • 已显式配置的源 JAR 工件:my-library-1.0-sources.jar

  • 已显式配置的 Javadoc JAR 工件:my-library-1.0-javadoc.jar

签名插件用于为每个工件生成签名文件。此外,将为所有工件和签名文件生成校验和文件。

publishToMavenLocal` 不会在$USER_HOME/.m2/repository.如果您想验证校验和文件是否已正确创建,或将其用于以后发布,请考虑使用 URL 配置自定义 Maven 存储库file://并将其用作发布目标。

删除延迟配置行为

在 Gradle 5.0 之前,该publishing {}块(默认情况下)被隐式处理,就好像其中的所有逻辑都是在项目评估后执行的。这种行为引起了相当大的混乱,并在 Gradle 4.8 中被弃用,因为它是唯一具有这种行为的块。

您的发布块或插件中可能有一些依赖于延迟配置行为的逻辑。例如,以下逻辑假设设置 artifactId 时将评估子项目:

build.gradle.kts
subprojects {
    publishing {
        publications {
            create<MavenPublication>("mavenJava") {
                from(components["java"])
                artifactId = tasks.jar.get().archiveBaseName.get()
            }
        }
    }
}
build.gradle
subprojects {
    publishing {
        publications {
            mavenJava(MavenPublication) {
                from components.java
                artifactId = jar.archiveBaseName
            }
        }
    }
}

这种逻辑现在必须包装在一个afterEvaluate {}块中。

build.gradle.kts
subprojects {
    publishing {
        publications {
            create<MavenPublication>("mavenJava") {
                from(components["java"])
                afterEvaluate {
                    artifactId = tasks.jar.get().archiveBaseName.get()
                }
            }
        }
    }
}
build.gradle
subprojects {
    publishing {
        publications {
            mavenJava(MavenPublication) {
                from components.java
                afterEvaluate {
                    artifactId = jar.archiveBaseName
                }
            }
        }
    }
}