[GRADLE-1615] Support publication of artifacts to .gradle/cache for collaboration across projects without need for .m2 Created: 14/Jun/11  Updated: 10/Feb/17  Resolved: 10/Feb/17

Status: Resolved
Project: Gradle
Affects Version/s: None
Fix Version/s: None

Type: New Feature
Reporter: Chris Beams Assignee: Unassigned
Resolution: Won't Fix Votes: 14


 Description   

Scenario: I have two independent Gradle-built projects A and B. A depends on artifacts from B.

Currently, in order to make artifacts from B available to A, I must do the following:

  • apply the maven plugin to both projects
  • add mavenLocal() to the repositories closure of project A
  • run `gradle install` from project B in order to publish its artifacts to the local .m2 cache

With the above complete, I can issue a `gradle build` from project A and it will resolve project B artifacts from the .m2 cache.

This is an ironic workaround at best. The maven support in gradle is crucially important for adoption in a largely maven-based world, obviously. But when a team has already committed to Gradle across multiple projects, forcing the team to revert to maven concepts and directory structures and plugins within Gradle leaves the team scratching their head.

I understand that this inability to self-publish is probably based on Ivy limitations under the hood, but this distinction is lost on everyday users and project maintainers.

I'd like to hear what the thinking has been so far on this topic, and whether we might see a native-Gradle local publication mechanism in the near future.

Thanks!



 Comments   
Comment by Adam Murdoch [ 21/Jun/11 ]

My preference for solving this problem is to provide some way to aggregate the 2 builds together into a single build. Any external dependency on a project in the aggregate build would be automatically replaced with a project dependency.

For example:

Given projectA has group = 'org.gradle.group', name = 'A', version = whatever

And projectB has

dependencies

{ compile 'org.gradle.group:A:1.2' }

Then, when aggregated in the same build, Gradle will replace the dependency in projectB with

dependencies

{ compile projectA }

There are a couple of advantages to this approach:

Firstly, the command-line workflow is much simpler. Instead of:

  • make change in A
  • cd A && gradle install
  • cd B && gradle build

I have:

  • make change in A
  • cd B && gradle build

Another important benefit is that the IDE integration can do the same sort of dependency substitution for projects in different builds. So, I can run gradle eclipse, and this will generate the eclipse meta-data for the projects in both builds, with external dependencies replaced with eclipse project dependencies. Which, of course, means my workflow IDE is really simple.

In the meantime, you could do something similar to the maven approach:

repositories {
    ivy {
        name = 'local'
        artifactPattern "${gradle.gradleUserHomeDir}/local-repo/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"
    }
}

task publishLocalArchives(type: Upload) {
    repositories.add(project.repositories.local)
    configuration = configurations.archives
}

This might by something useful to pop in a plugin somewhere.

Comment by Chris Beams [ 21/Jun/11 ]

Hi Adam,

I might be misunderstanding what you mean by "aggregating the two builds together into a single build", but for our purposes, the "less efficient" workflow of

  • make change in A
  • cd A && gradle install
  • cd B && gradle build

is not really a problem, it's actually desirable. It's very common for one of our developers to be working on one of the Spring projects, let's say Spring Social, that may currently compile against Spring Framework 3.1.0.BUILD-SNAPSHOT, which has been fetched from our artifact repository and populated into the user's ~/.gradle/cache. All good so far. Now, that same developer realizes that they'd like to make some changes to Spring Framework to support their current work on Spring Social. So they check out Spring Framework from SVN and make their change. At this point it's actually more convenient, and I would argue more intuitive and natural for the developer to simply do a `gradle install` from the Spring Framework checkout dir, and then cd over to Spring Social and pick it up than it would be for them to somehow 'aggregate' these two projects together. As far as I can tell, this aggregation would be artificial from the outset. The relationship in reality between Spring Social and Spring Framework is that the former depends on the latter. The build script already expresses that, and the ~/.gradle/cache already contains Spring Framework snapshot build artifacts. Why wouldn't we work within that current arrangement and simply allow the user to 'install' locally built snapshots over the top of those that were downloaded from the internet?

Thanks!

Comment by Matthew J. McCullough [ 03/Jul/11 ]

A prototype script (apply from of this idea is committed along with a shell based test.
https://github.com/matthewmccullough/gradle-applyfrom-scripts/tree/master/localinstall

Questions remain:
1) Should this be a first class/core plugin? I'm likely to say so and will work towards that if folks tell em to.
2) Should this script use ~/.m2/repository or some other ~/.gradle/<directory>? Right now, it uses $GRADLE_HOME/local-repo

Comment by Chris Beams [ 04/Jul/11 ]

Hi Matthew. The plugin looks great at a glance; it's certainly simple enough

To your first question, I believe the answer is that it should absolutely be core functionality. Right now, using your plugin has little benefit over using the 'maven' plugin - they both require an extra step (adding the 'apply from') directive, and require knowing and executing a plugin-specific task ('install', 'publishLocalArchives', etc). I would argue that this plugin should be applied by default (or whenever the 'java' plugin is applied at least), and that the task name should be something straightforward like 'install'. There is some concern about conflict with the 'maven' plugin's own 'install' task, so that should be considered.

