2015-01-23 23:15:10 +01:00
// Copyright 2014 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.
2020-01-02 18:37:49 +01:00
package proptools
2014-05-28 01:34:41 +02:00
import (
"fmt"
"reflect"
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
"sort"
"strconv"
"strings"
2020-01-02 18:37:49 +01:00
"text/scanner"
2015-06-22 22:38:45 +02:00
"github.com/google/blueprint/parser"
2014-05-28 01:34:41 +02:00
)
2020-01-02 18:37:49 +01:00
const maxUnpackErrors = 10
type UnpackError struct {
Err error
Pos scanner . Position
}
func ( e * UnpackError ) Error ( ) string {
return fmt . Sprintf ( "%s: %s" , e . Pos , e . Err )
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
// packedProperty helps to track properties usage (`used` will be true)
2014-05-28 01:34:41 +02:00
type packedProperty struct {
property * parser . Property
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
used bool
2014-05-28 01:34:41 +02:00
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
// unpackContext keeps compound names and their values in a map. It is initialized from
// parsed properties.
type unpackContext struct {
2022-08-02 16:51:08 +02:00
propertyMap map [ string ] * packedProperty
errs [ ] error
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
}
2014-05-28 01:34:41 +02:00
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
// UnpackProperties populates the list of runtime values ("property structs") from the parsed properties.
// If a property a.b.c has a value, a field with the matching name in each runtime value is initialized
// from it. See PropertyNameForField for field and property name matching.
// For instance, if the input contains
2023-02-21 20:40:56 +01:00
//
// { foo: "abc", bar: {x: 1},}
//
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
// and a runtime value being has been declared as
2023-02-21 20:40:56 +01:00
//
// var v struct { Foo string; Bar int }
//
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
// then v.Foo will be set to "abc" and v.Bar will be set to 1
// (cf. unpack_test.go for further examples)
//
// The type of a receiving field has to match the property type, i.e., a bool/int/string field
// can be set from a property with bool/int/string value, a struct can be set from a map (only the
// matching fields are set), and an slice can be set from a list.
// If a field of a runtime value has been already set prior to the UnpackProperties, the new value
// is appended to it (see somewhat inappropriately named ExtendBasicType).
// The same property can initialize fields in multiple runtime values. It is an error if any property
// value was not used to initialize at least one field.
func UnpackProperties ( properties [ ] * parser . Property , objects ... interface { } ) ( map [ string ] * parser . Property , [ ] error ) {
var unpackContext unpackContext
unpackContext . propertyMap = make ( map [ string ] * packedProperty )
if ! unpackContext . buildPropertyMap ( "" , properties ) {
return nil , unpackContext . errs
2014-05-28 01:34:41 +02:00
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
for _ , obj := range objects {
valueObject := reflect . ValueOf ( obj )
if ! isStructPtr ( valueObject . Type ( ) ) {
2020-01-28 01:48:30 +01:00
panic ( fmt . Errorf ( "properties must be *struct, got %s" ,
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
valueObject . Type ( ) ) )
2014-05-28 01:34:41 +02:00
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
unpackContext . unpackToStruct ( "" , valueObject . Elem ( ) )
if len ( unpackContext . errs ) >= maxUnpackErrors {
return nil , unpackContext . errs
2014-05-28 01:34:41 +02:00
}
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
// Gather property map, and collect any unused properties.
// Avoid reporting subproperties of unused properties.
2014-09-30 20:38:25 +02:00
result := make ( map [ string ] * parser . Property )
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
var unusedNames [ ] string
for name , v := range unpackContext . propertyMap {
if v . used {
result [ name ] = v . property
} else {
unusedNames = append ( unusedNames , name )
2014-05-28 01:34:41 +02:00
}
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
if len ( unusedNames ) == 0 && len ( unpackContext . errs ) == 0 {
return result , nil
2014-09-30 20:38:25 +02:00
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
return nil , unpackContext . reportUnusedNames ( unusedNames )
2014-09-30 20:38:25 +02:00
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
func ( ctx * unpackContext ) reportUnusedNames ( unusedNames [ ] string ) [ ] error {
sort . Strings ( unusedNames )
2023-10-30 22:48:37 +01:00
unusedNames = removeUnnecessaryUnusedNames ( unusedNames )
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
var lastReported string
for _ , name := range unusedNames {
// if 'foo' has been reported, ignore 'foo\..*' and 'foo\[.*'
if lastReported != "" {
trimmed := strings . TrimPrefix ( name , lastReported )
if trimmed != name && ( trimmed [ 0 ] == '.' || trimmed [ 0 ] == '[' ) {
2014-09-30 20:38:25 +02:00
continue
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
}
ctx . errs = append ( ctx . errs , & UnpackError {
fmt . Errorf ( "unrecognized property %q" , name ) ,
ctx . propertyMap [ name ] . property . ColonPos } )
lastReported = name
}
return ctx . errs
}
2023-10-30 22:48:37 +01:00
// When property a.b.c is not used, (also there is no a.* or a.b.* used)
// "a", "a.b" and "a.b.c" are all in unusedNames.
// removeUnnecessaryUnusedNames only keeps the last "a.b.c" as the real unused
// name.
func removeUnnecessaryUnusedNames ( names [ ] string ) [ ] string {
if len ( names ) == 0 {
return names
}
var simplifiedNames [ ] string
for index , name := range names {
if index == len ( names ) - 1 || ! strings . HasPrefix ( names [ index + 1 ] , name ) {
simplifiedNames = append ( simplifiedNames , name )
}
}
return simplifiedNames
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
func ( ctx * unpackContext ) buildPropertyMap ( prefix string , properties [ ] * parser . Property ) bool {
nOldErrors := len ( ctx . errs )
for _ , property := range properties {
name := fieldPath ( prefix , property . Name )
if first , present := ctx . propertyMap [ name ] ; present {
ctx . addError (
& UnpackError { fmt . Errorf ( "property %q already defined" , name ) , property . ColonPos } )
if ctx . addError (
& UnpackError { fmt . Errorf ( "<-- previous definition here" ) , first . property . ColonPos } ) {
return false
2014-09-30 20:38:25 +02:00
}
continue
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
ctx . propertyMap [ name ] = & packedProperty { property , false }
2020-03-04 02:36:00 +01:00
switch propValue := property . Value . Eval ( ) . ( type ) {
case * parser . Map :
ctx . buildPropertyMap ( name , propValue . Properties )
case * parser . List :
// If it is a list, unroll it unless its elements are of primitive type
// (no further mapping will be needed in that case, so we avoid cluttering
// the map).
if len ( propValue . Values ) == 0 {
continue
}
2022-08-02 16:51:08 +02:00
if t := propValue . Values [ 0 ] . Type ( ) ; t == parser . StringType || t == parser . Int64Type || t == parser . BoolType {
2020-03-04 02:36:00 +01:00
continue
}
2014-09-30 20:38:25 +02:00
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
itemProperties := make ( [ ] * parser . Property , len ( propValue . Values ) )
2020-03-04 02:36:00 +01:00
for i , expr := range propValue . Values {
itemProperties [ i ] = & parser . Property {
Name : property . Name + "[" + strconv . Itoa ( i ) + "]" ,
NamePos : property . NamePos ,
ColonPos : property . ColonPos ,
Value : expr ,
}
}
if ! ctx . buildPropertyMap ( prefix , itemProperties ) {
return false
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
}
}
2014-09-30 20:38:25 +02:00
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
return len ( ctx . errs ) == nOldErrors
2014-05-28 01:34:41 +02:00
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
func fieldPath ( prefix , fieldName string ) string {
if prefix == "" {
return fieldName
}
return prefix + "." + fieldName
}
2014-05-28 01:34:41 +02:00
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
func ( ctx * unpackContext ) addError ( e error ) bool {
ctx . errs = append ( ctx . errs , e )
return len ( ctx . errs ) < maxUnpackErrors
}
func ( ctx * unpackContext ) unpackToStruct ( namePrefix string , structValue reflect . Value ) {
2014-05-28 01:34:41 +02:00
structType := structValue . Type ( )
for i := 0 ; i < structValue . NumField ( ) ; i ++ {
fieldValue := structValue . Field ( i )
field := structType . Field ( i )
2016-09-07 02:20:57 +02:00
// In Go 1.7, runtime-created structs are unexported, so it's not
// possible to create an exported anonymous field with a generated
// type. So workaround this by special-casing "BlueprintEmbed" to
// behave like an anonymous field for structure unpacking.
if field . Name == "BlueprintEmbed" {
field . Name = ""
field . Anonymous = true
}
2014-06-23 02:02:55 +02:00
if field . PkgPath != "" {
// This is an unexported field, so just skip it.
continue
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
propertyName := fieldPath ( namePrefix , PropertyNameForField ( field . Name ) )
2015-11-21 02:03:25 +01:00
2014-05-28 01:34:41 +02:00
if ! fieldValue . CanSet ( ) {
2015-11-21 02:03:25 +01:00
panic ( fmt . Errorf ( "field %s is not settable" , propertyName ) )
2014-05-28 01:34:41 +02:00
}
2016-08-09 02:24:03 +02:00
// Get the property value if it was specified.
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
packedProperty , propertyIsSet := ctx . propertyMap [ propertyName ]
2016-08-09 02:24:03 +02:00
2016-08-06 02:19:36 +02:00
origFieldValue := fieldValue
2014-05-28 01:34:41 +02:00
// To make testing easier we validate the struct field's type regardless
// of whether or not the property was specified in the parsed string.
2016-08-09 02:24:03 +02:00
// TODO(ccross): we don't validate types inside nil struct pointers
// Move type validation to a function that runs on each factory once
2014-05-28 01:34:41 +02:00
switch kind := fieldValue . Kind ( ) ; kind {
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
case reflect . Bool , reflect . String , reflect . Struct , reflect . Slice :
2014-05-28 01:34:41 +02:00
// Do nothing
2014-09-30 20:38:25 +02:00
case reflect . Interface :
if fieldValue . IsNil ( ) {
2015-11-21 02:03:25 +01:00
panic ( fmt . Errorf ( "field %s contains a nil interface" , propertyName ) )
2014-09-30 20:38:25 +02:00
}
fieldValue = fieldValue . Elem ( )
elemType := fieldValue . Type ( )
if elemType . Kind ( ) != reflect . Ptr {
2015-11-21 02:03:25 +01:00
panic ( fmt . Errorf ( "field %s contains a non-pointer interface" , propertyName ) )
2014-09-30 20:38:25 +02:00
}
fallthrough
case reflect . Ptr :
2015-10-30 23:53:55 +01:00
switch ptrKind := fieldValue . Type ( ) . Elem ( ) . Kind ( ) ; ptrKind {
case reflect . Struct :
2016-08-09 02:24:03 +02:00
if fieldValue . IsNil ( ) && ( propertyIsSet || field . Anonymous ) {
// Instantiate nil struct pointers
// Set into origFieldValue in case it was an interface, in which case
// fieldValue points to the unsettable pointer inside the interface
2016-08-06 02:19:36 +02:00
fieldValue = reflect . New ( fieldValue . Type ( ) . Elem ( ) )
origFieldValue . Set ( fieldValue )
2015-10-30 23:53:55 +01:00
}
fieldValue = fieldValue . Elem ( )
Support parsing int64 in Blueprint file.
Support int64 number instead of int to be more fixed to bit size so
that the underlying arch won't affect overflow cases. Besides,
refection: func (v Value) Int() int64 always cast to int64 no matter the
input is int, int16, int32. Currently we always treat "-" as negative
sign to bind to next value, and "+" as plus operator to add operands
together.
So we allow:
a = 5 + -4 + 5 or a = -4 + 5
But we don't allow:
a = +5 + 4 + -4 since we don't treat "+" as a positive sign, otherwise,
a = 5 + +5 would exist which looks pretty weird. In the future, we may
want fully support number calculator logic eg, "+"/"-" can be
positive/negative sign or operator, and "(" and ")" will be considered
to group expressions with a higher precedence.
int & uint properties within struct keeps unchanged, which is only
allowed when tagged with 'blueprint:mutated'. We only allow *int64
property instead of int64 property within struct since it does't make
sense to do prepending or appending to int64.
Change-Id: I565e046dbd268af3538aee148cd7300037e56523
2017-11-01 22:03:28 +01:00
case reflect . Bool , reflect . Int64 , reflect . String :
2015-10-30 23:53:55 +01:00
// Nothing
default :
2015-11-21 02:03:25 +01:00
panic ( fmt . Errorf ( "field %s contains a pointer to %s" , propertyName , ptrKind ) )
2014-05-28 01:34:41 +02:00
}
2015-06-22 22:38:45 +02:00
2014-12-18 01:46:09 +01:00
case reflect . Int , reflect . Uint :
2020-01-02 18:37:49 +01:00
if ! HasTag ( field , "blueprint" , "mutated" ) {
2015-11-21 02:03:25 +01:00
panic ( fmt . Errorf ( ` int field %s must be tagged blueprint:"mutated" ` , propertyName ) )
2014-12-18 01:46:09 +01:00
}
2014-05-28 01:34:41 +02:00
default :
2015-11-21 02:03:25 +01:00
panic ( fmt . Errorf ( "unsupported kind for field %s: %s" , propertyName , kind ) )
}
2020-01-28 01:48:30 +01:00
if field . Anonymous && isStruct ( fieldValue . Type ( ) ) {
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
ctx . unpackToStruct ( namePrefix , fieldValue )
2015-11-21 02:03:25 +01:00
continue
2014-05-28 01:34:41 +02:00
}
2016-08-09 02:24:03 +02:00
if ! propertyIsSet {
2014-05-28 01:34:41 +02:00
// This property wasn't specified.
continue
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
packedProperty . used = true
property := packedProperty . property
2014-12-18 01:46:09 +01:00
2020-01-02 18:37:49 +01:00
if HasTag ( field , "blueprint" , "mutated" ) {
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
if ! ctx . addError (
2020-01-02 18:37:49 +01:00
& UnpackError {
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
fmt . Errorf ( "mutated field %s cannot be set in a Blueprint file" , propertyName ) ,
property . ColonPos ,
} ) {
return
2014-12-18 01:46:09 +01:00
}
continue
}
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
if isConfigurable ( fieldValue . Type ( ) ) {
// configurableType is the reflect.Type representation of a Configurable[whatever],
// while configuredType is the reflect.Type of the "whatever".
configurableType := fieldValue . Type ( )
configuredType := fieldValue . Interface ( ) . ( configurableReflection ) . configuredType ( )
if unpackedValue , ok := ctx . unpackToConfigurable ( propertyName , property , configurableType , configuredType ) ; ok {
2024-04-04 02:01:21 +02:00
ExtendBasicType ( fieldValue , unpackedValue . Elem ( ) , Append )
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
}
if len ( ctx . errs ) >= maxUnpackErrors {
return
}
} else if isStruct ( fieldValue . Type ( ) ) {
2021-01-16 05:09:51 +01:00
if property . Value . Eval ( ) . Type ( ) != parser . MapType {
ctx . addError ( & UnpackError {
fmt . Errorf ( "can't assign %s value to map property %q" ,
property . Value . Type ( ) , property . Name ) ,
property . Value . Pos ( ) ,
} )
continue
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
ctx . unpackToStruct ( propertyName , fieldValue )
if len ( ctx . errs ) >= maxUnpackErrors {
return
}
} else if isSlice ( fieldValue . Type ( ) ) {
if unpackedValue , ok := ctx . unpackToSlice ( propertyName , property , fieldValue . Type ( ) ) ; ok {
ExtendBasicType ( fieldValue , unpackedValue , Append )
}
if len ( ctx . errs ) >= maxUnpackErrors {
return
2017-07-29 02:51:37 +02:00
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
} else {
unpackedValue , err := propertyToValue ( fieldValue . Type ( ) , property )
if err != nil && ! ctx . addError ( err ) {
return
}
ExtendBasicType ( fieldValue , unpackedValue , Append )
2014-05-28 01:34:41 +02:00
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
}
}
2017-07-29 02:51:37 +02:00
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
// Converts the given property to a pointer to a configurable struct
func ( ctx * unpackContext ) unpackToConfigurable ( propertyName string , property * parser . Property , configurableType , configuredType reflect . Type ) ( reflect . Value , bool ) {
switch v := property . Value . ( type ) {
case * parser . String :
if configuredType . Kind ( ) != reflect . String {
ctx . addError ( & UnpackError {
fmt . Errorf ( "can't assign string value to configurable %s property %q" ,
configuredType . String ( ) , property . Name ) ,
property . Value . Pos ( ) ,
} )
return reflect . New ( configurableType ) , false
}
result := Configurable [ string ] {
propertyName : property . Name ,
2024-04-27 01:39:54 +02:00
inner : & configurableInner [ string ] {
single : singleConfigurable [ string ] {
cases : [ ] ConfigurableCase [ string ] { {
value : & v . Value ,
} } ,
} ,
} ,
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
}
return reflect . ValueOf ( & result ) , true
case * parser . Bool :
if configuredType . Kind ( ) != reflect . Bool {
ctx . addError ( & UnpackError {
fmt . Errorf ( "can't assign bool value to configurable %s property %q" ,
configuredType . String ( ) , property . Name ) ,
property . Value . Pos ( ) ,
} )
return reflect . New ( configurableType ) , false
}
result := Configurable [ bool ] {
propertyName : property . Name ,
2024-04-27 01:39:54 +02:00
inner : & configurableInner [ bool ] {
single : singleConfigurable [ bool ] {
cases : [ ] ConfigurableCase [ bool ] { {
value : & v . Value ,
} } ,
} ,
} ,
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
}
return reflect . ValueOf ( & result ) , true
case * parser . List :
if configuredType . Kind ( ) != reflect . Slice {
ctx . addError ( & UnpackError {
fmt . Errorf ( "can't assign list value to configurable %s property %q" ,
configuredType . String ( ) , property . Name ) ,
property . Value . Pos ( ) ,
} )
return reflect . New ( configurableType ) , false
}
switch configuredType . Elem ( ) . Kind ( ) {
case reflect . String :
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
var value [ ] string
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
if v . Values != nil {
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
value = make ( [ ] string , len ( v . Values ) )
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
itemProperty := & parser . Property { NamePos : property . NamePos , ColonPos : property . ColonPos }
for i , expr := range v . Values {
itemProperty . Name = propertyName + "[" + strconv . Itoa ( i ) + "]"
itemProperty . Value = expr
exprUnpacked , err := propertyToValue ( configuredType . Elem ( ) , itemProperty )
if err != nil {
ctx . addError ( err )
return reflect . ValueOf ( Configurable [ [ ] string ] { } ) , false
}
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
value [ i ] = exprUnpacked . Interface ( ) . ( string )
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
}
}
result := Configurable [ [ ] string ] {
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
propertyName : property . Name ,
2024-04-27 01:39:54 +02:00
inner : & configurableInner [ [ ] string ] {
single : singleConfigurable [ [ ] string ] {
cases : [ ] ConfigurableCase [ [ ] string ] { {
value : & value ,
} } ,
} ,
} ,
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
}
return reflect . ValueOf ( & result ) , true
default :
panic ( "This should be unreachable because ConfigurableElements only accepts slices of strings" )
}
case * parser . Operator :
property . Value = v . Value . Eval ( )
return ctx . unpackToConfigurable ( propertyName , property , configurableType , configuredType )
2024-04-12 23:43:10 +02:00
case * parser . Variable :
property . Value = v . Value . Eval ( )
return ctx . unpackToConfigurable ( propertyName , property , configurableType , configuredType )
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
case * parser . Select :
resultPtr := reflect . New ( configurableType )
result := resultPtr . Elem ( )
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
conditions := make ( [ ] ConfigurableCondition , len ( v . Conditions ) )
for i , cond := range v . Conditions {
args := make ( [ ] string , len ( cond . Args ) )
for j , arg := range cond . Args {
args [ j ] = arg . Value
}
conditions [ i ] = ConfigurableCondition {
2024-04-27 01:39:54 +02:00
functionName : cond . FunctionName ,
args : args ,
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
}
}
configurableCaseType := configurableCaseType ( configuredType )
cases := reflect . MakeSlice ( reflect . SliceOf ( configurableCaseType ) , 0 , len ( v . Cases ) )
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
for i , c := range v . Cases {
Add support for unset select branches
Currently, with the arch/os mutator, you can override a property
using the default value for just a few arch types, for example:
cc_defaults {
name: "my_defaults",
target: {
windows: {
enabled: true,
}
}
}
cc_binary {
name: "foo",
enabled: false,
defaults: ["my_defaults"],
}
You could make a select statment that acts like the above if it were
all in one module, but currently with select statements you can't make
a defaults module that can be generically applied to any other module
and have the same behavior as the above.
After this cl, the defaults module could look like:
cc_defaults {
name: "my_defaults",
enabled: select(variant("arch"), {
"windows": true,
_: unset,
}),
}
Which would have the same behavior. Unset may also be useful for
setting the property under some configurations, but wanting to leave
the implementation-specific default value in others.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: I3ea3277ea8b9a0ac5e613b4378945388b9df036a
2024-03-29 00:20:12 +01:00
p := & parser . Property {
Name : property . Name + "[" + strconv . Itoa ( i ) + "]" ,
NamePos : c . ColonPos ,
Value : c . Value ,
}
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
2024-04-23 22:50:40 +02:00
patterns := make ( [ ] ConfigurablePattern , len ( c . Patterns ) )
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
for i , pat := range c . Patterns {
switch pat := pat . ( type ) {
case * parser . String :
if pat . Value == "__soong_conditions_default__" {
patterns [ i ] . typ = configurablePatternTypeDefault
} else {
patterns [ i ] . typ = configurablePatternTypeString
patterns [ i ] . stringValue = pat . Value
}
case * parser . Bool :
patterns [ i ] . typ = configurablePatternTypeBool
patterns [ i ] . boolValue = pat . Value
default :
panic ( "unimplemented" )
}
}
var value reflect . Value
Add support for unset select branches
Currently, with the arch/os mutator, you can override a property
using the default value for just a few arch types, for example:
cc_defaults {
name: "my_defaults",
target: {
windows: {
enabled: true,
}
}
}
cc_binary {
name: "foo",
enabled: false,
defaults: ["my_defaults"],
}
You could make a select statment that acts like the above if it were
all in one module, but currently with select statements you can't make
a defaults module that can be generically applied to any other module
and have the same behavior as the above.
After this cl, the defaults module could look like:
cc_defaults {
name: "my_defaults",
enabled: select(variant("arch"), {
"windows": true,
_: unset,
}),
}
Which would have the same behavior. Unset may also be useful for
setting the property under some configurations, but wanting to leave
the implementation-specific default value in others.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: I3ea3277ea8b9a0ac5e613b4378945388b9df036a
2024-03-29 00:20:12 +01:00
// Map the "unset" keyword to a nil pointer in the cases map
if _ , ok := c . Value . ( parser . UnsetProperty ) ; ok {
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
value = reflect . Zero ( reflect . PointerTo ( configuredType ) )
} else {
var err error
switch configuredType . Kind ( ) {
case reflect . String , reflect . Bool :
value , err = propertyToValue ( reflect . PointerTo ( configuredType ) , p )
if err != nil {
ctx . addError ( & UnpackError {
err ,
c . Value . Pos ( ) ,
} )
return reflect . New ( configurableType ) , false
}
case reflect . Slice :
if configuredType . Elem ( ) . Kind ( ) != reflect . String {
panic ( "This should be unreachable because ConfigurableElements only accepts slices of strings" )
}
value , ok = ctx . unpackToSlice ( p . Name , p , reflect . PointerTo ( configuredType ) )
if ! ok {
return reflect . New ( configurableType ) , false
}
default :
panic ( "This should be unreachable because ConfigurableElements only accepts strings, boools, or slices of strings" )
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
}
}
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
case_ := reflect . New ( configurableCaseType )
case_ . Interface ( ) . ( configurableCaseReflection ) . initialize ( patterns , value . Interface ( ) )
cases = reflect . Append ( cases , case_ . Elem ( ) )
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
}
resultPtr . Interface ( ) . ( configurablePtrReflection ) . initialize (
property . Name ,
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
conditions ,
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
cases . Interface ( ) ,
)
if v . Append != nil {
p := & parser . Property {
Name : property . Name ,
NamePos : property . NamePos ,
Value : v . Append ,
}
val , ok := ctx . unpackToConfigurable ( propertyName , p , configurableType , configuredType )
if ! ok {
return reflect . New ( configurableType ) , false
}
2024-04-27 01:39:54 +02:00
result . Interface ( ) . ( configurableReflection ) . setAppend ( val . Elem ( ) . Interface ( ) , false , false )
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
}
return resultPtr , true
default :
ctx . addError ( & UnpackError {
fmt . Errorf ( "can't assign %s value to configurable %s property %q" ,
property . Value . Type ( ) , configuredType . String ( ) , property . Name ) ,
property . Value . Pos ( ) ,
} )
return reflect . New ( configurableType ) , false
}
}
2024-04-30 22:58:17 +02:00
// If the given property is a select, returns an error saying that you can't assign a select to
// a non-configurable property. Otherwise returns nil.
func selectOnNonConfigurablePropertyError ( property * parser . Property ) error {
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
if _ , ok := property . Value . Eval ( ) . ( * parser . Select ) ; ! ok {
2024-04-30 22:58:17 +02:00
return nil
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
}
2024-04-30 22:58:17 +02:00
return & UnpackError {
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
fmt . Errorf ( "can't assign select statement to non-configurable property %q. This requires a small soong change to enable in most cases, please file a go/soong-bug if you'd like to use a select statement here" ,
property . Name ) ,
property . Value . Pos ( ) ,
2024-04-30 22:58:17 +02:00
}
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
}
Add support for unset select branches
Currently, with the arch/os mutator, you can override a property
using the default value for just a few arch types, for example:
cc_defaults {
name: "my_defaults",
target: {
windows: {
enabled: true,
}
}
}
cc_binary {
name: "foo",
enabled: false,
defaults: ["my_defaults"],
}
You could make a select statment that acts like the above if it were
all in one module, but currently with select statements you can't make
a defaults module that can be generically applied to any other module
and have the same behavior as the above.
After this cl, the defaults module could look like:
cc_defaults {
name: "my_defaults",
enabled: select(variant("arch"), {
"windows": true,
_: unset,
}),
}
Which would have the same behavior. Unset may also be useful for
setting the property under some configurations, but wanting to leave
the implementation-specific default value in others.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: I3ea3277ea8b9a0ac5e613b4378945388b9df036a
2024-03-29 00:20:12 +01:00
// unpackSlice creates a value of a given slice or pointer to slice type from the property,
// which should be a list
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
func ( ctx * unpackContext ) unpackToSlice (
Add support for unset select branches
Currently, with the arch/os mutator, you can override a property
using the default value for just a few arch types, for example:
cc_defaults {
name: "my_defaults",
target: {
windows: {
enabled: true,
}
}
}
cc_binary {
name: "foo",
enabled: false,
defaults: ["my_defaults"],
}
You could make a select statment that acts like the above if it were
all in one module, but currently with select statements you can't make
a defaults module that can be generically applied to any other module
and have the same behavior as the above.
After this cl, the defaults module could look like:
cc_defaults {
name: "my_defaults",
enabled: select(variant("arch"), {
"windows": true,
_: unset,
}),
}
Which would have the same behavior. Unset may also be useful for
setting the property under some configurations, but wanting to leave
the implementation-specific default value in others.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: I3ea3277ea8b9a0ac5e613b4378945388b9df036a
2024-03-29 00:20:12 +01:00
sliceName string , property * parser . Property , sliceType reflect . Type ) ( reflect . Value , bool ) {
if sliceType . Kind ( ) == reflect . Pointer {
sliceType = sliceType . Elem ( )
result := reflect . New ( sliceType )
slice , ok := ctx . unpackToSliceInner ( sliceName , property , sliceType )
if ! ok {
return result , ok
}
result . Elem ( ) . Set ( slice )
return result , true
}
return ctx . unpackToSliceInner ( sliceName , property , sliceType )
}
// unpackToSliceInner creates a value of a given slice type from the property,
// which should be a list. It doesn't support pointers to slice types like unpackToSlice
// does.
func ( ctx * unpackContext ) unpackToSliceInner (
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
sliceName string , property * parser . Property , sliceType reflect . Type ) ( reflect . Value , bool ) {
propValueAsList , ok := property . Value . Eval ( ) . ( * parser . List )
if ! ok {
2024-04-30 22:58:17 +02:00
if err := selectOnNonConfigurablePropertyError ( property ) ; err != nil {
ctx . addError ( err )
} else {
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
ctx . addError ( & UnpackError {
fmt . Errorf ( "can't assign %s value to list property %q" ,
property . Value . Type ( ) , property . Name ) ,
property . Value . Pos ( ) ,
} )
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
return reflect . MakeSlice ( sliceType , 0 , 0 ) , false
}
exprs := propValueAsList . Values
value := reflect . MakeSlice ( sliceType , 0 , len ( exprs ) )
if len ( exprs ) == 0 {
return value , true
}
2017-07-29 02:51:37 +02:00
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
// The function to construct an item value depends on the type of list elements.
2022-08-02 16:51:08 +02:00
var getItemFunc func ( * parser . Property , reflect . Type ) ( reflect . Value , bool )
switch exprs [ 0 ] . Type ( ) {
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
case parser . BoolType , parser . StringType , parser . Int64Type :
2022-08-02 16:51:08 +02:00
getItemFunc = func ( property * parser . Property , t reflect . Type ) ( reflect . Value , bool ) {
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
value , err := propertyToValue ( t , property )
if err != nil {
ctx . addError ( err )
return value , false
2017-07-29 02:51:37 +02:00
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
return value , true
2014-05-28 01:34:41 +02:00
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
case parser . ListType :
2022-08-02 16:51:08 +02:00
getItemFunc = func ( property * parser . Property , t reflect . Type ) ( reflect . Value , bool ) {
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
return ctx . unpackToSlice ( property . Name , property , t )
}
case parser . MapType :
2022-08-02 16:51:08 +02:00
getItemFunc = func ( property * parser . Property , t reflect . Type ) ( reflect . Value , bool ) {
itemValue := reflect . New ( t ) . Elem ( )
ctx . unpackToStruct ( property . Name , itemValue )
return itemValue , true
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
}
case parser . NotEvaluatedType :
2022-08-02 16:51:08 +02:00
getItemFunc = func ( property * parser . Property , t reflect . Type ) ( reflect . Value , bool ) {
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
return reflect . New ( t ) , false
}
default :
2022-08-02 16:51:08 +02:00
panic ( fmt . Errorf ( "bizarre property expression type: %v" , exprs [ 0 ] . Type ( ) ) )
2014-05-28 01:34:41 +02:00
}
2022-08-02 16:51:08 +02:00
itemProperty := & parser . Property { NamePos : property . NamePos , ColonPos : property . ColonPos }
elemType := sliceType . Elem ( )
isPtr := elemType . Kind ( ) == reflect . Ptr
for i , expr := range exprs {
itemProperty . Name = sliceName + "[" + strconv . Itoa ( i ) + "]"
itemProperty . Value = expr
if packedProperty , ok := ctx . propertyMap [ itemProperty . Name ] ; ok {
packedProperty . used = true
}
if isPtr {
if itemValue , ok := getItemFunc ( itemProperty , elemType . Elem ( ) ) ; ok {
ptrValue := reflect . New ( itemValue . Type ( ) )
ptrValue . Elem ( ) . Set ( itemValue )
value = reflect . Append ( value , ptrValue )
}
} else {
if itemValue , ok := getItemFunc ( itemProperty , elemType ) ; ok {
value = reflect . Append ( value , itemValue )
}
}
}
return value , true
2014-05-28 01:34:41 +02:00
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
// propertyToValue creates a value of a given value type from the property.
2017-07-29 02:51:37 +02:00
func propertyToValue ( typ reflect . Type , property * parser . Property ) ( reflect . Value , error ) {
var value reflect . Value
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
var baseType reflect . Type
isPtr := typ . Kind ( ) == reflect . Ptr
if isPtr {
baseType = typ . Elem ( )
} else {
baseType = typ
2014-05-28 01:34:41 +02:00
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
switch kind := baseType . Kind ( ) ; kind {
2017-07-29 02:51:37 +02:00
case reflect . Bool :
b , ok := property . Value . Eval ( ) . ( * parser . Bool )
if ! ok {
2024-04-30 22:58:17 +02:00
if err := selectOnNonConfigurablePropertyError ( property ) ; err != nil {
return value , err
} else {
return value , & UnpackError {
fmt . Errorf ( "can't assign %s value to bool property %q" ,
property . Value . Type ( ) , property . Name ) ,
property . Value . Pos ( ) ,
}
2021-01-16 07:04:38 +01:00
}
2017-07-29 02:51:37 +02:00
}
value = reflect . ValueOf ( b . Value )
2014-05-28 01:34:41 +02:00
Support parsing int64 in Blueprint file.
Support int64 number instead of int to be more fixed to bit size so
that the underlying arch won't affect overflow cases. Besides,
refection: func (v Value) Int() int64 always cast to int64 no matter the
input is int, int16, int32. Currently we always treat "-" as negative
sign to bind to next value, and "+" as plus operator to add operands
together.
So we allow:
a = 5 + -4 + 5 or a = -4 + 5
But we don't allow:
a = +5 + 4 + -4 since we don't treat "+" as a positive sign, otherwise,
a = 5 + +5 would exist which looks pretty weird. In the future, we may
want fully support number calculator logic eg, "+"/"-" can be
positive/negative sign or operator, and "(" and ")" will be considered
to group expressions with a higher precedence.
int & uint properties within struct keeps unchanged, which is only
allowed when tagged with 'blueprint:mutated'. We only allow *int64
property instead of int64 property within struct since it does't make
sense to do prepending or appending to int64.
Change-Id: I565e046dbd268af3538aee148cd7300037e56523
2017-11-01 22:03:28 +01:00
case reflect . Int64 :
b , ok := property . Value . Eval ( ) . ( * parser . Int64 )
if ! ok {
2021-01-16 07:04:38 +01:00
return value , & UnpackError {
fmt . Errorf ( "can't assign %s value to int64 property %q" ,
property . Value . Type ( ) , property . Name ) ,
property . Value . Pos ( ) ,
}
Support parsing int64 in Blueprint file.
Support int64 number instead of int to be more fixed to bit size so
that the underlying arch won't affect overflow cases. Besides,
refection: func (v Value) Int() int64 always cast to int64 no matter the
input is int, int16, int32. Currently we always treat "-" as negative
sign to bind to next value, and "+" as plus operator to add operands
together.
So we allow:
a = 5 + -4 + 5 or a = -4 + 5
But we don't allow:
a = +5 + 4 + -4 since we don't treat "+" as a positive sign, otherwise,
a = 5 + +5 would exist which looks pretty weird. In the future, we may
want fully support number calculator logic eg, "+"/"-" can be
positive/negative sign or operator, and "(" and ")" will be considered
to group expressions with a higher precedence.
int & uint properties within struct keeps unchanged, which is only
allowed when tagged with 'blueprint:mutated'. We only allow *int64
property instead of int64 property within struct since it does't make
sense to do prepending or appending to int64.
Change-Id: I565e046dbd268af3538aee148cd7300037e56523
2017-11-01 22:03:28 +01:00
}
value = reflect . ValueOf ( b . Value )
2017-07-29 02:51:37 +02:00
case reflect . String :
s , ok := property . Value . Eval ( ) . ( * parser . String )
if ! ok {
2024-04-30 22:58:17 +02:00
if err := selectOnNonConfigurablePropertyError ( property ) ; err != nil {
return value , err
} else {
return value , & UnpackError {
fmt . Errorf ( "can't assign %s value to string property %q" ,
property . Value . Type ( ) , property . Name ) ,
property . Value . Pos ( ) ,
}
2021-01-16 07:04:38 +01:00
}
2014-05-28 01:34:41 +02:00
}
2017-07-29 02:51:37 +02:00
value = reflect . ValueOf ( s . Value )
2014-05-28 01:34:41 +02:00
2017-07-29 02:51:37 +02:00
default :
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
return value , & UnpackError {
fmt . Errorf ( "cannot assign %s value %s to %s property %s" , property . Value . Type ( ) , property . Value , kind , typ ) ,
property . NamePos }
2014-05-28 01:34:41 +02:00
}
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
if isPtr {
2017-07-29 02:51:37 +02:00
ptrValue := reflect . New ( value . Type ( ) )
ptrValue . Elem ( ) . Set ( value )
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
return ptrValue , nil
2014-05-28 01:34:41 +02:00
}
2017-07-29 02:51:37 +02:00
return value , nil
2014-05-28 01:34:41 +02:00
}