Always merge build files

Previous behavior:

- Packge not listed in bp2buildKeepExistingBuildFile:
    - Use bp2build generated build file
- Package listed in bp2buildKeepExistingBuildFile:
    - Use handcrafted build file even if there were allowlisted bp2build
      modules in the same package.
- Package listed in bp2buildKeepExistingBuildFile and a soong module has
  a bp2build: { label } attribute:
    - Merge the handcrafted and bp2build generated build files

New behavior:

- Packge not listed in bp2buildKeepExistingBuildFile:
    - Use bp2build generated build file
- Package listed in bp2buildKeepExistingBuildFile:
    - Merge with bp2build generated build file.

Bug: 234167862
Test: ./build/bazel/ci/bp2build.sh
Change-Id: Ifbaf4f8f0f5158b5b2bd6d534eb2311e2e5f399b
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index b0c87d4..88210c0 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -46,19 +46,16 @@
 		"bionic":                                Bp2BuildDefaultTrueRecursively,
 		"bootable/recovery/tools/recovery_l10n": Bp2BuildDefaultTrue,
 
-		"build/bazel/examples/apex/minimal":           Bp2BuildDefaultTrueRecursively,
-		"build/bazel/examples/soong_config_variables": Bp2BuildDefaultTrueRecursively,
-		"build/bazel/examples/python":                 Bp2BuildDefaultTrueRecursively,
-		"build/bazel/examples/gensrcs":                Bp2BuildDefaultTrueRecursively,
-		"build/make/target/product/security":          Bp2BuildDefaultTrue,
-		"build/make/tools/signapk":                    Bp2BuildDefaultTrue,
-		"build/make/tools/zipalign":                   Bp2BuildDefaultTrueRecursively,
-		"build/soong":                                 Bp2BuildDefaultTrue,
-		"build/soong/cc/libbuildversion":              Bp2BuildDefaultTrue, // Skip tests subdir
-		"build/soong/cc/ndkstubgen":                   Bp2BuildDefaultTrue,
-		"build/soong/cc/symbolfile":                   Bp2BuildDefaultTrue,
-		"build/soong/linkerconfig":                    Bp2BuildDefaultTrueRecursively,
-		"build/soong/scripts":                         Bp2BuildDefaultTrueRecursively,
+		"build/bazel":                        Bp2BuildDefaultTrueRecursively,
+		"build/make/target/product/security": Bp2BuildDefaultTrue,
+		"build/make/tools/signapk":           Bp2BuildDefaultTrue,
+		"build/make/tools/zipalign":          Bp2BuildDefaultTrueRecursively,
+		"build/soong":                        Bp2BuildDefaultTrue,
+		"build/soong/cc/libbuildversion":     Bp2BuildDefaultTrue, // Skip tests subdir
+		"build/soong/cc/ndkstubgen":          Bp2BuildDefaultTrue,
+		"build/soong/cc/symbolfile":          Bp2BuildDefaultTrue,
+		"build/soong/linkerconfig":           Bp2BuildDefaultTrueRecursively,
+		"build/soong/scripts":                Bp2BuildDefaultTrueRecursively,
 
 		"cts/common/device-side/nativetesthelper/jni": Bp2BuildDefaultTrueRecursively,
 		"development/apps/DevelopmentSettings":        Bp2BuildDefaultTrue,
@@ -282,24 +279,7 @@
 		// This is actually build/bazel/build.BAZEL symlinked to ./BUILD
 		".":/*recursive = */ false,
 
