Merge changes from topics "fix_selects_appending", "refactor_selects" into main
* changes: Refactor selects Fix bugs when appending selects Allow extending configurable propeties with non-configurable properties
This commit is contained in:
commit
ca5ffdd3ce
7 changed files with 739 additions and 348 deletions
|
@ -67,7 +67,7 @@ func copyProperties(dstValue, srcValue reflect.Value) {
|
||||||
dstFieldValue.Set(srcFieldValue)
|
dstFieldValue.Set(srcFieldValue)
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
if isConfigurable(srcFieldValue.Type()) {
|
if isConfigurable(srcFieldValue.Type()) {
|
||||||
dstFieldValue.Set(srcFieldValue.Interface().(configurableReflection).cloneToReflectValuePtr().Elem())
|
dstFieldValue.Set(reflect.ValueOf(srcFieldValue.Interface().(configurableReflection).clone()))
|
||||||
} else {
|
} else {
|
||||||
copyProperties(dstFieldValue, srcFieldValue)
|
copyProperties(dstFieldValue, srcFieldValue)
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,18 +36,44 @@ type configurableMarker bool
|
||||||
|
|
||||||
var configurableMarkerType reflect.Type = reflect.TypeOf((*configurableMarker)(nil)).Elem()
|
var configurableMarkerType reflect.Type = reflect.TypeOf((*configurableMarker)(nil)).Elem()
|
||||||
|
|
||||||
|
// 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.
|
||||||
type ConfigurableCondition struct {
|
type ConfigurableCondition struct {
|
||||||
FunctionName string
|
functionName string
|
||||||
Args []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]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurableCondition) String() string {
|
func (c *ConfigurableCondition) String() string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
sb.WriteString(c.FunctionName)
|
sb.WriteString(c.functionName)
|
||||||
sb.WriteRune('(')
|
sb.WriteRune('(')
|
||||||
for i, arg := range c.Args {
|
for i, arg := range c.args {
|
||||||
sb.WriteString(strconv.Quote(arg))
|
sb.WriteString(strconv.Quote(arg))
|
||||||
if i < len(c.Args)-1 {
|
if i < len(c.args)-1 {
|
||||||
sb.WriteString(", ")
|
sb.WriteString(", ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,6 +179,16 @@ func (v *configurablePatternType) String() string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
type ConfigurablePattern struct {
|
type ConfigurablePattern struct {
|
||||||
typ configurablePatternType
|
typ configurablePatternType
|
||||||
stringValue string
|
stringValue string
|
||||||
|
@ -209,18 +245,17 @@ func (p *ConfigurablePattern) matchesValueType(v ConfigurableValue) bool {
|
||||||
return p.typ == v.typ.patternType()
|
return p.typ == v.typ.patternType()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
type ConfigurableCase[T ConfigurableElements] struct {
|
type ConfigurableCase[T ConfigurableElements] struct {
|
||||||
patterns []ConfigurablePattern
|
patterns []ConfigurablePattern
|
||||||
value *T
|
value *T
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurableCase[T]) Clone() ConfigurableCase[T] {
|
|
||||||
return ConfigurableCase[T]{
|
|
||||||
patterns: slices.Clone(c.patterns),
|
|
||||||
value: copyConfiguredValue(c.value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type configurableCaseReflection interface {
|
type configurableCaseReflection interface {
|
||||||
initialize(patterns []ConfigurablePattern, value interface{})
|
initialize(patterns []ConfigurablePattern, value interface{})
|
||||||
}
|
}
|
||||||
|
@ -228,9 +263,11 @@ type configurableCaseReflection interface {
|
||||||
var _ configurableCaseReflection = &ConfigurableCase[string]{}
|
var _ configurableCaseReflection = &ConfigurableCase[string]{}
|
||||||
|
|
||||||
func NewConfigurableCase[T ConfigurableElements](patterns []ConfigurablePattern, value *T) ConfigurableCase[T] {
|
func NewConfigurableCase[T ConfigurableElements](patterns []ConfigurablePattern, value *T) ConfigurableCase[T] {
|
||||||
|
// Clone the values so they can't be modified from soong
|
||||||
|
patterns = slices.Clone(patterns)
|
||||||
return ConfigurableCase[T]{
|
return ConfigurableCase[T]{
|
||||||
patterns: patterns,
|
patterns: patterns,
|
||||||
value: value,
|
value: copyConfiguredValue(value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,6 +294,24 @@ func configurableCaseType(configuredType reflect.Type) reflect.Type {
|
||||||
panic("unimplemented")
|
panic("unimplemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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())
|
||||||
|
}
|
||||||
|
|
||||||
// Configurable can wrap the type of a blueprint property,
|
// Configurable can wrap the type of a blueprint property,
|
||||||
// in order to allow select statements to be used in bp files
|
// in order to allow select statements to be used in bp files
|
||||||
// for that property. For example, for the property struct:
|
// for that property. For example, for the property struct:
|
||||||
|
@ -284,11 +339,22 @@ func configurableCaseType(configuredType reflect.Type) reflect.Type {
|
||||||
// All configurable properties support being unset, so there is
|
// All configurable properties support being unset, so there is
|
||||||
// no need to use a pointer type like Configurable[*string].
|
// no need to use a pointer type like Configurable[*string].
|
||||||
type Configurable[T ConfigurableElements] struct {
|
type Configurable[T ConfigurableElements] struct {
|
||||||
marker configurableMarker
|
marker configurableMarker
|
||||||
propertyName string
|
propertyName string
|
||||||
conditions []ConfigurableCondition
|
inner *configurableInner[T]
|
||||||
cases []ConfigurableCase[T]
|
}
|
||||||
appendWrapper *appendWrapper[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]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore the warning about the unused marker variable, it's used via reflection
|
// Ignore the warning about the unused marker variable, it's used via reflection
|
||||||
|
@ -300,52 +366,61 @@ func NewConfigurable[T ConfigurableElements](conditions []ConfigurableCondition,
|
||||||
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)))
|
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)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Clone the slices so they can't be modified from soong
|
||||||
|
conditions = slices.Clone(conditions)
|
||||||
|
cases = slices.Clone(cases)
|
||||||
return Configurable[T]{
|
return Configurable[T]{
|
||||||
conditions: conditions,
|
inner: &configurableInner[T]{
|
||||||
cases: cases,
|
single: singleConfigurable[T]{
|
||||||
appendWrapper: &appendWrapper[T]{},
|
conditions: conditions,
|
||||||
|
cases: cases,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendWrapper exists so that we can set the value of append
|
|
||||||
// from a non-pointer method receiver. (setAppend)
|
|
||||||
type appendWrapper[T ConfigurableElements] struct {
|
|
||||||
append Configurable[T]
|
|
||||||
replace bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the final value for the configurable property.
|
// Get returns the final value for the configurable property.
|
||||||
// A configurable property may be unset, in which case Get will return nil.
|
// A configurable property may be unset, in which case Get will return nil.
|
||||||
func (c *Configurable[T]) Get(evaluator ConfigurableEvaluator) *T {
|
func (c *Configurable[T]) Get(evaluator ConfigurableEvaluator) *T {
|
||||||
if c == nil || c.appendWrapper == nil {
|
result := c.inner.evaluate(c.propertyName, evaluator)
|
||||||
return nil
|
// Copy the result so that it can't be changed from soong
|
||||||
}
|
return copyConfiguredValue(result)
|
||||||
if c.appendWrapper.replace {
|
|
||||||
return replaceConfiguredValues(
|
|
||||||
c.evaluateNonTransitive(evaluator),
|
|
||||||
c.appendWrapper.append.Get(evaluator),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return appendConfiguredValues(
|
|
||||||
c.evaluateNonTransitive(evaluator),
|
|
||||||
c.appendWrapper.append.Get(evaluator),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrDefault is the same as Get, but will return the provided default value if the property was unset.
|
// 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 {
|
func (c *Configurable[T]) GetOrDefault(evaluator ConfigurableEvaluator, defaultValue T) T {
|
||||||
result := c.Get(evaluator)
|
result := c.inner.evaluate(c.propertyName, evaluator)
|
||||||
if result != nil {
|
if result != nil {
|
||||||
return *result
|
// Copy the result so that it can't be changed from soong
|
||||||
|
return copyAndDereferenceConfiguredValue(result)
|
||||||
}
|
}
|
||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Configurable[T]) evaluateNonTransitive(evaluator ConfigurableEvaluator) *T {
|
func (c *configurableInner[T]) evaluate(propertyName string, evaluator ConfigurableEvaluator) *T {
|
||||||
|
if c == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if c.next == nil {
|
||||||
|
return c.single.evaluateNonTransitive(propertyName, evaluator)
|
||||||
|
}
|
||||||
|
if c.replace {
|
||||||
|
return replaceConfiguredValues(
|
||||||
|
c.single.evaluateNonTransitive(propertyName, evaluator),
|
||||||
|
c.next.evaluate(propertyName, evaluator),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return appendConfiguredValues(
|
||||||
|
c.single.evaluateNonTransitive(propertyName, evaluator),
|
||||||
|
c.next.evaluate(propertyName, evaluator),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *singleConfigurable[T]) evaluateNonTransitive(propertyName string, evaluator ConfigurableEvaluator) *T {
|
||||||
for i, case_ := range c.cases {
|
for i, case_ := range c.cases {
|
||||||
if len(c.conditions) != len(case_.patterns) {
|
if len(c.conditions) != len(case_.patterns) {
|
||||||
evaluator.PropertyErrorf(c.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))
|
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))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -355,13 +430,13 @@ func (c *Configurable[T]) evaluateNonTransitive(evaluator ConfigurableEvaluator)
|
||||||
} else if len(c.cases) == 1 {
|
} else if len(c.cases) == 1 {
|
||||||
return c.cases[0].value
|
return c.cases[0].value
|
||||||
} else {
|
} else {
|
||||||
evaluator.PropertyErrorf(c.propertyName, "Expected 0 or 1 branches in an unconfigured select, found %d", len(c.cases))
|
evaluator.PropertyErrorf(propertyName, "Expected 0 or 1 branches in an unconfigured select, found %d", len(c.cases))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
values := make([]ConfigurableValue, len(c.conditions))
|
values := make([]ConfigurableValue, len(c.conditions))
|
||||||
for i, condition := range c.conditions {
|
for i, condition := range c.conditions {
|
||||||
values[i] = evaluator.EvaluateConfiguration(condition, c.propertyName)
|
values[i] = evaluator.EvaluateConfiguration(condition, propertyName)
|
||||||
}
|
}
|
||||||
foundMatch := false
|
foundMatch := false
|
||||||
var result *T
|
var result *T
|
||||||
|
@ -369,7 +444,7 @@ func (c *Configurable[T]) evaluateNonTransitive(evaluator ConfigurableEvaluator)
|
||||||
allMatch := true
|
allMatch := true
|
||||||
for i, pat := range case_.patterns {
|
for i, pat := range case_.patterns {
|
||||||
if !pat.matchesValueType(values[i]) {
|
if !pat.matchesValueType(values[i]) {
|
||||||
evaluator.PropertyErrorf(c.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())
|
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())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if !pat.matchesValue(values[i]) {
|
if !pat.matchesValue(values[i]) {
|
||||||
|
@ -385,7 +460,7 @@ func (c *Configurable[T]) evaluateNonTransitive(evaluator ConfigurableEvaluator)
|
||||||
if foundMatch {
|
if foundMatch {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
evaluator.PropertyErrorf(c.propertyName, "%s had value %s, which was not handled by the select statement", c.conditions, values)
|
evaluator.PropertyErrorf(propertyName, "%s had value %s, which was not handled by the select statement", c.conditions, values)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,9 +522,9 @@ func replaceConfiguredValues[T ConfigurableElements](a, b *T) *T {
|
||||||
// the property unpacking code. You can't call unexported methods from reflection,
|
// 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.
|
// (at least without unsafe pointer trickery) so this is the next best thing.
|
||||||
type configurableReflection interface {
|
type configurableReflection interface {
|
||||||
setAppend(append any, replace bool)
|
setAppend(append any, replace bool, prepend bool)
|
||||||
configuredType() reflect.Type
|
configuredType() reflect.Type
|
||||||
cloneToReflectValuePtr() reflect.Value
|
clone() any
|
||||||
isEmpty() bool
|
isEmpty() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,64 +539,135 @@ var _ configurablePtrReflection = &Configurable[string]{}
|
||||||
|
|
||||||
func (c *Configurable[T]) initialize(propertyName string, conditions []ConfigurableCondition, cases any) {
|
func (c *Configurable[T]) initialize(propertyName string, conditions []ConfigurableCondition, cases any) {
|
||||||
c.propertyName = propertyName
|
c.propertyName = propertyName
|
||||||
c.conditions = conditions
|
c.inner = &configurableInner[T]{
|
||||||
c.cases = cases.([]ConfigurableCase[T])
|
single: singleConfigurable[T]{
|
||||||
c.appendWrapper = &appendWrapper[T]{}
|
conditions: conditions,
|
||||||
|
cases: cases.([]ConfigurableCase[T]),
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Configurable[T]) setAppend(append any, replace bool) {
|
func (c Configurable[T]) setAppend(append any, replace bool, prepend bool) {
|
||||||
if c.appendWrapper.append.isEmpty() {
|
a := append.(Configurable[T])
|
||||||
c.appendWrapper.append = append.(Configurable[T])
|
if a.inner.isEmpty() {
|
||||||
c.appendWrapper.replace = replace
|
return
|
||||||
} else {
|
|
||||||
c.appendWrapper.append.setAppend(append, replace)
|
|
||||||
}
|
}
|
||||||
|
c.inner.setAppend(a.inner, replace, prepend)
|
||||||
|
if c.inner == c.inner.next {
|
||||||
|
panic("pointer loop")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
} else {
|
||||||
|
// If we're replacing with something that always has a value set,
|
||||||
|
// we can optimize the code by replacing our entire append chain here.
|
||||||
|
if replace && append.alwaysHasValue() {
|
||||||
|
*c = *append.clone()
|
||||||
|
} else {
|
||||||
|
curr := c
|
||||||
|
for curr.next != nil {
|
||||||
|
curr = curr.next
|
||||||
|
}
|
||||||
|
curr.next = append.clone()
|
||||||
|
curr.replace = replace
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Configurable[T]) isEmpty() bool {
|
func (c Configurable[T]) isEmpty() bool {
|
||||||
if c.appendWrapper != nil && !c.appendWrapper.append.isEmpty() {
|
return c.inner.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *singleConfigurable[T]) isEmpty() bool {
|
||||||
|
if c == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if len(c.cases) > 1 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return len(c.cases) == 0
|
if len(c.cases) == 1 && c.cases[0].value != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
if len(c.cases) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, c := range c.cases {
|
||||||
|
if c.value == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Configurable[T]) configuredType() reflect.Type {
|
func (c Configurable[T]) configuredType() reflect.Type {
|
||||||
return reflect.TypeOf((*T)(nil)).Elem()
|
return reflect.TypeOf((*T)(nil)).Elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Configurable[T]) cloneToReflectValuePtr() reflect.Value {
|
|
||||||
return reflect.ValueOf(c.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Configurable[T]) clone() *Configurable[T] {
|
|
||||||
if c == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var inner *appendWrapper[T]
|
|
||||||
if c.appendWrapper != nil {
|
|
||||||
inner = &appendWrapper[T]{}
|
|
||||||
if !c.appendWrapper.append.isEmpty() {
|
|
||||||
inner.append = *c.appendWrapper.append.clone()
|
|
||||||
inner.replace = c.appendWrapper.replace
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
conditionsCopy := make([]ConfigurableCondition, len(c.conditions))
|
|
||||||
copy(conditionsCopy, c.conditions)
|
|
||||||
|
|
||||||
casesCopy := make([]ConfigurableCase[T], len(c.cases))
|
|
||||||
for i, case_ := range c.cases {
|
|
||||||
casesCopy[i] = case_.Clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Configurable[T]{
|
|
||||||
propertyName: c.propertyName,
|
|
||||||
conditions: conditionsCopy,
|
|
||||||
cases: casesCopy,
|
|
||||||
appendWrapper: inner,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyConfiguredValue[T ConfigurableElements](t *T) *T {
|
func copyConfiguredValue[T ConfigurableElements](t *T) *T {
|
||||||
if t == nil {
|
if t == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -531,6 +677,16 @@ func copyConfiguredValue[T ConfigurableElements](t *T) *T {
|
||||||
result := any(slices.Clone(t2)).(T)
|
result := any(slices.Clone(t2)).(T)
|
||||||
return &result
|
return &result
|
||||||
default:
|
default:
|
||||||
return t
|
x := *t
|
||||||
|
return &x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyAndDereferenceConfiguredValue[T ConfigurableElements](t *T) T {
|
||||||
|
switch t2 := any(*t).(type) {
|
||||||
|
case []string:
|
||||||
|
return any(slices.Clone(t2)).(T)
|
||||||
|
default:
|
||||||
|
return *t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -384,12 +384,16 @@ func extendPropertiesRecursive(dstValues []reflect.Value, srcValue reflect.Value
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
case reflect.Bool, reflect.String, reflect.Slice, reflect.Map:
|
case reflect.Bool, reflect.String, reflect.Slice, reflect.Map:
|
||||||
if srcFieldValue.Type() != dstFieldValue.Type() {
|
// If the types don't match or srcFieldValue cannot be converted to a Configurable type, it's an error
|
||||||
|
ct, err := configurableType(srcFieldValue.Type())
|
||||||
|
if srcFieldValue.Type() != dstFieldValue.Type() && (err != nil || dstFieldValue.Type() != ct) {
|
||||||
return extendPropertyErrorf(propertyName(srcField), "mismatched types %s and %s",
|
return extendPropertyErrorf(propertyName(srcField), "mismatched types %s and %s",
|
||||||
dstFieldValue.Type(), srcFieldValue.Type())
|
dstFieldValue.Type(), srcFieldValue.Type())
|
||||||
}
|
}
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
if srcFieldValue.Type() != dstFieldValue.Type() {
|
// If the types don't match or srcFieldValue cannot be converted to a Configurable type, it's an error
|
||||||
|
ct, err := configurableType(srcFieldValue.Type().Elem())
|
||||||
|
if srcFieldValue.Type() != dstFieldValue.Type() && (err != nil || dstFieldValue.Type() != ct) {
|
||||||
return extendPropertyErrorf(propertyName(srcField), "mismatched types %s and %s",
|
return extendPropertyErrorf(propertyName(srcField), "mismatched types %s and %s",
|
||||||
dstFieldValue.Type(), srcFieldValue.Type())
|
dstFieldValue.Type(), srcFieldValue.Type())
|
||||||
}
|
}
|
||||||
|
@ -457,25 +461,61 @@ func extendPropertiesRecursive(dstValues []reflect.Value, srcValue reflect.Value
|
||||||
func ExtendBasicType(dstFieldValue, srcFieldValue reflect.Value, order Order) {
|
func ExtendBasicType(dstFieldValue, srcFieldValue reflect.Value, order Order) {
|
||||||
prepend := order == Prepend || order == Prepend_replace
|
prepend := order == Prepend || order == Prepend_replace
|
||||||
|
|
||||||
|
if !srcFieldValue.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If dst is a Configurable and src isn't, promote src to a Configurable.
|
||||||
|
// This isn't necessary if all property structs are using Configurable values,
|
||||||
|
// but it's helpful to avoid having to change as many places in the code when
|
||||||
|
// converting properties to Configurable properties. For example, load hooks
|
||||||
|
// make their own mini-property structs and append them onto the main property
|
||||||
|
// structs when they want to change the default values of properties.
|
||||||
|
srcFieldType := srcFieldValue.Type()
|
||||||
|
if isConfigurable(dstFieldValue.Type()) && !isConfigurable(srcFieldType) {
|
||||||
|
var value reflect.Value
|
||||||
|
if srcFieldType.Kind() == reflect.Pointer {
|
||||||
|
srcFieldType = srcFieldType.Elem()
|
||||||
|
if srcFieldValue.IsNil() {
|
||||||
|
value = srcFieldValue
|
||||||
|
} else {
|
||||||
|
// Copy the pointer
|
||||||
|
value = reflect.New(srcFieldType)
|
||||||
|
value.Elem().Set(srcFieldValue.Elem())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value = reflect.New(srcFieldType)
|
||||||
|
value.Elem().Set(srcFieldValue)
|
||||||
|
}
|
||||||
|
caseType := configurableCaseType(srcFieldType)
|
||||||
|
case_ := reflect.New(caseType)
|
||||||
|
case_.Interface().(configurableCaseReflection).initialize(nil, value.Interface())
|
||||||
|
cases := reflect.MakeSlice(reflect.SliceOf(caseType), 0, 1)
|
||||||
|
cases = reflect.Append(cases, case_.Elem())
|
||||||
|
ct, err := configurableType(srcFieldType)
|
||||||
|
if err != nil {
|
||||||
|
// Should be unreachable due to earlier checks
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
temp := reflect.New(ct)
|
||||||
|
temp.Interface().(configurablePtrReflection).initialize("", nil, cases.Interface())
|
||||||
|
srcFieldValue = temp.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
switch srcFieldValue.Kind() {
|
switch srcFieldValue.Kind() {
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
if !isConfigurable(srcFieldValue.Type()) {
|
if !isConfigurable(srcFieldValue.Type()) {
|
||||||
panic("Should be unreachable")
|
panic("Should be unreachable")
|
||||||
}
|
}
|
||||||
if dstFieldValue.Interface().(configurableReflection).isEmpty() {
|
replace := order == Prepend_replace || order == Replace
|
||||||
dstFieldValue.Set(srcFieldValue)
|
unpackedDst := dstFieldValue.Interface().(configurableReflection)
|
||||||
} else if order == Prepend {
|
if unpackedDst.isEmpty() {
|
||||||
srcFieldValue.Interface().(configurableReflection).setAppend(dstFieldValue.Interface(), false)
|
// Properties that were never initialized via unpacking from a bp file value
|
||||||
dstFieldValue.Set(srcFieldValue)
|
// will have a nil inner value, making them unable to be modified without a pointer
|
||||||
} else if order == Append {
|
// like we don't have here. So instead replace the whole configurable object.
|
||||||
dstFieldValue.Interface().(configurableReflection).setAppend(srcFieldValue.Interface(), false)
|
dstFieldValue.Set(reflect.ValueOf(srcFieldValue.Interface().(configurableReflection).clone()))
|
||||||
} else if order == Replace {
|
|
||||||
dstFieldValue.Interface().(configurableReflection).setAppend(srcFieldValue.Interface(), true)
|
|
||||||
} else if order == Prepend_replace {
|
|
||||||
srcFieldValue.Interface().(configurableReflection).setAppend(dstFieldValue.Interface(), true)
|
|
||||||
dstFieldValue.Set(srcFieldValue)
|
|
||||||
} else {
|
} else {
|
||||||
panic(fmt.Sprintf("Unexpected order: %d", order))
|
unpackedDst.setAppend(srcFieldValue.Interface(), replace, prepend)
|
||||||
}
|
}
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
// Boolean OR
|
// Boolean OR
|
||||||
|
|
|
@ -1258,138 +1258,11 @@ func appendPropertiesTestCases() []appendPropertyTestCase {
|
||||||
name: "Append configurable",
|
name: "Append configurable",
|
||||||
dst: &struct{ S Configurable[[]string] }{
|
dst: &struct{ S Configurable[[]string] }{
|
||||||
S: Configurable[[]string]{
|
S: Configurable[[]string]{
|
||||||
conditions: []ConfigurableCondition{{
|
inner: &configurableInner[[]string]{
|
||||||
FunctionName: "soong_config_variable",
|
single: singleConfigurable[[]string]{
|
||||||
Args: []string{
|
|
||||||
"my_namespace",
|
|
||||||
"foo",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[[]string]{{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "a",
|
|
||||||
}},
|
|
||||||
value: &[]string{"1", "2"},
|
|
||||||
}},
|
|
||||||
appendWrapper: &appendWrapper[[]string]{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
src: &struct{ S Configurable[[]string] }{
|
|
||||||
S: Configurable[[]string]{
|
|
||||||
conditions: []ConfigurableCondition{{
|
|
||||||
FunctionName: "release_variable",
|
|
||||||
Args: []string{
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[[]string]{{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "b",
|
|
||||||
}},
|
|
||||||
value: &[]string{"3", "4"},
|
|
||||||
}},
|
|
||||||
appendWrapper: &appendWrapper[[]string]{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
out: &struct{ S Configurable[[]string] }{
|
|
||||||
S: Configurable[[]string]{
|
|
||||||
conditions: []ConfigurableCondition{{
|
|
||||||
FunctionName: "soong_config_variable",
|
|
||||||
Args: []string{
|
|
||||||
"my_namespace",
|
|
||||||
"foo",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[[]string]{{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "a",
|
|
||||||
}},
|
|
||||||
value: &[]string{"1", "2"},
|
|
||||||
}},
|
|
||||||
appendWrapper: &appendWrapper[[]string]{
|
|
||||||
append: Configurable[[]string]{
|
|
||||||
conditions: []ConfigurableCondition{{
|
conditions: []ConfigurableCondition{{
|
||||||
FunctionName: "release_variable",
|
functionName: "soong_config_variable",
|
||||||
Args: []string{
|
args: []string{
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[[]string]{{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "b",
|
|
||||||
}},
|
|
||||||
value: &[]string{"3", "4"},
|
|
||||||
}},
|
|
||||||
appendWrapper: &appendWrapper[[]string]{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Prepend configurable",
|
|
||||||
order: Prepend,
|
|
||||||
dst: &struct{ S Configurable[[]string] }{
|
|
||||||
S: Configurable[[]string]{
|
|
||||||
conditions: []ConfigurableCondition{{
|
|
||||||
FunctionName: "soong_config_variable",
|
|
||||||
Args: []string{
|
|
||||||
"my_namespace",
|
|
||||||
"foo",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[[]string]{{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "a",
|
|
||||||
}},
|
|
||||||
value: &[]string{"1", "2"},
|
|
||||||
}},
|
|
||||||
appendWrapper: &appendWrapper[[]string]{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
src: &struct{ S Configurable[[]string] }{
|
|
||||||
S: Configurable[[]string]{
|
|
||||||
conditions: []ConfigurableCondition{{
|
|
||||||
FunctionName: "release_variable",
|
|
||||||
Args: []string{
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[[]string]{{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "b",
|
|
||||||
}},
|
|
||||||
value: &[]string{"3", "4"},
|
|
||||||
}},
|
|
||||||
appendWrapper: &appendWrapper[[]string]{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
out: &struct{ S Configurable[[]string] }{
|
|
||||||
S: Configurable[[]string]{
|
|
||||||
conditions: []ConfigurableCondition{{
|
|
||||||
FunctionName: "release_variable",
|
|
||||||
Args: []string{
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[[]string]{{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "b",
|
|
||||||
}},
|
|
||||||
value: &[]string{"3", "4"},
|
|
||||||
}},
|
|
||||||
appendWrapper: &appendWrapper[[]string]{
|
|
||||||
append: Configurable[[]string]{
|
|
||||||
conditions: []ConfigurableCondition{{
|
|
||||||
FunctionName: "soong_config_variable",
|
|
||||||
Args: []string{
|
|
||||||
"my_namespace",
|
"my_namespace",
|
||||||
"foo",
|
"foo",
|
||||||
},
|
},
|
||||||
|
@ -1401,7 +1274,152 @@ func appendPropertiesTestCases() []appendPropertyTestCase {
|
||||||
}},
|
}},
|
||||||
value: &[]string{"1", "2"},
|
value: &[]string{"1", "2"},
|
||||||
}},
|
}},
|
||||||
appendWrapper: &appendWrapper[[]string]{},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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: &[]string{"3", "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: &[]string{"1", "2"},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
next: &configurableInner[[]string]{
|
||||||
|
single: singleConfigurable[[]string]{
|
||||||
|
conditions: []ConfigurableCondition{{
|
||||||
|
functionName: "release_variable",
|
||||||
|
args: []string{
|
||||||
|
"bar",
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
cases: []ConfigurableCase[[]string]{{
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeString,
|
||||||
|
stringValue: "b",
|
||||||
|
}},
|
||||||
|
value: &[]string{"3", "4"},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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: &[]string{"1", "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: &[]string{"3", "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: &[]string{"3", "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: &[]string{"1", "2"},
|
||||||
|
}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1869,6 +1887,150 @@ func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase {
|
||||||
},
|
},
|
||||||
err: extendPropertyErrorf("s", "mismatched types []int and []string"),
|
err: extendPropertyErrorf("s", "mismatched types []int and []string"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Append *bool to Configurable[bool]",
|
||||||
|
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: BoolPtr(true),
|
||||||
|
}, {
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeDefault,
|
||||||
|
}},
|
||||||
|
value: BoolPtr(false),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
src: &struct{ S *bool }{
|
||||||
|
S: BoolPtr(true),
|
||||||
|
},
|
||||||
|
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: BoolPtr(true),
|
||||||
|
}, {
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeDefault,
|
||||||
|
}},
|
||||||
|
value: BoolPtr(false),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
next: &configurableInner[bool]{
|
||||||
|
single: singleConfigurable[bool]{
|
||||||
|
cases: []ConfigurableCase[bool]{{
|
||||||
|
value: BoolPtr(true),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Append bool to Configurable[bool]",
|
||||||
|
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: BoolPtr(true),
|
||||||
|
}, {
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeDefault,
|
||||||
|
}},
|
||||||
|
value: BoolPtr(false),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
src: &struct{ S bool }{
|
||||||
|
S: true,
|
||||||
|
},
|
||||||
|
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: BoolPtr(true),
|
||||||
|
}, {
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeDefault,
|
||||||
|
}},
|
||||||
|
value: BoolPtr(false),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
next: &configurableInner[bool]{
|
||||||
|
single: singleConfigurable[bool]{
|
||||||
|
cases: []ConfigurableCase[bool]{{
|
||||||
|
value: BoolPtr(true),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,10 @@ func typeEqual(v1, v2 reflect.Value) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isConfigurable(v1.Type()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
for i := 0; i < v1.NumField(); i++ {
|
for i := 0; i < v1.NumField(); i++ {
|
||||||
v1 := v1.Field(i)
|
v1 := v1.Field(i)
|
||||||
v2 := v2.Field(i)
|
v2 := v2.Field(i)
|
||||||
|
@ -94,6 +98,10 @@ func concreteType(v reflect.Value) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isConfigurable(v.Type()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
for i := 0; i < v.NumField(); i++ {
|
for i := 0; i < v.NumField(); i++ {
|
||||||
v := v.Field(i)
|
v := v.Field(i)
|
||||||
|
|
||||||
|
|
|
@ -356,10 +356,13 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
|
||||||
}
|
}
|
||||||
result := Configurable[string]{
|
result := Configurable[string]{
|
||||||
propertyName: property.Name,
|
propertyName: property.Name,
|
||||||
cases: []ConfigurableCase[string]{{
|
inner: &configurableInner[string]{
|
||||||
value: &v.Value,
|
single: singleConfigurable[string]{
|
||||||
}},
|
cases: []ConfigurableCase[string]{{
|
||||||
appendWrapper: &appendWrapper[string]{},
|
value: &v.Value,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return reflect.ValueOf(&result), true
|
return reflect.ValueOf(&result), true
|
||||||
case *parser.Bool:
|
case *parser.Bool:
|
||||||
|
@ -373,10 +376,13 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
|
||||||
}
|
}
|
||||||
result := Configurable[bool]{
|
result := Configurable[bool]{
|
||||||
propertyName: property.Name,
|
propertyName: property.Name,
|
||||||
cases: []ConfigurableCase[bool]{{
|
inner: &configurableInner[bool]{
|
||||||
value: &v.Value,
|
single: singleConfigurable[bool]{
|
||||||
}},
|
cases: []ConfigurableCase[bool]{{
|
||||||
appendWrapper: &appendWrapper[bool]{},
|
value: &v.Value,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return reflect.ValueOf(&result), true
|
return reflect.ValueOf(&result), true
|
||||||
case *parser.List:
|
case *parser.List:
|
||||||
|
@ -407,10 +413,13 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
|
||||||
}
|
}
|
||||||
result := Configurable[[]string]{
|
result := Configurable[[]string]{
|
||||||
propertyName: property.Name,
|
propertyName: property.Name,
|
||||||
cases: []ConfigurableCase[[]string]{{
|
inner: &configurableInner[[]string]{
|
||||||
value: &value,
|
single: singleConfigurable[[]string]{
|
||||||
}},
|
cases: []ConfigurableCase[[]string]{{
|
||||||
appendWrapper: &appendWrapper[[]string]{},
|
value: &value,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return reflect.ValueOf(&result), true
|
return reflect.ValueOf(&result), true
|
||||||
default:
|
default:
|
||||||
|
@ -432,8 +441,8 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
|
||||||
args[j] = arg.Value
|
args[j] = arg.Value
|
||||||
}
|
}
|
||||||
conditions[i] = ConfigurableCondition{
|
conditions[i] = ConfigurableCondition{
|
||||||
FunctionName: cond.FunctionName,
|
functionName: cond.FunctionName,
|
||||||
Args: args,
|
args: args,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,7 +521,7 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
|
||||||
if !ok {
|
if !ok {
|
||||||
return reflect.New(configurableType), false
|
return reflect.New(configurableType), false
|
||||||
}
|
}
|
||||||
result.Interface().(configurableReflection).setAppend(val.Elem().Interface(), false)
|
result.Interface().(configurableReflection).setAppend(val.Elem().Interface(), false, false)
|
||||||
}
|
}
|
||||||
return resultPtr, true
|
return resultPtr, true
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -734,10 +734,13 @@ var validUnpackTestCases = []struct {
|
||||||
}{
|
}{
|
||||||
Foo: Configurable[string]{
|
Foo: Configurable[string]{
|
||||||
propertyName: "foo",
|
propertyName: "foo",
|
||||||
cases: []ConfigurableCase[string]{{
|
inner: &configurableInner[string]{
|
||||||
value: StringPtr("bar"),
|
single: singleConfigurable[string]{
|
||||||
}},
|
cases: []ConfigurableCase[string]{{
|
||||||
appendWrapper: &appendWrapper[string]{},
|
value: StringPtr("bar"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -755,10 +758,13 @@ var validUnpackTestCases = []struct {
|
||||||
}{
|
}{
|
||||||
Foo: Configurable[bool]{
|
Foo: Configurable[bool]{
|
||||||
propertyName: "foo",
|
propertyName: "foo",
|
||||||
cases: []ConfigurableCase[bool]{{
|
inner: &configurableInner[bool]{
|
||||||
value: BoolPtr(true),
|
single: singleConfigurable[bool]{
|
||||||
}},
|
cases: []ConfigurableCase[bool]{{
|
||||||
appendWrapper: &appendWrapper[bool]{},
|
value: BoolPtr(true),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -776,10 +782,13 @@ var validUnpackTestCases = []struct {
|
||||||
}{
|
}{
|
||||||
Foo: Configurable[[]string]{
|
Foo: Configurable[[]string]{
|
||||||
propertyName: "foo",
|
propertyName: "foo",
|
||||||
cases: []ConfigurableCase[[]string]{{
|
inner: &configurableInner[[]string]{
|
||||||
value: &[]string{"a", "b"},
|
single: singleConfigurable[[]string]{
|
||||||
}},
|
cases: []ConfigurableCase[[]string]{{
|
||||||
appendWrapper: &appendWrapper[[]string]{},
|
value: &[]string{"a", "b"},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -801,36 +810,39 @@ var validUnpackTestCases = []struct {
|
||||||
}{
|
}{
|
||||||
Foo: Configurable[string]{
|
Foo: Configurable[string]{
|
||||||
propertyName: "foo",
|
propertyName: "foo",
|
||||||
conditions: []ConfigurableCondition{{
|
inner: &configurableInner[string]{
|
||||||
FunctionName: "soong_config_variable",
|
single: singleConfigurable[string]{
|
||||||
Args: []string{
|
conditions: []ConfigurableCondition{{
|
||||||
"my_namespace",
|
functionName: "soong_config_variable",
|
||||||
"my_variable",
|
args: []string{
|
||||||
},
|
"my_namespace",
|
||||||
}},
|
"my_variable",
|
||||||
cases: []ConfigurableCase[string]{
|
},
|
||||||
{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "a",
|
|
||||||
}},
|
}},
|
||||||
value: StringPtr("a2"),
|
cases: []ConfigurableCase[string]{
|
||||||
},
|
{
|
||||||
{
|
patterns: []ConfigurablePattern{{
|
||||||
patterns: []ConfigurablePattern{{
|
typ: configurablePatternTypeString,
|
||||||
typ: configurablePatternTypeString,
|
stringValue: "a",
|
||||||
stringValue: "b",
|
}},
|
||||||
}},
|
value: StringPtr("a2"),
|
||||||
value: StringPtr("b2"),
|
},
|
||||||
},
|
{
|
||||||
{
|
patterns: []ConfigurablePattern{{
|
||||||
patterns: []ConfigurablePattern{{
|
typ: configurablePatternTypeString,
|
||||||
typ: configurablePatternTypeDefault,
|
stringValue: "b",
|
||||||
}},
|
}},
|
||||||
value: StringPtr("c2"),
|
value: StringPtr("b2"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeDefault,
|
||||||
|
}},
|
||||||
|
value: StringPtr("c2"),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
appendWrapper: &appendWrapper[string]{},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -856,68 +868,70 @@ var validUnpackTestCases = []struct {
|
||||||
}{
|
}{
|
||||||
Foo: Configurable[string]{
|
Foo: Configurable[string]{
|
||||||
propertyName: "foo",
|
propertyName: "foo",
|
||||||
conditions: []ConfigurableCondition{{
|
inner: &configurableInner[string]{
|
||||||
FunctionName: "soong_config_variable",
|
single: singleConfigurable[string]{
|
||||||
Args: []string{
|
|
||||||
"my_namespace",
|
|
||||||
"my_variable",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[string]{
|
|
||||||
{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "a",
|
|
||||||
}},
|
|
||||||
value: StringPtr("a2"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "b",
|
|
||||||
}},
|
|
||||||
value: StringPtr("b2"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeDefault,
|
|
||||||
}},
|
|
||||||
value: StringPtr("c2"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
appendWrapper: &appendWrapper[string]{
|
|
||||||
append: Configurable[string]{
|
|
||||||
propertyName: "foo",
|
|
||||||
conditions: []ConfigurableCondition{{
|
conditions: []ConfigurableCondition{{
|
||||||
FunctionName: "soong_config_variable",
|
functionName: "soong_config_variable",
|
||||||
Args: []string{
|
args: []string{
|
||||||
"my_namespace",
|
"my_namespace",
|
||||||
"my_2nd_variable",
|
"my_variable",
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
cases: []ConfigurableCase[string]{
|
cases: []ConfigurableCase[string]{
|
||||||
{
|
{
|
||||||
patterns: []ConfigurablePattern{{
|
patterns: []ConfigurablePattern{{
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "d",
|
stringValue: "a",
|
||||||
}},
|
}},
|
||||||
value: StringPtr("d2"),
|
value: StringPtr("a2"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
patterns: []ConfigurablePattern{{
|
patterns: []ConfigurablePattern{{
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "e",
|
stringValue: "b",
|
||||||
}},
|
}},
|
||||||
value: StringPtr("e2"),
|
value: StringPtr("b2"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
patterns: []ConfigurablePattern{{
|
patterns: []ConfigurablePattern{{
|
||||||
typ: configurablePatternTypeDefault,
|
typ: configurablePatternTypeDefault,
|
||||||
}},
|
}},
|
||||||
value: StringPtr("f2"),
|
value: StringPtr("c2"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
next: &configurableInner[string]{
|
||||||
|
single: singleConfigurable[string]{
|
||||||
|
conditions: []ConfigurableCondition{{
|
||||||
|
functionName: "soong_config_variable",
|
||||||
|
args: []string{
|
||||||
|
"my_namespace",
|
||||||
|
"my_2nd_variable",
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
cases: []ConfigurableCase[string]{
|
||||||
|
{
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeString,
|
||||||
|
stringValue: "d",
|
||||||
|
}},
|
||||||
|
value: StringPtr("d2"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeString,
|
||||||
|
stringValue: "e",
|
||||||
|
}},
|
||||||
|
value: StringPtr("e2"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeDefault,
|
||||||
|
}},
|
||||||
|
value: StringPtr("f2"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
appendWrapper: &appendWrapper[string]{},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -941,21 +955,23 @@ var validUnpackTestCases = []struct {
|
||||||
}{
|
}{
|
||||||
Foo: Configurable[string]{
|
Foo: Configurable[string]{
|
||||||
propertyName: "foo",
|
propertyName: "foo",
|
||||||
cases: []ConfigurableCase[string]{
|
inner: &configurableInner[string]{
|
||||||
{
|
single: singleConfigurable[string]{
|
||||||
value: StringPtr("asdf"),
|
cases: []ConfigurableCase[string]{{
|
||||||
|
value: StringPtr("asdf"),
|
||||||
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
appendWrapper: &appendWrapper[string]{},
|
|
||||||
},
|
},
|
||||||
Bar: Configurable[bool]{
|
Bar: Configurable[bool]{
|
||||||
propertyName: "bar",
|
propertyName: "bar",
|
||||||
cases: []ConfigurableCase[bool]{
|
inner: &configurableInner[bool]{
|
||||||
{
|
single: singleConfigurable[bool]{
|
||||||
value: BoolPtr(true),
|
cases: []ConfigurableCase[bool]{{
|
||||||
|
value: BoolPtr(true),
|
||||||
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
appendWrapper: &appendWrapper[bool]{},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue