Merge "Remove more unused code" into main

This commit is contained in:
Colin Cross 2024-04-22 18:50:08 +00:00 committed by Gerrit Code Review
commit a17792e2eb
2 changed files with 0 additions and 812 deletions

View file

@ -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"
@ -1899,428 +1894,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()),
)
}

View file

@ -20,8 +20,6 @@ import (
"runtime"
"strings"
"android/soong/bazel"
"github.com/google/blueprint/proptools"
)
@ -492,10 +490,6 @@ type ProductVariables struct {
CheckVendorSeappViolations *bool `json:",omitempty"`
// PartitionVarsForBazelMigrationOnlyDoNotUse are extra variables that are used to define the
// partition images. They should not be read from soong modules.
PartitionVarsForBazelMigrationOnlyDoNotUse PartitionVariables `json:",omitempty"`
BuildFlags map[string]string `json:",omitempty"`
BuildFromSourceStub *bool `json:",omitempty"`
@ -644,387 +638,6 @@ func (this *ProductVariables) GetBuildFlagBool(flag string) bool {
return val == "true"
}
// ProductConfigContext requires the access to the Module to get product config properties.
type ProductConfigContext interface {
Module() Module
}
// ProductConfigOrSoongConfigProperty represents either a soong config variable + its value
// or a product config variable. You can get both a ConfigurationAxis and a SelectKey from it
// for use in bazel attributes. ProductVariableProperties() will return a map from properties ->
// this interface -> property structs for use in bp2build converters
type ProductConfigOrSoongConfigProperty interface {
// Name of the product variable or soong config variable
Name() string
// AlwaysEmit returns true for soong config variables but false for product variables. This
// is intended to indicate if we need to always emit empty lists in the select statements.
AlwaysEmit() bool
// ConfigurationAxis returns the bazel.ConfigurationAxis that represents this variable. The
// configuration axis will change depending on the variable and whether it's arch/os variant
// as well.
ConfigurationAxis() bazel.ConfigurationAxis
// SelectKey returns a string that represents the key of a select branch, however, it is not
// actually the real label written out to the build file.
// this.ConfigurationAxis().SelectKey(this.SelectKey()) will give the actual label.
SelectKey() string
}
// ProductConfigProperty represents a product config variable, and if it is arch-variant or not.
type ProductConfigProperty struct {
// The name of the product variable, e.g. "safestack", "malloc_not_svelte",
// "board"
name string
arch string
}
func (p ProductConfigProperty) Name() string {
return p.name
}
func (p ProductConfigProperty) AlwaysEmit() bool {
return false
}
func (p ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
return bazel.ProductVariableConfigurationAxis(p.arch != "", p.name+"__"+p.arch)
}
func (p ProductConfigProperty) SelectKey() string {
if p.arch == "" {
return strings.ToLower(p.name)
} else {
return strings.ToLower(p.name + "-" + p.arch)
}
}
// SoongConfigProperty represents a soong config variable, its value if it's a string variable,
// and if it's dependent on the OS or not
type SoongConfigProperty struct {
name string
namespace string
// Can be an empty string for bool/value soong config variables
value string
// If there is a target: field inside a soong config property struct, the os that it selects
// on will be represented here.
os string
}
func (p SoongConfigProperty) Name() string {
return p.name
}
func (p SoongConfigProperty) AlwaysEmit() bool {
return true
}
func (p SoongConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
return bazel.ProductVariableConfigurationAxis(false, p.namespace+"__"+p.name+"__"+p.os)
}
// SelectKey returns the literal string that represents this variable in a BUILD
// select statement.
func (p SoongConfigProperty) SelectKey() string {
// p.value being conditions_default can happen with or without a desired os. When not using
// an os, we want to emit literally just //conditions:default in the select statement, but
// when using an os, we want to emit namespace__name__conditions_default__os, so that
// the branch is only taken if the variable is not set, and we're on the desired os.
// ConfigurationAxis#SelectKey will map the conditions_default result of this function to
// //conditions:default.
if p.value == bazel.ConditionsDefaultConfigKey && p.os == "" {
return bazel.ConditionsDefaultConfigKey
}
parts := []string{p.namespace, p.name}
if p.value != "" && p.value != bazel.ConditionsDefaultSelectKey {
parts = append(parts, p.value)
}
if p.os != "" {
parts = append(parts, p.os)
}
// e.g. acme__feature1, android__board__soc_a, my_namespace__my_variables__my_value__my_os
return strings.ToLower(strings.Join(parts, "__"))
}
// ProductConfigProperties is a map of maps to group property values according
// their property name and the product config variable they're set under.
//
// The outer map key is the name of the property, like "cflags".
//
// The inner map key is a ProductConfigProperty, which is a struct of product
// variable name, namespace, and the "full configuration" of the product
// variable.
//
// e.g. product variable name: board, namespace: acme, full config: vendor_chip_foo
//
// The value of the map is the interface{} representing the value of the
// property, like ["-DDEFINES"] for cflags.
type ProductConfigProperties map[string]map[ProductConfigOrSoongConfigProperty]interface{}
func (p *ProductConfigProperties) AddProductConfigProperty(
propertyName, productVariableName, arch string, propertyValue interface{}) {
productConfigProp := ProductConfigProperty{
name: productVariableName, // e.g. size, feature1, feature2, FEATURE3, board
arch: arch, // e.g. "", x86, arm64
}
p.AddEitherProperty(propertyName, productConfigProp, propertyValue)
}
func (p *ProductConfigProperties) AddSoongConfigProperty(
propertyName, namespace, variableName, value, os string, propertyValue interface{}) {
soongConfigProp := SoongConfigProperty{
namespace: namespace,
name: variableName, // e.g. size, feature1, feature2, FEATURE3, board
value: value,
os: os, // e.g. android, linux_x86
}
p.AddEitherProperty(propertyName, soongConfigProp, propertyValue)
}
func (p *ProductConfigProperties) AddEitherProperty(
propertyName string, key ProductConfigOrSoongConfigProperty, propertyValue interface{}) {
if (*p)[propertyName] == nil {
(*p)[propertyName] = make(map[ProductConfigOrSoongConfigProperty]interface{})
}
if existing, ok := (*p)[propertyName][key]; ok {
switch dst := existing.(type) {
case []string:
src, ok := propertyValue.([]string)
if !ok {
panic("Conflicting types")
}
dst = append(dst, src...)
(*p)[propertyName][key] = dst
default:
if existing != propertyValue {
panic(fmt.Errorf("TODO: handle merging value %#v", existing))
}
}
} else {
(*p)[propertyName][key] = propertyValue
}
}
// maybeExtractConfigVarProp attempts to read this value as a config var struct
// wrapped by interfaces and ptrs. If it's not the right type, the second return
// value is false.
func maybeExtractConfigVarProp(v reflect.Value) (reflect.Value, bool) {
if v.Kind() == reflect.Interface {
// The conditions_default value can be either
// 1) an ptr to an interface of a struct (bool config variables and product variables)
// 2) an interface of 1) (config variables with nested structs, like string vars)
v = v.Elem()
}
if v.Kind() != reflect.Ptr {
return v, false
}
v = reflect.Indirect(v)
if v.Kind() == reflect.Interface {
// Extract the struct from the interface
v = v.Elem()
}
if !v.IsValid() {
return v, false
}
if v.Kind() != reflect.Struct {
return v, false
}
return v, true
}
func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(variableValues reflect.Value, arch string) {
// Example of product_variables:
//
// product_variables: {
// malloc_not_svelte: {
// shared_libs: ["malloc_not_svelte_shared_lib"],
// whole_static_libs: ["malloc_not_svelte_whole_static_lib"],
// exclude_static_libs: [
// "malloc_not_svelte_static_lib_excludes",
// "malloc_not_svelte_whole_static_lib_excludes",
// ],
// },
// },
for i := 0; i < variableValues.NumField(); i++ {
// e.g. Platform_sdk_version, Unbundled_build, Malloc_not_svelte, etc.
productVariableName := variableValues.Type().Field(i).Name
variableValue := variableValues.Field(i)
// Check if any properties were set for the module
if variableValue.IsZero() {
// e.g. feature1: {}, malloc_not_svelte: {}
continue
}
for j := 0; j < variableValue.NumField(); j++ {
property := variableValue.Field(j)
// e.g. Asflags, Cflags, Enabled, etc.
propertyName := variableValue.Type().Field(j).Name
if property.Kind() != reflect.Interface {
productConfigProperties.AddProductConfigProperty(propertyName, productVariableName, arch, property.Interface())
}
}
}
}
func (productConfigProperties *ProductConfigProperties) AddSoongConfigProperties(namespace string, soongConfigVariablesStruct reflect.Value) error {
//
// Example of soong_config_variables:
//
// soong_config_variables: {
// feature1: {
// conditions_default: {
// ...
// },
// cflags: ...
// },
// feature2: {
// cflags: ...
// conditions_default: {
// ...
// },
// },
// board: {
// soc_a: {
// ...
// },
// soc_b: {
// ...
// },
// soc_c: {},
// conditions_default: {
// ...
// },
// },
// }
for i := 0; i < soongConfigVariablesStruct.NumField(); i++ {
// e.g. feature1, feature2, board
variableName := soongConfigVariablesStruct.Type().Field(i).Name
variableStruct := soongConfigVariablesStruct.Field(i)
// Check if any properties were set for the module
if variableStruct.IsZero() {
// e.g. feature1: {}
continue
}
// Unlike product variables, config variables require a few more
// indirections to extract the struct from the reflect.Value.
if v, ok := maybeExtractConfigVarProp(variableStruct); ok {
variableStruct = v
} else if !v.IsValid() {
// Skip invalid variables which may not used, else leads to panic
continue
}
for j := 0; j < variableStruct.NumField(); j++ {
propertyOrStruct := variableStruct.Field(j)
// propertyOrValueName can either be:
// - A property, like: Asflags, Cflags, Enabled, etc.
// - A soong config string variable's value, like soc_a, soc_b, soc_c in the example above
// - "conditions_default"
propertyOrValueName := variableStruct.Type().Field(j).Name
// If the property wasn't set, no need to pass it along
if propertyOrStruct.IsZero() {
continue
}
if v, ok := maybeExtractConfigVarProp(propertyOrStruct); ok {
// The field is a struct, which is used by:
// 1) soong_config_string_variables
//
// soc_a: {
// cflags: ...,
// }
//
// soc_b: {
// cflags: ...,
// }
//
// 2) conditions_default structs for all soong config variable types.
//
// conditions_default: {
// cflags: ...,
// static_libs: ...
// }
//
// This means that propertyOrValueName is either conditions_default, or a soong
// config string variable's value.
field := v
// Iterate over fields of this struct prop.
for k := 0; k < field.NumField(); k++ {
// For product variables, zero values are irrelevant; however, for soong config variables,
// empty values are relevant because there can also be a conditions default which is not
// applied for empty variables.
if field.Field(k).IsZero() && namespace == "" {
continue
}
propertyName := field.Type().Field(k).Name
if propertyName == "Target" {
productConfigProperties.AddSoongConfigPropertiesFromTargetStruct(namespace, variableName, proptools.PropertyNameForField(propertyOrValueName), field.Field(k))
} else if propertyName == "Arch" || propertyName == "Multilib" {
return fmt.Errorf("Arch/Multilib are not currently supported in soong config variable structs")
} else {
productConfigProperties.AddSoongConfigProperty(propertyName, namespace, variableName, proptools.PropertyNameForField(propertyOrValueName), "", field.Field(k).Interface())
}
}
} else if propertyOrStruct.Kind() != reflect.Interface {
// If not an interface, then this is not a conditions_default or
// a struct prop. That is, this is a bool/value config variable.
if propertyOrValueName == "Target" {
productConfigProperties.AddSoongConfigPropertiesFromTargetStruct(namespace, variableName, "", propertyOrStruct)
} else if propertyOrValueName == "Arch" || propertyOrValueName == "Multilib" {
return fmt.Errorf("Arch/Multilib are not currently supported in soong config variable structs")
} else {
productConfigProperties.AddSoongConfigProperty(propertyOrValueName, namespace, variableName, "", "", propertyOrStruct.Interface())
}
}
}
}
return nil
}
func (productConfigProperties *ProductConfigProperties) AddSoongConfigPropertiesFromTargetStruct(namespace, soongConfigVariableName string, soongConfigVariableValue string, targetStruct reflect.Value) {
// targetStruct will be a struct with fields like "android", "host", "arm", "x86",
// "android_arm", etc. The values of each of those fields will be a regular property struct.
for i := 0; i < targetStruct.NumField(); i++ {
targetFieldName := targetStruct.Type().Field(i).Name
archOrOsSpecificStruct := targetStruct.Field(i)
for j := 0; j < archOrOsSpecificStruct.NumField(); j++ {
property := archOrOsSpecificStruct.Field(j)
// e.g. Asflags, Cflags, Enabled, etc.
propertyName := archOrOsSpecificStruct.Type().Field(j).Name
if targetFieldName == "Android" {
productConfigProperties.AddSoongConfigProperty(propertyName, namespace, soongConfigVariableName, soongConfigVariableValue, "android", property.Interface())
} else if targetFieldName == "Host" {
for _, os := range osTypeList {
if os.Class == Host {
productConfigProperties.AddSoongConfigProperty(propertyName, namespace, soongConfigVariableName, soongConfigVariableValue, os.Name, property.Interface())
}
}
} else if !archOrOsSpecificStruct.IsZero() {
// One problem with supporting additional fields is that if multiple branches of
// "target" overlap, we don't want them to be in the same select statement (aka
// configuration axis). "android" and "host" are disjoint, so it's ok that we only
// have 2 axes right now. (soongConfigVariables and soongConfigVariablesPlusOs)
panic("TODO: support other target types in soong config variable structs: " + targetFieldName)
}
}
}
}
func VariableMutator(mctx BottomUpMutatorContext) {
var module Module
var ok bool