Skip to content

Commit 3393bc3

Browse files
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 ca8a1ed commit 3393bc3

File tree

3 files changed

+42
-5
lines changed

3 files changed

+42
-5
lines changed

src/arduino.cc/builder/builder_utils/utils.go

-5
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
package builder_utils
3131

3232
import (
33-
"fmt"
3433
"io"
3534
"os"
3635
"os/exec"
@@ -386,10 +385,6 @@ func PrepareCommandForRecipe(ctx *types.Context, buildProperties properties.Map,
386385
return nil, i18n.WrapError(err)
387386
}
388387

389-
if ctx.Verbose {
390-
fmt.Println(commandLine)
391-
}
392-
393388
return command, nil
394389
}
395390

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

src/arduino.cc/builder/utils/utils.go

+23
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
package utils
3131

3232
import (
33+
"fmt"
3334
"bytes"
3435
"crypto/md5"
3536
"encoding/hex"
@@ -255,6 +256,24 @@ func PrepareCommand(pattern string, logger i18n.Logger) (*exec.Cmd, error) {
255256
return PrepareCommandFilteredArgs(pattern, filterEmptyArg, logger)
256257
}
257258

259+
func printableArgument(arg string) string {
260+
if strings.ContainsAny(arg, "\"\\ \t") {
261+
arg = strings.Replace(arg, "\\", "\\\\", -1)
262+
arg = strings.Replace(arg, "\"", "\\\"", -1)
263+
return "\"" + arg + "\""
264+
} else {
265+
return arg
266+
}
267+
}
268+
269+
// Convert a command and argument slice back to a printable string.
270+
// This adds basic escaping which is sufficient for debug output, but
271+
// probably not for shell interpretation. This essentially reverses
272+
// ParseCommandLine.
273+
func PrintableCommand(parts []string) string {
274+
return strings.Join(Map(parts, printableArgument), " ")
275+
}
276+
258277
const (
259278
Ignore = 0 // Redirect to null
260279
Show = 1 // Show on stdout/stderr as normal
@@ -263,6 +282,10 @@ const (
263282
)
264283

265284
func ExecCommand(ctx *types.Context, command *exec.Cmd, stdout int, stderr int) ([]byte, []byte, error) {
285+
if ctx.Verbose {
286+
fmt.Println(PrintableCommand(command.Args))
287+
}
288+
266289
if stdout == Capture {
267290
buffer := &bytes.Buffer{}
268291
command.Stdout = buffer

0 commit comments

Comments
 (0)