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/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,