Merge "Be more explicit about headers in cc_library_static targets."
diff --git a/apex/apex_test.go b/apex/apex_test.go
index bdff41e..c507fb0 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -6368,8 +6368,7 @@
 }
 
 func TestAppSetBundlePrebuilt(t *testing.T) {
-	ctx := testApex(t, "", android.FixtureModifyMockFS(func(fs android.MockFS) {
-		bp := `
+	bp := `
 		apex_set {
 			name: "myapex",
 			filename: "foo_v2.apex",
@@ -6377,24 +6376,23 @@
 				none: { set: "myapex.apks", },
 				hwaddress: { set: "myapex.hwasan.apks", },
 			},
-		}`
-		fs["Android.bp"] = []byte(bp)
-	}),
-		prepareForTestWithSantitizeHwaddress,
-	)
+		}
+	`
+	ctx := testApex(t, bp, prepareForTestWithSantitizeHwaddress)
 
-	m := ctx.ModuleForTests("myapex", "android_common")
-	extractedApex := m.Output("out/soong/.intermediates/myapex/android_common/foo_v2.apex")
+	// Check that the extractor produces the correct output file from the correct input file.
+	extractorOutput := "out/soong/.intermediates/myapex.apex.extractor/android_common/extracted/myapex.hwasan.apks"
 
-	actual := extractedApex.Inputs
-	if len(actual) != 1 {
-		t.Errorf("expected a single input")
-	}
+	m := ctx.ModuleForTests("myapex.apex.extractor", "android_common")
+	extractedApex := m.Output(extractorOutput)
 
-	expected := "myapex.hwasan.apks"
-	if actual[0].String() != expected {
-		t.Errorf("expected %s, got %s", expected, actual[0].String())
-	}
+	android.AssertArrayString(t, "extractor input", []string{"myapex.hwasan.apks"}, extractedApex.Inputs.Strings())
+
+	// Ditto for the apex.
+	m = ctx.ModuleForTests("myapex", "android_common")
+	copiedApex := m.Output("out/soong/.intermediates/myapex/android_common/foo_v2.apex")
+
+	android.AssertStringEquals(t, "myapex input", extractorOutput, copiedApex.Input.String())
 }
 
 func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) {
@@ -7030,10 +7028,10 @@
 		}),
 	)
 
-	m := ctx.ModuleForTests("myapex", "android_common")
+	m := ctx.ModuleForTests("myapex.apex.extractor", "android_common")
 
 	// Check extract_apks tool parameters.
-	extractedApex := m.Output("out/soong/.intermediates/myapex/android_common/foo_v2.apex")
+	extractedApex := m.Output("extracted/myapex.apks")
 	actual := extractedApex.Args["abis"]
 	expected := "ARMEABI_V7A,ARM64_V8A"
 	if actual != expected {
@@ -7045,6 +7043,7 @@
 		t.Errorf("Unexpected abis parameter - expected %q vs actual %q", expected, actual)
 	}
 
+	m = ctx.ModuleForTests("myapex", "android_common")
 	a := m.Module().(*ApexSet)
 	expectedOverrides := []string{"foo"}
 	actualOverrides := android.AndroidMkEntriesForTest(t, ctx, a)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 9d84281..c8a0c0b 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -48,6 +48,9 @@
 type prebuiltCommon struct {
 	prebuilt   android.Prebuilt
 	properties prebuiltCommonProperties
+
+	deapexerProperties     DeapexerProperties
+	selectedApexProperties SelectedApexProperties
 }
 
 type sanitizedPrebuilt interface {
@@ -91,6 +94,96 @@
 	return false
 }
 
