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
This commit is contained in:
Dan Willemsen
2017-05-03 17:15:47 -07:00
parent 37a2aeb95a
commit 269a8c78e7
10 changed files with 285 additions and 72 deletions

107
ui/build/exec.go Normal file
View File

@@ -0,0 +1,107 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package build
import (
"os/exec"
)
// Cmd is a wrapper of os/exec.Cmd that integrates with the build context for
// logging, the config's Environment for simpler environment modification, and
// implements hooks for sandboxing
type Cmd struct {
*exec.Cmd
Environment *Environment
Sandbox Sandbox
ctx Context
config Config
name string
}
func Command(ctx Context, config Config, name string, executable string, args ...string) *Cmd {
ret := &Cmd{
Cmd: exec.CommandContext(ctx.Context, executable, args...),
Environment: config.Environment().Copy(),
Sandbox: noSandbox,
ctx: ctx,
config: config,
name: name,
}
return ret
}
func (c *Cmd) prepare() {
if c.Env == nil {
c.Env = c.Environment.Environ()
}
if c.sandboxSupported() {
c.wrapSandbox()
}
c.ctx.Verboseln(c.Path, c.Args)
}
func (c *Cmd) Start() error {
c.prepare()
return c.Cmd.Start()
}
func (c *Cmd) Run() error {
c.prepare()
return c.Cmd.Run()
}
func (c *Cmd) Output() ([]byte, error) {
c.prepare()
return c.Cmd.Output()
}
func (c *Cmd) CombinedOutput() ([]byte, error) {
c.prepare()
return c.Cmd.CombinedOutput()
}
// StartOrFatal is equivalent to Start, but handles the error with a call to ctx.Fatal
func (c *Cmd) StartOrFatal() {
if err := c.Start(); err != nil {
c.ctx.Fatalf("Failed to run %s: %v", c.name, err)
}
}
// RunOrFatal is equivalent to Run, but handles the error with a call to ctx.Fatal
func (c *Cmd) RunOrFatal() {
if err := c.Run(); err != nil {
if e, ok := err.(*exec.ExitError); ok {
c.ctx.Fatalf("%s failed with: %v", c.name, e.ProcessState.String())
} else {
c.ctx.Fatalf("Failed to run %s: %v", c.name, err)
}
}
}
// WaitOrFatal is equivalent to Wait, but handles the error with a call to ctx.Fatal
func (c *Cmd) WaitOrFatal() {
if err := c.Wait(); err != nil {
if e, ok := err.(*exec.ExitError); ok {
c.ctx.Fatalf("%s failed with: %v", c.name, e.ProcessState.String())
} else {
c.ctx.Fatalf("Failed to run %s: %v", c.name, err)
}
}
}