Publish BOM (as pom.xml) using gradle plugin java-platform - gradle

I am setting up a project specific BOM that will "inherit" definitions from other BOMs (available as pom.xml) and also define own managed dependendies.
I tried the following (as stated in the java-platform docs) in my build.gradle.kts:
plugins {
`java-platform`
`maven-publish`
}
dependencies {
constraints {
api(platform("org.camunda.bpm:camunda-bom:${Versions.camunda}"))
}
}
publishing {
publications {
create<MavenPublication>("camunda-bom") {
from(components["javaPlatform"])
}
}
}
But when I do gradle publishToMavenLocal and check the resulting pom.xml in .m2/repositories it looks like:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.camunda.bpm</groupId>
<artifactId>camunda-bom</artifactId>
<version>7.10.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
Which will not work because the syntax for importing poms should be
...
<type>pom</type>
<scope>import</scope>
...
How can I publish a valid BOM as pom.xml with gradle (using version 5.3.1)?

You are defining the BOM as a constraint, but that is most likely not what you want to do.
A constraint on a platform will just say that if that dependency enters the graph elsewhere it should use the platform part of it and the version recommendation from the constraint.
If you expect that constraints of that BOM to be visible to the consumers of your platform, then you need to add the BOM as a platform dependency by doing something like:
javaPlatform {
allowDependencies()
}
dependencies {
api(platform("org.camunda.bpm:camunda-bom:${Versions.camunda}"))
}
Then this will be properly published as an inlined BOM in Maven.

Related

Can I pull the version for the Gradle org.springframework.boot plugin from my Gradle platform definition

