Yu Lu
Yu Lu

Reputation: 83

Grade: Can't access configuration defined in one project from another project

I'm pretty new to both gradle and groovy.

The Problem

I've got a very simple multi-project structure just like below:

Root project 'gradle_test'
+--- Project ':sub1'
\--- Project ':sub2'

This is what the 'build.grade' file looks like for sub1 project:

// build.gradle of sub1 project
task testConfiguration {
    println project(':sub2').configurations.sub2FooConfiguration
}

And finally, this is the 'build.grade' file of sub2 project:

// build.gradle of sub2 project
configurations {
    sub2FooConfiguration
}

Very minimum. Now, if I run gradle :sub1:testConfiguration, I got the following error:

A problem occurred evaluating project ':sub1'.
> Could not find property 'sub2FooConfiguration' on configuration container.

However, everything just works if the testConfiguration task in sub1 project is modified like this:

// notice the "<<" (I believe this is calling the 'doLast' method on the task instance)
task testConfiguration << {
    println project(':sub2').configurations.sub2FooConfiguration
}

The Question

What I assume the difference between the two versions of the 'testConfiguration' task is that in the first instance, a configuration closure is passed to the task whereas in the modified version a 'normal' closure is passed to the 'doLast' method.

So, first of all, is my assumption correct?

Secondly, why I don't have access to 'sub2' project in the first instance?

And finally, is it possible to access 'sub2' project in the first instance (i.e. in the configuration closure)?

[Update] A Further Question

Given the accepted answer provided by "Invisible Arrow", I'd like to ask a further question regarding the best practice of referencing a configuration of another project (i.e. a task in sub1 needs to use an archive produced by sub2 project).

Should I declare evaluation dependency between the two projects?

Or Should I only reference sub2's configuration at execution time (e.g. in doLast())?

Or, should I create a dependency configuration between the two projects?

Upvotes: 3

Views: 186

Answers (1)

Invisible Arrow
Invisible Arrow

Reputation: 4905

Yes, there is a difference between the two. There are essentially 3 phases to a build which are Initialization, Configuration and Execution. This is described in detail under the Build Lifecycle chapter in the Gradle documentation.

In your case, the first instance falls under the Configuration phase, which is always evaluated irrespective of whether the task is executed or not. That means all statements within the closure are executed when you start a build.

task testConfiguration {
    // This always runs during a build,
    // irrespective of whether the task is executed or not
    println project(':sub2').configurations.sub2FooConfiguration
}

The second instance falls under the Execution phase. Note that << is a shorthand for doLast, and this closure is called when the task is executed.

task testConfiguration << {
    // Called during actual execution of the task,
    // and called only if the task was scheduled to be executed.
    // Note that Configuration phase for both projects are complete at this point,
    // which is why :sub1 is able to access :sub2's configurations.sub2FooConfiguration
    println project(':sub2').configurations.sub2FooConfiguration
}

Now coming to why the first instance gave the error. This was because the Configuration phase of sub2 project was not evaluated yet. Hence the sub2FooConfiguration wasn't yet created.

Why? Because there is no explicit evaluation dependency between sub1 and sub2. In your case, sub1 needs sub2 as an evaluation dependency, hence we can add that dependency in sub1's build.gradle before the task declaration, as follows:

evaluationDependsOn(':sub2')
task testConfiguration {
    println project(':sub2').configurations.sub2FooConfiguration
}

This will ensure that sub2 is always evaluated before sub1 (evaluation meaning the Configuration phase for the projects). sub1 will now be able to access configurations.sub2FooConfiguration in the task declaration closure. This is explained in detail in the Multi-project Builds chapter.

In the second instance, configurations.sub2FooConfiguration was accessible, as the call was in the execution block of the task (which is after the Configuration phase for both projects).

PS: Note that if you reversed the names of the projects, then the first instance might actually work, as Gradle configures projects alphabetically if there are no explicit dependencies. But, of course, you should never rely on this and ensure that the dependencies between projects are declared explicitly.

Upvotes: 3

Related Questions