Merge "Make MockBazelContext more specific to cquerys"
diff --git a/android/bazel.go b/android/bazel.go
index b2170be..b54ae64 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -174,6 +174,18 @@
 		"liblinker_debuggerd_stub",      // ruperts@, cc_library_static, depends on //system/libbase
 		"libbionic_tests_headers_posix", // ruperts@, cc_library_static
 		"libc_dns",                      // ruperts@, cc_library_static
+
+		"note_memtag_heap_async", // jingwen@, b/185079815, features.h includes not found
+		"note_memtag_heap_sync",  // jingwen@, b/185079815, features.h includes not found
+
+		// List of all full_cc_libraries in //bionic, with their immediate failures
+		"libc",              // jingwen@, cc_library, depends on //external/gwp_asan
+		"libc_malloc_debug", // jingwen@, cc_library, fatal error: 'assert.h' file not found
+		"libc_malloc_hooks", // jingwen@, cc_library, fatal error: 'errno.h' file not found
+		"libdl",             // jingwen@, cc_library, ld.lld: error: no input files
+		"libm",              // jingwen@, cc_library, fatal error: 'freebsd-compat.h' file not found
+		"libseccomp_policy", // jingwen@, cc_library, fatal error: 'seccomp_policy.h' file not found
+		"libstdc++",         // jingwen@, cc_library, depends on //external/gwp_asan
 	}
 
 	// Used for quicker lookups
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 7911632..97eec30 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -354,15 +354,20 @@
 # This file is generated by soong_build. Do not edit.
 local_repository(
     name = "sourceroot",
-    path = "%s",
+    path = "%[1]s",
 )
 
 local_repository(
     name = "rules_cc",
-    path = "%s/build/bazel/rules_cc",
+    path = "%[1]s/build/bazel/rules_cc",
+)
+
+local_repository(
+    name = "bazel_skylib",
+    path = "%[1]s/build/bazel/bazel_skylib",
 )
 `
-	return []byte(fmt.Sprintf(formatString, context.workspaceDir, context.workspaceDir))
+	return []byte(fmt.Sprintf(formatString, context.workspaceDir))
 }
 
 func (context *bazelContext) mainBzlFileContents() []byte {
diff --git a/android/paths.go b/android/paths.go
index ba1ab11..37b04dd 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -421,6 +421,9 @@
 // bazel-compatible labels.  Properties passed as the paths or excludes argument must have been
 // annotated with struct tag `android:"path"` so that dependencies on other modules will have
 // already been handled by the path_properties mutator.
+//
+// With expanded globs, we can catch package boundaries problem instead of
+// silently failing to potentially missing files from Bazel's globs.
 func BazelLabelForModuleSrc(ctx BazelConversionPathContext, paths []string) bazel.LabelList {
 	return BazelLabelForModuleSrcExcludes(ctx, paths, []string(nil))
 }
@@ -431,6 +434,9 @@
 // passed as the paths or excludes argument must have been annotated with struct tag
 // `android:"path"` so that dependencies on other modules will have already been handled by the
 // path_properties mutator.
+//
+// With expanded globs, we can catch package boundaries problem instead of
+// silently failing to potentially missing files from Bazel's globs.
 func BazelLabelForModuleSrcExcludes(ctx BazelConversionPathContext, paths, excludes []string) bazel.LabelList {
 	excludeLabels := expandSrcsForBazel(ctx, excludes, []string(nil))
 	excluded := make([]string, 0, len(excludeLabels.Includes))
diff --git a/bazel/properties.go b/bazel/properties.go
index 148386f..4bb2391 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"path/filepath"
 	"regexp"
 	"sort"
 )
@@ -47,6 +48,57 @@
 	Excludes []Label
 }
 
+// GlobsInDir returns a list of glob expressions for a list of extensions
+// (optionally recursive) within a directory.
+func GlobsInDir(dir string, recursive bool, extensions []string) []string {
+	globs := []string{}
+
+	globInfix := ""
+	if dir == "." {
+		if recursive {
+			// e.g "**/*.h"
+			globInfix = "**/"
+		} // else e.g. "*.h"
+		for _, ext := range extensions {
+			globs = append(globs, globInfix+"*"+ext)
+		}
+	} else {
+		if recursive {
+			// e.g. "foo/bar/**/*.h"
+			dir += "/**"
+		} // else e.g. "foo/bar/*.h"
+		for _, ext := range extensions {
+			globs = append(globs, dir+"/*"+ext)
+		}
+	}
+	return globs
+}
+
+// LooseHdrsGlobs returns the list of non-recursive header globs for each parent directory of
+// each source file in this LabelList's Includes.
+func (ll *LabelList) LooseHdrsGlobs(exts []string) []string {
+	var globs []string
+	for _, parentDir := range ll.uniqueParentDirectories() {
+		globs = append(globs,
+			GlobsInDir(parentDir, false, exts)...)
+	}
+	return globs
+}
+
+// uniqueParentDirectories returns a list of the unique parent directories for
+// all files in ll.Includes.
+func (ll *LabelList) uniqueParentDirectories() []string {
+	dirMap := map[string]bool{}
+	for _, label := range ll.Includes {
+		dirMap[filepath.Dir(label.Label)] = true
+	}
+	dirs := []string{}
+	for dir := range dirMap {
+		dirs = append(dirs, dir)
+	}
+	return dirs
+}
+
 // Append appends the fields of other labelList to the corresponding fields of ll.
 func (ll *LabelList) Append(other LabelList) {
 	if len(ll.Includes) > 0 || len(other.Includes) > 0 {
@@ -224,6 +276,26 @@
 	return LabelListAttribute{Value: UniqueBazelLabelList(value)}
 }
 
+// Append appends all values, including os and arch specific ones, from another
+// LabelListAttribute to this LabelListAttribute.
+func (attrs *LabelListAttribute) Append(other LabelListAttribute) {
+	for arch := range PlatformArchMap {
+		this := attrs.GetValueForArch(arch)
+		that := other.GetValueForArch(arch)
+		this.Append(that)
+		attrs.SetValueForArch(arch, this)
+	}
+
+	for os := range PlatformOsMap {
+		this := attrs.GetValueForOS(os)
+		that := other.GetValueForOS(os)
+		this.Append(that)
+		attrs.SetValueForOS(os, this)
+	}
+
+	attrs.Value.Append(other.Value)
+}
+
 // HasArchSpecificValues returns true if the attribute contains
 // architecture-specific label_list values.
 func (attrs LabelListAttribute) HasConfigurableValues() bool {
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index cc616f2..d2a8729 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -26,6 +26,7 @@
     testSrcs: [
         "build_conversion_test.go",
         "bzl_conversion_test.go",
+        "cc_library_conversion_test.go",
         "cc_library_headers_conversion_test.go",
         "cc_library_static_conversion_test.go",
         "cc_object_conversion_test.go",
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index dd14c7d..b7a2810 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -374,16 +374,9 @@
 		// value>)" to set the default value of unset attributes. In the cases
 		// where the bp2build converter didn't set the default value within the
 		// mutator when creating the BazelTargetModule, this would be a zero
-		// value. For those cases, we return a non-surprising default value so
-		// generated BUILD files are syntactically correct.
-		switch propertyValue.Kind() {
-		case reflect.Slice:
-			return "[]", nil
-		case reflect.Map:
-			return "{}", nil
-		default:
-			return "", nil
-		}
+		// value. For those cases, we return an empty string so we don't
+		// unnecessarily generate empty values.
+		return "", nil
 	}
 
 	var ret string
@@ -397,21 +390,38 @@
 	case reflect.Ptr:
 		return prettyPrint(propertyValue.Elem(), indent)
 	case reflect.Slice:
-		ret = "[\n"
-		for i := 0; i < propertyValue.Len(); i++ {
-			indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1)
+		if propertyValue.Len() == 0 {
+			return "", nil
+		}
+
+		if propertyValue.Len() == 1 {
+			// Single-line list for list with only 1 element
+			ret += "["
+			indexedValue, err := prettyPrint(propertyValue.Index(0), indent)
 			if err != nil {
 				return "", err
 			}
+			ret += indexedValue
+			ret += "]"
+		} else {
+			// otherwise, use a multiline list.
+			ret += "[\n"
+			for i := 0; i < propertyValue.Len(); i++ {
+				indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1)
+				if err != nil {
+					return "", err
+				}
 
-			if indexedValue != "" {
-				ret += makeIndent(indent + 1)
-				ret += indexedValue
-				ret += ",\n"
+				if indexedValue != "" {
+					ret += makeIndent(indent + 1)
+					ret += indexedValue
+					ret += ",\n"
+				}
 			}
+			ret += makeIndent(indent)
+			ret += "]"
 		}
-		ret += makeIndent(indent)
-		ret += "]"
+
 	case reflect.Struct:
 		// Special cases where the bp2build sends additional information to the codegenerator
 		// by wrapping the attributes in a custom struct type.
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 49897b3..1ede442 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -27,9 +27,7 @@
 		expectedBazelTarget string
 	}{
 		{
-			bp: `custom {
-	name: "foo",
-}
+			bp: `custom { name: "foo" }
 		`,
 			expectedBazelTarget: `soong_module(
     name = "foo",
@@ -85,9 +83,7 @@
     soong_module_variant = "",
     soong_module_deps = [
     ],
-    required = [
-        "bar",
-    ],
+    required = ["bar"],
 )`,
 		},
 		{
@@ -116,12 +112,10 @@
 		targets: ["goal_foo"],
 		tag: ".foo",
 	},