+func (p *prebuiltCommon) deapexerDeps(ctx android.BottomUpMutatorContext) {
+	// Add dependencies onto the java modules that represent the java libraries that are provided by
+	// and exported from this prebuilt apex.
+	for _, lib := range p.deapexerProperties.Exported_java_libs {
+		dep := prebuiltApexExportedModuleName(ctx, lib)
+		ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), exportedJavaLibTag, dep)
+	}
+}
+
+// apexInfoMutator marks any modules for which this apex exports a file as requiring an apex
+// specific variant and checks that they are supported.
+//
+// The apexMutator will ensure that the ApexInfo objects passed to BuildForApex(ApexInfo) are
+// associated with the apex specific variant using the ApexInfoProvider for later retrieval.
+//
+// Unlike the source apex module type the prebuilt_apex module type cannot share compatible variants
+// across prebuilt_apex modules. That is because there is no way to determine whether two
+// prebuilt_apex modules that export files for the same module are compatible. e.g. they could have
+// been built from different source at different times or they could have been built with different
+// build options that affect the libraries.
+//
+// While it may be possible to provide sufficient information to determine whether two prebuilt_apex
+// modules were compatible it would be a lot of work and would not provide much benefit for a couple
+// of reasons:
+// * The number of prebuilt_apex modules that will be exporting files for the same module will be
+//   low as the prebuilt_apex only exports files for the direct dependencies that require it and
+//   very few modules are direct dependencies of multiple prebuilt_apex modules, e.g. there are a
+//   few com.android.art* apex files that contain the same contents and could export files for the
+//   same modules but only one of them needs to do so. Contrast that with source apex modules which
+//   need apex specific variants for every module that contributes code to the apex, whether direct
+//   or indirect.
+// * The build cost of a prebuilt_apex variant is generally low as at worst it will involve some
+//   extra copying of files. Contrast that with source apex modules that has to build each variant
+//   from source.
+func (p *prebuiltCommon) apexInfoMutator(mctx android.TopDownMutatorContext) {
+
+	// Collect direct dependencies into contents.
+	contents := make(map[string]android.ApexMembership)
+
+	// Collect the list of dependencies.
+	var dependencies []android.ApexModule
+	mctx.VisitDirectDeps(func(m android.Module) {
+		tag := mctx.OtherModuleDependencyTag(m)
+		if tag == exportedJavaLibTag {
+			depName := mctx.OtherModuleName(m)
+
+			// It is an error if the other module is not a prebuilt.
+			if _, ok := m.(android.PrebuiltInterface); !ok {
+				mctx.PropertyErrorf("exported_java_libs", "%q is not a prebuilt module", depName)
+				return
+			}
+
+			// It is an error if the other module is not an ApexModule.
+			if _, ok := m.(android.ApexModule); !ok {
+				mctx.PropertyErrorf("exported_java_libs", "%q is not usable within an apex", depName)
+				return
+			}
+
+			// Strip off the prebuilt_ prefix if present before storing content to ensure consistent
+			// behavior whether there is a corresponding source module present or not.
+			depName = android.RemoveOptionalPrebuiltPrefix(depName)
+
+			// Remember that this module was added as a direct dependency.
+			contents[depName] = contents[depName].Add(true)
+
+			// Add the module to the list of dependencies that need to have an APEX variant.
+			dependencies = append(dependencies, m.(android.ApexModule))
+		}
+	})
+
+	// Create contents for the prebuilt_apex and store it away for later use.
+	apexContents := android.NewApexContents(contents)
+	mctx.SetProvider(ApexBundleInfoProvider, ApexBundleInfo{
+		Contents: apexContents,
+	})
+
+	// Create an ApexInfo for the prebuilt_apex.
+	apexInfo := android.ApexInfo{
+		ApexVariationName: mctx.ModuleName(),
+		InApexes:          []string{mctx.ModuleName()},
+		ApexContents:      []*android.ApexContents{apexContents},
+		ForPrebuiltApex:   true,
+	}
+
+	// Mark the dependencies of this module as requiring a variant for this module.
+	for _, am := range dependencies {
+		am.BuildForApex(apexInfo)
+	}
+}
+
 // prebuiltApexSelectorModule is a private module type that is only created by the prebuilt_apex
 // module. It selects the apex to use and makes it available for use by prebuilt_apex and the
 // deapexer.
