How do I collect certain artifacts of all subprojects in a multi-project build?

I have a multi-project build, with mainly Java code. I use the “os-package” plugin to build RPM packages in some of the subprojects.

Now, the subprojects are all individually versioned, and I need to make a release archive whenever I build a release version of the whole project. In other words, I want a Gradle task that collects the outputs of all tasks of type Rpm or all tasks called buildRpm.

All the examples I’ve found online either use only jar artifacts, and so can use configurations, or are single-project solutions.

The naive solution is to put the below in the parent project. Doesn’t work, because not all child projects have the buildRpm task.

task dist(type: Zip) {
    from {subprojects.buildRpm}
}
$ gradlew dist
FAILURE: Build failed with an exception.
* Where:
Build file 'C:\workspace\temip\build.gradle' line: 35
* What went wrong:
Could not determine the dependencies of task ':dist'.
> Could not find property 'buildRpm' on project ':foo'.

I tried this fancier version but to be honest I’m not 100% sure what I’m doing here. Anyway, it seems not to find any inputs (or outputs?). (I think I have to refer to the plugin by class, since parent project doesn’t (and shouldn’t) load the plugin, so I can’t refer to it by name.)

task dist(type: Zip) {
    plugins.matching {it.class.name == "com.netflix.gradle.plugins.packaging.SystemPackagingPlugin"}.all { 
        into('rpm') {
            from { buildRpm}
        }
    }
}

$ gradlew dist
:dist UP-TO-DATE

Any help would be most welcome.

Iterate on your subprojects to detect if they contain the tasks of type Rpm

task dist(type: Zip) { t - >
subprojects.each {s ->
subprojects.tasks.withType(Rpm).each {t2 - >
t.from t2.output
}
}
}

Or something like that

Hi Francois,
Thanks for the suggestion!
Unfortunately it doesn’t work. Even if I add the right plugin to the root project, to have Rpm defined as a type, I get

No signature of method: java.util.ArrayList.withType() is applicable for argument types: (java.lang.Class) values: [class com.netflix.gradle.plugins.rpm.Rpm]
Possible solutions: asType(java.lang.Class), asType(java.lang.Class), with(groovy.lang.Closure)

task dist(type: Zip) { t ->
    subprojects.each {s ->
        subprojects.tasks.withType(Rpm).each {t2 ->
             t.from t2.output
        }
    }
}

I thought subprojects.tasks would return a TaskContainer, so I tried “subprojects.tasks.getByName(“buildRpm”).each”, but that gives me

> No signature of method: java.util.ArrayList.getByName() is applicable for argument types: (java.lang.String) values: [buildRpm]

Try the following

notice that the tasks is called on the s variable instead, which is of type Project.

Thanks Andres,
No luck now either. I just get
:dist UP-TO-DATE
I think I have an idea of why. Adding some printouts to the root project’s task:

task dist(type: Zip) { t -> 
    subprojects.each {s ->
        println "project " + s.name
        s.tasks.withType(Rpm).each {t2 ->
            println " task has outputs:" + t.getName()
            t.from t2.output
        }
    }
}

And one printout to the child project:

buildRpm {
    println "configuring buildRpm"
    dependsOn build
}

They are obviously configured in this order, so perhaps the buildRpm does not yet exist on the child project when trying to collect its outputs?

$ gradlew dist
project utility
project adapter
configuring buildRpm
:dist UP-TO-DATE

BUILD SUCCESSFUL

try instead of using ‘subprojects.each’ use ‘subprojects.all’

René Gröschke
@breskeby

Nope. I didn’t even know “subprojects” was a TreeSet, but there you go. Doesn’t seem to have “all”.

No signature of method: java.util.TreeSet.all() is applicable for argument types: (build_dvf3gi1t92weg4b4j2twaad82$_run_closure3_closure6) values: [build_dvf3gi1t92weg4b4j2twaad82$_run_closure3_closure6@55e3dc28]
Possible solutions: any(), last(), last(), add(java.lang.Object), add(java.lang.Object), any(groovy.lang.Closure)