AndroidProducts.mk file reader
Fixes: 182418325
Test: go test android_products_test.go
Change-Id: Ibb6c4aedacb891d5d7e74ce351b2e19c888adc5d
diff --git a/mk2rbc/android_products.go b/mk2rbc/android_products.go
new file mode 100644
index 0000000..de38391
--- /dev/null
+++ b/mk2rbc/android_products.go
@@ -0,0 +1,110 @@
+// 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"
+ "path/filepath"
+ "strings"
+
+ mkparser "android/soong/androidmk/parser"
+)
+
+// Implements mkparser.Scope, to be used by mkparser.Value.Value()
+type localDirEval struct {
+ localDir string
+ hasErrors bool
+}
+
+func (l *localDirEval) Get(name string) string {
+ if name == "LOCAL_DIR" {
+ return l.localDir
+ }
+ l.hasErrors = true
+ return fmt.Sprintf("$(%s)", name)
+}
+
+func (l *localDirEval) Set(_, _ string) {
+}
+
+func (l *localDirEval) Call(_ string, _ []string) []string {
+ l.hasErrors = true
+ return []string{"$(call ...)"}
+}
+
+func (l *localDirEval) SetFunc(_ string, _ func([]string) []string) {
+}
+
+// UpdateProductConfigMap builds product configuration map.
+// The product configuration map maps a product name (i.e., the value of the
+// TARGET_PRODUCT variable) to the top-level configuration file.
+// In the Android's Make-based build machinery, the equivalent of the
+// product configuration map is $(PRODUCT_MAKEFILES), which is the list
+// of <product>:<configuration makefile> pairs (if <product>: is missing,
+// <product> is the basename of the configuration makefile).
+// UpdateProductConfigMap emulates this build logic by processing the
+// assignments to PRODUCT_MAKEFILES in the file passed to it.
+func UpdateProductConfigMap(configMap map[string]string, configMakefile string) error {
+ contents, err := ioutil.ReadFile(configMakefile)
+ if err != nil {
+ return err
+ }
+ parser := mkparser.NewParser(configMakefile, bytes.NewBuffer(contents))
+ nodes, errs := parser.Parse()
+ if len(errs) > 0 {
+ for _, e := range errs {
+ fmt.Fprintln(os.Stderr, "ERROR:", e)
+ }
+ return fmt.Errorf("cannot parse %s", configMakefile)
+ }
+
+ ldEval := &localDirEval{localDir: filepath.Dir(configMakefile)}
+
+ for _, node := range nodes {
+ // We are interested in assignments to 'PRODUCT_MAKEFILES'
+ asgn, ok := node.(*mkparser.Assignment)
+ if !ok {
+ continue
+ }
+ if !(asgn.Name.Const() && asgn.Name.Strings[0] == "PRODUCT_MAKEFILES") {
+ continue
+ }
+
+ // Resolve the references to $(LOCAL_DIR) in $(PRODUCT_MAKEFILES).
+ ldEval.hasErrors = false
+ value := asgn.Value.Value(ldEval)
+ if ldEval.hasErrors {
+ return fmt.Errorf("cannot evaluate %s", asgn.Value.Dump())
+ }
+ // Each item is either <product>:<configuration makefile>, or
+ // just <configuration makefile>
+ for _, token := range strings.Fields(value) {
+ var product, config_path string
+ if n := strings.Index(token, ":"); n >= 0 {
+ product = token[0:n]
+ config_path = token[n+1:]
+ } else {
+ config_path = token
+ product = filepath.Base(config_path)
+ product = strings.TrimSuffix(product, filepath.Ext(product))
+ }
+ configMap[product] = config_path
+ }
+ }
+ return nil
+}
diff --git a/mk2rbc/android_products_test.go b/mk2rbc/android_products_test.go
new file mode 100644
index 0000000..f8c930a
--- /dev/null
+++ b/mk2rbc/android_products_test.go
@@ -0,0 +1,38 @@
+// 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"
+ "testing"
+)
+
+func TestProductsMakefile(t *testing.T) {
+ testDir := getTestDirectory()
+ abspath := func(relPath string) string { return filepath.Join(testDir, relPath) }
+ actualProducts := make(map[string]string)
+ if err := UpdateProductConfigMap(actualProducts, abspath("android_products.mk.test")); err != nil {
+ t.Fatal(err)
+ }
+ expectedProducts := map[string]string{
+ "aosp_cf_x86_tv": abspath("vsoc_x86/tv/device.mk"),
+ "aosp_tv_arm": abspath("aosp_tv_arm.mk"),
+ "aosp_tv_arm64": abspath("aosp_tv_arm64.mk"),
+ }
+ if !reflect.DeepEqual(actualProducts, expectedProducts) {
+ t.Errorf("\nExpected: %v\n Actual: %v", expectedProducts, actualProducts)
+ }
+}
diff --git a/mk2rbc/test/android_products.mk.test b/mk2rbc/test/android_products.mk.test
new file mode 100644
index 0000000..a2220ed
--- /dev/null
+++ b/mk2rbc/test/android_products.mk.test
@@ -0,0 +1,4 @@
+PRODUCT_MAKEFILES := \
+ $(LOCAL_DIR)/aosp_tv_arm.mk \
+ $(LOCAL_DIR)/aosp_tv_arm64.mk \
+ aosp_cf_x86_tv:$(LOCAL_DIR)/vsoc_x86/tv/device.mk
\ No newline at end of file