Skip to content

Commit bdac43b

Browse files
matthijskooijmanfacchinm
authored andcommitted
Let utils.ExecCommand print the command in verbose mode
Previously, the command was printed by PrepareCommandForRecipe. Letting ExecCommand print seems more accurate, since it is only printed when it is actually run (though this already happened in practice). Additionally, the command can now be modified between PrepareCommandForRecipe and ExecCommand while preserving correct output. Since ExecCommand deals with a slice of arguments instead of a single command string, this requires merging them together into a proper commandline. Some care is taken to quote arguments containing spaces, quotes or backslashes, though this is mostly intended for display purposes. Arguments are only quoted when needed, regardless of whether they were quoted in the original pattern. Signed-off-by: Matthijs Kooijman <[email protected]>
1 parent 5fb21af commit bdac43b

File tree

2 files changed

+46
-5
lines changed

2 files changed

+46
-5
lines changed

Diff for: src/arduino.cc/builder/test/utils_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,25 @@ func TestCommandLineParser(t *testing.T) {
7070
require.Equal(t, "/tmp/sketch321469072.cpp", parts[22])
7171
}
7272

73+
func TestPrintableCommand(t *testing.T) {
74+
parts := []string{
75+
"/path/to/dir with spaces/cmd",
76+
"arg1",
77+
"arg-\"with\"-quotes",
78+
"specialchar-`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?-argument",
79+
"arg with spaces",
80+
"arg\twith\t\ttabs",
81+
"lastarg",
82+
}
83+
correct := "\"/path/to/dir with spaces/cmd\"" +
84+
" arg1 \"arg-\\\"with\\\"-quotes\"" +
85+
" \"specialchar-`~!@#$%^&*()-_=+[{]}\\\\|;:'\\\",<.>/?-argument\"" +
86+
" \"arg with spaces\" \"arg\twith\t\ttabs\"" +
87+
" lastarg"
88+
result := utils.PrintableCommand(parts)
89+
require.Equal(t, correct, result)
90+
}
91+
7392
func TestCommandLineParserError(t *testing.T) {
7493
command := "\"command missing quote"
7594

Diff for: src/arduino.cc/builder/utils/utils.go

+27-5
Original file line numberDiff line numberDiff line change
@@ -278,14 +278,36 @@ func PrepareCommand(pattern string, logger i18n.Logger, relativePath string) (*e
278278
return PrepareCommandFilteredArgs(pattern, filterEmptyArg, logger, relativePath)
279279
}
280280

281+
func printableArgument(arg string) string {
282+
if strings.ContainsAny(arg, "\"\\ \t") {
283+
arg = strings.Replace(arg, "\\", "\\\\", -1)
284+
arg = strings.Replace(arg, "\"", "\\\"", -1)
285+
return "\"" + arg + "\""
286+
} else {
287+
return arg
288+
}
289+
}
290+
291+
// Convert a command and argument slice back to a printable string.
292+
// This adds basic escaping which is sufficient for debug output, but
293+
// probably not for shell interpretation. This essentially reverses
294+
// ParseCommandLine.
295+
func PrintableCommand(parts []string) string {
296+
return strings.Join(Map(parts, printableArgument), " ")
297+
}
298+
281299
const (
282-
Ignore = 0 // Redirect to null
283-
Show = 1 // Show on stdout/stderr as normal
300+
Ignore = 0 // Redirect to null
301+
Show = 1 // Show on stdout/stderr as normal
284302
ShowIfVerbose = 2 // Show if verbose is set, Ignore otherwise
285-
Capture = 3 // Capture into buffer
303+
Capture = 3 // Capture into buffer
286304
)
287305

288306
func ExecCommand(ctx *types.Context, command *exec.Cmd, stdout int, stderr int) ([]byte, []byte, error) {
307+
if ctx.Verbose {
308+
fmt.Println(PrintableCommand(command.Args))
309+
}
310+
289311
if stdout == Capture {
290312
buffer := &bytes.Buffer{}
291313
command.Stdout = buffer
@@ -309,10 +331,10 @@ func ExecCommand(ctx *types.Context, command *exec.Cmd, stdout int, stderr int)
309331

310332
var outbytes, errbytes []byte
311333
if buf, ok := command.Stdout.(*bytes.Buffer); ok {
312-
outbytes = buf.Bytes()
334+
outbytes = buf.Bytes()
313335
}
314336
if buf, ok := command.Stderr.(*bytes.Buffer); ok {
315-
errbytes = buf.Bytes()
337+
errbytes = buf.Bytes()
316338
}
317339

318340
return outbytes, errbytes, i18n.WrapError(err)

0 commit comments

Comments
 (0)