7
7
"os/exec"
8
8
"sync"
9
9
10
+ "github.com/mattn/go-shellwords"
10
11
"golang.org/x/sync/errgroup"
11
12
"golang.org/x/sync/semaphore"
12
13
"golang.org/x/sys/execabs"
@@ -109,18 +110,38 @@ func (proc *concurrentProcess) wait() {
109
110
// newCommandRunner creates new external command runner for given executable. The executable path
110
111
// is resolved in this function.
111
112
func (proc * concurrentProcess ) newCommandRunner (exe string , combineOutput bool ) (* externalCommand , error ) {
112
- p , err := execabs .LookPath (exe )
113
+ var args []string
114
+ p , args , err := findExe (exe )
113
115
if err != nil {
114
116
return nil , err
115
117
}
116
118
cmd := & externalCommand {
117
119
proc : proc ,
118
120
exe : p ,
121
+ args : args ,
119
122
combineOutput : combineOutput ,
120
123
}
121
124
return cmd , nil
122
125
}
123
126
127
+ func findExe (exe string ) (string , []string , error ) {
128
+ p , err := execabs .LookPath (exe )
129
+ if err == nil {
130
+ return p , nil , nil
131
+ }
132
+ // See if the command string contains args. As it is best effort, we do not
133
+ // handle parse errors.
134
+ if exeArgs , _ := shellwords .Parse (exe ); len (exeArgs ) > 0 {
135
+ // We want to return the original error if this command isn't found so
136
+ // do not overwrite it.
137
+ if p , err2 := execabs .LookPath (exeArgs [0 ]); err2 == nil {
138
+ return p , exeArgs [1 :], nil
139
+ }
140
+ }
141
+
142
+ return "" , nil , err
143
+ }
144
+
124
145
// externalCommand is struct to run specific command concurrently with concurrentProcess bounding
125
146
// number of processes at the same time. This type manages fatal errors while running the command
126
147
// by using errgroup.Group. The wait() method must be called at the end for checking if some fatal
@@ -129,13 +150,20 @@ type externalCommand struct {
129
150
proc * concurrentProcess
130
151
eg errgroup.Group
131
152
exe string
153
+ args []string
132
154
combineOutput bool
133
155
}
134
156
135
157
// run runs the command with given arguments and stdin. The callback function is called after the
136
158
// process runs. First argument is stdout and the second argument is an error while running the
137
159
// process.
138
160
func (cmd * externalCommand ) run (args []string , stdin string , callback func ([]byte , error ) error ) {
161
+ if len (cmd .args ) > 0 {
162
+ var allArgs []string
163
+ allArgs = append (allArgs , cmd .args ... )
164
+ allArgs = append (allArgs , args ... )
165
+ args = allArgs
166
+ }
139
167
exec := & cmdExecution {cmd .exe , args , stdin , cmd .combineOutput }
140
168
cmd .proc .run (& cmd .eg , exec , callback )
141
169
}
0 commit comments