2015-01-23 23:15:10 +01:00
|
|
|
// Copyright 2014 Google Inc. All rights reserved.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
package parser
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2015-01-03 00:19:28 +01:00
|
|
|
"sort"
|
2014-05-28 01:34:41 +02:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"text/scanner"
|
|
|
|
)
|
|
|
|
|
|
|
|
var errTooManyErrors = errors.New("too many errors")
|
|
|
|
|
|
|
|
const maxErrors = 1
|
|
|
|
|
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
|
|
|
const default_select_branch_name = "__soong_conditions_default__"
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
type ParseError struct {
|
|
|
|
Err error
|
|
|
|
Pos scanner.Position
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *ParseError) Error() string {
|
|
|
|
return fmt.Sprintf("%s: %s", e.Pos, e.Err)
|
|
|
|
}
|
|
|
|
|
2015-01-08 23:56:03 +01:00
|
|
|
type File struct {
|
2015-07-01 01:05:22 +02:00
|
|
|
Name string
|
2015-01-08 23:56:03 +01:00
|
|
|
Defs []Definition
|
2016-06-11 02:27:12 +02:00
|
|
|
Comments []*CommentGroup
|
2016-06-10 02:40:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (f *File) Pos() scanner.Position {
|
|
|
|
return scanner.Position{
|
|
|
|
Filename: f.Name,
|
|
|
|
Line: 1,
|
|
|
|
Column: 1,
|
|
|
|
Offset: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *File) End() scanner.Position {
|
|
|
|
if len(f.Defs) > 0 {
|
|
|
|
return f.Defs[len(f.Defs)-1].End()
|
|
|
|
}
|
|
|
|
return noPos
|
2015-01-08 23:56:03 +01:00
|
|
|
}
|
|
|
|
|
2015-03-20 01:28:06 +01:00
|
|
|
func parse(p *parser) (file *File, errs []error) {
|
2014-05-28 01:34:41 +02:00
|
|
|
defer func() {
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
if r == errTooManyErrors {
|
|
|
|
errs = p.errors
|
|
|
|
return
|
|
|
|
}
|
|
|
|
panic(r)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2022-10-21 23:36:04 +02:00
|
|
|
p.next()
|
2015-01-08 23:56:03 +01:00
|
|
|
defs := p.parseDefinitions()
|
2014-05-28 01:34:41 +02:00
|
|
|
p.accept(scanner.EOF)
|
|
|
|
errs = p.errors
|
2015-01-08 23:56:03 +01:00
|
|
|
comments := p.comments
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2015-01-08 23:56:03 +01:00
|
|
|
return &File{
|
2015-07-01 01:05:22 +02:00
|
|
|
Name: p.scanner.Filename,
|
2015-01-08 23:56:03 +01:00
|
|
|
Defs: defs,
|
|
|
|
Comments: comments,
|
|
|
|
}, errs
|
2015-03-20 01:28:06 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func ParseAndEval(filename string, r io.Reader, scope *Scope) (file *File, errs []error) {
|
|
|
|
p := newParser(r, scope)
|
|
|
|
p.eval = true
|
|
|
|
p.scanner.Filename = filename
|
|
|
|
|
|
|
|
return parse(p)
|
|
|
|
}
|
|
|
|
|
|
|
|
func Parse(filename string, r io.Reader, scope *Scope) (file *File, errs []error) {
|
|
|
|
p := newParser(r, scope)
|
|
|
|
p.scanner.Filename = filename
|
|
|
|
|
|
|
|
return parse(p)
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2022-02-09 03:16:02 +01:00
|
|
|
func ParseExpression(r io.Reader) (value Expression, errs []error) {
|
|
|
|
p := newParser(r, NewScope(nil))
|
2022-10-21 23:36:04 +02:00
|
|
|
p.next()
|
2022-02-09 03:16:02 +01:00
|
|
|
value = p.parseExpression()
|
|
|
|
p.accept(scanner.EOF)
|
|
|
|
errs = p.errors
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
type parser struct {
|
2015-03-20 01:28:06 +01:00
|
|
|
scanner scanner.Scanner
|
|
|
|
tok rune
|
|
|
|
errors []error
|
|
|
|
scope *Scope
|
2016-06-11 02:27:12 +02:00
|
|
|
comments []*CommentGroup
|
2015-03-20 01:28:06 +01:00
|
|
|
eval bool
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2015-01-03 00:19:28 +01:00
|
|
|
func newParser(r io.Reader, scope *Scope) *parser {
|
2014-05-28 01:34:41 +02:00
|
|
|
p := &parser{}
|
2015-01-03 00:19:28 +01:00
|
|
|
p.scope = scope
|
2014-05-28 01:34:41 +02:00
|
|
|
p.scanner.Init(r)
|
|
|
|
p.scanner.Error = func(sc *scanner.Scanner, msg string) {
|
|
|
|
p.errorf(msg)
|
|
|
|
}
|
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
|
|
|
p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanStrings |
|
2015-01-08 23:56:03 +01:00
|
|
|
scanner.ScanRawStrings | scanner.ScanComments
|
2014-05-28 01:34:41 +02:00
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
2015-07-11 02:51:55 +02:00
|
|
|
func (p *parser) error(err error) {
|
2014-05-28 01:34:41 +02:00
|
|
|
pos := p.scanner.Position
|
|
|
|
if !pos.IsValid() {
|
|
|
|
pos = p.scanner.Pos()
|
|
|
|
}
|
2015-07-11 02:51:55 +02:00
|
|
|
err = &ParseError{
|
|
|
|
Err: err,
|
2014-05-28 01:34:41 +02:00
|
|
|
Pos: pos,
|
|
|
|
}
|
|
|
|
p.errors = append(p.errors, err)
|
|
|
|
if len(p.errors) >= maxErrors {
|
|
|
|
panic(errTooManyErrors)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-11 02:51:55 +02:00
|
|
|
func (p *parser) errorf(format string, args ...interface{}) {
|
|
|
|
p.error(fmt.Errorf(format, args...))
|
|
|
|
}
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
func (p *parser) accept(toks ...rune) bool {
|
|
|
|
for _, tok := range toks {
|
|
|
|
if p.tok != tok {
|
|
|
|
p.errorf("expected %s, found %s", scanner.TokenString(tok),
|
|
|
|
scanner.TokenString(p.tok))
|
|
|
|
return false
|
|
|
|
}
|
2015-01-08 23:56:03 +01:00
|
|
|
p.next()
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *parser) next() {
|
|
|
|
if p.tok != scanner.EOF {
|
|
|
|
p.tok = p.scanner.Scan()
|
2016-06-11 02:27:12 +02:00
|
|
|
if p.tok == scanner.Comment {
|
|
|
|
var comments []*Comment
|
|
|
|
for p.tok == scanner.Comment {
|
|
|
|
lines := strings.Split(p.scanner.TokenText(), "\n")
|
|
|
|
if len(comments) > 0 && p.scanner.Position.Line > comments[len(comments)-1].End().Line+1 {
|
|
|
|
p.comments = append(p.comments, &CommentGroup{Comments: comments})
|
|
|
|
comments = nil
|
|
|
|
}
|
|
|
|
comments = append(comments, &Comment{lines, p.scanner.Position})
|
|
|
|
p.tok = p.scanner.Scan()
|
|
|
|
}
|
|
|
|
p.comments = append(p.comments, &CommentGroup{Comments: comments})
|
2015-01-08 23:56:03 +01:00
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *parser) parseDefinitions() (defs []Definition) {
|
|
|
|
for {
|
|
|
|
switch p.tok {
|
|
|
|
case scanner.Ident:
|
|
|
|
ident := p.scanner.TokenText()
|
|
|
|
pos := p.scanner.Position
|
|
|
|
|
|
|
|
p.accept(scanner.Ident)
|
|
|
|
|
|
|
|
switch p.tok {
|
2015-02-04 19:50:22 +01:00
|
|
|
case '+':
|
|
|
|
p.accept('+')
|
|
|
|
defs = append(defs, p.parseAssignment(ident, pos, "+="))
|
2014-05-28 01:34:41 +02:00
|
|
|
case '=':
|
2015-02-04 19:50:22 +01:00
|
|
|
defs = append(defs, p.parseAssignment(ident, pos, "="))
|
2015-01-15 02:04:13 +01:00
|
|
|
case '{', '(':
|
2014-05-28 01:34:41 +02:00
|
|
|
defs = append(defs, p.parseModule(ident, pos))
|
|
|
|
default:
|
2015-02-04 19:50:22 +01:00
|
|
|
p.errorf("expected \"=\" or \"+=\" or \"{\" or \"(\", found %s",
|
2014-05-28 01:34:41 +02:00
|
|
|
scanner.TokenString(p.tok))
|
|
|
|
}
|
|
|
|
case scanner.EOF:
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
p.errorf("expected assignment or module definition, found %s",
|
|
|
|
scanner.TokenString(p.tok))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
func (p *parser) parseAssignment(name string, namePos scanner.Position,
|
|
|
|
assigner string) (assignment *Assignment) {
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2024-04-03 01:42:58 +02:00
|
|
|
// These are used as keywords in select statements, prevent making variables
|
|
|
|
// with the same name to avoid any confusion.
|
|
|
|
switch name {
|
|
|
|
case "default", "unset":
|
|
|
|
p.errorf("'default' and 'unset' are reserved keywords, and cannot be used as variable names")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
assignment = new(Assignment)
|
|
|
|
|
2015-01-08 23:56:03 +01:00
|
|
|
pos := p.scanner.Position
|
2014-05-28 01:34:41 +02:00
|
|
|
if !p.accept('=') {
|
|
|
|
return
|
|
|
|
}
|
2015-01-03 00:47:54 +01:00
|
|
|
value := p.parseExpression()
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2016-06-10 00:52:30 +02:00
|
|
|
assignment.Name = name
|
|
|
|
assignment.NamePos = namePos
|
2014-05-28 01:34:41 +02:00
|
|
|
assignment.Value = value
|
2015-02-04 19:50:22 +01:00
|
|
|
assignment.OrigValue = value
|
2016-06-10 02:03:57 +02:00
|
|
|
assignment.EqualsPos = pos
|
2015-02-04 19:50:22 +01:00
|
|
|
assignment.Assigner = assigner
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2015-01-03 00:19:28 +01:00
|
|
|
if p.scope != nil {
|
2015-02-04 19:50:22 +01:00
|
|
|
if assigner == "+=" {
|
2016-06-10 00:52:30 +02:00
|
|
|
if old, local := p.scope.Get(assignment.Name); old == nil {
|
|
|
|
p.errorf("modified non-existent variable %q with +=", assignment.Name)
|
2015-07-11 02:51:55 +02:00
|
|
|
} else if !local {
|
2016-06-10 00:52:30 +02:00
|
|
|
p.errorf("modified non-local variable %q with +=", assignment.Name)
|
2015-07-11 02:51:55 +02:00
|
|
|
} else if old.Referenced {
|
2016-06-10 00:52:30 +02:00
|
|
|
p.errorf("modified variable %q with += after referencing", assignment.Name)
|
2015-07-11 02:51:55 +02:00
|
|
|
} else {
|
2016-06-10 02:03:57 +02:00
|
|
|
val, err := p.evaluateOperator(old.Value, assignment.Value, '+', assignment.EqualsPos)
|
2015-07-11 02:51:55 +02:00
|
|
|
if err != nil {
|
|
|
|
p.error(err)
|
|
|
|
} else {
|
|
|
|
old.Value = val
|
2015-03-20 01:28:06 +01:00
|
|
|
}
|
2015-02-04 19:50:22 +01:00
|
|
|
}
|
2015-07-11 02:51:55 +02:00
|
|
|
} else {
|
|
|
|
err := p.scope.Add(assignment)
|
|
|
|
if err != nil {
|
|
|
|
p.error(err)
|
|
|
|
}
|
2015-03-20 01:28:06 +01:00
|
|
|
}
|
2015-01-03 00:19:28 +01:00
|
|
|
}
|
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
func (p *parser) parseModule(typ string, typPos scanner.Position) *Module {
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2015-01-15 02:04:13 +01:00
|
|
|
compat := false
|
2015-01-08 23:56:03 +01:00
|
|
|
lbracePos := p.scanner.Position
|
2015-01-15 02:04:13 +01:00
|
|
|
if p.tok == '{' {
|
|
|
|
compat = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if !p.accept(p.tok) {
|
2016-06-07 21:28:16 +02:00
|
|
|
return nil
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
2015-01-15 02:04:13 +01:00
|
|
|
properties := p.parsePropertyList(true, compat)
|
2015-01-08 23:56:03 +01:00
|
|
|
rbracePos := p.scanner.Position
|
2015-01-15 02:04:13 +01:00
|
|
|
if !compat {
|
|
|
|
p.accept(')')
|
|
|
|
} else {
|
|
|
|
p.accept('}')
|
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
return &Module{
|
2016-06-10 00:52:30 +02:00
|
|
|
Type: typ,
|
|
|
|
TypePos: typPos,
|
2016-06-07 21:28:16 +02:00
|
|
|
Map: Map{
|
|
|
|
Properties: properties,
|
|
|
|
LBracePos: lbracePos,
|
|
|
|
RBracePos: rbracePos,
|
|
|
|
},
|
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2015-01-15 02:04:13 +01:00
|
|
|
func (p *parser) parsePropertyList(isModule, compat bool) (properties []*Property) {
|
2014-05-28 01:34:41 +02:00
|
|
|
for p.tok == scanner.Ident {
|
2015-01-15 02:04:13 +01:00
|
|
|
property := p.parseProperty(isModule, compat)
|
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
|
|
|
|
|
|
|
// If a property is set to an empty select or a select where all branches are "unset",
|
|
|
|
// skip emitting the property entirely.
|
|
|
|
if property.Value.Type() != UnsetType {
|
|
|
|
properties = append(properties, property)
|
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
|
|
|
|
if p.tok != ',' {
|
|
|
|
// There was no comma, so the list is done.
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
p.accept(',')
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-01-15 02:04:13 +01:00
|
|
|
func (p *parser) parseProperty(isModule, compat bool) (property *Property) {
|
2014-05-28 01:34:41 +02:00
|
|
|
property = new(Property)
|
|
|
|
|
|
|
|
name := p.scanner.TokenText()
|
2015-01-08 23:56:03 +01:00
|
|
|
namePos := p.scanner.Position
|
|
|
|
p.accept(scanner.Ident)
|
2014-05-28 01:34:41 +02:00
|
|
|
pos := p.scanner.Position
|
2015-01-15 02:04:13 +01:00
|
|
|
|
|
|
|
if isModule {
|
2018-06-25 05:52:48 +02:00
|
|
|
if compat {
|
|
|
|
if !p.accept(':') {
|
|
|
|
return
|
|
|
|
}
|
2015-01-15 02:04:13 +01:00
|
|
|
} else {
|
|
|
|
if !p.accept('=') {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if !p.accept(':') {
|
|
|
|
return
|
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
2015-01-08 23:56:03 +01:00
|
|
|
|
2015-01-03 00:47:54 +01:00
|
|
|
value := p.parseExpression()
|
2014-05-28 01:34:41 +02:00
|
|
|
|
2016-06-10 00:52:30 +02:00
|
|
|
property.Name = name
|
|
|
|
property.NamePos = namePos
|
2014-05-28 01:34:41 +02:00
|
|
|
property.Value = value
|
2016-06-10 02:03:57 +02:00
|
|
|
property.ColonPos = pos
|
2014-05-28 01:34:41 +02:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
func (p *parser) parseExpression() (value Expression) {
|
2015-01-03 00:47:54 +01:00
|
|
|
value = p.parseValue()
|
|
|
|
switch p.tok {
|
|
|
|
case '+':
|
|
|
|
return p.parseOperator(value)
|
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 '-':
|
|
|
|
p.errorf("subtraction not supported: %s", p.scanner.String())
|
|
|
|
return value
|
2015-01-03 00:47:54 +01:00
|
|
|
default:
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
func (p *parser) evaluateOperator(value1, value2 Expression, operator rune,
|
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
|
|
|
pos scanner.Position) (Expression, error) {
|
|
|
|
|
|
|
|
if value1.Type() == UnsetType {
|
|
|
|
return value2, nil
|
|
|
|
}
|
|
|
|
if value2.Type() == UnsetType {
|
|
|
|
return value1, nil
|
|
|
|
}
|
2015-01-03 00:47:54 +01:00
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
value := value1
|
2015-01-08 23:56:03 +01:00
|
|
|
|
2015-03-20 01:28:06 +01:00
|
|
|
if p.eval {
|
2016-06-07 21:28:16 +02:00
|
|
|
e1 := value1.Eval()
|
|
|
|
e2 := value2.Eval()
|
|
|
|
if e1.Type() != e2.Type() {
|
|
|
|
return nil, fmt.Errorf("mismatched type in operator %c: %s != %s", operator,
|
|
|
|
e1.Type(), e2.Type())
|
2015-03-20 01:28:06 +01:00
|
|
|
}
|
|
|
|
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
if _, ok := e1.(*Select); !ok {
|
|
|
|
if _, ok := e2.(*Select); ok {
|
|
|
|
// Promote e1 to a select so we can add e2 to it
|
|
|
|
e1 = &Select{
|
|
|
|
Cases: []*SelectCase{{
|
|
|
|
Value: e1,
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
value = e1.Copy()
|
2015-03-20 01:28:06 +01:00
|
|
|
|
|
|
|
switch operator {
|
|
|
|
case '+':
|
2016-06-07 21:28:16 +02:00
|
|
|
switch v := value.(type) {
|
|
|
|
case *String:
|
|
|
|
v.Value += e2.(*String).Value
|
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 *Int64:
|
|
|
|
v.Value += e2.(*Int64).Value
|
2018-03-22 01:00:39 +01:00
|
|
|
v.Token = ""
|
2016-06-07 21:28:16 +02:00
|
|
|
case *List:
|
|
|
|
v.Values = append(v.Values, e2.(*List).Values...)
|
|
|
|
case *Map:
|
2015-03-20 01:28:06 +01:00
|
|
|
var err error
|
2016-06-07 21:28:16 +02:00
|
|
|
v.Properties, err = p.addMaps(v.Properties, e2.(*Map).Properties, pos)
|
2015-03-20 01:28:06 +01:00
|
|
|
if err != nil {
|
2016-06-07 21:28:16 +02:00
|
|
|
return nil, err
|
2015-03-20 01:28:06 +01:00
|
|
|
}
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
case *Select:
|
|
|
|
v.Append = e2
|
2015-03-20 01:28:06 +01:00
|
|
|
default:
|
2016-06-07 21:28:16 +02:00
|
|
|
return nil, fmt.Errorf("operator %c not supported on type %s", operator, v.Type())
|
2015-02-12 19:12:10 +01:00
|
|
|
}
|
2015-01-03 00:47:54 +01:00
|
|
|
default:
|
2015-03-20 01:28:06 +01:00
|
|
|
panic("unknown operator " + string(operator))
|
2015-01-03 00:47:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
return &Operator{
|
|
|
|
Args: [2]Expression{value1, value2},
|
|
|
|
Operator: operator,
|
|
|
|
OperatorPos: pos,
|
|
|
|
Value: value,
|
|
|
|
}, nil
|
2015-02-04 19:50:22 +01:00
|
|
|
}
|
|
|
|
|
2015-03-20 01:28:06 +01:00
|
|
|
func (p *parser) addMaps(map1, map2 []*Property, pos scanner.Position) ([]*Property, error) {
|
2015-02-12 19:12:10 +01:00
|
|
|
ret := make([]*Property, 0, len(map1))
|
2015-03-10 22:37:27 +01:00
|
|
|
|
2015-02-12 19:12:10 +01:00
|
|
|
inMap1 := make(map[string]*Property)
|
|
|
|
inMap2 := make(map[string]*Property)
|
|
|
|
inBoth := make(map[string]*Property)
|
2015-03-10 22:37:27 +01:00
|
|
|
|
2015-02-12 19:12:10 +01:00
|
|
|
for _, prop1 := range map1 {
|
2016-06-10 00:52:30 +02:00
|
|
|
inMap1[prop1.Name] = prop1
|
2015-02-12 19:12:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, prop2 := range map2 {
|
2016-06-10 00:52:30 +02:00
|
|
|
inMap2[prop2.Name] = prop2
|
|
|
|
if _, ok := inMap1[prop2.Name]; ok {
|
|
|
|
inBoth[prop2.Name] = prop2
|
2015-02-12 19:12:10 +01:00
|
|
|
}
|
|
|
|
}
|
2015-03-10 22:37:27 +01:00
|
|
|
|
2015-02-12 19:12:10 +01:00
|
|
|
for _, prop1 := range map1 {
|
2016-06-10 00:52:30 +02:00
|
|
|
if prop2, ok := inBoth[prop1.Name]; ok {
|
2015-02-12 19:12:10 +01:00
|
|
|
var err error
|
|
|
|
newProp := *prop1
|
2015-03-20 01:28:06 +01:00
|
|
|
newProp.Value, err = p.evaluateOperator(prop1.Value, prop2.Value, '+', pos)
|
2015-02-12 19:12:10 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ret = append(ret, &newProp)
|
|
|
|
} else {
|
|
|
|
ret = append(ret, prop1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, prop2 := range map2 {
|
2016-06-10 00:52:30 +02:00
|
|
|
if _, ok := inBoth[prop2.Name]; !ok {
|
2015-02-12 19:12:10 +01:00
|
|
|
ret = append(ret, prop2)
|
|
|
|
}
|
|
|
|
}
|
2015-03-10 22:37:27 +01:00
|
|
|
|
2015-02-12 19:12:10 +01:00
|
|
|
return ret, 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
|
|
|
func (p *parser) parseOperator(value1 Expression) Expression {
|
2015-02-04 19:50:22 +01:00
|
|
|
operator := p.tok
|
|
|
|
pos := p.scanner.Position
|
|
|
|
p.accept(operator)
|
|
|
|
|
|
|
|
value2 := p.parseExpression()
|
|
|
|
|
2015-03-20 01:28:06 +01:00
|
|
|
value, err := p.evaluateOperator(value1, value2, operator, pos)
|
2015-02-04 19:50:22 +01:00
|
|
|
if err != nil {
|
2015-07-11 02:51:55 +02:00
|
|
|
p.error(err)
|
2016-06-07 21:28:16 +02:00
|
|
|
return nil
|
2015-02-04 19:50:22 +01:00
|
|
|
}
|
|
|
|
|
2015-01-08 23:56:03 +01:00
|
|
|
return value
|
2016-06-07 21:28:16 +02:00
|
|
|
|
2015-01-03 00:47:54 +01:00
|
|
|
}
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
func (p *parser) parseValue() (value Expression) {
|
2014-05-28 01:34:41 +02:00
|
|
|
switch p.tok {
|
|
|
|
case scanner.Ident:
|
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
|
|
|
switch text := p.scanner.TokenText(); text {
|
|
|
|
case "true", "false":
|
|
|
|
return p.parseBoolean()
|
|
|
|
case "select":
|
|
|
|
return p.parseSelect()
|
|
|
|
default:
|
|
|
|
return p.parseVariable()
|
|
|
|
}
|
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 '-', scanner.Int: // Integer might have '-' sign ahead ('+' is only treated as operator now)
|
|
|
|
return p.parseIntValue()
|
2022-01-19 00:35:08 +01:00
|
|
|
case scanner.String, scanner.RawString:
|
2014-05-28 01:34:41 +02:00
|
|
|
return p.parseStringValue()
|
|
|
|
case '[':
|
|
|
|
return p.parseListValue()
|
2014-07-26 02:01:20 +02:00
|
|
|
case '{':
|
|
|
|
return p.parseMapValue()
|
2014-05-28 01:34:41 +02:00
|
|
|
default:
|
|
|
|
p.errorf("expected bool, list, or string value; found %s",
|
|
|
|
scanner.TokenString(p.tok))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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 (p *parser) parseBoolean() Expression {
|
2014-05-28 01:34:41 +02:00
|
|
|
switch text := p.scanner.TokenText(); text {
|
2016-06-07 21:28:16 +02:00
|
|
|
case "true", "false":
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
result := &Bool{
|
2016-06-07 21:28:16 +02:00
|
|
|
LiteralPos: p.scanner.Position,
|
|
|
|
Value: text == "true",
|
2018-03-22 01:00:39 +01:00
|
|
|
Token: text,
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
p.accept(scanner.Ident)
|
|
|
|
return result
|
2014-05-28 01:34:41 +02:00
|
|
|
default:
|
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
|
|
|
p.errorf("Expected true/false, got %q", text)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *parser) parseVariable() Expression {
|
|
|
|
var value Expression
|
|
|
|
|
|
|
|
text := p.scanner.TokenText()
|
|
|
|
if p.eval {
|
|
|
|
if assignment, local := p.scope.Get(text); assignment == nil {
|
|
|
|
p.errorf("variable %q is not set", text)
|
2020-01-21 22:31:06 +01:00
|
|
|
} else {
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
if local {
|
|
|
|
assignment.Referenced = true
|
|
|
|
}
|
|
|
|
value = assignment.Value
|
2016-06-07 21:28:16 +02:00
|
|
|
}
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
} else {
|
|
|
|
value = &NotEvaluated{}
|
|
|
|
}
|
|
|
|
value = &Variable{
|
|
|
|
Name: text,
|
|
|
|
NamePos: p.scanner.Position,
|
|
|
|
Value: value,
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
2015-01-03 00:19:28 +01:00
|
|
|
|
2014-05-28 01:34:41 +02:00
|
|
|
p.accept(scanner.Ident)
|
2016-06-07 21:28:16 +02:00
|
|
|
return value
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
func (p *parser) parseSelect() Expression {
|
|
|
|
result := &Select{
|
|
|
|
KeywordPos: p.scanner.Position,
|
|
|
|
}
|
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
|
|
|
// Read the "select("
|
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
|
|
|
p.accept(scanner.Ident)
|
|
|
|
if !p.accept('(') {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
|
|
|
// If we see another '(', there's probably multiple conditions and there must
|
|
|
|
// be a ')' after. Set the multipleConditions variable to remind us to check for
|
|
|
|
// the ')' after.
|
|
|
|
multipleConditions := false
|
|
|
|
if p.tok == '(' {
|
|
|
|
multipleConditions = true
|
|
|
|
p.accept('(')
|
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
|
|
|
// Read all individual conditions
|
|
|
|
conditions := []ConfigurableCondition{}
|
|
|
|
for first := true; first || multipleConditions; first = false {
|
|
|
|
condition := ConfigurableCondition{
|
|
|
|
position: p.scanner.Position,
|
|
|
|
FunctionName: p.scanner.TokenText(),
|
|
|
|
}
|
|
|
|
if !p.accept(scanner.Ident) {
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
return nil
|
|
|
|
}
|
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
|
|
|
if !p.accept('(') {
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
return nil
|
|
|
|
}
|
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
|
|
|
|
|
|
|
for p.tok != ')' {
|
|
|
|
if s := p.parseStringValue(); s != nil {
|
|
|
|
condition.Args = append(condition.Args, *s)
|
|
|
|
} else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if p.tok == ')' {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if !p.accept(',') {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p.accept(')')
|
|
|
|
|
|
|
|
for _, c := range conditions {
|
|
|
|
if c.Equals(condition) {
|
|
|
|
p.errorf("Duplicate select condition found: %s", c.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
conditions = append(conditions, condition)
|
|
|
|
|
|
|
|
if multipleConditions {
|
|
|
|
if p.tok == ')' {
|
|
|
|
p.next()
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if !p.accept(',') {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
// Retry the closing parent to allow for a trailing comma
|
|
|
|
if p.tok == ')' {
|
|
|
|
p.next()
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
}
|
|
|
|
|
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
|
|
|
if multipleConditions && len(conditions) < 2 {
|
|
|
|
p.errorf("Expected multiple select conditions due to the extra parenthesis, but only found 1. Please remove the extra parenthesis.")
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
|
|
|
result.Conditions = conditions
|
|
|
|
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
if !p.accept(',') {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
result.LBracePos = p.scanner.Position
|
|
|
|
if !p.accept('{') {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
|
|
|
parseOnePattern := func() Expression {
|
|
|
|
switch p.tok {
|
|
|
|
case scanner.Ident:
|
|
|
|
switch p.scanner.TokenText() {
|
|
|
|
case "default":
|
|
|
|
p.next()
|
|
|
|
return &String{
|
|
|
|
LiteralPos: p.scanner.Position,
|
|
|
|
Value: default_select_branch_name,
|
|
|
|
}
|
|
|
|
case "true":
|
|
|
|
p.next()
|
|
|
|
return &Bool{
|
|
|
|
LiteralPos: p.scanner.Position,
|
|
|
|
Value: true,
|
|
|
|
}
|
|
|
|
case "false":
|
|
|
|
p.next()
|
|
|
|
return &Bool{
|
|
|
|
LiteralPos: p.scanner.Position,
|
|
|
|
Value: false,
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
p.errorf("Expted a string, true, false, or default, got %s", p.scanner.TokenText())
|
|
|
|
}
|
|
|
|
case scanner.String:
|
|
|
|
if s := p.parseStringValue(); s != nil {
|
|
|
|
if strings.HasPrefix(s.Value, "__soong") {
|
|
|
|
p.errorf("select branch conditions starting with __soong are reserved for internal use")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
fallthrough
|
|
|
|
default:
|
|
|
|
p.errorf("Expted a string, true, false, or default, got %s", p.scanner.TokenText())
|
|
|
|
}
|
|
|
|
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
|
|
|
hasNonUnsetValue := false
|
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
|
|
|
for p.tok != '}' {
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
c := &SelectCase{}
|
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
|
|
|
|
|
|
|
if multipleConditions {
|
|
|
|
if !p.accept('(') {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
for i := 0; i < len(conditions); i++ {
|
|
|
|
if p := parseOnePattern(); p != nil {
|
|
|
|
c.Patterns = append(c.Patterns, p)
|
|
|
|
} else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if i < len(conditions)-1 {
|
|
|
|
if !p.accept(',') {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
} else if p.tok == ',' {
|
|
|
|
// allow optional trailing comma
|
|
|
|
p.next()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !p.accept(')') {
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
} else {
|
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
|
|
|
if p := parseOnePattern(); p != nil {
|
|
|
|
c.Patterns = append(c.Patterns, p)
|
|
|
|
} else {
|
|
|
|
return nil
|
|
|
|
}
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
}
|
|
|
|
c.ColonPos = p.scanner.Position
|
|
|
|
if !p.accept(':') {
|
|
|
|
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
|
|
|
if p.tok == scanner.Ident && p.scanner.TokenText() == "unset" {
|
|
|
|
c.Value = UnsetProperty{Position: p.scanner.Position}
|
|
|
|
p.accept(scanner.Ident)
|
|
|
|
} else {
|
|
|
|
hasNonUnsetValue = true
|
|
|
|
c.Value = p.parseExpression()
|
|
|
|
}
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
if !p.accept(',') {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
result.Cases = append(result.Cases, c)
|
|
|
|
}
|
|
|
|
|
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
|
|
|
// If all branches have the value "unset", then this is equivalent
|
|
|
|
// to an empty select.
|
|
|
|
if !hasNonUnsetValue {
|
|
|
|
p.errorf("This select statement is empty, remove it")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
patternsEqual := func(a, b Expression) bool {
|
|
|
|
switch a2 := a.(type) {
|
|
|
|
case *String:
|
|
|
|
if b2, ok := b.(*String); ok {
|
|
|
|
return a2.Value == b2.Value
|
|
|
|
} else {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
case *Bool:
|
|
|
|
if b2, ok := b.(*Bool); ok {
|
|
|
|
return a2.Value == b2.Value
|
|
|
|
} else {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
// true so that we produce an error in this unexpected scenario
|
|
|
|
return true
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
|
|
|
patternListsEqual := func(a, b []Expression) bool {
|
|
|
|
if len(a) != len(b) {
|
|
|
|
return false
|
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
|
|
|
}
|
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
|
|
|
for i := range a {
|
|
|
|
if !patternsEqual(a[i], b[i]) {
|
|
|
|
return false
|
|
|
|
}
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
}
|
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
|
|
|
return true
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
}
|
|
|
|
|
Support multi-variable selects and typed selects
This adds support for selecting on multiple variables at once, so that
you can do AND/OR combindations of them. For example:
select((
arch(),
os(),
), {
("arm64", "linux"): ["libfoo64"],
(default, "linux"): ["libfoo"],
(default, "windows"): ["libfoowindows"],
(default, default): ["libbar"],
})
It also allows for select conditions to be boolean-typed. You can
write literal true and false without quotes to select on them. Currently
we don't have any boolean-typed variables though, so a fake one was
added for testing.
Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: Ibe586e7b21865b8734027848cc421594cbd1d8cc
2024-04-10 23:57:34 +02:00
|
|
|
for i, c := range result.Cases {
|
|
|
|
// Check for duplicates
|
|
|
|
for _, d := range result.Cases[i+1:] {
|
|
|
|
if patternListsEqual(c.Patterns, d.Patterns) {
|
|
|
|
p.errorf("Found duplicate select patterns: %v", c.Patterns)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Check that the only all-default cases is the last one
|
|
|
|
if i < len(result.Cases)-1 {
|
|
|
|
isAllDefault := true
|
|
|
|
for _, x := range c.Patterns {
|
|
|
|
if x2, ok := x.(*String); !ok || x2.Value != default_select_branch_name {
|
|
|
|
isAllDefault = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if isAllDefault {
|
|
|
|
p.errorf("Found a default select branch at index %d, expected it to be last (index %d)", i, len(result.Cases)-1)
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
ty := UnsetType
|
|
|
|
for _, c := range result.Cases {
|
|
|
|
otherTy := c.Value.Type()
|
|
|
|
// Any other type can override UnsetType
|
|
|
|
if ty == UnsetType {
|
|
|
|
ty = otherTy
|
|
|
|
}
|
|
|
|
if otherTy != UnsetType && otherTy != ty {
|
|
|
|
p.errorf("Found select statement with differing types %q and %q in its cases", ty.String(), otherTy.String())
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result.ExpressionType = ty
|
|
|
|
|
Select statements
Select statements are a new blueprint feature inspired by bazel's select
statements. They are essentially alternative syntax for soong config
variables that require less boilerplate. In addition, they support
making decisions based on a module's variant, which will eliminate
the need for manual property struct manipulation, such as the arch
mutator's arch: and target: properties.
In order to support decisions based on the variant, select statements
cannot be evaluated as soon as they're parsed. Instead, they must be
stored in the property struct unevaluated. This means that individual
properties need to change their type from say, string, to
Configurable[string]. Currently, only configurable strings, bools, and
string slices are supported, but more types can be added later.
The module implementation must call my_property.Evaluate(ctx) in order
to get the final, resolved value of the select statement.
Bug: 323382414
Test: go tests
Change-Id: I62f8721d7f0ac3d1df4a06d7eaa260a5aa7fcba3
2024-02-02 02:44:27 +01:00
|
|
|
result.RBracePos = p.scanner.Position
|
|
|
|
if !p.accept('}') {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if !p.accept(')') {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
func (p *parser) parseStringValue() *String {
|
2014-05-28 01:34:41 +02:00
|
|
|
str, err := strconv.Unquote(p.scanner.TokenText())
|
|
|
|
if err != nil {
|
|
|
|
p.errorf("couldn't parse string: %s", err)
|
2016-06-07 21:28:16 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
value := &String{
|
|
|
|
LiteralPos: p.scanner.Position,
|
|
|
|
Value: str,
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
2022-01-19 00:35:08 +01:00
|
|
|
p.accept(p.tok)
|
2016-06-07 21:28:16 +02:00
|
|
|
return value
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
Support parsing int64 in Blueprint file.
Support int64 number instead of int to be more fixed to bit size so
that the underlying arch won't affect overflow cases. Besides,
refection: func (v Value) Int() int64 always cast to int64 no matter the
input is int, int16, int32. Currently we always treat "-" as negative
sign to bind to next value, and "+" as plus operator to add operands
together.
So we allow:
a = 5 + -4 + 5 or a = -4 + 5
But we don't allow:
a = +5 + 4 + -4 since we don't treat "+" as a positive sign, otherwise,
a = 5 + +5 would exist which looks pretty weird. In the future, we may
want fully support number calculator logic eg, "+"/"-" can be
positive/negative sign or operator, and "(" and ")" will be considered
to group expressions with a higher precedence.
int & uint properties within struct keeps unchanged, which is only
allowed when tagged with 'blueprint:mutated'. We only allow *int64
property instead of int64 property within struct since it does't make
sense to do prepending or appending to int64.
Change-Id: I565e046dbd268af3538aee148cd7300037e56523
2017-11-01 22:03:28 +01:00
|
|
|
func (p *parser) parseIntValue() *Int64 {
|
|
|
|
var str string
|
|
|
|
literalPos := p.scanner.Position
|
|
|
|
if p.tok == '-' {
|
|
|
|
str += string(p.tok)
|
|
|
|
p.accept(p.tok)
|
|
|
|
if p.tok != scanner.Int {
|
|
|
|
p.errorf("expected int; found %s", scanner.TokenString(p.tok))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
str += p.scanner.TokenText()
|
|
|
|
i, err := strconv.ParseInt(str, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
p.errorf("couldn't parse int: %s", err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
value := &Int64{
|
|
|
|
LiteralPos: literalPos,
|
|
|
|
Value: i,
|
2018-03-22 01:00:39 +01:00
|
|
|
Token: str,
|
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
|
|
|
}
|
|
|
|
p.accept(scanner.Int)
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
func (p *parser) parseListValue() *List {
|
|
|
|
lBracePos := p.scanner.Position
|
2014-05-28 01:34:41 +02:00
|
|
|
if !p.accept('[') {
|
2016-06-07 21:28:16 +02:00
|
|
|
return nil
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
var elements []Expression
|
2015-01-03 00:19:28 +01:00
|
|
|
for p.tok != ']' {
|
2015-01-03 00:47:54 +01:00
|
|
|
element := p.parseExpression()
|
2015-01-03 00:19:28 +01:00
|
|
|
elements = append(elements, element)
|
2014-05-28 01:34:41 +02:00
|
|
|
|
|
|
|
if p.tok != ',' {
|
|
|
|
// There was no comma, so the list is done.
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
p.accept(',')
|
|
|
|
}
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
rBracePos := p.scanner.Position
|
2014-05-28 01:34:41 +02:00
|
|
|
p.accept(']')
|
2016-06-07 21:28:16 +02:00
|
|
|
|
|
|
|
return &List{
|
|
|
|
LBracePos: lBracePos,
|
|
|
|
RBracePos: rBracePos,
|
|
|
|
Values: elements,
|
|
|
|
}
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
func (p *parser) parseMapValue() *Map {
|
|
|
|
lBracePos := p.scanner.Position
|
2014-07-26 02:01:20 +02:00
|
|
|
if !p.accept('{') {
|
2016-06-07 21:28:16 +02:00
|
|
|
return nil
|
2014-07-26 02:01:20 +02:00
|
|
|
}
|
|
|
|
|
2022-08-02 16:51:08 +02:00
|
|
|
properties := p.parsePropertyList(false, false)
|
2014-07-26 02:01:20 +02:00
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
rBracePos := p.scanner.Position
|
2014-07-26 02:01:20 +02:00
|
|
|
p.accept('}')
|
2015-07-06 21:29:57 +02:00
|
|
|
|
2016-06-07 21:28:16 +02:00
|
|
|
return &Map{
|
|
|
|
LBracePos: lBracePos,
|
|
|
|
RBracePos: rBracePos,
|
|
|
|
Properties: properties,
|
2014-05-28 01:34:41 +02:00
|
|
|
}
|
|
|
|
}
|
2015-01-03 00:19:28 +01:00
|
|
|
|
|
|
|
type Scope struct {
|
2015-07-11 02:51:55 +02:00
|
|
|
vars map[string]*Assignment
|
|
|
|
inheritedVars map[string]*Assignment
|
2015-01-03 00:19:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewScope(s *Scope) *Scope {
|
|
|
|
newScope := &Scope{
|
2015-07-11 02:51:55 +02:00
|
|
|
vars: make(map[string]*Assignment),
|
|
|
|
inheritedVars: make(map[string]*Assignment),
|
2015-01-03 00:19:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if s != nil {
|
|
|
|
for k, v := range s.vars {
|
2015-07-11 02:51:55 +02:00
|
|
|
newScope.inheritedVars[k] = v
|
|
|
|
}
|
|
|
|
for k, v := range s.inheritedVars {
|
|
|
|
newScope.inheritedVars[k] = v
|
2015-01-03 00:19:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return newScope
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Scope) Add(assignment *Assignment) error {
|
2016-06-10 00:52:30 +02:00
|
|
|
if old, ok := s.vars[assignment.Name]; ok {
|
2015-01-03 00:19:28 +01:00
|
|
|
return fmt.Errorf("variable already set, previous assignment: %s", old)
|
|
|
|
}
|
|
|
|
|
2016-06-10 00:52:30 +02:00
|
|
|
if old, ok := s.inheritedVars[assignment.Name]; ok {
|
2015-07-11 02:51:55 +02:00
|
|
|
return fmt.Errorf("variable already set in inherited scope, previous assignment: %s", old)
|
|
|
|
}
|
|
|
|
|
2016-06-10 00:52:30 +02:00
|
|
|
s.vars[assignment.Name] = assignment
|
2015-01-03 00:19:28 +01:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Scope) Remove(name string) {
|
|
|
|
delete(s.vars, name)
|
2015-07-11 02:51:55 +02:00
|
|
|
delete(s.inheritedVars, name)
|
2015-01-03 00:19:28 +01:00
|
|
|
}
|
|
|
|
|
2015-07-11 02:51:55 +02:00
|
|
|
func (s *Scope) Get(name string) (*Assignment, bool) {
|
2015-01-03 00:19:28 +01:00
|
|
|
if a, ok := s.vars[name]; ok {
|
2015-07-11 02:51:55 +02:00
|
|
|
return a, true
|
2015-01-03 00:19:28 +01:00
|
|
|
}
|
|
|
|
|
2015-07-11 02:51:55 +02:00
|
|
|
if a, ok := s.inheritedVars[name]; ok {
|
|
|
|
return a, false
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, false
|
2015-01-03 00:19:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Scope) String() string {
|
|
|
|
vars := []string{}
|
|
|
|
|
|
|
|
for k := range s.vars {
|
|
|
|
vars = append(vars, k)
|
|
|
|
}
|
2015-07-11 02:51:55 +02:00
|
|
|
for k := range s.inheritedVars {
|
|
|
|
vars = append(vars, k)
|
|
|
|
}
|
2015-01-03 00:19:28 +01:00
|
|
|
|
|
|
|
sort.Strings(vars)
|
|
|
|
|
|
|
|
ret := []string{}
|
|
|
|
for _, v := range vars {
|
2015-07-11 02:51:55 +02:00
|
|
|
if assignment, ok := s.vars[v]; ok {
|
|
|
|
ret = append(ret, assignment.String())
|
|
|
|
} else {
|
|
|
|
ret = append(ret, s.inheritedVars[v].String())
|
|
|
|
}
|
2015-01-03 00:19:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return strings.Join(ret, "\n")
|
|
|
|
}
|