Merge "Remove uses of buildDir from java/app_set_test.go"
diff --git a/android/config.go b/android/config.go
index 0de8928..e335de0 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1005,6 +1005,10 @@
 	return ioutil.ReadFile(absolutePath(path.String()))
 }
 
+func (c *deviceConfig) WithDexpreopt() bool {
+	return c.config.productVariables.WithDexpreopt
+}
+
 func (c *config) FrameworksBaseDirExists(ctx PathContext) bool {
 	return ExistentPathForSource(ctx, "frameworks", "base", "Android.bp").Valid()
 }
diff --git a/android/env.go b/android/env.go
index 289d803..725a145 100644
--- a/android/env.go
+++ b/android/env.go
@@ -18,12 +18,16 @@
 	"android/soong/shared"
 )
 
-// This file supports dependencies on environment variables.  During build manifest generation,
-// any dependency on an environment variable is added to a list.  During the singleton phase
-// a JSON file is written containing the current value of all used environment variables.
-// The next time the top-level build script is run, it uses the soong_env executable to
-// compare the contents of the environment variables, rewriting the file if necessary to cause
-// a manifest regeneration.
+// This file supports dependencies on environment variables.  During build
+// manifest generation, any dependency on an environment variable is added to a
+// list.  At the end of the build, a JSON file called soong.environment.used is
+// written containing the current value of all used environment variables. The
+// next time the top-level build script is run, soong_ui parses the compare the
+// contents of the used environment variables, then, if they changed, deletes
+// soong.environment.used to cause a rebuild.
+//
+// The dependency of build.ninja on soong.environment.used is declared in
+// build.ninja.d
 
 var originalEnv map[string]string
 
@@ -34,30 +38,3 @@
 		panic(err)
 	}
 }
-
-func EnvSingleton() Singleton {
-	return &envSingleton{}
-}
-
-type envSingleton struct{}
-
-func (c *envSingleton) GenerateBuildActions(ctx SingletonContext) {
-	envDeps := ctx.Config().EnvDeps()
-
-	envFile := PathForOutput(ctx, "soong.environment.used")
-	if ctx.Failed() {
-		return
-	}
-
-	data, err := shared.EnvFileContents(envDeps)
-	if err != nil {
-		ctx.Errorf(err.Error())
-	}
-
-	err = WriteFileToOutputDir(envFile, data, 0666)
-	if err != nil {
-		ctx.Errorf(err.Error())
-	}
-
-	ctx.AddNinjaFileDeps(envFile.String())
-}
diff --git a/android/paths.go b/android/paths.go
index f648c55..babf48c 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -511,6 +511,9 @@
 	if module == nil {
 		return nil, missingDependencyError{[]string{moduleName}}
 	}
+	if aModule, ok := module.(Module); ok && !aModule.Enabled() {
+		return nil, missingDependencyError{[]string{moduleName}}
+	}
 	if outProducer, ok := module.(OutputFileProducer); ok {
 		outputFiles, err := outProducer.OutputFiles(tag)
 		if err != nil {
diff --git a/android/register.go b/android/register.go
index 900edfa..aeaa6ff 100644
--- a/android/register.go
+++ b/android/register.go
@@ -206,7 +206,6 @@
 
 		// Register env and ninjadeps last so that they can track all used environment variables and
 		// Ninja file dependencies stored in the config.
-		singleton{false, "env", EnvSingleton},
 		singleton{false, "ninjadeps", ninjaDepsSingletonFactory},
 	)
 
diff --git a/android/testing.go b/android/testing.go
index f17de31..0dfe38c 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -401,9 +401,6 @@
 	globalOrder.mutatorOrder.enforceOrdering(mutators)
 	mutators.registerAll(ctx.Context)
 
-	// Register the env singleton with this context before sorting.
-	ctx.RegisterSingletonType("env", EnvSingleton)
-
 	// Ensure that the singletons used in the test are in the same order as they are used at runtime.
 	globalOrder.singletonOrder.enforceOrdering(ctx.singletons)
 	ctx.singletons.registerAll(ctx.Context)
diff --git a/android/variable.go b/android/variable.go
index 776a5c7..2ab51c7 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -341,6 +341,8 @@
 
 	DexpreoptGlobalConfig *string `json:",omitempty"`
 
+	WithDexpreopt bool `json:",omitempty"`
+
 	ManifestPackageNameOverrides []string `json:",omitempty"`
 	CertificateOverrides         []string `json:",omitempty"`
 	PackageNameOverrides         []string `json:",omitempty"`
diff --git a/bootstrap_test.sh b/bootstrap_test.sh
index 87f5e31..6c5338a 100755
--- a/bootstrap_test.sh
+++ b/bootstrap_test.sh
@@ -235,6 +235,86 @@
   grep -q my_little_library.py out/soong/build.ninja || fail "new file is not in output"
 }
 
