Merge "Remove more unused code" into main
This commit is contained in:
commit
a17792e2eb
2 changed files with 0 additions and 812 deletions
425
android/arch.go
425
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"
|
||||
|
@ -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()),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue