aboutsummaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorDan Willemsen <dwillemsen@google.com>2017-05-03 17:15:47 -0700
committerDan Willemsen <dwillemsen@google.com>2017-05-05 16:25:40 -0700
commit269a8c78e7c33a0253073ea6b136583650ab7051 (patch)
treec4e28d19bdf0ad9cb07626231996ffa721d805b0 /ui
parent37a2aeb95a7a94a17059e85327796fe7418395a4 (diff)
downloadbuild_soong-269a8c78e7c33a0253073ea6b136583650ab7051.tar.gz
build_soong-269a8c78e7c33a0253073ea6b136583650ab7051.tar.bz2
build_soong-269a8c78e7c33a0253073ea6b136583650ab7051.zip
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
Diffstat (limited to 'ui')
-rw-r--r--ui/build/Android.bp11
-rw-r--r--ui/build/build.go10
-rw-r--r--ui/build/exec.go107
-rw-r--r--ui/build/kati.go20
-rw-r--r--ui/build/make.go6
-rw-r--r--ui/build/ninja.go28
-rw-r--r--ui/build/sandbox/darwin/global.sb40
-rw-r--r--ui/build/sandbox_darwin.go69
-rw-r--r--ui/build/sandbox_linux.go32
-rw-r--r--ui/build/soong.go34
10 files changed, 285 insertions, 72 deletions
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index 51aed2c1..d44c112d 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -24,6 +24,7 @@ bootstrap_go_package {
"config.go",
"context.go",
"environment.go",
+ "exec.go",
"kati.go",
"make.go",
"ninja.go",
@@ -36,9 +37,15 @@ bootstrap_go_package {
"util_test.go",
],
darwin: {
- srcs: ["util_darwin.go"],
+ srcs: [
+ "sandbox_darwin.go",
+ "util_darwin.go"
+ ],
},
linux: {
- srcs: ["util_linux.go"],
+ srcs: [
+ "sandbox_linux.go",
+ "util_linux.go"
+ ],
},
}
diff --git a/ui/build/build.go b/ui/build/build.go
index 506ff519..d6059c0b 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -16,7 +16,6 @@ package build
import (
"os"
- "os/exec"
"path/filepath"
"text/template"
)
@@ -67,13 +66,12 @@ func Build(ctx Context, config Config, what int) {
ctx.Verboseln("Environment:", config.Environment().Environ())
if inList("help", config.Arguments()) {
- cmd := exec.CommandContext(ctx.Context, "make", "-f", "build/core/help.mk")
- cmd.Env = config.Environment().Environ()
+ cmd := Command(ctx, config, "make",
+ "make", "-f", "build/core/help.mk")
+ cmd.Sandbox = makeSandbox
cmd.Stdout = ctx.Stdout()
cmd.Stderr = ctx.Stderr()
- if err := cmd.Run(); err != nil {
- ctx.Fatalln("Failed to run make:", err)
- }
+ cmd.RunOrFatal()
return
}
diff --git a/ui/build/exec.go b/ui/build/exec.go
new file mode 100644
index 00000000..4c45c507
--- /dev/null
+++ b/ui/build/exec.go
@@ -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)
+ }
+ }
+}
diff --git a/ui/build/kati.go b/ui/build/kati.go
index 218ca698..f29d0c19 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -20,7 +20,6 @@ import (
"fmt"
"io"
"io/ioutil"
- "os/exec"
"path/filepath"
"regexp"
"strconv"
@@ -93,28 +92,17 @@ func runKati(ctx Context, config Config) {
args = append(args, "-j"+strconv.Itoa(config.Parallel()))
}
- cmd := exec.CommandContext(ctx.Context, executable, args...)
- cmd.Env = config.Environment().Environ()
+ cmd := Command(ctx, config, "ckati", executable, args...)
+ cmd.Sandbox = katiSandbox
pipe, err := cmd.StdoutPipe()
if err != nil {
ctx.Fatalln("Error getting output pipe for ckati:", err)
}
cmd.Stderr = cmd.Stdout
- ctx.Verboseln(cmd.Path, cmd.Args)
- if err := cmd.Start(); err != nil {
- ctx.Fatalln("Failed to run ckati:", err)
- }
-
+ cmd.StartOrFatal()
katiRewriteOutput(ctx, pipe)
-
- if err := cmd.Wait(); err != nil {
- if e, ok := err.(*exec.ExitError); ok {
- ctx.Fatalln("ckati failed with:", e.ProcessState.String())
- } else {
- ctx.Fatalln("Failed to run ckati:", err)
- }
- }
+ cmd.WaitOrFatal()
}
var katiIncludeRe = regexp.MustCompile(`^(\[\d+/\d+] )?including [^ ]+ ...$`)
diff --git a/ui/build/make.go b/ui/build/make.go
index a8d4483b..32dc17b5 100644
--- a/ui/build/make.go
+++ b/ui/build/make.go
@@ -16,7 +16,6 @@ package build
import (
"fmt"
- "os/exec"
"path/filepath"
"strings"
)
@@ -38,7 +37,7 @@ func DumpMakeVars(ctx Context, config Config, goals, extra_targets, vars []strin
ctx.BeginTrace("dumpvars")
defer ctx.EndTrace()
- cmd := exec.CommandContext(ctx.Context,
+ cmd := Command(ctx, config, "make",
"make",
"--no-print-directory",
"-f", "build/core/config.mk",
@@ -48,11 +47,10 @@ func DumpMakeVars(ctx Context, config Config, goals, extra_targets, vars []strin
"MAKECMDGOALS="+strings.Join(goals, " "),
"DUMP_MANY_VARS="+strings.Join(vars, " "),
"OUT_DIR="+config.OutDir())
- cmd.Env = config.Environment().Environ()
cmd.Args = append(cmd.Args, extra_targets...)
+ cmd.Sandbox = makeSandbox
// TODO: error out when Stderr contains any content
cmd.Stderr = ctx.Stderr()
- ctx.Verboseln(cmd.Path, cmd.Args)
output, err := cmd.Output()
if err != nil {
return nil, err
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 054af243..5787a00f 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 @@ func runNinja(ctx Context, config Config) {
}
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()
}
diff --git a/ui/build/sandbox/darwin/global.sb b/ui/build/sandbox/darwin/global.sb
new file mode 100644
index 00000000..47d0c434
--- /dev/null
+++ b/ui/build/sandbox/darwin/global.sb
@@ -0,0 +1,40 @@
+(version 1)
+
+; TODO: (deny default)
+(allow default (with report))
+
+; Import apple-defined rules for bsd daemons
+(import "bsd.sb")
+
+; Allow reading of any file
+(allow file-read*)
+
+; Allow writing to $OUT_DIR and $DIST_DIR
+(allow file-write*
+ (subpath (param "OUT_DIR"))
+ (subpath (param "DIST_DIR")))
+
+; Java attempts to write usage data to ~/.oracle_jre_usage, just ignore
+(deny file-write* (with no-log)
+ (subpath (string-append (param "HOME") "/.oracle_jre_usage")))
+
+; Allow writes to user-specific temp folders (Java stores hsperfdata there)
+(allow file-write*
+ (subpath "/private/var/folders"))
+
+; Allow writing to the terminal
+(allow file-write-data
+ (subpath "/dev/tty"))
+
+; Java
+(allow mach-lookup
+ (global-name "com.apple.SystemConfiguration.configd") ; Java
+ (global-name "com.apple.CoreServices.coreservicesd") ; xcodebuild in Soong
+ (global-name "com.apple.FSEvents") ; xcodebuild in Soong
+ (global-name "com.apple.lsd.mapdb") ; xcodebuild in Soong
+ (global-name-regex #"^com\.apple\.distributed_notifications") ; xcodebuild in Soong
+)
+
+; Allow executing any file
+(allow process-exec*)
+(allow process-fork)
diff --git a/ui/build/sandbox_darwin.go b/ui/build/sandbox_darwin.go
new file mode 100644
index 00000000..54c145c5
--- /dev/null
+++ b/ui/build/sandbox_darwin.go
@@ -0,0 +1,69 @@
+// 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"
+ "path/filepath"
+)
+
+type Sandbox string
+
+const (
+ noSandbox = ""
+ globalSandbox = "build/soong/ui/build/sandbox/darwin/global.sb"
+ makeSandbox = globalSandbox
+ soongSandbox = globalSandbox
+ katiSandbox = globalSandbox
+)
+
+var sandboxExecPath string
+
+func init() {
+ if p, err := exec.LookPath("sandbox-exec"); err == nil {
+ sandboxExecPath = p
+ }
+}
+
+func (c *Cmd) sandboxSupported() bool {
+ if c.Sandbox == "" {
+ return false
+ } else if sandboxExecPath == "" {
+ c.ctx.Verboseln("sandbox-exec not found, disabling sandboxing")
+ return false
+ }
+ return true
+}
+
+func (c *Cmd) wrapSandbox() {
+ homeDir, _ := c.Environment.Get("HOME")
+ outDir, err := filepath.Abs(c.config.OutDir())
+ if err != nil {
+ c.ctx.Fatalln("Failed to get absolute path of OUT_DIR:", err)
+ }
+ distDir, err := filepath.Abs(c.config.DistDir())
+ if err != nil {
+ c.ctx.Fatalln("Failed to get absolute path of DIST_DIR:", err)
+ }
+
+ c.Args[0] = c.Path
+ c.Path = sandboxExecPath
+ c.Args = append([]string{
+ "sandbox-exec", "-f", string(c.Sandbox),
+ "-D", "HOME=" + homeDir,
+ "-D", "OUT_DIR=" + outDir,
+ "-D", "DIST_DIR=" + distDir,
+ }, c.Args...)
+}
diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go
new file mode 100644
index 00000000..fb48b9cb
--- /dev/null
+++ b/ui/build/sandbox_linux.go
@@ -0,0 +1,32 @@
+// 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
+
+type Sandbox bool
+
+const (
+ noSandbox = false
+ globalSandbox = false
+ makeSandbox = false
+ soongSandbox = false
+ katiSandbox = false
+)
+
+func (c *Cmd) sandboxSupported() bool {
+ return false
+}
+
+func (c *Cmd) wrapSandbox() {
+}
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 6554f1d8..ddfe666c 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -15,7 +15,6 @@
package build
import (
- "os/exec"
"path/filepath"
)
@@ -23,42 +22,27 @@ func runSoongBootstrap(ctx Context, config Config) {
ctx.BeginTrace("bootstrap soong")
defer ctx.EndTrace()
- cmd := exec.CommandContext(ctx.Context, "./bootstrap.bash")
- env := config.Environment().Copy()
- env.Set("BUILDDIR", config.SoongOutDir())
- cmd.Env = env.Environ()
+ cmd := Command(ctx, config, "soong bootstrap", "./bootstrap.bash")
+ cmd.Environment.Set("BUILDDIR", config.SoongOutDir())
+ cmd.Sandbox = soongSandbox
cmd.Stdout = ctx.Stdout()
cmd.Stderr = ctx.Stderr()
- ctx.Verboseln(cmd.Path, cmd.Args)
- if err := cmd.Run(); err != nil {
- if e, ok := err.(*exec.ExitError); ok {
- ctx.Fatalln("soong bootstrap failed with:", e.ProcessState.String())
- } else {
- ctx.Fatalln("Failed to run soong bootstrap:", err)
- }
- }
+ cmd.RunOrFatal()
}
func runSoong(ctx Context, config Config) {
ctx.BeginTrace("soong")
defer ctx.EndTrace()
- cmd := exec.CommandContext(ctx.Context, filepath.Join(config.SoongOutDir(), "soong"), "-w", "dupbuild=err")
+ cmd := Command(ctx, config, "soong",
+ filepath.Join(config.SoongOutDir(), "soong"), "-w", "dupbuild=err")
if config.IsVerbose() {
cmd.Args = append(cmd.Args, "-v")
}
- env := config.Environment().Copy()
- env.Set("SKIP_NINJA", "true")
- cmd.Env = env.Environ()
+ cmd.Environment.Set("SKIP_NINJA", "true")
+ cmd.Sandbox = soongSandbox
cmd.Stdin = ctx.Stdin()
cmd.Stdout = ctx.Stdout()
cmd.Stderr = ctx.Stderr()
- ctx.Verboseln(cmd.Path, cmd.Args)
- if err := cmd.Run(); err != nil {
- if e, ok := err.(*exec.ExitError); ok {
- ctx.Fatalln("soong bootstrap failed with:", e.ProcessState.String())
- } else {
- ctx.Fatalln("Failed to run soong bootstrap:", err)
- }
- }
+ cmd.RunOrFatal()
}