Add explicit rspfile argument to RuleBuilderCommand.FlagWithRspFileInputList

Using $out.rsp as the rsp file adds extra complexity around keeping
the $ unescaped.  Make callers to FlagWithRspFileInputList provide
an explicit path for the rsp file instead.

Bug: 182612695
Test: rule_builder_test.go
Change-Id: I3f531d80c1efa8a9d09aac0a63790c5b11a9f0c6
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 22ae2b2..cc0bfa6 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
@@ -461,6 +476,8 @@
 	commands := r.NinjaEscapedCommands()
 	outputs := r.Outputs()
 	inputs := r.Inputs()
+	rspFileInputs := r.RspFileInputs()
+	rspFilePath := r.RspFile()
 
 	if len(commands) == 0 {
 		return
@@ -559,15 +576,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"
 	}
 
@@ -620,6 +636,7 @@
 	tools          Paths
 	packagedTools  []PackagingSpec
 	rspFileInputs  Paths
+	rspFile        WritablePath
 
 	// spans [start,end) of the command that should not be ninja escaped
 	unescapedSpans [][2]int
@@ -1020,8 +1037,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 +1051,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
 }