-		// build/bazel/examples/apex/... BUILD files should be generated, so
-		// build/bazel is not recursive. Instead list each subdirectory under
-		// build/bazel explicitly.
-		"build/bazel":/* recursive = */ false,
-		"build/bazel/ci/dist":/* recursive = */ false,
-		"build/bazel/examples/android_app":/* recursive = */ true,
-		"build/bazel/examples/cc":/* recursive = */ true,
-		"build/bazel/examples/java":/* recursive = */ true,
-		"build/bazel/examples/partitions":/* recursive = */ true,
-		"build/bazel/bazel_skylib":/* recursive = */ true,
-		"build/bazel/rules":/* recursive = */ true,
-		"build/bazel/rules_cc":/* recursive = */ true,
-		"build/bazel/scripts":/* recursive = */ true,
-		"build/bazel/tests":/* recursive = */ true,
-		"build/bazel/platforms":/* recursive = */ true,
-		"build/bazel/product_config":/* recursive = */ true,
-		"build/bazel/product_variables":/* recursive = */ true,
-		"build/bazel/vendor/google":/* recursive = */ true,
+		"build/bazel":/* recursive = */ true,
 		"build/bazel_common_rules":/* recursive = */ true,
 		// build/make/tools/signapk BUILD file is generated, so build/make/tools is not recursive.
 		"build/make/tools":/* recursive = */ false,
diff --git a/android/bazel.go b/android/bazel.go
index 3ae3d6f..15729c1 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -211,7 +211,7 @@
 	return "" // no label for unconverted module
 }
 
