Have Soong try to enforce that genrules declare all their outputs.

This causes Soong to put the outputs of each genrule into a temporary
location and copy the declared outputs back to the output directory.
This gets the process closer to having an actual sandbox.

Bug: 35562758
Test: make

Change-Id: I8048fbf1a3899a86fb99d71b60669b6633b07b3e
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index 25520da..489c06d 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -18,6 +18,7 @@
     deps: [
         "soong-ui-logger",
         "soong-ui-tracer",
+        "soong-shared",
     ],
     srcs: [
         "build.go",
diff --git a/ui/build/build.go b/ui/build/build.go
index 83dbcb6..1400c48 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -125,6 +125,8 @@
 
 	checkCaseSensitivity(ctx, config)
 
+	ensureEmptyDirectoriesExist(ctx, config.TempDir())
+
 	if what&BuildProductConfig != 0 {
 		// Run make for product config
 		runMakeProductConfig(ctx, config)
diff --git a/ui/build/config.go b/ui/build/config.go
index 7e8091b..16826f2 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -21,6 +21,8 @@
 	"runtime"
 	"strconv"
 	"strings"
+
+	"android/soong/shared"
 )
 
 type Config struct{ *configImpl }
@@ -250,6 +252,10 @@
 	return filepath.Join(c.OutDir(), "soong")
 }
 
+func (c *configImpl) TempDir() string {
+	return shared.TempDirForOutDir(c.SoongOutDir())
+}
+
 func (c *configImpl) KatiSuffix() string {
 	if c.katiSuffix != "" {
 		return c.katiSuffix
@@ -306,7 +312,7 @@
 }
 
 // RemoteParallel controls how many remote jobs (i.e., commands which contain
-// gomacc) are run in parallel.  Note the paralleism of all other jobs is
+// gomacc) are run in parallel.  Note the parallelism of all other jobs is
 // still limited by Parallel()
 func (c *configImpl) RemoteParallel() int {
 	if v, ok := c.environ.Get("NINJA_REMOTE_NUM_JOBS"); ok {
diff --git a/ui/build/util.go b/ui/build/util.go
index 37ac6b9..2555e8a 100644
--- a/ui/build/util.go
+++ b/ui/build/util.go
@@ -50,6 +50,19 @@
 	}
 }
 
+// ensureEmptyDirectoriesExist ensures that the given directories exist and are empty
+func ensureEmptyDirectoriesExist(ctx Context, dirs ...string) {
+	// remove all the directories
+	for _, dir := range dirs {
+		err := os.RemoveAll(dir)
+		if err != nil {
+			ctx.Fatalf("Error removing %s: %q\n", dir, err)
+		}
+	}
+	// recreate all the directories
+	ensureDirectoriesExist(ctx, dirs...)
+}
+
 // ensureEmptyFileExists ensures that the containing directory exists, and the
 // specified file exists. If it doesn't exist, it will write an empty file.
 func ensureEmptyFileExists(ctx Context, file string) {