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
// Copyright 2023 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 proptools
import (
2024-04-23 22:50:40 +02:00
"fmt"
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
"reflect"
"slices"
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
"strconv"
"strings"
2024-05-21 23:20:33 +02:00
"github.com/google/blueprint/optional"
2024-06-13 01:26:54 +02:00
"github.com/google/blueprint/parser"
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-05-21 23:20:33 +02:00
// ConfigurableOptional is the same as ShallowOptional, but we use this separate
// name to reserve the ability to switch to an alternative implementation later.
type ConfigurableOptional [ T any ] struct {
shallowOptional optional . ShallowOptional [ T ]
}
// IsPresent returns true if the optional contains a value
func ( o * ConfigurableOptional [ T ] ) IsPresent ( ) bool {
return o . shallowOptional . IsPresent ( )
}
// IsEmpty returns true if the optional does not have a value
func ( o * ConfigurableOptional [ T ] ) IsEmpty ( ) bool {
return o . shallowOptional . IsEmpty ( )
}
// Get() returns the value inside the optional. It panics if IsEmpty() returns true
func ( o * ConfigurableOptional [ T ] ) Get ( ) T {
return o . shallowOptional . Get ( )
}
// GetOrDefault() returns the value inside the optional if IsPresent() returns true,
// or the provided value otherwise.
func ( o * ConfigurableOptional [ T ] ) GetOrDefault ( other T ) T {
return o . shallowOptional . GetOrDefault ( other )
}
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
type ConfigurableElements interface {
string | bool | [ ] string
}
type ConfigurableEvaluator interface {
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
EvaluateConfiguration ( condition ConfigurableCondition , property string ) ConfigurableValue
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
PropertyErrorf ( property , fmt string , args ... interface { } )
}
// configurableMarker is just so that reflection can check type of the first field of
// the struct to determine if it is a configurable struct.
type configurableMarker bool
var configurableMarkerType reflect . Type = reflect . TypeOf ( ( * configurableMarker ) ( nil ) ) . Elem ( )
2024-04-27 01:39:54 +02:00
// ConfigurableCondition represents a condition that is being selected on, like
// arch(), os(), soong_config_variable("namespace", "variable"), or other variables.
// It's represented generically as a function name + arguments in blueprint, soong
// interprets the function name and args into specific variable values.
//
// ConfigurableCondition is treated as an immutable object so that it may be shared
// between different configurable properties.
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
type ConfigurableCondition struct {
2024-04-27 01:39:54 +02:00
functionName string
args [ ] string
}
func NewConfigurableCondition ( functionName string , args [ ] string ) ConfigurableCondition {
return ConfigurableCondition {
functionName : functionName ,
args : slices . Clone ( args ) ,
}
}
func ( c ConfigurableCondition ) FunctionName ( ) string {
return c . functionName
}
func ( c ConfigurableCondition ) NumArgs ( ) int {
return len ( c . args )
}
func ( c ConfigurableCondition ) Arg ( i int ) string {
return c . args [ i ]
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
}
func ( c * ConfigurableCondition ) String ( ) string {
var sb strings . Builder
2024-04-27 01:39:54 +02:00
sb . WriteString ( c . functionName )
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
sb . WriteRune ( '(' )
2024-04-27 01:39:54 +02:00
for i , arg := range c . 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
sb . WriteString ( strconv . Quote ( arg ) )
2024-04-27 01:39:54 +02:00
if i < len ( c . args ) - 1 {
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
sb . WriteString ( ", " )
}
}
sb . WriteRune ( ')' )
return sb . String ( )
}
type configurableValueType int
const (
configurableValueTypeString configurableValueType = iota
configurableValueTypeBool
configurableValueTypeUndefined
)
func ( v * configurableValueType ) patternType ( ) configurablePatternType {
switch * v {
case configurableValueTypeString :
return configurablePatternTypeString
case configurableValueTypeBool :
return configurablePatternTypeBool
default :
panic ( "unimplemented" )
}
}
func ( v * configurableValueType ) String ( ) string {
switch * v {
case configurableValueTypeString :
return "string"
case configurableValueTypeBool :
return "bool"
case configurableValueTypeUndefined :
return "undefined"
default :
panic ( "unimplemented" )
}
}
// ConfigurableValue represents the value of a certain condition being selected on.
// This type mostly exists to act as a sum type between string, bool, and undefined.
type ConfigurableValue struct {
typ configurableValueType
stringValue string
boolValue bool
}
func ( c * ConfigurableValue ) String ( ) string {
switch c . typ {
case configurableValueTypeString :
return strconv . Quote ( c . stringValue )
case configurableValueTypeBool :
if c . boolValue {
return "true"
} else {
return "false"
}
case configurableValueTypeUndefined :
return "undefined"
default :
panic ( "unimplemented" )
}
}
func ConfigurableValueString ( s string ) ConfigurableValue {
return ConfigurableValue {
typ : configurableValueTypeString ,
stringValue : s ,
}
}
func ConfigurableValueBool ( b bool ) ConfigurableValue {
return ConfigurableValue {
typ : configurableValueTypeBool ,
boolValue : b ,
}
}
func ConfigurableValueUndefined ( ) ConfigurableValue {
return ConfigurableValue {
typ : configurableValueTypeUndefined ,
}
}
type configurablePatternType int
const (
configurablePatternTypeString configurablePatternType = iota
configurablePatternTypeBool
configurablePatternTypeDefault
)
func ( v * configurablePatternType ) String ( ) string {
switch * v {
case configurablePatternTypeString :
return "string"
case configurablePatternTypeBool :
return "bool"
case configurablePatternTypeDefault :
return "default"
default :
panic ( "unimplemented" )
}
}
2024-04-27 01:39:54 +02:00
// ConfigurablePattern represents a concrete value for a ConfigurableCase.
// Currently this just means the value of whatever variable is being looked
// up with the ConfigurableCase, but in the future it may be expanded to
// match multiple values (e.g. ranges of integers like 3..7).
//
// ConfigurablePattern can represent different types of values, like
// strings vs bools.
//
// ConfigurablePattern must be immutable so it can be shared between
// different configurable properties.
2024-04-23 22:50:40 +02:00
type ConfigurablePattern struct {
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
typ configurablePatternType
stringValue string
boolValue bool
}
2024-04-23 22:50:40 +02:00
func NewStringConfigurablePattern ( s string ) ConfigurablePattern {
return ConfigurablePattern {
typ : configurablePatternTypeString ,
stringValue : s ,
}
}
func NewBoolConfigurablePattern ( b bool ) ConfigurablePattern {
return ConfigurablePattern {
typ : configurablePatternTypeBool ,
boolValue : b ,
}
}
func NewDefaultConfigurablePattern ( ) ConfigurablePattern {
return ConfigurablePattern {
typ : configurablePatternTypeDefault ,
}
}
func ( p * ConfigurablePattern ) matchesValue ( v ConfigurableValue ) bool {
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
if p . typ == configurablePatternTypeDefault {
return true
}
if v . typ == configurableValueTypeUndefined {
return false
}
if p . typ != v . typ . patternType ( ) {
return false
}
switch p . typ {
case configurablePatternTypeString :
return p . stringValue == v . stringValue
case configurablePatternTypeBool :
return p . boolValue == v . boolValue
default :
panic ( "unimplemented" )
}
}
2024-04-23 22:50:40 +02:00
func ( p * ConfigurablePattern ) matchesValueType ( v ConfigurableValue ) bool {
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
if p . typ == configurablePatternTypeDefault {
return true
}
if v . typ == configurableValueTypeUndefined {
return true
}
return p . typ == v . typ . patternType ( )
}
2024-04-27 01:39:54 +02:00
// ConfigurableCase represents a set of ConfigurablePatterns
// (exactly 1 pattern per ConfigurableCase), and a value to use
// if all of the patterns are matched.
//
// ConfigurableCase must be immutable so it can be shared between
// different configurable properties.
2024-04-23 22:50:40 +02:00
type ConfigurableCase [ T ConfigurableElements ] struct {
patterns [ ] ConfigurablePattern
2024-06-13 01:26:54 +02:00
value parser . Expression
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
}
type configurableCaseReflection interface {
2024-06-13 01:26:54 +02:00
initialize ( patterns [ ] ConfigurablePattern , value parser . Expression )
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
var _ configurableCaseReflection = & ConfigurableCase [ 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
2024-04-23 22:50:40 +02:00
func NewConfigurableCase [ T ConfigurableElements ] ( patterns [ ] ConfigurablePattern , value * T ) ConfigurableCase [ T ] {
2024-06-13 01:26:54 +02:00
var valueExpr parser . Expression
if value == nil {
valueExpr = & parser . UnsetProperty { }
} else {
switch v := any ( value ) . ( type ) {
case * string :
valueExpr = & parser . String { Value : * v }
case * bool :
valueExpr = & parser . Bool { Value : * v }
case * [ ] string :
innerValues := make ( [ ] parser . Expression , 0 , len ( * v ) )
for _ , x := range * v {
innerValues = append ( innerValues , & parser . String { Value : x } )
}
valueExpr = & parser . List { Values : innerValues }
default :
panic ( fmt . Sprintf ( "should be unreachable due to the ConfigurableElements restriction: %#v" , value ) )
}
}
2024-04-27 01:39:54 +02:00
// Clone the values so they can't be modified from soong
patterns = slices . Clone ( patterns )
2024-04-23 22:50:40 +02:00
return ConfigurableCase [ T ] {
patterns : patterns ,
2024-06-13 01:26:54 +02:00
value : valueExpr ,
2024-04-23 22:50:40 +02:00
}
}
2024-06-13 01:26:54 +02:00
func ( c * ConfigurableCase [ T ] ) initialize ( patterns [ ] ConfigurablePattern , value parser . Expression ) {
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
c . patterns = patterns
2024-06-13 01:26:54 +02:00
c . value = 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
}
// for the given T, return the reflect.type of configurableCase[T]
func configurableCaseType ( configuredType reflect . Type ) reflect . Type {
// I don't think it's possible to do this generically with go's
// current reflection apis unfortunately
switch configuredType . Kind ( ) {
case reflect . String :
2024-04-23 22:50:40 +02:00
return reflect . TypeOf ( ConfigurableCase [ 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
case reflect . Bool :
2024-04-23 22:50:40 +02:00
return reflect . TypeOf ( ConfigurableCase [ bool ] { } )
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 . Slice :
switch configuredType . Elem ( ) . Kind ( ) {
case reflect . String :
2024-04-23 22:50:40 +02:00
return reflect . TypeOf ( ConfigurableCase [ [ ] 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
}
}
panic ( "unimplemented" )
}
2024-04-17 19:24:51 +02:00
// for the given T, return the reflect.type of Configurable[T]
func configurableType ( configuredType reflect . Type ) ( reflect . Type , error ) {
// I don't think it's possible to do this generically with go's
// current reflection apis unfortunately
switch configuredType . Kind ( ) {
case reflect . String :
return reflect . TypeOf ( Configurable [ string ] { } ) , nil
case reflect . Bool :
return reflect . TypeOf ( Configurable [ bool ] { } ) , nil
case reflect . Slice :
switch configuredType . Elem ( ) . Kind ( ) {
case reflect . String :
return reflect . TypeOf ( Configurable [ [ ] string ] { } ) , nil
}
}
return nil , fmt . Errorf ( "configurable structs can only contain strings, bools, or string slices, found %s" , configuredType . 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
// Configurable can wrap the type of a blueprint property,
// in order to allow select statements to be used in bp files
// for that property. For example, for the property struct:
//
// my_props {
// Property_a: string,
// Property_b: Configurable[string],
// }
//
// property_b can then use select statements:
//
// my_module {
// property_a: "foo"
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
// property_b: select(soong_config_variable("my_namespace", "my_variable"), {
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
// "value_1": "bar",
// "value_2": "baz",
// default: "qux",
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
// })
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
// }
//
// The configurable property holds all the branches of the select
// statement in the bp file. To extract the final value, you must
// call Evaluate() on the configurable property.
//
// All configurable properties support being unset, so there is
// no need to use a pointer type like Configurable[*string].
type Configurable [ T ConfigurableElements ] struct {
2024-04-27 01:39:54 +02:00
marker configurableMarker
propertyName string
inner * configurableInner [ T ]
}
type configurableInner [ T ConfigurableElements ] struct {
single singleConfigurable [ T ]
replace bool
next * configurableInner [ T ]
}
// singleConfigurable must be immutable so it can be reused
// between multiple configurables
type singleConfigurable [ T ConfigurableElements ] struct {
conditions [ ] ConfigurableCondition
cases [ ] ConfigurableCase [ T ]
2024-06-13 01:26:54 +02:00
scope * parser . Scope
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
}
// Ignore the warning about the unused marker variable, it's used via reflection
var _ configurableMarker = Configurable [ string ] { } . marker
2024-04-23 22:50:40 +02:00
func NewConfigurable [ T ConfigurableElements ] ( conditions [ ] ConfigurableCondition , cases [ ] ConfigurableCase [ T ] ) Configurable [ T ] {
for _ , c := range cases {
if len ( c . patterns ) != len ( conditions ) {
panic ( fmt . Sprintf ( "All configurables cases must have as many patterns as the configurable has conditions. Expected: %d, found: %d" , len ( conditions ) , len ( c . patterns ) ) )
}
}
2024-04-27 01:39:54 +02:00
// Clone the slices so they can't be modified from soong
conditions = slices . Clone ( conditions )
cases = slices . Clone ( cases )
2024-04-23 22:50:40 +02:00
return Configurable [ T ] {
2024-04-27 01:39:54 +02:00
inner : & configurableInner [ T ] {
single : singleConfigurable [ T ] {
conditions : conditions ,
cases : cases ,
} ,
} ,
2024-04-23 22:50:40 +02:00
}
}
2024-06-13 23:50:50 +02:00
func ( c * Configurable [ T ] ) AppendSimpleValue ( value T ) {
value = copyConfiguredValue ( value )
// This may be a property that was never initialized from a bp file
if c . inner == nil {
c . inner = & configurableInner [ T ] {
single : singleConfigurable [ T ] {
cases : [ ] ConfigurableCase [ T ] { {
2024-06-13 01:26:54 +02:00
value : configuredValueToExpression ( value ) ,
2024-06-13 23:50:50 +02:00
} } ,
} ,
}
return
}
c . inner . appendSimpleValue ( value )
}
2024-04-04 19:53:07 +02:00
// Get returns the final value for the configurable property.
// A configurable property may be unset, in which case Get will return nil.
2024-05-21 23:20:33 +02:00
func ( c * Configurable [ T ] ) Get ( evaluator ConfigurableEvaluator ) ConfigurableOptional [ T ] {
2024-04-27 01:39:54 +02:00
result := c . inner . evaluate ( c . propertyName , evaluator )
2024-05-21 23:20:33 +02:00
return configuredValuePtrToOptional ( result )
2024-04-27 01:39:54 +02:00
}
// GetOrDefault is the same as Get, but will return the provided default value if the property was unset.
func ( c * Configurable [ T ] ) GetOrDefault ( evaluator ConfigurableEvaluator , defaultValue T ) T {
result := c . inner . evaluate ( c . propertyName , evaluator )
if result != nil {
// Copy the result so that it can't be changed from soong
2024-06-13 23:50:50 +02:00
return copyConfiguredValue ( * result )
2024-04-27 01:39:54 +02:00
}
return defaultValue
}
func ( c * configurableInner [ T ] ) evaluate ( propertyName string , evaluator ConfigurableEvaluator ) * T {
2024-04-26 00:38:56 +02:00
if c == 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
return nil
}
2024-04-27 01:39:54 +02:00
if c . next == nil {
return c . single . evaluateNonTransitive ( propertyName , evaluator )
2024-04-26 00:38:56 +02:00
}
2024-04-27 01:39:54 +02:00
if c . replace {
2024-04-04 02:01:21 +02:00
return replaceConfiguredValues (
2024-04-27 01:39:54 +02:00
c . single . evaluateNonTransitive ( propertyName , evaluator ) ,
c . next . evaluate ( propertyName , evaluator ) ,
2024-04-04 02:01:21 +02:00
)
} else {
return appendConfiguredValues (
2024-04-27 01:39:54 +02:00
c . single . evaluateNonTransitive ( propertyName , evaluator ) ,
c . next . evaluate ( propertyName , evaluator ) ,
2024-04-04 02:01:21 +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
}
2024-04-27 01:39:54 +02:00
func ( c * singleConfigurable [ T ] ) evaluateNonTransitive ( propertyName string , evaluator ConfigurableEvaluator ) * T {
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 , case_ := range c . cases {
if len ( c . conditions ) != len ( case_ . patterns ) {
2024-04-27 01:39:54 +02:00
evaluator . PropertyErrorf ( propertyName , "Expected each case to have as many patterns as conditions. conditions: %d, len(cases[%d].patterns): %d" , len ( c . conditions ) , i , len ( case_ . patterns ) )
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 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
if len ( c . conditions ) == 0 {
if len ( c . cases ) == 0 {
return nil
} else if len ( c . cases ) == 1 {
2024-06-13 01:26:54 +02:00
if result , err := expressionToConfiguredValue [ T ] ( c . cases [ 0 ] . value , c . scope ) ; err != nil {
evaluator . PropertyErrorf ( propertyName , "%s" , err . Error ( ) )
return nil
} else {
return result
}
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
} else {
2024-04-27 01:39:54 +02:00
evaluator . PropertyErrorf ( propertyName , "Expected 0 or 1 branches in an unconfigured select, found %d" , len ( c . cases ) )
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
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
}
}
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
values := make ( [ ] ConfigurableValue , len ( c . conditions ) )
for i , condition := range c . conditions {
2024-04-27 01:39:54 +02:00
values [ i ] = evaluator . EvaluateConfiguration ( condition , propertyName )
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
foundMatch := false
2024-05-10 00:50:50 +02:00
nonMatchingIndex := 0
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 result * T
for _ , case_ := range c . cases {
allMatch := true
for i , pat := range case_ . patterns {
if ! pat . matchesValueType ( values [ i ] ) {
2024-04-27 01:39:54 +02:00
evaluator . PropertyErrorf ( propertyName , "Expected all branches of a select on condition %s to have type %s, found %s" , c . conditions [ i ] . String ( ) , values [ i ] . typ . String ( ) , pat . typ . 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
return nil
}
if ! pat . matchesValue ( values [ i ] ) {
allMatch = false
2024-05-10 00:50:50 +02:00
nonMatchingIndex = i
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
break
}
}
if allMatch && ! foundMatch {
2024-06-13 01:26:54 +02:00
if r , err := expressionToConfiguredValue [ T ] ( case_ . value , c . scope ) ; err != nil {
evaluator . PropertyErrorf ( propertyName , "%s" , err . Error ( ) )
return nil
} else {
result = r
}
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
foundMatch = true
}
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
if foundMatch {
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
return result
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-05-10 00:50:50 +02:00
evaluator . PropertyErrorf ( propertyName , "%s had value %s, which was not handled by the select statement" , c . conditions [ nonMatchingIndex ] . String ( ) , values [ nonMatchingIndex ] . 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
return nil
}
2024-04-04 02:01:21 +02:00
func appendConfiguredValues [ T ConfigurableElements ] ( a , b * T ) * T {
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 a == nil && b == nil {
return nil
}
switch any ( a ) . ( type ) {
case * [ ] string :
var a2 [ ] string
var b2 [ ] string
if a != nil {
a2 = * any ( a ) . ( * [ ] string )
}
if b != nil {
b2 = * any ( b ) . ( * [ ] string )
}
result := make ( [ ] string , len ( a2 ) + len ( b2 ) )
idx := 0
for i := 0 ; i < len ( a2 ) ; i ++ {
result [ idx ] = a2 [ i ]
idx += 1
}
for i := 0 ; i < len ( b2 ) ; i ++ {
result [ idx ] = b2 [ i ]
idx += 1
}
return any ( & result ) . ( * T )
case * string :
a := String ( any ( a ) . ( * string ) )
b := String ( any ( b ) . ( * string ) )
result := a + b
return any ( & result ) . ( * T )
case * bool :
2024-03-28 00:39:07 +01:00
// Addition of bools will OR them together. This is inherited behavior
// from how proptools.ExtendBasicType works with non-configurable bools.
result := 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
if a != nil {
2024-03-28 00:39:07 +01:00
result = result || * any ( a ) . ( * bool )
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 b != nil {
2024-03-28 00:39:07 +01:00
result = result || * any ( b ) . ( * bool )
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-03-28 00:39:07 +01:00
return any ( & result ) . ( * T )
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
default :
panic ( "Should be unreachable" )
}
}
2024-04-04 02:01:21 +02:00
func replaceConfiguredValues [ T ConfigurableElements ] ( a , b * T ) * T {
if b != nil {
return b
}
return a
}
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
// configurableReflection is an interface that exposes some methods that are
// helpful when working with reflect.Values of Configurable objects, used by
// the property unpacking code. You can't call unexported methods from reflection,
// (at least without unsafe pointer trickery) so this is the next best thing.
type configurableReflection interface {
2024-04-27 01:39:54 +02:00
setAppend ( append any , replace bool , prepend bool )
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
configuredType ( ) reflect . Type
2024-04-27 01:39:54 +02:00
clone ( ) any
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
isEmpty ( ) bool
2024-05-22 23:28:49 +02:00
printfInto ( value string ) 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
}
// Same as configurableReflection, but since initialize needs to take a pointer
// to a Configurable, it was broken out into a separate interface.
type configurablePtrReflection interface {
2024-06-13 01:26:54 +02:00
initialize ( scope * parser . Scope , propertyName string , conditions [ ] ConfigurableCondition , cases any )
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
}
var _ configurableReflection = Configurable [ string ] { }
var _ configurablePtrReflection = & Configurable [ string ] { }
2024-06-13 01:26:54 +02:00
func ( c * Configurable [ T ] ) initialize ( scope * parser . Scope , propertyName string , conditions [ ] ConfigurableCondition , cases any ) {
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
c . propertyName = propertyName
2024-04-27 01:39:54 +02:00
c . inner = & configurableInner [ T ] {
single : singleConfigurable [ T ] {
conditions : conditions ,
cases : cases . ( [ ] ConfigurableCase [ T ] ) ,
2024-06-13 01:26:54 +02:00
scope : scope ,
2024-04-27 01:39:54 +02:00
} ,
}
}
func ( c Configurable [ T ] ) setAppend ( append any , replace bool , prepend bool ) {
a := append . ( Configurable [ T ] )
if a . inner . isEmpty ( ) {
return
}
c . inner . setAppend ( a . inner , replace , prepend )
if c . inner == c . inner . next {
panic ( "pointer loop" )
}
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-27 01:39:54 +02:00
func ( c * configurableInner [ T ] ) setAppend ( append * configurableInner [ T ] , replace bool , prepend bool ) {
if c . isEmpty ( ) {
* c = * append . clone ( )
} else if prepend {
if replace && c . alwaysHasValue ( ) {
// The current value would always override the prepended value, so don't do anything
return
}
// We're going to replace the head node with the one from append, so allocate
// a new one here.
old := & configurableInner [ T ] {
single : c . single ,
replace : c . replace ,
next : c . next ,
}
* c = * append . clone ( )
curr := c
for curr . next != nil {
curr = curr . next
}
curr . next = old
curr . replace = replace
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
} else {
2024-04-26 00:38:56 +02:00
// If we're replacing with something that always has a value set,
// we can optimize the code by replacing our entire append chain here.
2024-04-27 01:39:54 +02:00
if replace && append . alwaysHasValue ( ) {
* c = * append . clone ( )
2024-04-26 00:38:56 +02:00
} else {
2024-04-27 01:39:54 +02:00
curr := c
for curr . next != nil {
curr = curr . next
}
curr . next = append . clone ( )
curr . replace = replace
2024-04-26 00:38:56 +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
}
}
2024-06-13 23:50:50 +02:00
func ( c * configurableInner [ T ] ) appendSimpleValue ( value T ) {
if c . next == nil {
c . replace = false
c . next = & configurableInner [ T ] {
single : singleConfigurable [ T ] {
cases : [ ] ConfigurableCase [ T ] { {
2024-06-13 01:26:54 +02:00
value : configuredValueToExpression ( value ) ,
2024-06-13 23:50:50 +02:00
} } ,
} ,
}
} else {
c . next . appendSimpleValue ( value )
}
}
2024-05-22 23:28:49 +02:00
func ( c Configurable [ T ] ) printfInto ( value string ) error {
return c . inner . printfInto ( value )
}
func ( c * configurableInner [ T ] ) printfInto ( value string ) error {
for c != nil {
if err := c . single . printfInto ( value ) ; err != nil {
return err
}
c = c . next
}
return nil
}
func ( c * singleConfigurable [ T ] ) printfInto ( value string ) error {
for _ , c := range c . cases {
if c . value == nil {
continue
}
2024-06-13 01:26:54 +02:00
if err := c . value . PrintfInto ( value ) ; err != nil {
return err
2024-05-22 23:28:49 +02:00
}
}
return nil
}
2024-04-27 01:39:54 +02:00
func ( c Configurable [ T ] ) clone ( ) any {
return Configurable [ T ] {
propertyName : c . propertyName ,
inner : c . inner . clone ( ) ,
}
}
func ( c * configurableInner [ T ] ) clone ( ) * configurableInner [ T ] {
if c == nil {
return nil
}
return & configurableInner [ T ] {
// We don't need to clone the singleConfigurable because
// it's supposed to be immutable
single : c . single ,
replace : c . replace ,
next : c . next . clone ( ) ,
}
}
func ( c * configurableInner [ T ] ) isEmpty ( ) bool {
if c == nil {
return true
}
if ! c . single . isEmpty ( ) {
return false
}
return c . next . isEmpty ( )
}
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
func ( c Configurable [ T ] ) isEmpty ( ) bool {
2024-04-27 01:39:54 +02:00
return c . inner . isEmpty ( )
}
func ( c * singleConfigurable [ T ] ) isEmpty ( ) bool {
if c == nil {
return true
}
2024-04-26 00:38:56 +02:00
if len ( c . cases ) > 1 {
return false
}
if len ( c . cases ) == 1 && c . cases [ 0 ] . value != nil {
2024-06-13 01:26:54 +02:00
if _ , ok := c . cases [ 0 ] . value . ( * parser . UnsetProperty ) ; ok {
return true
}
2024-04-26 00:38:56 +02:00
return false
}
return true
}
2024-04-27 01:39:54 +02:00
func ( c * configurableInner [ T ] ) alwaysHasValue ( ) bool {
for curr := c ; curr != nil ; curr = curr . next {
if curr . single . alwaysHasValue ( ) {
return true
}
}
return false
}
func ( c * singleConfigurable [ T ] ) alwaysHasValue ( ) bool {
2024-04-26 00:38:56 +02:00
if len ( c . cases ) == 0 {
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 false
}
2024-04-26 00:38:56 +02:00
for _ , c := range c . cases {
2024-06-13 01:26:54 +02:00
if _ , isUnset := c . value . ( * parser . UnsetProperty ) ; isUnset || c . value == nil {
2024-04-26 00:38:56 +02:00
return false
}
}
return true
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
}
func ( c Configurable [ T ] ) configuredType ( ) reflect . Type {
return reflect . TypeOf ( ( * T ) ( nil ) ) . Elem ( )
}
2024-06-13 01:26:54 +02:00
func expressionToConfiguredValue [ T ConfigurableElements ] ( expr parser . Expression , scope * parser . Scope ) ( * T , error ) {
expr , err := expr . Eval ( scope )
if err != nil {
return nil , err
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
}
2024-06-13 01:26:54 +02:00
switch e := expr . ( type ) {
case * parser . UnsetProperty :
return nil , nil
case * parser . String :
if result , ok := any ( & e . Value ) . ( * T ) ; ok {
return result , nil
} else {
return nil , fmt . Errorf ( "can't assign string value to %s property" , configuredTypeToString [ T ] ( ) )
}
case * parser . Bool :
if result , ok := any ( & e . Value ) . ( * T ) ; ok {
return result , nil
} else {
return nil , fmt . Errorf ( "can't assign bool value to %s property" , configuredTypeToString [ T ] ( ) )
}
case * parser . List :
result := make ( [ ] string , 0 , len ( e . Values ) )
for _ , x := range e . Values {
if y , ok := x . ( * parser . String ) ; ok {
result = append ( result , y . Value )
} else {
return nil , fmt . Errorf ( "expected list of strings but found list of %s" , x . Type ( ) )
}
}
if result , ok := any ( & result ) . ( * T ) ; ok {
return result , nil
} else {
return nil , fmt . Errorf ( "can't assign list of strings to list of %s property" , configuredTypeToString [ T ] ( ) )
}
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
default :
2024-06-13 01:26:54 +02:00
// If the expression was not evaluated beforehand we could hit this error even when the types match,
// but that's an internal logic error.
return nil , fmt . Errorf ( "expected %s but found %s (%#v)" , configuredTypeToString [ T ] ( ) , expr . Type ( ) . String ( ) , expr )
2024-04-27 01:39:54 +02:00
}
}
2024-06-13 01:26:54 +02:00
func configuredValueToExpression [ T ConfigurableElements ] ( value T ) parser . Expression {
switch v := any ( value ) . ( type ) {
case string :
return & parser . String { Value : v }
case bool :
return & parser . Bool { Value : v }
case [ ] string :
values := make ( [ ] parser . Expression , 0 , len ( v ) )
for _ , x := range v {
values = append ( values , & parser . String { Value : x } )
}
return & parser . List { Values : values }
default :
panic ( "unhandled type in configuredValueToExpression" )
2024-05-21 23:20:33 +02:00
}
2024-06-13 01:26:54 +02:00
}
func configuredTypeToString [ T ConfigurableElements ] ( ) string {
var zero T
switch any ( zero ) . ( type ) {
case string :
return "string"
case bool :
return "bool"
2024-05-21 23:20:33 +02:00
case [ ] string :
2024-06-13 01:26:54 +02:00
return "list of strings"
2024-05-21 23:20:33 +02:00
default :
2024-06-13 01:26:54 +02:00
panic ( "should be unreachable" )
2024-05-21 23:20:33 +02:00
}
}
2024-06-13 23:50:50 +02:00
func copyConfiguredValue [ T ConfigurableElements ] ( t T ) T {
switch t2 := any ( t ) . ( type ) {
2024-04-27 01:39:54 +02:00
case [ ] string :
return any ( slices . Clone ( t2 ) ) . ( T )
default :
2024-06-13 23:50:50 +02:00
return t
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-05-22 23:28:49 +02:00
2024-06-13 01:26:54 +02:00
func configuredValuePtrToOptional [ T ConfigurableElements ] ( t * T ) ConfigurableOptional [ T ] {
if t == nil {
return ConfigurableOptional [ T ] { optional . NewShallowOptional ( t ) }
}
switch t2 := any ( * t ) . ( type ) {
case [ ] string :
result := any ( slices . Clone ( t2 ) ) . ( T )
return ConfigurableOptional [ T ] { optional . NewShallowOptional ( & result ) }
default :
return ConfigurableOptional [ T ] { optional . NewShallowOptional ( t ) }
}
}
2024-05-22 23:28:49 +02:00
// PrintfIntoConfigurable replaces %s occurrences in strings in Configurable properties
// with the provided string value. It's intention is to support soong config value variables
// on Configurable properties.
func PrintfIntoConfigurable ( c any , value string ) error {
return c . ( configurableReflection ) . printfInto ( value )
}
2024-06-13 01:26:54 +02:00
func promoteValueToConfigurable ( origional reflect . Value ) reflect . Value {
var expr parser . Expression
var kind reflect . Kind
if origional . Kind ( ) == reflect . Pointer && origional . IsNil ( ) {
expr = & parser . UnsetProperty { }
kind = origional . Type ( ) . Elem ( ) . Kind ( )
} else {
if origional . Kind ( ) == reflect . Pointer {
origional = origional . Elem ( )
}
kind = origional . Kind ( )
switch kind {
case reflect . String :
expr = & parser . String { Value : origional . String ( ) }
case reflect . Bool :
expr = & parser . Bool { Value : origional . Bool ( ) }
case reflect . Slice :
strList := origional . Interface ( ) . ( [ ] string )
exprList := make ( [ ] parser . Expression , 0 , len ( strList ) )
for _ , x := range strList {
exprList = append ( exprList , & parser . String { Value : x } )
}
expr = & parser . List { Values : exprList }
default :
panic ( "can only convert string/bool/[]string to configurable" )
}
}
switch kind {
case reflect . String :
return reflect . ValueOf ( Configurable [ string ] {
inner : & configurableInner [ string ] {
single : singleConfigurable [ string ] {
cases : [ ] ConfigurableCase [ string ] { {
value : expr ,
} } ,
} ,
} ,
} )
case reflect . Bool :
return reflect . ValueOf ( Configurable [ bool ] {
inner : & configurableInner [ bool ] {
single : singleConfigurable [ bool ] {
cases : [ ] ConfigurableCase [ bool ] { {
value : expr ,
} } ,
} ,
} ,
} )
case reflect . Slice :
return reflect . ValueOf ( Configurable [ [ ] string ] {
inner : & configurableInner [ [ ] string ] {
single : singleConfigurable [ [ ] string ] {
cases : [ ] ConfigurableCase [ [ ] string ] { {
value : expr ,
} } ,
} ,
} ,
} )
default :
panic ( fmt . Sprintf ( "Can't convert %s property to a configurable" , origional . Kind ( ) . String ( ) ) )
}
}