blob: dc360c35f0bd35534cbee085ad61f7aba0e21529 [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) {
Colin Crosse16ce362020-11-12 08:29:30 -0800398 rule := NewRuleBuilder().Sbox(PathForOutput(ctx, ""),
399 PathForOutput(ctx, "sbox.textproto"))
Dan Willemsen633c5022019-04-12 11:11:38 -0700400 addCommands(rule)
401
402 wantCommands := []string{
Colin Crosse16ce362020-11-12 08:29:30 -0800403 "__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",
404 "command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 tool2",
405 "command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3",
Dan Willemsen633c5022019-04-12 11:11:38 -0700406 }
407
Colin Crosse16ce362020-11-12 08:29:30 -0800408 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 -0700409
410 if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
411 t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
412 }
413
414 if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
415 t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
416 }
417 if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
418 t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
419 }
420 if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
421 t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
422 }
423 if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
424 t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
425 }
Colin Crossda71eda2020-02-21 16:55:19 -0800426 if g, w := rule.OrderOnlys(), wantOrderOnlys; !reflect.DeepEqual(w, g) {
427 t.Errorf("\nwant rule.OrderOnlys() = %#v\n got %#v", w, g)
428 }
Dan Willemsen633c5022019-04-12 11:11:38 -0700429
430 if g, w := rule.depFileMergerCmd(ctx, rule.DepFiles()).String(), wantDepMergerCommand; g != w {
431 t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
432 }
433 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800434}
435
436func testRuleBuilderFactory() Module {
437 module := &testRuleBuilderModule{}
438 module.AddProperties(&module.properties)
439 InitAndroidModule(module)
440 return module
441}
442
443type testRuleBuilderModule struct {
444 ModuleBase
445 properties struct {
Colin Cross3d680512020-11-13 16:23:53 -0800446 Srcs []string
Dan Willemsen633c5022019-04-12 11:11:38 -0700447
448 Restat bool
449 Sbox bool
Colin Crossfeec25b2019-01-30 17:32:39 -0800450 }
451}
452
453func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
Colin Cross3d680512020-11-13 16:23:53 -0800454 in := PathsForSource(ctx, t.properties.Srcs)
Colin Crosse16ce362020-11-12 08:29:30 -0800455 out := PathForModuleOut(ctx, "gen", ctx.ModuleName())
456 outDep := PathForModuleOut(ctx, "gen", ctx.ModuleName()+".d")
457 outDir := PathForModuleOut(ctx, "gen")
458 manifestPath := PathForModuleOut(ctx, "sbox.textproto")
Colin Crossfeec25b2019-01-30 17:32:39 -0800459
Colin Crosse16ce362020-11-12 08:29:30 -0800460 testRuleBuilder_Build(ctx, in, out, outDep, outDir, manifestPath, t.properties.Restat, t.properties.Sbox)
Colin Cross786cd6d2019-02-01 16:41:11 -0800461}
462
463type testRuleBuilderSingleton struct{}
464
465func testRuleBuilderSingletonFactory() Singleton {
466 return &testRuleBuilderSingleton{}
467}
468
469func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) {
470 in := PathForSource(ctx, "bar")
Colin Crosse16ce362020-11-12 08:29:30 -0800471 out := PathForOutput(ctx, "singleton/gen/baz")
472 outDep := PathForOutput(ctx, "singleton/gen/baz.d")
473 outDir := PathForOutput(ctx, "singleton/gen")
474 manifestPath := PathForOutput(ctx, "singleton/sbox.textproto")
475 testRuleBuilder_Build(ctx, Paths{in}, out, outDep, outDir, manifestPath, true, false)
Colin Cross786cd6d2019-02-01 16:41:11 -0800476}
477
Colin Crosse16ce362020-11-12 08:29:30 -0800478func testRuleBuilder_Build(ctx BuilderContext, in Paths, out, outDep, outDir, manifestPath WritablePath, restat, sbox bool) {
Colin Cross758290d2019-02-01 16:42:32 -0800479 rule := NewRuleBuilder()
Colin Cross786cd6d2019-02-01 16:41:11 -0800480
Dan Willemsen633c5022019-04-12 11:11:38 -0700481 if sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800482 rule.Sbox(outDir, manifestPath)
Dan Willemsen633c5022019-04-12 11:11:38 -0700483 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800484
Colin Cross3d680512020-11-13 16:23:53 -0800485 rule.Command().Tool(PathForSource(ctx, "cp")).Inputs(in).Output(out).ImplicitDepFile(outDep)
Dan Willemsen633c5022019-04-12 11:11:38 -0700486
487 if restat {
488 rule.Restat()
489 }
Colin Crossbaa676f2019-02-25 14:56:01 -0800490
Colin Crossfeec25b2019-01-30 17:32:39 -0800491 rule.Build(pctx, ctx, "rule", "desc")
492}
493
494func TestRuleBuilder_Build(t *testing.T) {
Colin Cross98be1bb2019-12-13 20:41:13 -0800495 fs := map[string][]byte{
496 "bar": nil,
497 "cp": nil,
498 }
499
Colin Crossfeec25b2019-01-30 17:32:39 -0800500 bp := `
501 rule_builder_test {
502 name: "foo",
Colin Cross3d680512020-11-13 16:23:53 -0800503 srcs: ["bar"],
Dan Willemsen633c5022019-04-12 11:11:38 -0700504 restat: true,
505 }
506 rule_builder_test {
507 name: "foo_sbox",
Colin Cross3d680512020-11-13 16:23:53 -0800508 srcs: ["bar"],
Dan Willemsen633c5022019-04-12 11:11:38 -0700509 sbox: true,
Colin Crossfeec25b2019-01-30 17:32:39 -0800510 }
511 `
512
Colin Cross98be1bb2019-12-13 20:41:13 -0800513 config := TestConfig(buildDir, nil, bp, fs)
Colin Crossae8600b2020-10-29 17:09:13 -0700514 ctx := NewTestContext(config)
Colin Cross4b49b762019-11-22 15:25:03 -0800515 ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
516 ctx.RegisterSingletonType("rule_builder_test", testRuleBuilderSingletonFactory)
Colin Crossae8600b2020-10-29 17:09:13 -0700517 ctx.Register()
Colin Crossfeec25b2019-01-30 17:32:39 -0800518
519 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
520 FailIfErrored(t, errs)
521 _, errs = ctx.PrepareBuildActions(config)
522 FailIfErrored(t, errs)
523
Colin Crosse16ce362020-11-12 08:29:30 -0800524 check := func(t *testing.T, params TestingBuildParams, wantCommand, wantOutput, wantDepfile string, wantRestat bool, extraImplicits, extraCmdDeps []string) {
Dan Willemsenc89b6f12019-08-29 14:47:40 -0700525 t.Helper()
Colin Cross3d680512020-11-13 16:23:53 -0800526 command := params.RuleParams.Command
Colin Crosse16ce362020-11-12 08:29:30 -0800527 re := regexp.MustCompile(" # hash of input list: [a-z0-9]*$")
Colin Cross3d680512020-11-13 16:23:53 -0800528 command = re.ReplaceAllLiteralString(command, "")
529 if command != wantCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700530 t.Errorf("\nwant RuleParams.Command = %q\n got %q", wantCommand, params.RuleParams.Command)
531 }
532
533 wantDeps := append([]string{"cp"}, extraCmdDeps...)
534 if !reflect.DeepEqual(params.RuleParams.CommandDeps, wantDeps) {
535 t.Errorf("\nwant RuleParams.CommandDeps = %q\n got %q", wantDeps, params.RuleParams.CommandDeps)
536 }
537
538 if params.RuleParams.Restat != wantRestat {
539 t.Errorf("want RuleParams.Restat = %v, got %v", wantRestat, params.RuleParams.Restat)
Colin Cross4c83e5c2019-02-25 14:54:28 -0800540 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800541
Colin Crosse16ce362020-11-12 08:29:30 -0800542 wantImplicits := append([]string{"bar"}, extraImplicits...)
543 if !reflect.DeepEqual(params.Implicits.Strings(), wantImplicits) {
Colin Cross4c83e5c2019-02-25 14:54:28 -0800544 t.Errorf("want Implicits = [%q], got %q", "bar", params.Implicits.Strings())
545 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800546
Colin Cross1d2cf042019-03-29 15:33:06 -0700547 if params.Output.String() != wantOutput {
548 t.Errorf("want Output = %q, got %q", wantOutput, params.Output)
Colin Cross4c83e5c2019-02-25 14:54:28 -0800549 }
Colin Crossbaa676f2019-02-25 14:56:01 -0800550
Dan Willemsen633c5022019-04-12 11:11:38 -0700551 if len(params.ImplicitOutputs) != 0 {
552 t.Errorf("want ImplicitOutputs = [], got %q", params.ImplicitOutputs.Strings())
553 }
554
555 if params.Depfile.String() != wantDepfile {
556 t.Errorf("want Depfile = %q, got %q", wantDepfile, params.Depfile)
557 }
558
559 if params.Deps != blueprint.DepsGCC {
560 t.Errorf("want Deps = %q, got %q", blueprint.DepsGCC, params.Deps)
Colin Crossbaa676f2019-02-25 14:56:01 -0800561 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800562 }
563
Colin Cross4c83e5c2019-02-25 14:54:28 -0800564 t.Run("module", func(t *testing.T) {
Colin Crosse16ce362020-11-12 08:29:30 -0800565 outFile := filepath.Join(buildDir, ".intermediates", "foo", "gen", "foo")
Colin Cross4c83e5c2019-02-25 14:54:28 -0800566 check(t, ctx.ModuleForTests("foo", "").Rule("rule"),
Dan Willemsen633c5022019-04-12 11:11:38 -0700567 "cp bar "+outFile,
Colin Crosse16ce362020-11-12 08:29:30 -0800568 outFile, outFile+".d", true, nil, nil)
Dan Willemsen633c5022019-04-12 11:11:38 -0700569 })
570 t.Run("sbox", func(t *testing.T) {
571 outDir := filepath.Join(buildDir, ".intermediates", "foo_sbox")
Colin Crosse16ce362020-11-12 08:29:30 -0800572 outFile := filepath.Join(outDir, "gen/foo_sbox")
573 depFile := filepath.Join(outDir, "gen/foo_sbox.d")
574 manifest := filepath.Join(outDir, "sbox.textproto")
Dan Willemsen633c5022019-04-12 11:11:38 -0700575 sbox := filepath.Join(buildDir, "host", config.PrebuiltOS(), "bin/sbox")
576 sandboxPath := shared.TempDirForOutDir(buildDir)
577
Colin Crosse16ce362020-11-12 08:29:30 -0800578 cmd := `rm -rf ` + outDir + `/gen && ` +
579 sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest
Dan Willemsen633c5022019-04-12 11:11:38 -0700580
Colin Crosse16ce362020-11-12 08:29:30 -0800581 check(t, ctx.ModuleForTests("foo_sbox", "").Output("gen/foo_sbox"),
582 cmd, outFile, depFile, false, []string{manifest}, []string{sbox})
Colin Cross4c83e5c2019-02-25 14:54:28 -0800583 })
584 t.Run("singleton", func(t *testing.T) {
Colin Crosse16ce362020-11-12 08:29:30 -0800585 outFile := filepath.Join(buildDir, "singleton/gen/baz")
Colin Cross4c83e5c2019-02-25 14:54:28 -0800586 check(t, ctx.SingletonForTests("rule_builder_test").Rule("rule"),
Colin Crosse16ce362020-11-12 08:29:30 -0800587 "cp bar "+outFile, outFile, outFile+".d", true, nil, nil)
Colin Cross4c83e5c2019-02-25 14:54:28 -0800588 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800589}
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700590
591func Test_ninjaEscapeExceptForSpans(t *testing.T) {
592 type args struct {
593 s string
594 spans [][2]int
595 }
596 tests := []struct {
597 name string
598 args args
599 want string
600 }{
601 {
602 name: "empty",
603 args: args{
604 s: "",
605 },
606 want: "",
607 },
608 {
609 name: "unescape none",
610 args: args{
611 s: "$abc",
612 },
613 want: "$$abc",
614 },
615 {
616 name: "unescape all",
617 args: args{
618 s: "$abc",
619 spans: [][2]int{{0, 4}},
620 },
621 want: "$abc",
622 },
623 {
624 name: "unescape first",
625 args: args{
626 s: "$abc$",
627 spans: [][2]int{{0, 1}},
628 },
629 want: "$abc$$",
630 },
631 {
632 name: "unescape last",
633 args: args{
634 s: "$abc$",
635 spans: [][2]int{{4, 5}},
636 },
637 want: "$$abc$",
638 },
639 {
640 name: "unescape middle",
641 args: args{
642 s: "$a$b$c$",
643 spans: [][2]int{{2, 5}},
644 },
645 want: "$$a$b$c$$",
646 },
647 {
648 name: "unescape multiple",
649 args: args{
650 s: "$a$b$c$",
651 spans: [][2]int{{2, 3}, {4, 5}},
652 },
653 want: "$$a$b$c$$",
654 },
655 }
656 for _, tt := range tests {
657 t.Run(tt.name, func(t *testing.T) {
658 if got := ninjaEscapeExceptForSpans(tt.args.s, tt.args.spans); got != tt.want {
659 t.Errorf("ninjaEscapeExceptForSpans() = %v, want %v", got, tt.want)
660 }
661 })
662 }
663}
Colin Cross3d680512020-11-13 16:23:53 -0800664
665func TestRuleBuilderHashInputs(t *testing.T) {
666 // The basic idea here is to verify that the command (in the case of a
667 // non-sbox rule) or the sbox textproto manifest contain a hash of the
668 // inputs.
669
670 // By including a hash of the inputs, we cause the rule to re-run if
671 // the list of inputs changes because the command line or a dependency
672 // changes.
673
674 bp := `
675 rule_builder_test {
676 name: "hash0",
677 srcs: ["in1.txt", "in2.txt"],
678 }
679 rule_builder_test {
680 name: "hash0_sbox",
681 srcs: ["in1.txt", "in2.txt"],
682 sbox: true,
683 }
684 rule_builder_test {
685 name: "hash1",
686 srcs: ["in1.txt", "in2.txt", "in3.txt"],
687 }
688 rule_builder_test {
689 name: "hash1_sbox",
690 srcs: ["in1.txt", "in2.txt", "in3.txt"],
691 sbox: true,
692 }
693 `
694 testcases := []struct {
695 name string
696 expectedHash string
697 }{
698 {
699 name: "hash0",
700 // sha256 value obtained from: echo -en 'in1.txt\nin2.txt' | sha256sum
701 expectedHash: "18da75b9b1cc74b09e365b4ca2e321b5d618f438cc632b387ad9dc2ab4b20e9d",
702 },
703 {
704 name: "hash1",
705 // sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
706 expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
707 },
708 }
709
710 config := TestConfig(buildDir, nil, bp, nil)
711 ctx := NewTestContext(config)
712 ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
713 ctx.Register()
714
715 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
716 FailIfErrored(t, errs)
717 _, errs = ctx.PrepareBuildActions(config)
718 FailIfErrored(t, errs)
719
720 for _, test := range testcases {
721 t.Run(test.name, func(t *testing.T) {
722 t.Run("sbox", func(t *testing.T) {
723 gen := ctx.ModuleForTests(test.name+"_sbox", "")
Colin Crosse16ce362020-11-12 08:29:30 -0800724 manifest := RuleBuilderSboxProtoForTests(t, gen.Output("sbox.textproto"))
725 hash := manifest.Commands[0].GetInputHash()
726
727 if g, w := hash, test.expectedHash; g != w {
728 t.Errorf("Expected has %q, got %q", w, g)
Colin Cross3d680512020-11-13 16:23:53 -0800729 }
730 })
731 t.Run("", func(t *testing.T) {
732 gen := ctx.ModuleForTests(test.name+"", "")
Colin Crosse16ce362020-11-12 08:29:30 -0800733 command := gen.Output("gen/" + test.name).RuleParams.Command
Colin Cross3d680512020-11-13 16:23:53 -0800734 if g, w := command, " # hash of input list: "+test.expectedHash; !strings.HasSuffix(g, w) {
735 t.Errorf("Expected command line to end with %q, got %q", w, g)
736 }
737 })
738 })
739 }
740}