How to break a maven build when there is a dependency conflict? - maven-2

I ran mvn dependency:tree for a project and I saw output like the following:
[INFO] my:project:jar:1.0.0-SNAPSHOT
[INFO] +- some.other:library:jar:2.0.0:compile
[INFO] | \- org.slf4j:slf4j-api:jar:1.6.1:compile
[INFO] +- org.slf4j:slf4j-simple:jar:1.6.0:compile
[INFO] | \- (org.slf4j:slf4j-api:jar:1.6.0:compile - omitted for conflict with 1.6.1)
This is a bad state to be in because my project depends directly on slf4j 1.6.0 and some library that we depend on transitively depends on slf4j 1.6.1. These two versions happen to be binary compatible so the build passes without any warnings. Is there a way to get Maven to be more strict about its dependency resolution so that I could configure a new build that would fail in this scenario? In this case, the solution would be to just update our dependency to the newer version of slf4j.

The maven-enforcer-plugin has a dependencyConvergence configuration which does what I want. Coincidentally, the example from the documentation uses slf4j.
Configure it like this:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<id>enforce</id>
<configuration>
<rules>
<DependencyConvergence />
</rules>
</configuration>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
</plugin>
This combination of dependencies will cause a build to fail:
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.6.0</version>
</dependency>
</dependencies>
With this being logged during compilation:
[ERROR]
Dependency convergence error for org.slf4j:slf4j-api:1.6.1 paths to dependency are:
+-org.myorg:my-project:1.0.0-SNAPSHOT
+-org.slf4j:slf4j-jdk14:1.6.1
+-org.slf4j:slf4j-api:1.6.1
and
+-org.myorg:my-project:1.0.0-SNAPSHOT
+-org.slf4j:slf4j-nop:1.6.0
+-org.slf4j:slf4j-api:1.6.0

Though dated, I think this SO discussion is related.

Related

Dependencies In Same Reactor

I have a very simple Tycho reactor with two modules: one is a standard Maven project with this addition to make it a bundle:
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>default-bundle</id>
<phase>package</phase>
<goals>
<goal>bundle</goal>
</goals>
<configuration>
<instructions>
<Export-Package>org.acme.jar</Export-Package>
</instructions>
<manifestLocation>META-INF</manifestLocation>
</configuration>
</execution>
</executions>
</plugin>
The second is a Tycho project that has a dependency to the above JAR in the MANIFEST.MF.
If I start the build, I get the following exception:
[ERROR] Cannot resolve project dependencies:
[ERROR] Software being installed: plugin 0.0.1.qualifier
[ERROR] Missing requirement: plugin 0.0.1.qualifier requires 'bundle org.acme.jar 0.0.1' but it could not be found
Which is really weird, because the bundle is in the same reactor.
But no worries, I can just add the Maven dependency, too:
<!-- parent pom.xml -->
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>target-platform-configuration</artifactId>
<version>0.26.0</version>
<configuration>
<pomDependencies>consider</pomDependencies>
</configuration>
</plugin>
<!-- plug-in pom.xml -->
<dependencies>
<dependency>
<groupId>org.acme</groupId>
<artifactId>jar</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
Still I get the same exception, which is weird because the documentation claims: Maven resolves the GAV dependencies according to the normal Maven rules.
That's just not true. Evidently org.acme.jar doesn't get resolved. Or maybe Tycho fails to see that it's a bundle.
The JAR module is an API project used for server side components, and we want to drop SWT / Tycho in the long run, so it's not an option to make org.acme.jar an Eclipse plug-in.
How do I define dependencies in the same reactor for Tycho?
I’m afraid that what you ask for is currently not possible. The Tycho Wiki documents this limitation in the dependency on pom-first artifacts HOW-TO.
That being said, if you really want your whole build (maven-bundle-plugin and Tycho parts) to run with a single mvn clean install, then using the maven-invoker-plugin at the end of the “plain Maven” build to fork a “Tycho build” should work. It’s a rather cumbersome workaround, however (example on Github).

Maven - pass argument to use in exec-maven-plugin

