Translate copy-files, add_soong_config_namespace and add_soong_config_var_value macros

Bug: 194521362
Test: internal

Change-Id: I88fb62f057476d96dfb056813a900e8497e7bbb9
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index 2173e9b..b99450f 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -60,8 +60,10 @@
 const (
 	// Phony makefile functions, they are eventually rewritten
 	// according to knownFunctions map
-	fileExistsPhony     = "$file_exists"
-	wildcardExistsPhony = "$wildcard_exists"
+	addSoongNamespace      = "add_soong_config_namespace"
+	addSoongConfigVarValue = "add_soong_config_var_value"
+	fileExistsPhony        = "$file_exists"
+	wildcardExistsPhony    = "$wildcard_exists"
 )
 
 const (
@@ -75,54 +77,58 @@
 	// something else.
 	runtimeName string
 	returnType  starlarkType
+	hiddenArg   hiddenArgType
 }{
-	"abspath":                             {baseName + ".abspath", starlarkTypeString},
-	fileExistsPhony:                       {baseName + ".file_exists", starlarkTypeBool},
-	wildcardExistsPhony:                   {baseName + ".file_wildcard_exists", starlarkTypeBool},
-	"add-to-product-copy-files-if-exists": {baseName + ".copy_if_exists", starlarkTypeList},
-	"addprefix":                           {baseName + ".addprefix", starlarkTypeList},
-	"addsuffix":                           {baseName + ".addsuffix", starlarkTypeList},
-	"dir":                                 {baseName + ".dir", starlarkTypeList},
-	"enforce-product-packages-exist":      {baseName + ".enforce_product_packages_exist", starlarkTypeVoid},
-	"error":                               {baseName + ".mkerror", starlarkTypeVoid},
-	"findstring":                          {"!findstring", starlarkTypeInt},
-	"find-copy-subdir-files":              {baseName + ".find_and_copy", starlarkTypeList},
-	"find-word-in-list":                   {"!find-word-in-list", starlarkTypeUnknown}, // internal macro
-	"filter":                              {baseName + ".filter", starlarkTypeList},
-	"filter-out":                          {baseName + ".filter_out", starlarkTypeList},
-	"firstword":                           {"!firstword", starlarkTypeString},
-	"get-vendor-board-platforms":          {"!get-vendor-board-platforms", starlarkTypeList}, // internal macro, used by is-board-platform, etc.
-	"info":                                {baseName + ".mkinfo", starlarkTypeVoid},
-	"is-android-codename":                 {"!is-android-codename", starlarkTypeBool},         // unused by product config
-	"is-android-codename-in-list":         {"!is-android-codename-in-list", starlarkTypeBool}, // unused by product config
-	"is-board-platform":                   {"!is-board-platform", starlarkTypeBool},
-	"is-board-platform-in-list":           {"!is-board-platform-in-list", starlarkTypeBool},
-	"is-chipset-in-board-platform":        {"!is-chipset-in-board-platform", starlarkTypeUnknown},     // unused by product config
-	"is-chipset-prefix-in-board-platform": {"!is-chipset-prefix-in-board-platform", starlarkTypeBool}, // unused by product config
-	"is-not-board-platform":               {"!is-not-board-platform", starlarkTypeBool},               // defined but never used
-	"is-platform-sdk-version-at-least":    {"!is-platform-sdk-version-at-least", starlarkTypeBool},    // unused by product config
-	"is-product-in-list":                  {"!is-product-in-list", starlarkTypeBool},
-	"is-vendor-board-platform":            {"!is-vendor-board-platform", starlarkTypeBool},
-	callLoadAlways:                        {"!inherit-product", starlarkTypeVoid},
-	callLoadIf:                            {"!inherit-product-if-exists", starlarkTypeVoid},
-	"lastword":                            {"!lastword", starlarkTypeString},
-	"match-prefix":                        {"!match-prefix", starlarkTypeUnknown},       // internal macro
-	"match-word":                          {"!match-word", starlarkTypeUnknown},         // internal macro
-	"match-word-in-list":                  {"!match-word-in-list", starlarkTypeUnknown}, // internal macro
-	"notdir":                              {baseName + ".notdir", starlarkTypeString},
-	"my-dir":                              {"!my-dir", starlarkTypeString},
-	"patsubst":                            {baseName + ".mkpatsubst", starlarkTypeString},
-	"produce_copy_files":                  {baseName + ".produce_copy_files", starlarkTypeList},
-	"require-artifacts-in-path":           {baseName + ".require_artifacts_in_path", starlarkTypeVoid},
-	"require-artifacts-in-path-relaxed":   {baseName + ".require_artifacts_in_path_relaxed", starlarkTypeVoid},
+	"abspath":                             {baseName + ".abspath", starlarkTypeString, hiddenArgNone},
+	fileExistsPhony:                       {baseName + ".file_exists", starlarkTypeBool, hiddenArgNone},
+	wildcardExistsPhony:                   {baseName + ".file_wildcard_exists", starlarkTypeBool, hiddenArgNone},
+	addSoongNamespace:                     {baseName + ".add_soong_config_namespace", starlarkTypeVoid, hiddenArgGlobal},
+	addSoongConfigVarValue:                {baseName + ".add_soong_config_var_value", starlarkTypeVoid, hiddenArgGlobal},
+	"add-to-product-copy-files-if-exists": {baseName + ".copy_if_exists", starlarkTypeList, hiddenArgNone},
+	"addprefix":                           {baseName + ".addprefix", starlarkTypeList, hiddenArgNone},
+	"addsuffix":                           {baseName + ".addsuffix", starlarkTypeList, hiddenArgNone},
+	"copy-files":                          {baseName + ".copy_files", starlarkTypeList, hiddenArgNone},
+	"dir":                                 {baseName + ".dir", starlarkTypeList, hiddenArgNone},
+	"enforce-product-packages-exist":      {baseName + ".enforce_product_packages_exist", starlarkTypeVoid, hiddenArgNone},
+	"error":                               {baseName + ".mkerror", starlarkTypeVoid, hiddenArgNone},
+	"findstring":                          {"!findstring", starlarkTypeInt, hiddenArgNone},
+	"find-copy-subdir-files":              {baseName + ".find_and_copy", starlarkTypeList, hiddenArgNone},
+	"find-word-in-list":                   {"!find-word-in-list", starlarkTypeUnknown, hiddenArgNone}, // internal macro
+	"filter":                              {baseName + ".filter", starlarkTypeList, hiddenArgNone},
+	"filter-out":                          {baseName + ".filter_out", starlarkTypeList, hiddenArgNone},
+	"firstword":                           {"!firstword", starlarkTypeString, hiddenArgNone},
+	"get-vendor-board-platforms":          {"!get-vendor-board-platforms", starlarkTypeList, hiddenArgNone}, // internal macro, used by is-board-platform, etc.
+	"info":                                {baseName + ".mkinfo", starlarkTypeVoid, hiddenArgNone},
+	"is-android-codename":                 {"!is-android-codename", starlarkTypeBool, hiddenArgNone},         // unused by product config
+	"is-android-codename-in-list":         {"!is-android-codename-in-list", starlarkTypeBool, hiddenArgNone}, // unused by product config
+	"is-board-platform":                   {"!is-board-platform", starlarkTypeBool, hiddenArgNone},
+	"is-board-platform-in-list":           {"!is-board-platform-in-list", starlarkTypeBool, hiddenArgNone},
+	"is-chipset-in-board-platform":        {"!is-chipset-in-board-platform", starlarkTypeUnknown, hiddenArgNone},     // unused by product config
+	"is-chipset-prefix-in-board-platform": {"!is-chipset-prefix-in-board-platform", starlarkTypeBool, hiddenArgNone}, // unused by product config
+	"is-not-board-platform":               {"!is-not-board-platform", starlarkTypeBool, hiddenArgNone},               // defined but never used
+	"is-platform-sdk-version-at-least":    {"!is-platform-sdk-version-at-least", starlarkTypeBool, hiddenArgNone},    // unused by product config
+	"is-product-in-list":                  {"!is-product-in-list", starlarkTypeBool, hiddenArgNone},
+	"is-vendor-board-platform":            {"!is-vendor-board-platform", starlarkTypeBool, hiddenArgNone},
+	callLoadAlways:                        {"!inherit-product", starlarkTypeVoid, hiddenArgNone},
+	callLoadIf:                            {"!inherit-product-if-exists", starlarkTypeVoid, hiddenArgNone},
+	"lastword":                            {"!lastword", starlarkTypeString, hiddenArgNone},
+	"match-prefix":                        {"!match-prefix", starlarkTypeUnknown, hiddenArgNone},       // internal macro
+	"match-word":                          {"!match-word", starlarkTypeUnknown, hiddenArgNone},         // internal macro
+	"match-word-in-list":                  {"!match-word-in-list", starlarkTypeUnknown, hiddenArgNone}, // internal macro
+	"notdir":                              {baseName + ".notdir", starlarkTypeString, hiddenArgNone},
+	"my-dir":                              {"!my-dir", starlarkTypeString, hiddenArgNone},
+	"patsubst":                            {baseName + ".mkpatsubst", starlarkTypeString, hiddenArgNone},
+	"produce_copy_files":                  {baseName + ".produce_copy_files", starlarkTypeList, hiddenArgNone},
+	"require-artifacts-in-path":           {baseName + ".require_artifacts_in_path", starlarkTypeVoid, hiddenArgNone},
+	"require-artifacts-in-path-relaxed":   {baseName + ".require_artifacts_in_path_relaxed", starlarkTypeVoid, hiddenArgNone},
 	// TODO(asmundak): remove it once all calls are removed from configuration makefiles. see b/183161002
-	"shell":      {baseName + ".shell", starlarkTypeString},
-	"strip":      {baseName + ".mkstrip", starlarkTypeString},
-	"tb-modules": {"!tb-modules", starlarkTypeUnknown}, // defined in hardware/amlogic/tb_modules/tb_detect.mk, unused
-	"subst":      {baseName + ".mksubst", starlarkTypeString},
-	"warning":    {baseName + ".mkwarning", starlarkTypeVoid},
-	"word":       {baseName + "!word", starlarkTypeString},
-	"wildcard":   {baseName + ".expand_wildcard", starlarkTypeList},
+	"shell":      {baseName + ".shell", starlarkTypeString, hiddenArgNone},
+	"strip":      {baseName + ".mkstrip", starlarkTypeString, hiddenArgNone},
+	"tb-modules": {"!tb-modules", starlarkTypeUnknown, hiddenArgNone}, // defined in hardware/amlogic/tb_modules/tb_detect.mk, unused
+	"subst":      {baseName + ".mksubst", starlarkTypeString, hiddenArgNone},
+	"warning":    {baseName + ".mkwarning", starlarkTypeVoid, hiddenArgNone},
+	"word":       {baseName + "!word", starlarkTypeString, hiddenArgNone},
+	"wildcard":   {baseName + ".expand_wildcard", starlarkTypeList, hiddenArgNone},
 }
 
 var builtinFuncRex = regexp.MustCompile(
@@ -392,6 +398,7 @@
 	receiverStack    []nodeReceiver
 	outputDir        string
 	dependentModules map[string]*moduleInfo
+	soongNamespaces  map[string]map[string]bool
 }
 
 func newParseContext(ss *StarlarkScript, nodes []mkparser.Node) *parseContext {
@@ -443,6 +450,7 @@
 		builtinMakeVars:  map[string]starlarkExpr{},
 		variables:        make(map[string]variable),
 		dependentModules: make(map[string]*moduleInfo),
+		soongNamespaces:  make(map[string]map[string]bool),
 	}
 	ctx.pushVarAssignments()
 	for _, item := range predefined {
@@ -521,6 +529,12 @@
 		return
 	}
 	name := a.Name.Strings[0]