@@ -197,7 +290,6 @@
 
 type PrebuiltProperties struct {
 	ApexFileProperties
-	DeapexerProperties
 
 	Installable *bool
 	// Optional name for the installed apex. If unspecified, name of the
@@ -267,7 +359,7 @@
 //
 func PrebuiltFactory() android.Module {
 	module := &Prebuilt{}
-	module.AddProperties(&module.properties, &module.selectedApexProperties)
+	module.AddProperties(&module.properties, &module.deapexerProperties, &module.selectedApexProperties)
 	android.InitSingleSourcePrebuiltModule(module, &module.selectedApexProperties, "Selected_apex")
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 
@@ -278,8 +370,8 @@
 		createApexSelectorModule(ctx, apexSelectorModuleName, &module.properties.ApexFileProperties)
 
 		apexFileSource := ":" + apexSelectorModuleName
-		if len(module.properties.Exported_java_libs) != 0 {
-			createDeapexerModule(ctx, deapexerModuleName(baseModuleName), apexFileSource, &module.properties.DeapexerProperties)
+		if len(module.deapexerProperties.Exported_java_libs) != 0 {
+			createDeapexerModule(ctx, deapexerModuleName(baseModuleName), apexFileSource, &module.deapexerProperties)
 		}
 
 		// Add a source reference to retrieve the selected apex from the selector module.
@@ -363,95 +455,13 @@
 )
 
 func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
-	// Add dependencies onto the java modules that represent the java libraries that are provided by
-	// and exported from this prebuilt apex.
-	for _, lib := range p.properties.Exported_java_libs {
-		dep := prebuiltApexExportedModuleName(ctx, lib)
-		ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), exportedJavaLibTag, dep)
-	}
+	p.deapexerDeps(ctx)
 }
 
 var _ ApexInfoMutator = (*Prebuilt)(nil)
 
-// ApexInfoMutator marks any modules for which this apex exports a file as requiring an apex
-// specific variant and checks that they are supported.
-//
-// The apexMutator will ensure that the ApexInfo objects passed to BuildForApex(ApexInfo) are
-// associated with the apex specific variant using the ApexInfoProvider for later retrieval.
-//
-// Unlike the source apex module type the prebuilt_apex module type cannot share compatible variants
-// across prebuilt_apex modules. That is because there is no way to determine whether two
-// prebuilt_apex modules that export files for the same module are compatible. e.g. they could have
-// been built from different source at different times or they could have been built with different
-// build options that affect the libraries.
-//
-// While it may be possible to provide sufficient information to determine whether two prebuilt_apex
-// modules were compatible it would be a lot of work and would not provide much benefit for a couple
-// of reasons:
-// * The number of prebuilt_apex modules that will be exporting files for the same module will be
-//   low as the prebuilt_apex only exports files for the direct dependencies that require it and
-//   very few modules are direct dependencies of multiple prebuilt_apex modules, e.g. there are a
-//   few com.android.art* apex files that contain the same contents and could export files for the
-//   same modules but only one of them needs to do so. Contrast that with source apex modules which
-//   need apex specific variants for every module that contributes code to the apex, whether direct
-//   or indirect.
-// * The build cost of a prebuilt_apex variant is generally low as at worst it will involve some
-//   extra copying of files. Contrast that with source apex modules that has to build each variant
-//   from source.
 func (p *Prebuilt) ApexInfoMutator(mctx android.TopDownMutatorContext) {
-
-	// Collect direct dependencies into contents.
-	contents := make(map[string]android.ApexMembership)
-
-	// Collect the list of dependencies.
-	var dependencies []android.ApexModule
-	mctx.VisitDirectDeps(func(m android.Module) {
-		tag := mctx.OtherModuleDependencyTag(m)
-		if tag == exportedJavaLibTag {
-			depName := mctx.OtherModuleName(m)
-
-			// It is an error if the other module is not a prebuilt.
-			if _, ok := m.(android.PrebuiltInterface); !ok {
-				mctx.PropertyErrorf("exported_java_libs", "%q is not a prebuilt module", depName)
-				return
-			}
-
-			// It is an error if the other module is not an ApexModule.
-			if _, ok := m.(android.ApexModule); !ok {
-				mctx.PropertyErrorf("exported_java_libs", "%q is not usable within an apex", depName)
-				return
-			}
-
-			// Strip off the prebuilt_ prefix if present before storing content to ensure consistent
-			// behavior whether there is a corresponding source module present or not.
-			depName = android.RemoveOptionalPrebuiltPrefix(depName)
-
-			// Remember that this module was added as a direct dependency.
-			contents[depName] = contents[depName].Add(true)
-
-			// Add the module to the list of dependencies that need to have an APEX variant.
-			dependencies = append(dependencies, m.(android.ApexModule))
-		}
-	})
-
-	// Create contents for the prebuilt_apex and store it away for later use.
-	apexContents := android.NewApexContents(contents)
-	mctx.SetProvider(ApexBundleInfoProvider, ApexBundleInfo{
-		Contents: apexContents,
-	})
-
-	// Create an ApexInfo for the prebuilt_apex.
-	apexInfo := android.ApexInfo{
-		ApexVariationName: mctx.ModuleName(),
-		InApexes:          []string{mctx.ModuleName()},
-		ApexContents:      []*android.ApexContents{apexContents},
-		ForPrebuiltApex:   true,
-	}
-
-	// Mark the dependencies of this module as requiring a variant for this module.
-	for _, am := range dependencies {
-		am.BuildForApex(apexInfo)
-	}
+	p.apexInfoMutator(mctx)
 }
 
 func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -505,6 +515,49 @@
 	}}
 }
 