+function test_soong_build_rerun_iff_environment_changes() {
+  setup
+
+  mkdir -p cherry
+  cat > cherry/Android.bp <<'EOF'
+bootstrap_go_package {
+  name: "cherry",
+  pkgPath: "android/soong/cherry",
+  deps: [
+    "blueprint",
+    "soong",
+    "soong-android",
+  ],
+  srcs: [
+    "cherry.go",
+  ],
+  pluginFor: ["soong_build"],
+}
+EOF
+
+  cat > cherry/cherry.go <<'EOF'
+package cherry
+
+import (
+  "android/soong/android"
+  "github.com/google/blueprint"
+)
+
+var (
+  pctx = android.NewPackageContext("cherry")
+)
+
+func init() {
+  android.RegisterSingletonType("cherry", CherrySingleton)
+}
+
+func CherrySingleton() android.Singleton {
+  return &cherrySingleton{}
+}
+
+type cherrySingleton struct{}
+
+func (p *cherrySingleton) GenerateBuildActions(ctx android.SingletonContext) {
+  cherryRule := ctx.Rule(pctx, "cherry",
+    blueprint.RuleParams{
+      Command: "echo CHERRY IS " + ctx.Config().Getenv("CHERRY") + " > ${out}",
+      CommandDeps: []string{},
+      Description: "Cherry",
+    })
+
+  outputFile := android.PathForOutput(ctx, "cherry", "cherry.txt")
+  var deps android.Paths
+
+  ctx.Build(pctx, android.BuildParams{
+    Rule: cherryRule,
+    Output: outputFile,
+    Inputs: deps,
+  })
+}
+EOF
+
+  export CHERRY=TASTY
+  run_soong
+  grep -q "CHERRY IS TASTY" out/soong/build.ninja \
+    || fail "first value of environment variable is not used"
+
+  export CHERRY=RED
+  run_soong
+  grep -q "CHERRY IS RED" out/soong/build.ninja \
+    || fail "second value of environment variable not used"
+  local mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+  run_soong
+  local mtime2=$(stat -c "%y" out/soong/build.ninja)
+  if [[ "$mtime1" != "$mtime2" ]]; then
+    fail "Output Ninja file changed when environment variable did not"
+  fi
+
+}
+
 function test_add_file_to_soong_build() {
   setup
   run_soong
@@ -308,12 +388,28 @@
   grep -q "Make it so" out/soong/build.ninja || fail "New action not present"
 }
 
+function test_null_build_after_docs {
+  setup
+  run_soong
+  local mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+  prebuilts/build-tools/linux-x86/bin/ninja -f out/soong/build.ninja soong_docs
+  run_soong
+  local mtime2=$(stat -c "%y" out/soong/build.ninja)
+
+  if [[ "$mtime1" != "$mtime2" ]]; then
+    fail "Output Ninja file changed on null build"
+  fi
+}
+
 test_bazel_smoke
 test_smoke
 test_null_build
