G'day
For Gradle 2.5, we'll be adding support for replacing external dependencies with project dependencies (and vice versa). This work has necessitated some pretty major changes to dependency resolution, which have now been incorporated into master.
Here's a short description of some of the changes, for your information.
1. Project dependencies can now appear at any node in the dependency graph
With the change to allow project and external dependencies to be substituted, it is now possible for a resolved configuration to transitively include a project via an external dependency.
It is now possible to have a graph like this:
:my-project -> "my.org:published-module:1.0" -> :my-other-project
Any code that assumes that an external module can only ever transitively depend on other external modules will break. This includes our IDE plugins, which currently fail to deal correctly with transitive project dependencies like this.
2. Any configuration that is a task input is resolved when adding the task to the execution graph
When "taskA" takes "configuration.compile" as an input, this implies that any build dependencies of "configuration.compile" become task dependencies of "taskA". If a configuration includes a project dependency, then this includes the tasks required to build that project dependency.
Previously, we were only looking at the directly declared dependencies of a configuration for any ProjectDependency instances. However, now that a ProjectDependency can appear anywhere in the dependency graph, it is necessary to resolve the configuration to detect all project dependencies transitively.
This means that more dependency resolution will be happening up-front, rather than just before a task is executed. This will likely delay the execution of the first task, and could result in slower builds in case of failure (because we are now resolving configurations for tasks that may never end up executing).
3. Configurations used as task input will be re-resolved if modified during execution
As per point 2), input configurations are now resolved early, when building the task graph. However, execution of one task can modify the dependencies of a configuration that is input to another task. An example is a task that looks up a tool version and adds that tool to the dependencies.
In the case that an already-resolved configuration is modified during task execution, that configuration will be resolved _again_ when used as a task input. Modifying a configuration in this way is now deprecated and we've added some extra checks to catch this case.
4. Artifact resolution is now (somewhat) separated from graph resolution.
The artifacts of a project can be modified during task execution. Instead of forcing a re-resolve when artifacts change (like we do for configuration _dependencies_ as noted in 3), we no defer the resolution of configuration artifacts until they are required for task execution.
This means that dependency resolution is now effectively split into 2 separate actions:
a) Resolve the configuration to a graph of module nodes. Conflict resolution happens at this stage.
b) Resolve the artifacts of each module node in the graph.
We've made efforts to minimise the additional state that is held in memory between resolving the dependency graph and resolving the artifacts, however it is possible that this will have an impact on heap allocation for very large builds. Optimisation and profiling will follow.