blob: fbf624ee364adb7acad63d4b39d50c2a0622c62e [file] [log] [blame]
Colin Crossfeec25b2019-01-30 17:32:39 -08001// Copyright 2019 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package android
16
17import (
Colin Cross758290d2019-02-01 16:42:32 -080018 "fmt"
Colin Crossfeec25b2019-01-30 17:32:39 -080019 "path/filepath"
20 "reflect"
Colin Cross3d680512020-11-13 16:23:53 -080021 "regexp"
Colin Cross758290d2019-02-01 16:42:32 -080022 "strings"
Colin Crossfeec25b2019-01-30 17:32:39 -080023 "testing"
Dan Willemsen633c5022019-04-12 11:11:38 -070024
25 "github.com/google/blueprint"
26
27 "android/soong/shared"
Colin Crossfeec25b2019-01-30 17:32:39 -080028)
29
Colin Crossf1a035e2020-11-16 17:32:30 -080030func builderContext() BuilderContext {
31 return BuilderContextForTesting(TestConfig("out", nil, "", map[string][]byte{
Colin Cross98be1bb2019-12-13 20:41:13 -080032 "ld": nil,
33 "a.o": nil,
34 "b.o": nil,
35 "cp": nil,
36 "a": nil,
37 "b": nil,
38 "ls": nil,
Jingwen Chence679d22020-09-23 04:30:02 +000039 "ln": nil,
Colin Cross98be1bb2019-12-13 20:41:13 -080040 "turbine": nil,
41 "java": nil,
42 "javac": nil,
43 }))
Colin Cross69f59a32019-02-15 10:39:37 -080044}
45
Colin Cross758290d2019-02-01 16:42:32 -080046func ExampleRuleBuilder() {
Colin Crossf1a035e2020-11-16 17:32:30 -080047 ctx := builderContext()
Colin Cross758290d2019-02-01 16:42:32 -080048
Colin Crossf1a035e2020-11-16 17:32:30 -080049 rule := NewRuleBuilder(pctx, ctx)
Colin Cross69f59a32019-02-15 10:39:37 -080050
51 rule.Command().
52 Tool(PathForSource(ctx, "ld")).
53 Inputs(PathsForTesting("a.o", "b.o")).
54 FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
Colin Cross758290d2019-02-01 16:42:32 -080055 rule.Command().Text("echo success")
56
57 // To add the command to the build graph:
Colin Crossf1a035e2020-11-16 17:32:30 -080058 // rule.Build("link", "link")
Colin Cross758290d2019-02-01 16:42:32 -080059
60 fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
61 fmt.Printf("tools: %q\n", rule.Tools())
62 fmt.Printf("inputs: %q\n", rule.Inputs())
63 fmt.Printf("outputs: %q\n", rule.Outputs())
64
65 // Output:
Colin Cross69f59a32019-02-15 10:39:37 -080066 // commands: "ld a.o b.o -o out/linked && echo success"
Colin Cross758290d2019-02-01 16:42:32 -080067 // tools: ["ld"]
68 // inputs: ["a.o" "b.o"]
Colin Cross69f59a32019-02-15 10:39:37 -080069 // outputs: ["out/linked"]
Colin Cross758290d2019-02-01 16:42:32 -080070}
71
Jingwen Chence679d22020-09-23 04:30:02 +000072func ExampleRuleBuilder_SymlinkOutputs() {
Colin Crossf1a035e2020-11-16 17:32:30 -080073 ctx := builderContext()
Jingwen Chence679d22020-09-23 04:30:02 +000074
Colin Crossf1a035e2020-11-16 17:32:30 -080075 rule := NewRuleBuilder(pctx, ctx)
Jingwen Chence679d22020-09-23 04:30:02 +000076
77 rule.Command().
78 Tool(PathForSource(ctx, "ln")).
79 FlagWithInput("-s ", PathForTesting("a.o")).
80 SymlinkOutput(PathForOutput(ctx, "a"))
81 rule.Command().Text("cp out/a out/b").
82 ImplicitSymlinkOutput(PathForOutput(ctx, "b"))
83
84 fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
85 fmt.Printf("tools: %q\n", rule.Tools())
86 fmt.Printf("inputs: %q\n", rule.Inputs())
87 fmt.Printf("outputs: %q\n", rule.Outputs())
88 fmt.Printf("symlink_outputs: %q\n", rule.SymlinkOutputs())
89
90 // Output:
91 // commands: "ln -s a.o out/a && cp out/a out/b"
92 // tools: ["ln"]
93 // inputs: ["a.o"]
94 // outputs: ["out/a" "out/b"]
95 // symlink_outputs: ["out/a" "out/b"]
96}
97
Colin Cross5cb5b092019-02-02 21:25:18 -080098func ExampleRuleBuilder_Temporary() {
Colin Crossf1a035e2020-11-16 17:32:30 -080099 ctx := builderContext()
Colin Cross5cb5b092019-02-02 21:25:18 -0800100
Colin Crossf1a035e2020-11-16 17:32:30 -0800101 rule := NewRuleBuilder(pctx, ctx)
Colin Cross69f59a32019-02-15 10:39:37 -0800102
103 rule.Command().
104 Tool(PathForSource(ctx, "cp")).
105 Input(PathForSource(ctx, "a")).
106 Output(PathForOutput(ctx, "b"))
107 rule.Command().
108 Tool(PathForSource(ctx, "cp")).
109 Input(PathForOutput(ctx, "b")).
110 Output(PathForOutput(ctx, "c"))
111 rule.Temporary(PathForOutput(ctx, "b"))
Colin Cross5cb5b092019-02-02 21:25:18 -0800112
113 fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
114 fmt.Printf("tools: %q\n", rule.Tools())
115 fmt.Printf("inputs: %q\n", rule.Inputs())
116 fmt.Printf("outputs: %q\n", rule.Outputs())
117
118 // Output:
Colin Cross69f59a32019-02-15 10:39:37 -0800119 // commands: "cp a out/b && cp out/b out/c"
Colin Cross5cb5b092019-02-02 21:25:18 -0800120 // tools: ["cp"]
121 // inputs: ["a"]
Colin Cross69f59a32019-02-15 10:39:37 -0800122 // outputs: ["out/c"]
Colin Cross5cb5b092019-02-02 21:25:18 -0800123}
124
125func ExampleRuleBuilder_DeleteTemporaryFiles() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800126 ctx := builderContext()
Colin Cross5cb5b092019-02-02 21:25:18 -0800127
Colin Crossf1a035e2020-11-16 17:32:30 -0800128 rule := NewRuleBuilder(pctx, ctx)
Colin Cross69f59a32019-02-15 10:39:37 -0800129
130 rule.Command().
131 Tool(PathForSource(ctx, "cp")).
132 Input(PathForSource(ctx, "a")).
133 Output(PathForOutput(ctx, "b"))
134 rule.Command().
135 Tool(PathForSource(ctx, "cp")).
136 Input(PathForOutput(ctx, "b")).
137 Output(PathForOutput(ctx, "c"))
138 rule.Temporary(PathForOutput(ctx, "b"))
Colin Cross5cb5b092019-02-02 21:25:18 -0800139 rule.DeleteTemporaryFiles()
140
141 fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
142 fmt.Printf("tools: %q\n", rule.Tools())
143 fmt.Printf("inputs: %q\n", rule.Inputs())
144 fmt.Printf("outputs: %q\n", rule.Outputs())
145
146 // Output:
Colin Cross69f59a32019-02-15 10:39:37 -0800147 // commands: "cp a out/b && cp out/b out/c && rm -f out/b"
Colin Cross5cb5b092019-02-02 21:25:18 -0800148 // tools: ["cp"]
149 // inputs: ["a"]
Colin Cross69f59a32019-02-15 10:39:37 -0800150 // outputs: ["out/c"]
Colin Cross5cb5b092019-02-02 21:25:18 -0800151}
152
Colin Crossdeabb942019-02-11 14:11:09 -0800153func ExampleRuleBuilder_Installs() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800154 ctx := builderContext()
Colin Crossdeabb942019-02-11 14:11:09 -0800155
Colin Crossf1a035e2020-11-16 17:32:30 -0800156 rule := NewRuleBuilder(pctx, ctx)
Colin Cross69f59a32019-02-15 10:39:37 -0800157
158 out := PathForOutput(ctx, "linked")
159
160 rule.Command().
161 Tool(PathForSource(ctx, "ld")).
162 Inputs(PathsForTesting("a.o", "b.o")).
163 FlagWithOutput("-o ", out)
164 rule.Install(out, "/bin/linked")
165 rule.Install(out, "/sbin/linked")
Colin Crossdeabb942019-02-11 14:11:09 -0800166
167 fmt.Printf("rule.Installs().String() = %q\n", rule.Installs().String())
168
169 // Output:
Colin Cross69f59a32019-02-15 10:39:37 -0800170 // rule.Installs().String() = "out/linked:/bin/linked out/linked:/sbin/linked"
Colin Crossdeabb942019-02-11 14:11:09 -0800171}
172
Colin Cross758290d2019-02-01 16:42:32 -0800173func ExampleRuleBuilderCommand() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800174 ctx := builderContext()
Colin Cross758290d2019-02-01 16:42:32 -0800175
Colin Crossf1a035e2020-11-16 17:32:30 -0800176 rule := NewRuleBuilder(pctx, ctx)
Colin Cross69f59a32019-02-15 10:39:37 -0800177
Colin Cross758290d2019-02-01 16:42:32 -0800178 // chained
Colin Cross69f59a32019-02-15 10:39:37 -0800179 rule.Command().
180 Tool(PathForSource(ctx, "ld")).
181 Inputs(PathsForTesting("a.o", "b.o")).
182 FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
Colin Cross758290d2019-02-01 16:42:32 -0800183
184 // unchained
185 cmd := rule.Command()
Colin Cross69f59a32019-02-15 10:39:37 -0800186 cmd.Tool(PathForSource(ctx, "ld"))
187 cmd.Inputs(PathsForTesting("a.o", "b.o"))
188 cmd.FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
Colin Cross758290d2019-02-01 16:42:32 -0800189
190 // mixed:
Colin Cross69f59a32019-02-15 10:39:37 -0800191 cmd = rule.Command().Tool(PathForSource(ctx, "ld"))
192 cmd.Inputs(PathsForTesting("a.o", "b.o"))
193 cmd.FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
Colin Cross758290d2019-02-01 16:42:32 -0800194}
195
196func ExampleRuleBuilderCommand_Flag() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800197 ctx := builderContext()
198 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross69f59a32019-02-15 10:39:37 -0800199 Tool(PathForSource(ctx, "ls")).Flag("-l"))
Colin Cross758290d2019-02-01 16:42:32 -0800200 // Output:
201 // ls -l
202}
203
Colin Cross92b7d582019-03-29 15:32:51 -0700204func ExampleRuleBuilderCommand_Flags() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800205 ctx := builderContext()
206 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross92b7d582019-03-29 15:32:51 -0700207 Tool(PathForSource(ctx, "ls")).Flags([]string{"-l", "-a"}))
208 // Output:
209 // ls -l -a
210}
211
Colin Cross758290d2019-02-01 16:42:32 -0800212func ExampleRuleBuilderCommand_FlagWithArg() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800213 ctx := builderContext()
214 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross69f59a32019-02-15 10:39:37 -0800215 Tool(PathForSource(ctx, "ls")).
Colin Cross758290d2019-02-01 16:42:32 -0800216 FlagWithArg("--sort=", "time"))
217 // Output:
218 // ls --sort=time
219}
220
Colin Crossc7ed0042019-02-11 14:11:09 -0800221func ExampleRuleBuilderCommand_FlagForEachArg() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800222 ctx := builderContext()
223 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross69f59a32019-02-15 10:39:37 -0800224 Tool(PathForSource(ctx, "ls")).
Colin Crossc7ed0042019-02-11 14:11:09 -0800225 FlagForEachArg("--sort=", []string{"time", "size"}))
226 // Output:
227 // ls --sort=time --sort=size
228}
229
Colin Cross758290d2019-02-01 16:42:32 -0800230func ExampleRuleBuilderCommand_FlagForEachInput() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800231 ctx := builderContext()
232 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross69f59a32019-02-15 10:39:37 -0800233 Tool(PathForSource(ctx, "turbine")).
234 FlagForEachInput("--classpath ", PathsForTesting("a.jar", "b.jar")))
Colin Cross758290d2019-02-01 16:42:32 -0800235 // Output:
236 // turbine --classpath a.jar --classpath b.jar
237}
238
239func ExampleRuleBuilderCommand_FlagWithInputList() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800240 ctx := builderContext()
241 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross69f59a32019-02-15 10:39:37 -0800242 Tool(PathForSource(ctx, "java")).
243 FlagWithInputList("-classpath=", PathsForTesting("a.jar", "b.jar"), ":"))
Colin Cross758290d2019-02-01 16:42:32 -0800244 // Output:
245 // java -classpath=a.jar:b.jar
246}
247
248func ExampleRuleBuilderCommand_FlagWithInput() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800249 ctx := builderContext()
250 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross69f59a32019-02-15 10:39:37 -0800251 Tool(PathForSource(ctx, "java")).
252 FlagWithInput("-classpath=", PathForSource(ctx, "a")))
Colin Cross758290d2019-02-01 16:42:32 -0800253 // Output:
254 // java -classpath=a
255}
256
257func ExampleRuleBuilderCommand_FlagWithList() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800258 ctx := builderContext()
259 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross69f59a32019-02-15 10:39:37 -0800260 Tool(PathForSource(ctx, "ls")).
Colin Cross758290d2019-02-01 16:42:32 -0800261 FlagWithList("--sort=", []string{"time", "size"}, ","))
262 // Output:
263 // ls --sort=time,size
264}
265
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700266func ExampleRuleBuilderCommand_FlagWithRspFileInputList() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800267 ctx := builderContext()
268 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700269 Tool(PathForSource(ctx, "javac")).
Colin Cross70c47412021-03-12 17:48:14 -0800270 FlagWithRspFileInputList("@", PathForOutput(ctx, "foo.rsp"), PathsForTesting("a.java", "b.java")).
271 String())
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700272 // Output:
Colin Cross70c47412021-03-12 17:48:14 -0800273 // javac @out/foo.rsp
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700274}
275
276func ExampleRuleBuilderCommand_String() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800277 ctx := builderContext()
278 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700279 Text("FOO=foo").
280 Text("echo $FOO").
281 String())
282 // Output:
283 // FOO=foo echo $FOO
284}
285
286func ExampleRuleBuilderCommand_NinjaEscapedString() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800287 ctx := builderContext()
288 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700289 Text("FOO=foo").
290 Text("echo $FOO").
291 NinjaEscapedString())
292 // Output:
293 // FOO=foo echo $$FOO
294}
295
Colin Crossfeec25b2019-01-30 17:32:39 -0800296func TestRuleBuilder(t *testing.T) {
Colin Cross69f59a32019-02-15 10:39:37 -0800297 fs := map[string][]byte{
Colin Crossda71eda2020-02-21 16:55:19 -0800298 "dep_fixer": nil,
299 "input": nil,
300 "Implicit": nil,
301 "Input": nil,
302 "OrderOnly": nil,
303 "OrderOnlys": nil,
304 "Tool": nil,
305 "input2": nil,
306 "tool2": nil,
307 "input3": nil,
Colin Cross69f59a32019-02-15 10:39:37 -0800308 }
309
Colin Crossf1a035e2020-11-16 17:32:30 -0800310 pathCtx := PathContextForTesting(TestConfig("out", nil, "", fs))
311 ctx := builderContextForTests{
312 PathContext: pathCtx,
313 }
Colin Cross69f59a32019-02-15 10:39:37 -0800314
Dan Willemsen633c5022019-04-12 11:11:38 -0700315 addCommands := func(rule *RuleBuilder) {
316 cmd := rule.Command().
317 DepFile(PathForOutput(ctx, "DepFile")).
318 Flag("Flag").
319 FlagWithArg("FlagWithArg=", "arg").
320 FlagWithDepFile("FlagWithDepFile=", PathForOutput(ctx, "depfile")).
321 FlagWithInput("FlagWithInput=", PathForSource(ctx, "input")).
322 FlagWithOutput("FlagWithOutput=", PathForOutput(ctx, "output")).
323 Implicit(PathForSource(ctx, "Implicit")).
324 ImplicitDepFile(PathForOutput(ctx, "ImplicitDepFile")).
325 ImplicitOutput(PathForOutput(ctx, "ImplicitOutput")).
326 Input(PathForSource(ctx, "Input")).
327 Output(PathForOutput(ctx, "Output")).
Colin Crossda71eda2020-02-21 16:55:19 -0800328 OrderOnly(PathForSource(ctx, "OrderOnly")).
Jingwen Chence679d22020-09-23 04:30:02 +0000329 SymlinkOutput(PathForOutput(ctx, "SymlinkOutput")).
330 ImplicitSymlinkOutput(PathForOutput(ctx, "ImplicitSymlinkOutput")).
Dan Willemsen633c5022019-04-12 11:11:38 -0700331 Text("Text").
332 Tool(PathForSource(ctx, "Tool"))
Colin Crossfeec25b2019-01-30 17:32:39 -0800333
Dan Willemsen633c5022019-04-12 11:11:38 -0700334 rule.Command().
335 Text("command2").
336 DepFile(PathForOutput(ctx, "depfile2")).
337 Input(PathForSource(ctx, "input2")).
338 Output(PathForOutput(ctx, "output2")).
Colin Crossda71eda2020-02-21 16:55:19 -0800339 OrderOnlys(PathsForSource(ctx, []string{"OrderOnlys"})).
Dan Willemsen633c5022019-04-12 11:11:38 -0700340 Tool(PathForSource(ctx, "tool2"))
Colin Crossfeec25b2019-01-30 17:32:39 -0800341
Dan Willemsen633c5022019-04-12 11:11:38 -0700342 // Test updates to the first command after the second command has been started
343 cmd.Text("after command2")
344 // Test updating a command when the previous update did not replace the cmd variable
345 cmd.Text("old cmd")
Colin Crossfeec25b2019-01-30 17:32:39 -0800346
Dan Willemsen633c5022019-04-12 11:11:38 -0700347 // Test a command that uses the output of a previous command as an input
348 rule.Command().
349 Text("command3").
350 Input(PathForSource(ctx, "input3")).
351 Input(PathForOutput(ctx, "output2")).
352 Output(PathForOutput(ctx, "output3"))
Colin Crossfeec25b2019-01-30 17:32:39 -0800353 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700354
Colin Cross69f59a32019-02-15 10:39:37 -0800355 wantInputs := PathsForSource(ctx, []string{"Implicit", "Input", "input", "input2", "input3"})
Jingwen Chence679d22020-09-23 04:30:02 +0000356 wantOutputs := PathsForOutput(ctx, []string{"ImplicitOutput", "ImplicitSymlinkOutput", "Output", "SymlinkOutput", "output", "output2", "output3"})
Colin Cross1d2cf042019-03-29 15:33:06 -0700357 wantDepFiles := PathsForOutput(ctx, []string{"DepFile", "depfile", "ImplicitDepFile", "depfile2"})
Colin Cross69f59a32019-02-15 10:39:37 -0800358 wantTools := PathsForSource(ctx, []string{"Tool", "tool2"})
Colin Crossda71eda2020-02-21 16:55:19 -0800359 wantOrderOnlys := PathsForSource(ctx, []string{"OrderOnly", "OrderOnlys"})
Jingwen Chence679d22020-09-23 04:30:02 +0000360 wantSymlinkOutputs := PathsForOutput(ctx, []string{"ImplicitSymlinkOutput", "SymlinkOutput"})
Colin Crossfeec25b2019-01-30 17:32:39 -0800361
Dan Willemsen633c5022019-04-12 11:11:38 -0700362 t.Run("normal", func(t *testing.T) {
Colin Crossf1a035e2020-11-16 17:32:30 -0800363 rule := NewRuleBuilder(pctx, ctx)
Dan Willemsen633c5022019-04-12 11:11:38 -0700364 addCommands(rule)
Colin Cross1d2cf042019-03-29 15:33:06 -0700365
Dan Willemsen633c5022019-04-12 11:11:38 -0700366 wantCommands := []string{
Jingwen Chence679d22020-09-23 04:30:02 +0000367 "out/DepFile Flag FlagWithArg=arg FlagWithDepFile=out/depfile FlagWithInput=input FlagWithOutput=out/output Input out/Output out/SymlinkOutput Text Tool after command2 old cmd",
Dan Willemsen633c5022019-04-12 11:11:38 -0700368 "command2 out/depfile2 input2 out/output2 tool2",
369 "command3 input3 out/output2 out/output3",
370 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700371
Dan Willemsen633c5022019-04-12 11:11:38 -0700372 wantDepMergerCommand := "out/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer out/DepFile out/depfile out/ImplicitDepFile out/depfile2"
373
374 if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
375 t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
376 }
377
378 if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
379 t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
380 }
381 if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
382 t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
383 }
Jingwen Chence679d22020-09-23 04:30:02 +0000384 if g, w := rule.SymlinkOutputs(), wantSymlinkOutputs; !reflect.DeepEqual(w, g) {
385 t.Errorf("\nwant rule.SymlinkOutputs() = %#v\n got %#v", w, g)
386 }
Dan Willemsen633c5022019-04-12 11:11:38 -0700387 if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
388 t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
389 }
390 if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
391 t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
392 }
Colin Crossda71eda2020-02-21 16:55:19 -0800393 if g, w := rule.OrderOnlys(), wantOrderOnlys; !reflect.DeepEqual(w, g) {
394 t.Errorf("\nwant rule.OrderOnlys() = %#v\n got %#v", w, g)
395 }
Dan Willemsen633c5022019-04-12 11:11:38 -0700396
Colin Crossf1a035e2020-11-16 17:32:30 -0800397 if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w {
Dan Willemsen633c5022019-04-12 11:11:38 -0700398 t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
399 }
400 })
401
402 t.Run("sbox", func(t *testing.T) {
Colin Crossf1a035e2020-11-16 17:32:30 -0800403 rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, ""),
Colin Crosse16ce362020-11-12 08:29:30 -0800404 PathForOutput(ctx, "sbox.textproto"))
Dan Willemsen633c5022019-04-12 11:11:38 -0700405 addCommands(rule)
406
407 wantCommands := []string{
Colin Crosse16ce362020-11-12 08:29:30 -0800408 "__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output Input __SBOX_SANDBOX_DIR__/out/Output __SBOX_SANDBOX_DIR__/out/SymlinkOutput Text Tool after command2 old cmd",
409 "command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 tool2",
410 "command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3",
Dan Willemsen633c5022019-04-12 11:11:38 -0700411 }
412
Colin Crosse16ce362020-11-12 08:29:30 -0800413 wantDepMergerCommand := "out/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
Dan Willemsen633c5022019-04-12 11:11:38 -0700414
415 if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
416 t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
417 }
418
419 if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
420 t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
421 }
422 if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
423 t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
424 }
425 if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
426 t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
427 }
428 if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
429 t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
430 }
Colin Crossda71eda2020-02-21 16:55:19 -0800431 if g, w := rule.OrderOnlys(), wantOrderOnlys; !reflect.DeepEqual(w, g) {
432 t.Errorf("\nwant rule.OrderOnlys() = %#v\n got %#v", w, g)
433 }
Dan Willemsen633c5022019-04-12 11:11:38 -0700434
Colin Crossf1a035e2020-11-16 17:32:30 -0800435 if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w {
Dan Willemsen633c5022019-04-12 11:11:38 -0700436 t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
437 }
438 })
Colin Crossba9e4032020-11-24 16:32:22 -0800439
440 t.Run("sbox tools", func(t *testing.T) {
441 rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, ""),
442 PathForOutput(ctx, "sbox.textproto")).SandboxTools()
443 addCommands(rule)
444
445 wantCommands := []string{
446 "__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output Input __SBOX_SANDBOX_DIR__/out/Output __SBOX_SANDBOX_DIR__/out/SymlinkOutput Text __SBOX_SANDBOX_DIR__/tools/src/Tool after command2 old cmd",
447 "command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/tools/src/tool2",
448 "command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3",
449 }
450
451 wantDepMergerCommand := "__SBOX_SANDBOX_DIR__/tools/out/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
452
453 if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
454 t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
455 }
456
457 if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
458 t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
459 }
460 if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
461 t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
462 }
463 if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
464 t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
465 }
466 if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
467 t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
468 }
469 if g, w := rule.OrderOnlys(), wantOrderOnlys; !reflect.DeepEqual(w, g) {
470 t.Errorf("\nwant rule.OrderOnlys() = %#v\n got %#v", w, g)
471 }
472
473 if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w {
474 t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
475 }
476 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800477}
478
479func testRuleBuilderFactory() Module {
480 module := &testRuleBuilderModule{}
481 module.AddProperties(&module.properties)
482 InitAndroidModule(module)
483 return module
484}
485
486type testRuleBuilderModule struct {
487 ModuleBase
488 properties struct {
Colin Cross3d680512020-11-13 16:23:53 -0800489 Srcs []string
Dan Willemsen633c5022019-04-12 11:11:38 -0700490
491 Restat bool
492 Sbox bool
Colin Crossfeec25b2019-01-30 17:32:39 -0800493 }
494}
495
496func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
Colin Cross3d680512020-11-13 16:23:53 -0800497 in := PathsForSource(ctx, t.properties.Srcs)
Colin Crosse16ce362020-11-12 08:29:30 -0800498 out := PathForModuleOut(ctx, "gen", ctx.ModuleName())
499 outDep := PathForModuleOut(ctx, "gen", ctx.ModuleName()+".d")
500 outDir := PathForModuleOut(ctx, "gen")
501 manifestPath := PathForModuleOut(ctx, "sbox.textproto")
Colin Crossfeec25b2019-01-30 17:32:39 -0800502
Colin Crosse16ce362020-11-12 08:29:30 -0800503 testRuleBuilder_Build(ctx, in, out, outDep, outDir, manifestPath, t.properties.Restat, t.properties.Sbox)
Colin Cross786cd6d2019-02-01 16:41:11 -0800504}
505
506type testRuleBuilderSingleton struct{}
507
508func testRuleBuilderSingletonFactory() Singleton {
509 return &testRuleBuilderSingleton{}
510}
511
512func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) {
513 in := PathForSource(ctx, "bar")
Colin Crosse16ce362020-11-12 08:29:30 -0800514 out := PathForOutput(ctx, "singleton/gen/baz")
515 outDep := PathForOutput(ctx, "singleton/gen/baz.d")
516 outDir := PathForOutput(ctx, "singleton/gen")
517 manifestPath := PathForOutput(ctx, "singleton/sbox.textproto")
518 testRuleBuilder_Build(ctx, Paths{in}, out, outDep, outDir, manifestPath, true, false)
Colin Cross786cd6d2019-02-01 16:41:11 -0800519}
520
Colin Crosse16ce362020-11-12 08:29:30 -0800521func testRuleBuilder_Build(ctx BuilderContext, in Paths, out, outDep, outDir, manifestPath WritablePath, restat, sbox bool) {
Colin Crossf1a035e2020-11-16 17:32:30 -0800522 rule := NewRuleBuilder(pctx, ctx)
Colin Cross786cd6d2019-02-01 16:41:11 -0800523
Dan Willemsen633c5022019-04-12 11:11:38 -0700524 if sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800525 rule.Sbox(outDir, manifestPath)
Dan Willemsen633c5022019-04-12 11:11:38 -0700526 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800527
Colin Cross3d680512020-11-13 16:23:53 -0800528 rule.Command().Tool(PathForSource(ctx, "cp")).Inputs(in).Output(out).ImplicitDepFile(outDep)
Dan Willemsen633c5022019-04-12 11:11:38 -0700529
530 if restat {
531 rule.Restat()
532 }
Colin Crossbaa676f2019-02-25 14:56:01 -0800533
Colin Crossf1a035e2020-11-16 17:32:30 -0800534 rule.Build("rule", "desc")
Colin Crossfeec25b2019-01-30 17:32:39 -0800535}
536
537func TestRuleBuilder_Build(t *testing.T) {
Colin Cross98be1bb2019-12-13 20:41:13 -0800538 fs := map[string][]byte{
539 "bar": nil,
540 "cp": nil,
541 }
542
Colin Crossfeec25b2019-01-30 17:32:39 -0800543 bp := `
544 rule_builder_test {
545 name: "foo",
Colin Cross3d680512020-11-13 16:23:53 -0800546 srcs: ["bar"],
Dan Willemsen633c5022019-04-12 11:11:38 -0700547 restat: true,
548 }
549 rule_builder_test {
550 name: "foo_sbox",
Colin Cross3d680512020-11-13 16:23:53 -0800551 srcs: ["bar"],
Dan Willemsen633c5022019-04-12 11:11:38 -0700552 sbox: true,
Colin Crossfeec25b2019-01-30 17:32:39 -0800553 }
554 `
555
Colin Cross98be1bb2019-12-13 20:41:13 -0800556 config := TestConfig(buildDir, nil, bp, fs)
Colin Crossae8600b2020-10-29 17:09:13 -0700557 ctx := NewTestContext(config)
Colin Cross4b49b762019-11-22 15:25:03 -0800558 ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
559 ctx.RegisterSingletonType("rule_builder_test", testRuleBuilderSingletonFactory)
Colin Crossae8600b2020-10-29 17:09:13 -0700560 ctx.Register()
Colin Crossfeec25b2019-01-30 17:32:39 -0800561
562 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
563 FailIfErrored(t, errs)
564 _, errs = ctx.PrepareBuildActions(config)
565 FailIfErrored(t, errs)
566
Colin Crosse16ce362020-11-12 08:29:30 -0800567 check := func(t *testing.T, params TestingBuildParams, wantCommand, wantOutput, wantDepfile string, wantRestat bool, extraImplicits, extraCmdDeps []string) {
Dan Willemsenc89b6f12019-08-29 14:47:40 -0700568 t.Helper()
Colin Cross3d680512020-11-13 16:23:53 -0800569 command := params.RuleParams.Command
Colin Crosse16ce362020-11-12 08:29:30 -0800570 re := regexp.MustCompile(" # hash of input list: [a-z0-9]*$")
Colin Cross3d680512020-11-13 16:23:53 -0800571 command = re.ReplaceAllLiteralString(command, "")
572 if command != wantCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700573 t.Errorf("\nwant RuleParams.Command = %q\n got %q", wantCommand, params.RuleParams.Command)
574 }
575
576 wantDeps := append([]string{"cp"}, extraCmdDeps...)
577 if !reflect.DeepEqual(params.RuleParams.CommandDeps, wantDeps) {
578 t.Errorf("\nwant RuleParams.CommandDeps = %q\n got %q", wantDeps, params.RuleParams.CommandDeps)
579 }
580
581 if params.RuleParams.Restat != wantRestat {
582 t.Errorf("want RuleParams.Restat = %v, got %v", wantRestat, params.RuleParams.Restat)
Colin Cross4c83e5c2019-02-25 14:54:28 -0800583 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800584
Colin Crosse16ce362020-11-12 08:29:30 -0800585 wantImplicits := append([]string{"bar"}, extraImplicits...)
586 if !reflect.DeepEqual(params.Implicits.Strings(), wantImplicits) {
Colin Cross4c83e5c2019-02-25 14:54:28 -0800587 t.Errorf("want Implicits = [%q], got %q", "bar", params.Implicits.Strings())
588 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800589
Colin Cross1d2cf042019-03-29 15:33:06 -0700590 if params.Output.String() != wantOutput {
591 t.Errorf("want Output = %q, got %q", wantOutput, params.Output)
Colin Cross4c83e5c2019-02-25 14:54:28 -0800592 }
Colin Crossbaa676f2019-02-25 14:56:01 -0800593
Dan Willemsen633c5022019-04-12 11:11:38 -0700594 if len(params.ImplicitOutputs) != 0 {
595 t.Errorf("want ImplicitOutputs = [], got %q", params.ImplicitOutputs.Strings())
596 }
597
598 if params.Depfile.String() != wantDepfile {
599 t.Errorf("want Depfile = %q, got %q", wantDepfile, params.Depfile)
600 }
601
602 if params.Deps != blueprint.DepsGCC {
603 t.Errorf("want Deps = %q, got %q", blueprint.DepsGCC, params.Deps)
Colin Crossbaa676f2019-02-25 14:56:01 -0800604 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800605 }
606
Colin Cross4c83e5c2019-02-25 14:54:28 -0800607 t.Run("module", func(t *testing.T) {
Colin Crosse16ce362020-11-12 08:29:30 -0800608 outFile := filepath.Join(buildDir, ".intermediates", "foo", "gen", "foo")
Colin Cross4c83e5c2019-02-25 14:54:28 -0800609 check(t, ctx.ModuleForTests("foo", "").Rule("rule"),
Dan Willemsen633c5022019-04-12 11:11:38 -0700610 "cp bar "+outFile,
Colin Crosse16ce362020-11-12 08:29:30 -0800611 outFile, outFile+".d", true, nil, nil)
Dan Willemsen633c5022019-04-12 11:11:38 -0700612 })
613 t.Run("sbox", func(t *testing.T) {
614 outDir := filepath.Join(buildDir, ".intermediates", "foo_sbox")
Colin Crosse16ce362020-11-12 08:29:30 -0800615 outFile := filepath.Join(outDir, "gen/foo_sbox")
616 depFile := filepath.Join(outDir, "gen/foo_sbox.d")
617 manifest := filepath.Join(outDir, "sbox.textproto")
Dan Willemsen633c5022019-04-12 11:11:38 -0700618 sbox := filepath.Join(buildDir, "host", config.PrebuiltOS(), "bin/sbox")
619 sandboxPath := shared.TempDirForOutDir(buildDir)
620
Colin Crosse16ce362020-11-12 08:29:30 -0800621 cmd := `rm -rf ` + outDir + `/gen && ` +
622 sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest
Dan Willemsen633c5022019-04-12 11:11:38 -0700623
Colin Crosse16ce362020-11-12 08:29:30 -0800624 check(t, ctx.ModuleForTests("foo_sbox", "").Output("gen/foo_sbox"),
625 cmd, outFile, depFile, false, []string{manifest}, []string{sbox})
Colin Cross4c83e5c2019-02-25 14:54:28 -0800626 })
627 t.Run("singleton", func(t *testing.T) {
Colin Crosse16ce362020-11-12 08:29:30 -0800628 outFile := filepath.Join(buildDir, "singleton/gen/baz")
Colin Cross4c83e5c2019-02-25 14:54:28 -0800629 check(t, ctx.SingletonForTests("rule_builder_test").Rule("rule"),
Colin Crosse16ce362020-11-12 08:29:30 -0800630 "cp bar "+outFile, outFile, outFile+".d", true, nil, nil)
Colin Cross4c83e5c2019-02-25 14:54:28 -0800631 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800632}
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700633
634func Test_ninjaEscapeExceptForSpans(t *testing.T) {
635 type args struct {
636 s string
637 spans [][2]int
638 }
639 tests := []struct {
640 name string
641 args args
642 want string
643 }{
644 {
645 name: "empty",
646 args: args{
647 s: "",
648 },
649 want: "",
650 },
651 {
652 name: "unescape none",
653 args: args{
654 s: "$abc",
655 },
656 want: "$$abc",
657 },
658 {
659 name: "unescape all",
660 args: args{
661 s: "$abc",
662 spans: [][2]int{{0, 4}},
663 },
664 want: "$abc",
665 },
666 {
667 name: "unescape first",
668 args: args{
669 s: "$abc$",
670 spans: [][2]int{{0, 1}},
671 },
672 want: "$abc$$",
673 },
674 {
675 name: "unescape last",
676 args: args{
677 s: "$abc$",
678 spans: [][2]int{{4, 5}},
679 },
680 want: "$$abc$",
681 },
682 {
683 name: "unescape middle",
684 args: args{
685 s: "$a$b$c$",
686 spans: [][2]int{{2, 5}},
687 },
688 want: "$$a$b$c$$",
689 },
690 {
691 name: "unescape multiple",
692 args: args{
693 s: "$a$b$c$",
694 spans: [][2]int{{2, 3}, {4, 5}},
695 },
696 want: "$$a$b$c$$",
697 },
698 }
699 for _, tt := range tests {
700 t.Run(tt.name, func(t *testing.T) {
701 if got := ninjaEscapeExceptForSpans(tt.args.s, tt.args.spans); got != tt.want {
702 t.Errorf("ninjaEscapeExceptForSpans() = %v, want %v", got, tt.want)
703 }
704 })
705 }
706}
Colin Cross3d680512020-11-13 16:23:53 -0800707
708func TestRuleBuilderHashInputs(t *testing.T) {
709 // The basic idea here is to verify that the command (in the case of a
710 // non-sbox rule) or the sbox textproto manifest contain a hash of the
711 // inputs.
712
713 // By including a hash of the inputs, we cause the rule to re-run if
714 // the list of inputs changes because the command line or a dependency
715 // changes.
716
717 bp := `
718 rule_builder_test {
719 name: "hash0",
720 srcs: ["in1.txt", "in2.txt"],
721 }
722 rule_builder_test {
723 name: "hash0_sbox",
724 srcs: ["in1.txt", "in2.txt"],
725 sbox: true,
726 }
727 rule_builder_test {
728 name: "hash1",
729 srcs: ["in1.txt", "in2.txt", "in3.txt"],
730 }
731 rule_builder_test {
732 name: "hash1_sbox",
733 srcs: ["in1.txt", "in2.txt", "in3.txt"],
734 sbox: true,
735 }
736 `
737 testcases := []struct {
738 name string
739 expectedHash string
740 }{
741 {
742 name: "hash0",
743 // sha256 value obtained from: echo -en 'in1.txt\nin2.txt' | sha256sum
744 expectedHash: "18da75b9b1cc74b09e365b4ca2e321b5d618f438cc632b387ad9dc2ab4b20e9d",
745 },
746 {
747 name: "hash1",
748 // sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
749 expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
750 },
751 }
752
753 config := TestConfig(buildDir, nil, bp, nil)
754 ctx := NewTestContext(config)
755 ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
756 ctx.Register()
757
758 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
759 FailIfErrored(t, errs)
760 _, errs = ctx.PrepareBuildActions(config)
761 FailIfErrored(t, errs)
762
763 for _, test := range testcases {
764 t.Run(test.name, func(t *testing.T) {
765 t.Run("sbox", func(t *testing.T) {
766 gen := ctx.ModuleForTests(test.name+"_sbox", "")
Colin Crosse16ce362020-11-12 08:29:30 -0800767 manifest := RuleBuilderSboxProtoForTests(t, gen.Output("sbox.textproto"))
768 hash := manifest.Commands[0].GetInputHash()
769
770 if g, w := hash, test.expectedHash; g != w {
771 t.Errorf("Expected has %q, got %q", w, g)
Colin Cross3d680512020-11-13 16:23:53 -0800772 }
773 })
774 t.Run("", func(t *testing.T) {
775 gen := ctx.ModuleForTests(test.name+"", "")
Colin Crosse16ce362020-11-12 08:29:30 -0800776 command := gen.Output("gen/" + test.name).RuleParams.Command
Colin Cross3d680512020-11-13 16:23:53 -0800777 if g, w := command, " # hash of input list: "+test.expectedHash; !strings.HasSuffix(g, w) {
778 t.Errorf("Expected command line to end with %q, got %q", w, g)
779 }
780 })
781 })
782 }
783}