Maven 發佈外掛提供將建置成品發佈到 Apache Maven 儲存庫的功能。發佈到 Maven 儲存庫的模組可以被 Maven、Gradle(請參閱宣告相依性)和其他理解 Maven 儲存庫格式的工具使用。您可以在發佈概觀中了解發佈的基本原理。
用法
若要使用 Maven 發佈外掛,請在您的建置腳本中包含以下內容
plugins {
`maven-publish`
}
plugins {
id 'maven-publish'
}
Maven 發佈外掛在專案上使用名為 publishing
的擴充功能,類型為 PublishingExtension。此擴充功能提供具名發佈物容器和具名儲存庫容器。Maven 發佈外掛可與 MavenPublication 發佈物和 MavenArtifactRepository 儲存庫搭配使用。
任務
generatePomFileForPubNamePublication
— GenerateMavenPom-
為名為 PubName 的發佈物建立 POM 檔案,填入已知的中繼資料,例如專案名稱、專案版本和相依性。POM 檔案的預設位置是 build/publications/$pubName/pom-default.xml。
publishPubNamePublicationToRepoNameRepository
— PublishToMavenRepository-
將 PubName 發佈物發佈到名為 RepoName 的儲存庫。如果您有未明確命名的儲存庫定義,則 RepoName 將為「Maven」。
publishPubNamePublicationToMavenLocal
— PublishToMavenLocal-
將 PubName 發佈物連同發佈物的 POM 檔案和其他中繼資料複製到本機 Maven 快取 — 通常為 <目前使用者的主目錄>/.m2/repository —。
publish
-
相依於:所有
publishPubNamePublicationToRepoNameRepository
任務將所有已定義的發佈物發佈到所有已定義的儲存庫的彙總任務。它不包括將發佈物複製到本機 Maven 快取。
publishToMavenLocal
-
相依於:所有
publishPubNamePublicationToMavenLocal
任務將所有已定義的發佈物(包括其中繼資料(POM 檔案等))複製到本機 Maven 快取。
發佈物
此外掛提供 發佈物,類型為 MavenPublication。若要了解如何定義和使用發佈物,請參閱關於基本發佈的章節。
您可以在 Maven 發佈物中配置四個主要項目
-
元件 — 透過 MavenPublication.from(org.gradle.api.component.SoftwareComponent)。
-
自訂成品 — 透過 MavenPublication.artifact(java.lang.Object) 方法。請參閱 MavenArtifact 以取得自訂 Maven 成品的可用配置選項。
-
標準中繼資料,例如
artifactId
、groupId
和version
。 -
POM 檔案的其他內容 — 透過 MavenPublication.pom(org.gradle.api.Action)。
您可以在完整發佈範例中查看所有這些操作。MavenPublication
的 API 文件中有其他程式碼範例。
產生的 POM 中的身分值
產生的 POM 檔案的屬性將包含從以下專案屬性衍生的身分值
-
groupId
- Project.getGroup() -
artifactId
- Project.getName() -
version
- Project.getVersion()
覆寫預設身分值很容易:只需在配置 MavenPublication 時指定 groupId
、artifactId
或 version
屬性即可。
publishing {
publications {
create<MavenPublication>("maven") {
groupId = "org.gradle.sample"
artifactId = "library"
version = "1.1"
from(components["java"])
}
}
}
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
(以及成品 extension
和 classifier
屬性),Gradle 將處理任何有效的 Unicode 字元。
唯一明確禁止的 Unicode 值為 \
、/
和任何 ISO 控制字元。提供的值會在發佈早期進行驗證。
自訂產生的 POM
產生的 POM 檔案可以在發佈之前進行自訂。例如,當將程式庫發佈到 Maven Central 時,您將需要設定某些中繼資料。Maven 發佈外掛為此目的提供 DSL。請參閱 DSL 參考中的 MavenPom 以取得可用屬性和方法的完整文件。以下範例顯示如何使用最常見的屬性和方法
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/"
}
}
}
}
}
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
publishing {
publications {
create<MavenPublication>("mavenJava") {
versionMapping {
usage("java-api") {
fromResolutionOf("runtimeClasspath")
}
usage("java-runtime") {
fromResolutionResult()
}
}
}
}
}
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 應使用變體的預設類別路徑,而 runtimeClasspath
是 java-runtime
的預設類別路徑。
儲存庫
此外掛提供 儲存庫,類型為 MavenArtifactRepository。若要了解如何定義和使用儲存庫進行發佈,請參閱關於基本發佈的章節。
以下是定義發佈儲存庫的簡單範例
publishing {
repositories {
maven {
// change to point to your repo, e.g. http://my.org/repo
url = uri(layout.buildDirectory.dir("repo"))
}
}
}
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
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)
}
}
}
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
時
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)
}
}
}
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 重新導向資訊
當專案變更其發佈成品的 groupId
或 artifactId
(座標)時,務必讓使用者知道可以在哪裡找到新的成品。Maven 可以透過重新導向功能來協助實現此目的。其運作方式是專案會在舊座標下發佈額外的成品,該成品僅包含最小的重新導向 POM;該 POM 檔案指定可以在哪裡找到新的成品。然後,Maven 儲存庫瀏覽器和建置工具可以通知使用者成品的座標已變更。
為此,專案會新增額外的 MavenPublication
,指定 MavenPomRelocation
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"
}
}
}
}
}
}
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
。所有其他屬性都是選用的。
當新成品具有不同的版本時,指定 自訂 |
重新導向 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
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)
}
}
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 時進行評估
subprojects {
publishing {
publications {
create<MavenPublication>("mavenJava") {
from(components["java"])
artifactId = tasks.jar.get().archiveBaseName.get()
}
}
}
}
subprojects {
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
artifactId = jar.archiveBaseName
}
}
}
}
這種邏輯現在必須包裝在 afterEvaluate {}
區塊中。
subprojects {
publishing {
publications {
create<MavenPublication>("mavenJava") {
from(components["java"])
afterEvaluate {
artifactId = tasks.jar.get().archiveBaseName.get()
}
}
}
}
}
subprojects {
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
afterEvaluate {
artifactId = jar.archiveBaseName
}
}
}
}
}