+// prebuiltApexExtractorModule is a private module type that is only created by the prebuilt_apex
+// module. It extracts the correct apex to use and makes it available for use by apex_set.
+type prebuiltApexExtractorModule struct {
+	android.ModuleBase
+
+	properties ApexExtractorProperties
+
+	extractedApex android.WritablePath
+}
+
+func privateApexExtractorModuleFactory() android.Module {
+	module := &prebuiltApexExtractorModule{}
+	module.AddProperties(
+		&module.properties,
+	)
+	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	return module
+}
+
+func (p *prebuiltApexExtractorModule) Srcs() android.Paths {
+	return android.Paths{p.extractedApex}
+}
+
+func (p *prebuiltApexExtractorModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	srcsSupplier := func(ctx android.BaseModuleContext, prebuilt android.Module) []string {
+		return p.properties.prebuiltSrcs(ctx)
+	}
+	apexSet := android.SingleSourcePathFromSupplier(ctx, srcsSupplier, "set")
+	p.extractedApex = android.PathForModuleOut(ctx, "extracted", apexSet.Base())
+	ctx.Build(pctx,
+		android.BuildParams{
+			Rule:        extractMatchingApex,
+			Description: "Extract an apex from an apex set",
+			Inputs:      android.Paths{apexSet},
+			Output:      p.extractedApex,
+			Args: map[string]string{
+				"abis":              strings.Join(java.SupportedAbis(ctx), ","),
+				"allow-prereleased": strconv.FormatBool(proptools.Bool(p.properties.Prerelease)),
+				"sdk-version":       ctx.Config().PlatformSdkVersion().String(),
+			},
+		})
+}
+
 type ApexSet struct {
 	android.ModuleBase
 	prebuiltCommon
@@ -523,7 +576,7 @@
 	postInstallCommands []string
 }
 
-type ApexSetProperties struct {
+type ApexExtractorProperties struct {
 	// the .apks file path that contains prebuilt apex files to be extracted.
 	Set *string
 
@@ -539,6 +592,37 @@
 		}
 	}
 
