Config variables reader
Fixes: 182330871
Test: go test config_variables_test.go
Change-Id: I89240fb0f1900172cb4d62057c86907bb9a1f58f
diff --git a/mk2rbc/config_variables.go b/mk2rbc/config_variables.go
new file mode 100644
index 0000000..dac509c
--- /dev/null
+++ b/mk2rbc/config_variables.go
@@ -0,0 +1,67 @@
+// 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"
+ "strings"
+
+ mkparser "android/soong/androidmk/parser"
+)
+
+// Extracts the list of product config variables from a file, calling
+// given registrar for each variable.
+func FindConfigVariables(mkFile string, vr variableRegistrar) 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 {
+ asgn, ok := node.(*mkparser.Assignment)
+ if !ok {
+ continue
+ }
+ // We are looking for a variable called '_product_list_vars'
+ // or '_product_single_value_vars'.
+ if !asgn.Name.Const() {
+ continue
+ }
+ varName := asgn.Name.Strings[0]
+ var starType starlarkType
+ if varName == "_product_list_vars" {
+ starType = starlarkTypeList
+ } else if varName == "_product_single_value_vars" {
+ starType = starlarkTypeUnknown
+ } else {
+ continue
+ }
+ for _, name := range strings.Fields(asgn.Value.Dump()) {
+ vr.NewVariable(name, VarClassConfig, starType)
+ }
+
+ }
+ return nil
+}
diff --git a/mk2rbc/config_variables_test.go b/mk2rbc/config_variables_test.go
new file mode 100644
index 0000000..f5a5180
--- /dev/null
+++ b/mk2rbc/config_variables_test.go
@@ -0,0 +1,60 @@
+// 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 (
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "testing"
+)
+
+type testVar struct {
+ name string
+ cl varClass
+ ty starlarkType
+}
+
+type testVariables struct {
+ v []testVar
+}
+
+func (v *testVariables) NewVariable(name string, varClass varClass, valueType starlarkType) {
+ v.v = append(v.v, testVar{name, varClass, valueType})
+}
+
+// getTestDirectory returns the test directory, which should be the test/ subdirectory
+func getTestDirectory() string {
+ _, myFile, _, _ := runtime.Caller(1)
+ return filepath.Join(filepath.Dir(myFile), "test")
+}
+
+func TestConfigVariables(t *testing.T) {
+ testFile := filepath.Join(getTestDirectory(), "config_variables.mk.test")
+ var actual testVariables
+ if err := FindConfigVariables(testFile, &actual); err != nil {
+ t.Fatal(err)
+ }
+ expected := testVariables{[]testVar{
+ {"PRODUCT_NAME", VarClassConfig, starlarkTypeUnknown},
+ {"PRODUCT_MODEL", VarClassConfig, starlarkTypeUnknown},
+ {"PRODUCT_LOCALES", VarClassConfig, starlarkTypeList},
+ {"PRODUCT_AAPT_CONFIG", VarClassConfig, starlarkTypeList},
+ {"PRODUCT_AAPT_PREF_CONFIG", VarClassConfig, starlarkTypeUnknown},
+ }}
+ if !reflect.DeepEqual(expected, actual) {
+ t.Errorf("\nExpected: %v\n Actual: %v", expected, actual)
+ }
+}
diff --git a/mk2rbc/test/config_variables.mk.test b/mk2rbc/test/config_variables.mk.test
new file mode 100644
index 0000000..e5cd0e9
--- /dev/null
+++ b/mk2rbc/test/config_variables.mk.test
@@ -0,0 +1,12 @@
+_product_single_value_vars :=
+
+# Variables that are lists of values.
+_product_list_vars :=
+
+_product_single_value_vars += PRODUCT_NAME
+_product_single_value_vars += PRODUCT_MODEL
+
+# The resoure configuration options to use for this product.
+_product_list_vars += PRODUCT_LOCALES
+_product_list_vars += PRODUCT_AAPT_CONFIG
+_product_single_value_vars += PRODUCT_AAPT_PREF_CONFIG
diff --git a/mk2rbc/types.go b/mk2rbc/types.go
new file mode 100644
index 0000000..22c8b58
--- /dev/null
+++ b/mk2rbc/types.go
@@ -0,0 +1,55 @@
+// 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
+
+// Starlark expression types we use
+type starlarkType int
+
+const (
+ starlarkTypeUnknown starlarkType = iota
+ starlarkTypeList starlarkType = iota
+ starlarkTypeString starlarkType = iota
+ starlarkTypeInt starlarkType = iota
+ starlarkTypeBool starlarkType = iota
+ starlarkTypeVoid starlarkType = iota
+)
+
+type varClass int
+
+const (
+ VarClassConfig varClass = iota
+ VarClassSoong varClass = iota
+ VarClassLocal varClass = iota
+)
+
+type variableRegistrar interface {
+ NewVariable(name string, varClass varClass, valueType starlarkType)
+}
+
+// ScopeBase is a dummy implementation of the mkparser.Scope.
+// All our scopes are read-only and resolve only simple variables.
+type ScopeBase struct{}
+
+func (s ScopeBase) Set(_, _ string) {
+ panic("implement me")
+}
+
+func (s ScopeBase) Call(_ string, _ []string) []string {
+ panic("implement me")
+}
+
+func (s ScopeBase) SetFunc(_ string, _ func([]string) []string) {
+ panic("implement me")
+}