in my pom I've added the exec-maven-plugin to call a java class which will generate a file. This class requires some parameters to be passed to the main method, one of those is the location of an input file (outside the project). Until now I've been using a relative path for this which works fine:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<phase>test</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.laco.projectmaster.util.LanguageGenerator</mainClass>
<arguments>
<argument>../PM-Config/dev/PMLanguage.xls</argument>
<argument>PM4.0</argument>
<argument>${project.build.outputDirectory}/com/laco/projectmaster/props/resources</argument>
<argument>ProjectMaster</argument>
<argument>Created during maven build (POM Version: ${pom.version})</argument>
</arguments>
</configuration>
</plugin>
Now I'm starting to use hudson to install/package and deploy the wars and I cannot longer use this relative path. Simple I thought, I just pass the location of the input file when invoking maven like:
mvn clean package -Dlangdir=C:/somedir
and then alter the pom like:
<argument>${langdir}/PMLanguage.xls</argument>
However, this parameter simply gets ignored here. The path the main class receives as argument becomes null/PMLanguage.xls . The parameter itself is available in maven, I tested with succes using an echo in the antrun plugin. The correct path was echoed.
Are the paremeters you pass to maven then not available by default no matter where you reference them in the pom?
thanks for any help,
Stijn
I can't reproduce the issue. I used the following test class:
package com.stackoverflow.q3421918;
public class Hello
{
public static void main( String[] args )
{
System.out.println( args[0] + " " + args[1] );
}
}
And the following pom.xml:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.stackoverflow.q3421918</groupId>
<artifactId>Q3421918</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- this was a test for a workaround -->
<properties>
<myprop>${langdir}</myprop>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2</version>
<executions>
<execution>
<phase>test</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.stackoverflow.q3421918.Hello</mainClass>
<arguments>
<argument>${myprop}</argument>
<argument>${langdir}</argument>
</arguments>
</configuration>
</plugin>
</plugins>
</build>
</project>
And here is the output I get:
$ mvn clean package -Dlangdir=C:/somedir
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building Q3421918
[INFO] task-segment: [clean, package]
[INFO] ------------------------------------------------------------------------
...
[INFO] Preparing exec:java
[WARNING] Removing: java from forked lifecycle, to prevent recursive invocation.
[INFO] No goals needed for project - skipping
[INFO] [exec:java {execution: default}]
Hello c:/somedir c:/somedir
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
...
Tested with Maven 2.2.1.

How do I get Maven to fail when conflicting versions of the same artifact are referenced?

