修改和新增變體至現有元件以進行發佈
Gradle 的發佈模型基於元件的概念,這些元件由外掛程式定義。例如,Java 程式庫外掛程式定義了一個 java
元件,該元件對應於一個程式庫,但 Java 平台外掛程式定義了另一種元件,名為 javaPlatform
,它實際上是一種不同的軟體元件(一個平台)。
有時我們想要為現有元件新增更多變體或修改現有變體。例如,如果您為不同的平台新增了 Java 程式庫的變體,您可能只想在 java
元件本身上宣告此額外變體。一般而言,宣告額外變體通常是發佈額外構件的最佳解決方案。
為了執行此類新增或修改,AdhocComponentWithVariants
介面宣告了兩個名為 addVariantsFromConfiguration
和 withVariantsFromConfiguration
的方法,它們接受兩個參數
-
用作變體來源的傳出配置
-
允許您篩選哪些變體將被發佈的自訂動作
若要利用這些方法,您必須確保您使用的 SoftwareComponent
本身是 AdhocComponentWithVariants
,Java 外掛程式(Java、Java 程式庫、Java 平台)建立的元件就是這種情況。然後新增變體非常簡單
val javaComponent = components.findByName("java") as AdhocComponentWithVariants
javaComponent.addVariantsFromConfiguration(outgoing) {
// dependencies for this variant are considered runtime dependencies
mapToMavenScope("runtime")
// and also optional dependencies, because we don't want them to leak
mapToOptional()
}
AdhocComponentWithVariants javaComponent = (AdhocComponentWithVariants) project.components.findByName("java")
javaComponent.addVariantsFromConfiguration(outgoing) {
// dependencies for this variant are considered runtime dependencies
it.mapToMavenScope("runtime")
// and also optional dependencies, because we don't want them to leak
it.mapToOptional()
}
在其他情況下,您可能想要修改 Java 外掛程式已新增的變體之一。例如,如果您啟用 Javadoc 和來源的發佈,這些將成為 java
元件的額外變體。如果您只想發佈其中一個,例如僅 Javadoc 但沒有來源,您可以修改 sources
變體以不發佈
java {
withJavadocJar()
withSourcesJar()
}
val javaComponent = components["java"] as AdhocComponentWithVariants
javaComponent.withVariantsFromConfiguration(configurations["sourcesElements"]) {
skip()
}
publishing {
publications {
create<MavenPublication>("mavenJava") {
from(components["java"])
}
}
}
java {
withJavadocJar()
withSourcesJar()
}
components.java.withVariantsFromConfiguration(configurations.sourcesElements) {
skip()
}
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
}
}
}
建立和發佈自訂元件
在先前的範例中,我們示範了如何擴充或修改現有元件,例如 Java 外掛程式提供的元件。但 Gradle 也允許您建置自訂元件(不是 Java 程式庫,不是 Java 平台,不是 Gradle 原生支援的元件)。
若要建立自訂元件,您首先需要建立一個空的特設元件。目前,這只能透過外掛程式完成,因為您需要取得 SoftwareComponentFactory 的控制代碼
class InstrumentedJarsPlugin @Inject constructor(
private val softwareComponentFactory: SoftwareComponentFactory) : Plugin<Project> {
private final SoftwareComponentFactory softwareComponentFactory
@Inject
InstrumentedJarsPlugin(SoftwareComponentFactory softwareComponentFactory) {
this.softwareComponentFactory = softwareComponentFactory
}
宣告自訂元件發佈什麼仍然透過 AdhocComponentWithVariants API 完成。對於自訂元件,第一步是建立自訂傳出變體,遵循本章中的說明。在這個階段,您應該擁有的是可用於跨專案依賴關係的變體,但我們現在要將其發佈到外部儲存庫。
// create an adhoc component
val adhocComponent = softwareComponentFactory.adhoc("myAdhocComponent")
// add it to the list of components that this project declares
components.add(adhocComponent)
// and register a variant for publication
adhocComponent.addVariantsFromConfiguration(outgoing) {
mapToMavenScope("runtime")
}
// create an adhoc component
def adhocComponent = softwareComponentFactory.adhoc("myAdhocComponent")
// add it to the list of components that this project declares
project.components.add(adhocComponent)
// and register a variant for publication
adhocComponent.addVariantsFromConfiguration(outgoing) {
it.mapToMavenScope("runtime")
}
首先,我們使用工廠建立一個新的特設元件。然後,我們透過 addVariantsFromConfiguration
方法新增變體,這在上一節中有更詳細的描述。
在簡單的情況下,Configuration
和變體之間存在一對一的映射,在這種情況下,您可以發佈從單個 Configuration
發出的所有變體,因為它們實際上是同一件事。但是,在某些情況下,Configuration
與額外的 配置發佈相關聯,我們也將其稱為次要變體。此類配置在多專案建置中很有意義,但在外部發佈時則不然。例如,專案之間共用檔案目錄就是這種情況,但是無法將目錄直接發佈到 Maven 儲存庫(僅限於 jar 或 zip 等封裝內容)。查看 ConfigurationVariantDetails 類別,以了解如何跳過特定變體的發佈。如果已針對配置呼叫 addVariantsFromConfiguration
,則可以使用 withVariantsFromConfiguration
執行對結果變體的進一步修改。
當發佈像這樣的特設元件時
-
Gradle 模組中繼資料將精確地表示已發佈的變體。特別是,所有傳出變體都將繼承已發佈配置的依賴關係、構件和屬性。
-
將產生 Maven 和 Ivy 中繼資料檔案,但您需要透過 ConfigurationVariantDetails 類別宣告依賴關係如何映射到 Maven 範圍。
實際上,這表示以這種方式建立的元件可以像「本機元件」一樣被 Gradle 使用。
新增自訂構件至發佈
您應該擁抱 Gradle 的變體感知模型,而不是從構件的角度思考。預期單個模組可能需要多個構件。但是,如果額外構件代表選用功能,則情況很少止於此,它們也可能具有不同的依賴關係等等。
Gradle 透過Gradle 模組中繼資料支援發佈額外變體,這些變體使構件為依賴關係解析引擎所知。請參閱文件的變體感知共用章節,以了解如何宣告此類變體,並查看如何發佈自訂元件。
如果您直接將額外構件附加到發佈,它們將在「上下文之外」發佈。這表示它們根本未在中繼資料中引用,然後只能透過依賴關係上的分類器直接尋址。與 Gradle 模組中繼資料相反,Maven pom 中繼資料將不包含有關額外構件的資訊,無論它們是透過變體還是直接新增的,因為變體無法以 pom 格式表示。
以下章節說明如果您確定中繼資料(例如 Gradle 或 POM 中繼資料)與您的使用案例無關,則如何直接發佈構件。例如,如果您的專案不需要被其他專案使用,並且發佈的唯一結果是構件本身。
一般而言,有兩個選項
若要建立基於構件的發佈,請首先定義自訂構件並將其附加到您選擇的 Gradle 配置。以下範例定義了由 rpm
工作(未顯示)產生的 RPM 構件,並將該構件附加到 conf
配置
configurations {
create("conf")
}
val rpmFile = layout.buildDirectory.file("rpms/my-package.rpm")
val rpmArtifact = artifacts.add("conf", rpmFile.get().asFile) {
type = "rpm"
builtBy("rpm")
}
configurations {
conf
}
def rpmFile = layout.buildDirectory.file('rpms/my-package.rpm')
def rpmArtifact = artifacts.add('conf', rpmFile.get().asFile) {
type = 'rpm'
builtBy 'rpm'
}
artifacts.add()
方法 — 來自 ArtifactHandler — 傳回 PublishArtifact 類型的構件物件,然後可以在定義發佈時使用,如下列範例所示
publishing {
publications {
create<MavenPublication>("maven") {
artifact(rpmArtifact)
}
}
}
publishing {
publications {
maven(MavenPublication) {
artifact rpmArtifact
}
}
}
-
artifact()
方法接受發佈構件作為引數 — 就像範例中的rpmArtifact
— 以及 Project.file(java.lang.Object) 接受的任何引數類型,例如File
實例、字串檔案路徑或封存工作。 -
發佈外掛程式支援不同的構件配置屬性,因此請始終查看外掛程式文件以獲取更多詳細資訊。 Maven 發佈外掛程式和 Ivy 發佈外掛程式都支援
classifier
和extension
屬性。 -
自訂構件需要在發佈中是不同的,通常透過
classifier
和extension
的唯一組合。請參閱您使用的外掛程式的文件,以了解精確的要求。 -
如果您將
artifact()
與封存工作一起使用,Gradle 會自動使用該工作中的classifier
和extension
屬性來填入構件的中繼資料。
現在您可以發佈 RPM。
如果您真的想根據元件將構件新增到發佈,而不是調整元件本身,您可以組合 from components.someComponent
和 artifact someArtifact
表示法。
限制發佈至特定儲存庫
當您定義了多個發佈或儲存庫時,您通常希望控制哪些發佈發佈到哪些儲存庫。例如,考慮以下範例,該範例定義了兩個發佈 — 一個僅包含二進制檔案,另一個包含二進制檔案和相關來源 — 以及兩個儲存庫 — 一個供內部使用,另一個供外部使用者使用
publishing {
publications {
create<MavenPublication>("binary") {
from(components["java"])
}
create<MavenPublication>("binaryAndSources") {
from(components["java"])
artifact(tasks["sourcesJar"])
}
}
repositories {
// change URLs to point to your repos, e.g. http://my.org/repo
maven {
name = "external"
url = uri(layout.buildDirectory.dir("repos/external"))
}
maven {
name = "internal"
url = uri(layout.buildDirectory.dir("repos/internal"))
}
}
}
publishing {
publications {
binary(MavenPublication) {
from components.java
}
binaryAndSources(MavenPublication) {
from components.java
artifact sourcesJar
}
}
repositories {
// change URLs to point to your repos, e.g. http://my.org/repo
maven {
name = 'external'
url = layout.buildDirectory.dir('repos/external')
}
maven {
name = 'internal'
url = layout.buildDirectory.dir('repos/internal')
}
}
}
發佈外掛程式將建立工作,允許您將任一發佈發佈到任一儲存庫。它們還將這些工作附加到 publish
聚合工作。但假設您想要將僅限二進制檔案的發佈限制為外部儲存庫,而將包含來源的二進制檔案發佈限制為內部儲存庫。為此,您需要使發佈成為有條件的。
Gradle 允許您基於條件透過 Task.onlyIf(String, org.gradle.api.specs.Spec) 方法跳過您想要的任何工作。以下範例示範了如何實作我們剛才提到的約束
tasks.withType<PublishToMavenRepository>().configureEach {
val predicate = provider {
(repository == publishing.repositories["external"] &&
publication == publishing.publications["binary"]) ||
(repository == publishing.repositories["internal"] &&
publication == publishing.publications["binaryAndSources"])
}
onlyIf("publishing binary to the external repository, or binary and sources to the internal one") {
predicate.get()
}
}
tasks.withType<PublishToMavenLocal>().configureEach {
val predicate = provider {
publication == publishing.publications["binaryAndSources"]
}
onlyIf("publishing binary and sources") {
predicate.get()
}
}
tasks.withType(PublishToMavenRepository) {
def predicate = provider {
(repository == publishing.repositories.external &&
publication == publishing.publications.binary) ||
(repository == publishing.repositories.internal &&
publication == publishing.publications.binaryAndSources)
}
onlyIf("publishing binary to the external repository, or binary and sources to the internal one") {
predicate.get()
}
}
tasks.withType(PublishToMavenLocal) {
def predicate = provider {
publication == publishing.publications.binaryAndSources
}
onlyIf("publishing binary and sources") {
predicate.get()
}
}
gradle publish
的輸出> gradle publish > Task :compileJava > Task :processResources > Task :classes > Task :jar > Task :generateMetadataFileForBinaryAndSourcesPublication > Task :generatePomFileForBinaryAndSourcesPublication > Task :sourcesJar > Task :publishBinaryAndSourcesPublicationToExternalRepository SKIPPED > Task :publishBinaryAndSourcesPublicationToInternalRepository > Task :generateMetadataFileForBinaryPublication > Task :generatePomFileForBinaryPublication > Task :publishBinaryPublicationToExternalRepository > Task :publishBinaryPublicationToInternalRepository SKIPPED > Task :publish BUILD SUCCESSFUL in 0s 10 actionable tasks: 10 executed
您可能還想定義自己的聚合工作來協助您的工作流程。例如,假設您有幾個應發佈到外部儲存庫的發佈。一次發佈所有這些發佈而不發佈內部發佈可能會非常有用。
以下範例示範了如何透過定義聚合工作 — publishToExternalRepository
— 該工作依賴於所有相關的發佈工作來完成此操作
tasks.register("publishToExternalRepository") {
group = "publishing"
description = "Publishes all Maven publications to the external Maven repository."
dependsOn(tasks.withType<PublishToMavenRepository>().matching {
it.repository == publishing.repositories["external"]
})
}
tasks.register('publishToExternalRepository') {
group = 'publishing'
description = 'Publishes all Maven publications to the external Maven repository.'
dependsOn tasks.withType(PublishToMavenRepository).matching {
it.repository == publishing.repositories.external
}
}
此特定範例透過將 TaskCollection.withType(java.lang.Class) 與 PublishToMavenRepository 工作類型一起使用來自動處理相關發佈工作的引入或移除。如果您要發佈到與 Ivy 相容的儲存庫,則可以使用 PublishToIvyRepository 執行相同的操作。
配置發佈工作
發佈外掛程式會在專案評估後建立其非聚合工作,這表示您無法直接從建置腳本中引用它們。如果您想要配置這些工作中的任何一項,則應使用延遲工作配置。這可以透過專案的 tasks
集合以多種方式完成。
例如,假設您想要變更 generatePomFileForPubNamePublication
工作寫入其 POM 檔案的位置。您可以透過使用 TaskCollection.withType(java.lang.Class) 方法來完成此操作,如本範例所示
tasks.withType<GenerateMavenPom>().configureEach {
val matcher = Regex("""generatePomFileFor(\w+)Publication""").matchEntire(name)
val publicationName = matcher?.let { it.groupValues[1] }
destination = layout.buildDirectory.file("poms/${publicationName}-pom.xml").get().asFile
}
tasks.withType(GenerateMavenPom).all {
def matcher = name =~ /generatePomFileFor(\w+)Publication/
def publicationName = matcher[0][1]
destination = layout.buildDirectory.file("poms/${publicationName}-pom.xml").get().asFile
}
上面的範例使用正則表達式從工作名稱中提取發佈的名稱。這是為了避免可能產生的所有 POM 檔案的檔案路徑之間發生衝突。如果您只有一個發佈,則不必擔心此類衝突,因為只會有一個 POM 檔案。