Merge "Skip initHiddenAPI check of prebuilt's boot dex jar for APEX modules"
diff --git a/android/bazel.go b/android/bazel.go
index 1f7f7e6..6e87d57 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -126,6 +126,42 @@
 )
 
 var (
+	// Do not write BUILD files for these directories
+	// NOTE: this is not recursive
+	bp2buildDoNotWriteBuildFileList = []string{
+		// Don't generate these BUILD files - because external BUILD files already exist
+		"external/boringssl",
+		"external/brotli",
+		"external/dagger2",
+		"external/flatbuffers",
+		"external/gflags",
+		"external/google-fruit",
+		"external/grpc-grpc",
+		"external/grpc-grpc/test/core/util",
+		"external/grpc-grpc/test/cpp/common",
+		"external/grpc-grpc/third_party/address_sorting",
+		"external/nanopb-c",
+		"external/nos/host/generic",
+		"external/nos/host/generic/libnos",
+		"external/nos/host/generic/libnos/generator",
+		"external/nos/host/generic/libnos_datagram",
+		"external/nos/host/generic/libnos_transport",
+		"external/nos/host/generic/nugget/proto",
+		"external/perfetto",
+		"external/protobuf",
+		"external/rust/cxx",
+		"external/rust/cxx/demo",
+		"external/ruy",
+		"external/tensorflow",
+		"external/tensorflow/tensorflow/lite",
+		"external/tensorflow/tensorflow/lite/java",
+		"external/tensorflow/tensorflow/lite/kernels",
+		"external/tflite-support",
+		"external/tinyalsa_new",
+		"external/wycheproof",
+		"external/libyuv",
+	}
+
 	// Configure modules in these directories to enable bp2build_available: true or false by default.
 	bp2buildDefaultConfig = Bp2BuildConfig{
 		"bionic":                Bp2BuildDefaultTrueRecursively,
@@ -190,11 +226,16 @@
 	}
 
 	// Used for quicker lookups
-	bp2buildModuleDoNotConvert = map[string]bool{}
-	mixedBuildsDisabled        = map[string]bool{}
+	bp2buildDoNotWriteBuildFile = map[string]bool{}
+	bp2buildModuleDoNotConvert  = map[string]bool{}
+	mixedBuildsDisabled         = map[string]bool{}
 )
 
 func init() {
+	for _, moduleName := range bp2buildDoNotWriteBuildFileList {
+		bp2buildDoNotWriteBuildFile[moduleName] = true
+	}
+
 	for _, moduleName := range bp2buildModuleDoNotConvertList {
 		bp2buildModuleDoNotConvert[moduleName] = true
 	}
@@ -204,6 +245,14 @@
 	}
 }
 