-	dists: [
-		{
-			targets: ["goal_bar"],
-			tag: ".bar",
-		},
-	],
+	dists: [{
+		targets: ["goal_bar"],
+		tag: ".bar",
+	}],
 }
 		`,
 			expectedBazelTarget: `soong_module(
@@ -133,18 +127,12 @@
     ],
     dist = {
         "tag": ".foo",
-        "targets": [
-            "goal_foo",
-        ],
+        "targets": ["goal_foo"],
     },
-    dists = [
-        {
-            "tag": ".bar",
-            "targets": [
-                "goal_bar",
-            ],
-        },
-    ],
+    dists = [{
+        "tag": ".bar",
+        "targets": ["goal_bar"],
+    }],
 )`,
 		},
 		{
@@ -169,19 +157,13 @@
     soong_module_variant = "",
     soong_module_deps = [
     ],
-    dists = [
-        {
-            "tag": ".tag",
-            "targets": [
-                "my_goal",
-            ],
-        },
-    ],
+    dists = [{
+        "tag": ".tag",
+        "targets": ["my_goal"],
+    }],
     owner = "custom_owner",
     ramdisk = True,
-    required = [
-        "bar",
-    ],
+    required = ["bar"],
     target_required = [
         "qux",
         "bazqux",
@@ -553,9 +535,7 @@
 }`,
 			expectedBazelTargets: []string{`filegroup(
     name = "fg_foo",
-    srcs = [
-        "b",
-    ],
+    srcs = ["b"],
 )`,
 			},
 		},
@@ -625,7 +605,7 @@
 			bp: `filegroup {
     name: "foobar",
     srcs: [
-      ":foo",
+        ":foo",
         "c",
     ],
     bazel_module: { bp2build_available: true },
@@ -671,25 +651,15 @@
 				`genrule(
     name = "foo",
     cmd = "$(location :foo.tool) --genDir=$(GENDIR) arg $(SRCS) $(OUTS)",
-    outs = [
-        "foo.out",
-    ],
-    srcs = [
-        "foo.in",
-    ],
-    tools = [
-        ":foo.tool",
-    ],
+    outs = ["foo.out"],
+    srcs = ["foo.in"],
+    tools = [":foo.tool"],
 )`,
 				`genrule(
     name = "foo.tool",
     cmd = "cp $(SRCS) $(OUTS)",
-    outs = [
-        "foo_tool.out",
-    ],
-    srcs = [
-        "foo_tool.in",
-    ],
+    outs = ["foo_tool.out"],
+    srcs = ["foo_tool.in"],
 )`,
 			},
 		},
@@ -718,15 +688,9 @@
 			expectedBazelTargets: []string{`genrule(
     name = "foo",
     cmd = "$(locations :foo.tools) -s $(OUTS) $(SRCS)",
-    outs = [
-        "foo.out",
-    ],
-    srcs = [
-        "foo.in",
-    ],
-    tools = [
-        ":foo.tools",
-    ],
+    outs = ["foo.out"],
+    srcs = ["foo.in"],
+    tools = [":foo.tools"],
 )`,
 				`genrule(
     name = "foo.tools",
@@ -735,9 +699,7 @@
         "foo_tool.out",
         "foo_tool2.out",
     ],
