doFirst() does not execute first, but only after task execution


(jai) #1

I am in the process of converting a plugin from Groovy to Java.
I have a Groovy task:

task dofirsttest(type: Exec) {
    doLast{
        println "Should be last"
    }
    doFirst{
        println "Should be first"
    }
    commandLine "echo", "Should be between"
}

that outputs

Should be first
Should be between
Should be last

I converted the task to Java:

class TestTask extends Exec {

    public TestTask() {
        
        doLast(new Action<Object>(){
            @Override
            public void execute(Object task) {
                System.out.println("Should be last ");
            }
        });

        doFirst(new Action<Object>(){
            @Override
            public void execute(Object task) {
                System.out.println("Should be first ");
            }
        });

        commandLine("echo", "Should be between");
    }
}

but this outputs

Should be between
Should be first
Should be last

The doFirst() gets executed only after task execution. Why does the Java task behave differently?
Thanks!


(Chris Doré) #2

This used to be a reported issue, but it didn’t survive the move to github issues.
https://issues.gradle.org/browse/GRADLE-2064


(jai) #3

Thanks for pointing this out. What is the recommended way to implement doFirst actions in subclasses? The proposed workaround seems very unintuitive to me.


(jai) #4

I just tried the workaround and it doesn’t seem to work anymore. Unless I did something wrong of course.

class TestTask extends Exec {

    public TestTask() {

        Task currentTask = this;
        TaskCollection<Task> workaround= getProject().getTasks().matching(new Spec<Task>() {
            @Override
            public boolean isSatisfiedBy(Task t){
                System.out.println(t.toString() +  " == " + currentTask.toString()); //current task does not appear here
                return t == currentTask;
            }
        });
        System.out.println(workaround.size()); // is 0
        for(Task t : workaround) {
            t.doFirst(new Action<Object>(){
                @Override
                public void execute(Object task) {
                    System.out.println("Should be first ");
                }
            });
        }
        commandLine("echo", "Should be between");
    }
}

I cannot get the currently constructing task from getTasks(), because it is not yet in the list of tasks.


(Garrett L. Ward) #5

Something like this might work:

class TestTask extends Exec {
    public TestTask() {
        commandLine("echo", "Should be between");
    }

    @Override
    @TaskAction
    protected void exec() {
        System.out.println("First");
        super.exec();
        System.out.println("Last");
    }
}

(Chris Doré) #6

If your new task type does not need to expose the Exec API to the users of the task, it may be better to use composition rather than inheritance. Project.exec can be used to execute something with the same type of configuration as an Exec task.

public class TestTask extends DefaultTask {
    @TaskAction
    public void doWork() {
        System.out.println( "First" );
        getProject().exec( new Action<ExecSpec>() {
            @Override
            public void execute( ExecSpec execSpec ) {
                execSpec.commandLine( "echo", "Should be between" );
            }
        } );
        System.out.println( "Last" );
    }
}

(jai) #7

Thank you for your replies. My original intent was to make sure a directory existed before executing a command in it. So far I solved it by splitting it into two tasks and making the Exec task depend on the dir creating one, but I may switch it back to use only one task.