+test_null_build_after_docs
 test_soong_build_rebuilt_if_blueprint_changes
 test_add_file_to_glob
 test_add_android_bp
 test_change_android_bp
 test_delete_android_bp
 test_add_file_to_soong_build
+test_soong_build_rerun_iff_environment_changes
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 11d3620..94efa4d 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -17,6 +17,7 @@
 import (
 	"flag"
 	"fmt"
+	"io/ioutil"
 	"os"
 	"path/filepath"
 	"strings"
@@ -95,11 +96,15 @@
 	android.InitSandbox(topDir)
 	android.InitEnvironment(shared.JoinPath(topDir, outDir, "soong.environment.available"))
 
+	usedVariablesFile := shared.JoinPath(outDir, "soong.environment.used")
 	// The top-level Blueprints file is passed as the first argument.
 	srcDir := filepath.Dir(flag.Arg(0))
 	var ctx *android.Context
 	configuration := newConfig(srcDir)
-	extraNinjaDeps := []string{configuration.ProductVariablesFileName}
+	extraNinjaDeps := []string{
+		configuration.ProductVariablesFileName,
+		shared.JoinPath(outDir, "soong.environment.used"),
+	}
 
 	if configuration.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" {
 		configuration.SetAllowMissingDependencies()
@@ -115,15 +120,12 @@
 		extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.BuildDir(), "always_rerun_for_delve"))
 	}
 
-	if bazelConversionRequested(configuration) {
+	bazelConversionRequested := bazelConversionRequested(configuration)
+	if bazelConversionRequested {
 		// Run the alternate pipeline of bp2build mutators and singleton to convert Blueprint to BUILD files
 		// before everything else.
-		runBp2Build(srcDir, configuration)
-		// Short-circuit and return.
-		return
-	}
-
-	if configuration.BazelContext.BazelEnabled() {
+		runBp2Build(srcDir, configuration, extraNinjaDeps)
+	} else if configuration.BazelContext.BazelEnabled() {
 		// Bazel-enabled mode. Soong runs in two passes.
 		// First pass: Analyze the build tree, but only store all bazel commands
 		// needed to correctly evaluate the tree in the second pass.
@@ -151,7 +153,7 @@
 	}
 
 	// Convert the Soong module graph into Bazel BUILD files.
-	if bazelQueryViewDir != "" {
+	if !bazelConversionRequested && bazelQueryViewDir != "" {
 		// Run the code-generation phase to convert BazelTargetModules to BUILD files.
 		codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.QueryView)
 		absoluteQueryViewDir := shared.JoinPath(topDir, bazelQueryViewDir)
@@ -161,7 +163,7 @@
 		}
 	}
 
-	if docFile != "" {
+	if !bazelConversionRequested && docFile != "" {
 		if err := writeDocs(ctx, configuration, docFile); err != nil {
 			fmt.Fprintf(os.Stderr, "%s", err)
 			os.Exit(1)
@@ -170,7 +172,7 @@
 
 	// TODO(ccross): make this a command line argument.  Requires plumbing through blueprint
 	//  to affect the command line of the primary builder.
-	if shouldPrepareBuildActions(configuration) {
+	if !bazelConversionRequested && shouldPrepareBuildActions(configuration) {
 		metricsFile := filepath.Join(bootstrap.CmdlineBuildDir(), "soong_build_metrics.pb")
 		err := android.WriteMetrics(configuration, metricsFile)
 		if err != nil {
@@ -178,12 +180,32 @@
 			os.Exit(1)
 		}
 	}
+
+	if docFile == "" {
+		// Let's not overwrite the used variables file when generating
+		// documentation
+		writeUsedVariablesFile(shared.JoinPath(topDir, usedVariablesFile), configuration)
+	}
+}
+
+func writeUsedVariablesFile(path string, configuration android.Config) {
+	data, err := shared.EnvFileContents(configuration.EnvDeps())
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "error writing used variables file %s: %s", path, err)
+		os.Exit(1)
+	}
+
+	err = ioutil.WriteFile(path, data, 0666)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "error writing used variables file %s: %s", path, err)
+		os.Exit(1)
+	}
 }
 
 // Run Soong in the bp2build mode. This creates a standalone context that registers
 // an alternate pipeline of mutators and singletons specifically for generating
 // Bazel BUILD files instead of Ninja files.
