Add PostProcessors to configurable properties

Some module types currently evaluate configurable properties in load
hooks, modify the results, and pass them onto properties of other
modules. Evaluating configurable properties in load hooks is
problematic, it happens so early that we can't decide the configuration
beforehand.

Add a "post processors" mechanism to configurable properties where
the result of evaluating the property will be passed through a post
processing function before being returned from Get(). This essentially
allows you to modify the property without evaluating it.

Bug: 362579941
Test: m nothing --no-skip-soong-tests
Change-Id: Ibddb3f14b3433364ba474b964c701e8915d4dc85
This commit is contained in:
Cole Faust 2024-08-27 14:54:04 -07:00 committed by Bartłomiej Rudecki
parent 738bb54ded
commit c472e38ec1
Signed by: przekichane
GPG key ID: 751F23C6F014EF76
6 changed files with 791 additions and 548 deletions

View file

@ -133,6 +133,7 @@ bootstrap_go_package {
],
testSrcs: [
"proptools/clone_test.go",
"proptools/configurable_test.go",
"proptools/escape_test.go",
"proptools/extend_test.go",
"proptools/filter_test.go",

View file

@ -412,6 +412,22 @@ type Configurable[T ConfigurableElements] struct {
marker configurableMarker
propertyName string
inner *configurableInner[T]
// See Configurable.evaluate for a description of the postProcessor algorithm and
// why this is a 2d list
postProcessors *[][]postProcessor[T]
}
type postProcessor[T ConfigurableElements] struct {
f func(T) T
// start and end represent the range of configurableInners
// that this postprocessor is applied to. When appending two configurables
// together, the start and end values will stay the same for the left
// configurable's postprocessors, but the rights will be rebased by the
// number of configurableInners in the left configurable. This way
// the postProcessors still only apply to the configurableInners they
// origionally applied to before the appending.
start int
end int
}
type configurableInner[T ConfigurableElements] struct {
@ -440,6 +456,7 @@ func NewConfigurable[T ConfigurableElements](conditions []ConfigurableCondition,
// Clone the slices so they can't be modified from soong
conditions = slices.Clone(conditions)
cases = slices.Clone(cases)
var zeroPostProcessors [][]postProcessor[T]
return Configurable[T]{
inner: &configurableInner[T]{
single: singleConfigurable[T]{
@ -447,35 +464,77 @@ func NewConfigurable[T ConfigurableElements](conditions []ConfigurableCondition,
cases: cases,
},
},
postProcessors: &zeroPostProcessors,
}
}
func newConfigurableWithPropertyName[T ConfigurableElements](propertyName string, conditions []ConfigurableCondition, cases []ConfigurableCase[T], addScope bool) Configurable[T] {
result := NewConfigurable(conditions, cases)
result.propertyName = propertyName
if addScope {
for curr := result.inner; curr != nil; curr = curr.next {
curr.single.scope = parser.NewScope(nil)
}
}
return result
}
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]{{
value: configuredValueToExpression(value),
}},
},
}
c.initialize(nil, "", nil, []ConfigurableCase[T]{{
value: configuredValueToExpression(value),
}})
return
}
c.inner.appendSimpleValue(value)
}
// AddPostProcessor adds a function that will modify the result of
// Get() when Get() is called. It operates on all the current contents
// of the Configurable property, but if other values are appended to
// the Configurable property afterwards, the postProcessor will not run
// on them. This can be useful to essentially modify a configurable
// property without evaluating it.
func (c *Configurable[T]) AddPostProcessor(p func(T) T) {
// Add the new postProcessor on top of the tallest stack of postProcessors.
// See Configurable.evaluate for more details on the postProcessors algorithm
// and data structure.
num_links := c.inner.numLinks()
if c.postProcessors == nil || len(*c.postProcessors) == 0 {
c.postProcessors = &[][]postProcessor[T]{{{
f: p,
start: 0,
end: num_links,
}}}
} else {
deepestI := 0
deepestDepth := 0
for i := 0; i < len(*c.postProcessors); i++ {
if len((*c.postProcessors)[i]) > deepestDepth {
deepestDepth = len((*c.postProcessors)[i])
deepestI = i
}
}
(*c.postProcessors)[deepestI] = append((*c.postProcessors)[deepestI], postProcessor[T]{
f: p,
start: 0,
end: num_links,
})
}
}
// Get returns the final value for the configurable property.
// A configurable property may be unset, in which case Get will return nil.
func (c *Configurable[T]) Get(evaluator ConfigurableEvaluator) ConfigurableOptional[T] {
result := c.inner.evaluate(c.propertyName, evaluator)
result := c.evaluate(c.propertyName, evaluator)
return configuredValuePtrToOptional(result)
}
// 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)
result := c.evaluate(c.propertyName, evaluator)
if result != nil {
// Copy the result so that it can't be changed from soong
return copyConfiguredValue(*result)
@ -483,6 +542,127 @@ func (c *Configurable[T]) GetOrDefault(evaluator ConfigurableEvaluator, defaultV
return defaultValue
}
type valueAndIndices[T ConfigurableElements] struct {
value *T
replace bool
// Similar to start/end in postProcessor, these represent the origional
// range or configurableInners that this merged group represents. It's needed
// in order to apply recursive postProcessors to only the relevant
// configurableInners, even after those configurableInners have been merged
// in order to apply an earlier postProcessor.
start int
end int
}
func (c *Configurable[T]) evaluate(propertyName string, evaluator ConfigurableEvaluator) *T {
if c.inner == nil {
return nil
}
if len(*c.postProcessors) == 0 {
// Use a simpler algorithm if there are no postprocessors
return c.inner.evaluate(propertyName, evaluator)
}
// The basic idea around evaluating with postprocessors is that each individual
// node in the chain (each configurableInner) is first evaluated, and then when
// a postprocessor operates on a certain range, that range is merged before passing
// it to the postprocessor. We want postProcessors to only accept a final merged
// value instead of a linked list, but at the same time, only operate over a portion
// of the list. If more configurables are appended onto this one, their values won't
// be operated on by the existing postProcessors, but they may have their own
// postprocessors.
//
// _____________________
// | __________|
// ______ | _____| ___
// | | | | | |
// a -> b -> c -> d -> e -> f -> g
//
// In this diagram, the letters along the bottom is the chain of configurableInners.
// The brackets on top represent postprocessors, where higher brackets are processed
// after lower ones.
//
// To evaluate this example, first we evaluate the raw values for all nodes a->g.
// Then we merge nodes a/b and d/e and apply the postprocessors to their merged values,
// and also to g. Those merged and postprocessed nodes are then reinserted into the
// list, and we move on to doing the higher level postprocessors (starting with the c->e one)
// in the same way. When all postprocessors are done, a final merge is done on anything
// leftover.
//
// The Configurable.postProcessors field is a 2d array to represent this hierarchy.
// The outer index moves right on this graph, the inner index goes up.
// When adding a new postProcessor, it will always be the last postProcessor to run
// until another is added or another configurable is appended. So in AddPostProcessor(),
// we add it to the tallest existing stack.
var currentValues []valueAndIndices[T]
for curr, i := c.inner, 0; curr != nil; curr, i = curr.next, i+1 {
value := curr.single.evaluateNonTransitive(propertyName, evaluator)
currentValues = append(currentValues, valueAndIndices[T]{
value: value,
replace: curr.replace,
start: i,
end: i + 1,
})
}
if c.postProcessors == nil || len(*c.postProcessors) == 0 {
return mergeValues(currentValues).value
}
foundPostProcessor := true
for depth := 0; foundPostProcessor; depth++ {
foundPostProcessor = false
var newValues []valueAndIndices[T]
i := 0
for _, postProcessorGroup := range *c.postProcessors {
if len(postProcessorGroup) > depth {
foundPostProcessor = true
postProcessor := postProcessorGroup[depth]
startI := 0
endI := 0
for currentValues[startI].start < postProcessor.start {
startI++
}
for currentValues[endI].end < postProcessor.end {
endI++
}
endI++
newValues = append(newValues, currentValues[i:startI]...)
merged := mergeValues(currentValues[startI:endI])
if merged.value != nil {
processed := postProcessor.f(*merged.value)
merged.value = &processed
}
newValues = append(newValues, merged)
i = endI
}
}
newValues = append(newValues, currentValues[i:]...)
currentValues = newValues
}
return mergeValues(currentValues).value
}
func mergeValues[T ConfigurableElements](values []valueAndIndices[T]) valueAndIndices[T] {
if len(values) < 0 {
panic("Expected at least 1 value in mergeValues")
}
result := values[0]
for i := 1; i < len(values); i++ {
if result.replace {
result.value = replaceConfiguredValues(result.value, values[i].value)
} else {
result.value = appendConfiguredValues(result.value, values[i].value)
}
result.end = values[i].end
result.replace = values[i].replace
}
return result
}
func (c *configurableInner[T]) evaluate(propertyName string, evaluator ConfigurableEvaluator) *T {
if c == nil {
return nil
@ -668,6 +848,12 @@ func (c *Configurable[T]) initialize(scope *parser.Scope, propertyName string, c
scope: scope,
},
}
var postProcessors [][]postProcessor[T]
c.postProcessors = &postProcessors
}
func (c *Configurable[T]) Append(other Configurable[T]) {
c.setAppend(other, false, false)
}
func (c Configurable[T]) setAppend(append any, replace bool, prepend bool) {
@ -675,12 +861,37 @@ func (c Configurable[T]) setAppend(append any, replace bool, prepend bool) {
if a.inner.isEmpty() {
return
}
if prepend {
newBase := a.inner.numLinks()
*c.postProcessors = appendPostprocessors(*a.postProcessors, *c.postProcessors, newBase)
} else {
newBase := c.inner.numLinks()
*c.postProcessors = appendPostprocessors(*c.postProcessors, *a.postProcessors, newBase)
}
c.inner.setAppend(a.inner, replace, prepend)
if c.inner == c.inner.next {
panic("pointer loop")
}
}
func appendPostprocessors[T ConfigurableElements](a, b [][]postProcessor[T], newBase int) [][]postProcessor[T] {
var result [][]postProcessor[T]
for i := 0; i < len(a); i++ {
result = append(result, slices.Clone(a[i]))
}
for i := 0; i < len(b); i++ {
n := slices.Clone(b[i])
for j := 0; j < len(n); j++ {
n[j].start += newBase
n[j].end += newBase
}
result = append(result, n)
}
return result
}
func (c *configurableInner[T]) setAppend(append *configurableInner[T], replace bool, prepend bool) {
if c.isEmpty() {
*c = *append.clone()
@ -719,6 +930,14 @@ func (c *configurableInner[T]) setAppend(append *configurableInner[T], replace b
}
}
func (c *configurableInner[T]) numLinks() int {
result := 0
for curr := c; curr != nil; curr = curr.next {
result++
}
return result
}
func (c *configurableInner[T]) appendSimpleValue(value T) {
if c.next == nil {
c.replace = false
@ -761,10 +980,20 @@ func (c *singleConfigurable[T]) printfInto(value string) error {
}
func (c Configurable[T]) clone() any {
return Configurable[T]{
propertyName: c.propertyName,
inner: c.inner.clone(),
var newPostProcessors *[][]postProcessor[T]
if c.postProcessors != nil {
x := appendPostprocessors(*c.postProcessors, nil, 0)
newPostProcessors = &x
}
return Configurable[T]{
propertyName: c.propertyName,
inner: c.inner.clone(),
postProcessors: newPostProcessors,
}
}
func (c Configurable[T]) Clone() Configurable[T] {
return c.clone().(Configurable[T])
}
func (c *configurableInner[T]) clone() *configurableInner[T] {
@ -973,6 +1202,7 @@ func promoteValueToConfigurable(origional reflect.Value) reflect.Value {
}},
},
},
postProcessors: &[][]postProcessor[string]{},
})
case reflect.Bool:
return reflect.ValueOf(Configurable[bool]{
@ -983,6 +1213,7 @@ func promoteValueToConfigurable(origional reflect.Value) reflect.Value {
}},
},
},
postProcessors: &[][]postProcessor[bool]{},
})
case reflect.Slice:
return reflect.ValueOf(Configurable[[]string]{
@ -993,6 +1224,7 @@ func promoteValueToConfigurable(origional reflect.Value) reflect.Value {
}},
},
},
postProcessors: &[][]postProcessor[[]string]{},
})
default:
panic(fmt.Sprintf("Can't convert %s property to a configurable", origional.Kind().String()))

