Add exec.Cmd wrapper for logging / sandboxing

Wrap os/exec.Cmd to use our Context and Config interfaces for automatic
logging and error handling. It also simplifies environment modification
based on the Config's environment.

This also adds sandboxing on Macs using sandbox-exec. A simple profile
is provided that only logs on violations, though multiproduct_kati on
AOSP has no violations. This isn't applied to ninja, only make / soong /
kati to start with. I measured <5% time increase in reading all
makefiles, and no noticable difference when kati doesn't regenerate.

I'd like to spin up a process to dump violation logs into our log file,
but the log reporting changed over the range of Mac versions that we
support, so that's going to be more complicated. Opening Console.app
works in all cases if you're local -- just search/filter for sandbox.

Linux sandboxing will be implemented later -- the sandbox definition is
opaque enough to support a different implementation.

Test: multiproduct_kati on AOSP master on Mac
Change-Id: I7046229333d0dcc8f426a493e0f7380828879f17
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 054af24..5787a00 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -15,7 +15,6 @@
 package build
 
 import (
-	"os/exec"
 	"path/filepath"
 	"strconv"
 	"strings"
@@ -51,35 +50,26 @@
 	}
 	args = append(args, "-w", "dupbuild=err")
 
-	env := config.Environment().Copy()
-	env.AppendFromKati(config.KatiEnvFile())
+	cmd := Command(ctx, config, "ninja", executable, args...)
+	cmd.Environment.AppendFromKati(config.KatiEnvFile())
 
 	// Allow both NINJA_ARGS and NINJA_EXTRA_ARGS, since both have been
 	// used in the past to specify extra ninja arguments.
-	if extra, ok := env.Get("NINJA_ARGS"); ok {
-		args = append(args, strings.Fields(extra)...)
+	if extra, ok := cmd.Environment.Get("NINJA_ARGS"); ok {
+		cmd.Args = append(cmd.Args, strings.Fields(extra)...)
 	}
-	if extra, ok := env.Get("NINJA_EXTRA_ARGS"); ok {
-		args = append(args, strings.Fields(extra)...)
+	if extra, ok := cmd.Environment.Get("NINJA_EXTRA_ARGS"); ok {
+		cmd.Args = append(cmd.Args, strings.Fields(extra)...)
 	}
 
-	if _, ok := env.Get("NINJA_STATUS"); !ok {
-		env.Set("NINJA_STATUS", "[%p %f/%t] ")
+	if _, ok := cmd.Environment.Get("NINJA_STATUS"); !ok {
+		cmd.Environment.Set("NINJA_STATUS", "[%p %f/%t] ")
 	}
 
-	cmd := exec.CommandContext(ctx.Context, executable, args...)
-	cmd.Env = env.Environ()
 	cmd.Stdin = ctx.Stdin()
 	cmd.Stdout = ctx.Stdout()
 	cmd.Stderr = ctx.Stderr()
-	ctx.Verboseln(cmd.Path, cmd.Args)
 	startTime := time.Now()
 	defer ctx.ImportNinjaLog(filepath.Join(config.OutDir(), ".ninja_log"), startTime)
-	if err := cmd.Run(); err != nil {
-		if e, ok := err.(*exec.ExitError); ok {
-			ctx.Fatalln("ninja failed with:", e.ProcessState.String())
-		} else {
-			ctx.Fatalln("Failed to run ninja:", err)
-		}
-	}
+	cmd.RunOrFatal()
 }