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