2016-06-07 21:28:16 +02:00
|
|
|
// Copyright 2016 Google Inc. All rights reserved.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package parser
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2024-06-13 01:26:54 +02:00
|
|
|
"os"
|
2016-06-07 21:28:16 +02:00
|
|
|
"strings"
|
|
|
|
"text/scanner"
|
|
|
|
)
|
|
|
|
|
2016-06-10 02:40:13 +02:00
|
|
|
type Node interface {
|
2018-03-22 01:00:39 +01:00
|
|
|
// Pos returns the position of the first token in the Node
|
2016-06-10 02:40:13 +02:00
|
|
|
Pos() scanner.Position
|
2018-03-22 01:00:39 +01:00
|
|
|
// End returns the position of the character after the last token in the Node
|
2016-06-10 02:40:13 +02:00
|
|
|
End() scanner.Position
|
|
|
|
}
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
// Definition is an Assignment or a Module at the top level of a Blueprints file
|
|
|
|
type Definition interface {
|
2016-06-10 02:40:13 +02:00
|
|
|
Node
|
2016-06-07 21:28:16 +02:00
|
|
|
String() string
|
|
|
|
definitionTag()
|
|
|
|
}
|
|
|
|
|
|
|
|
// An Assignment is a variable assignment at the top level of a Blueprints file, scoped to the
|
2022-01-11 04:46:23 +01:00
|
|
|
// file and subdirs.
|
2016-06-07 21:28:16 +02:00
|
|
|
type Assignment struct {
|
2016-06-10 00:52:30 +02:00
|
|
|
Name string
|
|
|
|
NamePos scanner.Position
|
2016-06-07 21:28:16 +02:00
|
|
|
Value Expression
|
2016-06-10 02:03:57 +02:00
|
|
|
EqualsPos scanner.Position
|
2016-06-07 21:28:16 +02:00
|
|
|
Assigner string
|
|
|
|
Referenced bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *Assignment) String() string {
|
2024-06-13 01:26:54 +02:00
|
|
|
return fmt.Sprintf("%s@%s %s %s %t", a.Name, a.EqualsPos, a.Assigner, a.Value, a.Referenced)
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
|
|
|
|
2016-06-10 02:40:13 +02:00
|
|
|
func (a *Assignment) Pos() scanner.Position { return a.NamePos }
|
|
|
|
func (a *Assignment) End() scanner.Position { return a.Value.End() }
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
func (a *Assignment) definitionTag() {}
|
|
|
|
|
|
|
|
// A Module is a module definition at the top level of a Blueprints file
|
|
|
|
type Module struct {
|
2016-06-10 00:52:30 +02:00
|
|
|
Type string
|
|
|
|
TypePos scanner.Position
|
2016-06-07 21:28:16 +02:00
|
|
|
Map
|
2023-02-21 18:10:27 +01:00
|
|
|
//TODO(delmerico) make this a private field once ag/21588220 lands
|
|
|
|
Name__internal_only *string
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Module) Copy() *Module {
|
|
|
|
ret := *m
|
|
|
|
ret.Properties = make([]*Property, len(m.Properties))
|
|
|
|
for i := range m.Properties {
|
|
|
|
ret.Properties[i] = m.Properties[i].Copy()
|
|
|
|
}
|
|
|
|
return &ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Module) String() string {
|
|
|
|
propertyStrings := make([]string, len(m.Properties))
|
|
|
|
for i, property := range m.Properties {
|
|
|
|
propertyStrings[i] = property.String()
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%s@%s-%s{%s}", m.Type,
|
|
|
|
m.LBracePos, m.RBracePos,
|
|
|
|
strings.Join(propertyStrings, ", "))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Module) definitionTag() {}
|
|
|
|
|
2016-06-10 00:52:30 +02:00
|
|
|
func (m *Module) Pos() scanner.Position { return m.TypePos }
|
|
|
|
func (m *Module) End() scanner.Position { return m.Map.End() }
|
|
|
|
|
2023-02-21 18:10:27 +01:00
|
|
|
func (m *Module) Name() string {
|
|
|
|
if m.Name__internal_only != nil {
|
|
|
|
return *m.Name__internal_only
|
|
|
|
}
|
|
|
|
for _, prop := range m.Properties {
|
|
|
|
if prop.Name == "name" {
|
|
|
|
if stringProp, ok := prop.Value.(*String); ok {
|
|
|
|
name := stringProp.Value
|
|
|
|
m.Name__internal_only = &name
|
|
|
|
} else {
|
|
|
|
name := prop.Value.String()
|
|
|
|
m.Name__internal_only = &name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if m.Name__internal_only == nil {
|
|
|
|
name := ""
|
|
|
|
m.Name__internal_only = &name
|
|
|
|
}
|
|
|
|
return *m.Name__internal_only
|
|
|
|
}
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
// A Property is a name: value pair within a Map, which may be a top level Module.
|
|
|
|
type Property struct {
|
2016-06-10 02:03:57 +02:00
|
|
|
Name string
|
|
|
|
NamePos scanner.Position
|
|
|
|
ColonPos scanner.Position
|
|
|
|
Value Expression
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Property) Copy() *Property {
|
|
|
|
ret := *p
|
|
|
|
ret.Value = p.Value.Copy()
|
|
|
|
return &ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Property) String() string {
|
2016-06-10 02:03:57 +02:00
|
|
|
return fmt.Sprintf("%s@%s: %s", p.Name, p.ColonPos, p.Value)
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
|
|
|
|
2016-06-10 02:40:13 +02:00
|
|
|
func (p *Property) Pos() scanner.Position { return p.NamePos }
|
|
|
|
func (p *Property) End() scanner.Position { return p.Value.End() }
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
// An Expression is a Value in a Property or Assignment. It can be a literal (String or Bool), a
|
|
|
|
// Map, a List, an Operator that combines two expressions of the same type, or a Variable that
|
|
|
|
// references and Assignment.
|
|
|
|
type Expression interface {
|
2016-06-10 02:40:13 +02:00
|
|
|
Node
|
2016-06-07 21:28:16 +02:00
|
|
|
// Copy returns a copy of the Expression that will not affect the original if mutated
|
|
|
|
Copy() Expression
|
|
|
|
String() string
|
2024-06-13 01:26:54 +02:00
|
|
|
// Type returns the underlying Type enum of the Expression if it were to be evaluated, if it's known.
|
|
|
|
// It's possible that the type isn't known, such as when a select statement with a late-bound variable
|
|
|
|
// is used. For that reason, Type() is mostly for use in error messages, not to make logic decisions
|
|
|
|
// off of.
|
2016-06-07 21:28:16 +02:00
|
|
|
Type() Type
|
2024-06-13 01:26:54 +02:00
|
|
|
// Eval returns an expression that is fully evaluated to a simple type (List, Map, String,
|
|
|
|
// Bool, or Select). It will return the origional expression if possible, or allocate a
|
|
|
|
// new one if modifications were necessary.
|
|
|
|
Eval(scope *Scope) (Expression, error)
|
|
|
|
// PrintfInto will substitute any %s's in string literals in the AST with the provided
|
|
|
|
// value. It will modify the AST in-place. This is used to implement soong config value
|
|
|
|
// variables, but should be removed when those have switched to selects.
|
|
|
|
PrintfInto(value string) error
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
|
|
|
|
2017-05-17 02:39:37 +02:00
|
|
|
// ExpressionsAreSame tells whether the two values are the same Expression.
|
|
|
|
// This includes the symbolic representation of each Expression but not their positions in the original source tree.
|
|
|
|
// This does not apply any simplification to the expressions before comparing them
|
|
|
|
// (for example, "!!a" wouldn't be deemed equal to "a")
|
|
|
|
func ExpressionsAreSame(a Expression, b Expression) (equal bool, err error) {
|
|
|
|
return hackyExpressionsAreSame(a, b)
|
|
|
|
}
|
|
|
|
|
Implement list of maps
Allow property value to be a list of maps, e.g.
my_module {
my_list: [
{ name: "foo", value: 42, something: true, },
{ name: "bar", value: 34, something: false, },
],
}
Test: internal
Change-Id: I2fc37d692aac39f23c9aa7bda2859ab49f3bc672
2020-02-12 07:39:47 +01:00
|
|
|
// TODO(jeffrygaston) once positions are removed from Expression structs,
|
2017-05-17 02:39:37 +02:00
|
|
|
// remove this function and have callers use reflect.DeepEqual(a, b)
|
|
|
|
func hackyExpressionsAreSame(a Expression, b Expression) (equal bool, err error) {
|
|
|
|
left, err := hackyFingerprint(a)
|
|
|
|
if err != nil {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
right, err := hackyFingerprint(b)
|
|
|
|
if err != nil {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
areEqual := string(left) == string(right)
|
|
|
|
return areEqual, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func hackyFingerprint(expression Expression) (fingerprint []byte, err error) {
|
2024-06-13 01:26:54 +02:00
|
|
|
assignment := &Assignment{"a", noPos, expression, noPos, "=", false}
|
2017-05-17 02:39:37 +02:00
|
|
|
module := &File{}
|
|
|
|
module.Defs = append(module.Defs, assignment)
|
|
|
|
p := newPrinter(module)
|
|
|
|
return p.Print()
|
|
|
|
}
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
type Type int
|
|
|
|
|
|
|
|
const (
|
2024-06-13 01:26:54 +02:00
|
|
|
UnknownType Type = iota
|
|
|
|
BoolType
|
2016-06-07 21:28:16 +02:00
|
|
|
StringType
|
Support parsing int64 in Blueprint file.
Support int64 number instead of int to be more fixed to bit size so
that the underlying arch won't affect overflow cases. Besides,
refection: func (v Value) Int() int64 always cast to int64 no matter the
input is int, int16, int32. Currently we always treat "-" as negative
sign to bind to next value, and "+" as plus operator to add operands
together.
So we allow:
a = 5 + -4 + 5 or a = -4 + 5
But we don't allow:
a = +5 + 4 + -4 since we don't treat "+" as a positive sign, otherwise,
a = 5 + +5 would exist which looks pretty weird. In the future, we may
want fully support number calculator logic eg, "+"/"-" can be
positive/negative sign or operator, and "(" and ")" will be considered
to group expressions with a higher precedence.
int & uint properties within struct keeps unchanged, which is only
allowed when tagged with 'blueprint:mutated'. We only allow *int64
property instead of int64 property within struct since it does't make
sense to do prepending or appending to int64.
Change-Id: I565e046dbd268af3538aee148cd7300037e56523
2017-11-01 22:03:28 +01:00
|
|
|
Int64Type
|
2016-06-07 21:28:16 +02:00
|
|
|
ListType
|
|
|
|
MapType
|
Add support for unset select branches
Currently, with the arch/os mutator, you can override a property
using the default value for just a few arch types, for example:
cc_defaults {
name: "my_defaults",
target: {
windows: {
enabled: true,
}
}
}
cc_binary {
name: "foo",
enabled: false,
defaults: ["my_defaults"],
}
You could make a select statment that acts like the above if it were
all in one module, but currently with select statements you can't make
a defaults module that can be generically applied to any other module
and have the same behavior as the above.
After this cl, the defaults module could look like:
cc_defaults {
name: "my_defaults",
enabled: select(variant("arch"), {
"windows": true,
_: unset,
}),
}
Which would have the same behavior. Unset may also be useful for
setting the property under some configurations, but wanting to leave
the implementation-specific default value in others.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: I3ea3277ea8b9a0ac5e613b4378945388b9df036a
2024-03-29 00:20:12 +01:00
|
|
|
UnsetType
|
2016-06-07 21:28:16 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
func (t Type) String() string {
|
|
|
|
switch t {
|
2024-06-13 01:26:54 +02:00
|
|
|
case UnknownType:
|
|
|
|
return "unknown"
|
2016-06-07 21:28:16 +02:00
|
|
|
case BoolType:
|
|
|
|
return "bool"
|
|
|
|
case StringType:
|
|
|
|
return "string"
|
Support parsing int64 in Blueprint file.
Support int64 number instead of int to be more fixed to bit size so
that the underlying arch won't affect overflow cases. Besides,
refection: func (v Value) Int() int64 always cast to int64 no matter the
input is int, int16, int32. Currently we always treat "-" as negative
sign to bind to next value, and "+" as plus operator to add operands
together.
So we allow:
a = 5 + -4 + 5 or a = -4 + 5
But we don't allow:
a = +5 + 4 + -4 since we don't treat "+" as a positive sign, otherwise,
a = 5 + +5 would exist which looks pretty weird. In the future, we may
want fully support number calculator logic eg, "+"/"-" can be
positive/negative sign or operator, and "(" and ")" will be considered
to group expressions with a higher precedence.
int & uint properties within struct keeps unchanged, which is only
allowed when tagged with 'blueprint:mutated'. We only allow *int64
property instead of int64 property within struct since it does't make
sense to do prepending or appending to int64.
Change-Id: I565e046dbd268af3538aee148cd7300037e56523
2017-11-01 22:03:28 +01:00
|
|
|
case Int64Type:
|
|
|
|
return "int64"
|
2016-06-07 21:28:16 +02:00
|
|
|
case ListType:
|
|
|
|
return "list"
|
|
|
|
case MapType:
|
|
|
|
return "map"
|
Add support for unset select branches
Currently, with the arch/os mutator, you can override a property
using the default value for just a few arch types, for example:
cc_defaults {
name: "my_defaults",
target: {
windows: {
enabled: true,
}
}
}
cc_binary {
name: "foo",
enabled: false,
defaults: ["my_defaults"],
}
You could make a select statment that acts like the above if it were
all in one module, but currently with select statements you can't make
a defaults module that can be generically applied to any other module
and have the same behavior as the above.
After this cl, the defaults module could look like:
cc_defaults {
name: "my_defaults",
enabled: select(variant("arch"), {
"windows": true,
_: unset,
}),
}
Which would have the same behavior. Unset may also be useful for
setting the property under some configurations, but wanting to leave
the implementation-specific default value in others.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: I3ea3277ea8b9a0ac5e613b4378945388b9df036a
2024-03-29 00:20:12 +01:00
|
|
|
case UnsetType:
|
|
|
|
return "unset"
|
2016-06-07 21:28:16 +02:00
|
|
|
default:
|
2024-06-13 01:26:54 +02:00
|
|
|
panic(fmt.Sprintf("Unknown type %d", t))
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type Operator struct {
|
|
|
|
Args [2]Expression
|
|
|
|
Operator rune
|
|
|
|
OperatorPos scanner.Position
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Operator) Copy() Expression {
|
|
|
|
ret := *x
|
|
|
|
ret.Args[0] = x.Args[0].Copy()
|
|
|
|
ret.Args[1] = x.Args[1].Copy()
|
|
|
|
return &ret
|
|
|
|
}
|
|
|
|
|
2024-06-13 01:26:54 +02:00
|
|
|
func (x *Operator) Type() Type {
|
|
|
|
t1 := x.Args[0].Type()
|
|
|
|
t2 := x.Args[1].Type()
|
|
|
|
if t1 == UnknownType {
|
|
|
|
return t2
|
|
|
|
}
|
|
|
|
if t2 == UnknownType {
|
|
|
|
return t1
|
|
|
|
}
|
|
|
|
if t1 != t2 {
|
|
|
|
return UnknownType
|
|
|
|
}
|
|
|
|
return t1
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Operator) Eval(scope *Scope) (Expression, error) {
|
|
|
|
return evaluateOperator(scope, x.Operator, x.Args[0], x.Args[1])
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
|
|
|
|
2024-06-13 01:26:54 +02:00
|
|
|
func evaluateOperator(scope *Scope, operator rune, left, right Expression) (Expression, error) {
|
|
|
|
if operator != '+' {
|
|
|
|
return nil, fmt.Errorf("unknown operator %c", operator)
|
|
|
|
}
|
|
|
|
l, err := left.Eval(scope)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
r, err := right.Eval(scope)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := l.(*Select); !ok {
|
|
|
|
if _, ok := r.(*Select); ok {
|
|
|
|
// Promote l to a select so we can add r to it
|
|
|
|
l = &Select{
|
|
|
|
Cases: []*SelectCase{{
|
|
|
|
Value: l,
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
l = l.Copy()
|
|
|
|
|
|
|
|
switch v := l.(type) {
|
|
|
|
case *String:
|
|
|
|
if _, ok := r.(*String); !ok {
|
|
|
|
fmt.Fprintf(os.Stderr, "not ok")
|
|
|
|
}
|
|
|
|
v.Value += r.(*String).Value
|
|
|
|
case *Int64:
|
|
|
|
v.Value += r.(*Int64).Value
|
|
|
|
v.Token = ""
|
|
|
|
case *List:
|
|
|
|
v.Values = append(v.Values, r.(*List).Values...)
|
|
|
|
case *Map:
|
|
|
|
var err error
|
|
|
|
v.Properties, err = addMaps(scope, v.Properties, r.(*Map).Properties)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
case *Select:
|
|
|
|
v.Append = r
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("operator %c not supported on %v", operator, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
return l, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func addMaps(scope *Scope, map1, map2 []*Property) ([]*Property, error) {
|
|
|
|
ret := make([]*Property, 0, len(map1))
|
|
|
|
|
|
|
|
inMap1 := make(map[string]*Property)
|
|
|
|
inMap2 := make(map[string]*Property)
|
|
|
|
inBoth := make(map[string]*Property)
|
|
|
|
|
|
|
|
for _, prop1 := range map1 {
|
|
|
|
inMap1[prop1.Name] = prop1
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, prop2 := range map2 {
|
|
|
|
inMap2[prop2.Name] = prop2
|
|
|
|
if _, ok := inMap1[prop2.Name]; ok {
|
|
|
|
inBoth[prop2.Name] = prop2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, prop1 := range map1 {
|
|
|
|
if prop2, ok := inBoth[prop1.Name]; ok {
|
|
|
|
var err error
|
|
|
|
newProp := *prop1
|
|
|
|
newProp.Value, err = evaluateOperator(scope, '+', prop1.Value, prop2.Value)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ret = append(ret, &newProp)
|
|
|
|
} else {
|
|
|
|
ret = append(ret, prop1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, prop2 := range map2 {
|
|
|
|
if _, ok := inBoth[prop2.Name]; !ok {
|
|
|
|
ret = append(ret, prop2)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Operator) PrintfInto(value string) error {
|
|
|
|
if err := x.Args[0].PrintfInto(value); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return x.Args[1].PrintfInto(value)
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Operator) Pos() scanner.Position { return x.Args[0].Pos() }
|
|
|
|
func (x *Operator) End() scanner.Position { return x.Args[1].End() }
|
|
|
|
|
|
|
|
func (x *Operator) String() string {
|
2024-06-13 01:26:54 +02:00
|
|
|
return fmt.Sprintf("(%s %c %s)@%s", x.Args[0].String(), x.Operator, x.Args[1].String(),
|
|
|
|
x.OperatorPos)
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type Variable struct {
|
|
|
|
Name string
|
|
|
|
NamePos scanner.Position
|
2024-06-13 01:26:54 +02:00
|
|
|
Type_ Type
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Variable) Pos() scanner.Position { return x.NamePos }
|
2018-03-22 01:00:39 +01:00
|
|
|
func (x *Variable) End() scanner.Position { return endPos(x.NamePos, len(x.Name)) }
|
2016-06-07 21:28:16 +02:00
|
|
|
|
|
|
|
func (x *Variable) Copy() Expression {
|
|
|
|
ret := *x
|
|
|
|
return &ret
|
|
|
|
}
|
|
|
|
|
2024-06-13 01:26:54 +02:00
|
|
|
func (x *Variable) Eval(scope *Scope) (Expression, error) {
|
|
|
|
if assignment := scope.Get(x.Name); assignment != nil {
|
|
|
|
assignment.Referenced = true
|
|
|
|
return assignment.Value, nil
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("undefined variable %s", x.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Variable) PrintfInto(value string) error {
|
|
|
|
return nil
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Variable) String() string {
|
2024-06-13 01:26:54 +02:00
|
|
|
return x.Name
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
|
|
|
|
2024-06-13 01:26:54 +02:00
|
|
|
func (x *Variable) Type() Type {
|
|
|
|
// Variables do not normally have a type associated with them, this is only
|
|
|
|
// filled out in the androidmk tool
|
|
|
|
return x.Type_
|
|
|
|
}
|
2016-06-07 21:28:16 +02:00
|
|
|
|
|
|
|
type Map struct {
|
|
|
|
LBracePos scanner.Position
|
|
|
|
RBracePos scanner.Position
|
|
|
|
Properties []*Property
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Map) Pos() scanner.Position { return x.LBracePos }
|
2018-03-22 01:00:39 +01:00
|
|
|
func (x *Map) End() scanner.Position { return endPos(x.RBracePos, 1) }
|
2016-06-07 21:28:16 +02:00
|
|
|
|
|
|
|
func (x *Map) Copy() Expression {
|
|
|
|
ret := *x
|
|
|
|
ret.Properties = make([]*Property, len(x.Properties))
|
|
|
|
for i := range x.Properties {
|
|
|
|
ret.Properties[i] = x.Properties[i].Copy()
|
|
|
|
}
|
|
|
|
return &ret
|
|
|
|
}
|
|
|
|
|
2024-06-13 01:26:54 +02:00
|
|
|
func (x *Map) Eval(scope *Scope) (Expression, error) {
|
|
|
|
newProps := make([]*Property, len(x.Properties))
|
|
|
|
for i, prop := range x.Properties {
|
|
|
|
newVal, err := prop.Value.Eval(scope)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
newProps[i] = &Property{
|
|
|
|
Name: prop.Name,
|
|
|
|
NamePos: prop.NamePos,
|
|
|
|
ColonPos: prop.ColonPos,
|
|
|
|
Value: newVal,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return &Map{
|
|
|
|
LBracePos: x.LBracePos,
|
|
|
|
RBracePos: x.RBracePos,
|
|
|
|
Properties: newProps,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Map) PrintfInto(value string) error {
|
|
|
|
// We should never reach this because selects cannot hold maps
|
|
|
|
panic("printfinto() is unsupported on maps")
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Map) String() string {
|
2022-08-02 16:51:08 +02:00
|
|
|
propertyStrings := make([]string, len(x.Properties))
|
|
|
|
for i, property := range x.Properties {
|
|
|
|
propertyStrings[i] = property.String()
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
2022-08-02 16:51:08 +02:00
|
|
|
return fmt.Sprintf("@%s-%s{%s}", x.LBracePos, x.RBracePos,
|
|
|
|
strings.Join(propertyStrings, ", "))
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Map) Type() Type { return MapType }
|
|
|
|
|
2017-05-17 02:39:37 +02:00
|
|
|
// GetProperty looks for a property with the given name.
|
|
|
|
// It resembles the bracket operator of a built-in Golang map.
|
|
|
|
func (x *Map) GetProperty(name string) (Property *Property, found bool) {
|
|
|
|
prop, found, _ := x.getPropertyImpl(name)
|
|
|
|
return prop, found // we don't currently expose the index to callers
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Map) getPropertyImpl(name string) (Property *Property, found bool, index int) {
|
|
|
|
for i, prop := range x.Properties {
|
|
|
|
if prop.Name == name {
|
|
|
|
return prop, true, i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, false, -1
|
|
|
|
}
|
|
|
|
|
2022-01-11 04:46:23 +01:00
|
|
|
// RemoveProperty removes the property with the given name, if it exists.
|
2017-05-17 02:39:37 +02:00
|
|
|
func (x *Map) RemoveProperty(propertyName string) (removed bool) {
|
|
|
|
_, found, index := x.getPropertyImpl(propertyName)
|
|
|
|
if found {
|
|
|
|
x.Properties = append(x.Properties[:index], x.Properties[index+1:]...)
|
|
|
|
}
|
|
|
|
return found
|
|
|
|
}
|
|
|
|
|
2022-05-11 18:40:26 +02:00
|
|
|
// MovePropertyContents moves the contents of propertyName into property newLocation
|
|
|
|
// If property newLocation doesn't exist, MovePropertyContents renames propertyName as newLocation.
|
|
|
|
// Otherwise, MovePropertyContents only supports moving contents that are a List of String.
|
|
|
|
func (x *Map) MovePropertyContents(propertyName string, newLocation string) (removed bool) {
|
|
|
|
oldProp, oldFound, _ := x.getPropertyImpl(propertyName)
|
|
|
|
newProp, newFound, _ := x.getPropertyImpl(newLocation)
|
|
|
|
|
|
|
|
// newLoc doesn't exist, simply renaming property
|
|
|
|
if oldFound && !newFound {
|
|
|
|
oldProp.Name = newLocation
|
|
|
|
return oldFound
|
|
|
|
}
|
|
|
|
|
|
|
|
if oldFound {
|
|
|
|
old, oldOk := oldProp.Value.(*List)
|
|
|
|
new, newOk := newProp.Value.(*List)
|
|
|
|
if oldOk && newOk {
|
|
|
|
toBeMoved := make([]string, len(old.Values)) //
|
|
|
|
for i, p := range old.Values {
|
|
|
|
toBeMoved[i] = p.(*String).Value
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, moved := range toBeMoved {
|
|
|
|
RemoveStringFromList(old, moved)
|
|
|
|
AddStringToList(new, moved)
|
|
|
|
}
|
|
|
|
// oldProp should now be empty and needs to be deleted
|
|
|
|
x.RemoveProperty(oldProp.Name)
|
|
|
|
} else {
|
|
|
|
print(`MovePropertyContents currently only supports moving PropertyName
|
|
|
|
with List of Strings into an existing newLocation with List of Strings\n`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return oldFound
|
|
|
|
}
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
type List struct {
|
|
|
|
LBracePos scanner.Position
|
|
|
|
RBracePos scanner.Position
|
|
|
|
Values []Expression
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *List) Pos() scanner.Position { return x.LBracePos }
|
2018-03-22 01:00:39 +01:00
|
|
|
func (x *List) End() scanner.Position { return endPos(x.RBracePos, 1) }
|
2016-06-07 21:28:16 +02:00
|
|
|
|
|
|
|
func (x *List) Copy() Expression {
|
|
|
|
ret := *x
|
|
|
|
ret.Values = make([]Expression, len(x.Values))
|
|
|
|
for i := range ret.Values {
|
|
|
|
ret.Values[i] = x.Values[i].Copy()
|
|
|
|
}
|
|
|
|
return &ret
|
|
|
|
}
|
|
|
|
|
2024-06-13 01:26:54 +02:00
|
|
|
func (x *List) Eval(scope *Scope) (Expression, error) {
|
|
|
|
newValues := make([]Expression, len(x.Values))
|
|
|
|
for i, val := range x.Values {
|
|
|
|
newVal, err := val.Eval(scope)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
newValues[i] = newVal
|
|
|
|
}
|
|
|
|
return &List{
|
|
|
|
LBracePos: x.LBracePos,
|
|
|
|
RBracePos: x.RBracePos,
|
|
|
|
Values: newValues,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *List) PrintfInto(value string) error {
|
|
|
|
for _, val := range x.Values {
|
|
|
|
if err := val.PrintfInto(value); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (x *List) String() string {
|
|
|
|
valueStrings := make([]string, len(x.Values))
|
|
|
|
for i, value := range x.Values {
|
|
|
|
valueStrings[i] = value.String()
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("@%s-%s[%s]", x.LBracePos, x.RBracePos,
|
|
|
|
strings.Join(valueStrings, ", "))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *List) Type() Type { return ListType }
|
|
|
|
|
|
|
|
type String struct {
|
|
|
|
LiteralPos scanner.Position
|
|
|
|
Value string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *String) Pos() scanner.Position { return x.LiteralPos }
|
2018-03-22 01:00:39 +01:00
|
|
|
func (x *String) End() scanner.Position { return endPos(x.LiteralPos, len(x.Value)+2) }
|
2016-06-07 21:28:16 +02:00
|
|
|
|
|
|
|
func (x *String) Copy() Expression {
|
|
|
|
ret := *x
|
|
|
|
return &ret
|
|
|
|
}
|
|
|
|
|
2024-06-13 01:26:54 +02:00
|
|
|
func (x *String) Eval(scope *Scope) (Expression, error) {
|
|
|
|
return x, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *String) PrintfInto(value string) error {
|
|
|
|
count := strings.Count(x.Value, "%")
|
|
|
|
if count == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if count > 1 {
|
|
|
|
return fmt.Errorf("list/value variable properties only support a single '%%'")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !strings.Contains(x.Value, "%s") {
|
|
|
|
return fmt.Errorf("unsupported %% in value variable property")
|
|
|
|
}
|
|
|
|
|
|
|
|
x.Value = fmt.Sprintf(x.Value, value)
|
|
|
|
return nil
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (x *String) String() string {
|
|
|
|
return fmt.Sprintf("%q@%s", x.Value, x.LiteralPos)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *String) Type() Type {
|
|
|
|
return StringType
|
|
|
|
}
|
|
|
|
|
Support parsing int64 in Blueprint file.
Support int64 number instead of int to be more fixed to bit size so
that the underlying arch won't affect overflow cases. Besides,
refection: func (v Value) Int() int64 always cast to int64 no matter the
input is int, int16, int32. Currently we always treat "-" as negative
sign to bind to next value, and "+" as plus operator to add operands
together.
So we allow:
a = 5 + -4 + 5 or a = -4 + 5
But we don't allow:
a = +5 + 4 + -4 since we don't treat "+" as a positive sign, otherwise,
a = 5 + +5 would exist which looks pretty weird. In the future, we may
want fully support number calculator logic eg, "+"/"-" can be
positive/negative sign or operator, and "(" and ")" will be considered
to group expressions with a higher precedence.
int & uint properties within struct keeps unchanged, which is only
allowed when tagged with 'blueprint:mutated'. We only allow *int64
property instead of int64 property within struct since it does't make
sense to do prepending or appending to int64.
Change-Id: I565e046dbd268af3538aee148cd7300037e56523
2017-11-01 22:03:28 +01:00
|
|
|
type Int64 struct {
|
|
|
|
LiteralPos scanner.Position
|
|
|
|
Value int64
|
2018-03-22 01:00:39 +01:00
|
|
|
Token string
|
Support parsing int64 in Blueprint file.
Support int64 number instead of int to be more fixed to bit size so
that the underlying arch won't affect overflow cases. Besides,
refection: func (v Value) Int() int64 always cast to int64 no matter the
input is int, int16, int32. Currently we always treat "-" as negative
sign to bind to next value, and "+" as plus operator to add operands
together.
So we allow:
a = 5 + -4 + 5 or a = -4 + 5
But we don't allow:
a = +5 + 4 + -4 since we don't treat "+" as a positive sign, otherwise,
a = 5 + +5 would exist which looks pretty weird. In the future, we may
want fully support number calculator logic eg, "+"/"-" can be
positive/negative sign or operator, and "(" and ")" will be considered
to group expressions with a higher precedence.
int & uint properties within struct keeps unchanged, which is only
allowed when tagged with 'blueprint:mutated'. We only allow *int64
property instead of int64 property within struct since it does't make
sense to do prepending or appending to int64.
Change-Id: I565e046dbd268af3538aee148cd7300037e56523
2017-11-01 22:03:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Int64) Pos() scanner.Position { return x.LiteralPos }
|
2018-03-22 01:00:39 +01:00
|
|
|
func (x *Int64) End() scanner.Position { return endPos(x.LiteralPos, len(x.Token)) }
|
Support parsing int64 in Blueprint file.
Support int64 number instead of int to be more fixed to bit size so
that the underlying arch won't affect overflow cases. Besides,
refection: func (v Value) Int() int64 always cast to int64 no matter the
input is int, int16, int32. Currently we always treat "-" as negative
sign to bind to next value, and "+" as plus operator to add operands
together.
So we allow:
a = 5 + -4 + 5 or a = -4 + 5
But we don't allow:
a = +5 + 4 + -4 since we don't treat "+" as a positive sign, otherwise,
a = 5 + +5 would exist which looks pretty weird. In the future, we may
want fully support number calculator logic eg, "+"/"-" can be
positive/negative sign or operator, and "(" and ")" will be considered
to group expressions with a higher precedence.
int & uint properties within struct keeps unchanged, which is only
allowed when tagged with 'blueprint:mutated'. We only allow *int64
property instead of int64 property within struct since it does't make
sense to do prepending or appending to int64.
Change-Id: I565e046dbd268af3538aee148cd7300037e56523
2017-11-01 22:03:28 +01:00
|
|
|
|
|
|
|
func (x *Int64) Copy() Expression {
|
|
|
|
ret := *x
|
|
|
|
return &ret
|
|
|
|
}
|
|
|
|
|
2024-06-13 01:26:54 +02:00
|
|
|
func (x *Int64) Eval(scope *Scope) (Expression, error) {
|
|
|
|
return x, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Int64) PrintfInto(value string) error {
|
|
|
|
return nil
|
Support parsing int64 in Blueprint file.
Support int64 number instead of int to be more fixed to bit size so
that the underlying arch won't affect overflow cases. Besides,
refection: func (v Value) Int() int64 always cast to int64 no matter the
input is int, int16, int32. Currently we always treat "-" as negative
sign to bind to next value, and "+" as plus operator to add operands
together.
So we allow:
a = 5 + -4 + 5 or a = -4 + 5
But we don't allow:
a = +5 + 4 + -4 since we don't treat "+" as a positive sign, otherwise,
a = 5 + +5 would exist which looks pretty weird. In the future, we may
want fully support number calculator logic eg, "+"/"-" can be
positive/negative sign or operator, and "(" and ")" will be considered
to group expressions with a higher precedence.
int & uint properties within struct keeps unchanged, which is only
allowed when tagged with 'blueprint:mutated'. We only allow *int64
property instead of int64 property within struct since it does't make
sense to do prepending or appending to int64.
Change-Id: I565e046dbd268af3538aee148cd7300037e56523
2017-11-01 22:03:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Int64) String() string {
|
|
|
|
return fmt.Sprintf("%q@%s", x.Value, x.LiteralPos)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Int64) Type() Type {
|
|
|
|
return Int64Type
|
|
|
|
}
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
type Bool struct {
|
|
|
|
LiteralPos scanner.Position
|
|
|
|
Value bool
|
2018-03-22 01:00:39 +01:00
|
|
|
Token string
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Bool) Pos() scanner.Position { return x.LiteralPos }
|
2018-03-22 01:00:39 +01:00
|
|
|
func (x *Bool) End() scanner.Position { return endPos(x.LiteralPos, len(x.Token)) }
|
2016-06-07 21:28:16 +02:00
|
|
|
|
|
|
|
func (x *Bool) Copy() Expression {
|
|
|
|
ret := *x
|
|
|
|
return &ret
|
|
|
|
}
|
|
|
|
|
2024-06-13 01:26:54 +02:00
|
|
|
func (x *Bool) Eval(scope *Scope) (Expression, error) {
|
|
|
|
return x, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Bool) PrintfInto(value string) error {
|
|
|
|
return nil
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Bool) String() string {
|
|
|
|
return fmt.Sprintf("%t@%s", x.Value, x.LiteralPos)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Bool) Type() Type {
|
|
|
|
return BoolType
|
|
|
|
}
|
|
|
|
|
2016-06-11 02:27:12 +02:00
|
|
|
type CommentGroup struct {
|
|
|
|
Comments []*Comment
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *CommentGroup) Pos() scanner.Position { return x.Comments[0].Pos() }
|
|
|
|
func (x *CommentGroup) End() scanner.Position { return x.Comments[len(x.Comments)-1].End() }
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
type Comment struct {
|
|
|
|
Comment []string
|
|
|
|
Slash scanner.Position
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Comment) Pos() scanner.Position {
|
|
|
|
return c.Slash
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Comment) End() scanner.Position {
|
|
|
|
pos := c.Slash
|
|
|
|
for _, comment := range c.Comment {
|
2018-03-22 01:00:39 +01:00
|
|
|
pos.Offset += len(comment) + 1
|
|
|
|
pos.Column = len(comment) + 1
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
|
|
|
pos.Line += len(c.Comment) - 1
|
|
|
|
return pos
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c Comment) String() string {
|
|
|
|
l := 0
|
|
|
|
for _, comment := range c.Comment {
|
|
|
|
l += len(comment) + 1
|
|
|
|
}
|
|
|
|
buf := make([]byte, 0, l)
|
|
|
|
for _, comment := range c.Comment {
|
|
|
|
buf = append(buf, comment...)
|
|
|
|
buf = append(buf, '\n')
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(buf) + "@" + c.Slash.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the text of the comment with // or /* and */ stripped
|
|
|
|
func (c Comment) Text() string {
|
|
|
|
l := 0
|
|
|
|
for _, comment := range c.Comment {
|
|
|
|
l += len(comment) + 1
|
|
|
|
}
|
|
|
|
buf := make([]byte, 0, l)
|
|
|
|
|
|
|
|
blockComment := false
|
|
|
|
if strings.HasPrefix(c.Comment[0], "/*") {
|
|
|
|
blockComment = true
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, comment := range c.Comment {
|
|
|
|
if blockComment {
|
|
|
|
if i == 0 {
|
|
|
|
comment = strings.TrimPrefix(comment, "/*")
|
|
|
|
}
|
|
|
|
if i == len(c.Comment)-1 {
|
|
|
|
comment = strings.TrimSuffix(comment, "*/")
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
comment = strings.TrimPrefix(comment, "//")
|
|
|
|
}
|
|
|
|
buf = append(buf, comment...)
|
|
|
|
buf = append(buf, '\n')
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(buf)
|
|
|
|
}
|
2018-03-22 01:00:39 +01:00
|
|
|
|
|
|
|
func endPos(pos scanner.Position, n int) scanner.Position {
|
|
|
|
pos.Offset += n
|
|
|
|
pos.Column += n
|
|
|
|
return pos
|
|
|
|
}
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
|
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
|
|
|
type ConfigurableCondition struct {
|
|
|
|
position scanner.Position
|
|
|
|
FunctionName string
|
|
|
|
Args []String
|
|
|
|
}
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
|
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
|
|
|
func (c *ConfigurableCondition) Equals(other ConfigurableCondition) bool {
|
|
|
|
if c.FunctionName != other.FunctionName {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if len(c.Args) != len(other.Args) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for i := range c.Args {
|
|
|
|
if c.Args[i] != other.Args[i] {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ConfigurableCondition) String() string {
|
|
|
|
var sb strings.Builder
|
|
|
|
sb.WriteString(c.FunctionName)
|
|
|
|
sb.WriteRune('(')
|
|
|
|
for i, arg := range c.Args {
|
|
|
|
sb.WriteRune('"')
|
|
|
|
sb.WriteString(arg.Value)
|
|
|
|
sb.WriteRune('"')
|
|
|
|
if i < len(c.Args)-1 {
|
|
|
|
sb.WriteString(", ")
|
|
|
|
}
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
}
|
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
|
|
|
sb.WriteRune(')')
|
|
|
|
return sb.String()
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type Select struct {
|
2024-06-13 01:26:54 +02:00
|
|
|
Scope *Scope // scope used to evaluate the body of the select later on
|
|
|
|
KeywordPos scanner.Position // the keyword "select"
|
|
|
|
Conditions []ConfigurableCondition
|
|
|
|
LBracePos scanner.Position
|
|
|
|
RBracePos scanner.Position
|
|
|
|
Cases []*SelectCase // the case statements
|
|
|
|
Append Expression
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Select) Pos() scanner.Position { return s.KeywordPos }
|
|
|
|
func (s *Select) End() scanner.Position { return endPos(s.RBracePos, 1) }
|
|
|
|
|
|
|
|
func (s *Select) Copy() Expression {
|
|
|
|
ret := *s
|
|
|
|
ret.Cases = make([]*SelectCase, len(ret.Cases))
|
|
|
|
for i, selectCase := range s.Cases {
|
|
|
|
ret.Cases[i] = selectCase.Copy()
|
|
|
|
}
|
|
|
|
if s.Append != nil {
|
|
|
|
ret.Append = s.Append.Copy()
|
|
|
|
}
|
|
|
|
return &ret
|
|
|
|
}
|
|
|
|
|
2024-06-13 01:26:54 +02:00
|
|
|
func (s *Select) Eval(scope *Scope) (Expression, error) {
|
|
|
|
s.Scope = scope
|
|
|
|
return s, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *Select) PrintfInto(value string) error {
|
|
|
|
// PrintfInto will be handled at the Configurable object level
|
|
|
|
panic("Cannot call PrintfInto on a select expression")
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Select) String() string {
|
|
|
|
return "<select>"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Select) Type() Type {
|
2024-06-13 01:26:54 +02:00
|
|
|
if len(s.Cases) == 0 {
|
|
|
|
return UnsetType
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
}
|
2024-06-13 01:26:54 +02:00
|
|
|
return UnknownType
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type SelectCase struct {
|
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
|
|
|
Patterns []Expression
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
ColonPos scanner.Position
|
|
|
|
Value Expression
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *SelectCase) Copy() *SelectCase {
|
|
|
|
ret := *c
|
|
|
|
ret.Value = c.Value.Copy()
|
|
|
|
return &ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *SelectCase) String() string {
|
|
|
|
return "<select case>"
|
|
|
|
}
|
|
|
|
|
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
|
|
|
func (c *SelectCase) Pos() scanner.Position { return c.Patterns[0].Pos() }
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
func (c *SelectCase) End() scanner.Position { return c.Value.End() }
|
Add support for unset select branches
Currently, with the arch/os mutator, you can override a property
using the default value for just a few arch types, for example:
cc_defaults {
name: "my_defaults",
target: {
windows: {
enabled: true,
}
}
}
cc_binary {
name: "foo",
enabled: false,
defaults: ["my_defaults"],
}
You could make a select statment that acts like the above if it were
all in one module, but currently with select statements you can't make
a defaults module that can be generically applied to any other module
and have the same behavior as the above.
After this cl, the defaults module could look like:
cc_defaults {
name: "my_defaults",
enabled: select(variant("arch"), {
"windows": true,
_: unset,
}),
}
Which would have the same behavior. Unset may also be useful for
setting the property under some configurations, but wanting to leave
the implementation-specific default value in others.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: I3ea3277ea8b9a0ac5e613b4378945388b9df036a
2024-03-29 00:20:12 +01:00
|
|
|
|
|
|
|
// UnsetProperty is the expression type of the "unset" keyword that can be
|
|
|
|
// used in select statements to make the property unset. For example:
|
|
|
|
//
|
|
|
|
// my_module_type {
|
|
|
|
// name: "foo",
|
|
|
|
// some_prop: select(soong_config_variable("my_namespace", "my_var"), {
|
|
|
|
// "foo": unset,
|
|
|
|
// "default": "bar",
|
|
|
|
// })
|
|
|
|
// }
|
|
|
|
type UnsetProperty struct {
|
|
|
|
Position scanner.Position
|
|
|
|
}
|
|
|
|
|
2024-06-13 01:26:54 +02:00
|
|
|
func (n *UnsetProperty) Copy() Expression {
|
|
|
|
return &UnsetProperty{Position: n.Position}
|
Add support for unset select branches
Currently, with the arch/os mutator, you can override a property
using the default value for just a few arch types, for example:
cc_defaults {
name: "my_defaults",
target: {
windows: {
enabled: true,
}
}
}
cc_binary {
name: "foo",
enabled: false,
defaults: ["my_defaults"],
}
You could make a select statment that acts like the above if it were
all in one module, but currently with select statements you can't make
a defaults module that can be generically applied to any other module
and have the same behavior as the above.
After this cl, the defaults module could look like:
cc_defaults {
name: "my_defaults",
enabled: select(variant("arch"), {
"windows": true,
_: unset,
}),
}
Which would have the same behavior. Unset may also be useful for
setting the property under some configurations, but wanting to leave
the implementation-specific default value in others.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: I3ea3277ea8b9a0ac5e613b4378945388b9df036a
2024-03-29 00:20:12 +01:00
|
|
|
}
|
|
|
|
|
2024-06-13 01:26:54 +02:00
|
|
|
func (n *UnsetProperty) String() string {
|
Add support for unset select branches
Currently, with the arch/os mutator, you can override a property
using the default value for just a few arch types, for example:
cc_defaults {
name: "my_defaults",
target: {
windows: {
enabled: true,
}
}
}
cc_binary {
name: "foo",
enabled: false,
defaults: ["my_defaults"],
}
You could make a select statment that acts like the above if it were
all in one module, but currently with select statements you can't make
a defaults module that can be generically applied to any other module
and have the same behavior as the above.
After this cl, the defaults module could look like:
cc_defaults {
name: "my_defaults",
enabled: select(variant("arch"), {
"windows": true,
_: unset,
}),
}
Which would have the same behavior. Unset may also be useful for
setting the property under some configurations, but wanting to leave
the implementation-specific default value in others.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: I3ea3277ea8b9a0ac5e613b4378945388b9df036a
2024-03-29 00:20:12 +01:00
|
|
|
return "unset"
|
|
|
|
}
|
|
|
|
|
2024-06-13 01:26:54 +02:00
|
|
|
func (n *UnsetProperty) Type() Type {
|
Add support for unset select branches
Currently, with the arch/os mutator, you can override a property
using the default value for just a few arch types, for example:
cc_defaults {
name: "my_defaults",
target: {
windows: {
enabled: true,
}
}
}
cc_binary {
name: "foo",
enabled: false,
defaults: ["my_defaults"],
}
You could make a select statment that acts like the above if it were
all in one module, but currently with select statements you can't make
a defaults module that can be generically applied to any other module
and have the same behavior as the above.
After this cl, the defaults module could look like:
cc_defaults {
name: "my_defaults",
enabled: select(variant("arch"), {
"windows": true,
_: unset,
}),
}
Which would have the same behavior. Unset may also be useful for
setting the property under some configurations, but wanting to leave
the implementation-specific default value in others.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: I3ea3277ea8b9a0ac5e613b4378945388b9df036a
2024-03-29 00:20:12 +01:00
|
|
|
return UnsetType
|
|
|
|
}
|
|
|
|
|
2024-06-13 01:26:54 +02:00
|
|
|
func (n *UnsetProperty) Eval(scope *Scope) (Expression, error) {
|
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *UnsetProperty) PrintfInto(value string) error {
|
|
|
|
return nil
|
Add support for unset select branches
Currently, with the arch/os mutator, you can override a property
using the default value for just a few arch types, for example:
cc_defaults {
name: "my_defaults",
target: {
windows: {
enabled: true,
}
}
}
cc_binary {
name: "foo",
enabled: false,
defaults: ["my_defaults"],
}
You could make a select statment that acts like the above if it were
all in one module, but currently with select statements you can't make
a defaults module that can be generically applied to any other module
and have the same behavior as the above.
After this cl, the defaults module could look like:
cc_defaults {
name: "my_defaults",
enabled: select(variant("arch"), {
"windows": true,
_: unset,
}),
}
Which would have the same behavior. Unset may also be useful for
setting the property under some configurations, but wanting to leave
the implementation-specific default value in others.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: I3ea3277ea8b9a0ac5e613b4378945388b9df036a
2024-03-29 00:20:12 +01:00
|
|
|
}
|
|
|
|
|
2024-06-13 01:26:54 +02:00
|
|
|
func (n *UnsetProperty) Pos() scanner.Position { return n.Position }
|
|
|
|
func (n *UnsetProperty) End() scanner.Position { return n.Position }
|