View file

@ -0,0 +1,77 @@
package proptools
import (
"fmt"
"reflect"
"testing"
)
func TestPostProcessor(t *testing.T) {
// Same as the ascii art example in Configurable.evaluate()
prop := NewConfigurable[[]string](nil, nil)
prop.AppendSimpleValue([]string{"a"})
prop.AppendSimpleValue([]string{"b"})
prop.AddPostProcessor(addToElements("1"))
prop2 := NewConfigurable[[]string](nil, nil)
prop2.AppendSimpleValue([]string{"c"})
prop3 := NewConfigurable[[]string](nil, nil)
prop3.AppendSimpleValue([]string{"d"})
prop3.AppendSimpleValue([]string{"e"})
prop3.AddPostProcessor(addToElements("2"))
prop4 := NewConfigurable[[]string](nil, nil)
prop4.AppendSimpleValue([]string{"f"})
prop5 := NewConfigurable[[]string](nil, nil)
prop5.AppendSimpleValue([]string{"g"})
prop5.AddPostProcessor(addToElements("3"))
prop2.Append(prop3)
prop2.AddPostProcessor(addToElements("z"))
prop.Append(prop2)
prop.AddPostProcessor(addToElements("y"))
prop.Append(prop4)
prop.Append(prop5)
expected := []string{"a1y", "b1y", "czy", "d2zy", "e2zy", "f", "g3"}
x := prop.Get(&configurableEvalutorForTesting{})
if !reflect.DeepEqual(x.Get(), expected) {
t.Fatalf("Expected %v, got %v", expected, x.Get())
}
}
func addToElements(s string) func([]string) []string {
return func(arr []string) []string {
for i := range arr {
arr[i] = arr[i] + s
}
return arr
}
}
type configurableEvalutorForTesting struct {
vars map[string]string
}
func (e *configurableEvalutorForTesting) EvaluateConfiguration(condition ConfigurableCondition, property string) ConfigurableValue {
if condition.functionName != "f" {
panic("Expected functionName to be f")
}
if len(condition.args) != 1 {
panic("Expected exactly 1 arg")
}
val, ok := e.vars[condition.args[0]]
if ok {
return ConfigurableValueString(val)
}
return ConfigurableValueUndefined()
}
func (e *configurableEvalutorForTesting) PropertyErrorf(property, fmtString string, args ...interface{}) {
panic(fmt.Sprintf(fmtString, args...))
}
var _ ConfigurableEvaluator = (*configurableEvalutorForTesting)(nil)

View file

@ -1259,196 +1259,166 @@ func appendPropertiesTestCases() []appendPropertyTestCase {
{
name: "Append configurable",
dst: &struct{ S Configurable[[]string] }{
S: Configurable[[]string]{
inner: &configurableInner[[]string]{
single: singleConfigurable[[]string]{
conditions: []ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"foo",
},
}},
cases: []ConfigurableCase[[]string]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser.List{Values: []parser.Expression{
&parser.String{Value: "1"},
&parser.String{Value: "2"},
}},
}},
},
S: NewConfigurable[[]string]([]ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"foo",
},
},
}},
[]ConfigurableCase[[]string]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser.List{Values: []parser.Expression{
&parser.String{Value: "1"},
&parser.String{Value: "2"},
}},
}},
),
},
src: &struct{ S Configurable[[]string] }{
S: Configurable[[]string]{
inner: &configurableInner[[]string]{
single: singleConfigurable[[]string]{
conditions: []ConfigurableCondition{{
functionName: "release_variable",
args: []string{
"bar",
},
}},
cases: []ConfigurableCase[[]string]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "b",
}},
value: &parser.List{Values: []parser.Expression{
&parser.String{Value: "3"},
&parser.String{Value: "4"},
}},
}},
},
S: NewConfigurable([]ConfigurableCondition{{
functionName: "release_variable",
args: []string{
"bar",
},
},
}},
[]ConfigurableCase[[]string]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "b",
}},
value: &parser.List{Values: []parser.Expression{
&parser.String{Value: "3"},
&parser.String{Value: "4"},
}},
}},
),
},
out: &struct{ S Configurable[[]string] }{
S: Configurable[[]string]{
inner: &configurableInner[[]string]{
single: singleConfigurable[[]string]{
conditions: []ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"foo",
},
}},
cases: []ConfigurableCase[[]string]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser.List{Values: []parser.Expression{
&parser.String{Value: "1"},
&parser.String{Value: "2"},
}},
}},
S: func() Configurable[[]string] {
result := NewConfigurable([]ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"foo",
},
next: &configurableInner[[]string]{
single: singleConfigurable[[]string]{
conditions: []ConfigurableCondition{{
functionName: "release_variable",
args: []string{
"bar",
},
}},
cases: []ConfigurableCase[[]string]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "b",
}},
value: &parser.List{Values: []parser.Expression{
&parser.String{Value: "3"},
&parser.String{Value: "4"},
}},
}},
},
}},
[]ConfigurableCase[[]string]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser.List{Values: []parser.Expression{
&parser.String{Value: "1"},
&parser.String{Value: "2"},
}},
}},
)
result.Append(NewConfigurable([]ConfigurableCondition{{
functionName: "release_variable",
args: []string{
"bar",
},
},
},
}},
[]ConfigurableCase[[]string]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "b",
}},
value: &parser.List{Values: []parser.Expression{
&parser.String{Value: "3"},
&parser.String{Value: "4"},
}},
}}))
return result
}(),
},
},
{
name: "Prepend configurable",
order: Prepend,
dst: &struct{ S Configurable[[]string] }{
S: Configurable[[]string]{
inner: &configurableInner[[]string]{
single: singleConfigurable[[]string]{
conditions: []ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"foo",
},
}},
cases: []ConfigurableCase[[]string]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser.List{Values: []parser.Expression{
&parser.String{Value: "1"},
&parser.String{Value: "2"},
}},
}},
},
S: NewConfigurable([]ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"foo",
},
},
}},
[]ConfigurableCase[[]string]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser.List{Values: []parser.Expression{
&parser.String{Value: "1"},
&parser.String{Value: "2"},
}},
}},
),
},
src: &struct{ S Configurable[[]string] }{
S: Configurable[[]string]{
inner: &configurableInner[[]string]{
single: singleConfigurable[[]string]{
conditions: []ConfigurableCondition{{
functionName: "release_variable",
args: []string{
"bar",
},
}},
cases: []ConfigurableCase[[]string]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "b",
}},
value: &parser.List{Values: []parser.Expression{
&parser.String{Value: "3"},
&parser.String{Value: "4"},
}},
}},
},
S: NewConfigurable([]ConfigurableCondition{{
functionName: "release_variable",
args: []string{
"bar",
},
},
}},
[]ConfigurableCase[[]string]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "b",
}},
value: &parser.List{Values: []parser.Expression{
&parser.String{Value: "3"},
&parser.String{Value: "4"},
}},
}},
),
},
out: &struct{ S Configurable[[]string] }{
S: Configurable[[]string]{
inner: &configurableInner[[]string]{
single: singleConfigurable[[]string]{
conditions: []ConfigurableCondition{{
functionName: "release_variable",
args: []string{
"bar",
},
}},
cases: []ConfigurableCase[[]string]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "b",
}},
value: &parser.List{Values: []parser.Expression{
&parser.String{Value: "3"},
&parser.String{Value: "4"},
}},
}},
},
next: &configurableInner[[]string]{
single: singleConfigurable[[]string]{
conditions: []ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"foo",
},
}},
cases: []ConfigurableCase[[]string]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser.List{Values: []parser.Expression{
&parser.String{Value: "1"},
&parser.String{Value: "2"},
}},
}},
S: func() Configurable[[]string] {
result := NewConfigurable(
[]ConfigurableCondition{{
functionName: "release_variable",
args: []string{
"bar",
},
},
},
},
}},
[]ConfigurableCase[[]string]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "b",
}},
value: &parser.List{Values: []parser.Expression{
&parser.String{Value: "3"},
&parser.String{Value: "4"},
}},
}},
)
result.Append(NewConfigurable(
[]ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"foo",
},
}},
[]ConfigurableCase[[]string]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser.List{Values: []parser.Expression{
&parser.String{Value: "1"},
&parser.String{Value: "2"},
}},
}}))
return result
}(),
},
},
}
@ -1918,31 +1888,24 @@ func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase {
order: Append,
dst: []interface{}{
&struct{ S Configurable[bool] }{
S: Configurable[bool]{
inner: &configurableInner[bool]{
single: singleConfigurable[bool]{
conditions: []ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"foo",
},
}},
cases: []ConfigurableCase[bool]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser.Bool{Value: true},
}, {
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeDefault,
}},
value: &parser.Bool{Value: false},
}},
},
S: NewConfigurable[bool]([]ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"foo",
},
},
}}, []ConfigurableCase[bool]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser.Bool{Value: true},
}, {
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeDefault,
}},
value: &parser.Bool{Value: false},
}}),
},
},
src: &struct{ S *bool }{
@ -1950,38 +1913,30 @@ func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase {
},
out: []interface{}{
&struct{ S Configurable[bool] }{
S: Configurable[bool]{
inner: &configurableInner[bool]{
single: singleConfigurable[bool]{
conditions: []ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"foo",
},
}},
cases: []ConfigurableCase[bool]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser.Bool{Value: true},
}, {
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeDefault,
}},
value: &parser.Bool{Value: false},
}},
S: func() Configurable[bool] {
result := NewConfigurable[bool]([]ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"foo",
},
next: &configurableInner[bool]{
single: singleConfigurable[bool]{
cases: []ConfigurableCase[bool]{{
value: &parser.Bool{Value: true},
}},
},
},
},
},
}},
[]ConfigurableCase[bool]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser.Bool{Value: true},
}, {
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeDefault,
}},
value: &parser.Bool{Value: false},
}},
)
result.AppendSimpleValue(true)
return result
}(),
},
},
},
@ -1990,31 +1945,26 @@ func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase {
order: Append,
dst: []interface{}{
&struct{ S Configurable[bool] }{
S: Configurable[bool]{
inner: &configurableInner[bool]{
single: singleConfigurable[bool]{
conditions: []ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"foo",
},
}},
cases: []ConfigurableCase[bool]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser.Bool{Value: true},
}, {
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeDefault,
}},
value: &parser.Bool{Value: false},
}},
},
S: NewConfigurable[bool]([]ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"foo",
},
},
}},
[]ConfigurableCase[bool]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser.Bool{Value: true},
}, {
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeDefault,
}},
value: &parser.Bool{Value: false},
}},
),
},
},
src: &struct{ S bool }{
@ -2022,38 +1972,31 @@ func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase {
},
out: []interface{}{
&struct{ S Configurable[bool] }{
S: Configurable[bool]{
inner: &configurableInner[bool]{
single: singleConfigurable[bool]{
conditions: []ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"foo",
},
}},
cases: []ConfigurableCase[bool]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser.Bool{Value: true},
}, {
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeDefault,
}},
value: &parser.Bool{Value: false},
}},
},
next: &configurableInner[bool]{
single: singleConfigurable[bool]{
cases: []ConfigurableCase[bool]{{
value: &parser.Bool{Value: true},
}},
S: func() Configurable[bool] {
result := NewConfigurable[bool](
[]ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"foo",
},
},
},
},
}},
[]ConfigurableCase[bool]{{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser.Bool{Value: true},
}, {
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeDefault,
}},
value: &parser.Bool{Value: false},
}},
)
result.AppendSimpleValue(true)
return result
}(),
},
},
},

