Merge "Add recovery image sdk trait to cc_library_headers"
diff --git a/android/bazel.go b/android/bazel.go
index 9e8d2e3..962a8f0e 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -223,13 +223,9 @@
 		"libbase_ndk", // http://b/186826477, cc_library, no such target '//build/bazel/platforms/os:darwin' when --platforms //build/bazel/platforms:android_x86 is added
 		// libcxx
 		"libBionicBenchmarksUtils", // cc_library_static, fatal error: 'map' file not found, from libcxx
-		"fmtlib",                   // cc_library_static, fatal error: 'cassert' file not found, from libcxx
-		"fmtlib_ndk",               // cc_library_static, fatal error: 'cassert' file not found
-		"liblog",                   // http://b/186822772: cc_library, 'sys/cdefs.h' file not found
-		"libbase",                  // Requires liblog. http://b/186826479, cc_library, fatal error: 'memory' file not found, from libcxx.
-		// Also depends on fmtlib.
+		"libbase",                  // Depends on fmtlib via static_libs and also whole_static_libs, which results in bazel errors.
 
-		"libfdtrack", // depends on STL
+		"libfdtrack", // depends on liblzma and libbase
 
 		"libseccomp_policy", // depends on libbase
 
diff --git a/android/variable.go b/android/variable.go
index a308d2b..ef453d7 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -61,6 +61,8 @@
 			Shared_libs         []string `android:"arch_variant"`
 			Whole_static_libs   []string `android:"arch_variant"`
 			Exclude_static_libs []string `android:"arch_variant"`
+			Srcs                []string `android:"arch_variant"`
+			Header_libs         []string `android:"arch_variant"`
 		} `android:"arch_variant"`
 
 		Malloc_zero_contents struct {
diff --git a/bazel/Android.bp b/bazel/Android.bp
index b68d65b..80af2bd 100644
--- a/bazel/Android.bp
+++ b/bazel/Android.bp
@@ -14,6 +14,7 @@
     testSrcs: [
         "aquery_test.go",
         "properties_test.go",
+        "testing.go",
     ],
     pluginFor: [
         "soong_build",
diff --git a/bazel/properties.go b/bazel/properties.go
index d3b40a2..bd8ef0d 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -19,6 +19,9 @@
 	"path/filepath"
 	"regexp"
 	"sort"
+	"strings"
+
+	"github.com/google/blueprint"
 )
 
 // BazelTargetModuleProperties contain properties and metadata used for
@@ -182,76 +185,6 @@
 	return strings
 }
 
-// Map a function over all labels in a LabelList.
-func MapLabelList(mapOver LabelList, mapFn func(string) string) LabelList {
-	var includes []Label
-	for _, inc := range mapOver.Includes {
-		mappedLabel := Label{Label: mapFn(inc.Label), OriginalModuleName: inc.OriginalModuleName}
-		includes = append(includes, mappedLabel)
-	}
-	// mapFn is not applied over excludes, but they are propagated as-is.
-	return LabelList{Includes: includes, Excludes: mapOver.Excludes}
-}
-
-// Map a function over all Labels in a LabelListAttribute
-func MapLabelListAttribute(mapOver LabelListAttribute, mapFn func(string) string) LabelListAttribute {
-	var result LabelListAttribute
-
-	result.Value = MapLabelList(mapOver.Value, mapFn)
-
-	for axis, configToLabels := range mapOver.ConfigurableValues {
-		for config, value := range configToLabels {
-			result.SetSelectValue(axis, config, MapLabelList(value, mapFn))
-		}
-	}
-
-	return result
-}
-
-// Return all needles in a given haystack, where needleFn is true for needles.
-func FilterLabelList(haystack LabelList, needleFn func(string) bool) LabelList {
-	var includes []Label
-	for _, inc := range haystack.Includes {
-		if needleFn(inc.Label) {
-			includes = append(includes, inc)
-		}
-	}
-	// needleFn is not applied over excludes, but they are propagated as-is.
-	return LabelList{Includes: includes, Excludes: haystack.Excludes}
-}
-
-// Return all needles in a given haystack, where needleFn is true for needles.
-func FilterLabelListAttribute(haystack LabelListAttribute, needleFn func(string) bool) LabelListAttribute {
-	result := MakeLabelListAttribute(FilterLabelList(haystack.Value, needleFn))
-
-	for config, selects := range haystack.ConfigurableValues {
-		newSelects := make(labelListSelectValues, len(selects))
-		for k, v := range selects {
-			newSelects[k] = FilterLabelList(v, needleFn)
-		}
-		result.ConfigurableValues[config] = newSelects
-	}
-
-	return result
-}
-
-// Subtract needle from haystack
-func SubtractBazelLabelListAttribute(haystack LabelListAttribute, needle LabelListAttribute) LabelListAttribute {
-	result := MakeLabelListAttribute(SubtractBazelLabelList(haystack.Value, needle.Value))
-
-	for config, selects := range haystack.ConfigurableValues {
-		newSelects := make(labelListSelectValues, len(selects))
-		needleSelects := needle.ConfigurableValues[config]
-
-		for k, v := range selects {
-			newSelects[k] = SubtractBazelLabelList(v, needleSelects[k])
-		}
-		result.ConfigurableValues[config] = newSelects
-	}
-
-	return result
-}
-
 // Subtract needle from haystack
 func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
 	// This is really a set
