blob: e676e4a924125a7c4aa0c29c51fba4ecf1a78bc2 [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")).
270 FlagWithRspFileInputList("@", PathsForTesting("a.java", "b.java")).
271 NinjaEscapedString())
272 // Output:
273 // javac @$out.rsp
274}
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 Crossfeec25b2019-01-30 17:32:39 -0800439}
440
441func testRuleBuilderFactory() Module {
442 module := &testRuleBuilderModule{}
443 module.AddProperties(&module.properties)
444 InitAndroidModule(module)
445 return module
446}
447
448type testRuleBuilderModule struct {
449 ModuleBase
450 properties struct {
Colin Cross3d680512020-11-13 16:23:53 -0800451 Srcs []string
Dan Willemsen633c5022019-04-12 11:11:38 -0700452
453 Restat bool
454 Sbox bool
Colin Crossfeec25b2019-01-30 17:32:39 -0800455 }
456}
457
458func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
Colin Cross3d680512020-11-13 16:23:53 -0800459 in := PathsForSource(ctx, t.properties.Srcs)
Colin Crosse16ce362020-11-12 08:29:30 -0800460 out := PathForModuleOut(ctx, "gen", ctx.ModuleName())
461 outDep := PathForModuleOut(ctx, "gen", ctx.ModuleName()+".d")
462 outDir := PathForModuleOut(ctx, "gen")
463 manifestPath := PathForModuleOut(ctx, "sbox.textproto")
Colin Crossfeec25b2019-01-30 17:32:39 -0800464
Colin Crosse16ce362020-11-12 08:29:30 -0800465 testRuleBuilder_Build(ctx, in, out, outDep, outDir, manifestPath, t.properties.Restat, t.properties.Sbox)
Colin Cross786cd6d2019-02-01 16:41:11 -0800466}
467
468type testRuleBuilderSingleton struct{}
469
470func testRuleBuilderSingletonFactory() Singleton {
471 return &testRuleBuilderSingleton{}
472}
473
474func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) {
475 in := PathForSource(ctx, "bar")
Colin Crosse16ce362020-11-12 08:29:30 -0800476 out := PathForOutput(ctx, "singleton/gen/baz")
477 outDep := PathForOutput(ctx, "singleton/gen/baz.d")
478 outDir := PathForOutput(ctx, "singleton/gen")
479 manifestPath := PathForOutput(ctx, "singleton/sbox.textproto")
480 testRuleBuilder_Build(ctx, Paths{in}, out, outDep, outDir, manifestPath, true, false)
Colin Cross786cd6d2019-02-01 16:41:11 -0800481}
482
Colin Crosse16ce362020-11-12 08:29:30 -0800483func testRuleBuilder_Build(ctx BuilderContext, in Paths, out, outDep, outDir, manifestPath WritablePath, restat, sbox bool) {
Colin Crossf1a035e2020-11-16 17:32:30 -0800484 rule := NewRuleBuilder(pctx, ctx)
Colin Cross786cd6d2019-02-01 16:41:11 -0800485
Dan Willemsen633c5022019-04-12 11:11:38 -0700486 if sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800487 rule.Sbox(outDir, manifestPath)
Dan Willemsen633c5022019-04-12 11:11:38 -0700488 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800489
Colin Cross3d680512020-11-13 16:23:53 -0800490 rule.Command().Tool(PathForSource(ctx, "cp")).Inputs(in).Output(out).ImplicitDepFile(outDep)
Dan Willemsen633c5022019-04-12 11:11:38 -0700491
492 if restat {
493 rule.Restat()
494 }
Colin Crossbaa676f2019-02-25 14:56:01 -0800495
Colin Crossf1a035e2020-11-16 17:32:30 -0800496 rule.Build("rule", "desc")
Colin Crossfeec25b2019-01-30 17:32:39 -0800497}
498
499func TestRuleBuilder_Build(t *testing.T) {
Colin Cross98be1bb2019-12-13 20:41:13 -0800500 fs := map[string][]byte{
501 "bar": nil,
502 "cp": nil,
503 }
504
Colin Crossfeec25b2019-01-30 17:32:39 -0800505 bp := `
506 rule_builder_test {
507 name: "foo",
Colin Cross3d680512020-11-13 16:23:53 -0800508 srcs: ["bar"],
Dan Willemsen633c5022019-04-12 11:11:38 -0700509 restat: true,
510 }
511 rule_builder_test {
512 name: "foo_sbox",
Colin Cross3d680512020-11-13 16:23:53 -0800513 srcs: ["bar"],
Dan Willemsen633c5022019-04-12 11:11:38 -0700514 sbox: true,
Colin Crossfeec25b2019-01-30 17:32:39 -0800515 }
516 `
517
Colin Cross98be1bb2019-12-13 20:41:13 -0800518 config := TestConfig(buildDir, nil, bp, fs)
Colin Crossae8600b2020-10-29 17:09:13 -0700519 ctx := NewTestContext(config)
Colin Cross4b49b762019-11-22 15:25:03 -0800520 ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
521 ctx.RegisterSingletonType("rule_builder_test", testRuleBuilderSingletonFactory)
Colin Crossae8600b2020-10-29 17:09:13 -0700522 ctx.Register()
Colin Crossfeec25b2019-01-30 17:32:39 -0800523
524 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
525 FailIfErrored(t, errs)
526 _, errs = ctx.PrepareBuildActions(config)
527 FailIfErrored(t, errs)
528
Colin Crosse16ce362020-11-12 08:29:30 -0800529 check := func(t *testing.T, params TestingBuildParams, wantCommand, wantOutput, wantDepfile string, wantRestat bool, extraImplicits, extraCmdDeps []string) {
Dan Willemsenc89b6f12019-08-29 14:47:40 -0700530 t.Helper()
Colin Cross3d680512020-11-13 16:23:53 -0800531 command := params.RuleParams.Command
Colin Crosse16ce362020-11-12 08:29:30 -0800532 re := regexp.MustCompile(" # hash of input list: [a-z0-9]*$")
Colin Cross3d680512020-11-13 16:23:53 -0800533 command = re.ReplaceAllLiteralString(command, "")
534 if command != wantCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700535 t.Errorf("\nwant RuleParams.Command = %q\n got %q", wantCommand, params.RuleParams.Command)
536 }
537
538 wantDeps := append([]string{"cp"}, extraCmdDeps...)
539 if !reflect.DeepEqual(params.RuleParams.CommandDeps, wantDeps) {
540 t.Errorf("\nwant RuleParams.CommandDeps = %q\n got %q", wantDeps, params.RuleParams.CommandDeps)
541 }
542
543 if params.RuleParams.Restat != wantRestat {
544 t.Errorf("want RuleParams.Restat = %v, got %v", wantRestat, params.RuleParams.Restat)
Colin Cross4c83e5c2019-02-25 14:54:28 -0800545 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800546
Colin Crosse16ce362020-11-12 08:29:30 -0800547 wantImplicits := append([]string{"bar"}, extraImplicits...)
548 if !reflect.DeepEqual(params.Implicits.Strings(), wantImplicits) {
Colin Cross4c83e5c2019-02-25 14:54:28 -0800549 t.Errorf("want Implicits = [%q], got %q", "bar", params.Implicits.Strings())
550 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800551
Colin Cross1d2cf042019-03-29 15:33:06 -0700552 if params.Output.String() != wantOutput {
553 t.Errorf("want Output = %q, got %q", wantOutput, params.Output)
Colin Cross4c83e5c2019-02-25 14:54:28 -0800554 }
Colin Crossbaa676f2019-02-25 14:56:01 -0800555
Dan Willemsen633c5022019-04-12 11:11:38 -0700556 if len(params.ImplicitOutputs) != 0 {
557 t.Errorf("want ImplicitOutputs = [], got %q", params.ImplicitOutputs.Strings())
558 }
559
560 if params.Depfile.String() != wantDepfile {
561 t.Errorf("want Depfile = %q, got %q", wantDepfile, params.Depfile)
562 }
563
564 if params.Deps != blueprint.DepsGCC {
565 t.Errorf("want Deps = %q, got %q", blueprint.DepsGCC, params.Deps)
Colin Crossbaa676f2019-02-25 14:56:01 -0800566 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800567 }
568
Colin Cross4c83e5c2019-02-25 14:54:28 -0800569 t.Run("module", func(t *testing.T) {
Colin Crosse16ce362020-11-12 08:29:30 -0800570 outFile := filepath.Join(buildDir, ".intermediates", "foo", "gen", "foo")
Colin Cross4c83e5c2019-02-25 14:54:28 -0800571 check(t, ctx.ModuleForTests("foo", "").Rule("rule"),
Dan Willemsen633c5022019-04-12 11:11:38 -0700572 "cp bar "+outFile,
Colin Crosse16ce362020-11-12 08:29:30 -0800573 outFile, outFile+".d", true, nil, nil)
Dan Willemsen633c5022019-04-12 11:11:38 -0700574 })
575 t.Run("sbox", func(t *testing.T) {
576 outDir := filepath.Join(buildDir, ".intermediates", "foo_sbox")
Colin Crosse16ce362020-11-12 08:29:30 -0800577 outFile := filepath.Join(outDir, "gen/foo_sbox")
578 depFile := filepath.Join(outDir, "gen/foo_sbox.d")
579 manifest := filepath.Join(outDir, "sbox.textproto")
Dan Willemsen633c5022019-04-12 11:11:38 -0700580 sbox := filepath.Join(buildDir, "host", config.PrebuiltOS(), "bin/sbox")
581 sandboxPath := shared.TempDirForOutDir(buildDir)
582
Colin Crosse16ce362020-11-12 08:29:30 -0800583 cmd := `rm -rf ` + outDir + `/gen && ` +
584 sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest
Dan Willemsen633c5022019-04-12 11:11:38 -0700585
Colin Crosse16ce362020-11-12 08:29:30 -0800586 check(t, ctx.ModuleForTests("foo_sbox", "").Output("gen/foo_sbox"),
587 cmd, outFile, depFile, false, []string{manifest}, []string{sbox})
Colin Cross4c83e5c2019-02-25 14:54:28 -0800588 })
589 t.Run("singleton", func(t *testing.T) {
Colin Crosse16ce362020-11-12 08:29:30 -0800590 outFile := filepath.Join(buildDir, "singleton/gen/baz")
Colin Cross4c83e5c2019-02-25 14:54:28 -0800591 check(t, ctx.SingletonForTests("rule_builder_test").Rule("rule"),
Colin Crosse16ce362020-11-12 08:29:30 -0800592 "cp bar "+outFile, outFile, outFile+".d", true, nil, nil)
Colin Cross4c83e5c2019-02-25 14:54:28 -0800593 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800594}
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700595
596func Test_ninjaEscapeExceptForSpans(t *testing.T) {
597 type args struct {
598 s string
599 spans [][2]int
600 }
601 tests := []struct {
602 name string
603 args args
604 want string
605 }{
606 {
607 name: "empty",
608 args: args{
609 s: "",
610 },
611 want: "",
612 },
613 {
614 name: "unescape none",
615 args: args{
616 s: "$abc",
617 },
618 want: "$$abc",
619 },
620 {
621 name: "unescape all",
622 args: args{
623 s: "$abc",
624 spans: [][2]int{{0, 4}},
625 },
626 want: "$abc",
627 },
628 {
629 name: "unescape first",
630 args: args{
631 s: "$abc$",
632 spans: [][2]int{{0, 1}},
633 },
634 want: "$abc$$",
635 },
636 {
637 name: "unescape last",
638 args: args{
639 s: "$abc$",
640 spans: [][2]int{{4, 5}},
641 },
642 want: "$$abc$",
643 },
644 {
645 name: "unescape middle",
646 args: args{
647 s: "$a$b$c$",
648 spans: [][2]int{{2, 5}},
649 },
650 want: "$$a$b$c$$",
651 },
652 {
653 name: "unescape multiple",
654 args: args{
655 s: "$a$b$c$",
656 spans: [][2]int{{2, 3}, {4, 5}},
657 },
658 want: "$$a$b$c$$",
659 },
660 }
661 for _, tt := range tests {
662 t.Run(tt.name, func(t *testing.T) {
663 if got := ninjaEscapeExceptForSpans(tt.args.s, tt.args.spans); got != tt.want {
664 t.Errorf("ninjaEscapeExceptForSpans() = %v, want %v", got, tt.want)
665 }
666 })
667 }
668}
Colin Cross3d680512020-11-13 16:23:53 -0800669
670func TestRuleBuilderHashInputs(t *testing.T) {
671 // The basic idea here is to verify that the command (in the case of a
672 // non-sbox rule) or the sbox textproto manifest contain a hash of the
673 // inputs.
674
675 // By including a hash of the inputs, we cause the rule to re-run if
676 // the list of inputs changes because the command line or a dependency
677 // changes.
678
679 bp := `
680 rule_builder_test {
681 name: "hash0",
682 srcs: ["in1.txt", "in2.txt"],
683 }
684 rule_builder_test {
685 name: "hash0_sbox",
686 srcs: ["in1.txt", "in2.txt"],
687 sbox: true,
688 }
689 rule_builder_test {
690 name: "hash1",
691 srcs: ["in1.txt", "in2.txt", "in3.txt"],
692 }
693 rule_builder_test {
694 name: "hash1_sbox",
695 srcs: ["in1.txt", "in2.txt", "in3.txt"],
696 sbox: true,
697 }
698 `
699 testcases := []struct {
700 name string
701 expectedHash string
702 }{
703 {
704 name: "hash0",
705 // sha256 value obtained from: echo -en 'in1.txt\nin2.txt' | sha256sum
706 expectedHash: "18da75b9b1cc74b09e365b4ca2e321b5d618f438cc632b387ad9dc2ab4b20e9d",
707 },
708 {
709 name: "hash1",
710 // sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
711 expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
712 },
713 }
714
715 config := TestConfig(buildDir, nil, bp, nil)
716 ctx := NewTestContext(config)
717 ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
718 ctx.Register()
719
720 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
721 FailIfErrored(t, errs)
722 _, errs = ctx.PrepareBuildActions(config)
723 FailIfErrored(t, errs)
724
725 for _, test := range testcases {
726 t.Run(test.name, func(t *testing.T) {
727 t.Run("sbox", func(t *testing.T) {
728 gen := ctx.ModuleForTests(test.name+"_sbox", "")
Colin Crosse16ce362020-11-12 08:29:30 -0800729 manifest := RuleBuilderSboxProtoForTests(t, gen.Output("sbox.textproto"))
730 hash := manifest.Commands[0].GetInputHash()
731
732 if g, w := hash, test.expectedHash; g != w {
733 t.Errorf("Expected has %q, got %q", w, g)
Colin Cross3d680512020-11-13 16:23:53 -0800734 }
735 })
736 t.Run("", func(t *testing.T) {
737 gen := ctx.ModuleForTests(test.name+"", "")
Colin Crosse16ce362020-11-12 08:29:30 -0800738 command := gen.Output("gen/" + test.name).RuleParams.Command
Colin Cross3d680512020-11-13 16:23:53 -0800739 if g, w := command, " # hash of input list: "+test.expectedHash; !strings.HasSuffix(g, w) {
740 t.Errorf("Expected command line to end with %q, got %q", w, g)
741 }
742 })
743 })
744 }
745}