Protobuf Exec task fails, but command works

I have a gradle Exec task that fails, but when I run the command manually it works fine.
I am getting a IOException No such file or directory, but all files and directories in the command does exist
There are a dozen proto files, but I have omitted all but one to keep it simple.

I cannot find any problems with the task configuration.

This is my protobuf task

task protobuf(type: Exec) {
    final def javaDir = "src/main/java"
    final def protoDir = "${projectDir}/protobuf"
    final def protoPaths = fileTree(dir: protoDir, include: '**/*.proto')
    final def protoFiles = protoPaths.each { it }.join(' ')

    commandLine = "protoc --proto_path=. --proto_path=/usr/include --java_out=${javaDir} -I${protoDir} ${protoFiles}"
}

The output from: gradle protobuf --info --console=verbose --stacktrace

    > Task :protobuf FAILED
    Task ':protobuf' is not up-to-date because:
      Task has not declared any outputs despite executing actions.
    Starting process 'command 'protoc --proto_path=. --proto_path=/usr/include --java_out=src/main/java -I/home/sverre/workspace2/server-protobuf/protobuf /home/sverre/workspace2/server-protobuf/protobuf/message1.proto''. Working directory: /home/sverre/workspace2/server-protobuf Command: protoc --proto_path=. --proto_path=/usr/include --java_out=src/main/java -I/home/sverre/workspace2/server-protobuf/protobuf /home/sverre/workspace2/server-protobuf/protobuf/message1.proto
    :protobuf (Thread[Task worker for ':',5,main]) completed. Took 0.01 secs.

    FAILURE: Build failed with an exception.

    * What went wrong:
    Execution failed for task ':protobuf'.
    > A problem occurred starting process 'command 'protoc --proto_path=. --proto_path=/usr/include --java_out=src/main/java -I/home/sverre/workspace2/server-protobuf/protobuf /home/sverre/workspace2/server-protobuf/protobuf/message1.proto''

    * Try:
    Run with --debug option to get more log output. Run with --scan to get full insights.

    * Exception is:
    org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':protobuf'.
            at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:110)
            at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:77)
            at org.gradle.api.internal.tasks.execution.OutputDirectoryCreatingTaskExecuter.execute(OutputDirectoryCreatingTaskExecuter.java:51)
            at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:59)
            at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:54)
            at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:59)
            at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:101)
            at org.gradle.api.internal.tasks.execution.FinalizeInputFilePropertiesTaskExecuter.execute(FinalizeInputFilePropertiesTaskExecuter.java:44)
            at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:91)
            at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:62)
            at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:59)
            at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
            at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
            at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
            at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.run(EventFiringTaskExecuter.java:51)
            at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:300)
            at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:292)
            at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:174)
            at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:90)
            at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
            at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:46)
            at org.gradle.execution.taskgraph.LocalTaskInfoExecutor.execute(LocalTaskInfoExecutor.java:42)
            at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareWorkItemExecutor.execute(DefaultTaskExecutionGraph.java:277)
            at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareWorkItemExecutor.execute(DefaultTaskExecutionGraph.java:262)
            at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:135)
            at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:130)
            at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.execute(DefaultTaskPlanExecutor.java:200)
            at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.executeWithWork(DefaultTaskPlanExecutor.java:191)
            at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.run(DefaultTaskPlanExecutor.java:130)
            at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
            at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
            at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
    Caused by: org.gradle.process.internal.ExecException: A problem occurred starting process 'command 'protoc --proto_path=. --proto_path=/usr/include --java_out=src/main/java -I/home/sverre/workspace2/server-protobuf/protobuf /home/sverre/workspace2/server-protobuf/protobuf/message1.proto''
            at org.gradle.process.internal.DefaultExecHandle.execExceptionFor(DefaultExecHandle.java:231)
            at org.gradle.process.internal.DefaultExecHandle.setEndStateInfo(DefaultExecHandle.java:209)
            at org.gradle.process.internal.DefaultExecHandle.failed(DefaultExecHandle.java:355)
            at org.gradle.process.internal.ExecHandleRunner.run(ExecHandleRunner.java:85)
            at org.gradle.internal.operations.CurrentBuildOperationPreservingRunnable.run(CurrentBuildOperationPreservingRunnable.java:42)
            ... 3 more
    Caused by: net.rubygrapefruit.platform.NativeException: Could not start 'protoc --proto_path=. --proto_path=/usr/include --java_out=src/main/java -I/home/sverre/workspace2/server-protobuf/protobuf /home/sverre/workspace2/server-protobuf/protobuf/message1.proto'
            at net.rubygrapefruit.platform.internal.DefaultProcessLauncher.start(DefaultProcessLauncher.java:27)
            at net.rubygrapefruit.platform.internal.WrapperProcessLauncher.start(WrapperProcessLauncher.java:36)
            at org.gradle.process.internal.ExecHandleRunner.run(ExecHandleRunner.java:67)
            ... 4 more
    Caused by: java.io.IOException: Cannot run program "protoc --proto_path=. --proto_path=/usr/include --java_out=src/main/java -I/home/sverre/workspace2/server-protobuf/protobuf /home/sverre/workspace2/server-protobuf/protobuf/message1.proto" (in directory "/home/sverre/workspace2/server-protobuf"): error=2, No such file or directory
            at net.rubygrapefruit.platform.internal.DefaultProcessLauncher.start(DefaultProcessLauncher.java:25)
            ... 6 more
    Caused by: java.io.IOException: error=2, No such file or directory
            ... 7 more


    11:51:06.110 [DEBUG] [org.gradle.process.internal.DefaultExecHandle] Changing state to: STARTING
    11:51:06.140 [DEBUG] [org.gradle.process.internal.DefaultExecHandle] Waiting until process started: command 'protoc --proto_path=. --proto_path=/usr/include --java_out=src/main/java -I/home/sverre/workspace2/server-protobuf/protobuf /home/sverre/workspace2/server-protobuf/protobuf/message1.proto'.
    11:51:06.144 [DEBUG] [org.gradle.process.internal.DefaultExecHandle] Changing state to: FAILED
    11:51:06.144 [DEBUG] [org.gradle.process.internal.DefaultExecHandle] Process 'command 'protoc --proto_path=. --proto_path=/usr/include --java_out=src/main/java -I/home/sverre/workspace2/server-protobuf/protobuf /home/sverre/workspace2/server-protobuf/protobuf/message1.proto'' finished with exit value -1 (state: FAILED)

Exec.commandLine is a List<String>. Try this

task protobuf(type: Exec) {
   ...
   commandLine "protoc --proto_path=. --proto_path=/usr/include --java_out=${javaDir} -I${protoDir} ${protoFiles}".split(' ')
} 

That worked. Thanks

Perhaps the documentation should be updated
https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Exec.html

The docs show this which is equivalent

commandLine 'cmd', '/c', 'stop.bat'

Ok, My eyes just ignored that because comment said Windows.

Using executable and args would be more precise than constructing the commandLine.

executable "protoc"
args = ["--proto_path=.",
        "--proto_path=/usr/include",
        "--java_out=${javaDir}",
        "-I${protoDir}",
        protoFiles
]

However this one also fails with “No such file or directory”.
What is wrong this time?

In your original example code protoFiles is a space separated String. If that’s still the case, then it will be treated as one single argument when used with args = [..., protoFiles]. You need to add each individual file as a separate entry in the args list.

Try args = [..., *protoPaths]
Might need to convert the File's to Strings yourself, *(protoPaths.collect { it.path })