-    srcs = [
-        "foo_tool.in",
-    ],
+    srcs = ["foo_tool.in"],
 )`,
 			},
 		},
@@ -758,15 +720,9 @@
 			expectedBazelTargets: []string{`genrule(
     name = "foo",
     cmd = "$(locations //other:foo.tool) -s $(OUTS) $(SRCS)",
-    outs = [
-        "foo.out",
-    ],
-    srcs = [
-        "foo.in",
-    ],
-    tools = [
-        "//other:foo.tool",
-    ],
+    outs = ["foo.out"],
+    srcs = ["foo.in"],
+    tools = ["//other:foo.tool"],
 )`,
 			},
 			fs: otherGenruleBp,
@@ -788,15 +744,9 @@
 			expectedBazelTargets: []string{`genrule(
     name = "foo",
     cmd = "$(locations //other:foo.tool) -s $(OUTS) $(location //other:other.tool)",
-    outs = [
-        "foo.out",
-    ],
-    srcs = [
-        "//other:other.tool",
-    ],
-    tools = [
-        "//other:foo.tool",
-    ],
+    outs = ["foo.out"],
+    srcs = ["//other:other.tool"],
+    tools = ["//other:foo.tool"],
 )`,
 			},
 			fs: otherGenruleBp,
@@ -818,12 +768,8 @@
 			expectedBazelTargets: []string{`genrule(
     name = "foo",
     cmd = "$(location //other:foo.tool) -s $(OUTS) $(SRCS)",
-    outs = [
-        "foo.out",
-    ],
-    srcs = [
-        "foo.in",
-    ],
+    outs = ["foo.out"],
+    srcs = ["foo.in"],
     tools = [
         "//other:foo.tool",
         "//other:other.tool",
@@ -849,12 +795,8 @@
 			expectedBazelTargets: []string{`genrule(
     name = "foo",
     cmd = "$(locations //other:foo.tool) -s $(OUTS) $(SRCS)",
-    outs = [
-        "foo.out",
-    ],
-    srcs = [
-        "foo.in",
-    ],
+    outs = ["foo.out"],
+    srcs = ["foo.in"],
     tools = [
         "//other:foo.tool",
         "//other:other.tool",
@@ -879,12 +821,8 @@
 			expectedBazelTargets: []string{`genrule(
     name = "foo",
     cmd = "cp $(SRCS) $(OUTS)",
-    outs = [
-        "foo.out",
-    ],
-    srcs = [
-        "foo.in",
-    ],
+    outs = ["foo.out"],
+    srcs = ["foo.in"],
 )`,
 			},
 		},
@@ -988,12 +926,8 @@
 			expectedBazelTarget: `genrule(
     name = "gen",
     cmd = "do-something $(SRCS) $(OUTS)",
-    outs = [
-        "out",
-    ],
-    srcs = [
-        "in1",
-    ],
+    outs = ["out"],
+    srcs = ["in1"],
 )`,
 			description: "genrule applies properties from a genrule_defaults dependency if not specified",
 		},
@@ -1062,12 +996,8 @@
 			expectedBazelTarget: `genrule(
     name = "gen",
     cmd = "cp $(SRCS) $(OUTS)",
-    outs = [
-        "out",
-    ],
-    srcs = [
-        "in1",
-    ],
+    outs = ["out"],
+    srcs = ["in1"],
 )`,
 			description: "genrule applies properties from list of genrule_defaults",
 		},
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
new file mode 100644
index 0000000..783af2e
--- /dev/null
+++ b/bp2build/cc_library_conversion_test.go
@@ -0,0 +1,271 @@
+// 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 bp2build
+
+import (
+	"android/soong/android"
+	"android/soong/cc"
+	"strings"
+	"testing"
+)
+
+const (
+	// See cc/testing.go for more context
+	soongCcLibraryPreamble = `
+cc_defaults {
+	name: "linux_bionic_supported",
+}
+
+toolchain_library {
+	name: "libclang_rt.builtins-x86_64-android",
+	defaults: ["linux_bionic_supported"],
+	vendor_available: true,
+	vendor_ramdisk_available: true,
+	product_available: true,
+	recovery_available: true,
+	native_bridge_supported: true,
+	src: "",
+}`
+)
+
+func TestCcLibraryBp2Build(t *testing.T) {
+	testCases := []struct {
+		description                        string
+		moduleTypeUnderTest                string
+		moduleTypeUnderTestFactory         android.ModuleFactory
+		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
+		bp                                 string
+		expectedBazelTargets               []string
+		filesystem                         map[string]string
+		dir                                string
+	}{
+		{
+			description:                        "cc_library - simple example",
+			moduleTypeUnderTest:                "cc_library",
+			moduleTypeUnderTestFactory:         cc.LibraryFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+			filesystem: map[string]string{
+				"android.cpp": "",
+				"darwin.cpp":  "",
+				// Refer to cc.headerExts for the supported header extensions in Soong.
+				"header.h":         "",
+				"header.hh":        "",
+				"header.hpp":       "",
+				"header.hxx":       "",
+				"header.h++":       "",
+				"header.inl":       "",
+				"header.inc":       "",
+				"header.ipp":       "",
+				"header.h.generic": "",
+				"impl.cpp":         "",
+				"linux.cpp":        "",
+				"x86.cpp":          "",
+				"x86_64.cpp":       "",
+				"foo-dir/a.h":      "",
+			},
+			bp: soongCcLibraryPreamble + `
+cc_library_headers { name: "some-headers" }
+cc_library {
+    name: "foo-lib",
+    srcs: ["impl.cpp"],
+    cflags: ["-Wall"],
+    header_libs: ["some-headers"],
+    export_include_dirs: ["foo-dir"],
+    ldflags: ["-Wl,--exclude-libs=bar.a"],
+    arch: {
+        x86: {
+            ldflags: ["-Wl,--exclude-libs=baz.a"],
+            srcs: ["x86.cpp"],
+        },
+        x86_64: {
+            ldflags: ["-Wl,--exclude-libs=qux.a"],
+            srcs: ["x86_64.cpp"],
+        },
+    },
+    target: {
+        android: {
+            srcs: ["android.cpp"],
+        },
+        linux_glibc: {
+            srcs: ["linux.cpp"],
+        },
+        darwin: {
+            srcs: ["darwin.cpp"],
+        },
+    },
+}
+`,
+			expectedBazelTargets: []string{`cc_library(
+    name = "foo-lib",
+    copts = ["-Wall"],
+    deps = [":some-headers"],
+    hdrs = [
+        "header.h",
+        "header.hh",
+        "header.hpp",
+        "header.hxx",
+        "header.h++",
+        "header.inl",
+        "header.inc",
+        "header.ipp",
+        "header.h.generic",
+        "foo-dir/a.h",
+    ],
+    includes = ["foo-dir"],
+    linkopts = ["-Wl,--exclude-libs=bar.a"] + select({
+        "//build/bazel/platforms/arch:x86": ["-Wl,--exclude-libs=baz.a"],
+        "//build/bazel/platforms/arch:x86_64": ["-Wl,--exclude-libs=qux.a"],
+        "//conditions:default": [],
+    }),
+    srcs = ["impl.cpp"] + select({
+        "//build/bazel/platforms/arch:x86": ["x86.cpp"],
+        "//build/bazel/platforms/arch:x86_64": ["x86_64.cpp"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms/os:android": ["android.cpp"],
+        "//build/bazel/platforms/os:darwin": ["darwin.cpp"],
+        "//build/bazel/platforms/os:linux": ["linux.cpp"],
+        "//conditions:default": [],
+    }),
+)`},
+		},
+		{
+			description:                        "cc_library - trimmed example of //bionic/linker:ld-android",
+			moduleTypeUnderTest:                "cc_library",
+			moduleTypeUnderTestFactory:         cc.LibraryFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+			filesystem: map[string]string{
+				"ld-android.cpp":           "",
+				"linked_list.h":            "",
+				"linker.h":                 "",
+				"linker_block_allocator.h": "",
+				"linker_cfi.h":             "",
+			},
+			bp: soongCcLibraryPreamble + `
+cc_library_headers { name: "libc_headers" }
+cc_library {
+    name: "fake-ld-android",
+    srcs: ["ld_android.cpp"],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Wunused",
+        "-Werror",
+    ],
+    header_libs: ["libc_headers"],
+    ldflags: [
+        "-Wl,--exclude-libs=libgcc.a",
+        "-Wl,--exclude-libs=libgcc_stripped.a",
+        "-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a",
+        "-Wl,--exclude-libs=libclang_rt.builtins-aarch64-android.a",
+        "-Wl,--exclude-libs=libclang_rt.builtins-i686-android.a",
+        "-Wl,--exclude-libs=libclang_rt.builtins-x86_64-android.a",
+    ],
+    arch: {
+        x86: {
+            ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
+        },
+        x86_64: {
+            ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
+        },
+    },
+}
+`,
+			expectedBazelTargets: []string{`cc_library(
+    name = "fake-ld-android",
+    copts = [
+        "-Wall",
+        "-Wextra",
+        "-Wunused",
+        "-Werror",
+    ],
+    deps = [":libc_headers"],
+    hdrs = [
+        "linked_list.h",
+        "linker.h",
+        "linker_block_allocator.h",
+        "linker_cfi.h",
+    ],
+    linkopts = [
+        "-Wl,--exclude-libs=libgcc.a",
+        "-Wl,--exclude-libs=libgcc_stripped.a",
+        "-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a",
+        "-Wl,--exclude-libs=libclang_rt.builtins-aarch64-android.a",
+        "-Wl,--exclude-libs=libclang_rt.builtins-i686-android.a",
+        "-Wl,--exclude-libs=libclang_rt.builtins-x86_64-android.a",
+    ] + select({
+        "//build/bazel/platforms/arch:x86": ["-Wl,--exclude-libs=libgcc_eh.a"],
+        "//build/bazel/platforms/arch:x86_64": ["-Wl,--exclude-libs=libgcc_eh.a"],
+        "//conditions:default": [],
+    }),
+    srcs = ["ld_android.cpp"],
+)`},
+		},
+	}
+
+	dir := "."
+	for _, testCase := range testCases {
+		filesystem := make(map[string][]byte)
+		toParse := []string{
+			"Android.bp",
+		}
+		for f, content := range testCase.filesystem {
+			if strings.HasSuffix(f, "Android.bp") {
+				toParse = append(toParse, f)
+			}
+			filesystem[f] = []byte(content)
+		}
+		config := android.TestConfig(buildDir, nil, testCase.bp, filesystem)
+		ctx := android.NewTestContext(config)
+
+		cc.RegisterCCBuildComponents(ctx)
+		ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
+		ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
+		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+		ctx.RegisterBp2BuildConfig(bp2buildConfig) // TODO(jingwen): make this the default for all tests
+		ctx.RegisterForBazelConversion()
+
+		_, errs := ctx.ParseFileList(dir, toParse)
+		if Errored(t, testCase.description, errs) {
+			continue
+		}
+		_, errs = ctx.ResolveDependencies(config)
+		if Errored(t, testCase.description, errs) {
+			continue
+		}
+
+		checkDir := dir
+		if testCase.dir != "" {
+			checkDir = testCase.dir
+		}
+		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+		bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
+		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
+			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
+		} else {
+			for i, target := range bazelTargets {
+				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+					t.Errorf(
+						"%s: Expected generated Bazel target to be '%s', got '%s'",
+						testCase.description,
+						w,
+						g,
+					)
+				}
+			}
+		}
+	}
+}
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index 74226ae..c59241f 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -23,7 +23,7 @@
 
 const (
 	// See cc/testing.go for more context
-	soongCcLibraryPreamble = `
+	soongCcLibraryHeadersPreamble = `
 cc_defaults {
 	name: "linux_bionic_supported",
 }
@@ -98,7 +98,7 @@
 				"arch_x86_exported_include_dir/b.h":    "",
 				"arch_x86_64_exported_include_dir/c.h": "",
 			},
-			bp: soongCcLibraryPreamble + `
+			bp: soongCcLibraryHeadersPreamble + `
 cc_library_headers {
     name: "lib-1",
     export_include_dirs: ["lib-1"],
@@ -141,30 +141,18 @@
         "dir-2/dir2a.h",
         "dir-2/dir2b.h",
     ] + select({
-        "//build/bazel/platforms/arch:arm64": [
-            "arch_arm64_exported_include_dir/a.h",
-        ],
-        "//build/bazel/platforms/arch:x86": [
-            "arch_x86_exported_include_dir/b.h",
-        ],
-        "//build/bazel/platforms/arch:x86_64": [
-            "arch_x86_64_exported_include_dir/c.h",
-        ],
+        "//build/bazel/platforms/arch:arm64": ["arch_arm64_exported_include_dir/a.h"],
+        "//build/bazel/platforms/arch:x86": ["arch_x86_exported_include_dir/b.h"],
+        "//build/bazel/platforms/arch:x86_64": ["arch_x86_64_exported_include_dir/c.h"],
         "//conditions:default": [],
     }),
     includes = [
         "dir-1",
         "dir-2",
     ] + select({
-        "//build/bazel/platforms/arch:arm64": [
-            "arch_arm64_exported_include_dir",
-        ],
-        "//build/bazel/platforms/arch:x86": [
-            "arch_x86_exported_include_dir",
-        ],
-        "//build/bazel/platforms/arch:x86_64": [
-            "arch_x86_64_exported_include_dir",
-        ],
+        "//build/bazel/platforms/arch:arm64": ["arch_arm64_exported_include_dir"],
+        "//build/bazel/platforms/arch:x86": ["arch_x86_exported_include_dir"],
+        "//build/bazel/platforms/arch:x86_64": ["arch_x86_64_exported_include_dir"],
         "//conditions:default": [],
     }),
 )`, `cc_library_headers(
@@ -173,18 +161,14 @@
         "lib-1/lib1a.h",
         "lib-1/lib1b.h",
     ],
-    includes = [
-        "lib-1",
-    ],
+    includes = ["lib-1"],
 )`, `cc_library_headers(
     name = "lib-2",
     hdrs = [
         "lib-2/lib2a.h",
         "lib-2/lib2b.h",
     ],
-    includes = [
-        "lib-2",
-    ],
+    includes = ["lib-2"],
 )`},
 		},
 		{
@@ -223,27 +207,13 @@
     name = "darwin-lib",
 )`, `cc_library_headers(
     name = "foo_headers",
-    deps = [
-        ":base-lib",
-    ] + select({
-        "//build/bazel/platforms/os:android": [
-            ":android-lib",
-        ],
-        "//build/bazel/platforms/os:darwin": [
-            ":darwin-lib",
-        ],
-        "//build/bazel/platforms/os:fuchsia": [
-            ":fuchsia-lib",
-        ],
-        "//build/bazel/platforms/os:linux": [
-            ":linux-lib",
-        ],
-        "//build/bazel/platforms/os:linux_bionic": [
-            ":linux_bionic-lib",
-        ],
-        "//build/bazel/platforms/os:windows": [
-            ":windows-lib",
-        ],
+    deps = [":base-lib"] + select({
+        "//build/bazel/platforms/os:android": [":android-lib"],
+        "//build/bazel/platforms/os:darwin": [":darwin-lib"],
+        "//build/bazel/platforms/os:fuchsia": [":fuchsia-lib"],
+        "//build/bazel/platforms/os:linux": [":linux-lib"],
+        "//build/bazel/platforms/os:linux_bionic": [":linux_bionic-lib"],
+        "//build/bazel/platforms/os:windows": [":windows-lib"],
         "//conditions:default": [],
     }),
 )`, `cc_library_headers(
@@ -278,7 +248,7 @@
     name = "exported-lib",
 )`, `cc_library_headers(
     name = "foo_headers",
-    deps = [] + select({
+    deps = select({
         "//build/bazel/platforms/os:android": [
             ":android-lib",
             ":exported-lib",
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index ef528e9..427aed3 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -194,6 +194,8 @@
         ":whole_static_lib_2",
     ],
     hdrs = [
+        "implicit_include_1.h",
+        "implicit_include_2.h",
         "export_include_dir_1/export_include_dir_1_a.h",
         "export_include_dir_1/export_include_dir_1_b.h",
         "export_include_dir_2/export_include_dir_2_a.h",
@@ -212,8 +214,6 @@
     srcs = [
         "foo_static1.cc",
         "foo_static2.cc",
-        "implicit_include_1.h",
-        "implicit_include_2.h",
         "include_dir_1/include_dir_1_a.h",
         "include_dir_1/include_dir_1_b.h",
         "include_dir_2/include_dir_2_a.h",
@@ -222,50 +222,60 @@
         "local_include_dir_1/local_include_dir_1_b.h",
         "local_include_dir_2/local_include_dir_2_a.h",
         "local_include_dir_2/local_include_dir_2_b.h",
+        "implicit_include_1.h",
+        "implicit_include_2.h",
     ],
 )`, `cc_library_static(
     name = "static_lib_1",
-    includes = [
-        ".",
-    ],
-    linkstatic = True,
-    srcs = [
+    hdrs = [
         "implicit_include_1.h",
         "implicit_include_2.h",
+    ],
+    includes = ["."],
+    linkstatic = True,
+    srcs = [
         "static_lib_1.cc",
+        "implicit_include_1.h",
+        "implicit_include_2.h",
     ],
 )`, `cc_library_static(
     name = "static_lib_2",
-    includes = [
-        ".",
-    ],
-    linkstatic = True,
-    srcs = [
+    hdrs = [
         "implicit_include_1.h",
         "implicit_include_2.h",
+    ],
+    includes = ["."],
+    linkstatic = True,
+    srcs = [
         "static_lib_2.cc",
+        "implicit_include_1.h",
+        "implicit_include_2.h",
     ],
 )`, `cc_library_static(
     name = "whole_static_lib_1",
-    includes = [
-        ".",
-    ],
-    linkstatic = True,
-    srcs = [
+    hdrs = [
         "implicit_include_1.h",
         "implicit_include_2.h",
+    ],
+    includes = ["."],
+    linkstatic = True,
+    srcs = [
         "whole_static_lib_1.cc",
+        "implicit_include_1.h",
+        "implicit_include_2.h",
     ],
 )`, `cc_library_static(
     name = "whole_static_lib_2",
-    includes = [
-        ".",
-    ],
-    linkstatic = True,
-    srcs = [
+    hdrs = [
         "implicit_include_1.h",
         "implicit_include_2.h",
+    ],
+    includes = ["."],
+    linkstatic = True,
+    srcs = [
         "whole_static_lib_2.cc",
+        "implicit_include_1.h",
+        "implicit_include_2.h",
     ],
 )`},
 		},
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index 4f3babe..a9d24ac 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -52,7 +52,6 @@
         "-Werror",
     ],
     srcs: [
-        "a/b/*.h",
         "a/b/*.c"
     ],
     exclude_srcs: ["a/b/exclude.c"],
@@ -68,15 +67,15 @@
         "-Wall",
         "-Werror",
     ],
+    hdrs = [
+        "a/b/bar.h",
+        "a/b/foo.h",
+    ],
     local_include_dirs = [
         "include",
         ".",
     ],
-    srcs = [
-        "a/b/bar.h",
-        "a/b/c.c",
-        "a/b/foo.h",
-    ],
+    srcs = ["a/b/c.c"],
 )`,
 			},
 		},
@@ -123,9 +122,7 @@
         "include",
         ".",
     ],
-    srcs = [
-        "a/b/c.c",
-    ],
+    srcs = ["a/b/c.c"],
 )`,
 			},
 		},
@@ -155,29 +152,15 @@
 `,
 			expectedBazelTargets: []string{`cc_object(
     name = "bar",
-    copts = [
-        "-fno-addrsig",
-    ],
-    local_include_dirs = [
-        ".",
-    ],
-    srcs = [
-        "x/y/z.c",
-    ],
+    copts = ["-fno-addrsig"],
+    local_include_dirs = ["."],
+    srcs = ["x/y/z.c"],
 )`, `cc_object(
     name = "foo",
-    copts = [
-        "-fno-addrsig",
-    ],
-    deps = [
-        ":bar",
-    ],
-    local_include_dirs = [
-        ".",
-    ],
-    srcs = [
-        "a/b/c.c",
-    ],
+    copts = ["-fno-addrsig"],
+    deps = [":bar"],
+    local_include_dirs = ["."],
+    srcs = ["a/b/c.c"],
 )`,
 			},
 		},
@@ -200,12 +183,8 @@
 `,
 			expectedBazelTargets: []string{`cc_object(
     name = "foo",
-    copts = [
-        "-fno-addrsig",
-    ],
-    srcs = [
-        "a/b/c.c",
-    ],
+    copts = ["-fno-addrsig"],
+    srcs = ["a/b/c.c"],
 )`,
 			},
 		},
@@ -228,12 +207,8 @@
 `,
 			expectedBazelTargets: []string{`cc_object(
     name = "foo",
-    asflags = [
-        "-DPLATFORM_SDK_VERSION={Platform_sdk_version}",
-    ],
-    copts = [
-        "-fno-addrsig",
-    ],
+    asflags = ["-DPLATFORM_SDK_VERSION={Platform_sdk_version}"],
+    copts = ["-fno-addrsig"],
 )`,
 			},
 		},
@@ -321,23 +296,13 @@
 			expectedBazelTargets: []string{
 				`cc_object(
     name = "foo",
-    copts = [
-        "-fno-addrsig",
-    ] + select({
-        "//build/bazel/platforms/arch:x86": [
-            "-fPIC",
-        ],
+    copts = ["-fno-addrsig"] + select({
+        "//build/bazel/platforms/arch:x86": ["-fPIC"],
         "//conditions:default": [],
     }),
-    local_include_dirs = [
-        ".",
-    ],
-    srcs = [
-        "a.cpp",
-    ] + select({
-        "//build/bazel/platforms/arch:arm": [
-            "arch/arm/file.S",
-        ],
+    local_include_dirs = ["."],
+    srcs = ["a.cpp"] + select({
+        "//build/bazel/platforms/arch:arm": ["arch/arm/file.S"],
         "//conditions:default": [],
     }),
 )`,
@@ -375,41 +340,19 @@
 			expectedBazelTargets: []string{
 				`cc_object(
     name = "foo",
-    copts = [
-        "-fno-addrsig",
-    ] + select({
-        "//build/bazel/platforms/arch:arm": [
-            "-Wall",
-        ],
-        "//build/bazel/platforms/arch:arm64": [
-            "-Wall",
-        ],
-        "//build/bazel/platforms/arch:x86": [
-            "-fPIC",
-        ],
-        "//build/bazel/platforms/arch:x86_64": [
-            "-fPIC",
-        ],
+    copts = ["-fno-addrsig"] + select({
+        "//build/bazel/platforms/arch:arm": ["-Wall"],
+        "//build/bazel/platforms/arch:arm64": ["-Wall"],
+        "//build/bazel/platforms/arch:x86": ["-fPIC"],
+        "//build/bazel/platforms/arch:x86_64": ["-fPIC"],
         "//conditions:default": [],
     }),
-    local_include_dirs = [
-        ".",
-    ],
-    srcs = [
-        "base.cpp",
-    ] + select({
-        "//build/bazel/platforms/arch:arm": [
-            "arm.cpp",
-        ],
-        "//build/bazel/platforms/arch:arm64": [
-            "arm64.cpp",
-        ],
-        "//build/bazel/platforms/arch:x86": [
-            "x86.cpp",
-        ],
-        "//build/bazel/platforms/arch:x86_64": [
-            "x86_64.cpp",
-        ],
+    local_include_dirs = ["."],
+    srcs = ["base.cpp"] + select({
+        "//build/bazel/platforms/arch:arm": ["arm.cpp"],
+        "//build/bazel/platforms/arch:arm64": ["arm64.cpp"],
+        "//build/bazel/platforms/arch:x86": ["x86.cpp"],
+        "//build/bazel/platforms/arch:x86_64": ["x86_64.cpp"],
         "//conditions:default": [],
     }),
 )`,
@@ -440,26 +383,14 @@
 			expectedBazelTargets: []string{
 				`cc_object(
     name = "foo",
-    copts = [
-        "-fno-addrsig",
-    ] + select({
-        "//build/bazel/platforms/os:android": [
-            "-fPIC",
-        ],
-        "//build/bazel/platforms/os:darwin": [
-            "-Wall",
-        ],
-        "//build/bazel/platforms/os:windows": [
-            "-fPIC",
-        ],
+    copts = ["-fno-addrsig"] + select({
+        "//build/bazel/platforms/os:android": ["-fPIC"],
+        "//build/bazel/platforms/os:darwin": ["-Wall"],
+        "//build/bazel/platforms/os:windows": ["-fPIC"],
         "//conditions:default": [],
     }),
-    local_include_dirs = [
-        ".",
-    ],
-    srcs = [
-        "base.cpp",
-    ],
+    local_include_dirs = ["."],
+    srcs = ["base.cpp"],
 )`,
 			},
 		},
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index b2b3379..97729df 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -69,20 +69,26 @@
 		return ret, err
 	}
 
-	// Create the selects for arch specific values.
-	selectMap, err := prettyPrintSelectMap(archSelects, "[]", indent)
+	// Convenience function to append selects components to an attribute value.
+	appendSelects := func(selectsData selects, defaultValue, s string) (string, error) {
+		selectMap, err := prettyPrintSelectMap(selectsData, defaultValue, indent)
+		if err != nil {
+			return "", err
+		}
+		if s != "" && selectMap != "" {
+			s += " + "
+		}
+		s += selectMap
+
+		return s, nil
+	}
+
+	ret, err = appendSelects(archSelects, "[]", ret)
 	if err != nil {
 		return "", err
 	}
-	ret += selectMap
 
-	// Create the selects for target os specific values.
-	selectMap, err = prettyPrintSelectMap(osSelects, "[]", indent)
-	if err != nil {
-		return "", err
-	}
-	ret += selectMap
-
+	ret, err = appendSelects(osSelects, "[]", ret)
 	return ret, err
 }
 
@@ -113,7 +119,7 @@
 	}
 
 	// Create the map.
-	ret := " + select({\n"
+	ret := "select({\n"
 	ret += selects
 	// default condition comes last.
 	ret += fmt.Sprintf("%s\"%s\": %s,\n", makeIndent(indent+1), "//conditions:default", defaultValue)
diff --git a/bp2build/python_binary_conversion_test.go b/bp2build/python_binary_conversion_test.go
index 7600e36..2054e06 100644
--- a/bp2build/python_binary_conversion_test.go
+++ b/bp2build/python_binary_conversion_test.go
@@ -33,24 +33,15 @@
 			blueprint: `python_binary_host {
     name: "foo",
     main: "a.py",
-    srcs: [
-        "**/*.py"
-    ],
-    exclude_srcs: [
-        "b/e.py"
-    ],
-    data: [
-        "files/data.txt",
-    ],
-
+    srcs: ["**/*.py"],
+    exclude_srcs: ["b/e.py"],
+    data: ["files/data.txt",],
     bazel_module: { bp2build_available: true },
 }
 `,
 			expectedBazelTargets: []string{`py_binary(
     name = "foo",
-    data = [
-        "files/data.txt",
-    ],
+    data = ["files/data.txt"],
     main = "a.py",
     srcs = [
         "a.py",
@@ -83,9 +74,7 @@
 			expectedBazelTargets: []string{`py_binary(
     name = "foo",
     python_version = "PY2",
-    srcs = [
-        "a.py",
-    ],
+    srcs = ["a.py"],
 )`,
 			},
 		},
@@ -113,9 +102,7 @@
 				// python_version is PY3 by default.
 				`py_binary(
     name = "foo",
-    srcs = [
-        "a.py",
-    ],
+    srcs = ["a.py"],
 )`,
 			},
 		},
diff --git a/bp2build/sh_conversion_test.go b/bp2build/sh_conversion_test.go
index 2aa373c..37f542e 100644
--- a/bp2build/sh_conversion_test.go
+++ b/bp2build/sh_conversion_test.go
@@ -74,9 +74,7 @@
 }`,
 			expectedBazelTargets: []string{`sh_binary(
     name = "foo",
-    srcs = [
-        "foo.sh",
-    ],
+    srcs = ["foo.sh"],
 )`},
 		},
 	}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index cffeb24..0bca30a 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -59,72 +59,111 @@
 	ctx.AddDependency(module, nil, android.SortedUniqueStrings(allDeps)...)
 }
 
-// bp2buildParseCflags creates a label list attribute containing the cflags of a module, including
-func bp2BuildParseCflags(ctx android.TopDownMutatorContext, module *Module) bazel.StringListAttribute {
-	var ret bazel.StringListAttribute
+// Convenience struct to hold all attributes parsed from compiler properties.
+type compilerAttributes struct {
+	copts bazel.StringListAttribute
+	srcs  bazel.LabelListAttribute
+	hdrs  bazel.LabelListAttribute
+}
+
+// bp2BuildParseCompilerProps returns copts, srcs and hdrs and other attributes.
+func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Module) compilerAttributes {
+	var hdrs, srcs bazel.LabelListAttribute
+	var copts bazel.StringListAttribute
+
+	hdrsAndSrcs := func(baseCompilerProps *BaseCompilerProperties) (bazel.LabelList, bazel.LabelList) {
+		srcsList := android.BazelLabelForModuleSrcExcludes(
+			ctx, baseCompilerProps.Srcs, baseCompilerProps.Exclude_srcs)
+		hdrsList := android.BazelLabelForModuleSrc(ctx, srcsList.LooseHdrsGlobs(headerExts))
+		return hdrsList, srcsList
+	}
+
 	for _, props := range module.compiler.compilerProps() {
 		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			ret.Value = baseCompilerProps.Cflags
+			hdrs.Value, srcs.Value = hdrsAndSrcs(baseCompilerProps)
+			copts.Value = baseCompilerProps.Cflags
 			break
 		}
 	}
 
 	for arch, props := range module.GetArchProperties(&BaseCompilerProperties{}) {
 		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			ret.SetValueForArch(arch.Name, baseCompilerProps.Cflags)
+			hdrsList, srcsList := hdrsAndSrcs(baseCompilerProps)
+			hdrs.SetValueForArch(arch.Name, bazel.SubtractBazelLabelList(hdrsList, hdrs.Value))
+			srcs.SetValueForArch(arch.Name, srcsList)
+			copts.SetValueForArch(arch.Name, baseCompilerProps.Cflags)
 		}
 	}
 
 	for os, props := range module.GetTargetProperties(&BaseCompilerProperties{}) {
 		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			ret.SetValueForOS(os.Name, baseCompilerProps.Cflags)
+			hdrsList, srcsList := hdrsAndSrcs(baseCompilerProps)
+			hdrs.SetValueForOS(os.Name, bazel.SubtractBazelLabelList(hdrsList, hdrs.Value))
+			srcs.SetValueForOS(os.Name, srcsList)
+			copts.SetValueForOS(os.Name, baseCompilerProps.Cflags)
 		}
 	}
 
