blob: 0b6952f30477dec9ab65522ef77dd9c1a04b516a [file] [log] [blame]
Colin Cross2a076922018-10-04 23:28:25 -07001// Copyright 2018 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 genrule
16
17import (
18 "io/ioutil"
19 "os"
Colin Crossba71a3f2019-03-18 12:12:48 -070020 "reflect"
Colin Cross2a076922018-10-04 23:28:25 -070021 "strings"
22 "testing"
23
24 "android/soong/android"
Colin Crossba71a3f2019-03-18 12:12:48 -070025
26 "github.com/google/blueprint/proptools"
Colin Cross2a076922018-10-04 23:28:25 -070027)
28
29var buildDir string
30
31func setUp() {
32 var err error
Colin Crossef354482018-10-23 11:27:50 -070033 buildDir, err = ioutil.TempDir("", "genrule_test")
Colin Cross2a076922018-10-04 23:28:25 -070034 if err != nil {
35 panic(err)
36 }
37}
38
39func tearDown() {
40 os.RemoveAll(buildDir)
41}
42
43func TestMain(m *testing.M) {
44 run := func() int {
45 setUp()
46 defer tearDown()
47
48 return m.Run()
49 }
50
51 os.Exit(run())
52}
53
54func testContext(config android.Config, bp string,
55 fs map[string][]byte) *android.TestContext {
56
57 ctx := android.NewTestArchContext()
58 ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
59 ctx.RegisterModuleType("genrule", android.ModuleFactoryAdaptor(GenRuleFactory))
Jaewoong Jung98716bd2018-12-10 08:13:18 -080060 ctx.RegisterModuleType("genrule_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
Colin Cross2a076922018-10-04 23:28:25 -070061 ctx.RegisterModuleType("tool", android.ModuleFactoryAdaptor(toolFactory))
Jaewoong Jung98716bd2018-12-10 08:13:18 -080062 ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
Colin Cross2a076922018-10-04 23:28:25 -070063 ctx.Register()
64
65 bp += `
66 tool {
67 name: "tool",
68 }
69
70 filegroup {
71 name: "tool_files",
72 srcs: [
73 "tool_file1",
74 "tool_file2",
75 ],
76 }
77
78 filegroup {
79 name: "1tool_file",
80 srcs: [
81 "tool_file1",
82 ],
83 }
84
85 filegroup {
86 name: "ins",
87 srcs: [
88 "in1",
89 "in2",
90 ],
91 }
92
93 filegroup {
94 name: "1in",
95 srcs: [
96 "in1",
97 ],
98 }
99
100 filegroup {
101 name: "empty",
102 }
103 `
104
105 mockFS := map[string][]byte{
106 "Android.bp": []byte(bp),
107 "tool": nil,
108 "tool_file1": nil,
109 "tool_file2": nil,
110 "in1": nil,
111 "in2": nil,
112 }
113
114 for k, v := range fs {
115 mockFS[k] = v
116 }
117
118 ctx.MockFileSystem(mockFS)
119
120 return ctx
121}
122
123func TestGenruleCmd(t *testing.T) {
124 testcases := []struct {
125 name string
126 prop string
127
Colin Crossba71a3f2019-03-18 12:12:48 -0700128 allowMissingDependencies bool
129
Colin Cross2a076922018-10-04 23:28:25 -0700130 err string
131 expect string
132 }{
133 {
134 name: "empty location tool",
135 prop: `
136 tools: ["tool"],
137 out: ["out"],
138 cmd: "$(location) > $(out)",
139 `,
140 expect: "out/tool > __SBOX_OUT_FILES__",
141 },
142 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700143 name: "empty location tool2",
144 prop: `
145 tools: [":tool"],
146 out: ["out"],
147 cmd: "$(location) > $(out)",
148 `,
149 expect: "out/tool > __SBOX_OUT_FILES__",
150 },
151 {
Colin Cross2a076922018-10-04 23:28:25 -0700152 name: "empty location tool file",
153 prop: `
154 tool_files: ["tool_file1"],
155 out: ["out"],
156 cmd: "$(location) > $(out)",
157 `,
158 expect: "tool_file1 > __SBOX_OUT_FILES__",
159 },
160 {
161 name: "empty location tool file fg",
162 prop: `
163 tool_files: [":1tool_file"],
164 out: ["out"],
165 cmd: "$(location) > $(out)",
166 `,
167 expect: "tool_file1 > __SBOX_OUT_FILES__",
168 },
169 {
170 name: "empty location tool and tool file",
171 prop: `
172 tools: ["tool"],
173 tool_files: ["tool_file1"],
174 out: ["out"],
175 cmd: "$(location) > $(out)",
176 `,
177 expect: "out/tool > __SBOX_OUT_FILES__",
178 },
179 {
180 name: "tool",
181 prop: `
182 tools: ["tool"],
183 out: ["out"],
184 cmd: "$(location tool) > $(out)",
185 `,
186 expect: "out/tool > __SBOX_OUT_FILES__",
187 },
188 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700189 name: "tool2",
190 prop: `
191 tools: [":tool"],
192 out: ["out"],
193 cmd: "$(location :tool) > $(out)",
194 `,
195 expect: "out/tool > __SBOX_OUT_FILES__",
196 },
197 {
Colin Cross2a076922018-10-04 23:28:25 -0700198 name: "tool file",
199 prop: `
200 tool_files: ["tool_file1"],
201 out: ["out"],
202 cmd: "$(location tool_file1) > $(out)",
203 `,
204 expect: "tool_file1 > __SBOX_OUT_FILES__",
205 },
206 {
207 name: "tool file fg",
208 prop: `
209 tool_files: [":1tool_file"],
210 out: ["out"],
Colin Cross08f15ab2018-10-04 23:29:14 -0700211 cmd: "$(location :1tool_file) > $(out)",
Colin Cross2a076922018-10-04 23:28:25 -0700212 `,
213 expect: "tool_file1 > __SBOX_OUT_FILES__",
214 },
215 {
216 name: "tool files",
217 prop: `
218 tool_files: [":tool_files"],
219 out: ["out"],
Colin Cross08f15ab2018-10-04 23:29:14 -0700220 cmd: "$(locations :tool_files) > $(out)",
Colin Cross2a076922018-10-04 23:28:25 -0700221 `,
222 expect: "tool_file1 tool_file2 > __SBOX_OUT_FILES__",
223 },
224 {
225 name: "in1",
226 prop: `
227 srcs: ["in1"],
228 out: ["out"],
229 cmd: "cat $(in) > $(out)",
230 `,
231 expect: "cat ${in} > __SBOX_OUT_FILES__",
232 },
233 {
234 name: "in1 fg",
235 prop: `
236 srcs: [":1in"],
237 out: ["out"],
238 cmd: "cat $(in) > $(out)",
239 `,
240 expect: "cat ${in} > __SBOX_OUT_FILES__",
241 },
242 {
243 name: "ins",
244 prop: `
245 srcs: ["in1", "in2"],
246 out: ["out"],
247 cmd: "cat $(in) > $(out)",
248 `,
249 expect: "cat ${in} > __SBOX_OUT_FILES__",
250 },
251 {
252 name: "ins fg",
253 prop: `
254 srcs: [":ins"],
255 out: ["out"],
256 cmd: "cat $(in) > $(out)",
257 `,
258 expect: "cat ${in} > __SBOX_OUT_FILES__",
259 },
260 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700261 name: "location in1",
262 prop: `
263 srcs: ["in1"],
264 out: ["out"],
265 cmd: "cat $(location in1) > $(out)",
266 `,
267 expect: "cat in1 > __SBOX_OUT_FILES__",
268 },
269 {
270 name: "location in1 fg",
271 prop: `
272 srcs: [":1in"],
273 out: ["out"],
274 cmd: "cat $(location :1in) > $(out)",
275 `,
276 expect: "cat in1 > __SBOX_OUT_FILES__",
277 },
278 {
279 name: "location ins",
280 prop: `
281 srcs: ["in1", "in2"],
282 out: ["out"],
283 cmd: "cat $(location in1) > $(out)",
284 `,
285 expect: "cat in1 > __SBOX_OUT_FILES__",
286 },
287 {
288 name: "location ins fg",
289 prop: `
290 srcs: [":ins"],
291 out: ["out"],
292 cmd: "cat $(locations :ins) > $(out)",
293 `,
294 expect: "cat in1 in2 > __SBOX_OUT_FILES__",
295 },
296 {
Colin Cross2a076922018-10-04 23:28:25 -0700297 name: "outs",
298 prop: `
299 out: ["out", "out2"],
300 cmd: "echo foo > $(out)",
301 `,
302 expect: "echo foo > __SBOX_OUT_FILES__",
303 },
304 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700305 name: "location out",
306 prop: `
307 out: ["out", "out2"],
308 cmd: "echo foo > $(location out2)",
309 `,
310 expect: "echo foo > __SBOX_OUT_DIR__/out2",
311 },
312 {
Colin Cross2a076922018-10-04 23:28:25 -0700313 name: "depfile",
314 prop: `
315 out: ["out"],
316 depfile: true,
317 cmd: "echo foo > $(out) && touch $(depfile)",
318 `,
319 expect: "echo foo > __SBOX_OUT_FILES__ && touch __SBOX_DEPFILE__",
320 },
321 {
322 name: "gendir",
323 prop: `
324 out: ["out"],
325 cmd: "echo foo > $(genDir)/foo && cp $(genDir)/foo $(out)",
326 `,
327 expect: "echo foo > __SBOX_OUT_DIR__/foo && cp __SBOX_OUT_DIR__/foo __SBOX_OUT_FILES__",
328 },
329
330 {
331 name: "error empty location",
332 prop: `
333 out: ["out"],
334 cmd: "$(location) > $(out)",
335 `,
336 err: "at least one `tools` or `tool_files` is required if $(location) is used",
337 },
338 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700339 name: "error empty location no files",
340 prop: `
341 tool_files: [":empty"],
342 out: ["out"],
343 cmd: "$(location) > $(out)",
344 `,
345 err: `default label ":empty" has no files`,
346 },
347 {
348 name: "error empty location multiple files",
349 prop: `
350 tool_files: [":tool_files"],
351 out: ["out"],
352 cmd: "$(location) > $(out)",
353 `,
354 err: `default label ":tool_files" has multiple files`,
355 },
356 {
Colin Cross2a076922018-10-04 23:28:25 -0700357 name: "error location",
358 prop: `
359 out: ["out"],
360 cmd: "echo foo > $(location missing)",
361 `,
362 err: `unknown location label "missing"`,
363 },
364 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700365 name: "error locations",
366 prop: `
367 out: ["out"],
368 cmd: "echo foo > $(locations missing)",
369 `,
370 err: `unknown locations label "missing"`,
371 },
372 {
373 name: "error location no files",
374 prop: `
375 out: ["out"],
376 srcs: [":empty"],
377 cmd: "echo $(location :empty) > $(out)",
378 `,
379 err: `label ":empty" has no files`,
380 },
381 {
382 name: "error locations no files",
383 prop: `
384 out: ["out"],
385 srcs: [":empty"],
386 cmd: "echo $(locations :empty) > $(out)",
387 `,
388 err: `label ":empty" has no files`,
389 },
390 {
391 name: "error location multiple files",
392 prop: `
393 out: ["out"],
394 srcs: [":ins"],
395 cmd: "echo $(location :ins) > $(out)",
396 `,
397 err: `label ":ins" has multiple files`,
398 },
399 {
Colin Cross2a076922018-10-04 23:28:25 -0700400 name: "error variable",
401 prop: `
402 out: ["out"],
403 srcs: ["in1"],
404 cmd: "echo $(foo) > $(out)",
405 `,
406 err: `unknown variable '$(foo)'`,
407 },
408 {
409 name: "error depfile",
410 prop: `
411 out: ["out"],
412 cmd: "echo foo > $(out) && touch $(depfile)",
413 `,
414 err: "$(depfile) used without depfile property",
415 },
416 {
417 name: "error no depfile",
418 prop: `
419 out: ["out"],
420 depfile: true,
421 cmd: "echo foo > $(out)",
422 `,
423 err: "specified depfile=true but did not include a reference to '${depfile}' in cmd",
424 },
425 {
426 name: "error no out",
427 prop: `
428 cmd: "echo foo > $(out)",
429 `,
430 err: "must have at least one output file",
431 },
Colin Crossba71a3f2019-03-18 12:12:48 -0700432 {
433 name: "srcs allow missing dependencies",
434 prop: `
435 srcs: [":missing"],
436 out: ["out"],
437 cmd: "cat $(location :missing) > $(out)",
438 `,
439
440 allowMissingDependencies: true,
441
442 expect: "cat ***missing srcs :missing*** > __SBOX_OUT_FILES__",
443 },
444 {
445 name: "tool allow missing dependencies",
446 prop: `
447 tools: [":missing"],
448 out: ["out"],
449 cmd: "$(location :missing) > $(out)",
450 `,
451
452 allowMissingDependencies: true,
453
454 expect: "***missing tool :missing*** > __SBOX_OUT_FILES__",
455 },
Colin Cross2a076922018-10-04 23:28:25 -0700456 }
457
458 for _, test := range testcases {
459 t.Run(test.name, func(t *testing.T) {
460 config := android.TestArchConfig(buildDir, nil)
461 bp := "genrule {\n"
462 bp += "name: \"gen\",\n"
463 bp += test.prop
464 bp += "}\n"
465
Colin Crossba71a3f2019-03-18 12:12:48 -0700466 config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(test.allowMissingDependencies)
467
Colin Cross2a076922018-10-04 23:28:25 -0700468 ctx := testContext(config, bp, nil)
Colin Crossba71a3f2019-03-18 12:12:48 -0700469 ctx.SetAllowMissingDependencies(test.allowMissingDependencies)
Colin Cross2a076922018-10-04 23:28:25 -0700470
471 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
472 if errs == nil {
473 _, errs = ctx.PrepareBuildActions(config)
474 }
475 if errs == nil && test.err != "" {
476 t.Fatalf("want error %q, got no error", test.err)
477 } else if errs != nil && test.err == "" {
478 android.FailIfErrored(t, errs)
479 } else if test.err != "" {
480 if len(errs) != 1 {
481 t.Errorf("want 1 error, got %d errors:", len(errs))
482 for _, err := range errs {
483 t.Errorf(" %s", err.Error())
484 }
485 t.FailNow()
486 }
487 if !strings.Contains(errs[0].Error(), test.err) {
488 t.Fatalf("want %q, got %q", test.err, errs[0].Error())
489 }
490 return
491 }
492
493 gen := ctx.ModuleForTests("gen", "").Module().(*Module)
Colin Crossba71a3f2019-03-18 12:12:48 -0700494 if g, w := gen.rawCommand, "'"+test.expect+"'"; w != g {
495 t.Errorf("want %q, got %q", w, g)
Colin Cross2a076922018-10-04 23:28:25 -0700496 }
497 })
498 }
499
500}
501
Jaewoong Jung98716bd2018-12-10 08:13:18 -0800502func TestGenruleDefaults(t *testing.T) {
503 config := android.TestArchConfig(buildDir, nil)
504 bp := `
505 genrule_defaults {
506 name: "gen_defaults1",
507 cmd: "cp $(in) $(out)",
508 }
509
510 genrule_defaults {
511 name: "gen_defaults2",
512 srcs: ["in1"],
513 }
514
515 genrule {
516 name: "gen",
517 out: ["out"],
518 defaults: ["gen_defaults1", "gen_defaults2"],
519 }
520 `
521 ctx := testContext(config, bp, nil)
522 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
523 if errs == nil {
524 _, errs = ctx.PrepareBuildActions(config)
525 }
526 if errs != nil {
527 t.Fatal(errs)
528 }
529 gen := ctx.ModuleForTests("gen", "").Module().(*Module)
530
531 expectedCmd := "'cp ${in} __SBOX_OUT_FILES__'"
532 if gen.rawCommand != expectedCmd {
533 t.Errorf("Expected cmd: %q, actual: %q", expectedCmd, gen.rawCommand)
534 }
535
536 expectedSrcs := []string{"in1"}
537 if !reflect.DeepEqual(expectedSrcs, gen.properties.Srcs) {
538 t.Errorf("Expected srcs: %q, actual: %q", expectedSrcs, gen.properties.Srcs)
539 }
540}
541
Colin Cross2a076922018-10-04 23:28:25 -0700542type testTool struct {
543 android.ModuleBase
544 outputFile android.Path
545}
546
547func toolFactory() android.Module {
548 module := &testTool{}
549 android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
550 return module
551}
552
Colin Cross2a076922018-10-04 23:28:25 -0700553func (t *testTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
554 t.outputFile = android.PathForTesting("out", ctx.ModuleName())
555}
556
557func (t *testTool) HostToolPath() android.OptionalPath {
558 return android.OptionalPathForPath(t.outputFile)
559}
560
Colin Crossfe17f6f2019-03-28 19:30:56 -0700561var _ android.HostToolProvider = (*testTool)(nil)