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.
--
Adam Murdoch
Gradle Developer
http://www.gradle.org
CTO, Gradle Inc. - Gradle Training, Support, Consulting
http://www.gradle.biz
---------------------------------------------------------------------
To unsubscribe from this list, please visit:
http://xircles.codehaus.org/manage_email