-	return ret
+	return compilerAttributes{
+		hdrs:  hdrs,
+		srcs:  srcs,
+		copts: copts,
+	}
 }
 
-// bp2BuildParseHeaderLibs creates a label list attribute containing the header library deps of a module, including
+// Convenience struct to hold all attributes parsed from linker properties.
+type linkerAttributes struct {
+	deps     bazel.LabelListAttribute
+	linkopts bazel.StringListAttribute
+}
+
+// bp2BuildParseLinkerProps creates a label list attribute containing the header library deps of a module, including
 // configurable attribute values.
-func bp2BuildParseHeaderLibs(ctx android.TopDownMutatorContext, module *Module) bazel.LabelListAttribute {
-	var ret bazel.LabelListAttribute
+func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) linkerAttributes {
+
+	var deps bazel.LabelListAttribute
+	var linkopts bazel.StringListAttribute
+
 	for _, linkerProps := range module.linker.linkerProps() {
 		if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok {
 			libs := baseLinkerProps.Header_libs
 			libs = append(libs, baseLinkerProps.Export_header_lib_headers...)
-			ret = bazel.MakeLabelListAttribute(
+			deps = bazel.MakeLabelListAttribute(
 				android.BazelLabelForModuleDeps(ctx, android.SortedUniqueStrings(libs)))
+			linkopts.Value = baseLinkerProps.Ldflags
 			break
 		}
 	}
 
+	for arch, p := range module.GetArchProperties(&BaseLinkerProperties{}) {
+		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
+			libs := baseLinkerProps.Header_libs
+			libs = append(libs, baseLinkerProps.Export_header_lib_headers...)
+			libs = android.SortedUniqueStrings(libs)
+			deps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, libs))
+			linkopts.SetValueForArch(arch.Name, baseLinkerProps.Ldflags)
+		}
+	}
+
 	for os, p := range module.GetTargetProperties(&BaseLinkerProperties{}) {
 		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
 			libs := baseLinkerProps.Header_libs
 			libs = append(libs, baseLinkerProps.Export_header_lib_headers...)
 			libs = android.SortedUniqueStrings(libs)
-			ret.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, libs))
+			deps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, libs))
+			linkopts.SetValueForOS(os.Name, baseLinkerProps.Ldflags)
 		}
 	}
 
