Merge "Support middleman actions in mixed builds"
diff --git a/android/bazel.go b/android/bazel.go
index f3e920a..4da899b 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -180,11 +180,10 @@
 		"liblinker_malloc", // http://b/186826466, cc_library_static, depends on //external/zlib:libz (http://b/186823782)
 		//                                                       also depends on //system/libziparchive:libziparchive (http://b/186823656)
 		//                                                       also depends on //system/logging/liblog:liblog (http://b/186822772)
-		"libc_jemalloc_wrapper", // http://b/187012490, cc_library_static, depends on //external/jemalloc_new:libjemalloc5 (http://b/186828626)
-		"libc_ndk",              // http://b/187013218, cc_library_static, depends on //bionic/libm:libm (http://b/183064661)
-		"libc",                  // http://b/183064430, cc_library, depends on //external/jemalloc_new:libjemalloc5 (http://b/186828626)
-		"libc_malloc_hooks",     // http://b/187016307, cc_library, ld.lld: error: undefined symbol: __malloc_hook
-		"libm",                  // http://b/183064661, cc_library, math.h:25:16: error: unexpected token in argument list
+		"libc_ndk",          // http://b/187013218, cc_library_static, depends on //bionic/libm:libm (http://b/183064661)
+		"libc",              // http://b/183064430, cc_library, depends on //external/jemalloc_new:libjemalloc5 (http://b/186828626)
+		"libc_malloc_hooks", // http://b/187016307, cc_library, ld.lld: error: undefined symbol: __malloc_hook
+		"libm",              // http://b/183064661, cc_library, math.h:25:16: error: unexpected token in argument list
 
 		// http://b/186823769: Needs C++ STL support, includes from unconverted standard libraries in //external/libcxx
 		// c++_static
@@ -204,7 +203,6 @@
 		"note_memtag_heap_async", // http://b/185127353: cc_library_static, error: feature.h not found
 		"note_memtag_heap_sync",  // http://b/185127353: cc_library_static, error: feature.h not found
 
-		"libjemalloc5",           // cc_library, ld.lld: error: undefined symbol: memset
 		"gwp_asan_crash_handler", // cc_library, ld.lld: error: undefined symbol: memset
 
 		// Tests. Handle later.
@@ -217,7 +215,8 @@
 	// Per-module denylist of cc_library modules to only generate the static
 	// variant if their shared variant isn't ready or buildable by Bazel.
 	bp2buildCcLibraryStaticOnlyList = []string{
-		"libstdc++", // http://b/186822597, cc_library, ld.lld: error: undefined symbol: __errno
+		"libstdc++",    // http://b/186822597, cc_library, ld.lld: error: undefined symbol: __errno
+		"libjemalloc5", // http://b/188503688, cc_library, `target: { android: { enabled: false } }` for android targets.
 	}
 
 	// Per-module denylist to opt modules out of mixed builds. Such modules will
diff --git a/bp2build/constants.go b/bp2build/constants.go
index 70f320e..4870dff 100644
--- a/bp2build/constants.go
+++ b/bp2build/constants.go
@@ -19,7 +19,10 @@
 	// be preferred for use within a Bazel build.
 
 	// The file name used for automatically generated files.
-	GeneratedBuildFileName = "BUILD"
+	GeneratedBuildFileName = "BUILD.bazel"
+
 	// The file name used for hand-crafted build targets.