To the second question, I think it should use ~/.gradle. The directory is already there, and already houses artifacts downloaded from repositories. I haven't heard a reason yet why we wouldn't simply publish local artifacts directly within that structure. I think that any solution that depends on Maven infrastructure by default is broken; Gradle should be completely self-contained as a build tool, and offer complete integration with Maven for those who need it. If Gradle depends on Maven's directory structures, etc by default, it seems like we're conceding the current and future ubiquity of Maven. Not the most forward-looking perspective.

Last thought: ideally, artifacts could be published to both ~/.gradle and ~/.m2 with one command. The default local-publication plugin always publishes artifacts to ~/.gradle, but if the 'maven' plugin has also been applied, then artifacts get published to .m2 as well. Doing this all with one task would be nice.

Comment by Matthew J. McCullough [ 05/Jul/11 ]

That was just step one. I've published this now (as a first step) to http://github.com/gradle/scripts and I'll be turning it into a first class plugin soon. That full plugin (API, not DSL) version will use ~/.gradle and have an option toggle property for simul-deploying to ~/.m2.

Comment by Chris Beams [ 05/Jul/11 ]

Sounds perfect, Matthew! Thanks for diving in on this one.

Comment by Tom Cawley [ 26/Jul/11 ]

If your ${gradle.gradleUserHomeDir} points to a non-HTTP(s) location, (e.g., C:\.gradle), then try the following:

localinstall.gradle
repositories {
    add(new org.apache.ivy.plugins.resolver.FileSystemResolver()) {
        name = 'local'
        addIvyPattern "${gradle.gradleUserHomeDir}/local-repo/[organisation]/[module]/[revision]/ivy-[revision].xml"
        addArtifactPattern "${gradle.gradleUserHomeDir}/local-repo/[organisation]/[module]/[revision]/[module]-[revision](-[classifier]).[ext]"
        descriptor = 'optional'
        checkmodified = true
        m2compatible = true
    }
}

task publishLocalArchives(type: Upload) {
    repositories.add(project.repositories.local)
    configuration = configurations.archives
}

My ${gradle.gradleUserHomeDir} was pointing to D:\blah and I was getting an exception: java.net.MalformedURLException: unknown protocol: d

Comment by Antony Stubbs [ 09/Apr/12 ]

That useFrom doesn't seem to work for me. I get task cannot be found. If I add the code manually to my build, it works.

Is there an easy to way get the javadocs / source jar deployed with that?

Comment by Antony Stubbs [ 09/Apr/12 ]

In the long term, being able to manually set the location for a 'sub project' would be useful.

Comment by Kevin Stembridge [ 15/Sep/12 ]

I've tried using the workaround from Matthew but, unless I'm doing something wrong, I don't think it solves the problem.

The first issue is that its not publishing a module descriptor (ivy.xml). So when Gradle is resolving dependencies it will prefer a repository that has a module descriptor over one that doesn't.
http://gradle.org/docs/current/userguide/dependency_management.html#sec:dependency_resolution

Even if an ivy.xml was published I think there is another issue. I'm just guessing here based on my understanding of how dependency resolution works but I think that if a newer version of the snapshot is published to my remote repository by another developer I will not be able to pull it down because Gradle will check the local repository first and go no further.

I'm just wondering where the Gradle folks stand on this issue. In my opinion it would be a big win for productivity to be able to publish to the local Gradle cache and it should be very easy to implement.

Chris, if you're still watching this thread, did you ever come up with an acceptable workflow to get around this issue?

Comment by Steve Ash [ 05/Jul/16 ]

Almost four years later and I've now had two teams try to switch to gradle and running into this general workflow problem: local "changing" deps vs remote "changing" deps published by our CI server from other team member updates.

Has anyone found a good workaround?

Comment by Benjamin Muschko [ 15/Nov/16 ]

As announced on the Gradle blog we are planning to completely migrate issues from JIRA to GitHub.

We intend to prioritize issues that are actionable and impactful while working more closely with the community. Many of our JIRA issues are inactionable or irrelevant. We would like to request your help to ensure we can appropriately prioritize JIRA issues you’ve contributed to.

Please confirm that you still advocate for your JIRA issue before December 10th, 2016 by:

  • Checking that your issues contain requisite context, impact, behaviors, and examples as described in our published guidelines.
  • Leave a comment on the JIRA issue or open a new GitHub issue confirming that the above is complete.

We look forward to collaborating with you more closely on GitHub. Thank you for your contribution to Gradle!

Comment by Benjamin Muschko [ 10/Feb/17 ]

Thanks again for reporting this issue. We haven't heard back from you after our inquiry from November 15th. We are closing this issue now. Please create an issue on GitHub if you still feel passionate about getting it resolved.

Generated at Wed Jun 30 12:00:04 CDT 2021 using Jira 8.4.2#804003-sha1:d21414fc212e3af190e92c2d2ac41299b89402cf.