-	return ret
+	return linkerAttributes{
+		deps:     deps,
+		linkopts: linkopts,
+	}
 }
 
 func bp2BuildListHeadersInDir(ctx android.TopDownMutatorContext, includeDir string) bazel.LabelList {
-	var globInfix string
-
-	if includeDir == "." {
-		globInfix = ""
-	} else {
-		globInfix = "/**"
-	}
-
-	var includeDirGlobs []string
-	includeDirGlobs = append(includeDirGlobs, includeDir+globInfix+"/*.h")
-	includeDirGlobs = append(includeDirGlobs, includeDir+globInfix+"/*.inc")
-	includeDirGlobs = append(includeDirGlobs, includeDir+globInfix+"/*.hpp")
-
-	return android.BazelLabelForModuleSrc(ctx, includeDirGlobs)
+	globs := bazel.GlobsInDir(includeDir, includeDir != ".", headerExts)
+	return android.BazelLabelForModuleSrc(ctx, globs)
 }
 
 // Bazel wants include paths to be relative to the module
diff --git a/cc/cc.go b/cc/cc.go
index 1ce83a9..9176bc3 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -56,8 +56,8 @@
 		ctx.TopDown("asan_deps", sanitizerDepsMutator(Asan))
 		ctx.BottomUp("asan", sanitizerMutator(Asan)).Parallel()
 
-		ctx.TopDown("hwasan_deps", sanitizerDepsMutator(hwasan))
-		ctx.BottomUp("hwasan", sanitizerMutator(hwasan)).Parallel()
+		ctx.TopDown("hwasan_deps", sanitizerDepsMutator(Hwasan))
+		ctx.BottomUp("hwasan", sanitizerMutator(Hwasan)).Parallel()
 
 		ctx.TopDown("fuzzer_deps", sanitizerDepsMutator(Fuzzer))
 		ctx.BottomUp("fuzzer", sanitizerMutator(Fuzzer)).Parallel()
diff --git a/cc/library.go b/cc/library.go
index 18f9fae..738b45f 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -206,6 +206,7 @@
 	RegisterLibraryBuildComponents(android.InitRegistrationContext)
 
 	android.RegisterBp2BuildMutator("cc_library_static", CcLibraryStaticBp2Build)
+	android.RegisterBp2BuildMutator("cc_library", CcLibraryBp2Build)
 }
 
 func RegisterLibraryBuildComponents(ctx android.RegistrationContext) {
@@ -216,6 +217,67 @@
 	ctx.RegisterModuleType("cc_library_host_shared", LibraryHostSharedFactory)
 }
 
+// For bp2build conversion.
+type bazelCcLibraryAttributes struct {
+	Srcs            bazel.LabelListAttribute
+	Hdrs            bazel.LabelListAttribute
+	Copts           bazel.StringListAttribute
+	Linkopts        bazel.StringListAttribute
+	Deps            bazel.LabelListAttribute
+	User_link_flags bazel.StringListAttribute
+	Includes        bazel.StringListAttribute
+}
+
+type bazelCcLibrary struct {
+	android.BazelTargetModuleBase
+	bazelCcLibraryAttributes
+}
+
+func (m *bazelCcLibrary) Name() string {
+	return m.BaseModuleName()
+}
+
+func (m *bazelCcLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+
+func BazelCcLibraryFactory() android.Module {
+	module := &bazelCcLibrary{}
+	module.AddProperties(&module.bazelCcLibraryAttributes)
+	android.InitBazelTargetModule(module)
+	return module
+}
+
+func CcLibraryBp2Build(ctx android.TopDownMutatorContext) {
+	m, ok := ctx.Module().(*Module)
+	if !ok || !m.ConvertWithBp2build(ctx) {
+		return
+	}
+
+	if ctx.ModuleType() != "cc_library" {
+		return
+	}
+
+	compilerAttrs := bp2BuildParseCompilerProps(ctx, m)
+	linkerAttrs := bp2BuildParseLinkerProps(ctx, m)
+	exportedIncludes, exportedIncludesHeaders := bp2BuildParseExportedIncludes(ctx, m)
+	compilerAttrs.hdrs.Append(exportedIncludesHeaders)
+
+	attrs := &bazelCcLibraryAttributes{
+		Srcs:     compilerAttrs.srcs,
+		Hdrs:     compilerAttrs.hdrs,
+		Copts:    compilerAttrs.copts,
+		Linkopts: linkerAttrs.linkopts,
+		Deps:     linkerAttrs.deps,
+		Includes: exportedIncludes,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "cc_library",
+		Bzl_load_location: "//build/bazel/rules:full_cc_library.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(BazelCcLibraryFactory, m.Name(), props, attrs)
+}
+
 // cc_library creates both static and/or shared libraries for a device and/or
 // host. By default, a cc_library has a single variant that targets the device.
 // Specifying `host_supported: true` also creates a library that targets the
@@ -2058,9 +2120,10 @@
 }
 
 type bazelCcLibraryStaticAttributes struct {
-	Copts      []string
+	Copts      bazel.StringListAttribute
 	Srcs       bazel.LabelListAttribute
 	Deps       bazel.LabelListAttribute
+	Linkopts   bazel.StringListAttribute
 	Linkstatic bool
 	Includes   bazel.StringListAttribute
 	Hdrs       bazel.LabelListAttribute
@@ -2091,14 +2154,13 @@
 		return
 	}
 
-	var copts []string
-	var srcs []string
+	compilerAttrs := bp2BuildParseCompilerProps(ctx, module)
+
 	var includeDirs []string
 	var localIncludeDirs []string
 	for _, props := range module.compiler.compilerProps() {
 		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			copts = baseCompilerProps.Cflags
-			srcs = baseCompilerProps.Srcs
+			// TODO: these should be arch and os specific.
 			includeDirs = bp2BuildMakePathsRelativeToModule(ctx, baseCompilerProps.Include_dirs)
 			localIncludeDirs = bp2BuildMakePathsRelativeToModule(ctx, baseCompilerProps.Local_include_dirs)
 			break
@@ -2111,19 +2173,18 @@
 		localIncludeDirs = append(localIncludeDirs, ".")
 	}
 
-	srcsLabels := android.BazelLabelForModuleSrc(ctx, srcs)
-
 	// For Bazel, be more explicit about headers - list all header files in include dirs as srcs
 	for _, includeDir := range includeDirs {
-		srcsLabels.Append(bp2BuildListHeadersInDir(ctx, includeDir))
+		compilerAttrs.srcs.Value.Append(bp2BuildListHeadersInDir(ctx, includeDir))
 	}
 	for _, localIncludeDir := range localIncludeDirs {
-		srcsLabels.Append(bp2BuildListHeadersInDir(ctx, localIncludeDir))
+		compilerAttrs.srcs.Value.Append(bp2BuildListHeadersInDir(ctx, localIncludeDir))
 	}
 
 	var staticLibs []string
 	var wholeStaticLibs []string
 	for _, props := range module.linker.linkerProps() {
+		// TODO: move this into bp2buildParseLinkerProps
 		if baseLinkerProperties, ok := props.(*BaseLinkerProperties); ok {
 			staticLibs = baseLinkerProperties.Static_libs
 			wholeStaticLibs = baseLinkerProperties.Whole_static_libs
@@ -2145,16 +2206,18 @@
 	allIncludes.Value = append(allIncludes.Value, includeDirs...)
 	allIncludes.Value = append(allIncludes.Value, localIncludeDirs...)
 
-	headerLibsLabels := bp2BuildParseHeaderLibs(ctx, module)
-	depsLabels.Append(headerLibsLabels.Value)
+	compilerAttrs.hdrs.Append(exportedIncludesHeaders)
+
+	linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
+	depsLabels.Append(linkerAttrs.deps.Value)
 
 	attrs := &bazelCcLibraryStaticAttributes{
-		Copts:      copts,
-		Srcs:       bazel.MakeLabelListAttribute(srcsLabels),
+		Copts:      compilerAttrs.copts,
+		Srcs:       compilerAttrs.srcs,
 		Deps:       bazel.MakeLabelListAttribute(depsLabels),
 		Linkstatic: true,
 		Includes:   allIncludes,
-		Hdrs:       exportedIncludesHeaders,
+		Hdrs:       compilerAttrs.hdrs,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/cc/library_headers.go b/cc/library_headers.go
index d35748b..076ce80 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -96,14 +96,14 @@
 	}
 
 	exportedIncludes, exportedIncludesHeaders := bp2BuildParseExportedIncludes(ctx, module)
-
-	headerLibs := bp2BuildParseHeaderLibs(ctx, module)
+	compilerAttrs := bp2BuildParseCompilerProps(ctx, module)
+	linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
 
 	attrs := &bazelCcLibraryHeadersAttributes{
-		Copts:    bp2BuildParseCflags(ctx, module),
+		Copts:    compilerAttrs.copts,
 		Includes: exportedIncludes,
 		Hdrs:     exportedIncludesHeaders,
-		Deps:     headerLibs,
+		Deps:     linkerAttrs.deps,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/cc/object.go b/cc/object.go
index 4f8797d..9bb279a 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -113,6 +113,7 @@
 // For bp2build conversion.
 type bazelObjectAttributes struct {
 	Srcs               bazel.LabelListAttribute
+	Hdrs               bazel.LabelListAttribute
 	Deps               bazel.LabelListAttribute
 	Copts              bazel.StringListAttribute
 	Asflags            []string
@@ -156,16 +157,11 @@
 	}
 
 	// Set arch-specific configurable attributes
-	var srcs bazel.LabelListAttribute
+	compilerAttrs := bp2BuildParseCompilerProps(ctx, m)
 	var localIncludeDirs []string
 	var asFlags []string
 	for _, props := range m.compiler.compilerProps() {
 		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			srcs = bazel.MakeLabelListAttribute(
-				android.BazelLabelForModuleSrcExcludes(
-					ctx,
-					baseCompilerProps.Srcs,
-					baseCompilerProps.Exclude_srcs))
 			localIncludeDirs = baseCompilerProps.Local_include_dirs
 			break
 		}
@@ -200,16 +196,11 @@
 	}
 	// TODO(b/183595872) warn/error if we're not handling product variables
 
-	for arch, p := range m.GetArchProperties(&BaseCompilerProperties{}) {
-		if cProps, ok := p.(*BaseCompilerProperties); ok {
-			srcs.SetValueForArch(arch.Name, android.BazelLabelForModuleSrcExcludes(ctx, cProps.Srcs, cProps.Exclude_srcs))
-		}
-	}
-
 	attrs := &bazelObjectAttributes{
-		Srcs:               srcs,
+		Srcs:               compilerAttrs.srcs,
+		Hdrs:               compilerAttrs.hdrs,
 		Deps:               deps,
-		Copts:              bp2BuildParseCflags(ctx, m),
+		Copts:              compilerAttrs.copts,
 		Asflags:            asFlags,
 		Local_include_dirs: localIncludeDirs,
 	}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 8f17e4e..397121e 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -83,7 +83,7 @@
 
 const (
 	Asan SanitizerType = iota + 1
-	hwasan
+	Hwasan
 	tsan
 	intOverflow
 	cfi
@@ -97,7 +97,7 @@
 	switch t {
 	case Asan:
 		return "asan"
-	case hwasan:
+	case Hwasan:
 		return "hwasan"
 	case tsan:
 		return "tsan"
@@ -121,7 +121,7 @@
 	switch t {
 	case Asan:
 		return "address"
-	case hwasan:
+	case Hwasan:
 		return "hwaddress"
 	case memtag_heap:
 		return "memtag_heap"
@@ -144,7 +144,7 @@
 	switch t {
 	case Asan:
 		return true
-	case hwasan:
+	case Hwasan:
 		return true
 	case tsan:
 		return true
@@ -163,7 +163,7 @@
 
 // incompatibleWithCfi returns true if a sanitizer is incompatible with CFI.
 func (t SanitizerType) incompatibleWithCfi() bool {
-	return t == Asan || t == Fuzzer || t == hwasan
+	return t == Asan || t == Fuzzer || t == Hwasan
 }
 
 type SanitizeUserProps struct {
@@ -745,7 +745,7 @@
 	switch t {
 	case Asan:
 		return sanitize.Properties.Sanitize.Address
-	case hwasan:
+	case Hwasan:
 		return sanitize.Properties.Sanitize.Hwaddress
 	case tsan:
 		return sanitize.Properties.Sanitize.Thread
@@ -767,7 +767,7 @@
 // isUnsanitizedVariant returns true if no sanitizers are enabled.
 func (sanitize *sanitize) isUnsanitizedVariant() bool {
 	return !sanitize.isSanitizerEnabled(Asan) &&
-		!sanitize.isSanitizerEnabled(hwasan) &&
+		!sanitize.isSanitizerEnabled(Hwasan) &&
 		!sanitize.isSanitizerEnabled(tsan) &&
 		!sanitize.isSanitizerEnabled(cfi) &&
 		!sanitize.isSanitizerEnabled(scs) &&
@@ -778,7 +778,7 @@
 // isVariantOnProductionDevice returns true if variant is for production devices (no non-production sanitizers enabled).
 func (sanitize *sanitize) isVariantOnProductionDevice() bool {
 	return !sanitize.isSanitizerEnabled(Asan) &&
-		!sanitize.isSanitizerEnabled(hwasan) &&
+		!sanitize.isSanitizerEnabled(Hwasan) &&
 		!sanitize.isSanitizerEnabled(tsan) &&
 		!sanitize.isSanitizerEnabled(Fuzzer)
 }
@@ -787,7 +787,7 @@
 	switch t {
 	case Asan:
 		sanitize.Properties.Sanitize.Address = boolPtr(b)
-	case hwasan:
+	case Hwasan:
 		sanitize.Properties.Sanitize.Hwaddress = boolPtr(b)
 	case tsan:
 		sanitize.Properties.Sanitize.Thread = boolPtr(b)
@@ -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 {
 							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.
@@ -1286,7 +1286,7 @@
 					// For cfi/scs/hwasan, we can export both sanitized and un-sanitized variants
 					// to Make, because the sanitized version has a different suffix in name.
 					// For other types of sanitizers, suppress the variation that is disabled.
-					if t != cfi && t != scs && t != hwasan {
+					if t != cfi && t != scs && t != Hwasan {
 						if isSanitizerEnabled {
 							modules[0].(PlatformSanitizeable).SetPreventInstall()
 							modules[0].(PlatformSanitizeable).SetHideFromMake()
@@ -1300,7 +1300,7 @@
 					if c.StaticallyLinked() && c.ExportedToMake() {
 						if t == cfi {
 							cfiStaticLibs(mctx.Config()).add(c, c.Module().Name())
-						} else if t == hwasan {
+						} else if t == Hwasan {
 							hwasanStaticLibs(mctx.Config()).add(c, c.Module().Name())
 						}
 					}
@@ -1417,7 +1417,7 @@
 
 func hwasanStaticLibs(config android.Config) *sanitizerStaticLibsMap {
 	return config.Once(hwasanStaticLibsKey, func() interface{} {
-		return newSanitizerStaticLibsMap(hwasan)
+		return newSanitizerStaticLibsMap(Hwasan)
 	}).(*sanitizerStaticLibsMap)
 }
 
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 3d31be4..3437d77 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -196,7 +196,7 @@
 		if m.sanitize != nil {
 			// scs and hwasan export both sanitized and unsanitized variants for static and header
 			// Always use unsanitized variants of them.
-			for _, t := range []SanitizerType{scs, hwasan} {
+			for _, t := range []SanitizerType{scs, Hwasan} {
 				if !l.shared() && m.sanitize.isSanitizerEnabled(t) {
 					return false
 				}
diff --git a/rust/androidmk.go b/rust/androidmk.go
index b0e6967..dea32a3 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -70,6 +70,11 @@
 		// If the compiler is disabled, this is a SourceProvider.
 		mod.SubAndroidMk(&ret, mod.sourceProvider)
 	}
+
+	if mod.sanitize != nil {
+		mod.SubAndroidMk(&ret, mod.sanitize)
+	}
+
 	ret.SubName += mod.Properties.SubName
 
 	return []android.AndroidMkEntries{ret}
diff --git a/rust/config/global.go b/rust/config/global.go
index 9208ddb..18776ab 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.50.0"
+	RustDefaultVersion = "1.51.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2018"
 	Stdlibs            = []string{
diff --git a/rust/config/lints.go b/rust/config/lints.go
index 7c05e4f..ef6b315 100644
--- a/rust/config/lints.go
+++ b/rust/config/lints.go
@@ -54,6 +54,7 @@
 		"-A clippy::type-complexity",
 		"-A clippy::unnecessary-wraps",
 		"-A clippy::unusual-byte-groupings",
+		"-A clippy::upper-case-acronyms",
 	}
 
 	// Rust lints for vendor code.
diff --git a/rust/sanitize.go b/rust/sanitize.go
index 2498aa1..ae3eff0 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -23,11 +23,12 @@
 )
 
 type SanitizeProperties struct {
-	// enable AddressSanitizer, ThreadSanitizer, or UndefinedBehaviorSanitizer
+	// enable AddressSanitizer, HWAddressSanitizer, and others.
 	Sanitize struct {
-		Address *bool `android:"arch_variant"`
-		Fuzzer  *bool `android:"arch_variant"`
-		Never   *bool `android:"arch_variant"`
+		Address   *bool `android:"arch_variant"`
+		Hwaddress *bool `android:"arch_variant"`
+		Fuzzer    *bool `android:"arch_variant"`
+		Never     *bool `android:"arch_variant"`
 	}
 	SanitizerEnabled bool `blueprint:"mutated"`
 	SanitizeDep      bool `blueprint:"mutated"`
@@ -43,7 +44,6 @@
 	"-C llvm-args=-sanitizer-coverage-level=3",
 	"-C llvm-args=-sanitizer-coverage-trace-compares",
 	"-C llvm-args=-sanitizer-coverage-inline-8bit-counters",
-	"-C llvm-args=-sanitizer-coverage-stack-depth",
 	"-C llvm-args=-sanitizer-coverage-trace-geps",
 	"-C llvm-args=-sanitizer-coverage-prune-blocks=0",
 	"-Z sanitizer=address",
@@ -57,6 +57,11 @@
 	"-Z sanitizer=address",
 }
 
+var hwasanFlags = []string{
+	"-Z sanitizer=hwaddress",
+	"-C target-feature=+tagged-globals",
+}
+
 func boolPtr(v bool) *bool {
 	if v {
 		return &v
@@ -83,6 +88,15 @@
 	if ctx.Os() == android.Android && Bool(s.Address) {
 		sanitize.Properties.SanitizerEnabled = true
 	}
+
+	// HWASan requires AArch64 hardware feature (top-byte-ignore).
+	if ctx.Arch().ArchType != android.Arm64 {
+		s.Hwaddress = nil
+	}
+
+	if ctx.Os() == android.Android && Bool(s.Hwaddress) {
+		sanitize.Properties.SanitizerEnabled = true
+	}
 }
 
 type sanitize struct {
@@ -99,6 +113,9 @@
 	if Bool(sanitize.Properties.Sanitize.Address) {
 		flags.RustFlags = append(flags.RustFlags, asanFlags...)
 	}
+	if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+		flags.RustFlags = append(flags.RustFlags, hwasanFlags...)
+	}
 	return flags, deps
 }
 
@@ -111,11 +128,38 @@
 		if !mod.Enabled() {
 			return
 		}
+
+		variations := mctx.Target().Variations()
+		var depTag blueprint.DependencyTag
+		var deps []string
+
 		if Bool(mod.sanitize.Properties.Sanitize.Fuzzer) || Bool(mod.sanitize.Properties.Sanitize.Address) {
-			mctx.AddFarVariationDependencies(append(mctx.Target().Variations(), []blueprint.Variation{
-				{Mutator: "link", Variation: "shared"},
-			}...), cc.SharedDepTag(), config.LibclangRuntimeLibrary(mod.toolchain(mctx), "asan"))
+			variations = append(variations,
+				blueprint.Variation{Mutator: "link", Variation: "shared"})
+			depTag = cc.SharedDepTag()
+			deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "asan")}
+		} else if mod.IsSanitizerEnabled(cc.Hwasan) {
+			// TODO(b/180495975): HWASan for static Rust binaries isn't supported yet.
+			if binary, ok := mod.compiler.(*binaryDecorator); ok {
+				if Bool(binary.Properties.Static_executable) {
+					mctx.ModuleErrorf("HWASan is not supported for static Rust executables yet.")
+				}
+			}
+
+			if mod.StaticallyLinked() {
+				variations = append(variations,
+					blueprint.Variation{Mutator: "link", Variation: "static"})
+				depTag = cc.StaticDepTag(false)
+				deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "hwasan_static")}
+			} else {
+				variations = append(variations,
+					blueprint.Variation{Mutator: "link", Variation: "shared"})
+				depTag = cc.SharedDepTag()
+				deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "hwasan")}
+			}
 		}
+
+		mctx.AddFarVariationDependencies(variations, depTag, deps...)
 	}
 }
 
@@ -128,6 +172,9 @@
 	case cc.Asan:
 		sanitize.Properties.Sanitize.Address = boolPtr(b)
 		sanitizerSet = true
+	case cc.Hwasan:
+		sanitize.Properties.Sanitize.Hwaddress = boolPtr(b)
+		sanitizerSet = true
 	default:
 		panic(fmt.Errorf("setting unsupported sanitizerType %d", t))
 	}
@@ -169,11 +216,23 @@
 		return sanitize.Properties.Sanitize.Fuzzer
 	case cc.Asan:
 		return sanitize.Properties.Sanitize.Address
+	case cc.Hwasan:
+		return sanitize.Properties.Sanitize.Hwaddress
 	default:
 		return nil
 	}
 }
 
+func (sanitize *sanitize) AndroidMk(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+	// Add a suffix for hwasan rlib libraries to allow surfacing both the sanitized and
+	// non-sanitized variants to make without a name conflict.
+	if entries.Class == "RLIB_LIBRARIES" || entries.Class == "STATIC_LIBRARIES" {
+		if sanitize.isSanitizerEnabled(cc.Hwasan) {
+			entries.SubName += ".hwasan"
+		}
+	}
+}
+
 func (mod *Module) SanitizerSupported(t cc.SanitizerType) bool {
 	if mod.Host() {
 		return false
@@ -183,6 +242,8 @@
 		return true
 	case cc.Asan:
 		return true
+	case cc.Hwasan:
+		return true
 	default:
 		return false
 	}