+func ShouldWriteBuildFileForDir(dir string) bool {
+	if _, ok := bp2buildDoNotWriteBuildFile[dir]; ok {
+		return false
+	} else {
+		return true
+	}
+}
+
 // MixedBuildsEnabled checks that a module is ready to be replaced by a
 // converted or handcrafted Bazel target.
 func (b *BazelModuleBase) MixedBuildsEnabled(ctx BazelConversionPathContext) bool {
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index 85f701f..cb25fee 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -11,7 +11,7 @@
 	label := "//foo:bar"
 	arch := Arm64
 	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
-		bazelCommand{command: "cquery", expression: "kind(rule, deps(//:buildroot))"}: `@sourceroot//foo:bar|arm64>>out/foo/bar.txt`,
+		bazelCommand{command: "cquery", expression: "kind(rule, deps(@soong_injection//:buildroot))"}: `//foo:bar|arm64>>out/foo/bar.txt`,
 	})
 	g, ok := bazelContext.GetOutputFiles(label, arch)
 	if ok {
@@ -35,19 +35,19 @@
 	if err != nil {
 		t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
 	}
-	if _, err := os.Stat(filepath.Join(baseDir, "bazel", "main.bzl")); os.IsNotExist(err) {
+	if _, err := os.Stat(filepath.Join(baseDir, "soong_injection", "main.bzl")); os.IsNotExist(err) {
 		t.Errorf("Expected main.bzl to exist, but it does not")
 	} else if err != nil {
 		t.Errorf("Unexpected error stating main.bzl %s", err)
 	}
 
-	if _, err := os.Stat(filepath.Join(baseDir, "bazel", "BUILD.bazel")); os.IsNotExist(err) {
+	if _, err := os.Stat(filepath.Join(baseDir, "soong_injection", "BUILD.bazel")); os.IsNotExist(err) {
 		t.Errorf("Expected BUILD.bazel to exist, but it does not")
 	} else if err != nil {
 		t.Errorf("Unexpected error stating BUILD.bazel %s", err)
 	}
 
-	if _, err := os.Stat(filepath.Join(baseDir, "bazel", "WORKSPACE.bazel")); os.IsNotExist(err) {
+	if _, err := os.Stat(filepath.Join(baseDir, "soong_injection", "WORKSPACE.bazel")); os.IsNotExist(err) {
 		t.Errorf("Expected WORKSPACE.bazel to exist, but it does not")
 	} else if err != nil {
 		t.Errorf("Unexpected error stating WORKSPACE.bazel %s", err)
@@ -56,7 +56,7 @@
 
 func TestInvokeBazelPopulatesBuildStatements(t *testing.T) {
 	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
-		bazelCommand{command: "aquery", expression: "deps(//:buildroot)"}: `
+		bazelCommand{command: "aquery", expression: "deps(@soong_injection//:buildroot)"}: `
 {
   "artifacts": [{
     "id": 1,
@@ -105,7 +105,7 @@
 		outputBase:   "outputbase",
 		workspaceDir: "workspace_dir",
 	}
-	aqueryCommand := bazelCommand{command: "aquery", expression: "deps(//:buildroot)"}
+	aqueryCommand := bazelCommand{command: "aquery", expression: "deps(@soong_injection//:buildroot)"}
 	if _, exists := bazelCommandResults[aqueryCommand]; !exists {
 		bazelCommandResults[aqueryCommand] = "{}\n"
 	}
diff --git a/android/fixture.go b/android/fixture.go
index 5fc668a..fd051a7 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -339,6 +339,15 @@
 	})
 }
 
+// PrepareForDebug_DO_NOT_SUBMIT puts the fixture into debug which will cause it to output its
+// state before running the test.
+//
+// This must only be added temporarily to a test for local debugging and must be removed from the
+// test before submitting.
+var PrepareForDebug_DO_NOT_SUBMIT = newSimpleFixturePreparer(func(fixture *fixture) {
+	fixture.debug = true
+})
+
 // GroupFixturePreparers creates a composite FixturePreparer that is equivalent to applying each of
 // the supplied FixturePreparer instances in order.
 //
@@ -708,6 +717,9 @@
 
 	// The error handler used to check the errors, if any, that are reported.
 	errorHandler FixtureErrorHandler
+
+	// Debug mode status
+	debug bool
 }
 
 func (f *fixture) Config() Config {
@@ -725,6 +737,11 @@
 func (f *fixture) RunTest() *TestResult {
 	f.t.Helper()
 
+	// If in debug mode output the state of the fixture before running the test.
+	if f.debug {
+		f.outputDebugState()
+	}
+
 	ctx := f.ctx
 
 	// Do not use the fixture's mockFS to initialize the config's mock file system if it has been
@@ -769,6 +786,39 @@
 	return result
 }
 
+func (f *fixture) outputDebugState() {
+	fmt.Printf("Begin Fixture State for %s\n", f.t.Name())
+	if len(f.config.env) == 0 {
+		fmt.Printf("  Fixture Env is empty\n")
+	} else {
+		fmt.Printf("  Begin Env\n")
+		for k, v := range f.config.env {
+			fmt.Printf("  - %s=%s\n", k, v)
+		}
+		fmt.Printf("  End Env\n")
+	}
+	if len(f.mockFS) == 0 {
+		fmt.Printf("  Mock FS is empty\n")
+	} else {
+		fmt.Printf("  Begin Mock FS Contents\n")
+		for p, c := range f.mockFS {
+			if c == nil {
+				fmt.Printf("\n  - %s: nil\n", p)
+			} else {
+				contents := string(c)
+				separator := "    ========================================================================"
+				fmt.Printf("  - %s\n%s\n", p, separator)
+				for i, line := range strings.Split(contents, "\n") {
+					fmt.Printf("      %6d:    %s\n", i+1, line)
+				}
+				fmt.Printf("%s\n", separator)
+			}
+		}
+		fmt.Printf("  End Mock FS Contents\n")
+	}
+	fmt.Printf("End Fixture State for %s\n", f.t.Name())
+}
+
 // NormalizePathForTesting removes the test invocation specific build directory from the supplied
 // path.
 //
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index 007d6d8..f1bf648 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -28,7 +28,7 @@
 	outputDir := android.PathForOutput(ctx, "bp2build")
 	android.RemoveAllOutputDir(outputDir)
 
-	buildToTargets, metrics := GenerateBazelTargets(ctx)
+	buildToTargets, metrics := GenerateBazelTargets(ctx, true)
 
 	filesToWrite := CreateBazelFiles(nil, buildToTargets, ctx.mode)
 
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index b7a2810..08790d1 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -176,7 +176,7 @@
 	return attributes
 }
 
-func GenerateBazelTargets(ctx *CodegenContext) (map[string]BazelTargets, CodegenMetrics) {
+func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (map[string]BazelTargets, CodegenMetrics) {
 	buildFileToTargets := make(map[string]BazelTargets)
 	buildFileToAppend := make(map[string]bool)
 
@@ -185,9 +185,13 @@
 		RuleClassCount: make(map[string]int),
 	}
 
+	dirs := make(map[string]bool)
+
 	bpCtx := ctx.Context()
 	bpCtx.VisitAllModules(func(m blueprint.Module) {
 		dir := bpCtx.ModuleDir(m)
+		dirs[dir] = true
+
 		var t BazelTarget
 
 		switch ctx.Mode() {
@@ -230,6 +234,17 @@
 
 		buildFileToTargets[dir] = append(buildFileToTargets[dir], t)
 	})
+	if generateFilegroups {
+		// Add a filegroup target that exposes all sources in the subtree of this package
+		// NOTE: This also means we generate a BUILD file for every Android.bp file (as long as it has at least one module)
+		for dir, _ := range dirs {
+			buildFileToTargets[dir] = append(buildFileToTargets[dir], BazelTarget{
+				name:      "bp2build_all_srcs",
+				content:   `filegroup(name = "bp2build_all_srcs", srcs = glob(["**/*"]))`,
+				ruleClass: "filegroup",
+			})
+		}
+	}
 
 	return buildFileToTargets, metrics
 }
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 6b47cd1..114b668 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -2,6 +2,7 @@
 
 import (
 	"android/soong/android"
+	"fmt"
 	"reflect"
 	"sort"
 	"strings"
@@ -48,6 +49,10 @@
 func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
 	files := make([]BazelFile, 0, len(buildToTargets))
 	for _, dir := range android.SortedStringKeys(buildToTargets) {
+		if !android.ShouldWriteBuildFileForDir(dir) {
+			fmt.Printf("[bp2build] Not writing generated BUILD file for dir: '%s'\n", dir)
+			continue
+		}
 		targets := buildToTargets[dir]
 		sort.Slice(targets, func(i, j int) bool {
 			// this will cover all bp2build generated targets
diff --git a/bp2build/symlink_forest.go b/bp2build/symlink_forest.go
index db7bef8..15a6335 100644
--- a/bp2build/symlink_forest.go
+++ b/bp2build/symlink_forest.go
@@ -94,7 +94,7 @@
 // contain every file in buildFilesDir and srcDir excluding the files in
 // exclude. Collects every directory encountered during the traversal of srcDir
 // into acc.
-func plantSymlinkForestRecursive(topdir string, forestDir string, buildFilesDir string, srcDir string, exclude *node, acc *[]string) {
+func plantSymlinkForestRecursive(topdir string, forestDir string, buildFilesDir string, srcDir string, exclude *node, acc *[]string, okay *bool) {
 	if exclude != nil && exclude.excluded {
 		// This directory is not needed, bail out
 		return
@@ -149,7 +149,7 @@
 			if buildFilesChildEntry.IsDir() && excludeChild != nil {
 				// Not in the source tree, but we have to exclude something from under
 				// this subtree, so descend
-				plantSymlinkForestRecursive(topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc)
+				plantSymlinkForestRecursive(topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
 			} else {
 				// Not in the source tree, symlink BUILD file
 				symlinkIntoForest(topdir, forestChild, buildFilesChild)
@@ -158,20 +158,26 @@
 			if srcChildEntry.IsDir() && excludeChild != nil {
 				// Not in the build file tree, but we have to exclude something from
 				// under this subtree, so descend
-				plantSymlinkForestRecursive(topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc)
+				plantSymlinkForestRecursive(topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
 			} else {
 				// Not in the build file tree, symlink source tree, carry on
 				symlinkIntoForest(topdir, forestChild, srcChild)
 			}
 		} else if srcChildEntry.IsDir() && buildFilesChildEntry.IsDir() {
 			// Both are directories. Descend.
-			plantSymlinkForestRecursive(topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc)
+			plantSymlinkForestRecursive(topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
+		} else if !srcChildEntry.IsDir() && !buildFilesChildEntry.IsDir() {
+			// Neither is a directory. Prioritize BUILD files generated by bp2build
+			// over any BUILD file imported into external/.
+			fmt.Fprintf(os.Stderr, "Both '%s' and '%s' exist, symlinking the former to '%s'\n",
+				buildFilesChild, srcChild, forestChild)
+			symlinkIntoForest(topdir, forestChild, buildFilesChild)
 		} else {
 			// Both exist and one is a file. This is an error.
 			fmt.Fprintf(os.Stderr,
-				"Conflict in workspace symlink tree creation: both '%s' and '%s' exist and at least one of them is a file\n",
+				"Conflict in workspace symlink tree creation: both '%s' and '%s' exist and exactly one is a directory\n",
 				srcChild, buildFilesChild)
-			os.Exit(1)
+			*okay = false
 		}
 	}
 }
@@ -184,6 +190,10 @@
 	deps := make([]string, 0)
 	os.RemoveAll(shared.JoinPath(topdir, forest))
 	excludeTree := treeFromExcludePathList(exclude)
-	plantSymlinkForestRecursive(topdir, forest, buildFiles, srcDir, excludeTree, &deps)
+	okay := true
+	plantSymlinkForestRecursive(topdir, forest, buildFiles, srcDir, excludeTree, &deps, &okay)
+	if !okay {
+		os.Exit(1)
+	}
 	return deps
 }
diff --git a/bp2build/testing.go b/bp2build/testing.go
index ef3a78f..c4661ea 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -183,6 +183,7 @@
 
 // Helper method for tests to easily access the targets in a dir.
 func generateBazelTargetsForDir(codegenCtx *CodegenContext, dir string) BazelTargets {
-	buildFileToTargets, _ := GenerateBazelTargets(codegenCtx)
+	// TODO: Set generateFilegroups to true and/or remove the generateFilegroups argument completely
+	buildFileToTargets, _ := GenerateBazelTargets(codegenCtx, false)
 	return buildFileToTargets[dir]
 }
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index edc8a42..e2ce772 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -27,7 +27,7 @@
 
 	// Ignore metrics reporting for queryview, since queryview is already a full-repo
 	// conversion and can use data from bazel query directly.
-	buildToTargets, _ := bp2build.GenerateBazelTargets(ctx)
+	buildToTargets, _ := bp2build.GenerateBazelTargets(ctx, true)
 
 	filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets, bp2build.QueryView)
 	for _, f := range filesToWrite {
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index e5dba33..ebf541d 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -64,7 +64,7 @@
 func (p *HiddenAPIFlagFileProperties) hiddenAPIFlagFileInfo(ctx android.ModuleContext) hiddenAPIFlagFileInfo {
 	info := hiddenAPIFlagFileInfo{categoryToPaths: map[*hiddenAPIFlagFileCategory]android.Paths{}}
 	for _, category := range hiddenAPIFlagFileCategories {
-		paths := android.PathsForModuleSrc(ctx, category.propertyAccessor(p))
+		paths := android.PathsForModuleSrc(ctx, category.propertyValueReader(p))
 		info.categoryToPaths[category] = paths
 	}
 	return info
@@ -74,9 +74,9 @@
 	// propertyName is the name of the property for this category.
 	propertyName string
 
-	// propertyAccessor retrieves the value of the property for this category from the set of
+	// propertyValueReader retrieves the value of the property for this category from the set of
 	// properties.
-	propertyAccessor func(properties *HiddenAPIFlagFileProperties) []string
+	propertyValueReader func(properties *HiddenAPIFlagFileProperties) []string
 
 	// commandMutator adds the appropriate command line options for this category to the supplied
 	// command
@@ -87,7 +87,7 @@
 	// See HiddenAPIFlagFileProperties.Unsupported
 	{
 		propertyName: "unsupported",
-		propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
 			return properties.Unsupported
 		},
 		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
@@ -97,7 +97,7 @@
 	// See HiddenAPIFlagFileProperties.Removed
 	{
 		propertyName: "removed",
-		propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
 			return properties.Removed
 		},
 		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
@@ -107,7 +107,7 @@
 	// See HiddenAPIFlagFileProperties.Max_target_r_low_priority
 	{
 		propertyName: "max_target_r_low_priority",
-		propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
 			return properties.Max_target_r_low_priority
 		},
 		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
@@ -117,7 +117,7 @@
 	// See HiddenAPIFlagFileProperties.Max_target_q
 	{
 		propertyName: "max_target_q",
-		propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
 			return properties.Max_target_q
 		},
 		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
@@ -127,7 +127,7 @@
 	// See HiddenAPIFlagFileProperties.Max_target_p
 	{
 		propertyName: "max_target_p",
-		propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
 			return properties.Max_target_p
 		},
 		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
@@ -137,7 +137,7 @@
 	// See HiddenAPIFlagFileProperties.Max_target_o_low_priority
 	{
 		propertyName: "max_target_o_low_priority",
-		propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
 			return properties.Max_target_o_low_priority
 		},
 		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
@@ -147,7 +147,7 @@
 	// See HiddenAPIFlagFileProperties.Blocked
 	{
 		propertyName: "blocked",
-		propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
 			return properties.Blocked
 		},
 		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
@@ -157,7 +157,7 @@
 	// See HiddenAPIFlagFileProperties.Unsupported_packages
 	{
 		propertyName: "unsupported_packages",
-		propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
 			return properties.Unsupported_packages
 		},
 		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 8edb7c9..ba0ab93 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -79,7 +79,7 @@
 	// binary must expect arguments in a similar fashion to bindgen, e.g.
 	//
 	// "my_bindgen [flags] wrapper_header.h -o [output_path] -- [clang flags]"
-	Custom_bindgen string `android:"path"`
+	Custom_bindgen string
 }
 
 type bindgenDecorator struct {
diff --git a/rust/compiler.go b/rust/compiler.go
index bfc23b2..a3f02c0 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -77,10 +77,10 @@
 	Lints *string
 
 	// flags to pass to rustc. To enable configuration options or features, use the "cfgs" or "features" properties.
-	Flags []string `android:"path,arch_variant"`
+	Flags []string `android:"arch_variant"`
 
 	// flags to pass to the linker
-	Ld_flags []string `android:"path,arch_variant"`
+	Ld_flags []string `android:"arch_variant"`
 
 	// list of rust rlib crate dependencies
 	Rlibs []string `android:"arch_variant"`
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index 1a39464..3f51114 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -1,5 +1,7 @@
 #!/bin/bash -eu
 
+set -o pipefail
+
 # 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.
 
@@ -609,11 +611,62 @@
   [[ -L out/soong/workspace/a/a2.txt ]] || fail "a/a2.txt not symlinked"
 }
 
+function test_bp2build_build_file_precedence {
+  setup
+
+  mkdir -p a
+  touch a/a.txt
+  touch a/BUILD
+  cat > a/Android.bp <<EOF
+filegroup {
+  name: "a",
+  srcs: ["a.txt"],
+  bazel_module: { bp2build_available: true },
+}
+EOF
+
+  GENERATE_BAZEL_FILES=1 run_soong
+  [[ -L out/soong/workspace/a/BUILD ]] || fail "BUILD file not symlinked"
+  [[ "$(readlink -f out/soong/workspace/a/BUILD)" =~ bp2build/a/BUILD$ ]] \
+    || fail "BUILD files symlinked to the wrong place"
+}
+
+function test_bp2build_reports_multiple_errors {
+  setup
+
+  mkdir -p a/BUILD
+  touch a/a.txt
+  cat > a/Android.bp <<EOF
+filegroup {
+  name: "a",
+  srcs: ["a.txt"],
+  bazel_module: { bp2build_available: true },
+}
+EOF
+
+  mkdir -p b/BUILD
+  touch b/b.txt
+  cat > b/Android.bp <<EOF
+filegroup {
+  name: "b",
+  srcs: ["b.txt"],
+  bazel_module: { bp2build_available: true },
+}
+EOF
+
+  if GENERATE_BAZEL_FILES=1 run_soong >& "$MOCK_TOP/errors"; then
+    fail "Build should have failed"
+  fi
+
+  grep -q "a/BUILD' exist" "$MOCK_TOP/errors" || fail "Error for a/BUILD not found"
+  grep -q "b/BUILD' exist" "$MOCK_TOP/errors" || fail "Error for b/BUILD not found"
+}
+
 test_smoke
 test_null_build
 test_null_build_after_docs
 test_soong_build_rebuilt_if_blueprint_changes
-# test_glob_noop_incremental  # Currently failing
+test_glob_noop_incremental
 test_add_file_to_glob
 test_add_android_bp
 test_change_android_bp
@@ -628,3 +681,5 @@
 test_bp2build_add_to_glob
 test_bp2build_bazel_workspace_structure
 test_bp2build_bazel_workspace_add_file
+test_bp2build_build_file_precedence
+test_bp2build_reports_multiple_errors
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
new file mode 100755
index 0000000..082cd06
--- /dev/null
+++ b/tests/bp2build_bazel_test.sh
@@ -0,0 +1,75 @@
+#!/bin/bash -eu
+
+set -o pipefail
+
+# Test that bp2build and Bazel can play nicely together
+
+source "$(dirname "$0")/lib.sh"
+
+function test_bp2build_generates_all_buildfiles {
+  setup
+  create_mock_bazel
+
+  mkdir -p foo/convertible_soong_module
+  cat > foo/convertible_soong_module/Android.bp <<'EOF'
+genrule {
+    name: "the_answer",
+    cmd: "echo '42' > $(out)",
+    out: [
+        "the_answer.txt",
+    ],
+    bazel_module: {
+        bp2build_available: true,
+    },
+  }
+EOF
+
+  mkdir -p foo/unconvertible_soong_module
+  cat > foo/unconvertible_soong_module/Android.bp <<'EOF'
+genrule {
+    name: "not_the_answer",
+    cmd: "echo '43' > $(out)",
+    out: [
+        "not_the_answer.txt",
+    ],
+    bazel_module: {
+        bp2build_available: false,
+    },
+  }
+EOF
+
+  run_bp2build
+
+  if [[ ! -f "./out/soong/workspace/foo/convertible_soong_module/BUILD" ]]; then
+    fail "./out/soong/workspace/foo/convertible_soong_module/BUILD was not generated"
+  fi
+
+  if [[ ! -f "./out/soong/workspace/foo/unconvertible_soong_module/BUILD" ]]; then
+    fail "./out/soong/workspace/foo/unconvertible_soong_module/BUILD was not generated"
+  fi
+
+  if ! grep "the_answer" "./out/soong/workspace/foo/convertible_soong_module/BUILD"; then
+    fail "missing BUILD target the_answer in convertible_soong_module/BUILD"
+  fi
+
+  if grep "not_the_answer" "./out/soong/workspace/foo/unconvertible_soong_module/BUILD"; then
+    fail "found unexpected BUILD target not_the_answer in unconvertible_soong_module/BUILD"
+  fi
+
+  if ! grep "filegroup" "./out/soong/workspace/foo/unconvertible_soong_module/BUILD"; then
+    fail "missing filegroup in unconvertible_soong_module/BUILD"
+  fi
+
+  # NOTE: We don't actually use the extra BUILD file for anything here
+  run_bazel build --package_path=out/soong/workspace //foo/...
+
+  local the_answer_file="bazel-out/k8-fastbuild/bin/foo/convertible_soong_module/the_answer.txt"
+  if [[ ! -f "${the_answer_file}" ]]; then
+    fail "Expected '${the_answer_file}' to be generated, but was missing"
+  fi
+  if ! grep 42 "${the_answer_file}"; then
+    fail "Expected to find 42 in '${the_answer_file}'"
+  fi
+}
+
+test_bp2build_generates_all_buildfiles
diff --git a/tests/lib.sh b/tests/lib.sh
index 3795dfc..e561a3d 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -1,5 +1,7 @@
 #!/bin/bash -eu
 
+set -o pipefail
+
 HARDWIRED_MOCK_TOP=
 # Uncomment this to be able to view the source tree after a test is run
 # HARDWIRED_MOCK_TOP=/tmp/td
@@ -102,7 +104,25 @@
 }
 
 function run_soong() {
-  build/soong/soong_ui.bash --make-mode --skip-ninja --skip-make --skip-soong-tests
+  build/soong/soong_ui.bash --make-mode --skip-ninja --skip-make --skip-soong-tests "$@"
+}
+
+function create_mock_bazel() {
+  copy_directory build/bazel
+
+  symlink_directory prebuilts/bazel
+  symlink_directory prebuilts/jdk
+
+  symlink_file WORKSPACE
+  symlink_file tools/bazel
+}
+
+run_bazel() {
+  tools/bazel "$@"
+}
+
+run_bp2build() {
+  GENERATE_BAZEL_FILES=true build/soong/soong_ui.bash --make-mode --skip-ninja --skip-make --skip-soong-tests nothing
 }
 
 info "Starting Soong integration test suite $(basename $0)"
diff --git a/tests/mixed_mode_test.sh b/tests/mixed_mode_test.sh
index 7dbafea..80774bf 100755
--- a/tests/mixed_mode_test.sh
+++ b/tests/mixed_mode_test.sh
@@ -1,5 +1,7 @@
 #!/bin/bash -eu
 
+set -o pipefail
+
 # This test exercises mixed builds where Soong and Bazel cooperate in building
 # Android.
 #
@@ -8,21 +10,11 @@
 
 source "$(dirname "$0")/lib.sh"
 
-function create_mock_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
   create_mock_bazel
 
-  tools/bazel info
+  run_bazel info
 }
 
 test_bazel_smoke
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
index 76b324b..8399573 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -1,6 +1,8 @@
 #!/bin/bash -eu
 
+set -o pipefail
+
 TOP="$(readlink -f "$(dirname "$0")"/../../..)"
 "$TOP/build/soong/tests/bootstrap_test.sh"
 "$TOP/build/soong/tests/mixed_mode_test.sh"
-
+"$TOP/build/soong/tests/bp2build_bazel_test.sh"