I really like the Gradle java-platform feature. I've created my own platform that bundles spring-boot-dependencies along with other things. Now I have (shortened for clarity):
plugins {
id 'org.springframework.boot' version '2.4.1'
}
dependencies {
implementation platform("my-group:my-base-bom:1.0.0")
}
And I'd like the spring boot plugin version to automatically adjust to match the version of spring-boot-dependencies that is bundled in my platform (so if the platform went to SB 2.5.0 then plugin would do the same without my needing to change the build.gradle.
I can't figure out how to do it though without resorting to external variables. Is it possible?
Not possible. Currently, there are (3) ways to define versions for plugins:
In the Gradle file directly:
// build.gradle.kts
plugins {
id("org.springframework.boot") version "2.4.1"
}
In the plugins dependencies spec:
// settings.gradle.kts
pluginManagement {
plugins {
id("org.springframework.boot") version "2.4.1"
}
}
or with a resolution rule:
// settings.gradle.kts
pluginManagement {
resolutionStrategy {
eachPlugin {
if (requested.id.id == "org.springframework.boot") {
useVersion("2.4.1")
}
}
}
}
All of which do not accept a platform, only a single version variable.
Another way I tested, but ultimately did not work was utilizing the buildscript:
// build.gradle.kts
buildscript {
dependencies {
classpath(platform("io.mateo.sample:platform-bom:1.0.0-SNAPSHOT"))
classpath("org.springframework.boot:spring-boot-gradle-plugin")
}
}
As mentioned at the start, it's not possible.
Your custom platform can provide an opinion about which version of the Spring Boot Gradle plugin you want clients to use (especially since it's not included in the spring-boot-dependencies BOM).
Here are the relevant parts of an example platform's build.gradle.kts file, for example:
plugins {
`java-platform`
}
javaPlatform {
allowDependencies()
}
dependencies {
// This platform extends the Spring Boot platform.
api(platform("org.springframework.boot:spring-boot-dependencies:2.7.6"))
constraints {
// Provide an opinion about which version of the Spring Boot Gradle plugin clients
// should use since it's not included in the standard spring-boot-dependencies BOM.
api("org.springframework.boot:spring-boot-gradle-plugin:2.7.6")
}
}
That will generate a BOM that aligns both spring-boot-gradle-plugin and spring-boot-dependencies:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-gradle-plugin</artifactId>
<version>2.7.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Your client projects can then just depend upon your platform and inherit its opinion about the Spring Boot version using something like this:
buildSrc/build.gradle.kts:
// Pull in the version of the Spring Boot Gradle plugin specified by your
// platform, making it available to your regular build script.
dependencies {
implementation(enforcedPlatform("my-group:my-base-bom:1.0.0"))
implementation("org.springframework.boot:spring-boot-gradle-plugin")
}
build.gradle.kts:
plugins {
id("org.springframework.boot") // version inherited from your platform
}
dependencies {
// It's necessary to specify it for each configuration.
implementation(enforcedPlatform("my-group:my-base-bom:1.0.0"))
// Pull in any normal Spring Boot-managed dependencies you need (versions come from platform).
implementation("org.springframework.boot:spring-boot-starter-web")
}
Of course, you could also use Gradle version catalogs to centralize versions, which are inlined for clarity in the examples.

How to use gradle feature variant dependecies in tests?

I am migrating a Maven library project to Gradle. The original project also has optional dependencies. I use the java-library plugin but moving the formerly optional dependencies to implementation results in runtime dependencies instead of compile. So I tried the gradle feature variants which results in the right dependencies in the pom.xml. But doing so results is failing test compile as the dependencies of the feature variant are missing on the test compile classpath!
Here is my current setup in build.gradle:
apply plugin: 'java'
apply plugin: 'java-library'
apply plugin: 'maven-publish'
sourceCompatibility = 1.8
java {
registerFeature('oSupport') {
usingSourceSet(sourceSets.main)
}
}
dependencies {
api 'my.compile:dep-a:1.0.0'
implementation 'my.runtime:dep-i:1.0.0'
oSupportApi 'my.optional:dep-o:1.0.0'
}
Let's assume there is a class O available from my.optional:dep-o. If I import O in any class in src/main/java it works perfectly. Also the dependencies are exported right to Maven (using gradle generatePomFileForMavenJavaPublication, see the dependencies from the generated pom.xml below). But any test in src/test/java using class O will not compile (import my.optional.O; creates error: package my.optional does not exist)
<dependencies>
<dependency>
<groupId>my.compile</groupId>
<artifactId>dep-a</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>my.rintime</groupId>
<artifactId>dep-r</artifactId>
<version>1.0.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>my.optional</groupId>
<artifactId>dep-0</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
</dependencies>
How to solve this? I know I could have used the nebula.optional-base plugin instead of the buildin Gradle feature variant but I would prefer the new gradle builtin support for optional dependencies instead.
PS: I use Java 8 and Gradle 5.6.2
This looks like a bug when the feature source set uses the main source set. Can you report on https://github.com/gradle/gradle/issues?
In the meantime, this should fix it:
configurations {
testCompileClasspath.extendsFrom(oSupportApi)
testRuntimeClasspath.extendsFrom(oSupportApi)
testRuntimeClasspath.extendsFrom(oSupportImplementation)
}
Really weird, I agree with #melix this seems to be a Gradle bug.
The following will fix it but should not be needed, imho:
dependencies {
api 'my.compile:dep-a:1.0.0'
implementation 'my.runtime:dep-i:1.0.0'
oSupportApi 'my.optional:dep-o:1.0.0'
testImplementation(project(":${project.name}")) {
capabilities {
requireCapability("${project.group}:${project.name}-o-support")
}
}
}
For this simplified setup with only one feature dependency could be replaced by testImplementation 'my.optional:dep-o:1.0.0' but for a general larger dependency list this approch avoids repetition of the dependencies as the extendsFrom solution of #melix.

Gradle 5: BOM dependencies are not written to pom file

I'm trying to use a BOM to build project A with Gradle 5. The pom file that's created does not correctly contain the BOM or the dependencies coming from it (see below). Moreover, Trying to build project B that depends on A fails on project A's pom.
When working with DependencyManagementPlugin (i.e. Not using Gradle 5 native support), everything works:
apply plugin:
io.spring.gradle.dependencymanagement.DependencyManagementPlugin
dependencyManagement {
imports {
mavenBom 'myGroup:infra-bom:1.0.+'
}
}
Creates this in project A's pom in a dependencyManagement block:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>myGroup</groupId>
<artifactId>infra-bom</artifactId>
<version>1.0.25</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
Trying to make it work with Gradle 5 isn't so lucky:
dependencies {
compile platform ('myGroup:infra-bom:1.0.+')
...
...
}
gradle dependencies shows correct versions from the BOM, but it creates this in project A's pom in the existing dependencies block:
<dependency>
<groupId>myGroup</groupId>
<artifactId>infra-bom</artifactId>
<version>null</version> // note the null here
<scope>compile</scope>
</dependency>
Which fails builds trying to use it.
Am I correctly using Gradle 5 native support for BOM? What does it include?

How to use pom type dependency in Gradle

I need to produce transitive dependency from my Java library which is of type pom. Here is an example on how I'm doing it:
plugins {
`java-library`
`maven-publish`
}
repositories {
// some maven repo
}
dependencies {
// This is POM type dependency:
api("org.apache.sshd:apache-sshd:1.6.0") {
exclude(group = "org.slf4j")
}
}
publications {
create<MavenPublication>("maven") {
from(components["java"])
}
}
The problem with this configuration is that in the published pom.xml of my library the dependency is of type jar (by default) and declared like that:
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>apache-sshd</artifactId>
<version>1.6.0</version>
<!-- Should declare pom type -->
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>org.slf4j<groupId>
</exclusion>
</exclusions>
</dependency>
So when I try to use my published library from another project it fails as there is no such artifact as apache-sshd because it's type should be pom. So how to correctly publish desired dependency using Gradle?
Running on Gradle 5.3.1 with Kotlin DSL.
Try to use following construction for declaring dependency in Gradle
api("org.apache.sshd:apache-sshd:1.6.0#pom") {
exclude(group = "org.slf4j")
isTransitive = true
}
Looks like Gradle consumes all dependencies as jar type by default. And Maven plugin generates dependency section in pom file by using this extracted type. For pom dependency it is necessary to put correct value into type field of generated file. But if you put pom extension for your dependency, Gradle won't resolve transitive dependencies that are declared in this artifact. Set the value of transitive flag resolves this issue.
I have used it in the following way:
compile(group: "dependency_group", name: "dependency_name", version: "dependency_name", extension: "pom")
and if you want to exclude the transitive dependencies
add transitive flag and set it to false
compile(group: "dependency_group", name: "dependency_name", version: "dependency_name", extension: "pom"){
transitive = false
}

How to use a maven BOM for Spring in Gradle?

I am converting POM to Gradle and one of the things I am stuck at is having dependency management in Gradle like the following that I have in POM:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Is there a way to have Edgware.SR4 in Gradle as well?
I checked https://docs.gradle.org/4.6/release-notes.html#bom-import but that doesn't really tell me a way on how to utilize Edgware.SR4 BOM.
UPDATE
I finally have my build.gradle as follows that seems to work:
plugins{
id 'org.springframework.boot' version '1.5.8.RELEASE'
}
apply plugin: 'io.spring.dependency-management'
dependencyManagement {
imports {
mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Edgware.SR4'
}
}
This seems to be working fine but wondering if there is any flaw in this approach. Documentation available at https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/html/ suggests to use apply false to begin with in
id 'org.springframework.boot' version '1.5.8.RELEASE'
I didn't do that and it worked fine. Wondering why it was suggested like that.
Assuming that you are using Spring Boot and, therefore, already have the Dependency Management Plugin applied, you can import Spring Cloud's bom by adding the following to your build.gradle file:
dependencyManagement {
imports {
mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Edgware.SR4'
}
}
As of today, the latest versions of gradle have a built-in solution.
dependencies {
implementation(platform("org.springframework.cloud:spring-cloud-dependencies:Edgware.SR4"))
}

Resources