Maven 發佈外掛提供將建置成品發佈到 Apache Maven 儲存庫的功能。發佈到 Maven 儲存庫的模組可以被 Maven、Gradle(請參閱宣告相依性)和其他理解 Maven 儲存庫格式的工具使用。您可以在發佈概觀中了解發佈的基本原理。

用法

若要使用 Maven 發佈外掛,請在您的建置腳本中包含以下內容

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

Maven 發佈外掛在專案上使用名為 publishing 的擴充功能,類型為 PublishingExtension。此擴充功能提供具名發佈物容器和具名儲存庫容器。Maven 發佈外掛可與 MavenPublication 發佈物和 MavenArtifactRepository 儲存庫搭配使用。

任務

generatePomFileForPubNamePublicationGenerateMavenPom

為名為 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 任務

將所有已定義的發佈物(包括其中繼資料(POM 檔案等))複製到本機 Maven 快取。

發佈物

此外掛提供 發佈物,類型為 MavenPublication。若要了解如何定義和使用發佈物,請參閱關於基本發佈的章節。

您可以在 Maven 發佈物中配置四個主要項目

您可以在完整發佈範例中查看所有這些操作。MavenPublication 的 API 文件中有其他程式碼範例。

產生的 POM 中的身分值

產生的 POM 檔案的屬性將包含從以下專案屬性衍生的身分值

覆寫預設身分值很容易:只需在配置 MavenPublication 時指定 groupIdartifactIdversion 屬性即可。

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 將 groupIdartifactId 限制為有限的字元集 ([A-Za-z0-9_\\-.]+),而 Gradle 強制執行此限制。對於 version(以及成品 extensionclassifier 屬性),Gradle 將處理任何有效的 Unicode 字元。

唯一明確禁止的 Unicode 值為 \/ 和任何 ISO 控制字元。提供的值會在發佈早期進行驗證。

自訂產生的 POM

產生的 POM 檔案可以在發佈之前進行自訂。例如,當將程式庫發佈到 Maven Central 時,您將需要設定某些中繼資料。Maven 發佈外掛為此目的提供 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。它不會依賴轉換,而是發佈已解析的版本。

這是透過使用 versionMapping DSL 方法完成的,該方法允許配置 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()
                }
            }
        }
    }
}

在上述範例中,Gradle 將使用在 runtimeClasspath 上解析的版本,用於在 api 中宣告的相依性,這些相依性會對應到 Maven 的 compile 範圍。Gradle 也將使用在 runtimeClasspath 上解析的版本,用於在 implementation 中宣告的相依性,這些相依性會對應到 Maven 的 runtime 範圍。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')
        }
    }
}

您會想要配置的兩個主要項目是儲存庫的

  • URL(必要)

  • 名稱(選用)

您可以定義多個儲存庫,只要它們在建置腳本中具有唯一的名稱即可。您也可以宣告一個(且僅限一個)未命名的儲存庫。該儲存庫將採用「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 安裝整合,有時將模組發佈到 Maven 本機儲存庫(通常位於 <目前使用者的主目錄>/.m2/repository)及其 POM 檔案和其他中繼資料中會很有用。在 Maven 術語中,這稱為「安裝」模組。

Maven 發佈外掛讓這項操作變得容易,方法是為 publishing.publications 容器中的每個 MavenPublication 自動建立 PublishToMavenLocal 任務。任務名稱遵循 publishPubNamePublicationToMavenLocal 的模式。這些任務中的每一個都連接到 publishToMavenLocal 彙總任務。您的 publishing.repositories 區段中不需要有 mavenLocal()

發佈 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.0 重新導向到 com.new-example:lib:2.0.0

重新導向 POM 只需要發佈一次,一旦發佈,應再次從建置檔案配置中移除它。

請注意,重新導向 POM 並非適用於所有情況;當成品已分割成兩個或多個個別成品時,重新導向 POM 可能沒有幫助。

追溯發佈重新導向資訊

在成品的座標過去已變更,且當時未發佈重新導向資訊之後,可以追溯發佈重新導向資訊。

上述相同的建議適用。為了簡化使用者的遷移,務必注意在重新導向 POM 中指定的 version。重新導向 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。然後,使用者可以從 com.example:lib 切換到 com.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 中建立檢查總和檔案。如果您想要驗證檢查總和檔案是否已正確建立,或將它們用於稍後的發佈,請考慮使用 file:// URL 配置自訂 Maven 儲存庫,並將其用作發佈目標。

移除延遲配置行為

在 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
                }
            }
        }
    }
}