eastwater
eastwater

Reputation: 5598

Gradle show content of compile configuration of a sub project

Try to show a configuration content of sub project foo:

task foo {

    project(':foo').configurations.compile.files.each { 
        println it.name
    }

    project(':foo').configurations.compileClasspath.files.each { 
        println it.name
    }

}

Error

* What went wrong:
A problem occurred evaluating project ':foo'.
> Cannot change dependencies of configuration ':foo:compile' after it has been resolved.

I am confused with configurations.compile and configurations.compileClasspath. Are they the same? both are FileCollection?

For current project (not :foo), they contain the same set of files.

Upvotes: 1

Views: 1984

Answers (1)

Lukas Körfer
Lukas Körfer

Reputation: 14503

Your problem is based on the same technical detail as most of the Gradle problems at StackOverflow: the difference between the configuration phase and the execution phase.

If you define a task (like foo in your example), the code in the following closure does NOT define what the task will do on execution. Instead, it is executed right after the task definition and should be used to configure the task. This is why the code will be executed every time you run Gradle on the project (in the configuration phase). On the other hand, the task in your example will do absolutely nothing when its executed (in the execution phase). Only task actions (defined by the task type), doFirst and doLast closures are executed during execution phase. Also note, that (task) configuration will be done for every task, but only selected tasks (via command line or dependencies) will be executed.

Knowing the above, we can understand the problem in your code: It tries to print the contents of a configuration of a subproject during the configuration phase of the root project. The configuration phase of the subproject, where you add dependencies, comes after the configuration phase of the root project, where you resolve the configuration (which is necessary to show the contents). But you can't add dependencies to configurations that are already resolved.

This means, that your code gets executed, resolves the configuration and prints nothing, because at the moment of execution the configuration is empty. After that, in your subprojects dependencies closure, you are trying to add a dependency to the configuration that is already resolved, which causes the error. Comment out all (compile) dependencies in your subproject foo and the error should disappear.

However, this is not what you want. You want to list the files of the configuration after they have been added. To achieve this, you can easily move your task code to a doLast (or doFirst) closure:

task foo {
    doLast {
        project(':foo').configurations.compile.files.each { 
            println it.name
        }
    }
}

Now, your configuration is resolved in the execution phase of your root project, which comes after the configuration phase of the subproject, so there won't be any problems adding dependencies. Please note, that the task will only be executed if you specify it (e.g. calling gradle foo / gradle build foo or using dependsOn foo on another task which will be executed).

Regarding the difference between the compile and the compileClasspath configuration, you can take a look at the docs of the Gradle Java Plugin, which defines both configurations: Configurations can extend other configurations, which means that they will contain all dependencies the other configuration contains, but can also contain additional dependencies. If you only use compile dependencies, compile and compileClasspath will be the same, but e.g. a compileOnly dependency will only be listed under compileClasspath.

Upvotes: 3

Related Questions