@@ -624,6 +557,144 @@
 	}
 }
 
+// OtherModuleContext is a limited context that has methods with information about other modules.
+type OtherModuleContext interface {
+	ModuleFromName(name string) (blueprint.Module, bool)
+	OtherModuleType(m blueprint.Module) string
+	OtherModuleName(m blueprint.Module) string
+	OtherModuleDir(m blueprint.Module) string
+	ModuleErrorf(fmt string, args ...interface{})
+}
+
+// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
+// label and whether it was changed.
+type LabelMapper func(OtherModuleContext, string) (string, bool)
+
+// LabelPartition contains descriptions of a partition for labels
+type LabelPartition struct {
+	// Extensions to include in this partition
+	Extensions []string
+	// LabelMapper is a function that can map a label to a new label, and indicate whether to include
+	// the mapped label in the partition
+	LabelMapper LabelMapper
+	// Whether to store files not included in any other partition in a group of LabelPartitions
+	// Only one partition in a group of LabelPartitions can enabled Keep_remainder
+	Keep_remainder bool
+}
+
+// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the
+// partition
+type LabelPartitions map[string]LabelPartition
+
+// filter returns a pointer to a label if the label should be included in the partition or nil if
+// not.
+func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
+	if lf.LabelMapper != nil {
+		if newLabel, changed := lf.LabelMapper(ctx, label.Label); changed {
+			return &Label{newLabel, label.OriginalModuleName}
+		}
+	}
+	for _, ext := range lf.Extensions {
+		if strings.HasSuffix(label.Label, ext) {
+			return &label
+		}
+	}
+
+	return nil
+}
+
+// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute
+type PartitionToLabelListAttribute map[string]LabelListAttribute
+
+type partitionToLabelList map[string]*LabelList
+
+func (p partitionToLabelList) appendIncludes(partition string, label Label) {
+	if _, ok := p[partition]; !ok {
+		p[partition] = &LabelList{}
+	}
+	p[partition].Includes = append(p[partition].Includes, label)
+}
+
+func (p partitionToLabelList) excludes(partition string, excludes []Label) {
+	if _, ok := p[partition]; !ok {
+		p[partition] = &LabelList{}
+	}
+	p[partition].Excludes = excludes
+}
+
+// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions
+func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute {
+	ret := PartitionToLabelListAttribute{}
+	var partitionNames []string
+	// Stored as a pointer to distinguish nil (no remainder partition) from empty string partition
+	var remainderPartition *string
+	for p, f := range partitions {
+		partitionNames = append(partitionNames, p)
+		if f.Keep_remainder {
+			if remainderPartition != nil {
+				panic("only one partition can store the remainder")
+			}
+			// If we take the address of p in a loop, we'll end up with the last value of p in
+			// remainderPartition, we want the requested partition
+			capturePartition := p
+			remainderPartition = &capturePartition
+		}
+	}
+
+	partitionLabelList := func(axis ConfigurationAxis, config string) {
+		value := lla.SelectValue(axis, config)
+		partitionToLabels := partitionToLabelList{}
+		for _, item := range value.Includes {
+			wasFiltered := false
+			var inPartition *string
+			for partition, f := range partitions {
+				filtered := f.filter(ctx, item)
+				if filtered == nil {
+					// did not match this filter, keep looking
+					continue
+				}
+				wasFiltered = true
+				partitionToLabels.appendIncludes(partition, *filtered)
+				// don't need to check other partitions if this filter used the item,
+				// continue checking if mapped to another name
+				if *filtered == item {
+					if inPartition != nil {
+						ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition)
+					}
+					capturePartition := partition
+					inPartition = &capturePartition
+				}
+			}
+
+			// if not specified in a partition, add to remainder partition if one exists
+			if !wasFiltered && remainderPartition != nil {
+				partitionToLabels.appendIncludes(*remainderPartition, item)
+			}
+		}
+
+		// ensure empty lists are maintained
+		if value.Excludes != nil {
+			for _, partition := range partitionNames {
+				partitionToLabels.excludes(partition, value.Excludes)
+			}
+		}
+
+		for partition, list := range partitionToLabels {
+			val := ret[partition]
+			(&val).SetSelectValue(axis, config, *list)
+			ret[partition] = val
+		}
+	}
+
+	partitionLabelList(NoConfigAxis, "")
+	for axis, configToList := range lla.ConfigurableValues {
+		for config, _ := range configToList {
+			partitionLabelList(axis, config)
+		}
+	}
+	return ret
+}
+
 // StringListAttribute corresponds to the string_list Bazel attribute type with
 // support for additional metadata, like configurations.
 type StringListAttribute struct {
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index 85596e2..f53fdc1 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -16,7 +16,10 @@
 
 import (
 	"reflect"
+	"strings"
 	"testing"
+
+	"github.com/google/blueprint/proptools"
 )
 
 func TestUniqueBazelLabels(t *testing.T) {
@@ -294,6 +297,222 @@
 	}
 }
 