+	const soongNsPrefix = "SOONG_CONFIG_"
+	// Soong confuguration
+	if strings.HasPrefix(name, soongNsPrefix) {
+		ctx.handleSoongNsAssignment(strings.TrimPrefix(name, soongNsPrefix), a)
+		return
+	}
 	lhs := ctx.addVariable(name)
 	if lhs == nil {
 		ctx.errorf(a, "unknown variable %s", name)
@@ -584,6 +598,88 @@
 	ctx.receiver.newNode(asgn)
 }
 
+func (ctx *parseContext) handleSoongNsAssignment(name string, asgn *mkparser.Assignment) {
+	val := ctx.parseMakeString(asgn, asgn.Value)
+	if xBad, ok := val.(*badExpr); ok {
+		ctx.wrapBadExpr(xBad)
+		return
+	}
+	val, _ = val.eval(ctx.builtinMakeVars)
+
+	// Unfortunately, Soong namespaces can be set up by directly setting corresponding Make
+	// variables instead of via add_soong_config_namespace + add_soong_config_var_value.
+	// Try to divine the call from the assignment as follows:
+	if name == "NAMESPACES" {
+		// Upon seeng
+		//      SOONG_CONFIG_NAMESPACES += foo
+		//    remember that there is a namespace `foo` and act as we saw
+		//      $(call add_soong_config_namespace,foo)
+		s, ok := maybeString(val)
+		if !ok {
+			ctx.errorf(asgn, "cannot handle variables in SOONG_CONFIG_NAMESPACES assignment, please use add_soong_config_namespace instead")
+			return
+		}
+		for _, ns := range strings.Fields(s) {
+			ctx.addSoongNamespace(ns)
+			ctx.receiver.newNode(&exprNode{&callExpr{
+				name:       addSoongNamespace,
+				args:       []starlarkExpr{&stringLiteralExpr{ns}},
+				returnType: starlarkTypeVoid,
+			}})
+		}
+	} else {
+		// Upon seeing
+		//      SOONG_CONFIG_x_y = v
+		// find a namespace called `x` and act as if we encountered
+		//      $(call add_config_var_value(x,y,v)
+		// or check that `x_y` is a namespace, and then add the RHS of this assignment as variables in
+		// it.
+		// Emit an error in the ambiguous situation (namespaces `foo_bar` with a variable `baz`
+		// and `foo` with a variable `bar_baz`.
+		namespaceName := ""
+		if ctx.hasSoongNamespace(name) {
+			namespaceName = name
+		}
+		var varName string
+		for pos, ch := range name {
+			if !(ch == '_' && ctx.hasSoongNamespace(name[0:pos])) {
+				continue
+			}
+			if namespaceName != "" {
+				ctx.errorf(asgn, "ambiguous soong namespace (may be either `%s` or  `%s`)", namespaceName, name[0:pos])
+				return
+			}
+			namespaceName = name[0:pos]
+			varName = name[pos+1:]
+		}
+		if namespaceName == "" {
+			ctx.errorf(asgn, "cannot figure out Soong namespace, please use add_soong_config_var_value macro instead")
+			return
+		}
+		if varName == "" {
+			// Remember variables in this namespace
+			s, ok := maybeString(val)
+			if !ok {
+				ctx.errorf(asgn, "cannot handle variables in SOONG_CONFIG_ assignment, please use add_soong_config_var_value instead")
+				return
+			}
+			ctx.updateSoongNamespace(asgn.Type != "+=", namespaceName, strings.Fields(s))
+			return
+		}
+
+		// Finally, handle assignment to a namespace variable
+		if !ctx.hasNamespaceVar(namespaceName, varName) {
+			ctx.errorf(asgn, "no %s variable in %s namespace, please use add_soong_config_var_value instead", varName, namespaceName)
+			return
+		}
+		ctx.receiver.newNode(&exprNode{&callExpr{
+			name:       addSoongConfigVarValue,
+			args:       []starlarkExpr{&stringLiteralExpr{namespaceName}, &stringLiteralExpr{varName}, val},
+			returnType: starlarkTypeVoid,
+		}})
+	}
+}
+
 func (ctx *parseContext) buildConcatExpr(a *mkparser.Assignment) *concatExpr {
 	xConcat := &concatExpr{}
 	var xItemList *listExpr
@@ -1411,6 +1507,38 @@
 	return filepath.Join(ctx.outputDir, loadedModuleDir, loadedModuleName)
 }
 
+func (ctx *parseContext) addSoongNamespace(ns string) {
+	if _, ok := ctx.soongNamespaces[ns]; ok {
+		return
+	}
+	ctx.soongNamespaces[ns] = make(map[string]bool)
+}
+
+func (ctx *parseContext) hasSoongNamespace(name string) bool {
+	_, ok := ctx.soongNamespaces[name]
+	return ok
+}
+
+func (ctx *parseContext) updateSoongNamespace(replace bool, namespaceName string, varNames []string) {
+	ctx.addSoongNamespace(namespaceName)
+	vars := ctx.soongNamespaces[namespaceName]
+	if replace {
+		vars = make(map[string]bool)
+		ctx.soongNamespaces[namespaceName] = vars
+	}
+	for _, v := range varNames {
+		vars[v] = true
+	}
+}
+
+func (ctx *parseContext) hasNamespaceVar(namespaceName string, varName string) bool {
+	vars, ok := ctx.soongNamespaces[namespaceName]
+	if ok {
+		_, ok = vars[varName]
+	}
+	return ok
+}
+
 func (ss *StarlarkScript) String() string {
 	return NewGenerateContext(ss).emit()
 }