-type bp2BuildConversionAllowlist struct {
+type Bp2BuildConversionAllowlist struct {
 	// Configure modules in these directories to enable bp2build_available: true or false by default.
 	defaultConfig allowlists.Bp2BuildConfig
 
@@ -241,14 +241,14 @@
 
 // GenerateCcLibraryStaticOnly returns whether a cc_library module should only
 // generate a static version of itself based on the current global configuration.
-func (a bp2BuildConversionAllowlist) GenerateCcLibraryStaticOnly(moduleName string) bool {
+func (a Bp2BuildConversionAllowlist) GenerateCcLibraryStaticOnly(moduleName string) bool {
 	return a.ccLibraryStaticOnly[moduleName]
 }
 
-// NewBp2BuildAllowlist creates a new, empty bp2BuildConversionAllowlist
+// NewBp2BuildAllowlist creates a new, empty Bp2BuildConversionAllowlist
 // which can be populated using builder pattern Set* methods
-func NewBp2BuildAllowlist() bp2BuildConversionAllowlist {
-	return bp2BuildConversionAllowlist{
+func NewBp2BuildAllowlist() Bp2BuildConversionAllowlist {
+	return Bp2BuildConversionAllowlist{
 		allowlists.Bp2BuildConfig{},
 		map[string]bool{},
 		map[string]bool{},
@@ -259,7 +259,7 @@
 }
 
 // SetDefaultConfig copies the entries from defaultConfig into the allowlist
-func (a bp2BuildConversionAllowlist) SetDefaultConfig(defaultConfig allowlists.Bp2BuildConfig) bp2BuildConversionAllowlist {
+func (a Bp2BuildConversionAllowlist) SetDefaultConfig(defaultConfig allowlists.Bp2BuildConfig) Bp2BuildConversionAllowlist {
 	if a.defaultConfig == nil {
 		a.defaultConfig = allowlists.Bp2BuildConfig{}
 	}
@@ -271,7 +271,7 @@
 }
 
 // SetKeepExistingBuildFile copies the entries from keepExistingBuildFile into the allowlist
-func (a bp2BuildConversionAllowlist) SetKeepExistingBuildFile(keepExistingBuildFile map[string]bool) bp2BuildConversionAllowlist {
+func (a Bp2BuildConversionAllowlist) SetKeepExistingBuildFile(keepExistingBuildFile map[string]bool) Bp2BuildConversionAllowlist {
 	if a.keepExistingBuildFile == nil {
 		a.keepExistingBuildFile = map[string]bool{}
 	}
@@ -283,7 +283,7 @@
 }
 
 // SetModuleAlwaysConvertList copies the entries from moduleAlwaysConvert into the allowlist
-func (a bp2BuildConversionAllowlist) SetModuleAlwaysConvertList(moduleAlwaysConvert []string) bp2BuildConversionAllowlist {
+func (a Bp2BuildConversionAllowlist) SetModuleAlwaysConvertList(moduleAlwaysConvert []string) Bp2BuildConversionAllowlist {
 	if a.moduleAlwaysConvert == nil {
 		a.moduleAlwaysConvert = map[string]bool{}
 	}
@@ -295,7 +295,7 @@
 }
 
 // SetModuleTypeAlwaysConvertList copies the entries from moduleTypeAlwaysConvert into the allowlist
-func (a bp2BuildConversionAllowlist) SetModuleTypeAlwaysConvertList(moduleTypeAlwaysConvert []string) bp2BuildConversionAllowlist {
+func (a Bp2BuildConversionAllowlist) SetModuleTypeAlwaysConvertList(moduleTypeAlwaysConvert []string) Bp2BuildConversionAllowlist {
 	if a.moduleTypeAlwaysConvert == nil {
 		a.moduleTypeAlwaysConvert = map[string]bool{}
 	}
@@ -307,7 +307,7 @@
 }
 
 // SetModuleDoNotConvertList copies the entries from moduleDoNotConvert into the allowlist
-func (a bp2BuildConversionAllowlist) SetModuleDoNotConvertList(moduleDoNotConvert []string) bp2BuildConversionAllowlist {
+func (a Bp2BuildConversionAllowlist) SetModuleDoNotConvertList(moduleDoNotConvert []string) Bp2BuildConversionAllowlist {
 	if a.moduleDoNotConvert == nil {
 		a.moduleDoNotConvert = map[string]bool{}
 	}
@@ -319,7 +319,7 @@
 }
 
 // SetCcLibraryStaticOnlyList copies the entries from ccLibraryStaticOnly into the allowlist
-func (a bp2BuildConversionAllowlist) SetCcLibraryStaticOnlyList(ccLibraryStaticOnly []string) bp2BuildConversionAllowlist {
+func (a Bp2BuildConversionAllowlist) SetCcLibraryStaticOnlyList(ccLibraryStaticOnly []string) Bp2BuildConversionAllowlist {
 	if a.ccLibraryStaticOnly == nil {
 		a.ccLibraryStaticOnly = map[string]bool{}
 	}
@@ -330,33 +330,15 @@
 	return a
 }
 
-var bp2BuildAllowListKey = NewOnceKey("Bp2BuildAllowlist")
-var bp2buildAllowlist OncePer
-
-func GetBp2BuildAllowList() bp2BuildConversionAllowlist {
-	return bp2buildAllowlist.Once(bp2BuildAllowListKey, func() interface{} {
-		return NewBp2BuildAllowlist().SetDefaultConfig(allowlists.Bp2buildDefaultConfig).
-			SetKeepExistingBuildFile(allowlists.Bp2buildKeepExistingBuildFile).
-			SetModuleAlwaysConvertList(allowlists.Bp2buildModuleAlwaysConvertList).
-			SetModuleTypeAlwaysConvertList(allowlists.Bp2buildModuleTypeAlwaysConvertList).
-			SetModuleDoNotConvertList(allowlists.Bp2buildModuleDoNotConvertList).
-			SetCcLibraryStaticOnlyList(allowlists.Bp2buildCcLibraryStaticOnlyList)
-	}).(bp2BuildConversionAllowlist)
-}
-
 // ShouldKeepExistingBuildFileForDir returns whether an existing BUILD file should be
 // added to the build symlink forest based on the current global configuration.
-func ShouldKeepExistingBuildFileForDir(dir string) bool {
-	return shouldKeepExistingBuildFileForDir(GetBp2BuildAllowList(), dir)
-}
-
-func shouldKeepExistingBuildFileForDir(allowlist bp2BuildConversionAllowlist, dir string) bool {
-	if _, ok := allowlist.keepExistingBuildFile[dir]; ok {
+func (a Bp2BuildConversionAllowlist) ShouldKeepExistingBuildFileForDir(dir string) bool {
+	if _, ok := a.keepExistingBuildFile[dir]; ok {
 		// Exact dir match
 		return true
 	}
 	// Check if subtree match
-	for prefix, recursive := range allowlist.keepExistingBuildFile {
+	for prefix, recursive := range a.keepExistingBuildFile {
 		if recursive {
 			if strings.HasPrefix(dir, prefix+"/") {
 				return true
@@ -367,6 +349,20 @@
 	return false
 }
 
+var bp2BuildAllowListKey = NewOnceKey("Bp2BuildAllowlist")
+var bp2buildAllowlist OncePer
+
+func GetBp2BuildAllowList() Bp2BuildConversionAllowlist {
+	return bp2buildAllowlist.Once(bp2BuildAllowListKey, func() interface{} {
+		return NewBp2BuildAllowlist().SetDefaultConfig(allowlists.Bp2buildDefaultConfig).
+			SetKeepExistingBuildFile(allowlists.Bp2buildKeepExistingBuildFile).
+			SetModuleAlwaysConvertList(allowlists.Bp2buildModuleAlwaysConvertList).
+			SetModuleTypeAlwaysConvertList(allowlists.Bp2buildModuleTypeAlwaysConvertList).
+			SetModuleDoNotConvertList(allowlists.Bp2buildModuleDoNotConvertList).
+			SetCcLibraryStaticOnlyList(allowlists.Bp2buildCcLibraryStaticOnlyList)
+	}).(Bp2BuildConversionAllowlist)
+}
+
 // MixedBuildsEnabled returns true if a module is ready to be replaced by a
 // converted or handcrafted Bazel target. As a side effect, calling this
 // method will also log whether this module is mixed build enabled for
@@ -431,7 +427,7 @@
 	}
 
 	moduleName := module.Name()
-	allowlist := ctx.Config().bp2buildPackageConfig
+	allowlist := ctx.Config().Bp2buildPackageConfig
 	moduleNameAllowed := allowlist.moduleAlwaysConvert[moduleName]
 	moduleTypeAllowed := allowlist.moduleTypeAlwaysConvert[ctx.OtherModuleType(module)]
 	allowlistConvert := moduleNameAllowed || moduleTypeAllowed
@@ -447,14 +443,6 @@
 		return false
 	}
 
-	if allowlistConvert && shouldKeepExistingBuildFileForDir(allowlist, packagePath) {
-		if moduleNameAllowed {
-			ctx.ModuleErrorf("A module cannot be in a directory listed in keepExistingBuildFile"+
-				" and also be in moduleAlwaysConvert. Directory: '%s'", packagePath)
-			return false
-		}
-	}
-
 	// This is a tristate value: true, false, or unset.
 	if ok, directoryPath := bp2buildDefaultTrueRecursively(packagePath, allowlist.defaultConfig); ok {
 		if moduleNameAllowed {
diff --git a/android/bazel_test.go b/android/bazel_test.go
index 98f0a46..b578cca 100644
--- a/android/bazel_test.go
+++ b/android/bazel_test.go
@@ -158,7 +158,7 @@
 
 type TestBazelConversionContext struct {
 	omc       bazel.OtherModuleTestContext
-	allowlist bp2BuildConversionAllowlist
+	allowlist Bp2BuildConversionAllowlist
 	errors    []string
 }
 
@@ -183,7 +183,7 @@
 func (bcc *TestBazelConversionContext) Config() Config {
 	return Config{
 		&config{
-			bp2buildPackageConfig: bcc.allowlist,
+			Bp2buildPackageConfig: bcc.allowlist,
 		},
 	}
 }
@@ -202,7 +202,7 @@
 		shouldConvert  bool
 		expectedErrors []string
 		module         TestBazelModule
-		allowlist      bp2BuildConversionAllowlist
+		allowlist      Bp2BuildConversionAllowlist
 	}{
 		{
 			description:   "allowlist enables module",
@@ -215,7 +215,7 @@
 				},
 				BazelModuleBase: bazelableBazelModuleBase,
 			},
-			allowlist: bp2BuildConversionAllowlist{
+			allowlist: Bp2BuildConversionAllowlist{
 				moduleAlwaysConvert: map[string]bool{
 					"foo": true,
 				},
@@ -233,7 +233,7 @@
 				},
 				BazelModuleBase: bazelableBazelModuleBase,
 			},
-			allowlist: bp2BuildConversionAllowlist{
+			allowlist: Bp2BuildConversionAllowlist{
 				moduleAlwaysConvert: map[string]bool{
 					"foo": true,
 				},
@@ -254,7 +254,7 @@
 				},
 				BazelModuleBase: bazelableBazelModuleBase,
 			},
-			allowlist: bp2BuildConversionAllowlist{
+			allowlist: Bp2BuildConversionAllowlist{
 				moduleAlwaysConvert: map[string]bool{
 					"foo": true,
 				},
@@ -264,27 +264,6 @@
 			},
 		},
 		{
-			description:    "module in allowlist and existing BUILD file",
-			shouldConvert:  false,
-			expectedErrors: []string{"A module cannot be in a directory listed in keepExistingBuildFile and also be in moduleAlwaysConvert. Directory: 'existing/build/dir'"},
-			module: TestBazelModule{
-				TestModuleInfo: bazel.TestModuleInfo{
-					ModuleName: "foo",
-					Typ:        "rule1",
-					Dir:        "existing/build/dir",
-				},
-				BazelModuleBase: bazelableBazelModuleBase,
-			},
-			allowlist: bp2BuildConversionAllowlist{
-				moduleAlwaysConvert: map[string]bool{
-					"foo": true,
-				},
-				keepExistingBuildFile: map[string]bool{
-					"existing/build/dir": true,
-				},
-			},
-		},
-		{
 			description:    "module allowlist and enabled directory",
 			shouldConvert:  false,
 			expectedErrors: []string{"A module cannot be in a directory marked Bp2BuildDefaultTrue or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: 'existing/build/dir'"},
@@ -296,7 +275,7 @@
 				},
 				BazelModuleBase: bazelableBazelModuleBase,
 			},
-			allowlist: bp2BuildConversionAllowlist{
+			allowlist: Bp2BuildConversionAllowlist{
 				moduleAlwaysConvert: map[string]bool{
 					"foo": true,
 				},
@@ -317,7 +296,7 @@
 				},
 				BazelModuleBase: bazelableBazelModuleBase,
 			},
-			allowlist: bp2BuildConversionAllowlist{
+			allowlist: Bp2BuildConversionAllowlist{
 				moduleAlwaysConvert: map[string]bool{
 					"foo": true,
 				},
@@ -344,7 +323,7 @@
 					},
 				},
 			},
-			allowlist: bp2BuildConversionAllowlist{
+			allowlist: Bp2BuildConversionAllowlist{
 				moduleAlwaysConvert: map[string]bool{
 					"foo": true,
 				},
diff --git a/android/config.go b/android/config.go
index a07cb6c..745410f 100644
--- a/android/config.go
+++ b/android/config.go
@@ -189,7 +189,7 @@
 	mockBpList string
 
 	BuildMode                      SoongBuildMode
-	bp2buildPackageConfig          bp2BuildConversionAllowlist
+	Bp2buildPackageConfig          Bp2BuildConversionAllowlist
 	Bp2buildSoongConfigDefinitions soongconfig.Bp2BuildSoongConfigDefinitions
 
 	// If testAllowNonExistentPaths is true then PathForSource and PathForModuleSrc won't error
@@ -482,7 +482,7 @@
 
 	config.BuildMode = buildMode
 	config.BazelContext, err = NewBazelContext(config)
-	config.bp2buildPackageConfig = GetBp2BuildAllowList()
+	config.Bp2buildPackageConfig = GetBp2BuildAllowList()
 
 	return Config{config}, err
 }
diff --git a/android/testing.go b/android/testing.go
index ef5e5a9..3708501 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -213,8 +213,8 @@
 	ctx.finalDeps = append(ctx.finalDeps, f)
 }
 
-func (ctx *TestContext) RegisterBp2BuildConfig(config bp2BuildConversionAllowlist) {
-	ctx.config.bp2buildPackageConfig = config
+func (ctx *TestContext) RegisterBp2BuildConfig(config Bp2BuildConversionAllowlist) {
+	ctx.config.Bp2buildPackageConfig = config
 }
 
 // PreArchBp2BuildMutators adds mutators to be register for converting Android Blueprint modules
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 242ea1e..8de6d18 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -373,6 +373,15 @@
 	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)
+		//
+		// This works because: https://bazel.build/reference/be/functions#exports_files
+		// "As a legacy behaviour, also files mentioned as input to a rule are exported with the
+		// default visibility until the flag --incompatible_no_implicit_file_export is flipped. However, this behavior
+		// should not be relied upon and actively migrated away from."
+		//
+		// TODO(b/198619163): We should change this to export_files(glob(["**/*"])) instead, but doing that causes these errors:
+		// "Error in exports_files: generated label '//external/avb:avbtool' conflicts with existing py_binary rule"
+		// So we need to solve all the "target ... is both a rule and a file" warnings first.
 		for dir, _ := range dirs {
 			buildFileToTargets[dir] = append(buildFileToTargets[dir], BazelTarget{
 				name:      "bp2build_all_srcs",
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 8cf9bea..d98b6a3 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -2,7 +2,6 @@
 
 import (
 	"encoding/json"
-	"fmt"
 	"reflect"
 	"strings"
 
@@ -80,21 +79,14 @@
 		files = append(files, newFile(bazelRulesSubDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims)))
 	}
 
-	files = append(files, createBuildFiles(cfg, buildToTargets, mode)...)
+	files = append(files, createBuildFiles(buildToTargets, mode)...)
 
 	return files
 }
 
-func createBuildFiles(cfg android.Config, buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
+func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
 	files := make([]BazelFile, 0, len(buildToTargets))
-	warnNotWriting := cfg.IsEnvTrue("BP2BUILD_VERBOSE")
 	for _, dir := range android.SortedStringKeys(buildToTargets) {
-		if mode == Bp2Build && android.ShouldKeepExistingBuildFileForDir(dir) {
-			if warnNotWriting {
-				fmt.Printf("[bp2build] Not writing generated BUILD file for dir: '%s'\n", dir)
-			}
-			continue
-		}
 		targets := buildToTargets[dir]
 		targets.sort()
 
diff --git a/bp2build/symlink_forest.go b/bp2build/symlink_forest.go
index 78e7b0e..023ec96 100644
--- a/bp2build/symlink_forest.go
+++ b/bp2build/symlink_forest.go
@@ -1,12 +1,27 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package bp2build
 
 import (
-	"android/soong/android"
 	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"regexp"
 
+	"android/soong/android"
 	"android/soong/shared"
 )
 
@@ -58,6 +73,57 @@
 	return result
 }
 
+func mergeBuildFiles(output string, srcBuildFile string, generatedBuildFile string, verbose bool) error {
+
+	srcBuildFileContent, err := os.ReadFile(srcBuildFile)
+	if err != nil {
+		return err
+	}
+
+	generatedBuildFileContent, err := os.ReadFile(generatedBuildFile)
+	if err != nil {
+		return err
+	}
+
+	// There can't be a package() call in both the source and generated BUILD files.
+	// bp2build will generate a package() call for licensing information, but if
+	// there's no licensing information, it will still generate a package() call
+	// that just sets default_visibility=public. If the handcrafted build file
+	// also has a package() call, we'll allow it to override the bp2build
+	// generated one if it doesn't have any licensing information. If the bp2build
+	// one has licensing information and the handcrafted one exists, we'll leave
+	// them both in for bazel to throw an error.
+	packageRegex := regexp.MustCompile(`(?m)^package\s*\(`)
+	packageDefaultVisibilityRegex := regexp.MustCompile(`(?m)^package\s*\(\s*default_visibility\s*=\s*\[\s*"//visibility:public",?\s*]\s*\)`)
+	if packageRegex.Find(srcBuildFileContent) != nil {
+		if verbose && packageDefaultVisibilityRegex.Find(generatedBuildFileContent) != nil {
+			fmt.Fprintf(os.Stderr, "Both '%s' and '%s' have a package() target, removing the first one\n",
+				generatedBuildFile, srcBuildFile)
+		}
+		generatedBuildFileContent = packageDefaultVisibilityRegex.ReplaceAll(generatedBuildFileContent, []byte{})
+	}
+
+	outFile, err := os.Create(output)
+	if err != nil {
+		return err
+	}
+
+	_, err = outFile.Write(generatedBuildFileContent)
+	if err != nil {
+		return err
+	}
+
+	if generatedBuildFileContent[len(generatedBuildFileContent)-1] != '\n' {
+		_, err = outFile.WriteString("\n")
+		if err != nil {
+			return err
+		}
+	}
+
+	_, err = outFile.Write(srcBuildFileContent)
+	return err
+}
+
 // Calls readdir() and returns it as a map from the basename of the files in dir
 // to os.FileInfo.
 func readdirToMap(dir string) map[string]os.FileInfo {
@@ -125,6 +191,17 @@
 	srcDirMap := readdirToMap(shared.JoinPath(topdir, srcDir))
 	buildFilesMap := readdirToMap(shared.JoinPath(topdir, buildFilesDir))
 
+	renamingBuildFile := false
+	if _, ok := srcDirMap["BUILD"]; ok {
+		if _, ok := srcDirMap["BUILD.bazel"]; !ok {
+			if _, ok := buildFilesMap["BUILD.bazel"]; ok {
+				renamingBuildFile = true
+				srcDirMap["BUILD.bazel"] = srcDirMap["BUILD"]
+				delete(srcDirMap, "BUILD")
+			}
+		}
+	}
+
 	allEntries := make(map[string]bool)
 	for n := range srcDirMap {
 		allEntries[n] = true
@@ -148,21 +225,28 @@
 		// The full paths of children in the input trees and in the output tree
 		forestChild := shared.JoinPath(forestDir, f)
 		srcChild := shared.JoinPath(srcDir, f)
+		if f == "BUILD.bazel" && renamingBuildFile {
+			srcChild = shared.JoinPath(srcDir, "BUILD")
+		}
 		buildFilesChild := shared.JoinPath(buildFilesDir, f)
 
 		// Descend in the exclusion tree, if there are any excludes left
-		var excludeChild *node
-		if exclude == nil {
-			excludeChild = nil
-		} else {
-			excludeChild = exclude.children[f]
+		var excludeChild *node = nil
+		if exclude != nil {
+			if f == "BUILD.bazel" && renamingBuildFile {
+				excludeChild = exclude.children["BUILD"]
+			} else {
+				excludeChild = exclude.children[f]
+			}
 		}
 
 		srcChildEntry, sExists := srcDirMap[f]
 		buildFilesChildEntry, bExists := buildFilesMap[f]
-		excluded := excludeChild != nil && excludeChild.excluded
 
-		if excluded {
+		if excludeChild != nil && excludeChild.excluded {
+			if bExists {
+				symlinkIntoForest(topdir, forestChild, buildFilesChild)
+			}
 			continue
 		}
 
@@ -198,13 +282,15 @@
 			// Both are directories. Descend.
 			plantSymlinkForestRecursive(cfg, topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
 		} else if !sDir && !bDir {
-			// Neither is a directory. Prioritize BUILD files generated by bp2build
-			// over any BUILD file imported into external/.
-			if cfg.IsEnvTrue("BP2BUILD_VERBOSE") {
-				fmt.Fprintf(os.Stderr, "Both '%s' and '%s' exist, symlinking the former to '%s'\n",
-					buildFilesChild, srcChild, forestChild)
+			// Neither is a directory. Merge them.
+			srcBuildFile := shared.JoinPath(topdir, srcChild)
+			generatedBuildFile := shared.JoinPath(topdir, buildFilesChild)
+			err = mergeBuildFiles(shared.JoinPath(topdir, forestChild), srcBuildFile, generatedBuildFile, cfg.IsEnvTrue("BP2BUILD_VERBOSE"))
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "Error merging %s and %s: %s",
+					srcBuildFile, generatedBuildFile, err)
+				*okay = false
 			}
-			symlinkIntoForest(topdir, forestChild, buildFilesChild)
 		} else {
 			// Both exist and one is a file. This is an error.
 			fmt.Fprintf(os.Stderr,
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index d15dea1..60e2f93 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -406,14 +406,10 @@
 	}
 }
 
-// Find BUILD files in the srcDir which...
-//
-// - are not on the allow list (android/bazel.go#ShouldKeepExistingBuildFileForDir())
-//
-// - won't be overwritten by corresponding bp2build generated files
-//
-// And return their paths so they can be left out of the Bazel workspace dir (i.e. ignored)
-func getPathsToIgnoredBuildFiles(topDir string, generatedRoot string, srcDirBazelFiles []string, verbose bool) []string {
+// Find BUILD files in the srcDir which are not in the allowlist
+// (android.Bp2BuildConversionAllowlist#ShouldKeepExistingBuildFileForDir)
+// and return their paths so they can be left out of the Bazel workspace dir (i.e. ignored)
+func getPathsToIgnoredBuildFiles(config android.Bp2BuildConversionAllowlist, topDir string, srcDirBazelFiles []string, verbose bool) []string {
 	paths := make([]string, 0)
 
 	for _, srcDirBazelFileRelativePath := range srcDirBazelFiles {
@@ -428,21 +424,14 @@
 			// Don't ignore entire directories
 			continue
 		}
-		if !(fileInfo.Name() == "BUILD" || fileInfo.Name() == "BUILD.bazel") {
+		if fileInfo.Name() != "BUILD" && fileInfo.Name() != "BUILD.bazel" {
 			// Don't ignore this file - it is not a build file
 			continue
 		}
-		srcDirBazelFileDir := filepath.Dir(srcDirBazelFileRelativePath)
-		if android.ShouldKeepExistingBuildFileForDir(srcDirBazelFileDir) {
+		if config.ShouldKeepExistingBuildFileForDir(filepath.Dir(srcDirBazelFileRelativePath)) {
 			// Don't ignore this existing build file
 			continue
 		}
-		correspondingBp2BuildFile := shared.JoinPath(topDir, generatedRoot, srcDirBazelFileRelativePath)
-		if _, err := os.Stat(correspondingBp2BuildFile); err == nil {
-			// If bp2build generated an alternate BUILD file, don't exclude this workspace path
-			// BUILD file clash resolution happens later in the symlink forest creation
-			continue
-		}
 		if verbose {
 			fmt.Fprintf(os.Stderr, "Ignoring existing BUILD file: %s\n", srcDirBazelFileRelativePath)
 		}
@@ -553,7 +542,7 @@
 			os.Exit(1)
 		}
 
-		pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(topDir, generatedRoot, existingBazelRelatedFiles, configuration.IsEnvTrue("BP2BUILD_VERBOSE"))
+		pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(configuration.Bp2buildPackageConfig, topDir, existingBazelRelatedFiles, configuration.IsEnvTrue("BP2BUILD_VERBOSE"))
 		excludes = append(excludes, pathsToIgnoredBuildFiles...)
 
 		excludes = append(excludes, getTemporaryExcludes()...)