Add ABFS (Android Build Filesystem) configuration option to Soong

Change-Id: Ibbe81004adc67b1212d7e619a35f7425bfee0d72
diff --git a/ui/build/build.go b/ui/build/build.go
index 03d8392..cb17cf4 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -210,9 +210,38 @@
 	}
 }
 
+func abfsBuildStarted(ctx Context, config Config) {
+	abfsBox := config.PrebuiltBuildTool("abfsbox")
+	cmdArgs := []string{"build-started", "--"}
+	cmdArgs = append(cmdArgs, config.Arguments()...)
+	cmd := Command(ctx, config, "abfsbox", abfsBox, cmdArgs...)
+	cmd.Sandbox = noSandbox
+	cmd.RunAndPrintOrFatal()
+}
+
+func abfsBuildFinished(ctx Context, config Config, finished bool) {
+	var errMsg string
+	if !finished {
+		errMsg = "build was interrupted"
+	}
+	abfsBox := config.PrebuiltBuildTool("abfsbox")
+	cmdArgs := []string{"build-finished", "-e", errMsg, "--"}
+	cmdArgs = append(cmdArgs, config.Arguments()...)
+	cmd := Command(ctx, config, "abfsbox", abfsBox, cmdArgs...)
+	cmd.RunAndPrintOrFatal()
+}
+
 // Build the tree. Various flags in `config` govern which components of
 // the build to run.
 func Build(ctx Context, config Config) {
+	done := false
+	if config.UseABFS() {
+		abfsBuildStarted(ctx, config)
+		defer func() {
+			abfsBuildFinished(ctx, config, done)
+		}()
+	}
+
 	ctx.Verboseln("Starting build with args:", config.Arguments())
 	ctx.Verboseln("Environment:", config.Environment().Environ())
 
@@ -349,6 +378,7 @@
 	if what&RunDistActions != 0 {
 		runDistActions(ctx, config)
 	}
+	done = true
 }
 
 func evaluateWhatToRun(config Config, verboseln func(v ...interface{})) int {
diff --git a/ui/build/config.go b/ui/build/config.go
index 2470f84..631b76f 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -1270,9 +1270,25 @@
 	if !c.StubbyExists() && strings.Contains(authType, "use_google_prod_creds") {
 		return false
 	}
+	if c.UseABFS() {
+		return false
+	}
 	return true
 }
 
+func (c *configImpl) UseABFS() bool {
+	if v, ok := c.environ.Get("NO_ABFS"); ok {
+		v = strings.ToLower(strings.TrimSpace(v))
+		if v == "true" || v == "1" {
+			return false
+		}
+	}
+
+	abfsBox := c.PrebuiltBuildTool("abfsbox")
+	err := exec.Command(abfsBox, "hash", srcDirFileCheck).Run()
+	return err == nil
+}
+
 func (c *configImpl) UseRBE() bool {
 	// These alternate modes of running Soong do not use RBE / reclient.
 	if c.Queryview() || c.JsonModuleGraph() {
@@ -1585,6 +1601,23 @@
 	}
 }
 
+func (c *configImpl) KatiBin() string {
+	binName := "ckati"
+	if c.UseABFS() {
+		binName = "ckati-wrap"
+	}
+
+	return c.PrebuiltBuildTool(binName)
+}
+
+func (c *configImpl) NinjaBin() string {
+	binName := "ninja"
+	if c.UseABFS() {
+		binName = "ninjago"
+	}
+	return c.PrebuiltBuildTool(binName)
+}
+
 func (c *configImpl) PrebuiltBuildTool(name string) string {
 	if v, ok := c.environ.Get("SANITIZE_HOST"); ok {
 		if sanitize := strings.Fields(v); inList("address", sanitize) {
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index e77df44..5df3a95 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -93,7 +93,7 @@
 	defer tool.Finish()
 
 	cmd := Command(ctx, config, "dumpvars",
-		config.PrebuiltBuildTool("ckati"),
+		config.KatiBin(),
 		"-f", "build/make/core/config.mk",
 		"--color_warnings",
 		"--kati_stats",
diff --git a/ui/build/kati.go b/ui/build/kati.go
index d599c99..a0efd2c 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -84,7 +84,7 @@
 // arguments, and a custom function closure to mutate the environment Kati runs
 // in.
 func runKati(ctx Context, config Config, extraSuffix string, args []string, envFunc func(*Environment)) {
-	executable := config.PrebuiltBuildTool("ckati")
+	executable := config.KatiBin()
 	// cKati arguments.
 	args = append([]string{
 		// Instead of executing commands directly, generate a Ninja file.
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 1935e72..b5e74b4 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -49,7 +49,7 @@
 	nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo)
 	defer nr.Close()
 
-	executable := config.PrebuiltBuildTool("ninja")
+	executable := config.NinjaBin()
 	args := []string{
 		"-d", "keepdepfile",
 		"-d", "keeprsp",
diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go
index edb3b66..5c3fec1 100644
--- a/ui/build/sandbox_linux.go
+++ b/ui/build/sandbox_linux.go
@@ -200,6 +200,9 @@
 		// Only log important warnings / errors
 		"-q",
 	}
+	if c.config.UseABFS() {
+		sandboxArgs = append(sandboxArgs, "-B", "{ABFS_DIR}")
+	}
 
 	// Mount srcDir RW allowlists as Read-Write
 	if len(c.config.sandboxConfig.SrcDirRWAllowlist()) > 0 && !c.config.sandboxConfig.SrcDirIsRO() {
diff --git a/ui/build/soong.go b/ui/build/soong.go
index e18cc25..2ccdfec 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -661,7 +661,7 @@
 		}
 
 		ninjaArgs = append(ninjaArgs, targets...)
-		ninjaCmd := config.PrebuiltBuildTool("ninja")
+		ninjaCmd := config.NinjaBin()
 		if config.useN2 {
 			ninjaCmd = config.PrebuiltBuildTool("n2")
 		}