Merge changes from topic "rule_builder_rsp"

* changes:
  Remove unescaped spans support from RuleBuilder
  Add explicit rspfile argument to RuleBuilderCommand.FlagWithRspFileInputList
  Ninja escape RuleBuilder rule params
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 84501fe..17f211b 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -385,6 +385,21 @@
 	return rspFileInputs
 }
 
+// RspFile returns the path to the rspfile that was passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
+func (r *RuleBuilder) RspFile() WritablePath {
+	var rspFile WritablePath
+	for _, c := range r.commands {
+		if c.rspFile != nil {
+			if rspFile != nil {
+				panic("Multiple commands in a rule may not have rsp file inputs")
+			}
+			rspFile = c.rspFile
+		}
+	}
+
+	return rspFile
+}
+
 // Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
 func (r *RuleBuilder) Commands() []string {
 	var commands []string
@@ -394,16 +409,6 @@
 	return commands
 }
 
-// NinjaEscapedCommands returns a slice containing the built command line after ninja escaping for each call to
-// RuleBuilder.Command.
-func (r *RuleBuilder) NinjaEscapedCommands() []string {
-	var commands []string
-	for _, c := range r.commands {
-		commands = append(commands, c.NinjaEscapedString())
-	}
-	return commands
-}
-
 // BuilderContext is a subset of ModuleContext and SingletonContext.
 type BuilderContext interface {
 	PathContext
@@ -458,9 +463,11 @@
 	}
 
 	tools := r.Tools()
-	commands := r.NinjaEscapedCommands()
+	commands := r.Commands()
 	outputs := r.Outputs()
 	inputs := r.Inputs()
+	rspFileInputs := r.RspFileInputs()
+	rspFilePath := r.RspFile()
 
 	if len(commands) == 0 {
 		return
@@ -530,7 +537,7 @@
 		}
 
 		// Create a rule to write the manifest as a the textproto.
-		WriteFileRule(r.ctx, r.sboxManifestPath, proto.MarshalTextString(&manifest))
+		WriteFileRule(r.ctx, r.sboxManifestPath, proptools.NinjaEscape(proto.MarshalTextString(&manifest)))
 
 		// Generate a new string to use as the command line of the sbox rule.  This uses
 		// a RuleBuilderCommand as a convenience method of building the command line, then
@@ -559,15 +566,14 @@
 	}
 
 	// Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
-	// ImplicitOutputs.  RuleBuilder only uses "$out" for the rsp file location, so the distinction between Outputs and
+	// ImplicitOutputs.  RuleBuilder doesn't use "$out", so the distinction between Outputs and
 	// ImplicitOutputs doesn't matter.
 	output := outputs[0]
 	implicitOutputs := outputs[1:]
 
 	var rspFile, rspFileContent string
-	rspFileInputs := r.RspFileInputs()
-	if rspFileInputs != nil {
-		rspFile = "$out.rsp"
+	if rspFilePath != nil {
+		rspFile = rspFilePath.String()
 		rspFileContent = "$in"
 	}
 
@@ -585,10 +591,10 @@
 
 	r.ctx.Build(r.pctx, BuildParams{
 		Rule: r.ctx.Rule(pctx, name, blueprint.RuleParams{
-			Command:        commandString,
-			CommandDeps:    tools.Strings(),
+			Command:        proptools.NinjaEscape(commandString),
+			CommandDeps:    proptools.NinjaEscapeList(tools.Strings()),
 			Restat:         r.restat,
-			Rspfile:        rspFile,
+			Rspfile:        proptools.NinjaEscape(rspFile),
 			RspfileContent: rspFileContent,
 			Pool:           pool,
 		}),
@@ -620,9 +626,7 @@
 	tools          Paths
 	packagedTools  []PackagingSpec
 	rspFileInputs  Paths
-
-	// spans [start,end) of the command that should not be ninja escaped
-	unescapedSpans [][2]int
+	rspFile        WritablePath
 }
 
 func (c *RuleBuilderCommand) addInput(path Path) string {
@@ -1020,8 +1024,9 @@
 }
 
 // FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with no separator
-// between them.  The paths will be written to the rspfile.
-func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, paths Paths) *RuleBuilderCommand {
+// between them.  The paths will be written to the rspfile.  If sbox is enabled, the rspfile must
+// be outside the sbox directory.
+func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, rspFile WritablePath, paths Paths) *RuleBuilderCommand {
 	if c.rspFileInputs != nil {
 		panic("FlagWithRspFileInputList cannot be called if rsp file inputs have already been provided")
 	}
@@ -1033,10 +1038,16 @@
 	}
 
 	c.rspFileInputs = paths
+	c.rspFile = rspFile
 
-	rspFile := "$out.rsp"
-	c.FlagWithArg(flag, rspFile)
-	c.unescapedSpans = append(c.unescapedSpans, [2]int{c.buf.Len() - len(rspFile), c.buf.Len()})
+	if c.rule.sbox {
+		if _, isRel, _ := maybeRelErr(c.rule.outDir.String(), rspFile.String()); isRel {
+			panic(fmt.Errorf("FlagWithRspFileInputList rspfile %q must not be inside out dir %q",
+				rspFile.String(), c.rule.outDir.String()))
+		}
+	}
+
+	c.FlagWithArg(flag, rspFile.String())
 	return c
 }
 
