platform_build_soong/mk2rbc/soong_variables.go
Cole Faust c7335de0dd Don't type variables as booleans in mk2rbc
Our boolean support is not fully fleshed out enough to make variables
real booleans. Keep them strings for now to resolve CI failures.

Bug: 275865081
Test: ./build/bazel/ci/rbc_dashboard.py --quick with aosp/2526609
Change-Id: I636bd9f39b50a47ecf92aecd2bf8ea41eac4d604
2023-04-12 12:13:28 -07:00

155 lines
5.2 KiB
Go

// 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
}