blob: c1d552198d99e4c237ec85f34dc4940a62437cdf [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 Cross69f59a32019-02-15 10:39:37 -080030func pathContext() PathContext {
Colin Cross98be1bb2019-12-13 20:41:13 -080031 return PathContextForTesting(TestConfig("out", nil, "", map[string][]byte{
32 "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() {
47 rule := NewRuleBuilder()
48
Colin Cross69f59a32019-02-15 10:39:37 -080049 ctx := pathContext()
50
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:
58 // rule.Build(pctx, ctx, "link", "link")
59
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() {
73 rule := NewRuleBuilder()
74
75 ctx := pathContext()
76
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() {
99 rule := NewRuleBuilder()
100
Colin Cross69f59a32019-02-15 10:39:37 -0800101 ctx := pathContext()
102
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() {
126 rule := NewRuleBuilder()
127
Colin Cross69f59a32019-02-15 10:39:37 -0800128 ctx := pathContext()
129
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() {
154 rule := NewRuleBuilder()
155
Colin Cross69f59a32019-02-15 10:39:37 -0800156 ctx := pathContext()
157
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() {
174 rule := NewRuleBuilder()
175
Colin Cross69f59a32019-02-15 10:39:37 -0800176 ctx := pathContext()
177
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 Cross69f59a32019-02-15 10:39:37 -0800197 ctx := pathContext()
Colin Cross758290d2019-02-01 16:42:32 -0800198 fmt.Println(NewRuleBuilder().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() {
205 ctx := pathContext()
206 fmt.Println(NewRuleBuilder().Command().
207 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 Cross69f59a32019-02-15 10:39:37 -0800213 ctx := pathContext()
Colin Cross758290d2019-02-01 16:42:32 -0800214 fmt.Println(NewRuleBuilder().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 Cross69f59a32019-02-15 10:39:37 -0800222 ctx := pathContext()
Colin Crossc7ed0042019-02-11 14:11:09 -0800223 fmt.Println(NewRuleBuilder().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 Cross69f59a32019-02-15 10:39:37 -0800231 ctx := pathContext()
Colin Cross758290d2019-02-01 16:42:32 -0800232 fmt.Println(NewRuleBuilder().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 Cross69f59a32019-02-15 10:39:37 -0800240 ctx := pathContext()
Colin Cross758290d2019-02-01 16:42:32 -0800241 fmt.Println(NewRuleBuilder().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 Cross69f59a32019-02-15 10:39:37 -0800249 ctx := pathContext()
Colin Cross758290d2019-02-01 16:42:32 -0800250 fmt.Println(NewRuleBuilder().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 Cross69f59a32019-02-15 10:39:37 -0800258 ctx := pathContext()
Colin Cross758290d2019-02-01 16:42:32 -0800259 fmt.Println(NewRuleBuilder().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() {
267 ctx := pathContext()
268 fmt.Println(NewRuleBuilder().Command().
269 Tool(PathForSource(ctx, "javac")).
270 FlagWithRspFileInputList("@", PathsForTesting("a.java", "b.java")).
271 NinjaEscapedString())
272 // Output:
273 // javac @$out.rsp
274}
275
276func ExampleRuleBuilderCommand_String() {
277 fmt.Println(NewRuleBuilder().Command().
278 Text("FOO=foo").
279 Text("echo $FOO").
280 String())
281 // Output:
282 // FOO=foo echo $FOO
283}
284
285func ExampleRuleBuilderCommand_NinjaEscapedString() {
286 fmt.Println(NewRuleBuilder().Command().
287 Text("FOO=foo").
288 Text("echo $FOO").
289 NinjaEscapedString())
290 // Output:
291 // FOO=foo echo $$FOO
292}
293
Colin Crossfeec25b2019-01-30 17:32:39 -0800294func TestRuleBuilder(t *testing.T) {
Colin Cross69f59a32019-02-15 10:39:37 -0800295 fs := map[string][]byte{
Colin Crossda71eda2020-02-21 16:55:19 -0800296 "dep_fixer": nil,
297 "input": nil,
298 "Implicit": nil,
299 "Input": nil,
300 "OrderOnly": nil,
301 "OrderOnlys": nil,
302 "Tool": nil,
303 "input2": nil,
304 "tool2": nil,
305 "input3": nil,
Colin Cross69f59a32019-02-15 10:39:37 -0800306 }
307
Colin Cross98be1bb2019-12-13 20:41:13 -0800308 ctx := PathContextForTesting(TestConfig("out", nil, "", fs))
Colin Cross69f59a32019-02-15 10:39:37 -0800309
Dan Willemsen633c5022019-04-12 11:11:38 -0700310 addCommands := func(rule *RuleBuilder) {
311 cmd := rule.Command().
312 DepFile(PathForOutput(ctx, "DepFile")).
313 Flag("Flag").
314 FlagWithArg("FlagWithArg=", "arg").
315 FlagWithDepFile("FlagWithDepFile=", PathForOutput(ctx, "depfile")).
316 FlagWithInput("FlagWithInput=", PathForSource(ctx, "input")).
317 FlagWithOutput("FlagWithOutput=", PathForOutput(ctx, "output")).
318 Implicit(PathForSource(ctx, "Implicit")).
319 ImplicitDepFile(PathForOutput(ctx, "ImplicitDepFile")).
320 ImplicitOutput(PathForOutput(ctx, "ImplicitOutput")).
321 Input(PathForSource(ctx, "Input")).
322 Output(PathForOutput(ctx, "Output")).
Colin Crossda71eda2020-02-21 16:55:19 -0800323 OrderOnly(PathForSource(ctx, "OrderOnly")).
Jingwen Chence679d22020-09-23 04:30:02 +0000324 SymlinkOutput(PathForOutput(ctx, "SymlinkOutput")).
325 ImplicitSymlinkOutput(PathForOutput(ctx, "ImplicitSymlinkOutput")).
Dan Willemsen633c5022019-04-12 11:11:38 -0700326 Text("Text").
327 Tool(PathForSource(ctx, "Tool"))
Colin Crossfeec25b2019-01-30 17:32:39 -0800328
Dan Willemsen633c5022019-04-12 11:11:38 -0700329 rule.Command().
330 Text("command2").
331 DepFile(PathForOutput(ctx, "depfile2")).
332 Input(PathForSource(ctx, "input2")).
333 Output(PathForOutput(ctx, "output2")).
Colin Crossda71eda2020-02-21 16:55:19 -0800334 OrderOnlys(PathsForSource(ctx, []string{"OrderOnlys"})).
Dan Willemsen633c5022019-04-12 11:11:38 -0700335 Tool(PathForSource(ctx, "tool2"))
Colin Crossfeec25b2019-01-30 17:32:39 -0800336
Dan Willemsen633c5022019-04-12 11:11:38 -0700337 // Test updates to the first command after the second command has been started
338 cmd.Text("after command2")
339 // Test updating a command when the previous update did not replace the cmd variable
340 cmd.Text("old cmd")
Colin Crossfeec25b2019-01-30 17:32:39 -0800341
Dan Willemsen633c5022019-04-12 11:11:38 -0700342 // Test a command that uses the output of a previous command as an input
343 rule.Command().
344 Text("command3").
345 Input(PathForSource(ctx, "input3")).
346 Input(PathForOutput(ctx, "output2")).
347 Output(PathForOutput(ctx, "output3"))
Colin Crossfeec25b2019-01-30 17:32:39 -0800348 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700349
Colin Cross69f59a32019-02-15 10:39:37 -0800350 wantInputs := PathsForSource(ctx, []string{"Implicit", "Input", "input", "input2", "input3"})
Jingwen Chence679d22020-09-23 04:30:02 +0000351 wantOutputs := PathsForOutput(ctx, []string{"ImplicitOutput", "ImplicitSymlinkOutput", "Output", "SymlinkOutput", "output", "output2", "output3"})
Colin Cross1d2cf042019-03-29 15:33:06 -0700352 wantDepFiles := PathsForOutput(ctx, []string{"DepFile", "depfile", "ImplicitDepFile", "depfile2"})
Colin Cross69f59a32019-02-15 10:39:37 -0800353 wantTools := PathsForSource(ctx, []string{"Tool", "tool2"})
Colin Crossda71eda2020-02-21 16:55:19 -0800354 wantOrderOnlys := PathsForSource(ctx, []string{"OrderOnly", "OrderOnlys"})
Jingwen Chence679d22020-09-23 04:30:02 +0000355 wantSymlinkOutputs := PathsForOutput(ctx, []string{"ImplicitSymlinkOutput", "SymlinkOutput"})
Colin Crossfeec25b2019-01-30 17:32:39 -0800356
Dan Willemsen633c5022019-04-12 11:11:38 -0700357 t.Run("normal", func(t *testing.T) {
358 rule := NewRuleBuilder()
359 addCommands(rule)
Colin Cross1d2cf042019-03-29 15:33:06 -0700360
Dan Willemsen633c5022019-04-12 11:11:38 -0700361 wantCommands := []string{
Jingwen Chence679d22020-09-23 04:30:02 +0000362 "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 -0700363 "command2 out/depfile2 input2 out/output2 tool2",
364 "command3 input3 out/output2 out/output3",
365 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700366
Dan Willemsen633c5022019-04-12 11:11:38 -0700367 wantDepMergerCommand := "out/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer out/DepFile out/depfile out/ImplicitDepFile out/depfile2"
368
369 if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
370 t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
371 }
372
373 if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
374 t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
375 }
376 if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
377 t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
378 }
Jingwen Chence679d22020-09-23 04:30:02 +0000379 if g, w := rule.SymlinkOutputs(), wantSymlinkOutputs; !reflect.DeepEqual(w, g) {
380 t.Errorf("\nwant rule.SymlinkOutputs() = %#v\n got %#v", w, g)
381 }
Dan Willemsen633c5022019-04-12 11:11:38 -0700382 if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
383 t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
384 }
385 if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
386 t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
387 }
Colin Crossda71eda2020-02-21 16:55:19 -0800388 if g, w := rule.OrderOnlys(), wantOrderOnlys; !reflect.DeepEqual(w, g) {
389 t.Errorf("\nwant rule.OrderOnlys() = %#v\n got %#v", w, g)
390 }
Dan Willemsen633c5022019-04-12 11:11:38 -0700391
392 if g, w := rule.depFileMergerCmd(ctx, rule.DepFiles()).String(), wantDepMergerCommand; g != w {
393 t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
394 }
395 })
396
397 t.Run("sbox", func(t *testing.T) {
398 rule := NewRuleBuilder().Sbox(PathForOutput(ctx))
399 addCommands(rule)
400
401 wantCommands := []string{
Jingwen Chence679d22020-09-23 04:30:02 +0000402 "__SBOX_OUT_DIR__/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_OUT_DIR__/depfile FlagWithInput=input FlagWithOutput=__SBOX_OUT_DIR__/output Input __SBOX_OUT_DIR__/Output __SBOX_OUT_DIR__/SymlinkOutput Text Tool after command2 old cmd",
Dan Willemsen633c5022019-04-12 11:11:38 -0700403 "command2 __SBOX_OUT_DIR__/depfile2 input2 __SBOX_OUT_DIR__/output2 tool2",
404 "command3 input3 __SBOX_OUT_DIR__/output2 __SBOX_OUT_DIR__/output3",
405 }
406
407 wantDepMergerCommand := "out/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer __SBOX_OUT_DIR__/DepFile __SBOX_OUT_DIR__/depfile __SBOX_OUT_DIR__/ImplicitDepFile __SBOX_OUT_DIR__/depfile2"
408
409 if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
410 t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
411 }
412
413 if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
414 t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
415 }
416 if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
417 t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
418 }
419 if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
420 t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
421 }
422 if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
423 t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
424 }
Colin Crossda71eda2020-02-21 16:55:19 -0800425 if g, w := rule.OrderOnlys(), wantOrderOnlys; !reflect.DeepEqual(w, g) {
426 t.Errorf("\nwant rule.OrderOnlys() = %#v\n got %#v", w, g)
427 }
Dan Willemsen633c5022019-04-12 11:11:38 -0700428
429 if g, w := rule.depFileMergerCmd(ctx, rule.DepFiles()).String(), wantDepMergerCommand; g != w {
430 t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
431 }
432 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800433}
434
435func testRuleBuilderFactory() Module {
436 module := &testRuleBuilderModule{}
437 module.AddProperties(&module.properties)
438 InitAndroidModule(module)
439 return module
440}
441
442type testRuleBuilderModule struct {
443 ModuleBase
444 properties struct {
Colin Cross3d680512020-11-13 16:23:53 -0800445 Srcs []string
Dan Willemsen633c5022019-04-12 11:11:38 -0700446
447 Restat bool
448 Sbox bool
Colin Crossfeec25b2019-01-30 17:32:39 -0800449 }
450}
451
452func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
Colin Cross3d680512020-11-13 16:23:53 -0800453 in := PathsForSource(ctx, t.properties.Srcs)
Colin Crossfeec25b2019-01-30 17:32:39 -0800454 out := PathForModuleOut(ctx, ctx.ModuleName())
Dan Willemsen633c5022019-04-12 11:11:38 -0700455 outDep := PathForModuleOut(ctx, ctx.ModuleName()+".d")
456 outDir := PathForModuleOut(ctx)
Colin Crossfeec25b2019-01-30 17:32:39 -0800457
Dan Willemsen633c5022019-04-12 11:11:38 -0700458 testRuleBuilder_Build(ctx, in, out, outDep, outDir, t.properties.Restat, t.properties.Sbox)
Colin Cross786cd6d2019-02-01 16:41:11 -0800459}
460
461type testRuleBuilderSingleton struct{}
462
463func testRuleBuilderSingletonFactory() Singleton {
464 return &testRuleBuilderSingleton{}
465}
466
467func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) {
468 in := PathForSource(ctx, "bar")
469 out := PathForOutput(ctx, "baz")
Dan Willemsen633c5022019-04-12 11:11:38 -0700470 outDep := PathForOutput(ctx, "baz.d")
471 outDir := PathForOutput(ctx)
Colin Cross3d680512020-11-13 16:23:53 -0800472 testRuleBuilder_Build(ctx, Paths{in}, out, outDep, outDir, true, false)
Colin Cross786cd6d2019-02-01 16:41:11 -0800473}
474
Colin Cross3d680512020-11-13 16:23:53 -0800475func testRuleBuilder_Build(ctx BuilderContext, in Paths, out, outDep, outDir WritablePath, restat, sbox bool) {
Colin Cross758290d2019-02-01 16:42:32 -0800476 rule := NewRuleBuilder()
Colin Cross786cd6d2019-02-01 16:41:11 -0800477
Dan Willemsen633c5022019-04-12 11:11:38 -0700478 if sbox {
479 rule.Sbox(outDir)
480 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800481
Colin Cross3d680512020-11-13 16:23:53 -0800482 rule.Command().Tool(PathForSource(ctx, "cp")).Inputs(in).Output(out).ImplicitDepFile(outDep)
Dan Willemsen633c5022019-04-12 11:11:38 -0700483
484 if restat {
485 rule.Restat()
486 }
Colin Crossbaa676f2019-02-25 14:56:01 -0800487
Colin Crossfeec25b2019-01-30 17:32:39 -0800488 rule.Build(pctx, ctx, "rule", "desc")
489}
490
491func TestRuleBuilder_Build(t *testing.T) {
Colin Cross98be1bb2019-12-13 20:41:13 -0800492 fs := map[string][]byte{
493 "bar": nil,
494 "cp": nil,
495 }
496
Colin Crossfeec25b2019-01-30 17:32:39 -0800497 bp := `
498 rule_builder_test {
499 name: "foo",
Colin Cross3d680512020-11-13 16:23:53 -0800500 srcs: ["bar"],
Dan Willemsen633c5022019-04-12 11:11:38 -0700501 restat: true,
502 }
503 rule_builder_test {
504 name: "foo_sbox",
Colin Cross3d680512020-11-13 16:23:53 -0800505 srcs: ["bar"],
Dan Willemsen633c5022019-04-12 11:11:38 -0700506 sbox: true,
Colin Crossfeec25b2019-01-30 17:32:39 -0800507 }
508 `
509
Colin Cross98be1bb2019-12-13 20:41:13 -0800510 config := TestConfig(buildDir, nil, bp, fs)
Colin Crossae8600b2020-10-29 17:09:13 -0700511 ctx := NewTestContext(config)
Colin Cross4b49b762019-11-22 15:25:03 -0800512 ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
513 ctx.RegisterSingletonType("rule_builder_test", testRuleBuilderSingletonFactory)
Colin Crossae8600b2020-10-29 17:09:13 -0700514 ctx.Register()
Colin Crossfeec25b2019-01-30 17:32:39 -0800515
516 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
517 FailIfErrored(t, errs)
518 _, errs = ctx.PrepareBuildActions(config)
519 FailIfErrored(t, errs)
520
Dan Willemsen633c5022019-04-12 11:11:38 -0700521 check := func(t *testing.T, params TestingBuildParams, wantCommand, wantOutput, wantDepfile string, wantRestat bool, extraCmdDeps []string) {
Dan Willemsenc89b6f12019-08-29 14:47:40 -0700522 t.Helper()
Colin Cross3d680512020-11-13 16:23:53 -0800523 command := params.RuleParams.Command
524 re := regexp.MustCompile(" (# hash of input list:|--input-hash) [a-z0-9]*")
525 command = re.ReplaceAllLiteralString(command, "")
526 if command != wantCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700527 t.Errorf("\nwant RuleParams.Command = %q\n got %q", wantCommand, params.RuleParams.Command)
528 }
529
530 wantDeps := append([]string{"cp"}, extraCmdDeps...)
531 if !reflect.DeepEqual(params.RuleParams.CommandDeps, wantDeps) {
532 t.Errorf("\nwant RuleParams.CommandDeps = %q\n got %q", wantDeps, params.RuleParams.CommandDeps)
533 }
534
535 if params.RuleParams.Restat != wantRestat {
536 t.Errorf("want RuleParams.Restat = %v, got %v", wantRestat, params.RuleParams.Restat)
Colin Cross4c83e5c2019-02-25 14:54:28 -0800537 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800538
Colin Cross4c83e5c2019-02-25 14:54:28 -0800539 if len(params.Implicits) != 1 || params.Implicits[0].String() != "bar" {
540 t.Errorf("want Implicits = [%q], got %q", "bar", params.Implicits.Strings())
541 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800542
Colin Cross1d2cf042019-03-29 15:33:06 -0700543 if params.Output.String() != wantOutput {
544 t.Errorf("want Output = %q, got %q", wantOutput, params.Output)
Colin Cross4c83e5c2019-02-25 14:54:28 -0800545 }
Colin Crossbaa676f2019-02-25 14:56:01 -0800546
Dan Willemsen633c5022019-04-12 11:11:38 -0700547 if len(params.ImplicitOutputs) != 0 {
548 t.Errorf("want ImplicitOutputs = [], got %q", params.ImplicitOutputs.Strings())
549 }
550
551 if params.Depfile.String() != wantDepfile {
552 t.Errorf("want Depfile = %q, got %q", wantDepfile, params.Depfile)
553 }
554
555 if params.Deps != blueprint.DepsGCC {
556 t.Errorf("want Deps = %q, got %q", blueprint.DepsGCC, params.Deps)
Colin Crossbaa676f2019-02-25 14:56:01 -0800557 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800558 }
559
Colin Cross4c83e5c2019-02-25 14:54:28 -0800560 t.Run("module", func(t *testing.T) {
Dan Willemsen633c5022019-04-12 11:11:38 -0700561 outFile := filepath.Join(buildDir, ".intermediates", "foo", "foo")
Colin Cross4c83e5c2019-02-25 14:54:28 -0800562 check(t, ctx.ModuleForTests("foo", "").Rule("rule"),
Dan Willemsen633c5022019-04-12 11:11:38 -0700563 "cp bar "+outFile,
564 outFile, outFile+".d", true, nil)
565 })
566 t.Run("sbox", func(t *testing.T) {
567 outDir := filepath.Join(buildDir, ".intermediates", "foo_sbox")
568 outFile := filepath.Join(outDir, "foo_sbox")
Dan Willemsenc89b6f12019-08-29 14:47:40 -0700569 depFile := filepath.Join(outDir, "foo_sbox.d")
Dan Willemsen633c5022019-04-12 11:11:38 -0700570 sbox := filepath.Join(buildDir, "host", config.PrebuiltOS(), "bin/sbox")
571 sandboxPath := shared.TempDirForOutDir(buildDir)
572
Dan Willemsenc89b6f12019-08-29 14:47:40 -0700573 cmd := sbox + ` -c 'cp bar __SBOX_OUT_DIR__/foo_sbox' --sandbox-path ` + sandboxPath + " --output-root " + outDir + " --depfile-out " + depFile + " __SBOX_OUT_DIR__/foo_sbox"
Dan Willemsen633c5022019-04-12 11:11:38 -0700574
575 check(t, ctx.ModuleForTests("foo_sbox", "").Rule("rule"),
Dan Willemsenc89b6f12019-08-29 14:47:40 -0700576 cmd, outFile, depFile, false, []string{sbox})
Colin Cross4c83e5c2019-02-25 14:54:28 -0800577 })
578 t.Run("singleton", func(t *testing.T) {
Dan Willemsen633c5022019-04-12 11:11:38 -0700579 outFile := filepath.Join(buildDir, "baz")
Colin Cross4c83e5c2019-02-25 14:54:28 -0800580 check(t, ctx.SingletonForTests("rule_builder_test").Rule("rule"),
Dan Willemsen633c5022019-04-12 11:11:38 -0700581 "cp bar "+outFile, outFile, outFile+".d", true, nil)
Colin Cross4c83e5c2019-02-25 14:54:28 -0800582 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800583}
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700584
585func Test_ninjaEscapeExceptForSpans(t *testing.T) {
586 type args struct {
587 s string
588 spans [][2]int
589 }
590 tests := []struct {
591 name string
592 args args
593 want string
594 }{
595 {
596 name: "empty",
597 args: args{
598 s: "",
599 },
600 want: "",
601 },
602 {
603 name: "unescape none",
604 args: args{
605 s: "$abc",
606 },
607 want: "$$abc",
608 },
609 {
610 name: "unescape all",
611 args: args{
612 s: "$abc",
613 spans: [][2]int{{0, 4}},
614 },
615 want: "$abc",
616 },
617 {
618 name: "unescape first",
619 args: args{
620 s: "$abc$",
621 spans: [][2]int{{0, 1}},
622 },
623 want: "$abc$$",
624 },
625 {
626 name: "unescape last",
627 args: args{
628 s: "$abc$",
629 spans: [][2]int{{4, 5}},
630 },
631 want: "$$abc$",
632 },
633 {
634 name: "unescape middle",
635 args: args{
636 s: "$a$b$c$",
637 spans: [][2]int{{2, 5}},
638 },
639 want: "$$a$b$c$$",
640 },
641 {
642 name: "unescape multiple",
643 args: args{
644 s: "$a$b$c$",
645 spans: [][2]int{{2, 3}, {4, 5}},
646 },
647 want: "$$a$b$c$$",
648 },
649 }
650 for _, tt := range tests {
651 t.Run(tt.name, func(t *testing.T) {
652 if got := ninjaEscapeExceptForSpans(tt.args.s, tt.args.spans); got != tt.want {
653 t.Errorf("ninjaEscapeExceptForSpans() = %v, want %v", got, tt.want)
654 }
655 })
656 }
657}
Colin Cross3d680512020-11-13 16:23:53 -0800658
659func TestRuleBuilderHashInputs(t *testing.T) {
660 // The basic idea here is to verify that the command (in the case of a
661 // non-sbox rule) or the sbox textproto manifest contain a hash of the
662 // inputs.
663
664 // By including a hash of the inputs, we cause the rule to re-run if
665 // the list of inputs changes because the command line or a dependency
666 // changes.
667
668 bp := `
669 rule_builder_test {
670 name: "hash0",
671 srcs: ["in1.txt", "in2.txt"],
672 }
673 rule_builder_test {
674 name: "hash0_sbox",
675 srcs: ["in1.txt", "in2.txt"],
676 sbox: true,
677 }
678 rule_builder_test {
679 name: "hash1",
680 srcs: ["in1.txt", "in2.txt", "in3.txt"],
681 }
682 rule_builder_test {
683 name: "hash1_sbox",
684 srcs: ["in1.txt", "in2.txt", "in3.txt"],
685 sbox: true,
686 }
687 `
688 testcases := []struct {
689 name string
690 expectedHash string
691 }{
692 {
693 name: "hash0",
694 // sha256 value obtained from: echo -en 'in1.txt\nin2.txt' | sha256sum
695 expectedHash: "18da75b9b1cc74b09e365b4ca2e321b5d618f438cc632b387ad9dc2ab4b20e9d",
696 },
697 {
698 name: "hash1",
699 // sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
700 expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
701 },
702 }
703
704 config := TestConfig(buildDir, nil, bp, nil)
705 ctx := NewTestContext(config)
706 ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
707 ctx.Register()
708
709 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
710 FailIfErrored(t, errs)
711 _, errs = ctx.PrepareBuildActions(config)
712 FailIfErrored(t, errs)
713
714 for _, test := range testcases {
715 t.Run(test.name, func(t *testing.T) {
716 t.Run("sbox", func(t *testing.T) {
717 gen := ctx.ModuleForTests(test.name+"_sbox", "")
718 command := gen.Output(test.name + "_sbox").RuleParams.Command
719 if g, w := command, " --input-hash "+test.expectedHash; !strings.Contains(g, w) {
720 t.Errorf("Expected command line to end with %q, got %q", w, g)
721 }
722 })
723 t.Run("", func(t *testing.T) {
724 gen := ctx.ModuleForTests(test.name+"", "")
725 command := gen.Output(test.name).RuleParams.Command
726 if g, w := command, " # hash of input list: "+test.expectedHash; !strings.HasSuffix(g, w) {
727 t.Errorf("Expected command line to end with %q, got %q", w, g)
728 }
729 })
730 })
731 }
732}