Gradle 5: BOM dependencies are not written to pom file - gradle

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?

Related

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.

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

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.

How does maven resolve the dependencies of the main dependencies on which our application is build?

I am trying to understand maven a little more. How is maven able to download the dependencies of the main dependency of the application? For example assuming my application has main dependency like this:
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.7.0</version>
<scope>provided</scope>
</dependency>
Now, when maven downloads this jar , it downloads the dependencies for this jar as well. For example, see the screen shot below:
As can be seen, maven has not only downloaded the hadoop-hdfs-2.7.0.jar but also all it dependencies.
Now, my questions is how maven knows what are the dependencies for the "top-level" dependency, that is in this case the "top-level" dependency is hadoop-hdfs, so what all jars it has to download for this?
I see this as well in the .m2/respository for hadoop-hdfs:
I opened the .pom file, the contents are (partly):
<project>
....
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.7.0</version>
<description>Apache Hadoop HDFS</description>
<name>Apache Hadoop HDFS</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-auth</artifactId>
<scope>provided</scope>
</dependency>
<dependencies>
...
</project>
What is this hadoop-hdfs-2.7.0.pom ? Does this file give information to maven what are the dependencies to be downloaded for hadoop-hdfs-2.7.0.jar?
Can anyone help me clear these things?
First of all you are right, the hadoop-hdfs-2.7.0.pom tells Maven
about the libraries that hadoop depends upon. But, when using hadoop
as a dependency in your project, maven uses the below strategies to
finalize the list of dependencies in addition to using the
hadoop-hdfs-2.7.0.pom.
If a dependency is specified with groupid, artifactid and version in the current project under the dependencies tag, it takes the first
precedence. This is how hadoop-hdfs got added in your project.
Dependency Management takes the next precedence. When a dependency is specified only with group and artifact id's under dependencies tag
but at the same time, the dependency is defined under
dependencyManagement tag with version and transitively inside hadoops pom.xml also,
the one under the dependencyManagement tag will be given preference.
Dependency Mediation takes the last precedence. Dependencies are resolved using dependency mediation. Meaning, in your case the
dependencies mentioned inside hadoop-hdfs-2.7.0.pom are the transitive
dependencies (indirectly depends on these dependencies since your
dependency "hadoop-hdfs" requires it) of your project and this process continues
recursively until all child dependencies are resolved.
Note: There are other features such as excluding dependencies, marking
one optional and importing a list of dependencies. But they are used
sparsely. More information with examples can be found in the below URL
[https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Management][1]

Override parent pom dependency

I made a simple EAR project with maven and wildfly, but I have some problems with obsolete dependencies.
Project structure is like:
Project
--EarProject
--BaseProject
--WarProject
--EjbProject
In parent Project's pom there is dependency:
<dependency>
<groupId>org.wildfly.bom</groupId>
<artifactId>jboss-javaee-7.0-with-tools</artifactId>
<version>${version.jboss.bom}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
And in BaseProject's pom I use Selenium:
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-remote-driver</artifactId>
</dependency>
The problem is that in BaseProject Maven libraries I see older versions of Selenium (ie. selenium-firefox-driver 2.40.0 instead of newer 2.44.0) and because of bugs in 2.40.0 my app does not work corectly.
I tried to add:
<version>2.44.0</version>
in BaseProject's pom, but I got warning like
Overriding managed version 2.40.0 for selenium-remote-driver
and it does not work.
How can I override version of dependency from parent's pom or exclude selenium from jboss-javaee-7.0-with-tools dependency?
How can I override version of dependency from parent's pom
The "Overriding managed version 2.40.0 for selenium-remote-driver" warning is a just a m2e warning not a Maven warning. With your code, you are correctly overriding the version of the dependency of the parent's pom. See this bug at Eclipse site for m2e about this warning. You can assert that by looking at the dependency tree (in Eclipse or with the mvn dependency:tree command)
or exclude selenium from jboss-javaee-7.0-with-tools dependency?
You can do that with the exclusions tag. It is a tag where you specify each groupId and artifactId that you want to exclude from the transitive dependencies resolved by Maven.

Gradle cannot resolve dependency

In my build.gradle file I have a line in dependencies:
compile group: 'org.jboss.seam.validation', name: 'seam-validation-api', version:'3.1.0.Final'
When I try to run the project, e.g. 'test' task, I get an error:
> Could not resolve org.jboss.seam.validation:seam-validation-api:3.1.0.Final.
Required by:
com.smspl.mc5:mc5-web-ui:1.0.0-SNAPSHOT
com.smspl.mc5:mc5-web-ui:1.0.0-SNAPSHOT > com.smspl.mc5:mc5-common-rest:1.0.0-SNAPSHOT
> Could not parse POM /Users/amorfis/.m2/repository/org/jboss/seam/validation/seam-validation-api/3.1.0.Final/seam-validation-api-3.1.0.Final.pom
> Unable to resolve version for dependency 'javax.enterprise:cdi-api:jar'
> Could not parse POM https://nexus.softwaremill.com/content/groups/smlcommon-repos/org/jboss/seam/validation/seam-validation-api/3.1.0.Final/seam-validation-api-3.1.0.Final.pom
> Unable to resolve version for dependency 'javax.enterprise:cdi-api:jar'
I'm aware that the problem is that version of 'javax.enterprise:cdi-api:jar' is not specified in the seam-validation-api pom. It is specified in it's parent, and gradle probably has some problems figuring it out. The parent part of seam-validation-api pom looks like this:
<parent>
<groupId>org.jboss.seam.validation</groupId>
<artifactId>seam-validation-parent</artifactId>
<version>3.1.0.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
My gut feeling tells me the problem is in part. I also tried to add this parent dependency explicitely, by adding new line to build.gradle 'dependencies', but without any luck.
Anyone knows solution to this?
UPDATE: In the parent pom:
<groupId>org.jboss.seam.validation</groupId>
<artifactId>seam-validation-parent</artifactId>
<packaging>pom</packaging>
<version>3.1.0.Final</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>seam-bom</artifactId>
<version>${seam.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Here is a little research of the problem (only research, NOT a solution):
seam-validation-api-3.1.0.Final.pom has parent seam-validation-parent-3.1.0.Final.pom
seam-validation-parent-3.1.0.Final.pom has parent seam-parent-19.pom
The file seam-parent-4.pom contained version definition for cdi-api, but seam-parent-19.pom does not.
The file seam-validation-parent-3.0.0.Final.pom did contain dependencyManagement for cdi-api, but newer version seam-validation-parent-3.1.0.Final.pom does not.
I also looked into the code of GradlePomModuleDescriptorBuilder (the class that throws the abovementioned "Could not resolve..." exception). It seems that the class looks for DependencyManagement sections up the parent-pom chain, but it does not respect resolutionStrategy (or any other definitions from gradle script). So currently it is not possible to augment/override dependencyManagement of POM.
My advice: contact the developer(s) of seam framework and ask them to fix the dependencies.

Resources