修改现有组件并添加变体以进行发布
Gradle 的发布模型基于组件的概念,组件由插件定义。例如,Java Library 插件定义了一个java
对应于库的组件,但是 Java Platform 插件定义了另一种组件,名为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
中有更详细的描述。
在简单的情况下,a 和变体之间存在一对一的映射Configuration
,在这种情况下,您可以发布从单个版本发出的所有变体,Configuration
因为它们实际上是相同的东西。但是,在某些情况下,a与其他配置发布Configuration
相关联,我们也将其称为次要变体。此类配置在跨项目发布用例中有意义,但在外部发布时则不然。例如,在项目之间共享文件目录,但无法直接在 Maven 存储库上发布目录(只能打包诸如 jar 或 zip 之类的东西)时,就会出现这种情况。有关如何跳过特定变体发布的详细信息,请参阅ConfigurationVariantDetails类。如果已经调用了配置,则可以使用 执行对结果变体的进一步修改。addVariantsFromConfiguration
withVariantsFromConfiguration
当发布这样的临时组件时:
-
Gradle 模块元数据将准确代表已发布的变体。特别是,所有传出变体都将继承已发布配置的依赖项、工件和属性。
-
将生成 Maven 和 Ivy 元数据文件,但您需要声明如何通过ConfigurationVariantDetails类将依赖项映射到 Maven 范围。
实际上,这意味着以这种方式创建的组件可以被 Gradle 使用,就像它们是“本地组件”一样。
将自定义工件添加到出版物中
您应该拥抱 Gradle 的变体感知模型,而不是考虑工件。预计单个模块可能需要多个工件。然而,这很少就此停止,如果附加工件代表可选功能,它们也可能具有不同的依赖关系等等。
Gradle 通过Gradle Module Metadata支持发布其他变体,使依赖解析引擎了解这些工件。请参阅文档的变体感知共享部分,了解如何声明此类变体并查看如何发布自定义组件。
如果您将额外的工件直接附加到出版物中,它们将“断章取义”地发布。这意味着,它们根本不在元数据中引用,只能通过依赖项上的分类器直接寻址。与 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 Publish Plugin和Ivy Publish Plugin均支持
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
集合以多种方式完成。
例如,假设您想要更改任务写入 POM 文件的位置。您可以使用TaskCollection.withType(java.lang.Class)方法来执行此操作,如以下示例所示:generatePomFileForPubNamePublication
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 文件。