diff options
Diffstat (limited to 'android/arch.go')
-rw-r--r-- | android/arch.go | 439 |
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()), - ) -} |