1// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package mk2rbc
16
17import (
18	"bytes"
19	"fmt"
20	"io/ioutil"
21	"os"
22	"path/filepath"
23	"strings"
24
25	mkparser "android/soong/androidmk/parser"
26)
27
28// Implements mkparser.Scope, to be used by mkparser.Value.Value()
29type localDirEval struct {
30	localDir  string
31	hasErrors bool
32}
33
34func (l *localDirEval) Get(name string) string {
35	if name == "LOCAL_DIR" {
36		return l.localDir
37	}
38	l.hasErrors = true
39	return fmt.Sprintf("$(%s)", name)
40}
41
42func (l *localDirEval) Set(_, _ string) {
43}
44
45func (l *localDirEval) Call(_ string, _ []string) []string {
46	l.hasErrors = true
47	return []string{"$(call ...)"}
48}
49
50func (l *localDirEval) SetFunc(_ string, _ func([]string) []string) {
51}
52
53// UpdateProductConfigMap builds product configuration map.
54// The product configuration map maps a product name (i.e., the value of the
55// TARGET_PRODUCT variable) to the top-level configuration file.
56// In the Android's Make-based build machinery, the equivalent of the
57// product configuration map is $(PRODUCT_MAKEFILES), which is the list
58// of <product>:<configuration makefile> pairs (if <product>: is missing,
59// <product> is the basename of the configuration makefile).
60// UpdateProductConfigMap emulates this build logic by processing the
61// assignments to PRODUCT_MAKEFILES in the file passed to it.
62func UpdateProductConfigMap(configMap map[string]string, configMakefile string) error {
63	contents, err := ioutil.ReadFile(configMakefile)
64	if err != nil {
65		return err
66	}
67	parser := mkparser.NewParser(configMakefile, bytes.NewBuffer(contents))
68	nodes, errs := parser.Parse()
69	if len(errs) > 0 {
70		for _, e := range errs {
71			fmt.Fprintln(os.Stderr, "ERROR:", e)
72		}
73		return fmt.Errorf("cannot parse %s", configMakefile)
74	}
75
76	ldEval := &localDirEval{localDir: filepath.Dir(configMakefile)}
77
78	for _, node := range nodes {
79		// We are interested in assignments to 'PRODUCT_MAKEFILES'
80		asgn, ok := node.(*mkparser.Assignment)
81		if !ok {
82			continue
83		}
84		if !(asgn.Name.Const() && asgn.Name.Strings[0] == "PRODUCT_MAKEFILES") {
85			continue
86		}
87
88		// Resolve the references to $(LOCAL_DIR) in $(PRODUCT_MAKEFILES).
89		ldEval.hasErrors = false
90		value := asgn.Value.Value(ldEval)
91		if ldEval.hasErrors {
92			return fmt.Errorf("cannot evaluate %s", asgn.Value.Dump())
93		}
94		// Each item is either <product>:<configuration makefile>, or
95		// just <configuration makefile>
96		for _, token := range strings.Fields(value) {
97			var product, config_path string
98			if n := strings.Index(token, ":"); n >= 0 {
99				product = token[0:n]
100				config_path = token[n+1:]
101			} else {
102				config_path = token
103				product = filepath.Base(config_path)
104				product = strings.TrimSuffix(product, filepath.Ext(product))
105			}
106			configMap[product] = config_path
107		}
108	}
109	return nil
110}
111