[GRADLE-1014] project dependency with file instead of multiproject Created: 30/Jun/10  Updated: 08/Feb/17  Resolved: 08/Feb/17

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

Type: Improvement
Reporter: Philip Crotwell Assignee: Unassigned
Resolution: Fixed Votes: 42


 Description   

On Wed, Jun 30, 2010 at 12:48 AM, Adam Murdoch <adam@gradle.biz> wrote:
On 26/06/10 4:10 AM, Philip Crotwell wrote:

Hi

I have found several time that I wish I could tell gradle that one
project depeneded on another gradle project, but not actually make
them part of the same multiproject. For example, I am developing a
library, say libA, and an application, say AppB. The application and
the library don't really make sense as a multiproject as they are very
separate entities, just that the application uses the library and I
happen to be the developer of both. When coding on the application, I
really want to use the lastest source for the library, instead of the
latest release so that I can improve both as I discover issues instead
of having to make a new "release" of the library every time for a new
feature to be used by the client. Of course, at some point the release
of the application would probably depend on the released version of
the library, but in the middle of the development cycle this doesn't
always make sense.

I think this is a pretty common problem, and Gradle should have a good solution for it.

At some point soon, we want to split up Gradle itself into a bunch of separate pieces which can be built and released separately (for example, core and several plugin bundles). And we will hit exactly the same problem, where we want to work on the core and plugins at the same time. So we will need a solution to this problem in order to split up Gradle.

The current way I do things is to have one
mega-miltuproject with everything project I have ever worked on
listed, so that I can choose what dependency graph I want. But this
seems wrong to me.

So, what I would like to do is instead of:
dependencies

{ compile project(':libA') }

do
dependencies

{ compile project('file:../myOtherDev/libA') }

and as long as there is a build.gradle in the ../myOtherDev/libA
directory, gradle will first build libA and use the resulting jar as
the dependency for building the application.

It would be good if the dependency declaration were independent of whether you're doing a composite or a standalone build, so that it doesn't have to change when you switch between the two. For example, I don't want to have to use something like this to do a local build:

dependencies {
compile project('someUrl')
}

and something like this to do a release or CI build:

dependencies {
compile group: 'mygroup', name: 'otherProject', version: '1.2'
}

One question is which of the above identifiers we should use for a dependency which is sometimes built locally, and sometimes fetched from a repository.

One option is to use the (group, name, version) tuple for the dependency, and somewhere else, provide some way to tell Gradle how to build the dependency locally. For example, the following might declare a repository that uses the build script 'someUrl' , if it exists, to build the dependency 'mygroup:otherProject:1.2' locally.

repositories {
local

{ group 'mygroup' name 'otherProject' version '1.2' buildFile 'someUrl' }

}

Then, you would declare the dependency as a normal external dependency:

dependencies {
compile group: 'mygroup', name: 'otherProject', version: '1.2'
}

Another option is to use a Project object for each dependency which can be built locally or fetched from a repository. For example, the following might create such a Project:

def libA = project {
buildFile 'someUrl'
group 'myGroup'
name 'otherProject'
version '1.2'
}

Then, you would declare the dependency as a normal project dependency:

dependencies {
compile libA
}

There are advantages and disadvantages to both these options.

Option 1 can be applied to any external dependency, whereas for option 2, you have to plan ahead and declare the dependency in a particular way. This means, for example, that option 1 would allow an init script, or a plugin, or another project, or Gradle itself, to inject the local build declaration into the project which contains the dependency.

Option 1 also allows us to use a convention for where to find local dependencies. For example, Gradle might, for every dependency, look for $rootDir/../$group/$name/.gradle or ~/gradle/$group/$name/.gradle, and build the dependency locally instead of fetching it from a repository.

Option 2 is interesting because it could potentially make other things from the target project available, such as tasks, configurations or properties. You could, for example, declare a task dependency on a task in the target project. This could be used for lots of interesting use cases. The Gradle website build, for example, could make use of this.

Option 2 also appeals because it could also be applied within a multi-project build, not just between builds. You could have, for example, a large multi-project build where you have some or all of the artifacts already built by a CI build, and you want to reuse those artifacts and build only a handful of projects locally. Or perhaps you have a build pipeline, where some artifacts are built and tested by one CI build, and then those same artifacts are assembled into a distribution by a later build. This way, we introduce the concept that a build does not always need to build every artifact every time.

