diff --git a/mk2rbc/android_products.go b/mk2rbc/android_products.go new file mode 100644 index 000000000..de38391fc --- /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 : pairs (if : is missing, +// 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 :, or + // just + 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 000000000..f8c930aeb --- /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 000000000..a2220edac --- /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