-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathsoong_variables.go
155 lines (141 loc) · 5.16 KB
/
soong_variables.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Copyright 2021 Google LLC
//
// 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 mk2rbc
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"regexp"
"strings"
mkparser "android/soong/androidmk/parser"
)
type context struct {
includeFileScope mkparser.Scope
registrar variableRegistrar
}
// Scans the makefile Soong uses to generate soong.variables file,
// collecting variable names and types from the lines that look like this:
//
// $(call add_json_XXX, <...>, $(VAR))
func FindSoongVariables(mkFile string, includeFileScope mkparser.Scope, registrar variableRegistrar) error {
ctx := context{includeFileScope, registrar}
return ctx.doFind(mkFile)
}
func (ctx *context) doFind(mkFile string) error {
mkContents, err := ioutil.ReadFile(mkFile)
if err != nil {
return err
}
parser := mkparser.NewParser(mkFile, bytes.NewBuffer(mkContents))
nodes, errs := parser.Parse()
if len(errs) > 0 {
for _, e := range errs {
fmt.Fprintln(os.Stderr, "ERROR:", e)
}
return fmt.Errorf("cannot parse %s", mkFile)
}
for _, node := range nodes {
switch t := node.(type) {
case *mkparser.Variable:
ctx.handleVariable(t)
case *mkparser.Directive:
ctx.handleInclude(t)
}
}
return nil
}
func (ctx context) NewSoongVariable(name, typeString string) {
var valueType starlarkType
switch typeString {
case "bool":
// TODO: We run into several issues later on if we type this as a bool:
// - We still assign bool-typed variables to strings
// - When emitting the final results as make code, some bool's false values have to
// be an empty string, and some have to be false in order to match the make variables.
valueType = starlarkTypeString
case "csv":
// Only PLATFORM_VERSION_ALL_CODENAMES, and it's a list
valueType = starlarkTypeList
case "list":
valueType = starlarkTypeList
case "str":
valueType = starlarkTypeString
case "val":
// Only PLATFORM_SDK_VERSION uses this, and it's integer
valueType = starlarkTypeInt
default:
panic(fmt.Errorf("unknown Soong variable type %s", typeString))
}
ctx.registrar.NewVariable(name, VarClassSoong, valueType)
}
func (ctx context) handleInclude(t *mkparser.Directive) {
if t.Name != "include" && t.Name != "-include" {
return
}
includedPath := t.Args.Value(ctx.includeFileScope)
err := ctx.doFind(includedPath)
if err != nil && t.Name == "include" {
fmt.Fprintf(os.Stderr, "cannot include %s: %s", includedPath, err)
}
}
var callFuncRex = regexp.MustCompile("^call +add_json_(str|val|bool|csv|list) *,")
func (ctx context) handleVariable(t *mkparser.Variable) {
// From the variable reference looking as follows:
// $(call json_add_TYPE,arg1,$(VAR))
// we infer that the type of $(VAR) is TYPE
// VAR can be a simple variable name, or another call
// (e.g., $(call invert_bool, $(X)), from which we can infer
// that the type of X is bool
if prefix, v, ok := prefixedVariable(t.Name); ok && strings.HasPrefix(prefix, "call add_json") {
if match := callFuncRex.FindStringSubmatch(prefix); match != nil {
ctx.inferSoongVariableType(match[1], v)
// NOTE(asmundak): sometimes arg1 (the name of the Soong variable defined
// in this statement) may indicate that there is a Make counterpart. E.g, from
// $(call add_json_bool, DisablePreopt, $(call invert_bool,$(ENABLE_PREOPT)))
// it may be inferred that there is a Make boolean variable DISABLE_PREOPT.
// Unfortunately, Soong variable names have no 1:1 correspondence to Make variables,
// for instance,
// $(call add_json_list, PatternsOnSystemOther, $(SYSTEM_OTHER_ODEX_FILTER))
// does not mean that there is PATTERNS_ON_SYSTEM_OTHER
// Our main interest lies in finding the variables whose values are lists, and
// so far there are none that can be found this way, so it is not important.
} else {
panic(fmt.Errorf("cannot match the call: %s", prefix))
}
}
}
var (
callInvertBoolRex = regexp.MustCompile("^call +invert_bool *, *$")
callFilterBoolRex = regexp.MustCompile("^(filter|filter-out) +(true|false), *$")
)
func (ctx context) inferSoongVariableType(vType string, n *mkparser.MakeString) {
if n.Const() {
ctx.NewSoongVariable(n.Strings[0], vType)
return
}
if prefix, v, ok := prefixedVariable(n); ok {
if callInvertBoolRex.MatchString(prefix) || callFilterBoolRex.MatchString(prefix) {
// It is $(call invert_bool, $(VAR)) or $(filter[-out] [false|true],$(VAR))
ctx.inferSoongVariableType("bool", v)
}
}
}
// If MakeString is foo$(BAR), returns 'foo', BAR(as *MakeString) and true
func prefixedVariable(s *mkparser.MakeString) (string, *mkparser.MakeString, bool) {
if len(s.Strings) != 2 || s.Strings[1] != "" {
return "", nil, false
}
return s.Strings[0], s.Variables[0].Name, true
}