2019-11-23 01:03:51 +01:00
|
|
|
// Copyright 2020 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 soongconfig
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"reflect"
|
|
|
|
"sort"
|
|
|
|
"strings"
|
2024-01-12 21:12:26 +01:00
|
|
|
|
|
|
|
"github.com/google/blueprint/parser"
|
|
|
|
"github.com/google/blueprint/proptools"
|
2019-11-23 01:03:51 +01:00
|
|
|
)
|
|
|
|
|
2020-12-16 21:42:02 +01:00
|
|
|
const conditionsDefault = "conditions_default"
|
|
|
|
|
2021-11-02 17:43:57 +01:00
|
|
|
var SoongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
|
2019-11-23 01:03:51 +01:00
|
|
|
|
|
|
|
// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
|
|
|
|
// result so each file is only parsed once.
|
|
|
|
func Parse(r io.Reader, from string) (*SoongConfigDefinition, []error) {
|
|
|
|
scope := parser.NewScope(nil)
|
|
|
|
file, errs := parser.ParseAndEval(from, r, scope)
|
|
|
|
|
|
|
|
if len(errs) > 0 {
|
|
|
|
return nil, errs
|
|
|
|
}
|
|
|
|
|
|
|
|
mtDef := &SoongConfigDefinition{
|
|
|
|
ModuleTypes: make(map[string]*ModuleType),
|
|
|
|
variables: make(map[string]soongConfigVariable),
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, def := range file.Defs {
|
|
|
|
switch def := def.(type) {
|
|
|
|
case *parser.Module:
|
|
|
|
newErrs := processImportModuleDef(mtDef, def)
|
|
|
|
|
|
|
|
if len(newErrs) > 0 {
|
|
|
|
errs = append(errs, newErrs...)
|
|
|
|
}
|
|
|
|
|
|
|
|
case *parser.Assignment:
|
|
|
|
// Already handled via Scope object
|
|
|
|
default:
|
|
|
|
panic("unknown definition type")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(errs) > 0 {
|
|
|
|
return nil, errs
|
|
|
|
}
|
|
|
|
|
|
|
|
for name, moduleType := range mtDef.ModuleTypes {
|
|
|
|
for _, varName := range moduleType.variableNames {
|
|
|
|
if v, ok := mtDef.variables[varName]; ok {
|
|
|
|
moduleType.Variables = append(moduleType.Variables, v)
|
|
|
|
} else {
|
|
|
|
return nil, []error{
|
|
|
|
fmt.Errorf("unknown variable %q in module type %q", varName, name),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return mtDef, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func processImportModuleDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
|
|
|
|
switch def.Type {
|
|
|
|
case "soong_config_module_type":
|
|
|
|
return processModuleTypeDef(v, def)
|
|
|
|
case "soong_config_string_variable":
|
|
|
|
return processStringVariableDef(v, def)
|
|
|
|
case "soong_config_bool_variable":
|
|
|
|
return processBoolVariableDef(v, def)
|
|
|
|
default:
|
|
|
|
// Unknown module types will be handled when the file is parsed as a normal
|
|
|
|
// Android.bp file.
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type ModuleTypeProperties struct {
|
|
|
|
// the name of the new module type. Unlike most modules, this name does not need to be unique,
|
|
|
|
// although only one module type with any name will be importable into an Android.bp file.
|
|
|
|
Name string
|
|
|
|
|
|
|
|
// the module type that this module type will extend.
|
|
|
|
Module_type string
|
|
|
|
|
|
|
|
// the SOONG_CONFIG_NAMESPACE value from a BoardConfig.mk that this module type will read
|
|
|
|
// configuration variables from.
|
|
|
|
Config_namespace string
|
|
|
|
|
|
|
|
// the list of SOONG_CONFIG variables that this module type will read
|
|
|
|
Variables []string
|
|
|
|
|
2020-03-24 03:39:34 +01:00
|
|
|
// the list of boolean SOONG_CONFIG variables that this module type will read
|
|
|
|
Bool_variables []string
|
|
|
|
|
soong config: add value_variable substitution
There are some cases that aren't handled with the existing variable
types for booleans or known lists of strings. Similarly to our
product_variables that uses %s / %d for things like
PLATFORM_SDK_VERSION, allow vendors to define their own config variables
to be substituted into properties.
For example, some of the makefiles that I've attempted to convert had
the option to pass in version numbers from the board, or the default
display size:
-DDISPLAY_VERSION=550
-DDISP_H=1080
These examples happen to be integers, but since our configuration
language (make) doesn't support numbers, %s works just as well.
This change will allow the above to be represented using:
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
value_variables: [
"DISPLAY_VERSION",
"DISP_H",
],
properties: ["cflags"],
}
acme_cc_defaults {
name: "my_defaults",
soong_config_variables: {
DISPLAY_VERSION: {
cflags: ["-DDISPLAY_VERSION=%s"],
},
DISP_H: {
cflags: ["-DDISP_H=%s"],
}
},
}
Test: built-in tests
Change-Id: I18f35746b5cc39c304a136980249e886d38c6df6
2020-03-24 03:42:18 +01:00
|
|
|
// the list of SOONG_CONFIG variables that this module type will read. The value will be
|
|
|
|
// inserted into the properties with %s substitution.
|
|
|
|
Value_variables []string
|
|
|
|
|
2019-11-23 01:03:51 +01:00
|
|
|
// the list of properties that this module type will extend.
|
|
|
|
Properties []string
|
|
|
|
}
|
|
|
|
|
|
|
|
func processModuleTypeDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
|
|
|
|
|
|
|
|
props := &ModuleTypeProperties{}
|
|
|
|
|
|
|
|
_, errs = proptools.UnpackProperties(def.Properties, props)
|
|
|
|
if len(errs) > 0 {
|
|
|
|
return errs
|
|
|
|
}
|
|
|
|
|
|
|
|
if props.Name == "" {
|
|
|
|
errs = append(errs, fmt.Errorf("name property must be set"))
|
|
|
|
}
|
|
|
|
|
|
|
|
if props.Config_namespace == "" {
|
|
|
|
errs = append(errs, fmt.Errorf("config_namespace property must be set"))
|
|
|
|
}
|
|
|
|
|
|
|
|
if props.Module_type == "" {
|
|
|
|
errs = append(errs, fmt.Errorf("module_type property must be set"))
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(errs) > 0 {
|
|
|
|
return errs
|
|
|
|
}
|
|
|
|
|
2020-12-16 21:42:02 +01:00
|
|
|
if mt, errs := newModuleType(props); len(errs) > 0 {
|
|
|
|
return errs
|
|
|
|
} else {
|
|
|
|
v.ModuleTypes[props.Name] = mt
|
soong config: add value_variable substitution
There are some cases that aren't handled with the existing variable
types for booleans or known lists of strings. Similarly to our
product_variables that uses %s / %d for things like
PLATFORM_SDK_VERSION, allow vendors to define their own config variables
to be substituted into properties.
For example, some of the makefiles that I've attempted to convert had
the option to pass in version numbers from the board, or the default
display size:
-DDISPLAY_VERSION=550
-DDISP_H=1080
These examples happen to be integers, but since our configuration
language (make) doesn't support numbers, %s works just as well.
This change will allow the above to be represented using:
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
value_variables: [
"DISPLAY_VERSION",
"DISP_H",
],
properties: ["cflags"],
}
acme_cc_defaults {
name: "my_defaults",
soong_config_variables: {
DISPLAY_VERSION: {
cflags: ["-DDISPLAY_VERSION=%s"],
},
DISP_H: {
cflags: ["-DDISP_H=%s"],
}
},
}
Test: built-in tests
Change-Id: I18f35746b5cc39c304a136980249e886d38c6df6
2020-03-24 03:42:18 +01:00
|
|
|
}
|
|
|
|
|
2019-11-23 01:03:51 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type VariableProperties struct {
|
|
|
|
Name string
|
|
|
|
}
|
|
|
|
|
|
|
|
type StringVariableProperties struct {
|
|
|
|
Values []string
|
|
|
|
}
|
|
|
|
|
|
|
|
func processStringVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
|
|
|
|
stringProps := &StringVariableProperties{}
|
|
|
|
|
|
|
|
base, errs := processVariableDef(def, stringProps)
|
|
|
|
if len(errs) > 0 {
|
|
|
|
return errs
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(stringProps.Values) == 0 {
|
|
|
|
return []error{fmt.Errorf("values property must be set")}
|
|
|
|
}
|
|
|
|
|
2022-02-03 14:42:10 +01:00
|
|
|
vals := make(map[string]bool, len(stringProps.Values))
|
2020-12-16 21:42:02 +01:00
|
|
|
for _, name := range stringProps.Values {
|
|
|
|
if err := checkVariableName(name); err != nil {
|
|
|
|
return []error{fmt.Errorf("soong_config_string_variable: values property error %s", err)}
|
2022-02-03 14:42:10 +01:00
|
|
|
} else if _, ok := vals[name]; ok {
|
|
|
|
return []error{fmt.Errorf("soong_config_string_variable: values property error: duplicate value: %q", name)}
|
2020-12-16 21:42:02 +01:00
|
|
|
}
|
2022-02-03 14:42:10 +01:00
|
|
|
vals[name] = true
|
2020-12-16 21:42:02 +01:00
|
|
|
}
|
|
|
|
|
2019-11-23 01:03:51 +01:00
|
|
|
v.variables[base.variable] = &stringVariable{
|
|
|
|
baseVariable: base,
|
|
|
|
values: CanonicalizeToProperties(stringProps.Values),
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func processBoolVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
|
|
|
|
base, errs := processVariableDef(def)
|
|
|
|
if len(errs) > 0 {
|
|
|
|
return errs
|
|
|
|
}
|
|
|
|
|
|
|
|
v.variables[base.variable] = &boolVariable{
|
|
|
|
baseVariable: base,
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func processVariableDef(def *parser.Module,
|
|
|
|
extraProps ...interface{}) (cond baseVariable, errs []error) {
|
|
|
|
|
|
|
|
props := &VariableProperties{}
|
|
|
|
|
|
|
|
allProps := append([]interface{}{props}, extraProps...)
|
|
|
|
|
|
|
|
_, errs = proptools.UnpackProperties(def.Properties, allProps...)
|
|
|
|
if len(errs) > 0 {
|
|
|
|
return baseVariable{}, errs
|
|
|
|
}
|
|
|
|
|
|
|
|
if props.Name == "" {
|
|
|
|
return baseVariable{}, []error{fmt.Errorf("name property must be set")}
|
|
|
|
}
|
|
|
|
|
|
|
|
return baseVariable{
|
|
|
|
variable: props.Name,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type SoongConfigDefinition struct {
|
|
|
|
ModuleTypes map[string]*ModuleType
|
|
|
|
|
|
|
|
variables map[string]soongConfigVariable
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateProperties returns a reflect.Value of a newly constructed type that contains the desired
|
|
|
|
// property layout for the Soong config variables, with each possible value an interface{} that
|
|
|
|
// contains a nil pointer to another newly constructed type that contains the affectable properties.
|
|
|
|
// The reflect.Value will be cloned for each call to the Soong config module type's factory method.
|
|
|
|
//
|
|
|
|
// For example, the acme_cc_defaults example above would
|
|
|
|
// produce a reflect.Value whose type is:
|
2022-08-16 19:27:33 +02:00
|
|
|
//
|
|
|
|
// *struct {
|
|
|
|
// Soong_config_variables struct {
|
|
|
|
// Board struct {
|
|
|
|
// Soc_a interface{}
|
|
|
|
// Soc_b interface{}
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
2019-11-23 01:03:51 +01:00
|
|
|
// And whose value is:
|
2022-08-16 19:27:33 +02:00
|
|
|
//
|
|
|
|
// &{
|
|
|
|
// Soong_config_variables: {
|
|
|
|
// Board: {
|
|
|
|
// Soc_a: (*struct{ Cflags []string })(nil),
|
|
|
|
// Soc_b: (*struct{ Cflags []string })(nil),
|
|
|
|
// },
|
|
|
|
// },
|
|
|
|
// }
|
2023-01-09 16:42:57 +01:00
|
|
|
func CreateProperties(factoryProps []interface{}, moduleType *ModuleType) reflect.Value {
|
2019-11-23 01:03:51 +01:00
|
|
|
var fields []reflect.StructField
|
|
|
|
|
|
|
|
affectablePropertiesType := createAffectablePropertiesType(moduleType.affectableProperties, factoryProps)
|
|
|
|
if affectablePropertiesType == nil {
|
|
|
|
return reflect.Value{}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range moduleType.Variables {
|
|
|
|
fields = append(fields, reflect.StructField{
|
|
|
|
Name: proptools.FieldNameForProperty(c.variableProperty()),
|
|
|
|
Type: c.variableValuesType(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
typ := reflect.StructOf([]reflect.StructField{{
|
2021-11-02 17:43:57 +01:00
|
|
|
Name: SoongConfigProperty,
|
2019-11-23 01:03:51 +01:00
|
|
|
Type: reflect.StructOf(fields),
|
|
|
|
}})
|
|
|
|
|
|
|
|
props := reflect.New(typ)
|
2021-11-02 17:43:57 +01:00
|
|
|
structConditions := props.Elem().FieldByName(SoongConfigProperty)
|
2019-11-23 01:03:51 +01:00
|
|
|
|
|
|
|
for i, c := range moduleType.Variables {
|
|
|
|
c.initializeProperties(structConditions.Field(i), affectablePropertiesType)
|
|
|
|
}
|
|
|
|
|
|
|
|
return props
|
|
|
|
}
|
|
|
|
|
|
|
|
// createAffectablePropertiesType creates a reflect.Type of a struct that has a field for each affectable property
|
|
|
|
// that exists in factoryProps.
|
|
|
|
func createAffectablePropertiesType(affectableProperties []string, factoryProps []interface{}) reflect.Type {
|
|
|
|
affectableProperties = append([]string(nil), affectableProperties...)
|
|
|
|
sort.Strings(affectableProperties)
|
|
|
|
|
|
|
|
var recurse func(prefix string, aps []string) ([]string, reflect.Type)
|
|
|
|
recurse = func(prefix string, aps []string) ([]string, reflect.Type) {
|
|
|
|
var fields []reflect.StructField
|
|
|
|
|
2021-03-06 02:25:41 +01:00
|
|
|
// Iterate while the list is non-empty so it can be modified in the loop.
|
2019-11-23 01:03:51 +01:00
|
|
|
for len(affectableProperties) > 0 {
|
|
|
|
p := affectableProperties[0]
|
|
|
|
if !strings.HasPrefix(affectableProperties[0], prefix) {
|
2021-03-06 02:25:41 +01:00
|
|
|
// The properties are sorted and recurse is always called with a prefix that matches
|
|
|
|
// the first property in the list, so if we've reached one that doesn't match the
|
|
|
|
// prefix we are done with this prefix.
|
2019-11-23 01:03:51 +01:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
nestedProperty := strings.TrimPrefix(p, prefix)
|
|
|
|
if i := strings.IndexRune(nestedProperty, '.'); i >= 0 {
|
|
|
|
var nestedType reflect.Type
|
|
|
|
nestedPrefix := nestedProperty[:i+1]
|
|
|
|
|
2021-03-06 02:25:41 +01:00
|
|
|
// Recurse to handle the properties with the found prefix. This will return
|
|
|
|
// an updated affectableProperties with the handled entries removed from the front
|
|
|
|
// of the list, and the type that contains the handled entries. The type may be
|
|
|
|
// nil if none of the entries matched factoryProps.
|
2019-11-23 01:03:51 +01:00
|
|
|
affectableProperties, nestedType = recurse(prefix+nestedPrefix, affectableProperties)
|
|
|
|
|
|
|
|
if nestedType != nil {
|
|
|
|
nestedFieldName := proptools.FieldNameForProperty(strings.TrimSuffix(nestedPrefix, "."))
|
|
|
|
|
|
|
|
fields = append(fields, reflect.StructField{
|
|
|
|
Name: nestedFieldName,
|
|
|
|
Type: nestedType,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
typ := typeForPropertyFromPropertyStructs(factoryProps, p)
|
|
|
|
if typ != nil {
|
|
|
|
fields = append(fields, reflect.StructField{
|
|
|
|
Name: proptools.FieldNameForProperty(nestedProperty),
|
|
|
|
Type: typ,
|
|
|
|
})
|
|
|
|
}
|
2021-03-06 02:25:41 +01:00
|
|
|
// The first element in the list has been handled, remove it from the list.
|
|
|
|
affectableProperties = affectableProperties[1:]
|
2019-11-23 01:03:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var typ reflect.Type
|
|
|
|
if len(fields) > 0 {
|
|
|
|
typ = reflect.StructOf(fields)
|
|
|
|
}
|
|
|
|
return affectableProperties, typ
|
|
|
|
}
|
|
|
|
|
|
|
|
affectableProperties, typ := recurse("", affectableProperties)
|
|
|
|
if len(affectableProperties) > 0 {
|
|
|
|
panic(fmt.Errorf("didn't handle all affectable properties"))
|
|
|
|
}
|
|
|
|
|
|
|
|
if typ != nil {
|
|
|
|
return reflect.PtrTo(typ)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func typeForPropertyFromPropertyStructs(psList []interface{}, property string) reflect.Type {
|
|
|
|
for _, ps := range psList {
|
|
|
|
if typ := typeForPropertyFromPropertyStruct(ps, property); typ != nil {
|
|
|
|
return typ
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func typeForPropertyFromPropertyStruct(ps interface{}, property string) reflect.Type {
|
|
|
|
v := reflect.ValueOf(ps)
|
|
|
|
for len(property) > 0 {
|
|
|
|
if !v.IsValid() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.Kind() == reflect.Interface {
|
|
|
|
if v.IsNil() {
|
|
|
|
return nil
|
|
|
|
} else {
|
|
|
|
v = v.Elem()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.Kind() == reflect.Ptr {
|
|
|
|
if v.IsNil() {
|
|
|
|
v = reflect.Zero(v.Type().Elem())
|
|
|
|
} else {
|
|
|
|
v = v.Elem()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.Kind() != reflect.Struct {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if index := strings.IndexRune(property, '.'); index >= 0 {
|
|
|
|
prefix := property[:index]
|
|
|
|
property = property[index+1:]
|
|
|
|
|
|
|
|
v = v.FieldByName(proptools.FieldNameForProperty(prefix))
|
|
|
|
} else {
|
|
|
|
f := v.FieldByName(proptools.FieldNameForProperty(property))
|
|
|
|
if !f.IsValid() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return f.Type()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// PropertiesToApply returns the applicable properties from a ModuleType that should be applied
|
|
|
|
// based on SoongConfig values.
|
2020-12-16 18:34:33 +01:00
|
|
|
// Expects that props contains a struct field with name soong_config_variables. The fields within
|
2020-12-16 21:42:02 +01:00
|
|
|
// soong_config_variables are expected to be in the same order as moduleType.Variables.
|
soong config: add value_variable substitution
There are some cases that aren't handled with the existing variable
types for booleans or known lists of strings. Similarly to our
product_variables that uses %s / %d for things like
PLATFORM_SDK_VERSION, allow vendors to define their own config variables
to be substituted into properties.
For example, some of the makefiles that I've attempted to convert had
the option to pass in version numbers from the board, or the default
display size:
-DDISPLAY_VERSION=550
-DDISP_H=1080
These examples happen to be integers, but since our configuration
language (make) doesn't support numbers, %s works just as well.
This change will allow the above to be represented using:
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
value_variables: [
"DISPLAY_VERSION",
"DISP_H",
],
properties: ["cflags"],
}
acme_cc_defaults {
name: "my_defaults",
soong_config_variables: {
DISPLAY_VERSION: {
cflags: ["-DDISPLAY_VERSION=%s"],
},
DISP_H: {
cflags: ["-DDISP_H=%s"],
}
},
}
Test: built-in tests
Change-Id: I18f35746b5cc39c304a136980249e886d38c6df6
2020-03-24 03:42:18 +01:00
|
|
|
func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
|
2019-11-23 01:03:51 +01:00
|
|
|
var ret []interface{}
|
2021-11-02 17:43:57 +01:00
|
|
|
props = props.Elem().FieldByName(SoongConfigProperty)
|
2019-11-23 01:03:51 +01:00
|
|
|
for i, c := range moduleType.Variables {
|
soong config: add value_variable substitution
There are some cases that aren't handled with the existing variable
types for booleans or known lists of strings. Similarly to our
product_variables that uses %s / %d for things like
PLATFORM_SDK_VERSION, allow vendors to define their own config variables
to be substituted into properties.
For example, some of the makefiles that I've attempted to convert had
the option to pass in version numbers from the board, or the default
display size:
-DDISPLAY_VERSION=550
-DDISP_H=1080
These examples happen to be integers, but since our configuration
language (make) doesn't support numbers, %s works just as well.
This change will allow the above to be represented using:
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
value_variables: [
"DISPLAY_VERSION",
"DISP_H",
],
properties: ["cflags"],
}
acme_cc_defaults {
name: "my_defaults",
soong_config_variables: {
DISPLAY_VERSION: {
cflags: ["-DDISPLAY_VERSION=%s"],
},
DISP_H: {
cflags: ["-DDISP_H=%s"],
}
},
}
Test: built-in tests
Change-Id: I18f35746b5cc39c304a136980249e886d38c6df6
2020-03-24 03:42:18 +01:00
|
|
|
if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil {
|
|
|
|
return nil, err
|
|
|
|
} else if ps != nil {
|
2019-11-23 01:03:51 +01:00
|
|
|
ret = append(ret, ps)
|
|
|
|
}
|
|
|
|
}
|
soong config: add value_variable substitution
There are some cases that aren't handled with the existing variable
types for booleans or known lists of strings. Similarly to our
product_variables that uses %s / %d for things like
PLATFORM_SDK_VERSION, allow vendors to define their own config variables
to be substituted into properties.
For example, some of the makefiles that I've attempted to convert had
the option to pass in version numbers from the board, or the default
display size:
-DDISPLAY_VERSION=550
-DDISP_H=1080
These examples happen to be integers, but since our configuration
language (make) doesn't support numbers, %s works just as well.
This change will allow the above to be represented using:
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
value_variables: [
"DISPLAY_VERSION",
"DISP_H",
],
properties: ["cflags"],
}
acme_cc_defaults {
name: "my_defaults",
soong_config_variables: {
DISPLAY_VERSION: {
cflags: ["-DDISPLAY_VERSION=%s"],
},
DISP_H: {
cflags: ["-DDISP_H=%s"],
}
},
}
Test: built-in tests
Change-Id: I18f35746b5cc39c304a136980249e886d38c6df6
2020-03-24 03:42:18 +01:00
|
|
|
return ret, nil
|
2019-11-23 01:03:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type ModuleType struct {
|
|
|
|
BaseModuleType string
|
|
|
|
ConfigNamespace string
|
|
|
|
Variables []soongConfigVariable
|
|
|
|
|
|
|
|
affectableProperties []string
|
|
|
|
variableNames []string
|
|
|
|
}
|
|
|
|
|
2020-12-16 21:42:02 +01:00
|
|
|
func newModuleType(props *ModuleTypeProperties) (*ModuleType, []error) {
|
|
|
|
mt := &ModuleType{
|
|
|
|
affectableProperties: props.Properties,
|
|
|
|
ConfigNamespace: props.Config_namespace,
|
|
|
|
BaseModuleType: props.Module_type,
|
|
|
|
variableNames: props.Variables,
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, name := range props.Bool_variables {
|
|
|
|
if err := checkVariableName(name); err != nil {
|
|
|
|
return nil, []error{fmt.Errorf("bool_variables %s", err)}
|
|
|
|
}
|
|
|
|
|
|
|
|
mt.Variables = append(mt.Variables, newBoolVariable(name))
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, name := range props.Value_variables {
|
|
|
|
if err := checkVariableName(name); err != nil {
|
|
|
|
return nil, []error{fmt.Errorf("value_variables %s", err)}
|
|
|
|
}
|
|
|
|
|
|
|
|
mt.Variables = append(mt.Variables, &valueVariable{
|
|
|
|
baseVariable: baseVariable{
|
|
|
|
variable: name,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return mt, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkVariableName(name string) error {
|
|
|
|
if name == "" {
|
|
|
|
return fmt.Errorf("name must not be blank")
|
|
|
|
} else if name == conditionsDefault {
|
|
|
|
return fmt.Errorf("%q is reserved", conditionsDefault)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-11-23 01:03:51 +01:00
|
|
|
type soongConfigVariable interface {
|
|
|
|
// variableProperty returns the name of the variable.
|
|
|
|
variableProperty() string
|
|
|
|
|
|
|
|
// conditionalValuesType returns a reflect.Type that contains an interface{} for each possible value.
|
|
|
|
variableValuesType() reflect.Type
|
|
|
|
|
|
|
|
// initializeProperties is passed a reflect.Value of the reflect.Type returned by conditionalValuesType and a
|
|
|
|
// reflect.Type of the affectable properties, and should initialize each interface{} in the reflect.Value with
|
|
|
|
// the zero value of the affectable properties type.
|
|
|
|
initializeProperties(v reflect.Value, typ reflect.Type)
|
|
|
|
|
|
|
|
// PropertiesToApply should return one of the interface{} values set by initializeProperties to be applied
|
|
|
|
// to the module.
|
soong config: add value_variable substitution
There are some cases that aren't handled with the existing variable
types for booleans or known lists of strings. Similarly to our
product_variables that uses %s / %d for things like
PLATFORM_SDK_VERSION, allow vendors to define their own config variables
to be substituted into properties.
For example, some of the makefiles that I've attempted to convert had
the option to pass in version numbers from the board, or the default
display size:
-DDISPLAY_VERSION=550
-DDISP_H=1080
These examples happen to be integers, but since our configuration
language (make) doesn't support numbers, %s works just as well.
This change will allow the above to be represented using:
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
value_variables: [
"DISPLAY_VERSION",
"DISP_H",
],
properties: ["cflags"],
}
acme_cc_defaults {
name: "my_defaults",
soong_config_variables: {
DISPLAY_VERSION: {
cflags: ["-DDISPLAY_VERSION=%s"],
},
DISP_H: {
cflags: ["-DDISP_H=%s"],
}
},
}
Test: built-in tests
Change-Id: I18f35746b5cc39c304a136980249e886d38c6df6
2020-03-24 03:42:18 +01:00
|
|
|
PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error)
|
2019-11-23 01:03:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type baseVariable struct {
|
|
|
|
variable string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *baseVariable) variableProperty() string {
|
|
|
|
return CanonicalizeToProperty(c.variable)
|
|
|
|
}
|
|
|
|
|
|
|
|
type stringVariable struct {
|
|
|
|
baseVariable
|
|
|
|
values []string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *stringVariable) variableValuesType() reflect.Type {
|
|
|
|
var fields []reflect.StructField
|
|
|
|
|
2020-12-16 21:42:02 +01:00
|
|
|
var values []string
|
|
|
|
values = append(values, s.values...)
|
|
|
|
values = append(values, conditionsDefault)
|
|
|
|
for _, v := range values {
|
2019-11-23 01:03:51 +01:00
|
|
|
fields = append(fields, reflect.StructField{
|
|
|
|
Name: proptools.FieldNameForProperty(v),
|
|
|
|
Type: emptyInterfaceType,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return reflect.StructOf(fields)
|
|
|
|
}
|
|
|
|
|
2020-12-16 21:42:02 +01:00
|
|
|
// initializeProperties initializes properties to zero value of typ for supported values and a final
|
|
|
|
// conditions default field.
|
2019-11-23 01:03:51 +01:00
|
|
|
func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
|
|
|
|
for i := range s.values {
|
|
|
|
v.Field(i).Set(reflect.Zero(typ))
|
|
|
|
}
|
2020-12-16 21:42:02 +01:00
|
|
|
v.Field(len(s.values)).Set(reflect.Zero(typ)) // conditions default is the final value
|
2019-11-23 01:03:51 +01:00
|
|
|
}
|
|
|
|
|
2020-12-16 21:42:02 +01:00
|
|
|
// Extracts an interface from values containing the properties to apply based on config.
|
|
|
|
// If config does not match a value with a non-nil property set, the default value will be returned.
|
soong config: add value_variable substitution
There are some cases that aren't handled with the existing variable
types for booleans or known lists of strings. Similarly to our
product_variables that uses %s / %d for things like
PLATFORM_SDK_VERSION, allow vendors to define their own config variables
to be substituted into properties.
For example, some of the makefiles that I've attempted to convert had
the option to pass in version numbers from the board, or the default
display size:
-DDISPLAY_VERSION=550
-DDISP_H=1080
These examples happen to be integers, but since our configuration
language (make) doesn't support numbers, %s works just as well.
This change will allow the above to be represented using:
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
value_variables: [
"DISPLAY_VERSION",
"DISP_H",
],
properties: ["cflags"],
}
acme_cc_defaults {
name: "my_defaults",
soong_config_variables: {
DISPLAY_VERSION: {
cflags: ["-DDISPLAY_VERSION=%s"],
},
DISP_H: {
cflags: ["-DDISP_H=%s"],
}
},
}
Test: built-in tests
Change-Id: I18f35746b5cc39c304a136980249e886d38c6df6
2020-03-24 03:42:18 +01:00
|
|
|
func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
|
2022-11-01 18:10:23 +01:00
|
|
|
configValue := config.String(s.variable)
|
|
|
|
if configValue != "" && !InList(configValue, s.values) {
|
|
|
|
return nil, fmt.Errorf("Soong config property %q must be one of %v, found %q", s.variable, s.values, configValue)
|
|
|
|
}
|
2019-11-23 01:03:51 +01:00
|
|
|
for j, v := range s.values {
|
2020-12-16 21:42:02 +01:00
|
|
|
f := values.Field(j)
|
2022-11-01 18:10:23 +01:00
|
|
|
if configValue == v && !f.Elem().IsNil() {
|
2020-12-16 21:42:02 +01:00
|
|
|
return f.Interface(), nil
|
2019-11-23 01:03:51 +01:00
|
|
|
}
|
|
|
|
}
|
2020-12-16 21:42:02 +01:00
|
|
|
// if we have reached this point, we have checked all valid values of string and either:
|
|
|
|
// * the value was not set
|
|
|
|
// * the value was set but that value was not specified in the Android.bp file
|
|
|
|
return values.Field(len(s.values)).Interface(), nil
|
2019-11-23 01:03:51 +01:00
|
|
|
}
|
|
|
|
|
2020-12-16 21:42:02 +01:00
|
|
|
// Struct to allow conditions set based on a boolean variable
|
2019-11-23 01:03:51 +01:00
|
|
|
type boolVariable struct {
|
|
|
|
baseVariable
|
|
|
|
}
|
|
|
|
|
2020-12-16 21:42:02 +01:00
|
|
|
// newBoolVariable constructs a boolVariable with the given name
|
2020-12-16 18:34:33 +01:00
|
|
|
func newBoolVariable(name string) *boolVariable {
|
|
|
|
return &boolVariable{
|
|
|
|
baseVariable{
|
|
|
|
variable: name,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-23 01:03:51 +01:00
|
|
|
func (b boolVariable) variableValuesType() reflect.Type {
|
|
|
|
return emptyInterfaceType
|
|
|
|
}
|
|
|
|
|
2020-12-16 21:42:02 +01:00
|
|
|
// initializeProperties initializes a property to zero value of typ with an additional conditions
|
|
|
|
// default field.
|
2019-11-23 01:03:51 +01:00
|
|
|
func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
|
2020-12-16 21:42:02 +01:00
|
|
|
initializePropertiesWithDefault(v, typ)
|
2019-11-23 01:03:51 +01:00
|
|
|
}
|
|
|
|
|
2020-12-16 21:42:02 +01:00
|
|
|
// initializePropertiesWithDefault, initialize with zero value, v to contain a field for each field
|
|
|
|
// in typ, with an additional field for defaults of type typ. This should be used to initialize
|
|
|
|
// boolVariable, valueVariable, or any future implementations of soongConfigVariable which support
|
|
|
|
// one variable and a default.
|
|
|
|
func initializePropertiesWithDefault(v reflect.Value, typ reflect.Type) {
|
|
|
|
sTyp := typ.Elem()
|
|
|
|
var fields []reflect.StructField
|
|
|
|
for i := 0; i < sTyp.NumField(); i++ {
|
|
|
|
fields = append(fields, sTyp.Field(i))
|
|
|
|
}
|
|
|
|
|
|
|
|
// create conditions_default field
|
|
|
|
nestedFieldName := proptools.FieldNameForProperty(conditionsDefault)
|
|
|
|
fields = append(fields, reflect.StructField{
|
|
|
|
Name: nestedFieldName,
|
|
|
|
Type: typ,
|
|
|
|
})
|
|
|
|
|
|
|
|
newTyp := reflect.PtrTo(reflect.StructOf(fields))
|
|
|
|
v.Set(reflect.Zero(newTyp))
|
|
|
|
}
|
|
|
|
|
|
|
|
// conditionsDefaultField extracts the conditions_default field from v. This is always the final
|
|
|
|
// field if initialized with initializePropertiesWithDefault.
|
|
|
|
func conditionsDefaultField(v reflect.Value) reflect.Value {
|
|
|
|
return v.Field(v.NumField() - 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// removeDefault removes the conditions_default field from values while retaining values from all
|
|
|
|
// other fields. This allows
|
|
|
|
func removeDefault(values reflect.Value) reflect.Value {
|
|
|
|
v := values.Elem().Elem()
|
|
|
|
s := conditionsDefaultField(v)
|
|
|
|
// if conditions_default field was not set, there will be no issues extending properties.
|
|
|
|
if !s.IsValid() {
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// If conditions_default field was set, it has the correct type for our property. Create a new
|
|
|
|
// reflect.Value of the conditions_default type and copy all fields (except for
|
|
|
|
// conditions_default) based on values to the result.
|
|
|
|
res := reflect.New(s.Type().Elem())
|
|
|
|
for i := 0; i < res.Type().Elem().NumField(); i++ {
|
|
|
|
val := v.Field(i)
|
|
|
|
res.Elem().Field(i).Set(val)
|
|
|
|
}
|
|
|
|
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
|
|
|
// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
|
|
|
|
// the module. If the value was not set, conditions_default interface will be returned; otherwise,
|
|
|
|
// the interface in values, without conditions_default will be returned.
|
soong config: add value_variable substitution
There are some cases that aren't handled with the existing variable
types for booleans or known lists of strings. Similarly to our
product_variables that uses %s / %d for things like
PLATFORM_SDK_VERSION, allow vendors to define their own config variables
to be substituted into properties.
For example, some of the makefiles that I've attempted to convert had
the option to pass in version numbers from the board, or the default
display size:
-DDISPLAY_VERSION=550
-DDISP_H=1080
These examples happen to be integers, but since our configuration
language (make) doesn't support numbers, %s works just as well.
This change will allow the above to be represented using:
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
value_variables: [
"DISPLAY_VERSION",
"DISP_H",
],
properties: ["cflags"],
}
acme_cc_defaults {
name: "my_defaults",
soong_config_variables: {
DISPLAY_VERSION: {
cflags: ["-DDISPLAY_VERSION=%s"],
},
DISP_H: {
cflags: ["-DDISP_H=%s"],
}
},
}
Test: built-in tests
Change-Id: I18f35746b5cc39c304a136980249e886d38c6df6
2020-03-24 03:42:18 +01:00
|
|
|
func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
|
2020-12-16 21:42:02 +01:00
|
|
|
// If this variable was not referenced in the module, there are no properties to apply.
|
|
|
|
if values.Elem().IsZero() {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2019-11-23 01:03:51 +01:00
|
|
|
if config.Bool(b.variable) {
|
2020-12-16 21:42:02 +01:00
|
|
|
values = removeDefault(values)
|
soong config: add value_variable substitution
There are some cases that aren't handled with the existing variable
types for booleans or known lists of strings. Similarly to our
product_variables that uses %s / %d for things like
PLATFORM_SDK_VERSION, allow vendors to define their own config variables
to be substituted into properties.
For example, some of the makefiles that I've attempted to convert had
the option to pass in version numbers from the board, or the default
display size:
-DDISPLAY_VERSION=550
-DDISP_H=1080
These examples happen to be integers, but since our configuration
language (make) doesn't support numbers, %s works just as well.
This change will allow the above to be represented using:
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
value_variables: [
"DISPLAY_VERSION",
"DISP_H",
],
properties: ["cflags"],
}
acme_cc_defaults {
name: "my_defaults",
soong_config_variables: {
DISPLAY_VERSION: {
cflags: ["-DDISPLAY_VERSION=%s"],
},
DISP_H: {
cflags: ["-DDISP_H=%s"],
}
},
}
Test: built-in tests
Change-Id: I18f35746b5cc39c304a136980249e886d38c6df6
2020-03-24 03:42:18 +01:00
|
|
|
return values.Interface(), nil
|
|
|
|
}
|
2020-12-16 21:42:02 +01:00
|
|
|
v := values.Elem().Elem()
|
|
|
|
if f := conditionsDefaultField(v); f.IsValid() {
|
|
|
|
return f.Interface(), nil
|
|
|
|
}
|
soong config: add value_variable substitution
There are some cases that aren't handled with the existing variable
types for booleans or known lists of strings. Similarly to our
product_variables that uses %s / %d for things like
PLATFORM_SDK_VERSION, allow vendors to define their own config variables
to be substituted into properties.
For example, some of the makefiles that I've attempted to convert had
the option to pass in version numbers from the board, or the default
display size:
-DDISPLAY_VERSION=550
-DDISP_H=1080
These examples happen to be integers, but since our configuration
language (make) doesn't support numbers, %s works just as well.
This change will allow the above to be represented using:
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
value_variables: [
"DISPLAY_VERSION",
"DISP_H",
],
properties: ["cflags"],
}
acme_cc_defaults {
name: "my_defaults",
soong_config_variables: {
DISPLAY_VERSION: {
cflags: ["-DDISPLAY_VERSION=%s"],
},
DISP_H: {
cflags: ["-DDISP_H=%s"],
}
},
}
Test: built-in tests
Change-Id: I18f35746b5cc39c304a136980249e886d38c6df6
2020-03-24 03:42:18 +01:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2020-12-16 21:42:02 +01:00
|
|
|
// Struct to allow conditions set based on a value variable, supporting string substitution.
|
soong config: add value_variable substitution
There are some cases that aren't handled with the existing variable
types for booleans or known lists of strings. Similarly to our
product_variables that uses %s / %d for things like
PLATFORM_SDK_VERSION, allow vendors to define their own config variables
to be substituted into properties.
For example, some of the makefiles that I've attempted to convert had
the option to pass in version numbers from the board, or the default
display size:
-DDISPLAY_VERSION=550
-DDISP_H=1080
These examples happen to be integers, but since our configuration
language (make) doesn't support numbers, %s works just as well.
This change will allow the above to be represented using:
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
value_variables: [
"DISPLAY_VERSION",
"DISP_H",
],
properties: ["cflags"],
}
acme_cc_defaults {
name: "my_defaults",
soong_config_variables: {
DISPLAY_VERSION: {
cflags: ["-DDISPLAY_VERSION=%s"],
},
DISP_H: {
cflags: ["-DDISP_H=%s"],
}
},
}
Test: built-in tests
Change-Id: I18f35746b5cc39c304a136980249e886d38c6df6
2020-03-24 03:42:18 +01:00
|
|
|
type valueVariable struct {
|
|
|
|
baseVariable
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *valueVariable) variableValuesType() reflect.Type {
|
|
|
|
return emptyInterfaceType
|
|
|
|
}
|
|
|
|
|
2020-12-16 21:42:02 +01:00
|
|
|
// initializeProperties initializes a property to zero value of typ with an additional conditions
|
|
|
|
// default field.
|
soong config: add value_variable substitution
There are some cases that aren't handled with the existing variable
types for booleans or known lists of strings. Similarly to our
product_variables that uses %s / %d for things like
PLATFORM_SDK_VERSION, allow vendors to define their own config variables
to be substituted into properties.
For example, some of the makefiles that I've attempted to convert had
the option to pass in version numbers from the board, or the default
display size:
-DDISPLAY_VERSION=550
-DDISP_H=1080
These examples happen to be integers, but since our configuration
language (make) doesn't support numbers, %s works just as well.
This change will allow the above to be represented using:
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
value_variables: [
"DISPLAY_VERSION",
"DISP_H",
],
properties: ["cflags"],
}
acme_cc_defaults {
name: "my_defaults",
soong_config_variables: {
DISPLAY_VERSION: {
cflags: ["-DDISPLAY_VERSION=%s"],
},
DISP_H: {
cflags: ["-DDISP_H=%s"],
}
},
}
Test: built-in tests
Change-Id: I18f35746b5cc39c304a136980249e886d38c6df6
2020-03-24 03:42:18 +01:00
|
|
|
func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
|
2020-12-16 21:42:02 +01:00
|
|
|
initializePropertiesWithDefault(v, typ)
|
soong config: add value_variable substitution
There are some cases that aren't handled with the existing variable
types for booleans or known lists of strings. Similarly to our
product_variables that uses %s / %d for things like
PLATFORM_SDK_VERSION, allow vendors to define their own config variables
to be substituted into properties.
For example, some of the makefiles that I've attempted to convert had
the option to pass in version numbers from the board, or the default
display size:
-DDISPLAY_VERSION=550
-DDISP_H=1080
These examples happen to be integers, but since our configuration
language (make) doesn't support numbers, %s works just as well.
This change will allow the above to be represented using:
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
value_variables: [
"DISPLAY_VERSION",
"DISP_H",
],
properties: ["cflags"],
}
acme_cc_defaults {
name: "my_defaults",
soong_config_variables: {
DISPLAY_VERSION: {
cflags: ["-DDISPLAY_VERSION=%s"],
},
DISP_H: {
cflags: ["-DDISP_H=%s"],
}
},
}
Test: built-in tests
Change-Id: I18f35746b5cc39c304a136980249e886d38c6df6
2020-03-24 03:42:18 +01:00
|
|
|
}
|
|
|
|
|
2020-12-16 21:42:02 +01:00
|
|
|
// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
|
|
|
|
// the module. If the variable was not set, conditions_default interface will be returned;
|
|
|
|
// otherwise, the interface in values, without conditions_default will be returned with all
|
|
|
|
// appropriate string substitutions based on variable being set.
|
soong config: add value_variable substitution
There are some cases that aren't handled with the existing variable
types for booleans or known lists of strings. Similarly to our
product_variables that uses %s / %d for things like
PLATFORM_SDK_VERSION, allow vendors to define their own config variables
to be substituted into properties.
For example, some of the makefiles that I've attempted to convert had
the option to pass in version numbers from the board, or the default
display size:
-DDISPLAY_VERSION=550
-DDISP_H=1080
These examples happen to be integers, but since our configuration
language (make) doesn't support numbers, %s works just as well.
This change will allow the above to be represented using:
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
value_variables: [
"DISPLAY_VERSION",
"DISP_H",
],
properties: ["cflags"],
}
acme_cc_defaults {
name: "my_defaults",
soong_config_variables: {
DISPLAY_VERSION: {
cflags: ["-DDISPLAY_VERSION=%s"],
},
DISP_H: {
cflags: ["-DDISP_H=%s"],
}
},
}
Test: built-in tests
Change-Id: I18f35746b5cc39c304a136980249e886d38c6df6
2020-03-24 03:42:18 +01:00
|
|
|
func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
|
2020-12-16 21:42:02 +01:00
|
|
|
// If this variable was not referenced in the module, there are no properties to apply.
|
|
|
|
if !values.IsValid() || values.Elem().IsZero() {
|
soong config: add value_variable substitution
There are some cases that aren't handled with the existing variable
types for booleans or known lists of strings. Similarly to our
product_variables that uses %s / %d for things like
PLATFORM_SDK_VERSION, allow vendors to define their own config variables
to be substituted into properties.
For example, some of the makefiles that I've attempted to convert had
the option to pass in version numbers from the board, or the default
display size:
-DDISPLAY_VERSION=550
-DDISP_H=1080
These examples happen to be integers, but since our configuration
language (make) doesn't support numbers, %s works just as well.
This change will allow the above to be represented using:
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
value_variables: [
"DISPLAY_VERSION",
"DISP_H",
],
properties: ["cflags"],
}
acme_cc_defaults {
name: "my_defaults",
soong_config_variables: {
DISPLAY_VERSION: {
cflags: ["-DDISPLAY_VERSION=%s"],
},
DISP_H: {
cflags: ["-DDISP_H=%s"],
}
},
}
Test: built-in tests
Change-Id: I18f35746b5cc39c304a136980249e886d38c6df6
2020-03-24 03:42:18 +01:00
|
|
|
return nil, nil
|
|
|
|
}
|
2020-12-16 21:42:02 +01:00
|
|
|
if !config.IsSet(s.variable) {
|
|
|
|
return conditionsDefaultField(values.Elem().Elem()).Interface(), nil
|
|
|
|
}
|
soong config: add value_variable substitution
There are some cases that aren't handled with the existing variable
types for booleans or known lists of strings. Similarly to our
product_variables that uses %s / %d for things like
PLATFORM_SDK_VERSION, allow vendors to define their own config variables
to be substituted into properties.
For example, some of the makefiles that I've attempted to convert had
the option to pass in version numbers from the board, or the default
display size:
-DDISPLAY_VERSION=550
-DDISP_H=1080
These examples happen to be integers, but since our configuration
language (make) doesn't support numbers, %s works just as well.
This change will allow the above to be represented using:
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
value_variables: [
"DISPLAY_VERSION",
"DISP_H",
],
properties: ["cflags"],
}
acme_cc_defaults {
name: "my_defaults",
soong_config_variables: {
DISPLAY_VERSION: {
cflags: ["-DDISPLAY_VERSION=%s"],
},
DISP_H: {
cflags: ["-DDISP_H=%s"],
}
},
}
Test: built-in tests
Change-Id: I18f35746b5cc39c304a136980249e886d38c6df6
2020-03-24 03:42:18 +01:00
|
|
|
configValue := config.String(s.variable)
|
|
|
|
|
2020-12-16 21:42:02 +01:00
|
|
|
values = removeDefault(values)
|
|
|
|
propStruct := values.Elem()
|
2020-12-16 19:59:00 +01:00
|
|
|
if !propStruct.IsValid() {
|
|
|
|
return nil, nil
|
|
|
|
}
|
soong config: add value_variable substitution
There are some cases that aren't handled with the existing variable
types for booleans or known lists of strings. Similarly to our
product_variables that uses %s / %d for things like
PLATFORM_SDK_VERSION, allow vendors to define their own config variables
to be substituted into properties.
For example, some of the makefiles that I've attempted to convert had
the option to pass in version numbers from the board, or the default
display size:
-DDISPLAY_VERSION=550
-DDISP_H=1080
These examples happen to be integers, but since our configuration
language (make) doesn't support numbers, %s works just as well.
This change will allow the above to be represented using:
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
value_variables: [
"DISPLAY_VERSION",
"DISP_H",
],
properties: ["cflags"],
}
acme_cc_defaults {
name: "my_defaults",
soong_config_variables: {
DISPLAY_VERSION: {
cflags: ["-DDISPLAY_VERSION=%s"],
},
DISP_H: {
cflags: ["-DDISP_H=%s"],
}
},
}
Test: built-in tests
Change-Id: I18f35746b5cc39c304a136980249e886d38c6df6
2020-03-24 03:42:18 +01:00
|
|
|
for i := 0; i < propStruct.NumField(); i++ {
|
|
|
|
field := propStruct.Field(i)
|
|
|
|
kind := field.Kind()
|
|
|
|
if kind == reflect.Ptr {
|
|
|
|
if field.IsNil() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
field = field.Elem()
|
2024-01-12 21:12:26 +01:00
|
|
|
kind = field.Kind()
|
soong config: add value_variable substitution
There are some cases that aren't handled with the existing variable
types for booleans or known lists of strings. Similarly to our
product_variables that uses %s / %d for things like
PLATFORM_SDK_VERSION, allow vendors to define their own config variables
to be substituted into properties.
For example, some of the makefiles that I've attempted to convert had
the option to pass in version numbers from the board, or the default
display size:
-DDISPLAY_VERSION=550
-DDISP_H=1080
These examples happen to be integers, but since our configuration
language (make) doesn't support numbers, %s works just as well.
This change will allow the above to be represented using:
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
value_variables: [
"DISPLAY_VERSION",
"DISP_H",
],
properties: ["cflags"],
}
acme_cc_defaults {
name: "my_defaults",
soong_config_variables: {
DISPLAY_VERSION: {
cflags: ["-DDISPLAY_VERSION=%s"],
},
DISP_H: {
cflags: ["-DDISP_H=%s"],
}
},
}
Test: built-in tests
Change-Id: I18f35746b5cc39c304a136980249e886d38c6df6
2020-03-24 03:42:18 +01:00
|
|
|
}
|
|
|
|
switch kind {
|
|
|
|
case reflect.String:
|
|
|
|
err := printfIntoProperty(field, configValue)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
|
|
|
|
}
|
|
|
|
case reflect.Slice:
|
|
|
|
for j := 0; j < field.Len(); j++ {
|
|
|
|
err := printfIntoProperty(field.Index(j), configValue)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case reflect.Bool:
|
|
|
|
// Nothing to do
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, propStruct.Type().Field(i).Name, kind)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return values.Interface(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func printfIntoProperty(propertyValue reflect.Value, configValue string) error {
|
|
|
|
s := propertyValue.String()
|
|
|
|
|
|
|
|
count := strings.Count(s, "%")
|
|
|
|
if count == 0 {
|
|
|
|
return nil
|
2019-11-23 01:03:51 +01:00
|
|
|
}
|
|
|
|
|
soong config: add value_variable substitution
There are some cases that aren't handled with the existing variable
types for booleans or known lists of strings. Similarly to our
product_variables that uses %s / %d for things like
PLATFORM_SDK_VERSION, allow vendors to define their own config variables
to be substituted into properties.
For example, some of the makefiles that I've attempted to convert had
the option to pass in version numbers from the board, or the default
display size:
-DDISPLAY_VERSION=550
-DDISP_H=1080
These examples happen to be integers, but since our configuration
language (make) doesn't support numbers, %s works just as well.
This change will allow the above to be represented using:
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
value_variables: [
"DISPLAY_VERSION",
"DISP_H",
],
properties: ["cflags"],
}
acme_cc_defaults {
name: "my_defaults",
soong_config_variables: {
DISPLAY_VERSION: {
cflags: ["-DDISPLAY_VERSION=%s"],
},
DISP_H: {
cflags: ["-DDISP_H=%s"],
}
},
}
Test: built-in tests
Change-Id: I18f35746b5cc39c304a136980249e886d38c6df6
2020-03-24 03:42:18 +01:00
|
|
|
if count > 1 {
|
|
|
|
return fmt.Errorf("value variable properties only support a single '%%'")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !strings.Contains(s, "%s") {
|
|
|
|
return fmt.Errorf("unsupported %% in value variable property")
|
|
|
|
}
|
|
|
|
|
|
|
|
propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, configValue)))
|
|
|
|
|
2019-11-23 01:03:51 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func CanonicalizeToProperty(v string) string {
|
|
|
|
return strings.Map(func(r rune) rune {
|
|
|
|
switch {
|
|
|
|
case r >= 'A' && r <= 'Z',
|
|
|
|
r >= 'a' && r <= 'z',
|
|
|
|
r >= '0' && r <= '9',
|
|
|
|
r == '_':
|
|
|
|
return r
|
|
|
|
default:
|
|
|
|
return '_'
|
|
|
|
}
|
|
|
|
}, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
func CanonicalizeToProperties(values []string) []string {
|
|
|
|
ret := make([]string, len(values))
|
|
|
|
for i, v := range values {
|
|
|
|
ret[i] = CanonicalizeToProperty(v)
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
type emptyInterfaceStruct struct {
|
|
|
|
i interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
var emptyInterfaceType = reflect.TypeOf(emptyInterfaceStruct{}).Field(0).Type
|
2022-11-01 18:10:23 +01:00
|
|
|
|
|
|
|
// InList checks if the string belongs to the list
|
|
|
|
func InList(s string, list []string) bool {
|
|
|
|
for _, s2 := range list {
|
|
|
|
if s2 == s {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|