How do I easily print commands being run by Exec tasks?

Debugging Exec tasks is painful if you don’t know the command line being executed. But the only way I have found to see the command line is to run “gradle --debug”, which swamps the information you need in a sea of uninteresting debug output.

So is there an easy way to have Gradle print the command being executed just before it runs? I want roughly the same behaviour as Make or SCons – nothing wrong with telling me the name of the task being executed, but I need to know the command line.

I started with http://gradle.org/docs/current/userguide/logging.html and implemented this little init.gradle file:

useLogger(new CustomEventLogger())
  class CustomEventLogger extends BuildAdapter implements TaskExecutionListener {
      public void beforeExecute(Task task) {
        if (task instanceof Exec) {
            //println "[$task.name: ${task.commandLine.join(' ')}]";
            println "[$task.name: ${task.commandLine}]";
        }
        else {
            println "[$task.name]";
        }
    }
      public void afterExecute(Task task, TaskState state) {
        println()
    }
      public void buildFinished(BuildResult result) {
        println 'build completed'
    }
}

That’s pretty close to what I want, except it prints the command line for every Exec task, regardless of whether it is run or not in a particular build. I only want to see commands that are actually executed in this build.

Are you sure? beforeExecute is supposed to only get called for tasks that are about to be executed.

Another way is to add an action to all exec tasks. This could be done from a build script or a build listener in init.gradle and would roughly look like this:

allprojects {
  tasks.withType(Exec) {
    doFirst {
      println commandLine
    }
  }
}
1 Like

Are you sure? beforeExecute is supposed to only get called for tasks that are about to be executed.

Yes, quite sure. I just tried this again in a different project with a completely different build script, and I’m getting the same result: TaskExecutionListener.beforeExecute() is being called for every task, regardless of whether they need to be run or not.

Demonstration. First, here is my build output without any init script:

$ gradle all
               :pylib UP-TO-DATE
:lbe UP-TO-DATE
:compileCommonJava UP-TO-DATE
:notif UP-TO-DATE
:pacsversions UP-TO-DATE
:compilePolicyJava UP-TO-DATE
:compileCoredbJava UP-TO-DATE
:processCoredbResources UP-TO-DATE
:coredbClasses UP-TO-DATE
:all UP-TO-DATE
  BUILD SUCCESSFUL
  Total time: 6.411 secs

Now here is my dead simple init script:

useLogger(new CustomEventLogger())
  class CustomEventLogger extends BuildAdapter implements TaskExecutionListener {
      public void beforeExecute(Task task) {
        println "beforeExecute(): " + task
    }
      public void afterExecute(Task task, TaskState state) {
    }
      public void buildFinished(BuildResult result) {
        println 'build completed'
    }
}

And here is my build output with that init script:

$ gradle all -I init.gradle
beforeExecute(): task ':pylib'
beforeExecute(): task ':lbe'
beforeExecute(): task ':compileCommonJava'
beforeExecute(): task ':notif'
beforeExecute(): task ':pacsversions'
beforeExecute(): task ':compilePolicyJava'
beforeExecute(): task ':compileCoredbJava'
beforeExecute(): task ':processCoredbResources'
beforeExecute(): task ':coredbClasses'
beforeExecute(): task ':all'
build completed
1 Like

A task that reports ‘up-to-date’ is being executed. You can additionally check ‘task.didWork’, but only after the task has executed.

A task that reports up-to-date is being executed. You can additionally check task.didWork, but only after the task has executed.

Darn. All I want is the feature that Make has had for 30+ years.

Your second suggestion, of tacking a doFirst() onto every task, works fine. (Of course it doesn’t apply to Java compilation tasks, but I presume that is because Gradle isn’t actually spawning javac in a separate process, so there is no command line to print.)

If my suggestion works, isn’t that case solved? You can’t have this feature for all tasks because not all tasks fork a new process (actually most don’t), hence there is no command line to print.