Hi,

We're about to start a long overdue reworking of the Gradle dependency DSL. By 
'dependency DSL', I mean the DSL for declaring and working with project 
dependencies, and declaring the outgoing publications of a project. It also 
covers declaring artifact repositories.

There are a number of problems with the current DSL, which we want to fix 
before we do a Gradle 1.0 release. I won't go into details here, except to 
describe the first major problem we want to tackle.

The problem is a conceptual one, but a fundamental and important one: 
Currently, you declare the dependencies of a project by grouping them into so 
called 'configurations'. You also declare the outgoing publications of a 
project by attaching them to configurations.

There are a few things wrong with this model.

Firstly, the name 'configuration' is meaningless - it doesn't tell you anything 
about what the purpose or meaning of the thing is.

Secondly, this model couples the incoming and outgoing view of the project too 
tightly. The reality is that the incoming dependencies of a project are not 
always the same thing as the outgoing dependencies. For example, if a project 
publishes a jar with some dependencies bundled in the jar (eg groovy-all.jar), 
then the published runtime dependencies are different to the runtime 
dependencies used at, say, test time. This becomes even more of a problem when 
a project publishes multiple jars, each with a different runtime classpath.

So, the plan is to split what is currently the Configuration interface into 2 
pieces:

* Something that represents a set of dependencies. These would be used to 
declare the incoming dependencies of the project. Not sure about name for this 
thing. Perhaps a 'dependency set'.

* Something that represents a set of published artifacts and their 
dependencies. These would be used to declare the outgoing publications of the 
project. This could be called a 'publication'.

The Configuration type and the configurations { } script block would be 
removed. The DSL for declaring dependencies would stay pretty much the same as 
it is now:

repositories { ... }

dependencies {
    compile 'some:dep:1.0'
}

You will be able to declare custom dependency sets, similar to declaring custom 
configurations now:

dependencies {
    custom { transitive = false; description = 'some configuration' }
}

To resolve the dependencies, you'd do the same sort of thing you currently do 
with a configuration:

copy { from dependencies.runtime; into 'libs' }

We might keep 'configurations' as a deprecated alias to 'dependencies', so you 
will be able to do the following, while migrating to the new DSL:

copy { from configurations.runtime; into 'libs' }

From a model point of view, a DependencySet will have:
* a name
* a mutable collection of dependency instances
* a mutable collection of exclude rules
* a transitive flag
* some way to resolve the dependencies, either as a set of Files or as a set of 
dependency meta-data.

Over time, we might further split this into a pure 'set of dependencies' 
abstraction, and some kind of 'resolvable' abstraction.

Project.dependencies will be a container of these DependencySet instances.

So far, this is very similar to what we have already.

From a model point of view, a Publication will have:
* a name
* a mutable collection of artifact publication instances
* a set of DependencySet instances
* some way to resolve the publication, as a set of Files or as a set of 
dependency meta-data.

Project.publications will be a container of these Publication instances.

By default, a project will have no publications. The java plugin would add a 
default publication, containing the jar file and the contents of 
dependencies.runtime as its runtime dependencies. You will be able to modify 
this default publication, to change the set of artifacts or runtime 
dependencies, or to add additional dependency sets.

You will also be able to add additional publications, configured however you 
like.

The DSL might look something like:

publications {
    main {
        // add artifacts to the default publication
        artifact srcJar
        artifact 'some/file'
        // change the published dependencies
        dependencies {
            runtime 'some:extra-dep:1.2'
        }
    }
    // publish a separate fat jar
    withDeps {
        artifact fatJar
        dependencies { runtime = [] }
    }
}

This will replace the artifacts { } script block.

By default, a project dependency will translate into a dependency on the 'main' 
publication. Initially, this will resolve to the artifacts of the publication, 
plus all the runtime dependencies of the publication. Later, we plan to change 
this so that it resolves to the 'api' dependencies at compile time, and the 
'runtime' dependencies at runtime.

Each publication will be separately 'uploadable' to a repository. The 
'upload<Config>' tasks will be replaced with 'publish<Publication>' tasks. 
Initially, each Publish task instance will have a separate collection of 
repositories attached to it, as Upload currently does. Later, we will look at 
moving these off the task and into the model.

When publishing to an ivy repository, a publication is mapped to an ivy module 
as follows:
* organisation is project.group
* module is project.name
* revision is project.version
* status is project.status
* each of the publication's dependency sets are mapped to an ivy configuration
* each of the publication's artifacts are mapped to an ivy artifact

Later, we will allow some customisation of this mapping, in particular:
* configurable ivy organisation, module and revision.
* some way to attach an artifact to particular ivy configurations.

We'll probably move this mapping to an 'ivy' plugin, which you will have to 
explicitly apply to your project if you want to publish it as an ivy module.

When publishing to a maven repository, a publication is mapped to a maven 
module as follows:
* groupId is project.group
* artifactId is project.name
* version is project.version
* dependency set 'runtime' maps to 'runtime' scope, 'api' to 'compile' scope, 
and 'provided' to 'provided' scope.
* one of the publication's artifacts is mapped to the main artifact, the others 
to classifier artifacts.

Again, we will allow some customisation of this mapping. And also, this mapping 
will live in the 'maven' plugin, which you will have to explicitly apply if you 
want to publish it as a maven module.

This is the first in a series of steps towards improving the DSL. I think the 
next step is to make use of the publication concept to simplify how you publish 
to a maven repository. More on this later.


--
Adam Murdoch
Gradle Developer
http://www.gradle.org
CTO, Gradle Inc. - Gradle Training, Support, Consulting
http://www.gradle.biz

Reply via email to