-func runBp2Build(srcDir string, configuration android.Config) {
+func runBp2Build(srcDir string, configuration android.Config, extraNinjaDeps []string) {
 	// Register an alternate set of singletons and mutators for bazel
 	// conversion for Bazel conversion.
 	bp2buildCtx := android.NewContext(configuration)
@@ -198,11 +220,13 @@
 	// configurations or variables, since those will generate different BUILD
 	// files based on how the user has configured their tree.
 	bp2buildCtx.SetModuleListFile(bootstrap.CmdlineModuleListFile())
-	extraNinjaDeps, err := bp2buildCtx.ListModulePaths(srcDir)
+	modulePaths, err := bp2buildCtx.ListModulePaths(srcDir)
 	if err != nil {
 		panic(err)
 	}
 
+	extraNinjaDeps = append(extraNinjaDeps, modulePaths...)
+
 	// Run the loading and analysis pipeline to prepare the graph of regular
 	// Modules parsed from Android.bp files, and the BazelTargetModules mapped
 	// from the regular Modules.
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index bb17e8e..2ee456d 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -36,6 +36,7 @@
 	PrepareForTestWithGenRuleBuildComponents,
 	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
 		ctx.RegisterModuleType("tool", toolFactory)
+		ctx.RegisterModuleType("output", outputProducerFactory)
 	}),
 	android.FixtureMergeMockFs(android.MockFS{
 		"tool":       nil,
@@ -653,6 +654,35 @@
 	android.AssertDeepEquals(t, "srcs", expectedSrcs, gen.properties.Srcs)
 }
 
+func TestGenruleAllowMissingDependencies(t *testing.T) {
+	bp := `
+		output {
+			name: "disabled",
+			enabled: false,
+		}
+
+		genrule {
+			name: "gen",
+			srcs: [
+				":disabled",
+			],
+			out: ["out"],
+			cmd: "cat $(in) > $(out)",
+		}
+       `
+	result := prepareForGenRuleTest.Extend(
+		android.FixtureModifyConfigAndContext(
+			func(config android.Config, ctx *android.TestContext) {
+				config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
+				ctx.SetAllowMissingDependencies(true)
+			})).RunTestWithBp(t, bp)
+
+	gen := result.ModuleForTests("gen", "").Output("out")
+	if gen.Rule != android.ErrorRule {
+		t.Errorf("Expected missing dependency error rule for gen, got %q", gen.Rule.String())
+	}
+}
+
 func TestGenruleWithBazel(t *testing.T) {
 	bp := `
 		genrule {
@@ -697,3 +727,24 @@
 }
 
 var _ android.HostToolProvider = (*testTool)(nil)
+
+type testOutputProducer struct {
+	android.ModuleBase
+	outputFile android.Path
+}
+
+func outputProducerFactory() android.Module {
+	module := &testOutputProducer{}
+	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+	return module
+}
+
+func (t *testOutputProducer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
+}
+
+func (t *testOutputProducer) OutputFiles(tag string) (android.Paths, error) {
+	return android.Paths{t.outputFile}, nil
+}
+
+var _ android.OutputFileProducer = (*testOutputProducer)(nil)
diff --git a/rust/clippy_test.go b/rust/clippy_test.go
index e24f666..e90564f 100644
--- a/rust/clippy_test.go
+++ b/rust/clippy_test.go
@@ -66,7 +66,7 @@
 	for _, tc := range clippyLintTests {
 		t.Run("path="+tc.modulePath, func(t *testing.T) {
 
-			config := android.TestArchConfig(buildDir, nil, bp, fs)
+			config := android.TestArchConfig(t.TempDir(), nil, bp, fs)
 			ctx := CreateTestContext(config)
 			ctx.Register()
 			_, errs := ctx.ParseFileList(".", []string{tc.modulePath + "Android.bp"})
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index 2b40727..3ed086f 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -153,7 +153,7 @@
 	for _, tc := range lintTests {
 		t.Run("path="+tc.modulePath, func(t *testing.T) {
 
-			config := android.TestArchConfig(buildDir, nil, bp, fs)
+			config := android.TestArchConfig(t.TempDir(), nil, bp, fs)
 			ctx := CreateTestContext(config)
 			ctx.Register()
 			_, errs := ctx.ParseFileList(".", []string{tc.modulePath + "Android.bp"})
diff --git a/rust/project_json_test.go b/rust/project_json_test.go
index 289bcb8..8f64f56 100644
--- a/rust/project_json_test.go
+++ b/rust/project_json_test.go
@@ -27,15 +27,14 @@
 // testProjectJson run the generation of rust-project.json. It returns the raw
 // content of the generated file.
 func testProjectJson(t *testing.T, bp string) []byte {
-	tctx := newTestRustCtx(t, bp)
-	tctx.env = map[string]string{"SOONG_GEN_RUST_PROJECT": "1"}
-	tctx.generateConfig()
-	tctx.parse(t)
+	result := prepareForRustTest.
+		Extend(android.FixtureMergeEnv(map[string]string{"SOONG_GEN_RUST_PROJECT": "1"})).
+		RunTestWithBp(t, bp)
 
 	// The JSON file is generated via WriteFileToOutputDir. Therefore, it
 	// won't appear in the Output of the TestingSingleton. Manually verify
 	// it exists.
-	content, err := ioutil.ReadFile(filepath.Join(buildDir, rustProjectJsonFileName))
+	content, err := ioutil.ReadFile(filepath.Join(result.Config.BuildDir(), rustProjectJsonFileName))
 	if err != nil {
 		t.Errorf("rust-project.json has not been generated")
 	}
diff --git a/rust/protobuf_test.go b/rust/protobuf_test.go
index 1ac66f3..f0f5ec0 100644
--- a/rust/protobuf_test.go
+++ b/rust/protobuf_test.go
@@ -101,7 +101,7 @@
 	}
 
 	// Check that we're including the exported directory from libprotobuf-cpp-full
-	if w := "-Ilibprotobuf-cpp-full-includes"; !strings.Contains(cmd, w) {
+	if w := "-I" + rustDefaultsDir + "libprotobuf-cpp-full-includes"; !strings.Contains(cmd, w) {
 		t.Errorf("expected %q in %q", w, cmd)
 	}
 
diff --git a/rust/rust_test.go b/rust/rust_test.go
index a0ed534..bed28ec 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -15,7 +15,6 @@
 package rust
 
 import (
-	"io/ioutil"
 	"os"
 	"runtime"
 	"strings"
@@ -24,72 +23,94 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
-	"android/soong/cc"
+	"android/soong/genrule"
 )
 
-var (
-	buildDir string
-)
-
-func setUp() {
-	var err error
-	buildDir, err = ioutil.TempDir("", "soong_rust_test")
-	if err != nil {
-		panic(err)
-	}
-}
-
-func tearDown() {
-	os.RemoveAll(buildDir)
-}
-
 func TestMain(m *testing.M) {
-	run := func() int {
-		setUp()
-		defer tearDown()
+	os.Exit(m.Run())
+}
 
-		return m.Run()
-	}
+var prepareForRustTest = android.GroupFixturePreparers(
+	android.PrepareForTestWithArchMutator,
+	android.PrepareForTestWithDefaults,
+	android.PrepareForTestWithPrebuilts,
 
-	os.Exit(run())
+	genrule.PrepareForTestWithGenRuleBuildComponents,
+
+	PrepareForIntegrationTestWithRust,
+)
+
+var rustMockedFiles = android.MockFS{
+	"foo.rs":          nil,
+	"foo.c":           nil,
+	"src/bar.rs":      nil,
+	"src/any.h":       nil,
+	"proto.proto":     nil,
+	"proto/buf.proto": nil,
+	"buf.proto":       nil,
+	"foo.proto":       nil,
+	"liby.so":         nil,
+	"libz.so":         nil,
+	"data.txt":        nil,
 }
 
 // testRust returns a TestContext in which a basic environment has been setup.
-// This environment contains a few mocked files. See testRustCtx.useMockedFs
-// for the list of these files.
+// This environment contains a few mocked files. See rustMockedFiles for the list of these files.
 func testRust(t *testing.T, bp string) *android.TestContext {
-	tctx := newTestRustCtx(t, bp)
-	tctx.useMockedFs()
-	tctx.generateConfig()
-	return tctx.parse(t)
+	skipTestIfOsNotSupported(t)
+	result := android.GroupFixturePreparers(
+		prepareForRustTest,
+		rustMockedFiles.AddToFixture(),
+	).
+		RunTestWithBp(t, bp)
+	return result.TestContext
 }
 
 func testRustVndk(t *testing.T, bp string) *android.TestContext {
-	tctx := newTestRustCtx(t, bp)
-	tctx.useMockedFs()
-	tctx.generateConfig()
-	tctx.setVndk(t)
-	return tctx.parse(t)
+	skipTestIfOsNotSupported(t)
+	result := android.GroupFixturePreparers(
+		prepareForRustTest,
+		rustMockedFiles.AddToFixture(),
+		android.FixtureModifyProductVariables(
+			func(variables android.FixtureProductVariables) {
+				variables.DeviceVndkVersion = StringPtr("current")
+				variables.ProductVndkVersion = StringPtr("current")
+				variables.Platform_vndk_version = StringPtr("VER")
+			},
+		),
+	).RunTestWithBp(t, bp)
+	return result.TestContext
 }
 
 // testRustCov returns a TestContext in which a basic environment has been
 // setup. This environment explicitly enables coverage.
 func testRustCov(t *testing.T, bp string) *android.TestContext {
-	tctx := newTestRustCtx(t, bp)
-	tctx.useMockedFs()
-	tctx.generateConfig()
-	tctx.enableCoverage(t)
-	return tctx.parse(t)
+	skipTestIfOsNotSupported(t)
+	result := android.GroupFixturePreparers(
+		prepareForRustTest,
+		rustMockedFiles.AddToFixture(),
+		android.FixtureModifyProductVariables(
+			func(variables android.FixtureProductVariables) {
+				variables.ClangCoverage = proptools.BoolPtr(true)
+				variables.Native_coverage = proptools.BoolPtr(true)
+				variables.NativeCoveragePaths = []string{"*"}
+			},
+		),
+	).RunTestWithBp(t, bp)
+	return result.TestContext
 }
 
 // testRustError ensures that at least one error was raised and its value
 // matches the pattern provided. The error can be either in the parsing of the
 // Blueprint or when generating the build actions.
 func testRustError(t *testing.T, pattern string, bp string) {
-	tctx := newTestRustCtx(t, bp)
-	tctx.useMockedFs()
-	tctx.generateConfig()
-	tctx.parseError(t, pattern)
+	skipTestIfOsNotSupported(t)
+	android.GroupFixturePreparers(
+		prepareForRustTest,
+		rustMockedFiles.AddToFixture(),
+	).
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
+		RunTestWithBp(t, bp)
 }
 
 // testRustCtx is used to build a particular test environment. Unless your
@@ -102,99 +123,11 @@
 	config *android.Config
 }
 
-// newTestRustCtx returns a new testRustCtx for the Blueprint definition argument.
-func newTestRustCtx(t *testing.T, bp string) *testRustCtx {
+func skipTestIfOsNotSupported(t *testing.T) {
 	// TODO (b/140435149)
 	if runtime.GOOS != "linux" {
 		t.Skip("Rust Soong tests can only be run on Linux hosts currently")
 	}
-	return &testRustCtx{bp: bp}
-}
-
-// useMockedFs setup a default mocked filesystem for the test environment.
-func (tctx *testRustCtx) useMockedFs() {
-	tctx.fs = map[string][]byte{
-		"foo.rs":          nil,
-		"foo.c":           nil,
-		"src/bar.rs":      nil,
-		"src/any.h":       nil,
-		"proto.proto":     nil,
-		"proto/buf.proto": nil,
-		"buf.proto":       nil,
-		"foo.proto":       nil,
-		"liby.so":         nil,
-		"libz.so":         nil,
-		"data.txt":        nil,
-	}
-}
-
-// generateConfig creates the android.Config based on the bp, fs and env
-// attributes of the testRustCtx.
-func (tctx *testRustCtx) generateConfig() {
-	tctx.bp = tctx.bp + GatherRequiredDepsForTest()
-	tctx.bp = tctx.bp + cc.GatherRequiredDepsForTest(android.NoOsType)
-	cc.GatherRequiredFilesForTest(tctx.fs)
-	config := android.TestArchConfig(buildDir, tctx.env, tctx.bp, tctx.fs)
-	tctx.config = &config
-}
-
-// enableCoverage configures the test to enable coverage.
-func (tctx *testRustCtx) enableCoverage(t *testing.T) {
-	if tctx.config == nil {
-		t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
-	}
-	tctx.config.TestProductVariables.ClangCoverage = proptools.BoolPtr(true)
-	tctx.config.TestProductVariables.Native_coverage = proptools.BoolPtr(true)
-	tctx.config.TestProductVariables.NativeCoveragePaths = []string{"*"}
-}
-
-func (tctx *testRustCtx) setVndk(t *testing.T) {
-	if tctx.config == nil {
-		t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
-	}
-	tctx.config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	tctx.config.TestProductVariables.ProductVndkVersion = StringPtr("current")
-	tctx.config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
-}
-
-// parse validates the configuration and parses the Blueprint file. It returns
-// a TestContext which can be used to retrieve the generated modules via
-// ModuleForTests.
-func (tctx testRustCtx) parse(t *testing.T) *android.TestContext {
-	if tctx.config == nil {
-		t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
-	}
-	ctx := CreateTestContext(*tctx.config)
-	ctx.Register()
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(*tctx.config)
-	android.FailIfErrored(t, errs)
-	return ctx
-}
-
-// parseError parses the Blueprint file and ensure that at least one error
-// matching the provided pattern is observed.
-func (tctx testRustCtx) parseError(t *testing.T, pattern string) {
-	if tctx.config == nil {
-		t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
-	}
-	ctx := CreateTestContext(*tctx.config)
-	ctx.Register()
-
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	if len(errs) > 0 {
-		android.FailIfNoMatchingErrors(t, pattern, errs)
-		return
-	}
-
-	_, errs = ctx.PrepareBuildActions(*tctx.config)
-	if len(errs) > 0 {
-		android.FailIfNoMatchingErrors(t, pattern, errs)
-		return
-	}
-
-	t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
 }
 
 // Test that we can extract the link path from a lib path.
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index 1fabd92..45ccd04 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -112,9 +112,15 @@
 
 func stopRBE(ctx Context, config Config) {
 	cmd := Command(ctx, config, "stopRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd), "-shutdown")
-	if output, err := cmd.CombinedOutput(); err != nil {
+	output, err := cmd.CombinedOutput()
+	if err != nil {
 		ctx.Fatalf("rbe bootstrap with shutdown failed with: %v\n%s\n", err, output)
 	}
+
+	if len(output) > 0 {
+		fmt.Fprintln(ctx.Writer, "")
+		fmt.Fprintln(ctx.Writer, fmt.Sprintf("%s", output))
+	}
 }
 
 // DumpRBEMetrics creates a metrics protobuf file containing RBE related metrics.