@@ -1045,11 +1056,6 @@
 	return c.buf.String()
 }
 
-// String returns the command line.
-func (c *RuleBuilderCommand) NinjaEscapedString() string {
-	return ninjaEscapeExceptForSpans(c.String(), c.unescapedSpans)
-}
-
 // RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox()
 // and returns sbox testproto generated by the RuleBuilder.
 func RuleBuilderSboxProtoForTests(t *testing.T, params TestingBuildParams) *sbox_proto.Manifest {
@@ -1063,25 +1069,6 @@
 	return &manifest
 }
 
-func ninjaEscapeExceptForSpans(s string, spans [][2]int) string {
-	if len(spans) == 0 {
-		return proptools.NinjaEscape(s)
-	}
-
-	sb := strings.Builder{}
-	sb.Grow(len(s) * 11 / 10)
-
-	i := 0
-	for _, span := range spans {
-		sb.WriteString(proptools.NinjaEscape(s[i:span[0]]))
-		sb.WriteString(s[span[0]:span[1]])
-		i = span[1]
-	}
-	sb.WriteString(proptools.NinjaEscape(s[i:]))
-
-	return sb.String()
-}
-
 func ninjaNameEscape(s string) string {
 	b := []byte(s)
 	escaped := false
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index 06ea124..080e236 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -267,10 +267,10 @@
 	ctx := builderContext()
 	fmt.Println(NewRuleBuilder(pctx, ctx).Command().
 		Tool(PathForSource(ctx, "javac")).
-		FlagWithRspFileInputList("@", PathsForTesting("a.java", "b.java")).
-		NinjaEscapedString())
+		FlagWithRspFileInputList("@", PathForOutput(ctx, "foo.rsp"), PathsForTesting("a.java", "b.java")).
+		String())
 	// Output:
-	// javac @$out.rsp
+	// javac @out/foo.rsp
 }
 
 func ExampleRuleBuilderCommand_String() {
@@ -283,16 +283,6 @@
 	// FOO=foo echo $FOO
 }
 
-func ExampleRuleBuilderCommand_NinjaEscapedString() {
-	ctx := builderContext()
-	fmt.Println(NewRuleBuilder(pctx, ctx).Command().
-		Text("FOO=foo").
-		Text("echo $FOO").
-		NinjaEscapedString())
-	// Output:
-	// FOO=foo echo $$FOO
-}
-
 func TestRuleBuilder(t *testing.T) {
 	fs := map[string][]byte{
 		"dep_fixer":  nil,
@@ -631,80 +621,6 @@
 	})
 }
 