+	// apexes in this set use prerelease SDK version
+	Prerelease *bool
+}
+
+func (e *ApexExtractorProperties) prebuiltSrcs(ctx android.BaseModuleContext) []string {
+	var srcs []string
+	if e.Set != nil {
+		srcs = append(srcs, *e.Set)
+	}
+
+	var sanitizers []string
+	if ctx.Host() {
+		sanitizers = ctx.Config().SanitizeHost()
+	} else {
+		sanitizers = ctx.Config().SanitizeDevice()
+	}
+
+	if android.InList("address", sanitizers) && e.Sanitized.Address.Set != nil {
+		srcs = append(srcs, *e.Sanitized.Address.Set)
+	} else if android.InList("hwaddress", sanitizers) && e.Sanitized.Hwaddress.Set != nil {
+		srcs = append(srcs, *e.Sanitized.Hwaddress.Set)
+	} else if e.Sanitized.None.Set != nil {
+		srcs = append(srcs, *e.Sanitized.None.Set)
+	}
+
+	return srcs
+}
+
+type ApexSetProperties struct {
+	ApexExtractorProperties
+
 	// whether the extracted apex file installable.
 	Installable *bool
 
@@ -552,33 +636,6 @@
 	// binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
 	// from PRODUCT_PACKAGES.
 	Overrides []string
-
-	// apexes in this set use prerelease SDK version
-	Prerelease *bool
-}
-
-func (a *ApexSet) prebuiltSrcs(ctx android.BaseModuleContext) []string {
-	var srcs []string
-	if a.properties.Set != nil {
-		srcs = append(srcs, *a.properties.Set)
-	}
-
-	var sanitizers []string
-	if ctx.Host() {
-		sanitizers = ctx.Config().SanitizeHost()
-	} else {
-		sanitizers = ctx.Config().SanitizeDevice()
-	}
-
-	if android.InList("address", sanitizers) && a.properties.Sanitized.Address.Set != nil {
-		srcs = append(srcs, *a.properties.Sanitized.Address.Set)
-	} else if android.InList("hwaddress", sanitizers) && a.properties.Sanitized.Hwaddress.Set != nil {
-		srcs = append(srcs, *a.properties.Sanitized.Hwaddress.Set)
-	} else if a.properties.Sanitized.None.Set != nil {
-		srcs = append(srcs, *a.properties.Sanitized.None.Set)
-	}
-
-	return srcs
 }
 
 func (a *ApexSet) hasSanitizedSource(sanitizer string) bool {
@@ -611,15 +668,41 @@
 // prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
 func apexSetFactory() android.Module {
 	module := &ApexSet{}
-	module.AddProperties(&module.properties)
+	module.AddProperties(&module.properties, &module.selectedApexProperties)
 
-	srcsSupplier := func(ctx android.BaseModuleContext, _ android.Module) []string {
-		return module.prebuiltSrcs(ctx)
+	android.InitSingleSourcePrebuiltModule(module, &module.selectedApexProperties, "Selected_apex")
+	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+		baseModuleName := module.BaseModuleName()
+
+		apexExtractorModuleName := apexExtractorModuleName(baseModuleName)
+		createApexExtractorModule(ctx, apexExtractorModuleName, &module.properties.ApexExtractorProperties)
+
+		apexFileSource := ":" + apexExtractorModuleName
+
+		// After passing the arch specific src properties to the creating the apex selector module
+		module.selectedApexProperties.Selected_apex = proptools.StringPtr(apexFileSource)
+	})
+
+	return module
+}
+
+func createApexExtractorModule(ctx android.LoadHookContext, name string, apexExtractorProperties *ApexExtractorProperties) {
+	props := struct {
+		Name *string
+	}{
+		Name: proptools.StringPtr(name),
 	}
 
-	android.InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, "set")
-	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
-	return module
+	ctx.CreateModule(privateApexExtractorModuleFactory,
+		&props,
+		apexExtractorProperties,
+	)
+}
+
+func apexExtractorModuleName(baseModuleName string) string {
+	return baseModuleName + ".apex.extractor"
 }
 
 func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -628,20 +711,13 @@
 		ctx.ModuleErrorf("filename should end in %s for apex_set", imageApexSuffix)
 	}
 
-	apexSet := a.prebuiltCommon.prebuilt.SingleSourcePath(ctx)
+	inputApex := android.OptionalPathForModuleSrc(ctx, a.selectedApexProperties.Selected_apex).Path()
 	a.outputApex = android.PathForModuleOut(ctx, a.installFilename)
-	ctx.Build(pctx,
-		android.BuildParams{
-			Rule:        extractMatchingApex,
-			Description: "Extract an apex from an apex set",
-			Inputs:      android.Paths{apexSet},
-			Output:      a.outputApex,
-			Args: map[string]string{
-				"abis":              strings.Join(java.SupportedAbis(ctx), ","),
-				"allow-prereleased": strconv.FormatBool(proptools.Bool(a.properties.Prerelease)),
-				"sdk-version":       ctx.Config().PlatformSdkVersion().String(),
-			},
-		})
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.Cp,
+		Input:  inputApex,
+		Output: a.outputApex,
+	})
 
 	if a.prebuiltCommon.checkForceDisable(ctx) {
 		a.HideFromMake()
diff --git a/rust/builder.go b/rust/builder.go
index 9d462d4..197c703 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -51,9 +51,12 @@
 			Command: "$envVars $clippyCmd " +
 				// Because clippy-driver uses rustc as backend, we need to have some output even during the linting.
 				// Use the metadata output as it has the smallest footprint.
-				"--emit metadata -o $out $in ${libFlags} " +
-				"$rustcFlags $clippyFlags",
+				"--emit metadata -o $out --emit dep-info=$out.d.raw $in ${libFlags} " +
+				"$rustcFlags $clippyFlags" +
+				" && grep \"^$out:\" $out.d.raw > $out.d",
 			CommandDeps: []string{"$clippyCmd"},
+			Deps:        blueprint.DepsGCC,
+			Depfile:     "$out.d",
 		},
 		"rustcFlags", "libFlags", "clippyFlags", "envVars")
 
