Merge "Add target/os configurable string_list attrs."
diff --git a/bazel/properties.go b/bazel/properties.go
index 250fea4..2440ca1 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -96,21 +96,10 @@
 )
 
 var (
-	// This is the list of architectures with a Bazel config_setting and
-	// constraint value equivalent. is actually android.ArchTypeList, but the
-	// android package depends on the bazel package, so a cyclic dependency
-	// prevents using that here.
-	selectableArchs = []string{ARCH_X86, ARCH_X86_64, ARCH_ARM, ARCH_ARM64}
-
-	// Likewise, this is the list of target operating systems.
-	selectableTargetOs = []string{
-		OS_ANDROID,
-		OS_DARWIN,
-		OS_FUCHSIA,
-		OS_LINUX,
-		OS_LINUX_BIONIC,
-		OS_WINDOWS,
-	}
+	// These are the list of OSes and architectures with a Bazel config_setting
+	// and constraint value equivalent. These exist in arch.go, but the android
+	// package depends on the bazel package, so a cyclic dependency prevents
+	// using those variables here.
 
 	// A map of architectures to the Bazel label of the constraint_value
 	// for the @platforms//cpu:cpu constraint_setting
@@ -133,6 +122,10 @@
 	}
 )
 
+type Attribute interface {
+	HasConfigurableValues() bool
+}
+
 // Arch-specific label_list typed Bazel attribute values. This should correspond
 // to the types of architectures supported for compilation in arch.go.
 type labelListArchValues struct {
@@ -176,14 +169,14 @@
 
 // HasArchSpecificValues returns true if the attribute contains
 // architecture-specific label_list values.
-func (attrs *LabelListAttribute) HasConfigurableValues() bool {
-	for _, arch := range selectableArchs {
+func (attrs LabelListAttribute) HasConfigurableValues() bool {
+	for arch := range PlatformArchMap {
 		if len(attrs.GetValueForArch(arch).Includes) > 0 {
 			return true
 		}
 	}
 
-	for _, os := range selectableTargetOs {
+	for os := range PlatformOsMap {
 		if len(attrs.GetValueForOS(os).Includes) > 0 {
 			return true
 		}
@@ -253,8 +246,15 @@
 	// The base value of the string list attribute.
 	Value []string
 
-	// Optional additive set of list values to the base value.
+	// The arch-specific attribute string list values. Optional. If used, these
+	// are generated in a select statement and appended to the non-arch specific
+	// label list Value.
 	ArchValues stringListArchValues
+
+	// The os-specific attribute string list values. Optional. If used, these
+	// are generated in a select statement and appended to the non-os specific
+	// label list Value.
+	OsValues stringListOsValues
 }
 
 // Arch-specific string_list typed Bazel attribute values. This should correspond
@@ -267,14 +267,29 @@
 	Common []string
 }
 
+type stringListOsValues struct {
+	Android     []string
+	Darwin      []string
+	Fuchsia     []string
+	Linux       []string
+	LinuxBionic []string
+	Windows     []string
+}
+
 // HasConfigurableValues returns true if the attribute contains
 // architecture-specific string_list values.
-func (attrs *StringListAttribute) HasConfigurableValues() bool {
-	for _, arch := range selectableArchs {
+func (attrs StringListAttribute) HasConfigurableValues() bool {
+	for arch := range PlatformArchMap {
 		if len(attrs.GetValueForArch(arch)) > 0 {
 			return true
 		}
 	}
+
+	for os := range PlatformOsMap {
+		if len(attrs.GetValueForOS(os)) > 0 {
+			return true
+		}
+	}
 	return false
 }
 
@@ -305,6 +320,35 @@
 	*v = value
 }
 
+func (attrs *StringListAttribute) osValuePtrs() map[string]*[]string {
+	return map[string]*[]string{
+		OS_ANDROID:      &attrs.OsValues.Android,
+		OS_DARWIN:       &attrs.OsValues.Darwin,
+		OS_FUCHSIA:      &attrs.OsValues.Fuchsia,
+		OS_LINUX:        &attrs.OsValues.Linux,
+		OS_LINUX_BIONIC: &attrs.OsValues.LinuxBionic,
+		OS_WINDOWS:      &attrs.OsValues.Windows,
+	}
+}
+
+// GetValueForOS returns the string_list attribute value for an OS target.
+func (attrs *StringListAttribute) GetValueForOS(os string) []string {
+	var v *[]string
+	if v = attrs.osValuePtrs()[os]; v == nil {
+		panic(fmt.Errorf("Unknown os: %s", os))
+	}
+	return *v
+}
+
+// SetValueForArch sets the string_list attribute value for an OS target.
+func (attrs *StringListAttribute) SetValueForOS(os string, value []string) {
+	var v *[]string
+	if v = attrs.osValuePtrs()[os]; v == nil {
+		panic(fmt.Errorf("Unknown os: %s", os))
+	}
+	*v = value
+}
+
 // TryVariableSubstitution, replace string substitution formatting within each string in slice with
 // Starlark string.format compatible tag for productVariable.
 func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 1d254c8..dd14c7d 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -415,12 +415,10 @@
 	case reflect.Struct:
 		// Special cases where the bp2build sends additional information to the codegenerator
 		// by wrapping the attributes in a custom struct type.
-		if labels, ok := propertyValue.Interface().(bazel.LabelListAttribute); ok {
-			return prettyPrintLabelListAttribute(labels, indent)
+		if attr, ok := propertyValue.Interface().(bazel.Attribute); ok {
+			return prettyPrintAttribute(attr, indent)
 		} else if label, ok := propertyValue.Interface().(bazel.Label); ok {
 			return fmt.Sprintf("%q", label.Label), nil
-		} else if stringList, ok := propertyValue.Interface().(bazel.StringListAttribute); ok {
-			return prettyPrintStringListAttribute(stringList, indent)
 		}
 
 		ret = "{\n"
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index fcc3080..4f3babe 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -415,6 +415,54 @@
 )`,
 			},
 		},
+		{
+			description:                        "cc_object setting cflags for multiple OSes",
+			moduleTypeUnderTest:                "cc_object",
+			moduleTypeUnderTestFactory:         cc.ObjectFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+			blueprint: `cc_object {
+    name: "foo",
+    srcs: ["base.cpp"],
+    target: {
+        android: {
+            cflags: ["-fPIC"],
+        },
+        windows: {
+            cflags: ["-fPIC"],
+        },
+        darwin: {
+            cflags: ["-Wall"],
+        },
+    },
+    bazel_module: { bp2build_available: true },
+}
+`,
+			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",
+        ],
+        "//conditions:default": [],
+    }),
+    local_include_dirs = [
+        ".",
+    ],
+    srcs = [
+        "base.cpp",
+    ],
+)`,
+			},
+		},
 	}
 
 	dir := "."
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index 6ca0d6d..b2b3379 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -9,48 +9,67 @@
 
 // Configurability support for bp2build.
 
-// prettyPrintStringListAttribute converts a StringListAttribute to its Bazel
-// syntax. May contain a select statement.
-func prettyPrintStringListAttribute(stringList bazel.StringListAttribute, indent int) (string, error) {
-	ret, err := prettyPrint(reflect.ValueOf(stringList.Value), indent)
-	if err != nil {
-		return ret, err
+type selects map[string]reflect.Value
+
+func getStringListValues(list bazel.StringListAttribute) (reflect.Value, selects, selects) {
+	value := reflect.ValueOf(list.Value)
+	if !list.HasConfigurableValues() {
+		return value, nil, nil
 	}
 
-	if !stringList.HasConfigurableValues() {
-		// Select statement not needed.
-		return ret, nil
-	}
-
-	// Create the selects for arch specific values.
-	selects := map[string]reflect.Value{}
-	for arch, selectKey := range bazel.PlatformArchMap {
-		selects[selectKey] = reflect.ValueOf(stringList.GetValueForArch(arch))
-	}
-
-	selectMap, err := prettyPrintSelectMap(selects, "[]", indent)
-	return ret + selectMap, err
-}
-
-// prettyPrintLabelListAttribute converts a LabelListAttribute to its Bazel
-// syntax. May contain select statements.
-func prettyPrintLabelListAttribute(labels bazel.LabelListAttribute, indent int) (string, error) {
-	// TODO(b/165114590): convert glob syntax
-	ret, err := prettyPrint(reflect.ValueOf(labels.Value.Includes), indent)
-	if err != nil {
-		return ret, err
-	}
-
-	if !labels.HasConfigurableValues() {
-		// Select statements not needed.
-		return ret, nil
-	}
-
-	// Create the selects for arch specific values.
 	archSelects := map[string]reflect.Value{}
 	for arch, selectKey := range bazel.PlatformArchMap {
-		archSelects[selectKey] = reflect.ValueOf(labels.GetValueForArch(arch).Includes)
+		archSelects[selectKey] = reflect.ValueOf(list.GetValueForArch(arch))
 	}
+
+	osSelects := map[string]reflect.Value{}
+	for os, selectKey := range bazel.PlatformOsMap {
+		osSelects[selectKey] = reflect.ValueOf(list.GetValueForOS(os))
+	}
+
+	return value, archSelects, osSelects
+}
+
+func getLabelListValues(list bazel.LabelListAttribute) (reflect.Value, selects, selects) {
+	value := reflect.ValueOf(list.Value.Includes)
+	if !list.HasConfigurableValues() {
+		return value, nil, nil
+	}
+
+	archSelects := map[string]reflect.Value{}
+	for arch, selectKey := range bazel.PlatformArchMap {
+		archSelects[selectKey] = reflect.ValueOf(list.GetValueForArch(arch).Includes)
+	}
+
+	osSelects := map[string]reflect.Value{}
+	for os, selectKey := range bazel.PlatformOsMap {
+		osSelects[selectKey] = reflect.ValueOf(list.GetValueForOS(os).Includes)
+	}
+
+	return value, archSelects, osSelects
+}
+
+// prettyPrintAttribute converts an Attribute to its Bazel syntax. May contain
+// select statements.
+func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) {
+	var value reflect.Value
+	var archSelects, osSelects selects
+
+	switch list := v.(type) {
+	case bazel.StringListAttribute:
+		value, archSelects, osSelects = getStringListValues(list)
+	case bazel.LabelListAttribute:
+		value, archSelects, osSelects = getLabelListValues(list)
+	default:
+		return "", fmt.Errorf("Not a supported Bazel attribute type: %s", v)
+	}
+
+	ret, err := prettyPrint(value, indent)
+	if err != nil {
+		return ret, err
+	}
+
+	// Create the selects for arch specific values.
 	selectMap, err := prettyPrintSelectMap(archSelects, "[]", indent)
 	if err != nil {
 		return "", err
@@ -58,17 +77,22 @@
 	ret += selectMap
 
 	// Create the selects for target os specific values.
-	osSelects := map[string]reflect.Value{}
-	for os, selectKey := range bazel.PlatformOsMap {
-		osSelects[selectKey] = reflect.ValueOf(labels.GetValueForOS(os).Includes)
-	}
 	selectMap, err = prettyPrintSelectMap(osSelects, "[]", indent)
-	return ret + selectMap, err
+	if err != nil {
+		return "", err
+	}
+	ret += selectMap
+
+	return ret, err
 }
 
 // prettyPrintSelectMap converts a map of select keys to reflected Values as a generic way
 // to construct a select map for any kind of attribute type.
 func prettyPrintSelectMap(selectMap map[string]reflect.Value, defaultValue string, indent int) (string, error) {
+	if selectMap == nil {
+		return "", nil
+	}
+
 	var selects string
 	for _, selectKey := range android.SortedStringKeys(selectMap) {
 		value := selectMap[selectKey]
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 2a590eb..497d227 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -58,6 +58,31 @@
 	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
+	for _, props := range module.compiler.compilerProps() {
+		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
+			ret.Value = baseCompilerProps.Cflags
+			break
+		}
+	}
+
+	for arch, props := range module.GetArchProperties(&BaseCompilerProperties{}) {
+		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
+			ret.SetValueForArch(arch.Name, baseCompilerProps.Cflags)
+		}
+	}
+
+	for os, props := range module.GetTargetProperties(&BaseCompilerProperties{}) {
+		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
+			ret.SetValueForOS(os.Name, baseCompilerProps.Cflags)
+		}
+	}
+
+	return ret
+}
+
 // bp2BuildParseHeaderLibs 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 {
diff --git a/cc/library_headers.go b/cc/library_headers.go
index c8dd2c2..82af16a 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -62,6 +62,7 @@
 }
 
 type bazelCcLibraryHeadersAttributes struct {
+	Copts    bazel.StringListAttribute
 	Hdrs     bazel.LabelListAttribute
 	Includes bazel.LabelListAttribute
 	Deps     bazel.LabelListAttribute
@@ -99,6 +100,7 @@
 	headerLibsLabels := bp2BuildParseHeaderLibs(ctx, module)
 
 	attrs := &bazelCcLibraryHeadersAttributes{
+		Copts:    bp2BuildParseCflags(ctx, module),
 		Includes: exportedIncludesLabels,
 		Hdrs:     exportedIncludesHeadersLabels,
 		Deps:     headerLibsLabels,
diff --git a/cc/object.go b/cc/object.go
index ea8d7d3..4f8797d 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -156,13 +156,11 @@
 	}
 
 	// Set arch-specific configurable attributes
-	var copts bazel.StringListAttribute
 	var srcs bazel.LabelListAttribute
 	var localIncludeDirs []string
 	var asFlags []string
 	for _, props := range m.compiler.compilerProps() {
 		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			copts.Value = baseCompilerProps.Cflags
 			srcs = bazel.MakeLabelListAttribute(
 				android.BazelLabelForModuleSrcExcludes(
 					ctx,
@@ -205,14 +203,13 @@
 	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))
-			copts.SetValueForArch(arch.Name, cProps.Cflags)
 		}
 	}
 
 	attrs := &bazelObjectAttributes{
 		Srcs:               srcs,
 		Deps:               deps,
-		Copts:              copts,
+		Copts:              bp2BuildParseCflags(ctx, m),
 		Asflags:            asFlags,
 		Local_include_dirs: localIncludeDirs,
 	}