Option 2 also gives us a way to express in the DSL the way that buildSrc project works. Then, the buildSrc project stops being some special case and becomes a reusable concept:

def buildSrc = project

{ projectDir "$rootDir/buildDir }

dependencies

{ classpath buildSrc }

This might also be a way to get away from settings.gradle, as what is
important is that gradle can find a dependency project relative to an
existing one rather then all the projects being listed in the setting
file.

Absolutely.

One down side is that if the source code is given to another
developer, the relative directories would have to be maintained or
updated, but this seems reasonable to me.

One last idea would be to have a general URL instead of just a file,
perhaps allowing one project to depend on another that is in a public
web accessible version control system. Gradle could cache the source
of the dependency project and build it locally. That may be a little
ambitious a goal, but...

I think we should end up with something like this. At the very least, Gradle should understand that some dependencies need to be built before they are usable. That might mean building a JAR, compiling a shared lib, installing and starting some software, whatever.

If this sounds like an idea worth considering, I'll open a jira issue.

It's an excellent idea. Please open a JIRA issue.



 Comments   
Comment by Russ Rollins [ 09/Aug/10 ]

I believe I echo the same core concept that Philip's communicated: a given environment with x number of Gradle projects, with some subset relying on artifacts generated by one or more in the superset--where these projects may exist in or out of the subproject convention.

In my case I will potentially have multiple plugins that may be developed concurrently with projects that will depend on them independent of each build cycle. Thus, it makes sense to have an equivalent native task in Gradle that will install the artifact(s) into the Gradle cache very much akin to mvn install.

Comment by Davide Cavestro [ 26/Aug/11 ]

What if we had a way to resolve dependencies against usual libraries or projects using some sort of external mapping, letting different users configure that mapping for their runtime context/workspace. I gave a look at WorkspacePlugin, but maybe it's not the same thing.
BTW having this improvement at gradle level could even enable better IDE integration scenarios.
I quote here what Szczepan Faber said some time ago

...
we plan to make it easy to toggle binary dependency with a project dependency. Our main use case is to allow working on the subset of projects in a big multi-module project
...
Consider a large, hierarchical project consisting of 50+ modules. It should be easy to check out / work with / build only the selected subproject(s). This may mean the 'project dependencies' are satisfied via some repo instead of being a part of a multi-module build run.

Any chance to have it in roadmap for the near future? I guess people working on heavily structured projects could really benefit from this all the time.

Comment by Marius Kotsbak [ 05/Sep/11 ]

We also see this need. I hoped that Gradle would be better at this than Maven (which it looked like in the description of Gradle, but turned out to work only for multiprojects, which Maven does also support OK).

For us, extracting common code in common modules used by different product modules has backfired because of the need to change code in multiple modules (common module+product module) to implement new features (requiring with maven a lot of "mvn install" of SNAPSHOT objects to local repo).

Comment by Scott Stewart [ 13/Oct/11 ]

Want to echo this as well. We have a set of about 35 projects that we're trying to make easier to build. A primary requirement is the ability to build some sub-project without needing the entire X GB project structure. If project A depends on B, depends on C you should only need these 3 project directories to build A, not all 35 projects.

My first thought was like Philip's: Why not allow relative file paths to a dependent project's gradle file as an alternative to the virtual path relative to the base multiproject directory?

dependencies

{ compile project('file:../myOtherDev/libA') }

or
dependencies

{ compile project('file:../myOtherDev/libA/build.gradle') }
Comment by Hendy Irawan [ 19/Mar/13 ]

+1 for this!

Especially coupled with generation of Eclipse project classpath settings.

Comment by Davide Cavestro [ 19/Mar/13 ]

We have some additional modules (custom reports) built to be hot deployed into the main application (a web application).

A notation like

dependencies { compile project('file:../theWarMultiprj/libA') }

would be great for these modules, cause it could be easily integrated even in our scenario, where the war is produced merging contents from a gradle multiproject where in turn each project is backed by an eclipse project that somewhat mirrors the gradle dependencies (with some partial overrides just to reflect the war merging logic).

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 [ 08/Feb/17 ]

From what I understand from the the problem description we are talking about composite builds. Composite builds are not available in Gradle so I am going mark the issue as fixed. Some other aspects were raised. For those I'd like to ask you to raise a separate issue on GitHub.

Generated at Wed Jun 30 11:44:42 CDT 2021 using Jira 8.4.2#804003-sha1:d21414fc212e3af190e92c2d2ac41299b89402cf.