aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2019-07-11 10:59:15 -0700
committerColin Cross <ccross@android.com>2019-07-16 11:12:04 -0700
commit0cb0d7b1c5548b763a06cb553b1adb6cb49b3930 (patch)
tree7c827e4f35e172ff5ecc4fa2035cb16e290bf71f
parent1563815780ab3ccfda01e63268f5beeab525f368 (diff)
downloadbuild_soong-0cb0d7b1c5548b763a06cb553b1adb6cb49b3930.tar.gz
build_soong-0cb0d7b1c5548b763a06cb553b1adb6cb49b3930.tar.bz2
build_soong-0cb0d7b1c5548b763a06cb553b1adb6cb49b3930.zip
Add rspfile support to RuleBuilder
Allow RuleBuilderCommands to use an rspfile with FlagWithRspFileInputList. This requires being more careful with ninja escaping, as the rspfile will be $out.rsp, and the value for $out may not be known yet so it must be inserted as a ninja variable that must not be escaped. Test: rule_builder_test.go Change-Id: Ifa91e24a0bb8f0ceeb5c9bfa5689be2a4ff3b9cd
-rw-r--r--android/rule_builder.go112
-rw-r--r--android/rule_builder_test.go103
2 files changed, 201 insertions, 14 deletions
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 5797bc26..1238ddc6 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -263,11 +263,36 @@ func (r *RuleBuilder) Tools() Paths {
return toolsList
}
-// Commands returns a slice containing a the built command line for each call to RuleBuilder.Command.
+// RspFileInputs returns the list of paths that were passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
+func (r *RuleBuilder) RspFileInputs() Paths {
+ var rspFileInputs Paths
+ for _, c := range r.commands {
+ if c.rspFileInputs != nil {
+ if rspFileInputs != nil {
+ panic("Multiple commands in a rule may not have rsp file inputs")
+ }
+ rspFileInputs = c.rspFileInputs
+ }
+ }
+
+ return rspFileInputs
+}
+
+// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
func (r *RuleBuilder) Commands() []string {
var commands []string
for _, c := range r.commands {
- commands = append(commands, c.buf.String())
+ commands = append(commands, c.String())
+ }
+ return commands
+}
+
+// NinjaEscapedCommands returns a slice containin 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
}
@@ -324,7 +349,7 @@ func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string
}
tools := r.Tools()
- commands := r.Commands()
+ commands := r.NinjaEscapedCommands()
outputs := r.Outputs()
if len(commands) == 0 {
@@ -334,7 +359,7 @@ func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string
panic("No outputs specified from any Commands")
}
- commandString := strings.Join(proptools.NinjaEscapeList(commands), " && ")
+ commandString := strings.Join(commands, " && ")
if r.sbox {
sboxOutputs := make([]string, len(outputs))
@@ -363,17 +388,27 @@ func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string
}
// Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
- // ImplicitOutputs. RuleBuilder never uses "$out", so the distinction between Outputs and ImplicitOutputs
- // doesn't matter.
+ // ImplicitOutputs. RuleBuilder only uses "$out" for the rsp file location, 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"
+ rspFileContent = "$in"
+ }
+
ctx.Build(pctx, BuildParams{
Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
- Command: commandString,
- CommandDeps: tools.Strings(),
- Restat: r.restat,
+ Command: commandString,
+ CommandDeps: tools.Strings(),
+ Restat: r.restat,
+ Rspfile: rspFile,
+ RspfileContent: rspFileContent,
}),
+ Inputs: rspFileInputs,
Implicits: r.Inputs(),
Output: output,
ImplicitOutputs: implicitOutputs,
@@ -388,11 +423,15 @@ func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string
// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
// space as a separator from the previous method.
type RuleBuilderCommand struct {
- buf strings.Builder
- inputs Paths
- outputs WritablePaths
- depFiles WritablePaths
- tools Paths
+ buf strings.Builder
+ inputs Paths
+ outputs WritablePaths
+ depFiles WritablePaths
+ tools Paths
+ rspFileInputs Paths
+
+ // spans [start,end) of the command that should not be ninja escaped
+ unescapedSpans [][2]int
sbox bool
sboxOutDir WritablePath
@@ -624,11 +663,56 @@ func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *Ru
return c.Text(flag + c.outputStr(path))
}
+// 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 {
+ if c.rspFileInputs != nil {
+ panic("FlagWithRspFileInputList cannot be called if rsp file inputs have already been provided")
+ }
+
+ // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
+ // generated.
+ if paths == nil {
+ paths = Paths{}
+ }
+
+ c.rspFileInputs = paths
+
+ rspFile := "$out.rsp"
+ c.FlagWithArg(flag, rspFile)
+ c.unescapedSpans = append(c.unescapedSpans, [2]int{c.buf.Len() - len(rspFile), c.buf.Len()})
+ return c
+}
+
// String returns the command line.
func (c *RuleBuilderCommand) String() string {
return c.buf.String()
}
+// String returns the command line.
+func (c *RuleBuilderCommand) NinjaEscapedString() string {
+ return ninjaEscapeExceptForSpans(c.String(), c.unescapedSpans)
+}
+
+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 cfbc2abe..6eba4f12 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -38,6 +38,7 @@ func pathContext() PathContext {
"ls": nil,
"turbine": nil,
"java": nil,
+ "javac": nil,
})
}
@@ -235,6 +236,34 @@ func ExampleRuleBuilderCommand_FlagWithList() {
// ls --sort=time,size
}
+func ExampleRuleBuilderCommand_FlagWithRspFileInputList() {
+ ctx := pathContext()
+ fmt.Println(NewRuleBuilder().Command().
+ Tool(PathForSource(ctx, "javac")).
+ FlagWithRspFileInputList("@", PathsForTesting("a.java", "b.java")).
+ NinjaEscapedString())
+ // Output:
+ // javac @$out.rsp
+}
+
+func ExampleRuleBuilderCommand_String() {
+ fmt.Println(NewRuleBuilder().Command().
+ Text("FOO=foo").
+ Text("echo $FOO").
+ String())
+ // Output:
+ // FOO=foo echo $FOO
+}
+
+func ExampleRuleBuilderCommand_NinjaEscapedString() {
+ fmt.Println(NewRuleBuilder().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,
@@ -503,3 +532,77 @@ func TestRuleBuilder_Build(t *testing.T) {
"cp bar "+outFile, outFile, outFile+".d", true, nil)
})
}
+
+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)
+ }
+ })
+ }
+}