diff --git a/bootstrap_test.sh b/tests/bootstrap_test.sh
similarity index 77%
rename from bootstrap_test.sh
rename to tests/bootstrap_test.sh
index 9d87697..b2f7b2b 100755
--- a/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -3,106 +3,13 @@
 # This test exercises the bootstrapping process of the build system
 # in a source tree that only contains enough files for Bazel and Soong to work.
 
-HARDWIRED_MOCK_TOP=
-# Uncomment this to be able to view the source tree after a test is run
-# HARDWIRED_MOCK_TOP=/tmp/td
-
-REAL_TOP="$(readlink -f "$(dirname "$0")"/../..)"
-
-function fail {
-  echo ERROR: $1
-  exit 1
-}
-
-function copy_directory() {
-  local dir="$1"
-  local parent="$(dirname "$dir")"
-
-  mkdir -p "$MOCK_TOP/$parent"
-  cp -R "$REAL_TOP/$dir" "$MOCK_TOP/$parent"
-}
-
-function symlink_file() {
-  local file="$1"
-
-  mkdir -p "$MOCK_TOP/$(dirname "$file")"
-  ln -s "$REAL_TOP/$file" "$MOCK_TOP/$file"
-}
-
-function symlink_directory() {
-  local dir="$1"
-
-  mkdir -p "$MOCK_TOP/$dir"
-  # We need to symlink the contents of the directory individually instead of
-  # using one symlink for the whole directory because finder.go doesn't follow
-  # symlinks when looking for Android.bp files
-  for i in $(ls "$REAL_TOP/$dir"); do
-    local target="$MOCK_TOP/$dir/$i"
-    local source="$REAL_TOP/$dir/$i"
-
-    if [[ -e "$target" ]]; then
-      if [[ ! -d "$source" || ! -d "$target" ]]; then
-        fail "Trying to symlink $dir twice"
-      fi
-    else
-      ln -s "$REAL_TOP/$dir/$i" "$MOCK_TOP/$dir/$i";
-    fi
-  done
-}
-
-function setup_bazel() {
-  copy_directory build/bazel
-
-  symlink_directory prebuilts/bazel
-  symlink_directory prebuilts/jdk
-
-  symlink_file WORKSPACE
-  symlink_file tools/bazel
-}
-
-function setup() {
-  if [[ ! -z "$HARDWIRED_MOCK_TOP" ]]; then
-    MOCK_TOP="$HARDWIRED_MOCK_TOP"
-    rm -fr "$MOCK_TOP"
-    mkdir -p "$MOCK_TOP"
-  else
-    MOCK_TOP=$(mktemp -t -d st.XXXXX)
-    trap 'echo cd / && echo rm -fr "$MOCK_TOP"' EXIT
-  fi
-
-  echo "Test case: ${FUNCNAME[1]}, mock top path: $MOCK_TOP"
-  cd "$MOCK_TOP"
-
-  copy_directory build/blueprint
-  copy_directory build/soong
-
-  symlink_directory prebuilts/go
-  symlink_directory prebuilts/build-tools
-  symlink_directory external/golang-protobuf
-
-  touch "$MOCK_TOP/Android.bp"
-
-  export ALLOW_MISSING_DEPENDENCIES=true
-
-  mkdir -p out/soong
-}
-
-function run_soong() {
-  build/soong/soong_ui.bash --make-mode --skip-ninja --skip-make --skip-soong-tests
-}
+source "$(dirname "$0")/lib.sh"
 
 function test_smoke {
   setup
   run_soong
 }
 
-function test_bazel_smoke {
-  setup
-  setup_bazel
-
-  tools/bazel info
-
-}
 function test_null_build() {
   setup
   run_soong
@@ -410,7 +317,6 @@
   fi
 }
 
-test_bazel_smoke
 test_smoke
 test_null_build
 test_null_build_after_docs