View file

@ -354,6 +354,7 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
})
return reflect.New(configurableType), false
}
var postProcessors [][]postProcessor[string]
result := Configurable[string]{
propertyName: property.Name,
inner: &configurableInner[string]{
@ -363,6 +364,7 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
}},
},
},
postProcessors: &postProcessors,
}
return reflect.ValueOf(&result), true
case *parser.Bool:
@ -374,6 +376,7 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
})
return reflect.New(configurableType), false
}
var postProcessors [][]postProcessor[bool]
result := Configurable[bool]{
propertyName: property.Name,
inner: &configurableInner[bool]{
@ -383,6 +386,7 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
}},
},
},
postProcessors: &postProcessors,
}
return reflect.ValueOf(&result), true
case *parser.List:
@ -411,6 +415,7 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
value[i] = exprUnpacked.Interface().(string)
}
}
var postProcessors [][]postProcessor[[]string]
result := Configurable[[]string]{
propertyName: property.Name,
inner: &configurableInner[[]string]{
@ -420,6 +425,7 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
}},
},
},
postProcessors: &postProcessors,
}
return reflect.ValueOf(&result), true
default:

View file

@ -733,23 +733,21 @@ var validUnpackTestCases = []struct {
&struct {
Foo Configurable[string]
}{
Foo: Configurable[string]{
propertyName: "foo",
inner: &configurableInner[string]{
single: singleConfigurable[string]{
cases: []ConfigurableCase[string]{{
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 17,
Line: 3,
Column: 10,
},
Value: "bar",
},
}},
Foo: newConfigurableWithPropertyName(
"foo",
nil,
[]ConfigurableCase[string]{{
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 17,
Line: 3,
Column: 10,
},
Value: "bar",
},
},
},
}},
false,
),
},
},
},
@ -764,24 +762,22 @@ var validUnpackTestCases = []struct {
&struct {
Foo Configurable[bool]
}{
Foo: Configurable[bool]{
propertyName: "foo",
inner: &configurableInner[bool]{
single: singleConfigurable[bool]{
cases: []ConfigurableCase[bool]{{
value: &parser.Bool{
LiteralPos: scanner.Position{
Offset: 17,
Line: 3,
Column: 10,
},
Value: true,
Token: "true",
},
}},
Foo: newConfigurableWithPropertyName(
"foo",
nil,
[]ConfigurableCase[bool]{{
value: &parser.Bool{
LiteralPos: scanner.Position{
Offset: 17,
Line: 3,
Column: 10,
},
Value: true,
Token: "true",
},
},
},
}},
false,
),
},
},
},
@ -796,45 +792,43 @@ var validUnpackTestCases = []struct {
&struct {
Foo Configurable[[]string]
}{
Foo: Configurable[[]string]{
propertyName: "foo",
inner: &configurableInner[[]string]{
single: singleConfigurable[[]string]{
cases: []ConfigurableCase[[]string]{{
value: &parser.List{
LBracePos: scanner.Position{
Offset: 17,
Foo: newConfigurableWithPropertyName(
"foo",
nil,
[]ConfigurableCase[[]string]{{
value: &parser.List{
LBracePos: scanner.Position{
Offset: 17,
Line: 3,
Column: 10,
},
RBracePos: scanner.Position{
Offset: 26,
Line: 3,
Column: 19,
},
Values: []parser.Expression{
&parser.String{
LiteralPos: scanner.Position{
Offset: 18,
Line: 3,
Column: 10,
},
RBracePos: scanner.Position{
Offset: 26,
Line: 3,
Column: 19,
},
Values: []parser.Expression{
&parser.String{
LiteralPos: scanner.Position{
Offset: 18,
Line: 3,
Column: 11,
},
Value: "a",
},
&parser.String{
LiteralPos: scanner.Position{
Offset: 23,
Line: 3,
Column: 16,
},
Value: "b",
},
Column: 11,
},
Value: "a",
},
}},
&parser.String{
LiteralPos: scanner.Position{
Offset: 23,
Line: 3,
Column: 16,
},
Value: "b",
},
},
},
},
},
}},
false,
),
},
},
},
@ -853,64 +847,60 @@ var validUnpackTestCases = []struct {
&struct {
Foo Configurable[string]
}{
Foo: Configurable[string]{
propertyName: "foo",
inner: &configurableInner[string]{
single: singleConfigurable[string]{
scope: parser.NewScope(nil),
conditions: []ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"my_variable",
},
Foo: newConfigurableWithPropertyName(
"foo",
[]ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"my_variable",
},
}},
[]ConfigurableCase[string]{
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
cases: []ConfigurableCase[string]{
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 90,
Line: 4,
Column: 11,
},
Value: "a2",
},
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 90,
Line: 4,
Column: 11,
},
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "b",
}},
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 106,
Line: 5,
Column: 11,
},
Value: "b2",
},
Value: "a2",
},
},
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "b",
}},
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 106,
Line: 5,
Column: 11,
},
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeDefault,
}},
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 126,
Line: 6,
Column: 15,
},
Value: "c2",
},
Value: "b2",
},
},
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeDefault,
}},
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 126,
Line: 6,
Column: 15,
},
Value: "c2",
},
},
},
},
true,
),
},
},
},
@ -933,119 +923,117 @@ var validUnpackTestCases = []struct {
&struct {
Foo Configurable[string]
}{
Foo: Configurable[string]{
propertyName: "foo",
inner: &configurableInner[string]{
single: singleConfigurable[string]{
scope: parser.NewScope(nil),
conditions: []ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"my_variable",
},
}},
cases: []ConfigurableCase[string]{
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 90,
Line: 4,
Column: 11,
},
Value: "a2",
},
},
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "b",
}},
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 106,
Line: 5,
Column: 11,
},
Value: "b2",
},
},
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeDefault,
}},
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 126,
Line: 6,
Column: 15,
},
Value: "c2",
},
},
Foo: func() Configurable[string] {
result := newConfigurableWithPropertyName(
"foo",
[]ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"my_variable",
},
},
next: &configurableInner[string]{
single: singleConfigurable[string]{
scope: parser.NewScope(nil),
conditions: []ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"my_2nd_variable",
},
}},
[]ConfigurableCase[string]{
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "a",
}},
cases: []ConfigurableCase[string]{
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "d",
}},
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 218,
Line: 8,
Column: 11,
},
Value: "d2",
},
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 90,
Line: 4,
Column: 11,
},
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "e",
}},
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 234,
Line: 9,
Column: 11,
},
Value: "e2",
},
Value: "a2",
},
},
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "b",
}},
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 106,
Line: 5,
Column: 11,
},
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeDefault,
}},
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 254,
Line: 10,
Column: 15,
},
Value: "f2",
},
Value: "b2",
},
},
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeDefault,
}},
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 126,
Line: 6,
Column: 15,
},
Value: "c2",
},
},
},
},
},
true,
)
result.Append(newConfigurableWithPropertyName(
"",
[]ConfigurableCondition{{
functionName: "soong_config_variable",
args: []string{
"my_namespace",
"my_2nd_variable",
},
}},
[]ConfigurableCase[string]{
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "d",
}},
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 218,
Line: 8,
Column: 11,
},
Value: "d2",
},
},
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeString,
stringValue: "e",
}},
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 234,
Line: 9,
Column: 11,
},
Value: "e2",
},
},
{
patterns: []ConfigurablePattern{{
typ: configurablePatternTypeDefault,
}},
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 254,
Line: 10,
Column: 15,
},
Value: "f2",
},
},
},
true,
))
return result
}(),
},
},
},
@ -1064,41 +1052,37 @@ var validUnpackTestCases = []struct {
Foo Configurable[string]
Bar Configurable[bool]
}{
Foo: Configurable[string]{
propertyName: "foo",
inner: &configurableInner[string]{
single: singleConfigurable[string]{
cases: []ConfigurableCase[string]{{
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 25,
Line: 2,
Column: 25,
},
Value: "asdf",
},
}},
Foo: newConfigurableWithPropertyName(
"foo",
nil,
[]ConfigurableCase[string]{{
value: &parser.String{
LiteralPos: scanner.Position{
Offset: 25,
Line: 2,
Column: 25,
},
Value: "asdf",
},
},
},
Bar: Configurable[bool]{
propertyName: "bar",
inner: &configurableInner[bool]{
single: singleConfigurable[bool]{
cases: []ConfigurableCase[bool]{{
value: &parser.Bool{
LiteralPos: scanner.Position{
Offset: 54,
Line: 3,
Column: 23,
},
Value: true,
Token: "true",
},
}},
}},
false,
),
Bar: newConfigurableWithPropertyName(
"bar",
nil,
[]ConfigurableCase[bool]{{
value: &parser.Bool{
LiteralPos: scanner.Position{
Offset: 54,
Line: 3,
Column: 23,
},
Value: true,
Token: "true",
},
},
},
}},
false,
),
},
},
},