Exec task failing to perform process, no error

Gradle Version: 3.1 (and earlier)
Operating System and JVM version:
Red Hat Enterprise Linux Server release 6.7 (Santiago)

java version "1.8.0_77"
Java™ SE Runtime Environment (build 1.8.0_77-b03)
Java HotSpot™ 64-Bit Server VM (build 25.77-b03, mixed mode)

Is this a regression? If yes, which version of Gradle do you know it last worked for?
No

NOTE: I have updated this post. The previous version was based on a run where the code was not as stated below.

I have defined a simple exec task. It deletes everything in a directory that is outside of the build tree.

        Task nativeClean = project.tasks.create('nativeClean', Exec) {
            // does not depend on anything, must be manually invoked.
            workingDir nativeDir
            commandLine 'rm', '-rfv', "*"
        }    

If I then run the task

$ gradlew nativeClean -i

I see that the task does execute but the contents of nativeDir remain undeleted:

Starting process 'command 'rm''. Working directory: /usr/local/openssl Command: rm -rfv *
Successfully started process 'command 'rm''
:openssl64:nativeClean (Thread[Daemon worker Thread 18,5,main]) completed. Took 0.02 secs.

If it were some sort of permissions error, say, I would expect an exception to be thrown. But I tried forcing that by changing the permissions, and the results were the same.

I think the process is starting but never actually executing. It only says it’s started the process. It’s as if gradle isn’t waiting for the process to complete.

Okay, I don’t know what to try next. It certainly seems like Gradle thinks it’s performing the rm but nothing is happening. Nothing I try is giving me any visibility into this situation.

I’ve rewritten the task to provide as much info as possible:

        Task nativeClean = project.tasks.create('nativeClean', Exec) {
            // does not depend on anything, must be manually invoked.
            workingDir nativeDir
            commandLine 'rm', '-rfv', "*"
            standardOutput = new ByteArrayOutputStream()
            errorOutput = new ByteArrayOutputStream()
            ext.stdout = {
                return standardOutput.toString()
            }
            ext.stderr = {
                return errorOutput.toString()
            }
            doLast {
                println "rm output:" + ext.stdout()
                println "rm error output:" + ext.stderr()
            }
        }        

Now running under debug logging, it still looks like everything is working - yet nothing gets deleted:

12:33:02.801 [LIFECYCLE] [class org.gradle.internal.buildevents.TaskExecutionLogger] :openssl64:nativeClean
12:33:02.801 [DEBUG] [org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter] Starting to execute task ':openssl64:nativeClean'
12:33:02.801 [DEBUG] [org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter] Determining if task ':openssl64:nativeClean' is up-to-date
12:33:02.801 [INFO] [org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter] Executing task ':openssl64:nativeClean' (up-to-date check took 0.0 secs) due to:
  Task has not declared any outputs.
12:33:02.801 [DEBUG] [org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter] Executing actions for task ':openssl64:nativeClean'.
12:33:02.801 [INFO] [org.gradle.process.internal.DefaultExecHandle] Starting process 'command 'rm''. Working directory: /usr/local/openssl Command: rm -rfv *
12:33:02.801 [DEBUG] [org.gradle.process.internal.DefaultExecHandle] Environment for process 'command 'rm'': {... (edited)`
}, LS_COLORS=, HOME=/home/sc1478, SHLVL=1}
12:33:02.801 [DEBUG] [org.gradle.process.internal.DefaultExecHandle] Changing state to: STARTING
12:33:02.802 [DEBUG] [org.gradle.process.internal.DefaultExecHandle] Waiting until process started: command 'rm'.
12:33:02.804 [DEBUG] [org.gradle.process.internal.DefaultExecHandle] Changing state to: STARTED
12:33:02.806 [DEBUG] [org.gradle.process.internal.ExecHandleRunner] waiting until streams are handled...
12:33:02.806 [INFO] [org.gradle.process.internal.DefaultExecHandle] Successfully started process 'command 'rm''
12:33:02.808 [DEBUG] [org.gradle.process.internal.DefaultExecHandle] Changing state to: SUCCEEDED
12:33:02.808 [DEBUG] [org.gradle.process.internal.DefaultExecHandle] Process 'command 'rm'' finished with exit value 0 (state: SUCCEEDED)
12:33:02.808 [QUIET] [system.out] rm output:
12:33:02.808 [QUIET] [system.out] rm error output:
12:33:02.808 [DEBUG] [org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter] Finished executing task ':openssl64:nativeClean'

It seems possible that the “*” is not being globbed, as I don’t think this is executing through through a command line shell. You could verify this by changing the argument to a single file name that you know will exist. If that “works”, I’m still not sure what the proper solution is.

Thank you, David, good call! That is the issue.

Now what to do about it? That is the $64K question.

I’m pretty sure I could use the Delete task instead of Exec with a properly constructed collection of Files. But is it true that globs are not properly processible by Exec? Is there a way around this?

Delete seems to have another issue with confusion about UP-TO-DATE-ness.

With the task now defined as

        Task nativeClean = project.tasks.create('nativeClean', Delete) {
            File nativeRoot = new File(nativeDir)
            // everything under /usr/local/openssl, not including /usr/local/openssl itself
            FileCollection natives = project.files(nativeRoot).filter{ File f ->
                !(f.equals(nativeRoot))
            }
            delete natives
            outputs.upToDateWhen {
                FileTree tree = project.fileTree(dir: nativeDir)
                tree.files.size() <=1
            }
        }        

we can see that upToDateWhen functions correctly but Gradle still finds it up-to-date:

:openssl64:nativeClean (Thread[Daemon worker Thread 33,5,main]) started.
:openssl64:nativeClean
Executing task ':openssl64:nativeClean' (up-to-date check took 0.01 secs) due to:
  Task.upToDateWhen is false.
:openssl64:nativeClean UP-TO-DATE
:openssl64:nativeClean (Thread[Daemon worker Thread 33,5,main]) completed. Took 0.012 secs.

First it says upToDateWhen is false, then it UP-TO-DATEs it anyway.

Fortunately, this simple suggestion from Stack Overflow works and is properly evaluated for up-to-dateness.:

Task nativeClean = project.tasks.create('nativeClean', Exec) {
    // does not depend on anything, must be manually invoked.
    workingDir nativeDir
    commandLine 'bash', '-c', "/bin/rm -rf *"
}