Files
thpeetz-notes/Quellen/IT/Customizing publishing.md
T

351 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Customizing publishing
source: https://docs.gradle.org/current/userguide/publishing_customization.html
---
## [](#sec:adding-variants-to-existing-components)[Modifying and adding variants to existing components for publishing](#sec:adding-variants-to-existing-components)
Gradles publication model is based on the notion of *components*, which are defined by plugins. For example, the Java Library plugin defines a `java` component which corresponds to a library, but the Java Platform plugin defines another kind of component, named `javaPlatform`, which is effectively a different kind of software component (a *platform*).
Sometimes we want to add *more variants* to or modify *existing variants* of an existing component. For example, if you [added a variant of a Java library for a different platform](https://docs.gradle.org/current/userguide/cross_project_publications.html#targeting-different-platforms), you may just want to declare this additional variant on the `java` component itself. In general, declaring additional variants is often the best solution to publish *additional artifacts*.
To perform such additions or modifications, the `AdhocComponentWithVariants` interface declares two methods called `addVariantsFromConfiguration` and `withVariantsFromConfiguration` which accept two parameters:
- the [outgoing configuration](https://docs.gradle.org/current/userguide/declaring_dependencies.html#sec:resolvable-consumable-configs) that is used as a variant source
- a customization action which allows you to *filter* which variants are going to be published
To utilise these methods, you must make sure that the `SoftwareComponent` you work with is itself an `AdhocComponentWithVariants`, which is the case for the components created by the Java plugins (Java, Java Library, Java Platform). Adding a variant is then very simple:
Example 1. [Adding a variant to an existing software component](#ex-adding-a-variant-to-an-existing-software-component)
`Kotlin``Groovy`
InstrumentedJarsPlugin.kt
```
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()
}
```
In other cases, you might want to modify a variant that was added by one of the Java plugins already. For example, if you activate publishing of Javadoc and sources, these become additional variants of the `java` component. If you only want to publish one of them, e.g. only Javadoc but no sources, you can modify the `sources` variant to not being published:
`Kotlin``Groovy`
build.gradle.kts
```
java { withJavadocJar() withSourcesJar()
} val javaComponent = components["java"]
as
AdhocComponentWithVariants javaComponent.withVariantsFromConfiguration(configurations["sourcesElements"])
{ skip()
} publishing { publications { create<MavenPublication>("mavenJava")
{
from(components["java"])
}
}
}
```
## [](#sec:publishing-custom-components)[Creating and publishing custom components](#sec:publishing-custom-components)
In the [previous example](#sec:adding-variants-to-existing-components), we have demonstrated how to extend or modify an existing component, like the components provided by the Java plugins. But Gradle also allows you to build a custom component (not a Java Library, not a Java Platform, not something supported natively by Gradle).
To create a custom component, you first need to create an empty *adhoc* component. At the moment, this is only possible via a plugin because you need to get a handle on the [SoftwareComponentFactory](https://docs.gradle.org/current/javadoc/org/gradle/api/component/SoftwareComponentFactory.html) :
Example 3. [Injecting the software component factory](#ex-injecting-the-software-component-factory)
`Kotlin``Groovy`
InstrumentedJarsPlugin.kt
```
class
InstrumentedJarsPlugin
@Inject constructor(
private val softwareComponentFactory:
SoftwareComponentFactory)
:
Plugin<Project>
{
```
Declaring *what* a custom component publishes is still done via the [AdhocComponentWithVariants](https://docs.gradle.org/current/javadoc/org/gradle/api/component/AdhocComponentWithVariants.html) API. For a custom component, the first step is to create custom outgoing variants, following the instructions in [this chapter](https://docs.gradle.org/current/userguide/cross_project_publications.html#sec:variant-aware-sharing). At this stage, what you should have is variants which can be used in cross-project dependencies, but that we are now going to publish to external repositories.
Example 4. [Creating a custom, adhoc component](#ex-creating-a-custom-adhoc-component)
`Kotlin``Groovy`
InstrumentedJarsPlugin.kt
```
// 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")
}
```
First we use the factory to create a new adhoc component. Then we add a variant through the `addVariantsFromConfiguration` method, which is described in more detail in the [previous section](#sec:adding-variants-to-existing-components).
In simple cases, theres a one-to-one mapping between a `Configuration` and a variant, in which case you can publish all variants issued from a single `Configuration` because they are effectively the same thing. However, there are cases where a `Configuration` is associated with additional [configuration publications](https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/ConfigurationPublications.html) that we also call *secondary variants*. Such configurations make sense in the [cross-project publications](https://docs.gradle.org/current/userguide/cross_project_publications.html#cross_project_publications) use case, but not when publishing externally. This is for example the case when between projects you share a *directory of files*, but theres no way you can publish a *directory* directly on a Maven repository (only packaged things like jars or zips). Look at the [ConfigurationVariantDetails](https://docs.gradle.org/current/javadoc/org/gradle/api/component/ConfigurationVariantDetails.html) class for details about how to skip publication of a particular variant. If `addVariantsFromConfiguration` has already been called for a configuration, further modification of the resulting variants can be performed using `withVariantsFromConfiguration`.
When publishing an adhoc component like this:
- Gradle Module Metadata will *exactly* represent the published variants. In particular, all outgoing variants will inherit dependencies, artifacts and attributes of the published configuration.
- Maven and Ivy metadata files will be generated, but you need to declare how the dependencies are mapped to Maven scopes via the [ConfigurationVariantDetails](https://docs.gradle.org/current/javadoc/org/gradle/api/component/ConfigurationVariantDetails.html) class.
In practice, it means that components created this way can be consumed by Gradle the same way as if they were "local components".
## [](#sec:publishing_custom_artifacts_to_maven)[Adding custom artifacts to a publication](#sec:publishing_custom_artifacts_to_maven)
| | |
| --- | --- |
| | Instead of thinking in terms of artifacts, you should embrace the variant aware model of Gradle. It is expected that a single module may need multiple artifacts. However this rarely stops there, if the additional artifacts represent an [optional feature](https://docs.gradle.org/current/userguide/feature_variants.html#feature_variants), they might also have different dependencies and more.<br><br>Gradle, via *Gradle Module Metadata*, supports the publication of *additional variants* which make those artifacts known to the dependency resolution engine. Please refer to the [variant-aware sharing](https://docs.gradle.org/current/userguide/cross_project_publications.html#sec:variant-aware-sharing) section of the documentation to see how to declare such variants and [check out how to publish custom components](#sec:publishing-custom-components).<br><br>If you attach extra artifacts to a publication directly, they are published "out of context". That means, they are not referenced in the metadata at all and can then only be addressed directly through a classifier on a dependency. In contrast to Gradle Module Metadata, Maven pom metadata will not contain information on additional artifacts regardless of whether they are added through a variant or directly, as variants cannot be represented in the pom format. |
The following section describes how you publish artifacts directly if you are sure that metadata, for example Gradle or POM metadata, is irrelevant for your use case. For example, if your project doesnt need to be consumed by other projects and the only thing required as result of the publishing are the artifacts themselves.
In general, there are two options:
- Create a publication only with artifacts
- Add artifacts to a publication based on a component with metadata (not recommended, instead [adjust a component](#sec:adding-variants-to-existing-components) or use a [adhoc component publication](#sec:publishing-custom-components) which will both also produce metadata fitting your artifacts)
To create a publication based on artifacts, start by defining a custom artifact and attaching it to a Gradle [configuration](https://docs.gradle.org/current/userguide/dependency_management_terminology.html#sub:terminology_configuration) of your choice. The following sample defines an RPM artifact that is produced by an `rpm` task (not shown) and attaches that artifact to the `archives` configuration:
Example 5. [Defining a custom artifact for a configuration](#ex-defining-a-custom-artifact-for-a-configuration)
`Kotlin``Groovy`
build.gradle.kts
```
val rpmFile = layout.buildDirectory.file("rpms/my-package.rpm") val rpmArtifact = artifacts.add("archives", rpmFile.get().asFile)
{ type =
"rpm" builtBy("rpm")
}
```
The `artifacts.add()` method — from [ArtifactHandler](https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.dsl.ArtifactHandler.html) — returns an artifact object of type [PublishArtifact](https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/PublishArtifact.html) that can then be used in defining a publication, as shown in the following sample:
Example 6. [Attaching a custom PublishArtifact to a publication](#ex-attaching-a-custom-publishartifact-to-a-publication)
`Kotlin``Groovy`
build.gradle.kts
```
publishing { publications { create<MavenPublication>("maven")
{ artifact(rpmArtifact)
}
}
}
```
- The `artifact()` method accepts *publish artifacts* as argument — like `rpmArtifact` in the sample — as well as any type of argument accepted by [Project.file(java.lang.Object)](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file%28java.lang.Object%29), such as a `File` instance, a string file path or a archive task.
- Publishing plugins support different artifact configuration properties, so always check the plugin documentation for more details. The `classifier` and `extension` properties are supported by both the [Maven Publish Plugin](https://docs.gradle.org/current/userguide/publishing_maven.html#publishing_maven) and the [Ivy Publish Plugin](https://docs.gradle.org/current/userguide/publishing_ivy.html#publishing_ivy).
- Custom artifacts need to be distinct within a publication, typically via a unique combination of `classifier` and `extension`. See the documentation for the plugin youre using for the precise requirements.
- If you use `artifact()` with an archive task, Gradle automatically populates the artifacts metadata with the `classifier` and `extension` properties from that task.
Now you can publish the RPM.
If you really want to add an artifact to a publication based on a component, instead of [adjusting the component](#sec:adding-variants-to-existing-components) itself, you can combine the `from components.someComponent` and `artifact someArtifact` notations.
## [](#sec:publishing_maven:conditional_publishing)[Restricting publications to specific repositories](#sec:publishing_maven:conditional_publishing)
When you have defined multiple publications or repositories, you often want to control which publications are published to which repositories. For instance, consider the following sample that defines two publications — one that consists of just a binary and another that contains the binary and associated sources — and two repositories — one for internal use and one for external consumers:
Example 7. [Adding multiple publications and repositories](#ex-adding-multiple-publications-and-repositories)
`Kotlin``Groovy`
build.gradle.kts
```
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"))
}
}
}
```
The publishing plugins will create tasks that allow you to publish either of the publications to either repository. They also attach those tasks to the `publish` aggregate task. But lets say you want to restrict the binary-only publication to the external repository and the binary-with-sources publication to the internal one. To do that, you need to make the publishing *conditional*.
Gradle allows you to skip any task you want based on a condition via the [Task.onlyIf(String, org.gradle.api.specs.Spec)](https://docs.gradle.org/current/dsl/org.gradle.api.Task.html#org.gradle.api.Task:onlyIf%28java.lang.String,org.gradle.api.specs.Spec%29) method. The following sample demonstrates how to implement the constraints we just mentioned:
`Kotlin``Groovy`
build.gradle.kts
```
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()
}
}
```
Output of `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
You may also want to define your own aggregate tasks to help with your workflow. For example, imagine that you have several publications that should be published to the external repository. It could be very useful to publish all of them in one go without publishing the internal ones.
The following sample demonstrates how you can do this by defining an aggregate task — `publishToExternalRepository` — that depends on all the relevant publish tasks:
Example 9. [Defining your own shorthand tasks for publishing](#ex-defining-your-own-shorthand-tasks-for-publishing)
`Kotlin``Groovy`
build.gradle.kts
```
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"]
})
}
```
## [](#sec:configuring_publishing_tasks)[Configuring publishing tasks](#sec:configuring_publishing_tasks)
The publishing plugins create their non-aggregate tasks after the project has been evaluated, which means you cannot directly reference them from your build script. If you would like to configure any of these tasks, you should use deferred task configuration. This can be done in a number of ways via the projects `tasks` collection.
For example, imagine you want to change where the `generatePomFileForPubNamePublication` tasks write their POM files. You can do this by using the [TaskCollection.withType(java.lang.Class)](https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/TaskCollection.html#withType-java.lang.Class-) method, as demonstrated by this sample:
`Kotlin``Groovy`
build.gradle.kts
```
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 }
```
The above sample uses a regular expression to extract the name of the publication from the name of the task. This is so that there is no conflict between the file paths of all the POM files that might be generated. If you only have one publication, then you dont have to worry about such conflicts since there will only be one POM file.