Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9abd62d1 authored by Liz Kammer's avatar Liz Kammer
Browse files

Use maps in bazel *attribute types

This is to simplify the process of resolving label + exclude labels
across the various configuration axes we have and across the various
properties/modules that use this behavior.

Test: ci/bp2build.sh && ci/mixed_droid.sh
Change-Id: I8efae3e75ddb365384f5caaf5bb504a5206618d3
parent 37b3626f
Loading
Loading
Loading
Loading
+49 −82
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
package android

import (
	"android/soong/bazel"
	"encoding"
	"fmt"
	"reflect"
@@ -897,7 +898,7 @@ func createArchPropTypeDesc(props reflect.Type) []archPropTypeDesc {

			// Add the OS/Arch combinations, e.g. "android_arm64".
			for _, archType := range osArchTypeMap[os] {
				targets = append(targets, GetCompoundTargetName(os, archType))
				targets = append(targets, GetCompoundTargetField(os, archType))

				// Also add the special "linux_<arch>" and "bionic_<arch>" property structs.
				if os.Linux() {
@@ -1217,7 +1218,7 @@ func getMultilibStruct(ctx ArchVariantContext, archProperties interface{}, archT
	return getChildPropertyStruct(ctx, multilibProp, archType.Multilib, "multilib."+archType.Multilib)
}

func GetCompoundTargetName(os OsType, arch ArchType) string {
func GetCompoundTargetField(os OsType, arch ArchType) string {
	return os.Field + "_" + arch.Name
}

@@ -1327,7 +1328,7 @@ func getArchProperties(ctx BaseMutatorContext, archProperties interface{}, arch
		//         key: value,
		//     },
		// },
		field := GetCompoundTargetName(os, archType)
		field := GetCompoundTargetField(os, archType)
		userFriendlyField := "target." + os.Name + "_" + archType.Name
		if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
			result = append(result, osArchProperties)
@@ -1882,27 +1883,38 @@ type ArchVariantContext interface {
	PropertyErrorf(property, fmt string, args ...interface{})
}

// GetArchProperties returns a map of architectures to the values of the
// properties of the 'propertySet' struct that are specific to that architecture.
// 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 arch specific property value specified by the module if defined.
// 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`.
//
// Implemented in a way very similar to GetTargetProperties().
func (m *ModuleBase) GetArchProperties(ctx ArchVariantContext, propertySet interface{}) map[ArchType]interface{} {
func (m *ModuleBase) GetArchVariantProperties(ctx ArchVariantContext, propertySet interface{}) ConfigurationAxisToArchVariantProperties {
	// Return value of the arch types to the prop values for that arch.
	archToProp := map[ArchType]interface{}{}
	axisToProps := ConfigurationAxisToArchVariantProperties{}

	// Nothing to do for non-arch-specific modules.
	if !m.ArchSpecific() {
		return archToProp
		return axisToProps
	}

	dstType := reflect.ValueOf(propertySet).Type()
@@ -1920,9 +1932,10 @@ func (m *ModuleBase) GetArchProperties(ctx ArchVariantContext, propertySet inter

	if archProperties == nil {
		// This module does not have the property set requested
		return archToProp
		return axisToProps
	}

	archToProp := ArchVariantProperties{}
	// For each arch type (x86, arm64, etc.)
	for _, arch := range ArchTypeList() {
		// Arch properties are sometimes sharded (see createArchPropTypeDesc() ).
@@ -1948,10 +1961,30 @@ func (m *ModuleBase) GetArchProperties(ctx ArchVariantContext, propertySet inter
			mergePropertyStruct(ctx, value, propertyStruct)
		}

		archToProp[arch] = value
		archToProp[arch.Name] = value
	}
	axisToProps[bazel.ArchConfigurationAxis] = archToProp

	osToProp := ArchVariantProperties{}
	archOsToProp := ArchVariantProperties{}
	// For android, linux, ...
	for _, os := range osTypeList {
		if os == CommonOS {
			// It looks like this OS value is not used in Blueprint files
			continue
		}
		osToProp[os.Name] = getTargetStruct(ctx, propertySet, archProperties, os.Field)
		// For arm, x86, ...
		for _, arch := range osArchTypeMap[os] {
			targetField := GetCompoundTargetField(os, arch)
			targetName := fmt.Sprintf("%s_%s", os.Name, arch.Name)
			archOsToProp[targetName] = getTargetStruct(ctx, propertySet, archProperties, targetField)
		}
	}
	axisToProps[bazel.OsConfigurationAxis] = osToProp
	axisToProps[bazel.OsArchConfigurationAxis] = archOsToProp

	return archToProp
	return axisToProps
}

// Returns a struct matching the propertySet interface, containing properties specific to the targetName
@@ -1989,69 +2022,3 @@ func getTargetStruct(ctx ArchVariantContext, propertySet interface{}, archProper

	return value
}

// Properties corresponds to e.g. Target: android: {...}
// ArchProperties corresponds to e.g. Target: android_arm: {...}, android_arm64: {...}, ...
type TargetProperties struct {
	Properties     interface{}
	ArchProperties map[ArchType]interface{}
}

// GetTargetProperties returns a map of OS target (e.g. android, windows) to the
// values of the properties of the 'propertySet' struct that are specific to
// that OS target.
//
// 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 os-specific property value specified by the module if defined.
//
// Implemented in a way very similar to GetArchProperties().
//
// NOTE: "Target" == OS
func (m *ModuleBase) GetTargetProperties(ctx ArchVariantContext, propertySet interface{}) map[OsType]TargetProperties {
	// Return value of the target types to the prop values for that target.
	targetToProp := map[OsType]TargetProperties{}

	// Nothing to do for non-target-specific modules.
	if !m.ArchSpecific() {
		return targetToProp
	}

	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.generalProperties[i].
	for i, generalProp := range m.generalProperties {
		srcType := reflect.ValueOf(generalProp).Type()
		if srcType == dstType {
			archProperties = m.archProperties[i]
			break
		}
	}

	if archProperties == nil {
		// This module does not have the property set requested
		return targetToProp
	}

	// For android, linux, ...
	for _, os := range osTypeList {
		if os == CommonOS {
			// It looks like this OS value is not used in Blueprint files
			continue
		}
		targetProperties := TargetProperties{
			Properties:     getTargetStruct(ctx, propertySet, archProperties, os.Field),
			ArchProperties: make(map[ArchType]interface{}),
		}
		// For arm, x86, ...
		for _, arch := range osArchTypeMap[os] {
			targetName := GetCompoundTargetName(os, arch)
			targetProperties.ArchProperties[arch] = getTargetStruct(ctx, propertySet, archProperties, targetName)
		}
		targetToProp[os] = targetProperties
	}

	return targetToProp
}
+5 −12
Original line number Diff line number Diff line
@@ -479,18 +479,11 @@ func ProductVariableProperties(ctx BaseMutatorContext) ProductConfigProperties {

	productVariableValues(moduleBase.variableProperties, "", &productConfigProperties)

	for arch, targetProps := range moduleBase.GetArchProperties(ctx, moduleBase.variableProperties) {
		// GetArchProperties is creating an instance of the requested type
	for _, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
		for config, props := range configToProps {
			// GetArchVariantProperties is creating an instance of the requested type
			// and productVariablesValues expects an interface, so no need to cast
		productVariableValues(targetProps, arch.Name, &productConfigProperties)
	}

	for os, targetProps := range moduleBase.GetTargetProperties(ctx, moduleBase.variableProperties) {
		// GetTargetProperties is creating an instance of the requested type
		// and productVariablesValues expects an interface, so no need to cast
		productVariableValues(targetProps.Properties, os.Name, &productConfigProperties)
		for arch, archProperties := range targetProps.ArchProperties {
			productVariableValues(archProperties, os.Name+"_"+arch.Name, &productConfigProperties)
			productVariableValues(props, config, &productConfigProperties)
		}
	}

+1 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ bootstrap_go_package {
    pkgPath: "android/soong/bazel",
    srcs: [
        "aquery.go",
        "configurability.go",
        "constants.go",
        "properties.go",
    ],
+213 −0
Original line number Diff line number Diff line
// Copyright 2021 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bazel

import (
	"fmt"
	"strings"
)

const (
	// ArchType names in arch.go
	archArm    = "arm"
	archArm64  = "arm64"
	archX86    = "x86"
	archX86_64 = "x86_64"

	// OsType names in arch.go
	osAndroid     = "android"
	osDarwin      = "darwin"
	osFuchsia     = "fuchsia"
	osLinux       = "linux_glibc"
	osLinuxBionic = "linux_bionic"
	osWindows     = "windows"

	// Targets in arch.go
	osArchAndroidArm        = "android_arm"
	osArchAndroidArm64      = "android_arm64"
	osArchAndroidX86        = "android_x86"
	osArchAndroidX86_64     = "android_x86_64"
	osArchDarwinX86_64      = "darwin_x86_64"
	osArchFuchsiaArm64      = "fuchsia_arm64"
	osArchFuchsiaX86_64     = "fuchsia_x86_64"
	osArchLinuxX86          = "linux_glibc_x86"
	osArchLinuxX86_64       = "linux_glibc_x86_64"
	osArchLinuxBionicArm64  = "linux_bionic_arm64"
	osArchLinuxBionicX86_64 = "linux_bionic_x86_64"
	osArchWindowsX86        = "windows_x86"
	osArchWindowsX86_64     = "windows_x86_64"

	// This is the string representation of the default condition wherever a
	// configurable attribute is used in a select statement, i.e.
	// //conditions:default for Bazel.
	//
	// This is consistently named "conditions_default" to mirror the Soong
	// config variable default key in an Android.bp file, although there's no
	// integration with Soong config variables (yet).
	ConditionsDefault = "conditions_default"

	ConditionsDefaultSelectKey = "//conditions:default"

	productVariableBazelPackage = "//build/bazel/product_variables"
)

var (
	// These are the list of OSes and architectures with a Bazel config_setting
	// and constraint value equivalent. These exist in arch.go, but the android
	// package depends on the bazel package, so a cyclic dependency prevents
	// using those variables here.

	// A map of architectures to the Bazel label of the constraint_value
	// for the @platforms//cpu:cpu constraint_setting
	platformArchMap = map[string]string{
		archArm:           "//build/bazel/platforms/arch:arm",
		archArm64:         "//build/bazel/platforms/arch:arm64",
		archX86:           "//build/bazel/platforms/arch:x86",
		archX86_64:        "//build/bazel/platforms/arch:x86_64",
		ConditionsDefault: ConditionsDefaultSelectKey, // The default condition of as arch select map.
	}

	// A map of target operating systems to the Bazel label of the
	// constraint_value for the @platforms//os:os constraint_setting
	platformOsMap = map[string]string{
		osAndroid:         "//build/bazel/platforms/os:android",
		osDarwin:          "//build/bazel/platforms/os:darwin",
		osFuchsia:         "//build/bazel/platforms/os:fuchsia",
		osLinux:           "//build/bazel/platforms/os:linux",
		osLinuxBionic:     "//build/bazel/platforms/os:linux_bionic",
		osWindows:         "//build/bazel/platforms/os:windows",
		ConditionsDefault: ConditionsDefaultSelectKey, // The default condition of an os select map.
	}

	platformOsArchMap = map[string]string{
		osArchAndroidArm:        "//build/bazel/platforms/os_arch:android_arm",
		osArchAndroidArm64:      "//build/bazel/platforms/os_arch:android_arm64",
		osArchAndroidX86:        "//build/bazel/platforms/os_arch:android_x86",
		osArchAndroidX86_64:     "//build/bazel/platforms/os_arch:android_x86_64",
		osArchDarwinX86_64:      "//build/bazel/platforms/os_arch:darwin_x86_64",
		osArchFuchsiaArm64:      "//build/bazel/platforms/os_arch:fuchsia_arm64",
		osArchFuchsiaX86_64:     "//build/bazel/platforms/os_arch:fuchsia_x86_64",
		osArchLinuxX86:          "//build/bazel/platforms/os_arch:linux_glibc_x86",
		osArchLinuxX86_64:       "//build/bazel/platforms/os_arch:linux_glibc_x86_64",
		osArchLinuxBionicArm64:  "//build/bazel/platforms/os_arch:linux_bionic_arm64",
		osArchLinuxBionicX86_64: "//build/bazel/platforms/os_arch:linux_bionic_x86_64",
		osArchWindowsX86:        "//build/bazel/platforms/os_arch:windows_x86",
		osArchWindowsX86_64:     "//build/bazel/platforms/os_arch:windows_x86_64",
		ConditionsDefault:       ConditionsDefaultSelectKey, // The default condition of an os select map.
	}
)

// basic configuration types
type configurationType int

const (
	noConfig configurationType = iota
	arch
	os
	osArch
	productVariables
)

func (ct configurationType) String() string {
	return map[configurationType]string{
		noConfig:         "no_config",
		arch:             "arch",
		os:               "os",
		osArch:           "arch_os",
		productVariables: "product_variables",
	}[ct]
}

func (ct configurationType) validateConfig(config string) {
	switch ct {
	case noConfig:
		if config != "" {
			panic(fmt.Errorf("Cannot specify config with %s, but got %s", ct, config))
		}
	case arch:
		if _, ok := platformArchMap[config]; !ok {
			panic(fmt.Errorf("Unknown arch: %s", config))
		}
	case os:
		if _, ok := platformOsMap[config]; !ok {
			panic(fmt.Errorf("Unknown os: %s", config))
		}
	case osArch:
		if _, ok := platformOsArchMap[config]; !ok {
			panic(fmt.Errorf("Unknown os+arch: %s", config))
		}
	case productVariables:
		// do nothing
	default:
		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct))
	}
}

// SelectKey returns the Bazel select key for a given configurationType and config string.
func (ct configurationType) SelectKey(config string) string {
	ct.validateConfig(config)
	switch ct {
	case noConfig:
		panic(fmt.Errorf("SelectKey is unnecessary for noConfig ConfigurationType "))
	case arch:
		return platformArchMap[config]
	case os:
		return platformOsMap[config]
	case osArch:
		return platformOsArchMap[config]
	case productVariables:
		if config == ConditionsDefault {
			return ConditionsDefaultSelectKey
		}
		return fmt.Sprintf("%s:%s", productVariableBazelPackage, strings.ToLower(config))
	default:
		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct))
	}
}

var (
	// Indicating there is no configuration axis
	NoConfigAxis = ConfigurationAxis{configurationType: noConfig}
	// An axis for architecture-specific configurations
	ArchConfigurationAxis = ConfigurationAxis{configurationType: arch}
	// An axis for os-specific configurations
	OsConfigurationAxis = ConfigurationAxis{configurationType: os}
	// An axis for arch+os-specific configurations
	OsArchConfigurationAxis = ConfigurationAxis{configurationType: osArch}
)

// ProductVariableConfigurationAxis returns an axis for the given product variable
func ProductVariableConfigurationAxis(variable string) ConfigurationAxis {
	return ConfigurationAxis{
		configurationType: productVariables,
		subType:           variable,
	}
}

// ConfigurationAxis is an independent axis for configuration, there should be no overlap between
// elements within an axis.
type ConfigurationAxis struct {
	configurationType
	// some configuration types (e.g. productVariables) have multiple independent axes, subType helps
	// distinguish between them without needing to list all 17 product variables.
	subType string
}

func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool {
	if ca.configurationType < other.configurationType {
		return true
	}
	return ca.subType < other.subType
}
+239 −669

File changed.

Preview size limit exceeded, changes collapsed.

Loading