+// labelAddSuffixForTypeMapper returns a LabelMapper that adds suffix to label name for modules of
+// typ
+func labelAddSuffixForTypeMapper(suffix, typ string) LabelMapper {
+	return func(omc OtherModuleContext, label string) (string, bool) {
+		m, ok := omc.ModuleFromName(label)
+		if !ok {
+			return label, false
+		}
+		mTyp := omc.OtherModuleType(m)
+		if typ == mTyp {
+			return label + suffix, true
+		}
+		return label, false
+	}
+}
+
+func TestPartitionLabelListAttribute(t *testing.T) {
+	testCases := []struct {
+		name           string
+		ctx            *otherModuleTestContext
+		labelList      LabelListAttribute
+		filters        LabelPartitions
+		expected       PartitionToLabelListAttribute
+		expectedErrMsg *string
+	}{
+		{
+			name: "no configurable values",
+			ctx:  &otherModuleTestContext{},
+			labelList: LabelListAttribute{
+				Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
+			},
+			filters: LabelPartitions{
+				"A": LabelPartition{Extensions: []string{".a"}},
+				"B": LabelPartition{Extensions: []string{".b"}},
+				"C": LabelPartition{Extensions: []string{".c"}},
+			},
+			expected: PartitionToLabelListAttribute{
+				"A": LabelListAttribute{Value: makeLabelList([]string{"a.a"}, []string{})},
+				"B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})},
+				"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
+			},
+		},
+		{
+			name: "no configurable values, remainder partition",
+			ctx:  &otherModuleTestContext{},
+			labelList: LabelListAttribute{
+				Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
+			},
+			filters: LabelPartitions{
+				"A": LabelPartition{Extensions: []string{".a"}, Keep_remainder: true},
+				"B": LabelPartition{Extensions: []string{".b"}},
+				"C": LabelPartition{Extensions: []string{".c"}},
+			},
+			expected: PartitionToLabelListAttribute{
+				"A": LabelListAttribute{Value: makeLabelList([]string{"a.a", "d.d", "e.e"}, []string{})},
+				"B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})},
+				"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
+			},
+		},
+		{
+			name: "no configurable values, empty partition",
+			ctx:  &otherModuleTestContext{},
+			labelList: LabelListAttribute{
+				Value: makeLabelList([]string{"a.a", "c.c"}, []string{}),
+			},
+			filters: LabelPartitions{
+				"A": LabelPartition{Extensions: []string{".a"}},
+				"B": LabelPartition{Extensions: []string{".b"}},
+				"C": LabelPartition{Extensions: []string{".c"}},
+			},
+			expected: PartitionToLabelListAttribute{
+				"A": LabelListAttribute{Value: makeLabelList([]string{"a.a"}, []string{})},
+				"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
+			},
+		},
+		{
+			name: "no configurable values, has map",
+			ctx: &otherModuleTestContext{
+				modules: []testModuleInfo{testModuleInfo{name: "srcs", typ: "fg", dir: "dir"}},
+			},
+			labelList: LabelListAttribute{
+				Value: makeLabelList([]string{"a.a", "srcs", "b.b", "c.c"}, []string{}),
+			},
+			filters: LabelPartitions{
+				"A": LabelPartition{Extensions: []string{".a"}, LabelMapper: labelAddSuffixForTypeMapper("_a", "fg")},
+				"B": LabelPartition{Extensions: []string{".b"}},
+				"C": LabelPartition{Extensions: []string{".c"}},
+			},
+			expected: PartitionToLabelListAttribute{
+				"A": LabelListAttribute{Value: makeLabelList([]string{"a.a", "srcs_a"}, []string{})},
+				"B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})},
+				"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
+			},
+		},
+		{
+			name: "configurable values, keeps empty if excludes",
+			ctx:  &otherModuleTestContext{},
+			labelList: LabelListAttribute{
+				ConfigurableValues: configurableLabelLists{
+					ArchConfigurationAxis: labelListSelectValues{
+						"x86":    makeLabelList([]string{"a.a", "c.c"}, []string{}),
+						"arm":    makeLabelList([]string{"b.b"}, []string{}),
+						"x86_64": makeLabelList([]string{"b.b"}, []string{"d.d"}),
+					},
+				},
+			},
+			filters: LabelPartitions{
+				"A": LabelPartition{Extensions: []string{".a"}},
+				"B": LabelPartition{Extensions: []string{".b"}},
+				"C": LabelPartition{Extensions: []string{".c"}},
+			},
+			expected: PartitionToLabelListAttribute{
+				"A": LabelListAttribute{
+					ConfigurableValues: configurableLabelLists{
+						ArchConfigurationAxis: labelListSelectValues{
+							"x86":    makeLabelList([]string{"a.a"}, []string{}),
+							"x86_64": makeLabelList([]string{}, []string{"c.c"}),
+						},
+					},
+				},
+				"B": LabelListAttribute{
+					ConfigurableValues: configurableLabelLists{
+						ArchConfigurationAxis: labelListSelectValues{
+							"arm":    makeLabelList([]string{"b.b"}, []string{}),
+							"x86_64": makeLabelList([]string{"b.b"}, []string{"c.c"}),
+						},
+					},
+				},
+				"C": LabelListAttribute{
+					ConfigurableValues: configurableLabelLists{
+						ArchConfigurationAxis: labelListSelectValues{
+							"x86":    makeLabelList([]string{"c.c"}, []string{}),
+							"x86_64": makeLabelList([]string{}, []string{"c.c"}),
+						},
+					},
+				},
+			},
+		},
+		{
+			name: "error for multiple partitions same value",
+			ctx:  &otherModuleTestContext{},
+			labelList: LabelListAttribute{
+				Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
+			},
+			filters: LabelPartitions{
+				"A":       LabelPartition{Extensions: []string{".a"}},
+				"other A": LabelPartition{Extensions: []string{".a"}},
+			},
+			expected:       PartitionToLabelListAttribute{},
+			expectedErrMsg: proptools.StringPtr(`"a.a" was found in multiple partitions:`),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := PartitionLabelListAttribute(tc.ctx, &tc.labelList, tc.filters)
+
+			if hasErrors, expectsErr := len(tc.ctx.errors) > 0, tc.expectedErrMsg != nil; hasErrors != expectsErr {
+				t.Errorf("Unexpected error(s): %q, expected: %q", tc.ctx.errors, *tc.expectedErrMsg)
+			} else if tc.expectedErrMsg != nil {
+				found := false
+				for _, err := range tc.ctx.errors {
+					if strings.Contains(err, *tc.expectedErrMsg) {
+						found = true
+						break
+					}
+				}
+
+				if !found {
+					t.Errorf("Expected error message: %q, got %q", *tc.expectedErrMsg, tc.ctx.errors)
+				}
+				return
+			}
+
+			if len(tc.expected) != len(got) {
+				t.Errorf("Expected %d partitions, got %d partitions", len(tc.expected), len(got))
+			}
+			for partition, expectedLla := range tc.expected {
+				gotLla, ok := got[partition]
+				if !ok {
+					t.Errorf("Expected partition %q, but it was not found %v", partition, got)
+					continue
+				}
+				expectedLabelList := expectedLla.Value
+				gotLabelList := gotLla.Value
+				if !reflect.DeepEqual(expectedLabelList.Includes, gotLabelList.Includes) {
+					t.Errorf("Expected no config includes %v, got %v", expectedLabelList.Includes, gotLabelList.Includes)
+				}
+				expectedAxes := expectedLla.SortedConfigurationAxes()
+				gotAxes := gotLla.SortedConfigurationAxes()
+				if !reflect.DeepEqual(expectedAxes, gotAxes) {
+					t.Errorf("Expected axes %v, got %v (%#v)", expectedAxes, gotAxes, gotLla)
+				}
+				for _, axis := range expectedLla.SortedConfigurationAxes() {
+					if _, exists := gotLla.ConfigurableValues[axis]; !exists {
+						t.Errorf("Expected %s to be a supported axis, but it was not found", axis)
+					}
+					if expected, got := expectedLla.ConfigurableValues[axis], gotLla.ConfigurableValues[axis]; len(expected) != len(got) {
+						t.Errorf("For axis %q: expected configs %v, got %v", axis, expected, got)
+					}
+					for config, expectedLabelList := range expectedLla.ConfigurableValues[axis] {
+						gotLabelList, exists := gotLla.ConfigurableValues[axis][config]
+						if !exists {
+							t.Errorf("Expected %s to be a supported config, but config was not found", config)
+							continue
+						}
+						if !reflect.DeepEqual(expectedLabelList.Includes, gotLabelList.Includes) {
+							t.Errorf("Expected %s %s includes %v, got %v", axis, config, expectedLabelList.Includes, gotLabelList.Includes)
+						}
+					}
+				}
+			}
+		})
+	}
+}
+
 func TestDeduplicateAxesFromBase(t *testing.T) {
 	attr := StringListAttribute{
 		Value: []string{
diff --git a/bazel/testing.go b/bazel/testing.go
new file mode 100644
index 0000000..23c8350
--- /dev/null
+++ b/bazel/testing.go
@@ -0,0 +1,105 @@
+// 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 bazel
+
+import (
+	"fmt"
+
+	"github.com/google/blueprint"
+)
+
+// testModuleInfo implements blueprint.Module interface with sufficient information to mock a subset of
+// a blueprint ModuleContext
+type testModuleInfo struct {
+	name string
+	typ  string
+	dir  string
+}
+
+// Name returns name for testModuleInfo -- required to implement blueprint.Module
+func (mi testModuleInfo) Name() string {
+	return mi.name
+}
+
+// GenerateBuildActions unused, but required to implmeent blueprint.Module
+func (mi testModuleInfo) GenerateBuildActions(blueprint.ModuleContext) {}
+
+func (mi testModuleInfo) equals(other testModuleInfo) bool {
+	return mi.name == other.name && mi.typ == other.typ && mi.dir == other.dir
+}
+
+// ensure testModuleInfo implements blueprint.Module
+var _ blueprint.Module = testModuleInfo{}
+
+// otherModuleTestContext is a mock context that implements OtherModuleContext
+type otherModuleTestContext struct {
+	modules []testModuleInfo
+	errors  []string
+}
+
+// ModuleFromName retrieves the testModuleInfo corresponding to name, if it exists
+func (omc *otherModuleTestContext) ModuleFromName(name string) (blueprint.Module, bool) {
+	for _, m := range omc.modules {
+		if m.name == name {
+			return m, true
+		}
+	}
+	return testModuleInfo{}, false
+}
+
+// testModuleInfo returns the testModuleInfo corresponding to a blueprint.Module if it exists in omc
+func (omc *otherModuleTestContext) testModuleInfo(m blueprint.Module) (testModuleInfo, bool) {
+	mi, ok := m.(testModuleInfo)
+	if !ok {
+		return testModuleInfo{}, false
+	}
+	for _, other := range omc.modules {
+		if other.equals(mi) {
+			return mi, true
+		}
+	}
+	return testModuleInfo{}, false
+}
+
+// OtherModuleType returns type of m if it exists in omc
+func (omc *otherModuleTestContext) OtherModuleType(m blueprint.Module) string {
+	if mi, ok := omc.testModuleInfo(m); ok {
+		return mi.typ
+	}
+	return ""
+}
+
+// OtherModuleName returns name of m if it exists in omc
+func (omc *otherModuleTestContext) OtherModuleName(m blueprint.Module) string {
+	if mi, ok := omc.testModuleInfo(m); ok {
+		return mi.name
+	}
+	return ""
+}
+
+// OtherModuleDir returns dir of m if it exists in omc
+func (omc *otherModuleTestContext) OtherModuleDir(m blueprint.Module) string {
+	if mi, ok := omc.testModuleInfo(m); ok {
+		return mi.dir
+	}
+	return ""
+}
+
+func (omc *otherModuleTestContext) ModuleErrorf(format string, args ...interface{}) {
+	omc.errors = append(omc.errors, fmt.Sprintf(format, args...))
+}
+
+// Ensure otherModuleTestContext implements OtherModuleContext
+var _ OtherModuleContext = &otherModuleTestContext{}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index ec8882b..5952b15 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -679,7 +679,7 @@
     "both_source.c",
     "both_source.s",
     "both_source.S",
-        ":both_filegroup",
+    ":both_filegroup",
   ],
     static: {
         srcs: [
@@ -732,9 +732,9 @@
     local_includes = ["."],
     shared = {
         "srcs": [
-            ":shared_filegroup_cpp_srcs",
-            "shared_source.cc",
             "shared_source.cpp",
+            "shared_source.cc",
+            ":shared_filegroup_cpp_srcs",
         ],
         "srcs_as": [
             "shared_source.s",
@@ -747,9 +747,9 @@
         ],
     },
     srcs = [
-        ":both_filegroup_cpp_srcs",
-        "both_source.cc",
         "both_source.cpp",
+        "both_source.cc",
+        ":both_filegroup_cpp_srcs",
     ],
     srcs_as = [
         "both_source.s",
@@ -762,9 +762,9 @@
     ],
     static = {
         "srcs": [
-            ":static_filegroup_cpp_srcs",
-            "static_source.cc",
             "static_source.cpp",
+            "static_source.cc",
+            ":static_filegroup_cpp_srcs",
         ],
         "srcs_as": [
             "static_source.s",
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 1b38a75..22bd90b 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -14,7 +14,6 @@
 package cc
 
 import (
-	"fmt"
 	"path/filepath"
 	"strings"
 
@@ -44,18 +43,6 @@
 }
 
 func groupSrcsByExtension(ctx android.TopDownMutatorContext, srcs bazel.LabelListAttribute) (cppSrcs, cSrcs, asSrcs bazel.LabelListAttribute) {
-	// Branch srcs into three language-specific groups.
-	// C++ is the "catch-all" group, and comprises generated sources because we don't
-	// know the language of these sources until the genrule is executed.
-	// TODO(b/190006308): Handle language detection of sources in a Bazel rule.
-	isCSrcOrFilegroup := func(s string) bool {
-		return strings.HasSuffix(s, ".c") || strings.HasSuffix(s, "_c_srcs")
-	}
-
-	isAsmSrcOrFilegroup := func(s string) bool {
-		return strings.HasSuffix(s, ".S") || strings.HasSuffix(s, ".s") || strings.HasSuffix(s, "_as_srcs")
-	}
-
 	// Check that a module is a filegroup type named <label>.
 	isFilegroupNamed := func(m android.Module, fullLabel string) bool {
 		if ctx.OtherModuleType(m) != "filegroup" {
@@ -64,54 +51,39 @@
 		labelParts := strings.Split(fullLabel, ":")
 		if len(labelParts) > 2 {
 			// There should not be more than one colon in a label.
-			panic(fmt.Errorf("%s is not a valid Bazel label for a filegroup", fullLabel))
-		} else {
-			return m.Name() == labelParts[len(labelParts)-1]
+			ctx.ModuleErrorf("%s is not a valid Bazel label for a filegroup", fullLabel)
 		}
+		return m.Name() == labelParts[len(labelParts)-1]
 	}
 
-	// Convert the filegroup dependencies into the extension-specific filegroups
-	// filtered in the filegroup.bzl macro.
-	cppFilegroup := func(label string) string {
-		m, exists := ctx.ModuleFromName(label)
-		if exists {
-			aModule, _ := m.(android.Module)
-			if isFilegroupNamed(aModule, label) {
-				label = label + "_cpp_srcs"
+	// Convert filegroup dependencies into extension-specific filegroups filtered in the filegroup.bzl
+	// macro.
+	addSuffixForFilegroup := func(suffix string) bazel.LabelMapper {
+		return func(ctx bazel.OtherModuleContext, label string) (string, bool) {
+			m, exists := ctx.ModuleFromName(label)
+			if !exists {
+				return label, false
 			}
-		}
-		return label
-	}
-	cFilegroup := func(label string) string {
-		m, exists := ctx.ModuleFromName(label)
-		if exists {
 			aModule, _ := m.(android.Module)
-			if isFilegroupNamed(aModule, label) {
-				label = label + "_c_srcs"
+			if !isFilegroupNamed(aModule, label) {
+				return label, false
 			}
+			return label + suffix, true
 		}
-		return label
-	}
-	asFilegroup := func(label string) string {
-		m, exists := ctx.ModuleFromName(label)
-		if exists {
-			aModule, _ := m.(android.Module)
-			if isFilegroupNamed(aModule, label) {
-				label = label + "_as_srcs"
-			}
-		}
-		return label
 	}
 
-	cSrcs = bazel.MapLabelListAttribute(srcs, cFilegroup)
-	cSrcs = bazel.FilterLabelListAttribute(cSrcs, isCSrcOrFilegroup)
+	// TODO(b/190006308): Handle language detection of sources in a Bazel rule.
+	partitioned := bazel.PartitionLabelListAttribute(ctx, &srcs, bazel.LabelPartitions{
+		"c":  bazel.LabelPartition{Extensions: []string{".c"}, LabelMapper: addSuffixForFilegroup("_c_srcs")},
+		"as": bazel.LabelPartition{Extensions: []string{".s", ".S"}, LabelMapper: addSuffixForFilegroup("_as_srcs")},
+		// C++ is the "catch-all" group, and comprises generated sources because we don't
+		// know the language of these sources until the genrule is executed.
+		"cpp": bazel.LabelPartition{Extensions: []string{".cpp", ".cc", ".cxx", ".mm"}, LabelMapper: addSuffixForFilegroup("_cpp_srcs"), Keep_remainder: true},
+	})
 
-	asSrcs = bazel.MapLabelListAttribute(srcs, asFilegroup)
-	asSrcs = bazel.FilterLabelListAttribute(asSrcs, isAsmSrcOrFilegroup)
-
-	cppSrcs = bazel.MapLabelListAttribute(srcs, cppFilegroup)
-	cppSrcs = bazel.SubtractBazelLabelListAttribute(cppSrcs, cSrcs)
-	cppSrcs = bazel.SubtractBazelLabelListAttribute(cppSrcs, asSrcs)
+	cSrcs = partitioned["c"]
+	asSrcs = partitioned["as"]
+	cppSrcs = partitioned["cpp"]
 	return
 }
 
@@ -254,6 +226,7 @@
 	srcs     bazel.LabelListAttribute
 
 	rtti bazel.BoolAttribute
+	stl  *string
 
 	localIncludes    bazel.StringListAttribute
 	absoluteIncludes bazel.StringListAttribute
@@ -349,6 +322,24 @@
 
 	srcs, cSrcs, asSrcs := groupSrcsByExtension(ctx, srcs)
 
+	var stl *string = nil
+	stlPropsByArch := module.GetArchVariantProperties(ctx, &StlProperties{})
+	for _, configToProps := range stlPropsByArch {
+		for _, props := range configToProps {
+			if stlProps, ok := props.(*StlProperties); ok {
+				if stlProps.Stl != nil {
+					if stl == nil {
+						stl = stlProps.Stl
+					} else {
+						if stl != stlProps.Stl {
+							ctx.ModuleErrorf("Unsupported conversion: module with different stl for different variants: %s and %s", *stl, stlProps.Stl)
+						}
+					}
+				}
+			}
+		}
+	}
+
 	return compilerAttributes{
 		copts:            copts,
 		srcs:             srcs,
@@ -358,6 +349,7 @@
 		conlyFlags:       conlyFlags,
 		cppFlags:         cppFlags,
 		rtti:             rtti,
+		stl:              stl,
 		localIncludes:    localIncludes,
 		absoluteIncludes: absoluteIncludes,
 	}
diff --git a/cc/library.go b/cc/library.go
index 9114f14..de9d01e 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -248,6 +248,7 @@
 	Linkopts               bazel.StringListAttribute
 	Use_libcrt             bazel.BoolAttribute
 	Rtti                   bazel.BoolAttribute
+	Stl                    *string
 
 	// This is shared only.
 	Version_script bazel.LabelAttribute
@@ -322,6 +323,7 @@
 		Linkopts:                    linkerAttrs.linkopts,
 		Use_libcrt:                  linkerAttrs.useLibcrt,
 		Rtti:                        compilerAttrs.rtti,
+		Stl:                         compilerAttrs.stl,
 
 		Version_script: linkerAttrs.versionScript,
 
@@ -2388,6 +2390,7 @@
 			Linkopts:               linkerAttrs.linkopts,
 			Use_libcrt:             linkerAttrs.useLibcrt,
 			Rtti:                   compilerAttrs.rtti,
+			Stl:                    compilerAttrs.stl,
 			Export_includes:        exportedIncludes.Includes,
 			Export_system_includes: exportedIncludes.SystemIncludes,
 			Local_includes:         compilerAttrs.localIncludes,
@@ -2408,6 +2411,7 @@
 
 			Use_libcrt: linkerAttrs.useLibcrt,
 			Rtti:       compilerAttrs.rtti,
+			Stl:        compilerAttrs.stl,
 
 			Export_includes:        exportedIncludes.Includes,
 			Export_system_includes: exportedIncludes.SystemIncludes,
@@ -2440,6 +2444,7 @@
 	Linkopts   bazel.StringListAttribute
 	Use_libcrt bazel.BoolAttribute
 	Rtti       bazel.BoolAttribute
+	Stl        *string
 
 	Export_includes        bazel.StringListAttribute
 	Export_system_includes bazel.StringListAttribute
@@ -2463,6 +2468,7 @@
 	Linkopts   bazel.StringListAttribute
 	Use_libcrt bazel.BoolAttribute
 	Rtti       bazel.BoolAttribute
+	Stl        *string
 
 	Export_includes        bazel.StringListAttribute
 	Export_system_includes bazel.StringListAttribute
diff --git a/java/robolectric.go b/java/robolectric.go
index 5d62aee..a3603ad 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -212,7 +212,13 @@
 		installDeps = append(installDeps, installedData)
 	}
 
-	ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.combinedJar, installDeps...)
+	installed := ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.combinedJar, installDeps...)
+
+	if r.ExportedToMake() {
+		// Soong handles installation here, but Make is usually what creates the phony rule that atest
+		// uses to build the module.  Create it here for now.
+		ctx.Phony(ctx.ModuleName(), installed)
+	}
 }
 
 func generateRoboTestConfig(ctx android.ModuleContext, outputFile android.WritablePath,
diff --git a/rust/test.go b/rust/test.go
index e95b47c..56da509 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -59,6 +59,10 @@
 
 	// Test options.
 	Test_options TestOptions
+
+	// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
+	// with root permission.
+	Require_root *bool
 }
 
 // A test module is a binary module with extra --test compiler flag
@@ -109,12 +113,27 @@
 }
 
 func (test *testDecorator) install(ctx ModuleContext) {
+	testInstallBase := "/data/local/tests/unrestricted"
+	if ctx.RustModule().InVendor() || ctx.RustModule().UseVndk() {
+		testInstallBase = "/data/local/tests/vendor"
+	}
+
+	var configs []tradefed.Config
+	if Bool(test.Properties.Require_root) {
+		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", nil})
+	} else {
+		var options []tradefed.Option
+		options = append(options, tradefed.Option{Name: "force-root", Value: "false"})
+		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", options})
+	}
+
 	test.testConfig = tradefed.AutoGenRustTestConfig(ctx,
 		test.Properties.Test_config,
 		test.Properties.Test_config_template,
 		test.Properties.Test_suites,
-		nil,
-		test.Properties.Auto_gen_config)
+		configs,
+		test.Properties.Auto_gen_config,
+		testInstallBase)
 
 	dataSrcPaths := android.PathsForModuleSrc(ctx, test.Properties.Data)
 
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index 3d96c84..da55829 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -227,17 +227,17 @@
 }
 
 func AutoGenRustTestConfig(ctx android.ModuleContext, testConfigProp *string,
-	testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool) android.Path {
+	testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool, testInstallBase string) android.Path {
 	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
 	if autogenPath != nil {
 		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
 		if templatePath.Valid() {
-			autogenTemplate(ctx, autogenPath, templatePath.String(), config, "")
+			autogenTemplate(ctx, autogenPath, templatePath.String(), config, testInstallBase)
 		} else {
 			if ctx.Device() {
-				autogenTemplate(ctx, autogenPath, "${RustDeviceTestConfigTemplate}", config, "")
+				autogenTemplate(ctx, autogenPath, "${RustDeviceTestConfigTemplate}", config, testInstallBase)
 			} else {
-				autogenTemplate(ctx, autogenPath, "${RustHostTestConfigTemplate}", config, "")
+				autogenTemplate(ctx, autogenPath, "${RustHostTestConfigTemplate}", config, testInstallBase)
 			}
 		}
 		return autogenPath