Merge "Add test fixture support"
diff --git a/androidmk/androidmk/androidmk.go b/androidmk/androidmk/androidmk.go
index 2d1bbb4..b8316a3 100644
--- a/androidmk/androidmk/androidmk.go
+++ b/androidmk/androidmk/androidmk.go
@@ -48,6 +48,22 @@
 	"-": "_dash_",
 }
 
+// Fix steps that should only run in the androidmk tool, i.e. should only be applied to
+// newly-converted Android.bp files.
+var fixSteps = bpfix.FixStepsExtension{
+	Name: "androidmk",
+	Steps: []bpfix.FixStep{
+		{
+			Name: "RewriteRuntimeResourceOverlay",
+			Fix:  bpfix.RewriteRuntimeResourceOverlay,
+		},
+	},
+}
+
+func init() {
+	bpfix.RegisterFixStepExtension(&fixSteps)
+}
+
 func (f *bpFile) insertComment(s string) {
 	f.comments = append(f.comments, &bpparser.CommentGroup{
 		Comments: []*bpparser.Comment{
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index 94b8252..fae6101 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -670,6 +670,26 @@
 	return nil
 }
 
+func RewriteRuntimeResourceOverlay(f *Fixer) error {
+	for _, def := range f.tree.Defs {
+		mod, ok := def.(*parser.Module)
+		if !(ok && mod.Type == "runtime_resource_overlay") {
+			continue
+		}
+		// runtime_resource_overlays are always product specific in Make.
+		if _, ok := mod.GetProperty("product_specific"); !ok {
+			prop := &parser.Property{
+				Name: "product_specific",
+				Value: &parser.Bool{
+					Value: true,
+				},
+			}
+			mod.Properties = append(mod.Properties, prop)
+		}
+	}
+	return nil
+}
+
 // Removes library dependencies which are empty (and restricted from usage in Soong)
 func removeEmptyLibDependencies(f *Fixer) error {
 	emptyLibraries := []string{
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index ef9814f..61dfe1a 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -1056,3 +1056,71 @@
 		})
 	}
 }
+
+func TestRewriteRuntimeResourceOverlay(t *testing.T) {
+	tests := []struct {
+		name string
+		in   string
+		out  string
+	}{
+		{
+			name: "product_specific runtime_resource_overlay",
+			in: `
+				runtime_resource_overlay {
+					name: "foo",
+					resource_dirs: ["res"],
+					product_specific: true,
+				}
+			`,
+			out: `
+				runtime_resource_overlay {
+					name: "foo",
+					resource_dirs: ["res"],
+					product_specific: true,
+				}
+			`,
+		},
+		{
+			// It's probably wrong for runtime_resource_overlay not to be product specific, but let's not
+			// debate it here.
+			name: "non-product_specific runtime_resource_overlay",
+			in: `
+				runtime_resource_overlay {
+					name: "foo",
+					resource_dirs: ["res"],
+					product_specific: false,
+				}
+			`,
+			out: `
+				runtime_resource_overlay {
+					name: "foo",
+					resource_dirs: ["res"],
+					product_specific: false,
+				}
+			`,
+		},
+		{
+			name: "runtime_resource_overlay without product_specific value",
+			in: `
+				runtime_resource_overlay {
+					name: "foo",
+					resource_dirs: ["res"],
+				}
+			`,
+			out: `
+				runtime_resource_overlay {
+					name: "foo",
+					resource_dirs: ["res"],
+					product_specific: true,
+				}
+			`,
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			runPass(t, test.in, test.out, func(fixer *Fixer) error {
+				return RewriteRuntimeResourceOverlay(fixer)
+			})
+		})
+	}
+}
diff --git a/cc/tidy.go b/cc/tidy.go
index 251c67b..616cf8a 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -42,6 +42,21 @@
 	Properties TidyProperties
 }
 
+var quotedFlagRegexp, _ = regexp.Compile(`^-?-[^=]+=('|").*('|")$`)
+
+// When passing flag -name=value, if user add quotes around 'value',
+// the quotation marks will be preserved by NinjaAndShellEscapeList
+// and the 'value' string with quotes won't work like the intended value.
+// So here we report an error if -*='*' is found.
+func checkNinjaAndShellEscapeList(ctx ModuleContext, prop string, slice []string) []string {
+	for _, s := range slice {
+		if quotedFlagRegexp.MatchString(s) {
+			ctx.PropertyErrorf(prop, "Extra quotes in: %s", s)
+		}
+	}
+	return proptools.NinjaAndShellEscapeList(slice)
+}
+
 func (tidy *tidyFeature) props() []interface{} {
 	return []interface{}{&tidy.Properties}
 }
@@ -74,8 +89,8 @@
 	if len(withTidyFlags) > 0 {
 		flags.TidyFlags = append(flags.TidyFlags, withTidyFlags)
 	}
-	esc := proptools.NinjaAndShellEscapeList
-	flags.TidyFlags = append(flags.TidyFlags, esc(tidy.Properties.Tidy_flags)...)
+	esc := checkNinjaAndShellEscapeList
+	flags.TidyFlags = append(flags.TidyFlags, esc(ctx, "tidy_flags", tidy.Properties.Tidy_flags)...)
 	// If TidyFlags does not contain -header-filter, add default header filter.
 	// Find the substring because the flag could also appear as --header-filter=...
 	// and with or without single or double quotes.
@@ -119,7 +134,7 @@
 		tidyChecks += config.TidyChecksForDir(ctx.ModuleDir())
 	}
 	if len(tidy.Properties.Tidy_checks) > 0 {
-		tidyChecks = tidyChecks + "," + strings.Join(esc(
+		tidyChecks = tidyChecks + "," + strings.Join(esc(ctx, "tidy_checks",
 			config.ClangRewriteTidyChecks(tidy.Properties.Tidy_checks)), ",")
 	}
 	if ctx.Windows() {
@@ -165,7 +180,7 @@
 			flags.TidyFlags = append(flags.TidyFlags, "-warnings-as-errors=-*")
 		}
 	} else if len(tidy.Properties.Tidy_checks_as_errors) > 0 {
-		tidyChecksAsErrors := "-warnings-as-errors=" + strings.Join(esc(tidy.Properties.Tidy_checks_as_errors), ",")
+		tidyChecksAsErrors := "-warnings-as-errors=" + strings.Join(esc(ctx, "tidy_checks_as_errors", tidy.Properties.Tidy_checks_as_errors), ",")
 		flags.TidyFlags = append(flags.TidyFlags, tidyChecksAsErrors)
 	}
 	return flags