diff --git a/tests/lib.sh b/tests/lib.sh
new file mode 100644
index 0000000..3c97e14
--- /dev/null
+++ b/tests/lib.sh
@@ -0,0 +1,79 @@
+#!/bin/bash -eu
+
+HARDWIRED_MOCK_TOP=
+# Uncomment this to be able to view the source tree after a test is run
+# HARDWIRED_MOCK_TOP=/tmp/td
+
+REAL_TOP="$(readlink -f "$(dirname "$0")"/../../..)"
+
+function fail {
+  echo ERROR: $1
+  exit 1
+}
+
+function copy_directory() {
+  local dir="$1"
+  local parent="$(dirname "$dir")"
+
+  mkdir -p "$MOCK_TOP/$parent"
+  cp -R "$REAL_TOP/$dir" "$MOCK_TOP/$parent"
+}
+
+function symlink_file() {
+  local file="$1"
+
+  mkdir -p "$MOCK_TOP/$(dirname "$file")"
+  ln -s "$REAL_TOP/$file" "$MOCK_TOP/$file"
+}
+
+function symlink_directory() {
+  local dir="$1"
+
+  mkdir -p "$MOCK_TOP/$dir"
+  # We need to symlink the contents of the directory individually instead of
+  # using one symlink for the whole directory because finder.go doesn't follow
+  # symlinks when looking for Android.bp files
+  for i in $(ls "$REAL_TOP/$dir"); do
+    local target="$MOCK_TOP/$dir/$i"
+    local source="$REAL_TOP/$dir/$i"
+
+    if [[ -e "$target" ]]; then
+      if [[ ! -d "$source" || ! -d "$target" ]]; then
+        fail "Trying to symlink $dir twice"
+      fi
+    else
+      ln -s "$REAL_TOP/$dir/$i" "$MOCK_TOP/$dir/$i";
+    fi
+  done
+}
+
+function setup() {
+  if [[ ! -z "$HARDWIRED_MOCK_TOP" ]]; then
+    MOCK_TOP="$HARDWIRED_MOCK_TOP"
+    rm -fr "$MOCK_TOP"
+    mkdir -p "$MOCK_TOP"
+  else
+    MOCK_TOP=$(mktemp -t -d st.XXXXX)
+    trap 'echo cd / && echo rm -fr "$MOCK_TOP"' EXIT
+  fi
+
+  echo "Test case: ${FUNCNAME[1]}, mock top path: $MOCK_TOP"
+  cd "$MOCK_TOP"
+
+  copy_directory build/blueprint
+  copy_directory build/soong
+
+  symlink_directory prebuilts/go
+  symlink_directory prebuilts/build-tools
+  symlink_directory external/golang-protobuf
+
+  touch "$MOCK_TOP/Android.bp"
+
+  export ALLOW_MISSING_DEPENDENCIES=true
+
+  mkdir -p out/soong
+}
+
+function run_soong() {
+  build/soong/soong_ui.bash --make-mode --skip-ninja --skip-make --skip-soong-tests
+}
diff --git a/tests/mixed_mode_test.sh b/tests/mixed_mode_test.sh
new file mode 100755
index 0000000..54f0689
--- /dev/null
+++ b/tests/mixed_mode_test.sh
@@ -0,0 +1,28 @@
+#!/bin/bash -eu
+
+# This test exercises mixed builds where Soong and Bazel cooperate in building
+# Android.
+#
+# When the execroot is deleted, the Bazel server process will automatically
+# terminate itself.
+
+source "$(dirname "$0")/lib.sh"
+
+function setup_bazel() {
+  copy_directory build/bazel
+
+  symlink_directory prebuilts/bazel
+  symlink_directory prebuilts/jdk
+
+  symlink_file WORKSPACE
+  symlink_file tools/bazel
+}
+
+function test_bazel_smoke {
+  setup
+  setup_bazel
+
+  tools/bazel info
+}
+
+test_bazel_smoke
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
new file mode 100755
index 0000000..db24037
--- /dev/null
+++ b/tests/run_integration_tests.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+TOP="$(readlink -f "$(dirname "$0")"/../../..)"
+"$TOP/build/soong/tests/bootstrap_test.sh"
+"$TOP/build/soong/tests/mixed_mode_test.sh"
+