I'd like my Maven build to fail if the same artifact is referenced with different versions in my dependency tree. This would seem like a fairly trivial option, but I can't work out how to do it. Any clues?
The maven-enforcer-plugin has a dependencyConvergence rule which does what you want. Here's an example from the documentation.
This will cause a build to fail:
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.6.0</version>
</dependency>
</dependencies>
With this being logged during compilation:
[ERROR]
Dependency convergence error for org.slf4j:slf4j-api:1.6.1 paths to dependency are:
+-org.myorg:my-project:1.0.0-SNAPSHOT
+-org.slf4j:slf4j-jdk14:1.6.1
+-org.slf4j:slf4j-api:1.6.1
and
+-org.myorg:my-project:1.0.0-SNAPSHOT
+-org.slf4j:slf4j-nop:1.6.0
+-org.slf4j:slf4j-api:1.6.0
The Maven Dependency Plugin will include a new dependency:analyze-duplicate in its version 2.2 and this is IMHO the closest thing you can get without specific development (it won't fail the build but will list duplicate dependencies).
This goal has been added for MDEP-206 (Add new Mojo to find duplicate dependency declared), which is fixed, but the version 2.2 has yet to be released so if you want to use this feature, you'll have to build a SNAPSHOT from the source tree:
https://svn.apache.org/repos/asf/maven/plugins/trunk/maven-dependency-plugin/
If you really want to fail the build in case of duplicate, either write your own mojo (extend the above one) or submit a RFE of the Maven Dependency Plugin.
As a side note, Maven 3 does Throw [a] Validation Error if pom contains a dependency with two different versions out of the box. To be totally accurate, this will be reported as warning in Maven 3.0 to not break existing Maven 2.x builds (see MNG-4005) and will be increased to error in 3.1 (see MNG-4622).
You can have the build fail on dependency analysis warnings using the dependency plugin.
See
http://maven.apache.org/plugins/maven-dependency-plugin/examples/failing-the-build-on-dependency-analysis-warnings.html
Add the following plugin:
Source: http://maven.apache.org/enforcer/enforcer-rules/dependencyConvergence.html
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<configuration>
<rules>
<dependencyConvergence/>
</rules>
</configuration>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
</plugin>

maven dependencies groovy

I'm running a project that has a dependency on groovy 1.7-beta-1. The gmaven plugin uses groovy version 1.6 as a dependency. In my pom, I specify in the dependency management section the grooyv-all version as :
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>1.7-beta-1</version>
</dependency>
</dependencies>
</dependencyManagement>
Yet when I run maven in debug mode I see that groovy 1.6 is being used for a dependency to the gmaven plugin. I thought my dependency management section would override this so they all use 1.7-beta-1, but I'm getting errors due to the different groovy versions. any help here would be appreciated.
thanks,
Jeff
Here's a refined version of Pascal's answer. I upgraded the main plugin version to 1.2, the dependency to Groovy 1.7, and wrapped it all in a pluginManagement tag so that it will nicely leverage the inheritance model.
Keep in mind that the 1.3-SNAPSHOT of the GMaven plugin has already begun using the 1.7-rc2 Groovy provider.
<!-- I wrapped everything in a plugin management section so that this can be neatly inherited across all your poms -->
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>gmaven-plugin</artifactId>
<!-- Notice I upgraded it to 1.2 -->
<!-- Details here http://repo1.maven.org/maven2/org/codehaus/gmaven/gmaven-plugin/1.2/gmaven-plugin-1.2.pom -->
<version>1.2</version>
<dependencies>
<dependency>
<groupId>org.codehaus.gmaven.runtime</groupId>
<artifactId>gmaven-runtime-1.7</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</pluginManagement>
Overriding a dependency used by a plugin is a nice ability that was actually introduced by Maven 2.0.9.
To do so, at least with a plugin that you are using as a normal build plugin - as opposed to a report which is not the case with the the gmaven-plugin so I won't cover this case here - simply add a dependency block inside the plugin block, like this (this is a sample so versions may be inaccurate):
<plugin>
<groupId>org.codehaus.groovy.maven</groupId>
<artifactId>gmaven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<goals>
<goal>generateStubs</goal>
<goal>compile</goal>
<goal>generateTestStubs</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>1.7-beta-1</version>
</dependency>
</dependencies>
</plugin>
As long as the new version of the dependency is "API compatible" with the version the plugin was linked against, you should be ok. If not, then you'll obviously have to upgrade to a newer version of the plugin compatible with the new API (i.e. likely using it as dependency), which is what you did.
To make gmaven accurately picks the right runtime is by configuring the "providerSelection" value, e.g.
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>gmaven-plugin</artifactId>
<configuration>
<providerSelection>1.7</providerSelection>
</configuration>
FYI, for the groovy:providers mojo, these are the configurations it expects (I extracted them by debugging to org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(List, Stack, MavenSession, MavenProject) (look for XmlPlexusConfiguration):
<configuration>
<remoteRepositories implementation="java.util.List">${project.pluginArtifactRepositories}</remoteRepositories>
<project implementation="org.apache.maven.project.MavenProject">${project}</project>
<artifactRepository implementation="org.apache.maven.artifact.repository.ArtifactRepository">${localRepository}</artifactRepository>
<pluginArtifactMap implementation="java.util.Map">${plugin.artifactMap}</pluginArtifactMap>
<providerSelection implementation="java.lang.String">${gmaven.runtime}</providerSelection>
</configuration>
You need to add a similar 1.7 dependency to the dependencies of the plugin in a similarly structured <plugin> or <pluginManagement> section. Your dependency management section you are adding is proper, but does not affect the plugin dependencies. I'll try to review this reply and post an example later when I'm back at my desk.

Maven test dependency in multi module project

I use maven to build a multi module project. My module 2 depends on Module 1 src at compile scope and module 1 tests in test scope.
Module 2 -
<dependency>
<groupId>blah</groupId>
<artifactId>MODULE1</artifactId>
<version>blah</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
This works fine. Say my module 3 depends on Module1 src and tests at compile time.
Module 3 -
<dependency>
<groupId>blah</groupId>
<artifactId>MODULE1</artifactId>
<version>blah</version>
<classifier>tests</classifier>
<scope>compile</scope>
</dependency>
When I run mvn clean install, my build runs till module 3, fails at module 3 as it couldn't resolve the module 1 test dependency. Then I do a mvn install on module 3 alone, go back and run mvn install on my parent pom to make it build. How can I fix this?
I have a doubt about what you are trying to do but but I'll assume you want to reuse the tests that you have created for a project (module1) in another. As explained in the note at the bottom of the Guide to using attached tests:
Note that previous editions of this guide suggested to use <classifier>tests</classifier> instead of <type>test-jar</type>. While this currently works for some cases, it does not properly work during a reactor build of the test JAR module and any consumer if a lifecycle phase prior to install is invoked. In such a scenario, Maven will not resolve the test JAR from the output of the reactor build but from the local/remote repository. Apparently, the JAR from the repositories could be outdated or completely missing, causing a build failure (cf. MNG-2045).
So, first, to package up compiled tests in a JAR and deploy them for general reuse, configure the maven-jar-plugin as follows:
<project>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.2</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Then, install/deploy the test JAR artifact as usual (using mvn install or mvn deploy).
Finally, to use the test JAR, you should specify a dependency with a specified type of test-jar:
<project>
...
<dependencies>
<dependency>
<groupId>com.myco.app</groupId>
<artifactId>foo</artifactId>
<version>1.0-SNAPSHOT</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>
...
</project>
Regarding to my comment to Pascals question i think i have found a stuitable answer :
<plugins>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>2.2</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
<phase>test-compile</phase>
</execution>
</executions>
<configuration>
<outputDirectory>${basedir}\target</outputDirectory>
</configuration>
</plugin>
</plugins>
The main difference here as you see here is the <phase> tag.
I will create the test-jar and it will be available in the compile phase of the tests and not only after the package phase.
Works for me.