-func Test_ninjaEscapeExceptForSpans(t *testing.T) {
-	type args struct {
-		s     string
-		spans [][2]int
-	}
-	tests := []struct {
-		name string
-		args args
-		want string
-	}{
-		{
-			name: "empty",
-			args: args{
-				s: "",
-			},
-			want: "",
-		},
-		{
-			name: "unescape none",
-			args: args{
-				s: "$abc",
-			},
-			want: "$$abc",
-		},
-		{
-			name: "unescape all",
-			args: args{
-				s:     "$abc",
-				spans: [][2]int{{0, 4}},
-			},
-			want: "$abc",
-		},
-		{
-			name: "unescape first",
-			args: args{
-				s:     "$abc$",
-				spans: [][2]int{{0, 1}},
-			},
-			want: "$abc$$",
-		},
-		{
-			name: "unescape last",
-			args: args{
-				s:     "$abc$",
-				spans: [][2]int{{4, 5}},
-			},
-			want: "$$abc$",
-		},
-		{
-			name: "unescape middle",
-			args: args{
-				s:     "$a$b$c$",
-				spans: [][2]int{{2, 5}},
-			},
-			want: "$$a$b$c$$",
-		},
-		{
-			name: "unescape multiple",
-			args: args{
-				s:     "$a$b$c$",
-				spans: [][2]int{{2, 3}, {4, 5}},
-			},
-			want: "$$a$b$c$$",
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if got := ninjaEscapeExceptForSpans(tt.args.s, tt.args.spans); got != tt.want {
-				t.Errorf("ninjaEscapeExceptForSpans() = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}
-
 func TestRuleBuilderHashInputs(t *testing.T) {
 	// The basic idea here is to verify that the command (in the case of a
 	// non-sbox rule) or the sbox textproto manifest contain a hash of the
diff --git a/android/test_suites.go b/android/test_suites.go
index 7ecb8d2..6b7b909 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -68,7 +68,7 @@
 		FlagWithOutput("-o ", outputFile).
 		FlagWithArg("-P ", "host/testcases").
 		FlagWithArg("-C ", testCasesDir.String()).
-		FlagWithRspFileInputList("-r ", installedPaths.Paths())
+		FlagWithRspFileInputList("-r ", outputFile.ReplaceExtension(ctx, "rsp"), installedPaths.Paths())
 	rule.Build("robolectric_tests_zip", "robolectric-tests.zip")
 
 	return outputFile
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 9fe5b17..5219ebc 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -440,7 +440,8 @@
 			command := builder.Command().BuiltTool("soong_zip").
 				Flag("-j").
 				FlagWithOutput("-o ", corpusZip)
-			command.FlagWithRspFileInputList("-r ", fuzzModule.corpus)
+			rspFile := corpusZip.ReplaceExtension(ctx, "rsp")
+			command.FlagWithRspFileInputList("-r ", rspFile, fuzzModule.corpus)
 			files = append(files, fileToZip{corpusZip, ""})
 		}
 
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index fdd1fec..4014fe0 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -528,10 +528,11 @@
 		ctx,
 		snapshotDir,
 		c.name+"-"+ctx.Config().DeviceName()+"_list")
+	rspFile := snapshotOutputList.ReplaceExtension(ctx, "rsp")
 	zipRule.Command().
 		Text("tr").
 		FlagWithArg("-d ", "\\'").
-		FlagWithRspFileInputList("< ", snapshotOutputs).
+		FlagWithRspFileInputList("< ", rspFile, snapshotOutputs).
 		FlagWithOutput("> ", snapshotOutputList)
 
 	zipRule.Temporary(snapshotOutputList)
diff --git a/cc/vndk.go b/cc/vndk.go
index 85028d0..b7047e9 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -827,10 +827,11 @@
 
 	// filenames in rspfile from FlagWithRspFileInputList might be single-quoted. Remove it with tr
 	snapshotOutputList := android.PathForOutput(ctx, snapshotDir, "android-vndk-"+ctx.DeviceConfig().DeviceArch()+"_list")
+	rspFile := snapshotOutputList.ReplaceExtension(ctx, "rsp")
 	zipRule.Command().
 		Text("tr").
 		FlagWithArg("-d ", "\\'").
-		FlagWithRspFileInputList("< ", snapshotOutputs).
+		FlagWithRspFileInputList("< ", rspFile, snapshotOutputs).
 		FlagWithOutput("> ", snapshotOutputList)
 
 	zipRule.Temporary(snapshotOutputList)
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index d131e94..199a7df 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -298,6 +298,14 @@
 			`,
 			expect: "echo foo > __SBOX_SANDBOX_DIR__/out/foo && cp __SBOX_SANDBOX_DIR__/out/foo __SBOX_SANDBOX_DIR__/out/out",
 		},
+		{
+			name: "$",
+			prop: `
+				out: ["out"],
+				cmd: "echo $$ > $(out)",
+			`,
+			expect: "echo $ > __SBOX_SANDBOX_DIR__/out/out",
+		},
 
 		{
 			name: "error empty location",
diff --git a/java/droiddoc.go b/java/droiddoc.go
index f0decec..da13c62 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -812,7 +812,7 @@
 		BuiltTool("soong_javac_wrapper").Tool(config.JavadocCmd(ctx)).
 		Flag(config.JavacVmFlags).
 		FlagWithArg("-encoding ", "UTF-8").
-		FlagWithRspFileInputList("@", srcs).
+		FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "javadoc.rsp"), srcs).
 		FlagWithInput("@", srcJarList)
 
 	// TODO(ccross): Remove this if- statement once we finish migration for all Doclava
@@ -1243,7 +1243,7 @@
 		Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED").
 		FlagWithArg("-encoding ", "UTF-8").
 		FlagWithArg("-source ", javaVersion.String()).
-		FlagWithRspFileInputList("@", srcs).
+		FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "metalava.rsp"), srcs).
 		FlagWithInput("@", srcJarList)
 
 	if javaHome := ctx.Config().Getenv("ANDROID_JAVA_HOME"); javaHome != "" {
@@ -1446,7 +1446,9 @@
 	// add a large number of inputs to a file without exceeding bash command length limits (which
 	// would happen if we use the WriteFile rule). The cp is needed because RuleBuilder sets the
 	// rsp file to be ${output}.rsp.
-	impCmd.Text("cp").FlagWithRspFileInputList("", cmd.GetImplicits()).Output(implicitsRsp)
+	impCmd.Text("cp").
+		FlagWithRspFileInputList("", android.PathForModuleOut(ctx, "metalava-implicits.rsp"), cmd.GetImplicits()).
+		Output(implicitsRsp)
 	impRule.Build("implicitsGen", "implicits generation")
 	cmd.Implicit(implicitsRsp)
 
@@ -1750,7 +1752,7 @@
 		Flag("-jar").
 		FlagWithOutput("-o ", p.stubsSrcJar).
 		FlagWithArg("-C ", srcDir.String()).
-		FlagWithRspFileInputList("-r ", srcPaths)
+		FlagWithRspFileInputList("-r ", p.stubsSrcJar.ReplaceExtension(ctx, "rsp"), srcPaths)
 
 	rule.Restat()
 
diff --git a/java/kotlin.go b/java/kotlin.go
index 8067ad5..2960f81 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -64,7 +64,9 @@
 		// Insert a second rule to write out the list of resources to a file.
 		commonSrcsList := android.PathForModuleOut(ctx, "kotlinc_common_srcs.list")
 		rule := android.NewRuleBuilder(pctx, ctx)
-		rule.Command().Text("cp").FlagWithRspFileInputList("", commonSrcFiles).Output(commonSrcsList)
+		rule.Command().Text("cp").
+			FlagWithRspFileInputList("", commonSrcsList.ReplaceExtension(ctx, "rsp"), commonSrcFiles).
+			Output(commonSrcsList)
 		rule.Build("kotlin_common_srcs_list", "kotlin common_srcs list")
 		return android.OptionalPathForPath(commonSrcsList)
 	}
diff --git a/java/lint.go b/java/lint.go
index 9f677db..fccd1a5 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -220,7 +220,9 @@
 		// Insert a second rule to write out the list of resources to a file.
 		resourcesList = android.PathForModuleOut(ctx, "lint", "resources.list")
 		resListRule := android.NewRuleBuilder(pctx, ctx)
-		resListRule.Command().Text("cp").FlagWithRspFileInputList("", l.resources).Output(resourcesList)
+		resListRule.Command().Text("cp").
+			FlagWithRspFileInputList("", resourcesList.ReplaceExtension(ctx, "rsp"), l.resources).
+			Output(resourcesList)
 		resListRule.Build("lint_resources_list", "lint resources list")
 		trackRSPDependency(l.resources, resourcesList)
 	}
@@ -241,7 +243,10 @@
 	// TODO(ccross): some of the files in l.srcs are generated sources and should be passed to
 	// lint separately.
 	srcsList := android.PathForModuleOut(ctx, "lint", "srcs.list")
-	rule.Command().Text("cp").FlagWithRspFileInputList("", l.srcs).Output(srcsList)
+	srcsListRsp := android.PathForModuleOut(ctx, "lint-srcs.list.rsp")
+	rule.Command().Text("cp").
+		FlagWithRspFileInputList("", srcsListRsp, l.srcs).
+		Output(srcsList)
 	trackRSPDependency(l.srcs, srcsList)
 
 	cmd := rule.Command().
@@ -635,7 +640,7 @@
 	rule.Command().BuiltTool("soong_zip").
 		FlagWithOutput("-o ", outputPath).
 		FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
-		FlagWithRspFileInputList("-r ", paths)
+		FlagWithRspFileInputList("-r ", outputPath.ReplaceExtension(ctx, "rsp"), paths)
 
 	rule.Build(outputPath.Base(), outputPath.Base())
 }