blob: 52c32df5c7326d9546b3b7a813d9a5f0429d1cf7 [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 Cross758290d2019-02-01 16:42:32 -080021 "strings"
Colin Crossfeec25b2019-01-30 17:32:39 -080022 "testing"
Dan Willemsen633c5022019-04-12 11:11:38 -070023
24 "github.com/google/blueprint"
25
26 "android/soong/shared"
Colin Crossfeec25b2019-01-30 17:32:39 -080027)
28
Colin Cross69f59a32019-02-15 10:39:37 -080029func pathContext() PathContext {
30 return PathContextForTesting(TestConfig("out", nil),
31 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,
39 "turbine": nil,
40 "java": nil,
Colin Cross0cb0d7b2019-07-11 10:59:15 -070041 "javac": nil,
Colin Cross69f59a32019-02-15 10:39:37 -080042 })
43}
44
Colin Cross758290d2019-02-01 16:42:32 -080045func ExampleRuleBuilder() {
46 rule := NewRuleBuilder()
47
Colin Cross69f59a32019-02-15 10:39:37 -080048 ctx := pathContext()
49
50 rule.Command().
51 Tool(PathForSource(ctx, "ld")).
52 Inputs(PathsForTesting("a.o", "b.o")).
53 FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
Colin Cross758290d2019-02-01 16:42:32 -080054 rule.Command().Text("echo success")
55
56 // To add the command to the build graph:
57 // rule.Build(pctx, ctx, "link", "link")
58
59 fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
60 fmt.Printf("tools: %q\n", rule.Tools())
61 fmt.Printf("inputs: %q\n", rule.Inputs())
62 fmt.Printf("outputs: %q\n", rule.Outputs())
63
64 // Output:
Colin Cross69f59a32019-02-15 10:39:37 -080065 // commands: "ld a.o b.o -o out/linked && echo success"
Colin Cross758290d2019-02-01 16:42:32 -080066 // tools: ["ld"]
67 // inputs: ["a.o" "b.o"]
Colin Cross69f59a32019-02-15 10:39:37 -080068 // outputs: ["out/linked"]
Colin Cross758290d2019-02-01 16:42:32 -080069}
70
Colin Cross5cb5b092019-02-02 21:25:18 -080071func ExampleRuleBuilder_Temporary() {
72 rule := NewRuleBuilder()
73
Colin Cross69f59a32019-02-15 10:39:37 -080074 ctx := pathContext()
75
76 rule.Command().
77 Tool(PathForSource(ctx, "cp")).
78 Input(PathForSource(ctx, "a")).
79 Output(PathForOutput(ctx, "b"))
80 rule.Command().
81 Tool(PathForSource(ctx, "cp")).
82 Input(PathForOutput(ctx, "b")).
83 Output(PathForOutput(ctx, "c"))
84 rule.Temporary(PathForOutput(ctx, "b"))
Colin Cross5cb5b092019-02-02 21:25:18 -080085
86 fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
87 fmt.Printf("tools: %q\n", rule.Tools())
88 fmt.Printf("inputs: %q\n", rule.Inputs())
89 fmt.Printf("outputs: %q\n", rule.Outputs())
90
91 // Output:
Colin Cross69f59a32019-02-15 10:39:37 -080092 // commands: "cp a out/b && cp out/b out/c"
Colin Cross5cb5b092019-02-02 21:25:18 -080093 // tools: ["cp"]
94 // inputs: ["a"]
Colin Cross69f59a32019-02-15 10:39:37 -080095 // outputs: ["out/c"]
Colin Cross5cb5b092019-02-02 21:25:18 -080096}
97
98func ExampleRuleBuilder_DeleteTemporaryFiles() {
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 rule.DeleteTemporaryFiles()
113
114 fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
115 fmt.Printf("tools: %q\n", rule.Tools())
116 fmt.Printf("inputs: %q\n", rule.Inputs())
117 fmt.Printf("outputs: %q\n", rule.Outputs())
118
119 // Output:
Colin Cross69f59a32019-02-15 10:39:37 -0800120 // commands: "cp a out/b && cp out/b out/c && rm -f out/b"
Colin Cross5cb5b092019-02-02 21:25:18 -0800121 // tools: ["cp"]
122 // inputs: ["a"]
Colin Cross69f59a32019-02-15 10:39:37 -0800123 // outputs: ["out/c"]
Colin Cross5cb5b092019-02-02 21:25:18 -0800124}
125
Colin Crossdeabb942019-02-11 14:11:09 -0800126func ExampleRuleBuilder_Installs() {
127 rule := NewRuleBuilder()
128
Colin Cross69f59a32019-02-15 10:39:37 -0800129 ctx := pathContext()
130
131 out := PathForOutput(ctx, "linked")
132
133 rule.Command().
134 Tool(PathForSource(ctx, "ld")).
135 Inputs(PathsForTesting("a.o", "b.o")).
136 FlagWithOutput("-o ", out)
137 rule.Install(out, "/bin/linked")
138 rule.Install(out, "/sbin/linked")
Colin Crossdeabb942019-02-11 14:11:09 -0800139
140 fmt.Printf("rule.Installs().String() = %q\n", rule.Installs().String())
141
142 // Output:
Colin Cross69f59a32019-02-15 10:39:37 -0800143 // rule.Installs().String() = "out/linked:/bin/linked out/linked:/sbin/linked"
Colin Crossdeabb942019-02-11 14:11:09 -0800144}
145
Colin Cross758290d2019-02-01 16:42:32 -0800146func ExampleRuleBuilderCommand() {
147 rule := NewRuleBuilder()
148
Colin Cross69f59a32019-02-15 10:39:37 -0800149 ctx := pathContext()
150
Colin Cross758290d2019-02-01 16:42:32 -0800151 // chained
Colin Cross69f59a32019-02-15 10:39:37 -0800152 rule.Command().
153 Tool(PathForSource(ctx, "ld")).
154 Inputs(PathsForTesting("a.o", "b.o")).
155 FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
Colin Cross758290d2019-02-01 16:42:32 -0800156
157 // unchained
158 cmd := rule.Command()
Colin Cross69f59a32019-02-15 10:39:37 -0800159 cmd.Tool(PathForSource(ctx, "ld"))
160 cmd.Inputs(PathsForTesting("a.o", "b.o"))
161 cmd.FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
Colin Cross758290d2019-02-01 16:42:32 -0800162
163 // mixed:
Colin Cross69f59a32019-02-15 10:39:37 -0800164 cmd = rule.Command().Tool(PathForSource(ctx, "ld"))
165 cmd.Inputs(PathsForTesting("a.o", "b.o"))
166 cmd.FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
Colin Cross758290d2019-02-01 16:42:32 -0800167}
168
169func ExampleRuleBuilderCommand_Flag() {
Colin Cross69f59a32019-02-15 10:39:37 -0800170 ctx := pathContext()
Colin Cross758290d2019-02-01 16:42:32 -0800171 fmt.Println(NewRuleBuilder().Command().
Colin Cross69f59a32019-02-15 10:39:37 -0800172 Tool(PathForSource(ctx, "ls")).Flag("-l"))
Colin Cross758290d2019-02-01 16:42:32 -0800173 // Output:
174 // ls -l
175}
176
Colin Cross92b7d582019-03-29 15:32:51 -0700177func ExampleRuleBuilderCommand_Flags() {
178 ctx := pathContext()
179 fmt.Println(NewRuleBuilder().Command().
180 Tool(PathForSource(ctx, "ls")).Flags([]string{"-l", "-a"}))
181 // Output:
182 // ls -l -a
183}
184
Colin Cross758290d2019-02-01 16:42:32 -0800185func ExampleRuleBuilderCommand_FlagWithArg() {
Colin Cross69f59a32019-02-15 10:39:37 -0800186 ctx := pathContext()
Colin Cross758290d2019-02-01 16:42:32 -0800187 fmt.Println(NewRuleBuilder().Command().
Colin Cross69f59a32019-02-15 10:39:37 -0800188 Tool(PathForSource(ctx, "ls")).
Colin Cross758290d2019-02-01 16:42:32 -0800189 FlagWithArg("--sort=", "time"))
190 // Output:
191 // ls --sort=time
192}
193
Colin Crossc7ed0042019-02-11 14:11:09 -0800194func ExampleRuleBuilderCommand_FlagForEachArg() {
Colin Cross69f59a32019-02-15 10:39:37 -0800195 ctx := pathContext()
Colin Crossc7ed0042019-02-11 14:11:09 -0800196 fmt.Println(NewRuleBuilder().Command().
Colin Cross69f59a32019-02-15 10:39:37 -0800197 Tool(PathForSource(ctx, "ls")).
Colin Crossc7ed0042019-02-11 14:11:09 -0800198 FlagForEachArg("--sort=", []string{"time", "size"}))
199 // Output:
200 // ls --sort=time --sort=size
201}
202
Colin Cross758290d2019-02-01 16:42:32 -0800203func ExampleRuleBuilderCommand_FlagForEachInput() {
Colin Cross69f59a32019-02-15 10:39:37 -0800204 ctx := pathContext()
Colin Cross758290d2019-02-01 16:42:32 -0800205 fmt.Println(NewRuleBuilder().Command().
Colin Cross69f59a32019-02-15 10:39:37 -0800206 Tool(PathForSource(ctx, "turbine")).
207 FlagForEachInput("--classpath ", PathsForTesting("a.jar", "b.jar")))
Colin Cross758290d2019-02-01 16:42:32 -0800208 // Output:
209 // turbine --classpath a.jar --classpath b.jar
210}
211
212func ExampleRuleBuilderCommand_FlagWithInputList() {
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, "java")).
216 FlagWithInputList("-classpath=", PathsForTesting("a.jar", "b.jar"), ":"))
Colin Cross758290d2019-02-01 16:42:32 -0800217 // Output:
218 // java -classpath=a.jar:b.jar
219}
220
221func ExampleRuleBuilderCommand_FlagWithInput() {
Colin Cross69f59a32019-02-15 10:39:37 -0800222 ctx := pathContext()
Colin Cross758290d2019-02-01 16:42:32 -0800223 fmt.Println(NewRuleBuilder().Command().
Colin Cross69f59a32019-02-15 10:39:37 -0800224 Tool(PathForSource(ctx, "java")).
225 FlagWithInput("-classpath=", PathForSource(ctx, "a")))
Colin Cross758290d2019-02-01 16:42:32 -0800226 // Output:
227 // java -classpath=a
228}
229
230func ExampleRuleBuilderCommand_FlagWithList() {
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, "ls")).
Colin Cross758290d2019-02-01 16:42:32 -0800234 FlagWithList("--sort=", []string{"time", "size"}, ","))
235 // Output:
236 // ls --sort=time,size
237}
238
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700239func ExampleRuleBuilderCommand_FlagWithRspFileInputList() {
240 ctx := pathContext()
241 fmt.Println(NewRuleBuilder().Command().
242 Tool(PathForSource(ctx, "javac")).
243 FlagWithRspFileInputList("@", PathsForTesting("a.java", "b.java")).
244 NinjaEscapedString())
245 // Output:
246 // javac @$out.rsp
247}
248
249func ExampleRuleBuilderCommand_String() {
250 fmt.Println(NewRuleBuilder().Command().
251 Text("FOO=foo").
252 Text("echo $FOO").
253 String())
254 // Output:
255 // FOO=foo echo $FOO
256}
257
258func ExampleRuleBuilderCommand_NinjaEscapedString() {
259 fmt.Println(NewRuleBuilder().Command().
260 Text("FOO=foo").
261 Text("echo $FOO").
262 NinjaEscapedString())
263 // Output:
264 // FOO=foo echo $$FOO
265}
266
Colin Crossfeec25b2019-01-30 17:32:39 -0800267func TestRuleBuilder(t *testing.T) {
Colin Cross69f59a32019-02-15 10:39:37 -0800268 fs := map[string][]byte{
Colin Cross1d2cf042019-03-29 15:33:06 -0700269 "dep_fixer": nil,
270 "input": nil,
271 "Implicit": nil,
272 "Input": nil,
273 "Tool": nil,
274 "input2": nil,
275 "tool2": nil,
276 "input3": nil,
Colin Cross69f59a32019-02-15 10:39:37 -0800277 }
278
279 ctx := PathContextForTesting(TestConfig("out", nil), fs)
280
Dan Willemsen633c5022019-04-12 11:11:38 -0700281 addCommands := func(rule *RuleBuilder) {
282 cmd := rule.Command().
283 DepFile(PathForOutput(ctx, "DepFile")).
284 Flag("Flag").
285 FlagWithArg("FlagWithArg=", "arg").
286 FlagWithDepFile("FlagWithDepFile=", PathForOutput(ctx, "depfile")).
287 FlagWithInput("FlagWithInput=", PathForSource(ctx, "input")).
288 FlagWithOutput("FlagWithOutput=", PathForOutput(ctx, "output")).
289 Implicit(PathForSource(ctx, "Implicit")).
290 ImplicitDepFile(PathForOutput(ctx, "ImplicitDepFile")).
291 ImplicitOutput(PathForOutput(ctx, "ImplicitOutput")).
292 Input(PathForSource(ctx, "Input")).
293 Output(PathForOutput(ctx, "Output")).
294 Text("Text").
295 Tool(PathForSource(ctx, "Tool"))
Colin Crossfeec25b2019-01-30 17:32:39 -0800296
Dan Willemsen633c5022019-04-12 11:11:38 -0700297 rule.Command().
298 Text("command2").
299 DepFile(PathForOutput(ctx, "depfile2")).
300 Input(PathForSource(ctx, "input2")).
301 Output(PathForOutput(ctx, "output2")).
302 Tool(PathForSource(ctx, "tool2"))
Colin Crossfeec25b2019-01-30 17:32:39 -0800303
Dan Willemsen633c5022019-04-12 11:11:38 -0700304 // Test updates to the first command after the second command has been started
305 cmd.Text("after command2")
306 // Test updating a command when the previous update did not replace the cmd variable
307 cmd.Text("old cmd")
Colin Crossfeec25b2019-01-30 17:32:39 -0800308
Dan Willemsen633c5022019-04-12 11:11:38 -0700309 // Test a command that uses the output of a previous command as an input
310 rule.Command().
311 Text("command3").
312 Input(PathForSource(ctx, "input3")).
313 Input(PathForOutput(ctx, "output2")).
314 Output(PathForOutput(ctx, "output3"))
Colin Crossfeec25b2019-01-30 17:32:39 -0800315 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700316
Colin Cross69f59a32019-02-15 10:39:37 -0800317 wantInputs := PathsForSource(ctx, []string{"Implicit", "Input", "input", "input2", "input3"})
318 wantOutputs := PathsForOutput(ctx, []string{"ImplicitOutput", "Output", "output", "output2", "output3"})
Colin Cross1d2cf042019-03-29 15:33:06 -0700319 wantDepFiles := PathsForOutput(ctx, []string{"DepFile", "depfile", "ImplicitDepFile", "depfile2"})
Colin Cross69f59a32019-02-15 10:39:37 -0800320 wantTools := PathsForSource(ctx, []string{"Tool", "tool2"})
Colin Crossfeec25b2019-01-30 17:32:39 -0800321
Dan Willemsen633c5022019-04-12 11:11:38 -0700322 t.Run("normal", func(t *testing.T) {
323 rule := NewRuleBuilder()
324 addCommands(rule)
Colin Cross1d2cf042019-03-29 15:33:06 -0700325
Dan Willemsen633c5022019-04-12 11:11:38 -0700326 wantCommands := []string{
327 "out/DepFile Flag FlagWithArg=arg FlagWithDepFile=out/depfile FlagWithInput=input FlagWithOutput=out/output Input out/Output Text Tool after command2 old cmd",
328 "command2 out/depfile2 input2 out/output2 tool2",
329 "command3 input3 out/output2 out/output3",
330 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700331
Dan Willemsen633c5022019-04-12 11:11:38 -0700332 wantDepMergerCommand := "out/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer out/DepFile out/depfile out/ImplicitDepFile out/depfile2"
333
334 if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
335 t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
336 }
337
338 if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
339 t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
340 }
341 if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
342 t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
343 }
344 if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
345 t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
346 }
347 if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
348 t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
349 }
350
351 if g, w := rule.depFileMergerCmd(ctx, rule.DepFiles()).String(), wantDepMergerCommand; g != w {
352 t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
353 }
354 })
355
356 t.Run("sbox", func(t *testing.T) {
357 rule := NewRuleBuilder().Sbox(PathForOutput(ctx))
358 addCommands(rule)
359
360 wantCommands := []string{
361 "__SBOX_OUT_DIR__/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_OUT_DIR__/depfile FlagWithInput=input FlagWithOutput=__SBOX_OUT_DIR__/output Input __SBOX_OUT_DIR__/Output Text Tool after command2 old cmd",
362 "command2 __SBOX_OUT_DIR__/depfile2 input2 __SBOX_OUT_DIR__/output2 tool2",
363 "command3 input3 __SBOX_OUT_DIR__/output2 __SBOX_OUT_DIR__/output3",
364 }
365
366 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"
367
368 if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
369 t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
370 }
371
372 if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
373 t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
374 }
375 if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
376 t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
377 }
378 if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
379 t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
380 }
381 if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
382 t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
383 }
384
385 if g, w := rule.depFileMergerCmd(ctx, rule.DepFiles()).String(), wantDepMergerCommand; g != w {
386 t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
387 }
388 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800389}
390
391func testRuleBuilderFactory() Module {
392 module := &testRuleBuilderModule{}
393 module.AddProperties(&module.properties)
394 InitAndroidModule(module)
395 return module
396}
397
398type testRuleBuilderModule struct {
399 ModuleBase
400 properties struct {
401 Src string
Dan Willemsen633c5022019-04-12 11:11:38 -0700402
403 Restat bool
404 Sbox bool
Colin Crossfeec25b2019-01-30 17:32:39 -0800405 }
406}
407
408func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
Colin Crossfeec25b2019-01-30 17:32:39 -0800409 in := PathForSource(ctx, t.properties.Src)
410 out := PathForModuleOut(ctx, ctx.ModuleName())
Dan Willemsen633c5022019-04-12 11:11:38 -0700411 outDep := PathForModuleOut(ctx, ctx.ModuleName()+".d")
412 outDir := PathForModuleOut(ctx)
Colin Crossfeec25b2019-01-30 17:32:39 -0800413
Dan Willemsen633c5022019-04-12 11:11:38 -0700414 testRuleBuilder_Build(ctx, in, out, outDep, outDir, t.properties.Restat, t.properties.Sbox)
Colin Cross786cd6d2019-02-01 16:41:11 -0800415}
416
417type testRuleBuilderSingleton struct{}
418
419func testRuleBuilderSingletonFactory() Singleton {
420 return &testRuleBuilderSingleton{}
421}
422
423func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) {
424 in := PathForSource(ctx, "bar")
425 out := PathForOutput(ctx, "baz")
Dan Willemsen633c5022019-04-12 11:11:38 -0700426 outDep := PathForOutput(ctx, "baz.d")
427 outDir := PathForOutput(ctx)
428 testRuleBuilder_Build(ctx, in, out, outDep, outDir, true, false)
Colin Cross786cd6d2019-02-01 16:41:11 -0800429}
430
Dan Willemsen633c5022019-04-12 11:11:38 -0700431func testRuleBuilder_Build(ctx BuilderContext, in Path, out, outDep, outDir WritablePath, restat, sbox bool) {
Colin Cross758290d2019-02-01 16:42:32 -0800432 rule := NewRuleBuilder()
Colin Cross786cd6d2019-02-01 16:41:11 -0800433
Dan Willemsen633c5022019-04-12 11:11:38 -0700434 if sbox {
435 rule.Sbox(outDir)
436 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800437
Dan Willemsen633c5022019-04-12 11:11:38 -0700438 rule.Command().Tool(PathForSource(ctx, "cp")).Input(in).Output(out).ImplicitDepFile(outDep)
439
440 if restat {
441 rule.Restat()
442 }
Colin Crossbaa676f2019-02-25 14:56:01 -0800443
Colin Crossfeec25b2019-01-30 17:32:39 -0800444 rule.Build(pctx, ctx, "rule", "desc")
445}
446
447func TestRuleBuilder_Build(t *testing.T) {
Colin Crossfeec25b2019-01-30 17:32:39 -0800448 bp := `
449 rule_builder_test {
450 name: "foo",
451 src: "bar",
Dan Willemsen633c5022019-04-12 11:11:38 -0700452 restat: true,
453 }
454 rule_builder_test {
455 name: "foo_sbox",
456 src: "bar",
457 sbox: true,
Colin Crossfeec25b2019-01-30 17:32:39 -0800458 }
459 `
460
461 config := TestConfig(buildDir, nil)
462 ctx := NewTestContext()
463 ctx.MockFileSystem(map[string][]byte{
464 "Android.bp": []byte(bp),
465 "bar": nil,
466 "cp": nil,
467 })
Colin Cross4b49b762019-11-22 15:25:03 -0800468 ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
469 ctx.RegisterSingletonType("rule_builder_test", testRuleBuilderSingletonFactory)
Colin Crossfeec25b2019-01-30 17:32:39 -0800470 ctx.Register()
471
472 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
473 FailIfErrored(t, errs)
474 _, errs = ctx.PrepareBuildActions(config)
475 FailIfErrored(t, errs)
476
Dan Willemsen633c5022019-04-12 11:11:38 -0700477 check := func(t *testing.T, params TestingBuildParams, wantCommand, wantOutput, wantDepfile string, wantRestat bool, extraCmdDeps []string) {
Dan Willemsenc89b6f12019-08-29 14:47:40 -0700478 t.Helper()
Dan Willemsen633c5022019-04-12 11:11:38 -0700479 if params.RuleParams.Command != wantCommand {
480 t.Errorf("\nwant RuleParams.Command = %q\n got %q", wantCommand, params.RuleParams.Command)
481 }
482
483 wantDeps := append([]string{"cp"}, extraCmdDeps...)
484 if !reflect.DeepEqual(params.RuleParams.CommandDeps, wantDeps) {
485 t.Errorf("\nwant RuleParams.CommandDeps = %q\n got %q", wantDeps, params.RuleParams.CommandDeps)
486 }
487
488 if params.RuleParams.Restat != wantRestat {
489 t.Errorf("want RuleParams.Restat = %v, got %v", wantRestat, params.RuleParams.Restat)
Colin Cross4c83e5c2019-02-25 14:54:28 -0800490 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800491
Colin Cross4c83e5c2019-02-25 14:54:28 -0800492 if len(params.Implicits) != 1 || params.Implicits[0].String() != "bar" {
493 t.Errorf("want Implicits = [%q], got %q", "bar", params.Implicits.Strings())
494 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800495
Colin Cross1d2cf042019-03-29 15:33:06 -0700496 if params.Output.String() != wantOutput {
497 t.Errorf("want Output = %q, got %q", wantOutput, params.Output)
Colin Cross4c83e5c2019-02-25 14:54:28 -0800498 }
Colin Crossbaa676f2019-02-25 14:56:01 -0800499
Dan Willemsen633c5022019-04-12 11:11:38 -0700500 if len(params.ImplicitOutputs) != 0 {
501 t.Errorf("want ImplicitOutputs = [], got %q", params.ImplicitOutputs.Strings())
502 }
503
504 if params.Depfile.String() != wantDepfile {
505 t.Errorf("want Depfile = %q, got %q", wantDepfile, params.Depfile)
506 }
507
508 if params.Deps != blueprint.DepsGCC {
509 t.Errorf("want Deps = %q, got %q", blueprint.DepsGCC, params.Deps)
Colin Crossbaa676f2019-02-25 14:56:01 -0800510 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800511 }
512
Colin Cross4c83e5c2019-02-25 14:54:28 -0800513 t.Run("module", func(t *testing.T) {
Dan Willemsen633c5022019-04-12 11:11:38 -0700514 outFile := filepath.Join(buildDir, ".intermediates", "foo", "foo")
Colin Cross4c83e5c2019-02-25 14:54:28 -0800515 check(t, ctx.ModuleForTests("foo", "").Rule("rule"),
Dan Willemsen633c5022019-04-12 11:11:38 -0700516 "cp bar "+outFile,
517 outFile, outFile+".d", true, nil)
518 })
519 t.Run("sbox", func(t *testing.T) {
520 outDir := filepath.Join(buildDir, ".intermediates", "foo_sbox")
521 outFile := filepath.Join(outDir, "foo_sbox")
Dan Willemsenc89b6f12019-08-29 14:47:40 -0700522 depFile := filepath.Join(outDir, "foo_sbox.d")
Dan Willemsen633c5022019-04-12 11:11:38 -0700523 sbox := filepath.Join(buildDir, "host", config.PrebuiltOS(), "bin/sbox")
524 sandboxPath := shared.TempDirForOutDir(buildDir)
525
Dan Willemsenc89b6f12019-08-29 14:47:40 -0700526 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 -0700527
528 check(t, ctx.ModuleForTests("foo_sbox", "").Rule("rule"),
Dan Willemsenc89b6f12019-08-29 14:47:40 -0700529 cmd, outFile, depFile, false, []string{sbox})
Colin Cross4c83e5c2019-02-25 14:54:28 -0800530 })
531 t.Run("singleton", func(t *testing.T) {
Dan Willemsen633c5022019-04-12 11:11:38 -0700532 outFile := filepath.Join(buildDir, "baz")
Colin Cross4c83e5c2019-02-25 14:54:28 -0800533 check(t, ctx.SingletonForTests("rule_builder_test").Rule("rule"),
Dan Willemsen633c5022019-04-12 11:11:38 -0700534 "cp bar "+outFile, outFile, outFile+".d", true, nil)
Colin Cross4c83e5c2019-02-25 14:54:28 -0800535 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800536}
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700537
538func Test_ninjaEscapeExceptForSpans(t *testing.T) {
539 type args struct {
540 s string
541 spans [][2]int
542 }
543 tests := []struct {
544 name string
545 args args
546 want string
547 }{
548 {
549 name: "empty",
550 args: args{
551 s: "",
552 },
553 want: "",
554 },
555 {
556 name: "unescape none",
557 args: args{
558 s: "$abc",
559 },
560 want: "$$abc",
561 },
562 {
563 name: "unescape all",
564 args: args{
565 s: "$abc",
566 spans: [][2]int{{0, 4}},
567 },
568 want: "$abc",
569 },
570 {
571 name: "unescape first",
572 args: args{
573 s: "$abc$",
574 spans: [][2]int{{0, 1}},
575 },
576 want: "$abc$$",
577 },
578 {
579 name: "unescape last",
580 args: args{
581 s: "$abc$",
582 spans: [][2]int{{4, 5}},
583 },
584 want: "$$abc$",
585 },
586 {
587 name: "unescape middle",
588 args: args{
589 s: "$a$b$c$",
590 spans: [][2]int{{2, 5}},
591 },
592 want: "$$a$b$c$$",
593 },
594 {
595 name: "unescape multiple",
596 args: args{
597 s: "$a$b$c$",
598 spans: [][2]int{{2, 3}, {4, 5}},
599 },
600 want: "$$a$b$c$$",
601 },
602 }
603 for _, tt := range tests {
604 t.Run(tt.name, func(t *testing.T) {
605 if got := ninjaEscapeExceptForSpans(tt.args.s, tt.args.spans); got != tt.want {
606 t.Errorf("ninjaEscapeExceptForSpans() = %v, want %v", got, tt.want)
607 }
608 })
609 }
610}