blob: 080e236b4390245dbc1b91755636219bfd88df56 [file] [log] [blame]
Colin Crossfeec25b2019-01-30 17:32:39 -08001// Copyright 2019 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package android
16
17import (
Colin Cross758290d2019-02-01 16:42:32 -080018 "fmt"
Colin Crossfeec25b2019-01-30 17:32:39 -080019 "path/filepath"
20 "reflect"
Colin Cross3d680512020-11-13 16:23:53 -080021 "regexp"
Colin Cross758290d2019-02-01 16:42:32 -080022 "strings"
Colin Crossfeec25b2019-01-30 17:32:39 -080023 "testing"
Dan Willemsen633c5022019-04-12 11:11:38 -070024
25 "github.com/google/blueprint"
26
27 "android/soong/shared"
Colin Crossfeec25b2019-01-30 17:32:39 -080028)
29
Colin Crossf1a035e2020-11-16 17:32:30 -080030func builderContext() BuilderContext {
31 return BuilderContextForTesting(TestConfig("out", nil, "", map[string][]byte{
Colin Cross98be1bb2019-12-13 20:41:13 -080032 "ld": nil,
33 "a.o": nil,
34 "b.o": nil,
35 "cp": nil,
36 "a": nil,
37 "b": nil,
38 "ls": nil,
Jingwen Chence679d22020-09-23 04:30:02 +000039 "ln": nil,
Colin Cross98be1bb2019-12-13 20:41:13 -080040 "turbine": nil,
41 "java": nil,
42 "javac": nil,
43 }))
Colin Cross69f59a32019-02-15 10:39:37 -080044}
45
Colin Cross758290d2019-02-01 16:42:32 -080046func ExampleRuleBuilder() {
Colin Crossf1a035e2020-11-16 17:32:30 -080047 ctx := builderContext()
Colin Cross758290d2019-02-01 16:42:32 -080048
Colin Crossf1a035e2020-11-16 17:32:30 -080049 rule := NewRuleBuilder(pctx, ctx)
Colin Cross69f59a32019-02-15 10:39:37 -080050
51 rule.Command().
52 Tool(PathForSource(ctx, "ld")).
53 Inputs(PathsForTesting("a.o", "b.o")).
54 FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
Colin Cross758290d2019-02-01 16:42:32 -080055 rule.Command().Text("echo success")
56
57 // To add the command to the build graph:
Colin Crossf1a035e2020-11-16 17:32:30 -080058 // rule.Build("link", "link")
Colin Cross758290d2019-02-01 16:42:32 -080059
60 fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
61 fmt.Printf("tools: %q\n", rule.Tools())
62 fmt.Printf("inputs: %q\n", rule.Inputs())
63 fmt.Printf("outputs: %q\n", rule.Outputs())
64
65 // Output:
Colin Cross69f59a32019-02-15 10:39:37 -080066 // commands: "ld a.o b.o -o out/linked && echo success"
Colin Cross758290d2019-02-01 16:42:32 -080067 // tools: ["ld"]
68 // inputs: ["a.o" "b.o"]
Colin Cross69f59a32019-02-15 10:39:37 -080069 // outputs: ["out/linked"]
Colin Cross758290d2019-02-01 16:42:32 -080070}
71
Jingwen Chence679d22020-09-23 04:30:02 +000072func ExampleRuleBuilder_SymlinkOutputs() {
Colin Crossf1a035e2020-11-16 17:32:30 -080073 ctx := builderContext()
Jingwen Chence679d22020-09-23 04:30:02 +000074
Colin Crossf1a035e2020-11-16 17:32:30 -080075 rule := NewRuleBuilder(pctx, ctx)
Jingwen Chence679d22020-09-23 04:30:02 +000076
77 rule.Command().
78 Tool(PathForSource(ctx, "ln")).
79 FlagWithInput("-s ", PathForTesting("a.o")).
80 SymlinkOutput(PathForOutput(ctx, "a"))
81 rule.Command().Text("cp out/a out/b").
82 ImplicitSymlinkOutput(PathForOutput(ctx, "b"))
83
84 fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
85 fmt.Printf("tools: %q\n", rule.Tools())
86 fmt.Printf("inputs: %q\n", rule.Inputs())
87 fmt.Printf("outputs: %q\n", rule.Outputs())
88 fmt.Printf("symlink_outputs: %q\n", rule.SymlinkOutputs())
89
90 // Output:
91 // commands: "ln -s a.o out/a && cp out/a out/b"
92 // tools: ["ln"]
93 // inputs: ["a.o"]
94 // outputs: ["out/a" "out/b"]
95 // symlink_outputs: ["out/a" "out/b"]
96}
97
Colin Cross5cb5b092019-02-02 21:25:18 -080098func ExampleRuleBuilder_Temporary() {
Colin Crossf1a035e2020-11-16 17:32:30 -080099 ctx := builderContext()
Colin Cross5cb5b092019-02-02 21:25:18 -0800100
Colin Crossf1a035e2020-11-16 17:32:30 -0800101 rule := NewRuleBuilder(pctx, ctx)
Colin Cross69f59a32019-02-15 10:39:37 -0800102
103 rule.Command().
104 Tool(PathForSource(ctx, "cp")).
105 Input(PathForSource(ctx, "a")).
106 Output(PathForOutput(ctx, "b"))
107 rule.Command().
108 Tool(PathForSource(ctx, "cp")).
109 Input(PathForOutput(ctx, "b")).
110 Output(PathForOutput(ctx, "c"))
111 rule.Temporary(PathForOutput(ctx, "b"))
Colin Cross5cb5b092019-02-02 21:25:18 -0800112
113 fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
114 fmt.Printf("tools: %q\n", rule.Tools())
115 fmt.Printf("inputs: %q\n", rule.Inputs())
116 fmt.Printf("outputs: %q\n", rule.Outputs())
117
118 // Output:
Colin Cross69f59a32019-02-15 10:39:37 -0800119 // commands: "cp a out/b && cp out/b out/c"
Colin Cross5cb5b092019-02-02 21:25:18 -0800120 // tools: ["cp"]
121 // inputs: ["a"]
Colin Cross69f59a32019-02-15 10:39:37 -0800122 // outputs: ["out/c"]
Colin Cross5cb5b092019-02-02 21:25:18 -0800123}
124
125func ExampleRuleBuilder_DeleteTemporaryFiles() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800126 ctx := builderContext()
Colin Cross5cb5b092019-02-02 21:25:18 -0800127
Colin Crossf1a035e2020-11-16 17:32:30 -0800128 rule := NewRuleBuilder(pctx, ctx)
Colin Cross69f59a32019-02-15 10:39:37 -0800129
130 rule.Command().
131 Tool(PathForSource(ctx, "cp")).
132 Input(PathForSource(ctx, "a")).
133 Output(PathForOutput(ctx, "b"))
134 rule.Command().
135 Tool(PathForSource(ctx, "cp")).
136 Input(PathForOutput(ctx, "b")).
137 Output(PathForOutput(ctx, "c"))
138 rule.Temporary(PathForOutput(ctx, "b"))
Colin Cross5cb5b092019-02-02 21:25:18 -0800139 rule.DeleteTemporaryFiles()
140
141 fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
142 fmt.Printf("tools: %q\n", rule.Tools())
143 fmt.Printf("inputs: %q\n", rule.Inputs())
144 fmt.Printf("outputs: %q\n", rule.Outputs())
145
146 // Output:
Colin Cross69f59a32019-02-15 10:39:37 -0800147 // commands: "cp a out/b && cp out/b out/c && rm -f out/b"
Colin Cross5cb5b092019-02-02 21:25:18 -0800148 // tools: ["cp"]
149 // inputs: ["a"]
Colin Cross69f59a32019-02-15 10:39:37 -0800150 // outputs: ["out/c"]
Colin Cross5cb5b092019-02-02 21:25:18 -0800151}
152
Colin Crossdeabb942019-02-11 14:11:09 -0800153func ExampleRuleBuilder_Installs() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800154 ctx := builderContext()
Colin Crossdeabb942019-02-11 14:11:09 -0800155
Colin Crossf1a035e2020-11-16 17:32:30 -0800156 rule := NewRuleBuilder(pctx, ctx)
Colin Cross69f59a32019-02-15 10:39:37 -0800157
158 out := PathForOutput(ctx, "linked")
159
160 rule.Command().
161 Tool(PathForSource(ctx, "ld")).
162 Inputs(PathsForTesting("a.o", "b.o")).
163 FlagWithOutput("-o ", out)
164 rule.Install(out, "/bin/linked")
165 rule.Install(out, "/sbin/linked")
Colin Crossdeabb942019-02-11 14:11:09 -0800166
167 fmt.Printf("rule.Installs().String() = %q\n", rule.Installs().String())
168
169 // Output:
Colin Cross69f59a32019-02-15 10:39:37 -0800170 // rule.Installs().String() = "out/linked:/bin/linked out/linked:/sbin/linked"
Colin Crossdeabb942019-02-11 14:11:09 -0800171}
172
Colin Cross758290d2019-02-01 16:42:32 -0800173func ExampleRuleBuilderCommand() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800174 ctx := builderContext()
Colin Cross758290d2019-02-01 16:42:32 -0800175
Colin Crossf1a035e2020-11-16 17:32:30 -0800176 rule := NewRuleBuilder(pctx, ctx)
Colin Cross69f59a32019-02-15 10:39:37 -0800177
Colin Cross758290d2019-02-01 16:42:32 -0800178 // chained
Colin Cross69f59a32019-02-15 10:39:37 -0800179 rule.Command().
180 Tool(PathForSource(ctx, "ld")).
181 Inputs(PathsForTesting("a.o", "b.o")).
182 FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
Colin Cross758290d2019-02-01 16:42:32 -0800183
184 // unchained
185 cmd := rule.Command()
Colin Cross69f59a32019-02-15 10:39:37 -0800186 cmd.Tool(PathForSource(ctx, "ld"))
187 cmd.Inputs(PathsForTesting("a.o", "b.o"))
188 cmd.FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
Colin Cross758290d2019-02-01 16:42:32 -0800189
190 // mixed:
Colin Cross69f59a32019-02-15 10:39:37 -0800191 cmd = rule.Command().Tool(PathForSource(ctx, "ld"))
192 cmd.Inputs(PathsForTesting("a.o", "b.o"))
193 cmd.FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
Colin Cross758290d2019-02-01 16:42:32 -0800194}
195
196func ExampleRuleBuilderCommand_Flag() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800197 ctx := builderContext()
198 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross69f59a32019-02-15 10:39:37 -0800199 Tool(PathForSource(ctx, "ls")).Flag("-l"))
Colin Cross758290d2019-02-01 16:42:32 -0800200 // Output:
201 // ls -l
202}
203
Colin Cross92b7d582019-03-29 15:32:51 -0700204func ExampleRuleBuilderCommand_Flags() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800205 ctx := builderContext()
206 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross92b7d582019-03-29 15:32:51 -0700207 Tool(PathForSource(ctx, "ls")).Flags([]string{"-l", "-a"}))
208 // Output:
209 // ls -l -a
210}
211
Colin Cross758290d2019-02-01 16:42:32 -0800212func ExampleRuleBuilderCommand_FlagWithArg() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800213 ctx := builderContext()
214 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross69f59a32019-02-15 10:39:37 -0800215 Tool(PathForSource(ctx, "ls")).
Colin Cross758290d2019-02-01 16:42:32 -0800216 FlagWithArg("--sort=", "time"))
217 // Output:
218 // ls --sort=time
219}
220
Colin Crossc7ed0042019-02-11 14:11:09 -0800221func ExampleRuleBuilderCommand_FlagForEachArg() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800222 ctx := builderContext()
223 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross69f59a32019-02-15 10:39:37 -0800224 Tool(PathForSource(ctx, "ls")).
Colin Crossc7ed0042019-02-11 14:11:09 -0800225 FlagForEachArg("--sort=", []string{"time", "size"}))
226 // Output:
227 // ls --sort=time --sort=size
228}
229
Colin Cross758290d2019-02-01 16:42:32 -0800230func ExampleRuleBuilderCommand_FlagForEachInput() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800231 ctx := builderContext()
232 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross69f59a32019-02-15 10:39:37 -0800233 Tool(PathForSource(ctx, "turbine")).
234 FlagForEachInput("--classpath ", PathsForTesting("a.jar", "b.jar")))
Colin Cross758290d2019-02-01 16:42:32 -0800235 // Output:
236 // turbine --classpath a.jar --classpath b.jar
237}
238
239func ExampleRuleBuilderCommand_FlagWithInputList() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800240 ctx := builderContext()
241 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross69f59a32019-02-15 10:39:37 -0800242 Tool(PathForSource(ctx, "java")).
243 FlagWithInputList("-classpath=", PathsForTesting("a.jar", "b.jar"), ":"))
Colin Cross758290d2019-02-01 16:42:32 -0800244 // Output:
245 // java -classpath=a.jar:b.jar
246}
247
248func ExampleRuleBuilderCommand_FlagWithInput() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800249 ctx := builderContext()
250 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross69f59a32019-02-15 10:39:37 -0800251 Tool(PathForSource(ctx, "java")).
252 FlagWithInput("-classpath=", PathForSource(ctx, "a")))
Colin Cross758290d2019-02-01 16:42:32 -0800253 // Output:
254 // java -classpath=a
255}
256
257func ExampleRuleBuilderCommand_FlagWithList() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800258 ctx := builderContext()
259 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross69f59a32019-02-15 10:39:37 -0800260 Tool(PathForSource(ctx, "ls")).
Colin Cross758290d2019-02-01 16:42:32 -0800261 FlagWithList("--sort=", []string{"time", "size"}, ","))
262 // Output:
263 // ls --sort=time,size
264}
265
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700266func ExampleRuleBuilderCommand_FlagWithRspFileInputList() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800267 ctx := builderContext()
268 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700269 Tool(PathForSource(ctx, "javac")).
Colin Cross70c47412021-03-12 17:48:14 -0800270 FlagWithRspFileInputList("@", PathForOutput(ctx, "foo.rsp"), PathsForTesting("a.java", "b.java")).
271 String())
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700272 // Output:
Colin Cross70c47412021-03-12 17:48:14 -0800273 // javac @out/foo.rsp
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700274}
275
276func ExampleRuleBuilderCommand_String() {
Colin Crossf1a035e2020-11-16 17:32:30 -0800277 ctx := builderContext()
278 fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700279 Text("FOO=foo").
280 Text("echo $FOO").
281 String())
282 // Output:
283 // FOO=foo echo $FOO
284}
285
Colin Crossfeec25b2019-01-30 17:32:39 -0800286func TestRuleBuilder(t *testing.T) {
Colin Cross69f59a32019-02-15 10:39:37 -0800287 fs := map[string][]byte{
Colin Crossda71eda2020-02-21 16:55:19 -0800288 "dep_fixer": nil,
289 "input": nil,
290 "Implicit": nil,
291 "Input": nil,
292 "OrderOnly": nil,
293 "OrderOnlys": nil,
294 "Tool": nil,
295 "input2": nil,
296 "tool2": nil,
297 "input3": nil,
Colin Cross69f59a32019-02-15 10:39:37 -0800298 }
299
Colin Crossf1a035e2020-11-16 17:32:30 -0800300 pathCtx := PathContextForTesting(TestConfig("out", nil, "", fs))
301 ctx := builderContextForTests{
302 PathContext: pathCtx,
303 }
Colin Cross69f59a32019-02-15 10:39:37 -0800304
Dan Willemsen633c5022019-04-12 11:11:38 -0700305 addCommands := func(rule *RuleBuilder) {
306 cmd := rule.Command().
307 DepFile(PathForOutput(ctx, "DepFile")).
308 Flag("Flag").
309 FlagWithArg("FlagWithArg=", "arg").
310 FlagWithDepFile("FlagWithDepFile=", PathForOutput(ctx, "depfile")).
311 FlagWithInput("FlagWithInput=", PathForSource(ctx, "input")).
312 FlagWithOutput("FlagWithOutput=", PathForOutput(ctx, "output")).
313 Implicit(PathForSource(ctx, "Implicit")).
314 ImplicitDepFile(PathForOutput(ctx, "ImplicitDepFile")).
315 ImplicitOutput(PathForOutput(ctx, "ImplicitOutput")).
316 Input(PathForSource(ctx, "Input")).
317 Output(PathForOutput(ctx, "Output")).
Colin Crossda71eda2020-02-21 16:55:19 -0800318 OrderOnly(PathForSource(ctx, "OrderOnly")).
Jingwen Chence679d22020-09-23 04:30:02 +0000319 SymlinkOutput(PathForOutput(ctx, "SymlinkOutput")).
320 ImplicitSymlinkOutput(PathForOutput(ctx, "ImplicitSymlinkOutput")).
Dan Willemsen633c5022019-04-12 11:11:38 -0700321 Text("Text").
322 Tool(PathForSource(ctx, "Tool"))
Colin Crossfeec25b2019-01-30 17:32:39 -0800323
Dan Willemsen633c5022019-04-12 11:11:38 -0700324 rule.Command().
325 Text("command2").
326 DepFile(PathForOutput(ctx, "depfile2")).
327 Input(PathForSource(ctx, "input2")).
328 Output(PathForOutput(ctx, "output2")).
Colin Crossda71eda2020-02-21 16:55:19 -0800329 OrderOnlys(PathsForSource(ctx, []string{"OrderOnlys"})).
Dan Willemsen633c5022019-04-12 11:11:38 -0700330 Tool(PathForSource(ctx, "tool2"))
Colin Crossfeec25b2019-01-30 17:32:39 -0800331
Dan Willemsen633c5022019-04-12 11:11:38 -0700332 // Test updates to the first command after the second command has been started
333 cmd.Text("after command2")
334 // Test updating a command when the previous update did not replace the cmd variable
335 cmd.Text("old cmd")
Colin Crossfeec25b2019-01-30 17:32:39 -0800336
Dan Willemsen633c5022019-04-12 11:11:38 -0700337 // Test a command that uses the output of a previous command as an input
338 rule.Command().
339 Text("command3").
340 Input(PathForSource(ctx, "input3")).
341 Input(PathForOutput(ctx, "output2")).
342 Output(PathForOutput(ctx, "output3"))
Colin Crossfeec25b2019-01-30 17:32:39 -0800343 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700344
Colin Cross69f59a32019-02-15 10:39:37 -0800345 wantInputs := PathsForSource(ctx, []string{"Implicit", "Input", "input", "input2", "input3"})
Jingwen Chence679d22020-09-23 04:30:02 +0000346 wantOutputs := PathsForOutput(ctx, []string{"ImplicitOutput", "ImplicitSymlinkOutput", "Output", "SymlinkOutput", "output", "output2", "output3"})
Colin Cross1d2cf042019-03-29 15:33:06 -0700347 wantDepFiles := PathsForOutput(ctx, []string{"DepFile", "depfile", "ImplicitDepFile", "depfile2"})
Colin Cross69f59a32019-02-15 10:39:37 -0800348 wantTools := PathsForSource(ctx, []string{"Tool", "tool2"})
Colin Crossda71eda2020-02-21 16:55:19 -0800349 wantOrderOnlys := PathsForSource(ctx, []string{"OrderOnly", "OrderOnlys"})
Jingwen Chence679d22020-09-23 04:30:02 +0000350 wantSymlinkOutputs := PathsForOutput(ctx, []string{"ImplicitSymlinkOutput", "SymlinkOutput"})
Colin Crossfeec25b2019-01-30 17:32:39 -0800351
Dan Willemsen633c5022019-04-12 11:11:38 -0700352 t.Run("normal", func(t *testing.T) {
Colin Crossf1a035e2020-11-16 17:32:30 -0800353 rule := NewRuleBuilder(pctx, ctx)
Dan Willemsen633c5022019-04-12 11:11:38 -0700354 addCommands(rule)
Colin Cross1d2cf042019-03-29 15:33:06 -0700355
Dan Willemsen633c5022019-04-12 11:11:38 -0700356 wantCommands := []string{
Jingwen Chence679d22020-09-23 04:30:02 +0000357 "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 -0700358 "command2 out/depfile2 input2 out/output2 tool2",
359 "command3 input3 out/output2 out/output3",
360 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700361
Dan Willemsen633c5022019-04-12 11:11:38 -0700362 wantDepMergerCommand := "out/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer out/DepFile out/depfile out/ImplicitDepFile out/depfile2"
363
364 if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
365 t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
366 }
367
368 if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
369 t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
370 }
371 if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
372 t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
373 }
Jingwen Chence679d22020-09-23 04:30:02 +0000374 if g, w := rule.SymlinkOutputs(), wantSymlinkOutputs; !reflect.DeepEqual(w, g) {
375 t.Errorf("\nwant rule.SymlinkOutputs() = %#v\n got %#v", w, g)
376 }
Dan Willemsen633c5022019-04-12 11:11:38 -0700377 if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
378 t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
379 }
380 if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
381 t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
382 }
Colin Crossda71eda2020-02-21 16:55:19 -0800383 if g, w := rule.OrderOnlys(), wantOrderOnlys; !reflect.DeepEqual(w, g) {
384 t.Errorf("\nwant rule.OrderOnlys() = %#v\n got %#v", w, g)
385 }
Dan Willemsen633c5022019-04-12 11:11:38 -0700386
Colin Crossf1a035e2020-11-16 17:32:30 -0800387 if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w {
Dan Willemsen633c5022019-04-12 11:11:38 -0700388 t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
389 }
390 })
391
392 t.Run("sbox", func(t *testing.T) {
Colin Crossf1a035e2020-11-16 17:32:30 -0800393 rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, ""),
Colin Crosse16ce362020-11-12 08:29:30 -0800394 PathForOutput(ctx, "sbox.textproto"))
Dan Willemsen633c5022019-04-12 11:11:38 -0700395 addCommands(rule)
396
397 wantCommands := []string{
Colin Crosse16ce362020-11-12 08:29:30 -0800398 "__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",
399 "command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 tool2",
400 "command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3",
Dan Willemsen633c5022019-04-12 11:11:38 -0700401 }
402
Colin Crosse16ce362020-11-12 08:29:30 -0800403 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 -0700404
405 if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
406 t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
407 }
408
409 if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
410 t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
411 }
412 if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
413 t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
414 }
415 if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
416 t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
417 }
418 if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
419 t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
420 }
Colin Crossda71eda2020-02-21 16:55:19 -0800421 if g, w := rule.OrderOnlys(), wantOrderOnlys; !reflect.DeepEqual(w, g) {
422 t.Errorf("\nwant rule.OrderOnlys() = %#v\n got %#v", w, g)
423 }
Dan Willemsen633c5022019-04-12 11:11:38 -0700424
Colin Crossf1a035e2020-11-16 17:32:30 -0800425 if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w {
Dan Willemsen633c5022019-04-12 11:11:38 -0700426 t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
427 }
428 })
Colin Crossba9e4032020-11-24 16:32:22 -0800429
430 t.Run("sbox tools", func(t *testing.T) {
431 rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, ""),
432 PathForOutput(ctx, "sbox.textproto")).SandboxTools()
433 addCommands(rule)
434
435 wantCommands := []string{
436 "__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output Input __SBOX_SANDBOX_DIR__/out/Output __SBOX_SANDBOX_DIR__/out/SymlinkOutput Text __SBOX_SANDBOX_DIR__/tools/src/Tool after command2 old cmd",
437 "command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/tools/src/tool2",
438 "command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3",
439 }
440
441 wantDepMergerCommand := "__SBOX_SANDBOX_DIR__/tools/out/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
442
443 if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
444 t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
445 }
446
447 if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
448 t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
449 }
450 if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
451 t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
452 }
453 if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
454 t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
455 }
456 if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
457 t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
458 }
459 if g, w := rule.OrderOnlys(), wantOrderOnlys; !reflect.DeepEqual(w, g) {
460 t.Errorf("\nwant rule.OrderOnlys() = %#v\n got %#v", w, g)
461 }
462
463 if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w {
464 t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
465 }
466 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800467}
468
469func testRuleBuilderFactory() Module {
470 module := &testRuleBuilderModule{}
471 module.AddProperties(&module.properties)
472 InitAndroidModule(module)
473 return module
474}
475
476type testRuleBuilderModule struct {
477 ModuleBase
478 properties struct {
Colin Cross3d680512020-11-13 16:23:53 -0800479 Srcs []string
Dan Willemsen633c5022019-04-12 11:11:38 -0700480
481 Restat bool
482 Sbox bool
Colin Crossfeec25b2019-01-30 17:32:39 -0800483 }
484}
485
486func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
Colin Cross3d680512020-11-13 16:23:53 -0800487 in := PathsForSource(ctx, t.properties.Srcs)
Colin Crosse16ce362020-11-12 08:29:30 -0800488 out := PathForModuleOut(ctx, "gen", ctx.ModuleName())
489 outDep := PathForModuleOut(ctx, "gen", ctx.ModuleName()+".d")
490 outDir := PathForModuleOut(ctx, "gen")
491 manifestPath := PathForModuleOut(ctx, "sbox.textproto")
Colin Crossfeec25b2019-01-30 17:32:39 -0800492
Colin Crosse16ce362020-11-12 08:29:30 -0800493 testRuleBuilder_Build(ctx, in, out, outDep, outDir, manifestPath, t.properties.Restat, t.properties.Sbox)
Colin Cross786cd6d2019-02-01 16:41:11 -0800494}
495
496type testRuleBuilderSingleton struct{}
497
498func testRuleBuilderSingletonFactory() Singleton {
499 return &testRuleBuilderSingleton{}
500}
501
502func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) {
503 in := PathForSource(ctx, "bar")
Colin Crosse16ce362020-11-12 08:29:30 -0800504 out := PathForOutput(ctx, "singleton/gen/baz")
505 outDep := PathForOutput(ctx, "singleton/gen/baz.d")
506 outDir := PathForOutput(ctx, "singleton/gen")
507 manifestPath := PathForOutput(ctx, "singleton/sbox.textproto")
508 testRuleBuilder_Build(ctx, Paths{in}, out, outDep, outDir, manifestPath, true, false)
Colin Cross786cd6d2019-02-01 16:41:11 -0800509}
510
Colin Crosse16ce362020-11-12 08:29:30 -0800511func testRuleBuilder_Build(ctx BuilderContext, in Paths, out, outDep, outDir, manifestPath WritablePath, restat, sbox bool) {
Colin Crossf1a035e2020-11-16 17:32:30 -0800512 rule := NewRuleBuilder(pctx, ctx)
Colin Cross786cd6d2019-02-01 16:41:11 -0800513
Dan Willemsen633c5022019-04-12 11:11:38 -0700514 if sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800515 rule.Sbox(outDir, manifestPath)
Dan Willemsen633c5022019-04-12 11:11:38 -0700516 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800517
Colin Cross3d680512020-11-13 16:23:53 -0800518 rule.Command().Tool(PathForSource(ctx, "cp")).Inputs(in).Output(out).ImplicitDepFile(outDep)
Dan Willemsen633c5022019-04-12 11:11:38 -0700519
520 if restat {
521 rule.Restat()
522 }
Colin Crossbaa676f2019-02-25 14:56:01 -0800523
Colin Crossf1a035e2020-11-16 17:32:30 -0800524 rule.Build("rule", "desc")
Colin Crossfeec25b2019-01-30 17:32:39 -0800525}
526
527func TestRuleBuilder_Build(t *testing.T) {
Colin Cross98be1bb2019-12-13 20:41:13 -0800528 fs := map[string][]byte{
529 "bar": nil,
530 "cp": nil,
531 }
532
Colin Crossfeec25b2019-01-30 17:32:39 -0800533 bp := `
534 rule_builder_test {
535 name: "foo",
Colin Cross3d680512020-11-13 16:23:53 -0800536 srcs: ["bar"],
Dan Willemsen633c5022019-04-12 11:11:38 -0700537 restat: true,
538 }
539 rule_builder_test {
540 name: "foo_sbox",
Colin Cross3d680512020-11-13 16:23:53 -0800541 srcs: ["bar"],
Dan Willemsen633c5022019-04-12 11:11:38 -0700542 sbox: true,
Colin Crossfeec25b2019-01-30 17:32:39 -0800543 }
544 `
545
Colin Cross98be1bb2019-12-13 20:41:13 -0800546 config := TestConfig(buildDir, nil, bp, fs)
Colin Crossae8600b2020-10-29 17:09:13 -0700547 ctx := NewTestContext(config)
Colin Cross4b49b762019-11-22 15:25:03 -0800548 ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
549 ctx.RegisterSingletonType("rule_builder_test", testRuleBuilderSingletonFactory)
Colin Crossae8600b2020-10-29 17:09:13 -0700550 ctx.Register()
Colin Crossfeec25b2019-01-30 17:32:39 -0800551
552 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
553 FailIfErrored(t, errs)
554 _, errs = ctx.PrepareBuildActions(config)
555 FailIfErrored(t, errs)
556
Colin Crosse16ce362020-11-12 08:29:30 -0800557 check := func(t *testing.T, params TestingBuildParams, wantCommand, wantOutput, wantDepfile string, wantRestat bool, extraImplicits, extraCmdDeps []string) {
Dan Willemsenc89b6f12019-08-29 14:47:40 -0700558 t.Helper()
Colin Cross3d680512020-11-13 16:23:53 -0800559 command := params.RuleParams.Command
Colin Crosse16ce362020-11-12 08:29:30 -0800560 re := regexp.MustCompile(" # hash of input list: [a-z0-9]*$")
Colin Cross3d680512020-11-13 16:23:53 -0800561 command = re.ReplaceAllLiteralString(command, "")
562 if command != wantCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700563 t.Errorf("\nwant RuleParams.Command = %q\n got %q", wantCommand, params.RuleParams.Command)
564 }
565
566 wantDeps := append([]string{"cp"}, extraCmdDeps...)
567 if !reflect.DeepEqual(params.RuleParams.CommandDeps, wantDeps) {
568 t.Errorf("\nwant RuleParams.CommandDeps = %q\n got %q", wantDeps, params.RuleParams.CommandDeps)
569 }
570
571 if params.RuleParams.Restat != wantRestat {
572 t.Errorf("want RuleParams.Restat = %v, got %v", wantRestat, params.RuleParams.Restat)
Colin Cross4c83e5c2019-02-25 14:54:28 -0800573 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800574
Colin Crosse16ce362020-11-12 08:29:30 -0800575 wantImplicits := append([]string{"bar"}, extraImplicits...)
576 if !reflect.DeepEqual(params.Implicits.Strings(), wantImplicits) {
Colin Cross4c83e5c2019-02-25 14:54:28 -0800577 t.Errorf("want Implicits = [%q], got %q", "bar", params.Implicits.Strings())
578 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800579
Colin Cross1d2cf042019-03-29 15:33:06 -0700580 if params.Output.String() != wantOutput {
581 t.Errorf("want Output = %q, got %q", wantOutput, params.Output)
Colin Cross4c83e5c2019-02-25 14:54:28 -0800582 }
Colin Crossbaa676f2019-02-25 14:56:01 -0800583
Dan Willemsen633c5022019-04-12 11:11:38 -0700584 if len(params.ImplicitOutputs) != 0 {
585 t.Errorf("want ImplicitOutputs = [], got %q", params.ImplicitOutputs.Strings())
586 }
587
588 if params.Depfile.String() != wantDepfile {
589 t.Errorf("want Depfile = %q, got %q", wantDepfile, params.Depfile)
590 }
591
592 if params.Deps != blueprint.DepsGCC {
593 t.Errorf("want Deps = %q, got %q", blueprint.DepsGCC, params.Deps)
Colin Crossbaa676f2019-02-25 14:56:01 -0800594 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800595 }
596
Colin Cross4c83e5c2019-02-25 14:54:28 -0800597 t.Run("module", func(t *testing.T) {
Colin Crosse16ce362020-11-12 08:29:30 -0800598 outFile := filepath.Join(buildDir, ".intermediates", "foo", "gen", "foo")
Colin Cross4c83e5c2019-02-25 14:54:28 -0800599 check(t, ctx.ModuleForTests("foo", "").Rule("rule"),
Dan Willemsen633c5022019-04-12 11:11:38 -0700600 "cp bar "+outFile,
Colin Crosse16ce362020-11-12 08:29:30 -0800601 outFile, outFile+".d", true, nil, nil)
Dan Willemsen633c5022019-04-12 11:11:38 -0700602 })
603 t.Run("sbox", func(t *testing.T) {
604 outDir := filepath.Join(buildDir, ".intermediates", "foo_sbox")
Colin Crosse16ce362020-11-12 08:29:30 -0800605 outFile := filepath.Join(outDir, "gen/foo_sbox")
606 depFile := filepath.Join(outDir, "gen/foo_sbox.d")
607 manifest := filepath.Join(outDir, "sbox.textproto")
Dan Willemsen633c5022019-04-12 11:11:38 -0700608 sbox := filepath.Join(buildDir, "host", config.PrebuiltOS(), "bin/sbox")
609 sandboxPath := shared.TempDirForOutDir(buildDir)
610
Colin Crosse16ce362020-11-12 08:29:30 -0800611 cmd := `rm -rf ` + outDir + `/gen && ` +
612 sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest
Dan Willemsen633c5022019-04-12 11:11:38 -0700613
Colin Crosse16ce362020-11-12 08:29:30 -0800614 check(t, ctx.ModuleForTests("foo_sbox", "").Output("gen/foo_sbox"),
615 cmd, outFile, depFile, false, []string{manifest}, []string{sbox})
Colin Cross4c83e5c2019-02-25 14:54:28 -0800616 })
617 t.Run("singleton", func(t *testing.T) {
Colin Crosse16ce362020-11-12 08:29:30 -0800618 outFile := filepath.Join(buildDir, "singleton/gen/baz")
Colin Cross4c83e5c2019-02-25 14:54:28 -0800619 check(t, ctx.SingletonForTests("rule_builder_test").Rule("rule"),
Colin Crosse16ce362020-11-12 08:29:30 -0800620 "cp bar "+outFile, outFile, outFile+".d", true, nil, nil)
Colin Cross4c83e5c2019-02-25 14:54:28 -0800621 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800622}
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700623
Colin Cross3d680512020-11-13 16:23:53 -0800624func TestRuleBuilderHashInputs(t *testing.T) {
625 // The basic idea here is to verify that the command (in the case of a
626 // non-sbox rule) or the sbox textproto manifest contain a hash of the
627 // inputs.
628
629 // By including a hash of the inputs, we cause the rule to re-run if
630 // the list of inputs changes because the command line or a dependency
631 // changes.
632
633 bp := `
634 rule_builder_test {
635 name: "hash0",
636 srcs: ["in1.txt", "in2.txt"],
637 }
638 rule_builder_test {
639 name: "hash0_sbox",
640 srcs: ["in1.txt", "in2.txt"],
641 sbox: true,
642 }
643 rule_builder_test {
644 name: "hash1",
645 srcs: ["in1.txt", "in2.txt", "in3.txt"],
646 }
647 rule_builder_test {
648 name: "hash1_sbox",
649 srcs: ["in1.txt", "in2.txt", "in3.txt"],
650 sbox: true,
651 }
652 `
653 testcases := []struct {
654 name string
655 expectedHash string
656 }{
657 {
658 name: "hash0",
659 // sha256 value obtained from: echo -en 'in1.txt\nin2.txt' | sha256sum
660 expectedHash: "18da75b9b1cc74b09e365b4ca2e321b5d618f438cc632b387ad9dc2ab4b20e9d",
661 },
662 {
663 name: "hash1",
664 // sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
665 expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
666 },
667 }
668
669 config := TestConfig(buildDir, nil, bp, nil)
670 ctx := NewTestContext(config)
671 ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
672 ctx.Register()
673
674 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
675 FailIfErrored(t, errs)
676 _, errs = ctx.PrepareBuildActions(config)
677 FailIfErrored(t, errs)
678
679 for _, test := range testcases {
680 t.Run(test.name, func(t *testing.T) {
681 t.Run("sbox", func(t *testing.T) {
682 gen := ctx.ModuleForTests(test.name+"_sbox", "")
Colin Crosse16ce362020-11-12 08:29:30 -0800683 manifest := RuleBuilderSboxProtoForTests(t, gen.Output("sbox.textproto"))
684 hash := manifest.Commands[0].GetInputHash()
685
686 if g, w := hash, test.expectedHash; g != w {
687 t.Errorf("Expected has %q, got %q", w, g)
Colin Cross3d680512020-11-13 16:23:53 -0800688 }
689 })
690 t.Run("", func(t *testing.T) {
691 gen := ctx.ModuleForTests(test.name+"", "")
Colin Crosse16ce362020-11-12 08:29:30 -0800692 command := gen.Output("gen/" + test.name).RuleParams.Command
Colin Cross3d680512020-11-13 16:23:53 -0800693 if g, w := command, " # hash of input list: "+test.expectedHash; !strings.HasSuffix(g, w) {
694 t.Errorf("Expected command line to end with %q, got %q", w, g)
695 }
696 })
697 })
698 }
699}