Maven plugin does not read dependencies from .m2


(tulsoba) #1

I have two projects A and B that I build with gradle using the maven plugin. B depends on A. When I run install on project A the corresponding jar is installed into:

.m2\repository\com\mygroup\A-1.0-SNAPSHOT\

Afterwards I build B (also running install) which also installs into:

.m2\repository\com\mygroup\B-1.0-SNAPSHOT\

Now to the wierd part. If I add the eclipse plugin and generate .classpath files:

clean --refresh dependencies cleanEclipseClasspath eclipseClasspath install

I see that B actually uses A from (opening the corresponding .classpath file):

<classpathentry kind="lib" path="C:/Users/.../.gradle/caches/artifacts-14/filestore/com.mygroup/A/1.0-SNAPSHOT/jar/af40dc0b198bd036ac19d000f530ad88f179ccf4/A-1.0-SNAPSHOT.jar" exported="true"/>

Even worse the A artifacts in .gradle and .m2 are not identical (I have tried to wipe both repos and doing a clean build)! Any ideas what goes wrong here?

And why are my projects installed into .gradle when I use the maven plugin?


(Peter Niederwieser) #2

Is this one multi-project Gradle build, or two separate single-project builds?


(tulsoba) #3

Its separate projects. I have extended it a bit so now there is a project C which depends on B. When B is build its artifact is installed into .m2. When C is build it installs B into:

.gradle\caches\artifacts-14\filestore

So its actually first when building/installing C that B is dumped into .gradle\caches\artifacts-14\filestore.


(Peter Niederwieser) #4

In many respects, Gradle treats the local Maven repository like any other Maven repository. This explains the behavior that you see.


(tulsoba) #5

Not sure I understand. If I apply the maven plugin I would expect that artifact are both installed and read from .m2. In the above example artifacts are installed into .m2 but read from .gradle\caches\artifacts-14\filestore. And since the artifacts are different between the two repos my build currently does not work.

Is it possible to completely disable .gradle\caches\artifacts-14\filestore and only use .m2. Its rather confusing to have multiple local repos especially when all 3rdparties are installed into .gradle\caches\artifacts-14\filestore instead of .m2.


(Peter Niederwieser) #6

The purpose of the Maven plugin is to publish to Maven repositories. It doesn’t replace Gradle’s cache. The artifacts are probably different because Gradle caches artifacts for 24 hours by default. You can overcome this with ‘–refresh-dependencies’ or by reconfiguring cache timeouts. See ResolutionStrategy in the DSL reference for the latter.


(tulsoba) #7

I already build with --refresh-dependencies and setting:

project.configurations.all {
        resolutionStrategy {
          cacheChangingModulesFor 0, 'seconds'
      }
    }

has no effect. I have just added a new class MyNewClass to project A but when I build B (which depend on A) it cannot see MyNewClass. When I go to:

.gradle\caches\artifacts-14\filestore\com.mygroup\A\1.0-SNAPSHOT\jar\90261b0c0e128e8bd9e9aae170cc6b38457f4a88\A-1.0-SNAPSHOT.jar

the archive does NOT contain the MyNewClass but the archive in:

.m2\repository\com\mygroup\A\1.0-SNAPSHOT\A-1.0-SNAPSHOT.jar

does. But since B insist on using the artifact from the gradle cache my project fails to build. Any ideas?


(Peter Niederwieser) #8

Hard to say what’s wrong without seeing your build scripts. Does B declare ‘mavenLocal()’ as a repository? Do you use ‘–refresh-dependencies’ when building B?


(tulsoba) #9

Yes I also build project B with --refresh-dependencies. This might have something to say though. Each project applies a custom plugin before anything else which does:

void apply(Project project) {
    project.configurations.all {
      resolutionStrategy {
        cacheChangingModulesFor 0, 'seconds'
      }
    }
    project.getPlugins().apply('java')
    project.getPlugins().apply('maven')
    project.getPlugins().apply('eclipse')
      project.repositories {
      maven {
        url "http://internal.repo"
      }
      mavenLocal()
    }
      project.afterEvaluate {
      // Parse dependencies from pom.xml
      parseDependencies(project)
        project.getTasks().add('testsJar', Jar).configure {
        dependsOn: project.testClasses
        classifier = 'test'
        from project.sourceSets.test.output
      }
         project.artifacts {
        archives project.testsJar
      }
    }

In the afterEvaluate scope a pom.xml file is read and dependencies are parsed into gradle dependencies. Could the scope of afterEvaluate have anything to say when it comes to correctly refreshing artifacts?


(Peter Niederwieser) #10

The suspicious thing here is the internal Maven repo before ‘mavenLocal()’. Maybe it finds A there. Once a snapshot has been found in a repository, no further repositories are searched.


(tulsoba) #11

EDIT: Reversing the order of the maven repos solves the problem (it tried to use the old deployed on the remote repo)

But generally when you specify a build with --refresh-dependencies should it not select the latest version (based on some timestamp info) independently of how the order of the repos are specified?

Eg. in maven it would use the one from the local cache if it was build after the version located in the remote repository.


(Peter Niederwieser) #12

Gradle and Maven use the same strategy here. It’s just that Gradle has its own cache and treats the local Maven repository like any other Maven repository.


(Peter Niederwieser) #13

Regarding your issue, I’d try two things:

  1. Remove the internal Maven repo from the build script 2. Delete the Gradle cache

(tulsoba) #14

Yes I solved it by reversing the order of the repos - so now mavenLocal() is always checked first.

There were some old versions in the internal repo that it used (deployed a few days ago). I cannot delete the internal repo since its used for downloading all the 3rdparties. From maven I am used to this build flow:

  1. Check local .m2 to see if dependencies can be resolved. 2) If user builds with -U (equivalent to refresh–dependencies) check local .m2 first. Next check remote repo for and updated version. If a newer version is found in the remote repo update the local version with remote updated version and continue build.

(tulsoba) #15

What needs to be verified is (assuming the maven plugin is used and mavenLocal() is specified first):

  1. User A build and deploys project X which is a dependency for project Y. 2) User B builds project Y with --refresh-dependencies. Instead of using the local X artifact the updated version from remote repo is downloaded and used. 3) User B modifies X and builds it locally. 4) User B rebuilds Y with --refresh-dependencies and now his local version of X is used instead of the remote version because the remote version of X is outdated compared to his version in .m2

(Peter Niederwieser) #16

As I’ve tried to explain, Gradle behaves the same. To get the behavior you are asking for, it would have to treat ‘mavenLocal()’ different from other Maven repositories, but it doesn’t currently do that. And like Maven, it will stop the search at the first repository (other than its cache) that has a snapshot.


(tulsoba) #17

The problem with specifying mavenLocal() before remote maven repos is that I get the same error described here:

http://forums.gradle.org/gradle/topics/_could_not_resolve_all_dependencies_because_of_maven_local_repository

so currently I need to manually change the order of the repos when I get either:

  1. A newer version exists in mavenLocal() - mavenLocal() must be specified first

  2. Getting the error:

Could not resolve all dependencies for configuration ‘:compile’. > Artifact ‘commons-lang:commons-lang:2.5@jar’ not found.

In the above case remote maven repos must be specified first.


(Peter Niederwieser) #18

It’s unclear why that would happen. Can you provide a reproducible sample build?