summaryrefslogtreecommitdiff
path: root/android/arch.go
diff options
context:
space:
mode:
Diffstat (limited to 'android/arch.go')
-rw-r--r--android/arch.go439
1 files changed, 10 insertions, 429 deletions
diff --git a/android/arch.go b/android/arch.go
index 27ce4d464..cd8882bbd 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -16,16 +16,11 @@ package android
import (
"encoding"
- "encoding/json"
"fmt"
"reflect"
"runtime"
- "sort"
"strings"
- "android/soong/bazel"
- "android/soong/starlark_fmt"
-
"github.com/google/blueprint"
"github.com/google/blueprint/bootstrap"
"github.com/google/blueprint/proptools"
@@ -980,12 +975,18 @@ func filterArchStruct(field reflect.StructField, prefix string) (bool, reflect.S
panic(fmt.Errorf("unexpected tag format %q", field.Tag))
}
// these tags don't need to be present in the runtime generated struct type.
- values = RemoveListFromList(values, []string{"arch_variant", "variant_prepend", "path", "replace_instead_of_append"})
+ // However replace_instead_of_append does, because it's read by the blueprint
+ // property extending util functions, which can operate on these generated arch
+ // property structs.
+ values = RemoveListFromList(values, []string{"arch_variant", "variant_prepend", "path"})
if len(values) > 0 {
- panic(fmt.Errorf("unknown tags %q in field %q", values, prefix+field.Name))
+ if values[0] != "replace_instead_of_append" || len(values) > 1 {
+ panic(fmt.Errorf("unknown tags %q in field %q", values, prefix+field.Name))
+ }
+ field.Tag = `android:"replace_instead_of_append"`
+ } else {
+ field.Tag = ``
}
-
- field.Tag = ``
return true, field
}
return false, field
@@ -1899,428 +1900,8 @@ func decodeMultilibTargets(multilib string, targets []Target, prefer32 bool) ([]
return buildTargets, nil
}
-func (m *ModuleBase) getArchPropertySet(propertySet interface{}, archType ArchType) interface{} {
- archString := archType.Field
- for i := range m.archProperties {
- if m.archProperties[i] == nil {
- // Skip over nil properties
- continue
- }
-
- // Not archProperties are usable; this function looks for properties of a very specific
- // form, and ignores the rest.
- for _, archProperty := range m.archProperties[i] {
- // archPropValue is a property struct, we are looking for the form:
- // `arch: { arm: { key: value, ... }}`
- archPropValue := reflect.ValueOf(archProperty).Elem()
-
- // Unwrap src so that it should looks like a pointer to `arm: { key: value, ... }`
- src := archPropValue.FieldByName("Arch").Elem()
-
- // Step into non-nil pointers to structs in the src value.
- if src.Kind() == reflect.Ptr {
- if src.IsNil() {
- continue
- }
- src = src.Elem()
- }
-
- // Find the requested field (e.g. arm, x86) in the src struct.
- src = src.FieldByName(archString)
-
- // We only care about structs.
- if !src.IsValid() || src.Kind() != reflect.Struct {
- continue
- }
-
- // If the value of the field is a struct then step into the
- // BlueprintEmbed field. The special "BlueprintEmbed" name is
- // used by createArchPropTypeDesc to embed the arch properties
- // in the parent struct, so the src arch prop should be in this
- // field.
- //
- // See createArchPropTypeDesc for more details on how Arch-specific
- // module properties are processed from the nested props and written
- // into the module's archProperties.
- src = src.FieldByName("BlueprintEmbed")
-
- // Clone the destination prop, since we want a unique prop struct per arch.
- propertySetClone := reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
-
- // Copy the located property struct into the cloned destination property struct.
- err := proptools.ExtendMatchingProperties([]interface{}{propertySetClone}, src.Interface(), nil, proptools.OrderReplace)
- if err != nil {
- // This is fine, it just means the src struct doesn't match the type of propertySet.
- continue
- }
-
- return propertySetClone
- }
- }
- // No property set was found specific to the given arch, so return an empty
- // property set.
- return reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
-}
-
-// getMultilibPropertySet returns a property set struct matching the type of
-// `propertySet`, containing multilib-specific module properties for the given architecture.
-// If no multilib-specific properties exist for the given architecture, returns an empty property
-// set matching `propertySet`'s type.
-func (m *ModuleBase) getMultilibPropertySet(propertySet interface{}, archType ArchType) interface{} {
- // archType.Multilib is lowercase (for example, lib32) but property struct field is
- // capitalized, such as Lib32, so use strings.Title to capitalize it.
- multiLibString := strings.Title(archType.Multilib)
-
- for i := range m.archProperties {
- if m.archProperties[i] == nil {
- // Skip over nil properties
- continue
- }
-
- // Not archProperties are usable; this function looks for properties of a very specific
- // form, and ignores the rest.
- for _, archProperties := range m.archProperties[i] {
- // archPropValue is a property struct, we are looking for the form:
- // `multilib: { lib32: { key: value, ... }}`
- archPropValue := reflect.ValueOf(archProperties).Elem()
-
- // Unwrap src so that it should looks like a pointer to `lib32: { key: value, ... }`
- src := archPropValue.FieldByName("Multilib").Elem()
-
- // Step into non-nil pointers to structs in the src value.
- if src.Kind() == reflect.Ptr {
- if src.IsNil() {
- // Ignore nil pointers.
- continue
- }
- src = src.Elem()
- }
-
- // Find the requested field (e.g. lib32) in the src struct.
- src = src.FieldByName(multiLibString)
-
- // We only care about valid struct pointers.
- if !src.IsValid() || src.Kind() != reflect.Ptr || src.Elem().Kind() != reflect.Struct {
- continue
- }
-
- // Get the zero value for the requested property set.
- propertySetClone := reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
-
- // Copy the located property struct into the "zero" property set struct.
- err := proptools.ExtendMatchingProperties([]interface{}{propertySetClone}, src.Interface(), nil, proptools.OrderReplace)
-
- if err != nil {
- // This is fine, it just means the src struct doesn't match.
- continue
- }
-
- return propertySetClone
- }
- }
-
- // There were no multilib properties specifically matching the given archtype.
- // Return zeroed value.
- return reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
-}
-
// ArchVariantContext defines the limited context necessary to retrieve arch_variant properties.
type ArchVariantContext interface {
ModuleErrorf(fmt string, args ...interface{})
PropertyErrorf(property, fmt string, args ...interface{})
}
-
-// ArchVariantProperties represents a map of arch-variant config strings to a property interface{}.
-type ArchVariantProperties map[string]interface{}
-
-// ConfigurationAxisToArchVariantProperties represents a map of bazel.ConfigurationAxis to
-// ArchVariantProperties, such that each independent arch-variant axis maps to the
-// configs/properties for that axis.
-type ConfigurationAxisToArchVariantProperties map[bazel.ConfigurationAxis]ArchVariantProperties
-
-// GetArchVariantProperties returns a ConfigurationAxisToArchVariantProperties where the
-// arch-variant properties correspond to the values of the properties of the 'propertySet' struct
-// that are specific to that axis/configuration. Each axis is independent, containing
-// non-overlapping configs that correspond to the various "arch-variant" support, at this time:
-//
-// arches (including multilib)
-// oses
-// arch+os combinations
-//
-// For example, passing a struct { Foo bool, Bar string } will return an interface{} that can be
-// type asserted back into the same struct, containing the config-specific property value specified
-// by the module if defined.
-//
-// Arch-specific properties may come from an arch stanza or a multilib stanza; properties
-// in these stanzas are combined.
-// For example: `arch: { x86: { Foo: ["bar"] } }, multilib: { lib32: {` Foo: ["baz"] } }`
-// will result in `Foo: ["bar", "baz"]` being returned for architecture x86, if the given
-// propertyset contains `Foo []string`.
-func (m *ModuleBase) GetArchVariantProperties(ctx ArchVariantContext, propertySet interface{}) ConfigurationAxisToArchVariantProperties {
- // Return value of the arch types to the prop values for that arch.
- axisToProps := ConfigurationAxisToArchVariantProperties{}
-
- // Nothing to do for non-arch-specific modules.
- if !m.ArchSpecific() {
- return axisToProps
- }
-
- dstType := reflect.ValueOf(propertySet).Type()
- var archProperties []interface{}
-
- // First find the property set in the module that corresponds to the requested
- // one. m.archProperties[i] corresponds to m.GetProperties()[i].
- for i, generalProp := range m.GetProperties() {
- srcType := reflect.ValueOf(generalProp).Type()
- if srcType == dstType {
- archProperties = m.archProperties[i]
- axisToProps[bazel.NoConfigAxis] = ArchVariantProperties{"": generalProp}
- break
- }
- }
-
- if archProperties == nil {
- // This module does not have the property set requested
- return axisToProps
- }
-
- archToProp := ArchVariantProperties{}
- // For each arch type (x86, arm64, etc.)
- for _, arch := range ArchTypeList() {
- // Arch properties are sometimes sharded (see createArchPropTypeDesc() ).
- // Iterate over every shard and extract a struct with the same type as the
- // input one that contains the data specific to that arch.
- propertyStructs := make([]reflect.Value, 0)
- archFeaturePropertyStructs := make(map[string][]reflect.Value, 0)
- for _, archProperty := range archProperties {
- archTypeStruct, ok := getArchTypeStruct(ctx, archProperty, arch)
- if ok {
- propertyStructs = append(propertyStructs, archTypeStruct)
-
- // For each feature this arch supports (arm: neon, x86: ssse3, sse4, ...)
- for _, feature := range archFeatures[arch] {
- prefix := "arch." + arch.Name + "." + feature
- if featureProperties, ok := getChildPropertyStruct(ctx, archTypeStruct, feature, prefix); ok {
- archFeaturePropertyStructs[feature] = append(archFeaturePropertyStructs[feature], featureProperties)
- }
- }
- }
- multilibStruct, ok := getMultilibStruct(ctx, archProperty, arch)
- if ok {
- propertyStructs = append(propertyStructs, multilibStruct)
- }
- }
-
- archToProp[arch.Name] = mergeStructs(ctx, propertyStructs, propertySet)
-
- // In soong, if multiple features match the current configuration, they're
- // all used. In bazel, we have to have unambiguous select() statements, so
- // we can't have two features that are both active in the same select().
- // One alternative is to split out each feature into a separate select(),
- // but then it's difficult to support exclude_srcs, which may need to
- // exclude things from the regular arch select() statement if a certain
- // feature is active. Instead, keep the features in the same select
- // statement as the arches, but emit the power set of all possible
- // combinations of features, so that bazel can match the most precise one.
- allFeatures := make([]string, 0, len(archFeaturePropertyStructs))
- for feature := range archFeaturePropertyStructs {
- allFeatures = append(allFeatures, feature)
- }
- for _, features := range bazel.PowerSetWithoutEmptySet(allFeatures) {
- sort.Strings(features)
- propsForCurrentFeatureSet := make([]reflect.Value, 0)
- propsForCurrentFeatureSet = append(propsForCurrentFeatureSet, propertyStructs...)
- for _, feature := range features {
- propsForCurrentFeatureSet = append(propsForCurrentFeatureSet, archFeaturePropertyStructs[feature]...)
- }
- archToProp[arch.Name+"-"+strings.Join(features, "-")] =
- mergeStructs(ctx, propsForCurrentFeatureSet, propertySet)
- }
- }
- axisToProps[bazel.ArchConfigurationAxis] = archToProp
-
- osToProp := ArchVariantProperties{}
- archOsToProp := ArchVariantProperties{}
-
- linuxStructs := getTargetStructs(ctx, archProperties, "Linux")
- bionicStructs := getTargetStructs(ctx, archProperties, "Bionic")
- hostStructs := getTargetStructs(ctx, archProperties, "Host")
- hostLinuxStructs := getTargetStructs(ctx, archProperties, "Host_linux")
- hostNotWindowsStructs := getTargetStructs(ctx, archProperties, "Not_windows")
-
- // For android, linux, ...
- for _, os := range osTypeList {
- if os == CommonOS {
- // It looks like this OS value is not used in Blueprint files
- continue
- }
- osStructs := make([]reflect.Value, 0)
-
- osSpecificStructs := getTargetStructs(ctx, archProperties, os.Field)
- if os.Class == Host {
- osStructs = append(osStructs, hostStructs...)
- }
- if os.Linux() {
- osStructs = append(osStructs, linuxStructs...)
- }
- if os.Bionic() {
- osStructs = append(osStructs, bionicStructs...)
- }
- if os.Linux() && os.Class == Host {
- osStructs = append(osStructs, hostLinuxStructs...)
- }
-
- if os == LinuxMusl {
- osStructs = append(osStructs, getTargetStructs(ctx, archProperties, "Musl")...)
- }
- if os == Linux {
- osStructs = append(osStructs, getTargetStructs(ctx, archProperties, "Glibc")...)
- }
-
- osStructs = append(osStructs, osSpecificStructs...)
-
- if os.Class == Host && os != Windows {
- osStructs = append(osStructs, hostNotWindowsStructs...)
- }
- osToProp[os.Name] = mergeStructs(ctx, osStructs, propertySet)
-
- // For arm, x86, ...
- for _, arch := range osArchTypeMap[os] {
- osArchStructs := make([]reflect.Value, 0)
-
- // Auto-combine with Linux_ and Bionic_ targets. This potentially results in
- // repetition and select() bloat, but use of Linux_* and Bionic_* targets is rare.
- // TODO(b/201423152): Look into cleanup.
- if os.Linux() {
- targetField := "Linux_" + arch.Name
- targetStructs := getTargetStructs(ctx, archProperties, targetField)
- osArchStructs = append(osArchStructs, targetStructs...)
- }
- if os.Bionic() {
- targetField := "Bionic_" + arch.Name
- targetStructs := getTargetStructs(ctx, archProperties, targetField)
- osArchStructs = append(osArchStructs, targetStructs...)
- }
- if os == LinuxMusl {
- targetField := "Musl_" + arch.Name
- targetStructs := getTargetStructs(ctx, archProperties, targetField)
- osArchStructs = append(osArchStructs, targetStructs...)
- }
- if os == Linux {
- targetField := "Glibc_" + arch.Name
- targetStructs := getTargetStructs(ctx, archProperties, targetField)
- osArchStructs = append(osArchStructs, targetStructs...)
- }
-
- targetField := GetCompoundTargetField(os, arch)
- targetName := fmt.Sprintf("%s_%s", os.Name, arch.Name)
- targetStructs := getTargetStructs(ctx, archProperties, targetField)
- osArchStructs = append(osArchStructs, targetStructs...)
-
- archOsToProp[targetName] = mergeStructs(ctx, osArchStructs, propertySet)
- }
- }
-
- axisToProps[bazel.OsConfigurationAxis] = osToProp
- axisToProps[bazel.OsArchConfigurationAxis] = archOsToProp
- return axisToProps
-}
-
-// Returns a struct matching the propertySet interface, containing properties specific to the targetName
-// For example, given these arguments:
-//
-// propertySet = BaseCompilerProperties
-// targetName = "android_arm"
-//
-// And given this Android.bp fragment:
-//
-// target:
-// android_arm: {
-// srcs: ["foo.c"],
-// }
-// android_arm64: {
-// srcs: ["bar.c"],
-// }
-// }
-//
-// This would return a BaseCompilerProperties with BaseCompilerProperties.Srcs = ["foo.c"]
-func getTargetStructs(ctx ArchVariantContext, archProperties []interface{}, targetName string) []reflect.Value {
- var propertyStructs []reflect.Value
- for _, archProperty := range archProperties {
- archPropValues := reflect.ValueOf(archProperty).Elem()
- targetProp := archPropValues.FieldByName("Target").Elem()
- targetStruct, ok := getChildPropertyStruct(ctx, targetProp, targetName, targetName)
- if ok {
- propertyStructs = append(propertyStructs, targetStruct)
- } else {
- return []reflect.Value{}
- }
- }
-
- return propertyStructs
-}
-
-func mergeStructs(ctx ArchVariantContext, propertyStructs []reflect.Value, propertySet interface{}) interface{} {
- // Create a new instance of the requested property set
- value := reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
-
- // Merge all the structs together
- for _, propertyStruct := range propertyStructs {
- mergePropertyStruct(ctx, value, propertyStruct)
- }
-
- return value
-}
-
-func printArchTypeStarlarkDict(dict map[ArchType][]string) string {
- valDict := make(map[string]string, len(dict))
- for k, v := range dict {
- valDict[k.String()] = starlark_fmt.PrintStringList(v, 1)
- }
- return starlark_fmt.PrintDict(valDict, 0)
-}
-
-func printArchTypeNestedStarlarkDict(dict map[ArchType]map[string][]string) string {
- valDict := make(map[string]string, len(dict))
- for k, v := range dict {
- valDict[k.String()] = starlark_fmt.PrintStringListDict(v, 1)
- }
- return starlark_fmt.PrintDict(valDict, 0)
-}
-
-func printArchConfigList(arches []archConfig) string {
- jsonOut, err := json.MarshalIndent(arches, "", starlark_fmt.Indention(1))
- if err != nil {
- panic(fmt.Errorf("Error converting arch configs %#v to json: %q", arches, err))
- }
- return fmt.Sprintf("json.decode('''%s''')", string(jsonOut))
-}
-
-func StarlarkArchConfigurations() string {
- return fmt.Sprintf(`
-_arch_to_variants = %s
-
-_arch_to_cpu_variants = %s
-
-_arch_to_features = %s
-
-_android_arch_feature_for_arch_variant = %s
-
-_aml_arches = %s
-
-_ndk_arches = %s
-
-arch_to_variants = _arch_to_variants
-arch_to_cpu_variants = _arch_to_cpu_variants
-arch_to_features = _arch_to_features
-android_arch_feature_for_arch_variants = _android_arch_feature_for_arch_variant
-aml_arches = _aml_arches
-ndk_arches = _ndk_arches
-`, printArchTypeStarlarkDict(archVariants),
- printArchTypeStarlarkDict(cpuVariants),
- printArchTypeStarlarkDict(archFeatures),
- printArchTypeNestedStarlarkDict(androidArchFeatureMap),
- printArchConfigList(getAmlAbisConfig()),
- printArchConfigList(getNdkAbisConfig()),
- )
-}