How to clean the top level project?

(Steve Cohen) #1

I have written some logic in my multiproject build that copies some build artifacts to the top level project’s build directory, which, normally would not exist.

This works, but I’ve just learned that running gradle clean does not clean them out. How can I make it do so?

(davidmichaelkarr) #2

Irrespective of any particular solution to this, I think it’s generally better to structure builds so that you don’t need to do this. If another project needs the artifacts from another project, that project should reference that as a dependency. It might be best to fully describe what you generally need to do, so someone could advise on how and whether to transform the build to not require copying artifacts this way.

(Steve Cohen) #3

What I’m trying to do here, David, is emulate an existing process, in which the build for a particular project builds a bunch of RPMs, bundles them into a tar file for convenience and also puts the rpms AND the tar file, for some historical reason unknown to me, in the top level directory. Our current automated build does this and it’s a convention for our team. I suppose it’s easier for people to find them this way. There might be other suggestions that are as good, but right now, we haven’t done anything on automated gradle building.

(Steve Cohen) #4

So, I’d like to get some answer for this. I hear what David says about avoiding what I’m trying to do, but I’m also listening to existing practices.

I would like to emulate these by maintaining a central directory containing the output of our (not-yet-existing) automated build. Current convention puts the output in the top level directory of each project, without developers needing to dig through subprojects’ build/distributions or whatever.

If Gradle allows a subproject to write files to the buildDir of the top level project (and it does), shouldn’t there be some way to clean this directory?

(Stefan Oehme) #5

Hi Steve,

I think there are two reasons why you don’t have an answer yet:

  • Your replies to people that are trying to help you have a passive-aggressive tone. I’m sure this is not your intention, but just imagine yourself receiving a reply like this:
clean {
  delete "someFileOrFolder"


(davidmichaelkarr) #6

Just so it’s clear, I understand what you’re saying, but I didn’t even detect any issue with his tone, and his comment was addressed to me. Perhaps there’s a cultural difference here. If we were all from New York (not that I am), perhaps this would be all hunky-dory. :slight_smile:

(Steve Cohen) #7

Wow! I meant no disrespect whatsoever to David. We are actually employees of the same corporation and chat occasionally on our internal instant messaging system. I was just trying to explain myself, why I was feeling the need to override what probably is best practice, that there is sometimes a tradeoff between optimality with established custom. Referring to him by name might appear condescending to you, and I wouldn’t usually do it, but since I actually know David (electronically anyway), it didn’t seem that way to me. That could not have been clear to you and I understand why it might have seemed to you the way it obviously did.

As for your link, thanks. It helps but raises a further question: In a multi-project build, where would you put this logic? I am guessing that the place to put it would be in the same project that copies the file into that directory. Yes?

I had done some research but probably did not google this. I think I looked instead through the User’s Guide.

(Stefan Oehme) #8

That’s right, the project responsible for creating some output should also be responsible for cleaning it up.

(Steve Cohen) #9

Ok. I’ve been on vacation and am only now getting back to this issue. I tried the approach mentioned in the link that Stefan originally provided and it does work; however, it produces a deprecation warning message:

Defining custom ‘clean’ task when using the standard Gradle lifecycle plugins has been deprecated and is scheduled to be removed in Gradle 3.0

So this does not seem like a stable solution. Is there another way?

(Stefan Oehme) #10

You probably wrote

task clean {

Which defines a new clean task. You should just do

clean {

(Steve Cohen) #11

Not quite. I’m doing this in a plugin. Here’s the way I implemented it:

        BundlerTask bundleTask = project.tasks.create("bundle", BundlerTask.class)
        project.afterEvaluate {
            if (project.parent != null ) {
                project.task('clean').each { cl->
                    cl.configure {


(Steve Cohen) #12

Since my implementation doesn’t work (or works but with a deprecation threat that it won’t work forever) I am looking into this strategy:

require plugins to extend the default tasks by adding their tasks as a
dependency. So instead of the java plugin defining it’s own build task,
it would have a task buildJava that is added to the build workflow
through a call to build.dependsOn “buildJava”. Likewise if I have a
python plugin that operates on the same project, it would add it’s tasks
to the default workflow with a call to build.dependsOn “buildPython”.
Now both my python and java plugins can exist side by side, and
furthermore I can add dependencies between the plugins and say something
like buildPython.dependsOn “buildJava”.

So following this model, I would have to create my own extraClean task in my plugin and make the project’s clean task depend on it. But I haven’t created a Task class in a plugin yet that wasn’t derived from a concrete class (such as rpm, tar, java, etc). Mine would be a very simple task that would just perform one delete action. Since AbstractTask is not in the public API, what Gradle API class should such a task class extend?

(Steve Cohen) #13

OK, I realized I didn’t need to create a Task-derived class at all. I could just use a Delete to do my work. So I created the following:

import org.gradle.api.Project;
import org.gradle.api.tasks.Delete

class RootDirCleanTask {
    static Delete create(Project project, String archive) {
        Delete rootDirCleanTask = project.tasks.create("rootDirClean", Delete.class)
        project.task('clean').each {

Then call this logic from my plugin:

        project.afterEvaluate {
            if (project.parent != null ) {
                RootDirCleanTask.create(project, "$project.parent.buildDir/$bundleTask.archiveName")

Again, it works, but again, I get the warning.

$ gw clean
:buildSrc:compileJava UP-TO-DATE
:buildSrc:compileGroovy UP-TO-DATE
:buildSrc:processResources UP-TO-DATE
:buildSrc:classes UP-TO-DATE
:buildSrc:jar UP-TO-DATE
:buildSrc:assemble UP-TO-DATE
:buildSrc:compileTestJava UP-TO-DATE
:buildSrc:compileTestGroovy UP-TO-DATE
:buildSrc:processTestResources UP-TO-DATE
:buildSrc:testClasses UP-TO-DATE
:buildSrc:test UP-TO-DATE
:buildSrc:check UP-TO-DATE
:buildSrc:build UP-TO-DATE
Defining custom 'clean' task when using the standard Gradle lifecycle plugins has been deprecated and is scheduled to be removed in Gradle 3.0

:bundle:rootDirClean UP-TO-DATE
:bundle:clean UP-TO-DATE

It appears to be the case that any reference to the lifecycle clean task will create this warning, even when I am NOT defining a custom clean task. In my latest iteration, I am simply making the lifecycle task depend on mine as indicated in the strategy I am following.

It would appear that this strategy has the same flaw as every other strategy I’ve tried. Whether I am adding configuration to the ‘clean’ task or making the ‘clean’ task depend some other task of my own devising, it is a modification of the lifecycle task and this warning message is displayed. Is it even correct? Can I ignore it?

(Stefan Oehme) #14

You are creating a clean task, don’t do this.

What you are looking for is project.tasks.getByName('clean')

(Steve Cohen) #15


Guess my mind was still on vacation. :slight_smile: