Task does not execute another task based on the configuration it uses

I’ve got a very simple multiproject build like below:

module1, which generates a public API jar and exposes it through “publicAPI” configuration:

configurations {

publicAPI

}

task generatePublicAPI(type: Jar) {

outputs.upToDateWhen { false }

baseName ‘public-api’

from sourceSets.main.output

}

artifacts {

publicAPI generatePublicAPI

}

module2, which uses the public API jar (by referencing ‘publicAPI’ configuration defined in module1) to generate a application jar:

configurations {

generateApplication

}

dependencies {

generateApplication project(path: ‘:module1’, configuration: ‘publicAPI’)

}

task jarApp(type: Jar) {

baseName ‘app’

from configurations.generateApplication.collect {

it.isDirectory() ? it : zipTree(it)

}

}

Now, when I execute 'gradle :module2:jarApp" task, I got the following error:

Cannot expand ZIP > ‘/home/picasso/Documents/GradlePlayground/module1/build/libs/public-api.jar’ > as it does not exist

and I can see that gradle was not trying to execute ‘generatePublicAPI’ of module1.

However, if I make “jarApp” task depends on “generatePublicAPI” task explicitly,

task jarApp(dependsOn: ‘module1:generatePublicAPI’, type: Jar) {…}

then everything’s fine.

BUT, wouldn’t this approach against one of the purpose of using dependency configuration so that I don’t have to worry about the details of how module1 is built, e.g. which task generates the jar and what artifacts it produces?

I thought gradle is able to work out the tasks it needs to execute by following along the “route” of the referenced dependency configuration. Also tried to add ‘evaluationDependsOn(’:module1’)’ to module2 but it didn’t solve the problem.

Am I missing something here so that “generatePublicAPI” task can be executed automatically without have to declare “dependsOn” explicitly for “createApp” task?

Unfortunately because you are using the ‘collect()’ method to actually return a new collection, Gradle is unable to infer the task dependencies of that new collection. This is typically the case when using the built-in Groovy methods that return a new collection.

To solve this issue, you’ll have to explicitly define a dependency to the configuration itself. Gradle will then be able to determine what tasks are required to be executed to resolve that configuration.

task jarApp(type: Jar, dependsOn: configurations.generateApplication)

Thanks Mark! That solves the problem!

I never realised that a task can depend on a configuration. Just had another look at the API doc, “Configuration” is a subclass of “Buildable”, which is an eligible type for dependency.

Make the task depend on a configuration instead of a task from another project sounds like a much better approach to me.

I’m pretty much learning gradle on my own at the moment so I’m wondering if this is the better way of managing dependencies in comparsion of declaring dependencies on tasks, especially when those tasks belongs to another project?

In general it’s best practice to use task dependencies when defining dependencies within a project, and configurations and project dependencies when defining dependencies across projects. Simply, it’s generally not recommended to depend directly on a task from another project.