+	// NOTE: It is okay that this matches GeneratedBuildFileName, since we generate BUILD files in a different directory to source files
+	// FIXME: Because there are hundreds of existing BUILD.bazel files in the AOSP tree, we should pick another name here, like BUILD.android
 	HandcraftedBuildFileName = "BUILD.bazel"
 )
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index a08c03d..0931ff7 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -29,7 +29,7 @@
 	expectedFilePaths := []bazelFilepath{
 		{
 			dir:      "",
-			basename: "BUILD",
+			basename: "BUILD.bazel",
 		},
 		{
 			dir:      "",
@@ -37,7 +37,7 @@
 		},
 		{
 			dir:      bazelRulesSubDir,
-			basename: "BUILD",
+			basename: "BUILD.bazel",
 		},
 		{
 			dir:      bazelRulesSubDir,
@@ -69,7 +69,7 @@
 
 		if actualFile.Dir != expectedFile.dir || actualFile.Basename != expectedFile.basename {
 			t.Errorf("Did not find expected file %s/%s", actualFile.Dir, actualFile.Basename)
-		} else if actualFile.Basename == "BUILD" || actualFile.Basename == "WORKSPACE" {
+		} else if actualFile.Basename == "BUILD.bazel" || actualFile.Basename == "WORKSPACE" {
 			if actualFile.Contents != "" {
 				t.Errorf("Expected %s to have no content.", actualFile)
 			}
diff --git a/cc/Android.bp b/cc/Android.bp
index c32cca8..1fc8d9f 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -92,6 +92,7 @@
         "object_test.go",
         "prebuilt_test.go",
         "proto_test.go",
+        "sanitize_test.go",
         "test_data_test.go",
         "vendor_public_library_test.go",
         "vendor_snapshot_test.go",
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 605a8d0..f486ee4 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -902,7 +902,7 @@
 					if d, ok := child.(PlatformSanitizeable); ok && d.SanitizePropDefined() &&
 						!d.SanitizeNever() &&
 						!d.IsSanitizerExplicitlyDisabled(t) {
-						if t == cfi || t == Hwasan || t == scs {
+						if t == cfi || t == Hwasan || t == scs || t == Asan {
 							if d.StaticallyLinked() && d.SanitizerSupported(t) {
 								// Rust does not support some of these sanitizers, so we need to check if it's
 								// supported before setting this true.
@@ -1261,7 +1261,7 @@
 				modules[0].(PlatformSanitizeable).SetSanitizer(t, true)
 			} else if c.IsSanitizerEnabled(t) || c.SanitizeDep() {
 				isSanitizerEnabled := c.IsSanitizerEnabled(t)
-				if c.StaticallyLinked() || c.Header() || t == Asan || t == Fuzzer {
+				if c.StaticallyLinked() || c.Header() || t == Fuzzer {
 					// Static and header libs are split into non-sanitized and sanitized variants.
 					// Shared libs are not split. However, for asan and fuzzer, we split even for shared
 					// libs because a library sanitized for asan/fuzzer can't be linked from a library
diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go
new file mode 100644
index 0000000..f126346
--- /dev/null
+++ b/cc/sanitize_test.go
@@ -0,0 +1,204 @@
+// Copyright 2021 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 cc
+
+import (
+	"testing"
+
+	"android/soong/android"
+)
+
+var prepareForAsanTest = android.FixtureAddFile("asan/Android.bp", []byte(`
+	cc_library_shared {
+		name: "libclang_rt.asan-aarch64-android",
+	}
+
+	cc_library_shared {
+		name: "libclang_rt.asan-arm-android",
+	}
+`))
+
+func TestAsan(t *testing.T) {
+	bp := `
+		cc_binary {
+			name: "bin_with_asan",
+			host_supported: true,
+			shared_libs: [
+				"libshared",
+				"libasan",
+			],
+			static_libs: [
+				"libstatic",
+				"libnoasan",
+			],
+			sanitize: {
+				address: true,
+			}
+		}
+
+		cc_binary {
+			name: "bin_no_asan",
+			host_supported: true,
+			shared_libs: [
+				"libshared",
+				"libasan",
+			],
+			static_libs: [
+				"libstatic",
+				"libnoasan",
+			],
+		}
+
+		cc_library_shared {
+			name: "libshared",
+			host_supported: true,
+			shared_libs: ["libtransitive"],
+		}
+
+		cc_library_shared {
+			name: "libasan",
+			host_supported: true,
+			shared_libs: ["libtransitive"],
+			sanitize: {
+				address: true,
+			}
+		}
+
+		cc_library_shared {
+			name: "libtransitive",
+			host_supported: true,
+		}
+
+		cc_library_static {
+			name: "libstatic",
+			host_supported: true,
+		}
+
+		cc_library_static {
+			name: "libnoasan",
+			host_supported: true,
+			sanitize: {
+				address: false,
+			}
+		}
+	`
+
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+		prepareForAsanTest,
+	).RunTestWithBp(t, bp)
+
+	check := func(t *testing.T, result *android.TestResult, variant string) {
+		asanVariant := variant + "_asan"
+		sharedVariant := variant + "_shared"
+		sharedAsanVariant := sharedVariant + "_asan"
+		staticVariant := variant + "_static"
+		staticAsanVariant := staticVariant + "_asan"
+
+		// The binaries, one with asan and one without
+		binWithAsan := result.ModuleForTests("bin_with_asan", asanVariant)
+		binNoAsan := result.ModuleForTests("bin_no_asan", variant)
+
+		// Shared libraries that don't request asan
+		libShared := result.ModuleForTests("libshared", sharedVariant)
+		libTransitive := result.ModuleForTests("libtransitive", sharedVariant)
+
+		// Shared library that requests asan
+		libAsan := result.ModuleForTests("libasan", sharedAsanVariant)
+
+		// Static library that uses an asan variant for bin_with_asan and a non-asan variant
+		// for bin_no_asan.
+		libStaticAsanVariant := result.ModuleForTests("libstatic", staticAsanVariant)
+		libStaticNoAsanVariant := result.ModuleForTests("libstatic", staticVariant)
+
+		// Static library that never uses asan.
+		libNoAsan := result.ModuleForTests("libnoasan", staticVariant)
+
+		// expectSharedLinkDep verifies that the from module links against the to module as a
+		// shared library.
+		expectSharedLinkDep := func(from, to android.TestingModule) {
+			t.Helper()
+			fromLink := from.Description("link")
+			toLink := to.Description("strip")
+
+			if g, w := fromLink.OrderOnly.Strings(), toLink.Output.String(); !android.InList(w, g) {
+				t.Errorf("%s should link against %s, expected %q, got %q",
+					from.Module(), to.Module(), w, g)
+			}
+		}
+
+		// expectStaticLinkDep verifies that the from module links against the to module as a
+		// static library.
+		expectStaticLinkDep := func(from, to android.TestingModule) {
+			t.Helper()
+			fromLink := from.Description("link")
+			toLink := to.Description("static link")
+
+			if g, w := fromLink.Implicits.Strings(), toLink.Output.String(); !android.InList(w, g) {
+				t.Errorf("%s should link against %s, expected %q, got %q",
+					from.Module(), to.Module(), w, g)
+			}
+
+		}
+
+		// expectInstallDep verifies that the install rule of the from module depends on the
+		// install rule of the to module.
+		expectInstallDep := func(from, to android.TestingModule) {
+			t.Helper()
+			fromInstalled := from.Description("install")
+			toInstalled := to.Description("install")
+
+			// combine implicits and order-only dependencies, host uses implicit but device uses
+			// order-only.
+			got := append(fromInstalled.Implicits.Strings(), fromInstalled.OrderOnly.Strings()...)
+			want := toInstalled.Output.String()
+			if !android.InList(want, got) {
+				t.Errorf("%s installation should depend on %s, expected %q, got %q",
+					from.Module(), to.Module(), want, got)
+			}
+		}
+
+		expectSharedLinkDep(binWithAsan, libShared)
+		expectSharedLinkDep(binWithAsan, libAsan)
+		expectSharedLinkDep(libShared, libTransitive)
+		expectSharedLinkDep(libAsan, libTransitive)
+
+		expectStaticLinkDep(binWithAsan, libStaticAsanVariant)
+		expectStaticLinkDep(binWithAsan, libNoAsan)
+
+		expectInstallDep(binWithAsan, libShared)
+		expectInstallDep(binWithAsan, libAsan)
+		expectInstallDep(binWithAsan, libTransitive)
+		expectInstallDep(libShared, libTransitive)
+		expectInstallDep(libAsan, libTransitive)
+
+		expectSharedLinkDep(binNoAsan, libShared)
+		expectSharedLinkDep(binNoAsan, libAsan)
+		expectSharedLinkDep(libShared, libTransitive)
+		expectSharedLinkDep(libAsan, libTransitive)
+
+		expectStaticLinkDep(binNoAsan, libStaticNoAsanVariant)
+		expectStaticLinkDep(binNoAsan, libNoAsan)
+
+		expectInstallDep(binNoAsan, libShared)
+		expectInstallDep(binNoAsan, libAsan)
+		expectInstallDep(binNoAsan, libTransitive)
+		expectInstallDep(libShared, libTransitive)
+		expectInstallDep(libAsan, libTransitive)
+	}
+
+	t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) })
+	t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") })
+}
diff --git a/java/base.go b/java/base.go
index c828503..0d2f1ac 100644
--- a/java/base.go
+++ b/java/base.go
@@ -889,8 +889,8 @@
 		kotlincFlags := j.properties.Kotlincflags
 		CheckKotlincFlags(ctx, kotlincFlags)
 
-		// Dogfood the JVM_IR backend.
-		kotlincFlags = append(kotlincFlags, "-Xuse-ir")
+		// Workaround for KT-46512
+		kotlincFlags = append(kotlincFlags, "-Xsam-conversions=class")
 
 		// If there are kotlin files, compile them first but pass all the kotlin and java files
 		// kotlinc will use the java files to resolve types referenced by the kotlin files, but
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index 3f51114..42363e9 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -7,6 +7,8 @@
 
 source "$(dirname "$0")/lib.sh"
 
+readonly GENERATED_BUILD_FILE_NAME="BUILD.bazel"
+
 function test_smoke {
   setup
   run_soong
@@ -505,8 +507,8 @@
 EOF
 
   GENERATE_BAZEL_FILES=1 run_soong
-  [[ -e out/soong/bp2build/a/BUILD ]] || fail "a/BUILD not created"
-  [[ -L out/soong/workspace/a/BUILD ]] || fail "a/BUILD not symlinked"
+  [[ -e out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not created"
+  [[ -L out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not symlinked"
 
   mkdir -p b
   touch b/b.txt
@@ -519,8 +521,8 @@
 EOF
 
   GENERATE_BAZEL_FILES=1 run_soong
-  [[ -e out/soong/bp2build/b/BUILD ]] || fail "a/BUILD not created"
-  [[ -L out/soong/workspace/b/BUILD ]] || fail "a/BUILD not symlinked"
+  [[ -e out/soong/bp2build/b/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not created"
+  [[ -L out/soong/workspace/b/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not symlinked"
 }
 
 function test_bp2build_null_build {
@@ -551,11 +553,11 @@
 EOF
 
   GENERATE_BAZEL_FILES=1 run_soong
-  grep -q a1.txt out/soong/bp2build/a/BUILD || fail "a1.txt not in BUILD file"
+  grep -q a1.txt "out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME}" || fail "a1.txt not in ${GENERATED_BUILD_FILE_NAME} file"
 
   touch a/a2.txt
   GENERATE_BAZEL_FILES=1 run_soong
-  grep -q a2.txt out/soong/bp2build/a/BUILD || fail "a2.txt not in BUILD file"
+  grep -q a2.txt "out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME}" || fail "a2.txt not in ${GENERATED_BUILD_FILE_NAME} file"
 }
 
 function test_dump_json_module_graph() {
@@ -583,8 +585,8 @@
   GENERATE_BAZEL_FILES=1 run_soong
   [[ -e out/soong/workspace ]] || fail "Bazel workspace not created"
   [[ -d out/soong/workspace/a/b ]] || fail "module directory not a directory"
-  [[ -L out/soong/workspace/a/b/BUILD ]] || fail "BUILD file not symlinked"
-  [[ "$(readlink -f out/soong/workspace/a/b/BUILD)" =~ bp2build/a/b/BUILD$ ]] \
+  [[ -L "out/soong/workspace/a/b/${GENERATED_BUILD_FILE_NAME}" ]] || fail "${GENERATED_BUILD_FILE_NAME} file not symlinked"
+  [[ "$(readlink -f out/soong/workspace/a/b/${GENERATED_BUILD_FILE_NAME})" =~ "bp2build/a/b/${GENERATED_BUILD_FILE_NAME}"$ ]] \
     || fail "BUILD files symlinked at the wrong place"
   [[ -L out/soong/workspace/a/b/b.txt ]] || fail "a/b/b.txt not symlinked"
   [[ -L out/soong/workspace/a/a.txt ]] || fail "a/b/a.txt not symlinked"
@@ -616,7 +618,7 @@
 
   mkdir -p a
   touch a/a.txt
-  touch a/BUILD
+  touch a/${GENERATED_BUILD_FILE_NAME}
   cat > a/Android.bp <<EOF
 filegroup {
   name: "a",
@@ -626,15 +628,15 @@
 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"
+  [[ -L "out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME}" ]] || fail "${GENERATED_BUILD_FILE_NAME} file not symlinked"
+  [[ "$(readlink -f out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME})" =~ "bp2build/a/${GENERATED_BUILD_FILE_NAME}"$ ]] \
+    || fail "${GENERATED_BUILD_FILE_NAME} files symlinked to the wrong place"
 }
 
 function test_bp2build_reports_multiple_errors {
   setup
 
-  mkdir -p a/BUILD
+  mkdir -p "a/${GENERATED_BUILD_FILE_NAME}"
   touch a/a.txt
   cat > a/Android.bp <<EOF
 filegroup {
@@ -644,7 +646,7 @@
 }
 EOF
 
-  mkdir -p b/BUILD
+  mkdir -p "b/${GENERATED_BUILD_FILE_NAME}"
   touch b/b.txt
   cat > b/Android.bp <<EOF
 filegroup {
@@ -658,8 +660,8 @@
     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"
+  grep -q "a/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for a/${GENERATED_BUILD_FILE_NAME} not found"
+  grep -q "b/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for b/${GENERATED_BUILD_FILE_NAME} not found"
 }
 
 test_smoke
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index 082cd06..e357710 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -6,6 +6,8 @@
 
 source "$(dirname "$0")/lib.sh"
 
+readonly GENERATED_BUILD_FILE_NAME="BUILD.bazel"
+
 function test_bp2build_generates_all_buildfiles {
   setup
   create_mock_bazel
@@ -40,24 +42,24 @@
 
   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"
+  if [[ ! -f "./out/soong/workspace/foo/convertible_soong_module/${GENERATED_BUILD_FILE_NAME}" ]]; then
+    fail "./out/soong/workspace/foo/convertible_soong_module/${GENERATED_BUILD_FILE_NAME} 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"
+  if [[ ! -f "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}" ]]; then
+    fail "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME} 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"
+  if ! grep "the_answer" "./out/soong/workspace/foo/convertible_soong_module/${GENERATED_BUILD_FILE_NAME}"; then
+    fail "missing BUILD target the_answer in convertible_soong_module/${GENERATED_BUILD_FILE_NAME}"
   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"
+  if grep "not_the_answer" "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}"; then
+    fail "found unexpected BUILD target not_the_answer in unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}"
   fi
 
-  if ! grep "filegroup" "./out/soong/workspace/foo/unconvertible_soong_module/BUILD"; then
-    fail "missing filegroup in unconvertible_soong_module/BUILD"
+  if ! grep "filegroup" "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}"; then
+    fail "missing filegroup in unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}"
   fi
 
   # NOTE: We don't actually use the extra BUILD file for anything here