Migration from Gradle 4.4 to 4.5 breaks packaging referencing other submodules artifacts

Doing a long due upgrade of Gradle on a project (started from 3.5 aiming at latest Gradle version) I’m stuck at Gradle 4.5.

The project is a multimodule one.
Several modules produces artifacts (war or zip).
One submodule packages it all.

On Gradle 4.4 packaging is fine.

gw ev-rpm:buildRpm -m

Parallel execution is an incubating feature.
:EverestSchema:zip SKIPPED
...
:everest-bcs:war SKIPPED
:everest-rpm:buildRpm SKIPPED

BUILD SUCCESSFUL in 7s

On Gradle 4.5.1 configuration fails.

gw ev-rpm:buildRpm -m

* What went wrong:
A problem occurred evaluating project ':everest-rpm'.
> Task with name 'zip' not found in project ':EverestSchema'.

everest-rpm is the packaging submodule
EverestSchema is one submodule producing a zip file using a Zip task called zip.

everest-rpm/build.gradle (partial)

task buildRpm(type: Rpm) {

    dependsOn ':EverestSchema:zip'
    into('/appli/EverestSchema') {
        from zipTree(project(':EverestSchema').tasks['zip'].archivePath)
        fileMode 0755
    }
}

EverestSchema/buil.gradle (partial)

task zip(type: Zip) {
    baseName = project.name
    destinationDir = project.buildDir
...
}

I could not locate any change in the release note related to tasks references/dependencies.
Feels like some configuration steps on the referenced submodule are not done yet when trying to configure the failing task.

Could not found any documentation about this kind of packaging tasks for how reference or dependency could have changed.

Pleas epoint me to a working sample or to the related documentation.

This issue is that this build relies on the project configuration ordering. The order in which projects are configured can change unless you specify an evaluation dependency.

However, there’s another/better solution available to you.

The EverestSchema build should declare the Zip it creates as an artifact of the build:

configurations {
    schema
}
artifacts {
    schema tasks.zip
}

The everest-rpm project declares a dependency on that subproject and consumes it:

configurations {
    schema // names do not need to match between the projects
}
dependencies {
    schema project( path: ':EverestSchema', configuration: 'schema' )
}
// Avoid resolving the configuration during configuration by configuring it during task execution.
// If we didn't need to unzip the files and just wanted the zips included directly in the rpm,
// then you wouldn't need this task and the buildRpm task could be configured simply with
// from(configurations.schema), and the inputs.files call would be moved to buildRpm to maintain
// the task dependencies.
task configRpm {
    inputs.files( configurations.schema ) // infers a tasks dependency on EverestSchema project through the dependency relationship
    doLast {
        tasks.buildRpm.into( '/appli/EverestSchema' ) {
            fileMode 0755
            configurations.schema.each { schemaZip ->
                from( zipTree( schemaZip ) )
            }
        }
    }
}
task buildRpm( type: Rpm )

I didn’t test this, so I might have made a typo.

2 Likes

Thank you a lot for the tip about the appropriate solution.
I gave it a try on the first dependency and it solves my troubles.

As a matter of fact, several plugins already expose their artifact using a configuration named archives.
I’ll used that name to be dev-friendly.

Another detail, I used collect instead of doLast on the configuration to refer to the artifact after every projects are configured. But this is just for readability.

into('/appli/EverestSchema') {
  from { project.configurations.schemaZip.collect { zipTree(it) } }
}

Depending on the plugin, they may also have the default configuration setup to provide the artifacts. If that’s the case, then you can use the project(':EverestSchema') syntax instead of project(path: ':EverestSchema', configuration: 'xyz'). For example, the ‘java’ plugin has default extending from the runtime configuration, which includes the output of the jar task. The Java Plugin

I should also point out that if the :EverestSchema project declares dependencies on its schema configuration, then those would be transitively included in :everest-rpm’s dependencies.