Hi,

One question that we need to answer to make progress on the publication DSL is 
how should change our dependency notation, so that we can:

1. Substitute a reference to a locally built thing with something downloaded 
from a repository [1]. For example, when I partially check out a source tree, I 
want to build the things for which there is source, and download the things for 
which there is no source, without changing the build scripts.

2. Substitute a reference to a thing in a repository with something built 
locally. For example, when I'm working on multiple source trees, I want to 
build a local version of some things that would otherwise be downloaded, 
without changing the build scripts.

3. Skip the configuration of projects which are not required to build the local 
things we need. For example, when I'm working on source tree with many 
projects, I want Gradle to configure only those projects that are relevant to 
whatever I'm doing.

4. Distinguish between the various things built by a project. For example, I 
have a project that builds a web app and a java library, and in some places I 
want to refer to the java library and in other places I want to refer to the 
web application.

[1] "Downloaded from a repository" really means "fetched from somewhere else". 
It may or may not be downloaded and it may or may not come from a repository.

Just as some background, the basic model we want to aim for is this:

1. Projects produce components.
2. A component is some logical thing that I can use, such as a java library or 
web application.
3. Dependencies are criteria that select one or more components.

There's more detail here: 
https://github.com/gradle/gradle/blob/master/design-docs/dependency-model.md

A few options (not all good options - just brainstorming here):

1. References to locally built things and things in a repository share the 
existing namespace we use for things in a repository. That is, every dependency 
declaration would use (group, component, version), including project 
dependencies:

dependencies {
    compile 'my.org:some-repo-thing:1.2'
    compile 'my.org:some-other-project:1.4'  // A project dependency
}

The obvious downside here is that version is required for a project dependency, 
but version is almost always a calculated value. Another downside is that 
projects already have a convenient identifier in their path, whereas this 
approach would require a project to define an additional identifier.

2. References to locally built things and things in a repository share a new 
namespace. Every dependency declaration would use a single identifier, and 
there would be a separate mapping, shared by all the projects, from identifier 
to (group, module, version) or project.

The idea here is that we also deal with the 'dependencyManagement' use case as 
well, so that you define (group, module, version) in one place and use a simple 
name everywhere that you need to refer to the dependency. This is, in practise 
what larger builds do already, so it's not necessarily more 'complicated'.

allprojects {
    modules {
        someRepoThing 'my.org:some-repo-thing:1.2'
    }
}

dependencies {
    compile someRepoThing
    runtime someLocalThing  // Probably some default rule to look for a locally 
built component called 'someLocalThing'
}

The obvious downside here is that you need to extra work if the dependency is 
not reused. Plus it means every dependency declaration in every Gradle build 
script in existence would need to change.

3. A combination of the above, so that all dependency declarations refer to 
things using (group, component, version), but group and version are optional. 
If not specified, we look for a mapping from component name to (group, 
component, version) or (project, component). We'd have some place where you can 
register these mapping to things in repositories, and probably also have a 
default mapping where we look for a locally built component with the given name 
and same group as the referring project.

allprojects {
    dependencies.modules {
        junit "junit:junit:4.11"
    }
}

dependencies {
    compile "com.google.guava:guava:13"
    testCompile "junit" // or perhaps ":junit:" - maps to "junit:junit:4.11"
    testCompile "junit:4.8.2" // maps to "junit:junit:4.8.2"
    runtime "some-local-thing" // default rule maps to component 
'some-local-thing' built by another project
}

For me, this one is almost all upsides. It gives me a unified way to refer to 
things, using (if I like) a simple name, and a place to define meta-data about 
external things in a single place (such as, every dependency to 'junit' should 
use 'junit:junit:4.11' unless otherwise specified, or "my-lib" uses semantic 
versioning, or "groovy-all" conflicts with "groovy", etc). It also gives me a 
point where I can put logic in to influence whether a given dependency 
declaration is mapped to a locally built thing or a thing in a repository.

One major downside is that every project dependency in the world would need to 
be changed, because project dependencies would go away (via a long period of 
deprecation).

4. Keep things as they are, but remove (via deprecation) the ability to 
navigate to the target project via a ProjectDependency. We would offer a way to 
ask Gradle to substitute a project dependency with an external dependency and 
vice versa.

Major downside for me is that it doesn't push the move to components far enough.

One not-so-obvious advantage of this approach is that it is very easy to 
implement configure-projects-on-demand, whereas the other approaches would 
require some kind of caching.


Thoughts? Other options?


--
Adam Murdoch
Gradle Co-founder
http://www.gradle.org
VP of Engineering, Gradleware Inc. - Gradle Training, Support, Consulting
http://www.gradleware.com

Reply via email to