blob: 70b9090b14bf577184f63778f77ad551d2c90055 [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"
20 "strings"
21 "testing"
22
23 "android/soong/android"
Jaewoong Jung98716bd2018-12-10 08:13:18 -080024 "reflect"
Colin Cross2a076922018-10-04 23:28:25 -070025)
26
27var buildDir string
28
29func setUp() {
30 var err error
Colin Crossef354482018-10-23 11:27:50 -070031 buildDir, err = ioutil.TempDir("", "genrule_test")
Colin Cross2a076922018-10-04 23:28:25 -070032 if err != nil {
33 panic(err)
34 }
35}
36
37func tearDown() {
38 os.RemoveAll(buildDir)
39}
40
41func TestMain(m *testing.M) {
42 run := func() int {
43 setUp()
44 defer tearDown()
45
46 return m.Run()
47 }
48
49 os.Exit(run())
50}
51
52func testContext(config android.Config, bp string,
53 fs map[string][]byte) *android.TestContext {
54
55 ctx := android.NewTestArchContext()
56 ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
57 ctx.RegisterModuleType("genrule", android.ModuleFactoryAdaptor(GenRuleFactory))
Jaewoong Jung98716bd2018-12-10 08:13:18 -080058 ctx.RegisterModuleType("genrule_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
Colin Cross2a076922018-10-04 23:28:25 -070059 ctx.RegisterModuleType("tool", android.ModuleFactoryAdaptor(toolFactory))
Jaewoong Jung98716bd2018-12-10 08:13:18 -080060 ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
Colin Cross2a076922018-10-04 23:28:25 -070061 ctx.Register()
62
63 bp += `
64 tool {
65 name: "tool",
66 }
67
68 filegroup {
69 name: "tool_files",
70 srcs: [
71 "tool_file1",
72 "tool_file2",
73 ],
74 }
75
76 filegroup {
77 name: "1tool_file",
78 srcs: [
79 "tool_file1",
80 ],
81 }
82
83 filegroup {
84 name: "ins",
85 srcs: [
86 "in1",
87 "in2",
88 ],
89 }
90
91 filegroup {
92 name: "1in",
93 srcs: [
94 "in1",
95 ],
96 }
97
98 filegroup {
99 name: "empty",
100 }
101 `
102
103 mockFS := map[string][]byte{
104 "Android.bp": []byte(bp),
105 "tool": nil,
106 "tool_file1": nil,
107 "tool_file2": nil,
108 "in1": nil,
109 "in2": nil,
110 }
111
112 for k, v := range fs {
113 mockFS[k] = v
114 }
115
116 ctx.MockFileSystem(mockFS)
117
118 return ctx
119}
120
121func TestGenruleCmd(t *testing.T) {
122 testcases := []struct {
123 name string
124 prop string
125
126 err string
127 expect string
128 }{
129 {
130 name: "empty location tool",
131 prop: `
132 tools: ["tool"],
133 out: ["out"],
134 cmd: "$(location) > $(out)",
135 `,
136 expect: "out/tool > __SBOX_OUT_FILES__",
137 },
138 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700139 name: "empty location tool2",
140 prop: `
141 tools: [":tool"],
142 out: ["out"],
143 cmd: "$(location) > $(out)",
144 `,
145 expect: "out/tool > __SBOX_OUT_FILES__",
146 },
147 {
Colin Cross2a076922018-10-04 23:28:25 -0700148 name: "empty location tool file",
149 prop: `
150 tool_files: ["tool_file1"],
151 out: ["out"],
152 cmd: "$(location) > $(out)",
153 `,
154 expect: "tool_file1 > __SBOX_OUT_FILES__",
155 },
156 {
157 name: "empty location tool file fg",
158 prop: `
159 tool_files: [":1tool_file"],
160 out: ["out"],
161 cmd: "$(location) > $(out)",
162 `,
163 expect: "tool_file1 > __SBOX_OUT_FILES__",
164 },
165 {
166 name: "empty location tool and tool file",
167 prop: `
168 tools: ["tool"],
169 tool_files: ["tool_file1"],
170 out: ["out"],
171 cmd: "$(location) > $(out)",
172 `,
173 expect: "out/tool > __SBOX_OUT_FILES__",
174 },
175 {
176 name: "tool",
177 prop: `
178 tools: ["tool"],
179 out: ["out"],
180 cmd: "$(location tool) > $(out)",
181 `,
182 expect: "out/tool > __SBOX_OUT_FILES__",
183 },
184 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700185 name: "tool2",
186 prop: `
187 tools: [":tool"],
188 out: ["out"],
189 cmd: "$(location :tool) > $(out)",
190 `,
191 expect: "out/tool > __SBOX_OUT_FILES__",
192 },
193 {
Colin Cross2a076922018-10-04 23:28:25 -0700194 name: "tool file",
195 prop: `
196 tool_files: ["tool_file1"],
197 out: ["out"],
198 cmd: "$(location tool_file1) > $(out)",
199 `,
200 expect: "tool_file1 > __SBOX_OUT_FILES__",
201 },
202 {
203 name: "tool file fg",
204 prop: `
205 tool_files: [":1tool_file"],
206 out: ["out"],
Colin Cross08f15ab2018-10-04 23:29:14 -0700207 cmd: "$(location :1tool_file) > $(out)",
Colin Cross2a076922018-10-04 23:28:25 -0700208 `,
209 expect: "tool_file1 > __SBOX_OUT_FILES__",
210 },
211 {
212 name: "tool files",
213 prop: `
214 tool_files: [":tool_files"],
215 out: ["out"],
Colin Cross08f15ab2018-10-04 23:29:14 -0700216 cmd: "$(locations :tool_files) > $(out)",
Colin Cross2a076922018-10-04 23:28:25 -0700217 `,
218 expect: "tool_file1 tool_file2 > __SBOX_OUT_FILES__",
219 },
220 {
221 name: "in1",
222 prop: `
223 srcs: ["in1"],
224 out: ["out"],
225 cmd: "cat $(in) > $(out)",
226 `,
227 expect: "cat ${in} > __SBOX_OUT_FILES__",
228 },
229 {
230 name: "in1 fg",
231 prop: `
232 srcs: [":1in"],
233 out: ["out"],
234 cmd: "cat $(in) > $(out)",
235 `,
236 expect: "cat ${in} > __SBOX_OUT_FILES__",
237 },
238 {
239 name: "ins",
240 prop: `
241 srcs: ["in1", "in2"],
242 out: ["out"],
243 cmd: "cat $(in) > $(out)",
244 `,
245 expect: "cat ${in} > __SBOX_OUT_FILES__",
246 },
247 {
248 name: "ins fg",
249 prop: `
250 srcs: [":ins"],
251 out: ["out"],
252 cmd: "cat $(in) > $(out)",
253 `,
254 expect: "cat ${in} > __SBOX_OUT_FILES__",
255 },
256 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700257 name: "location in1",
258 prop: `
259 srcs: ["in1"],
260 out: ["out"],
261 cmd: "cat $(location in1) > $(out)",
262 `,
263 expect: "cat in1 > __SBOX_OUT_FILES__",
264 },
265 {
266 name: "location in1 fg",
267 prop: `
268 srcs: [":1in"],
269 out: ["out"],
270 cmd: "cat $(location :1in) > $(out)",
271 `,
272 expect: "cat in1 > __SBOX_OUT_FILES__",
273 },
274 {
275 name: "location ins",
276 prop: `
277 srcs: ["in1", "in2"],
278 out: ["out"],
279 cmd: "cat $(location in1) > $(out)",
280 `,
281 expect: "cat in1 > __SBOX_OUT_FILES__",
282 },
283 {
284 name: "location ins fg",
285 prop: `
286 srcs: [":ins"],
287 out: ["out"],
288 cmd: "cat $(locations :ins) > $(out)",
289 `,
290 expect: "cat in1 in2 > __SBOX_OUT_FILES__",
291 },
292 {
Colin Cross2a076922018-10-04 23:28:25 -0700293 name: "outs",
294 prop: `
295 out: ["out", "out2"],
296 cmd: "echo foo > $(out)",
297 `,
298 expect: "echo foo > __SBOX_OUT_FILES__",
299 },
300 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700301 name: "location out",
302 prop: `
303 out: ["out", "out2"],
304 cmd: "echo foo > $(location out2)",
305 `,
306 expect: "echo foo > __SBOX_OUT_DIR__/out2",
307 },
308 {
Colin Cross2a076922018-10-04 23:28:25 -0700309 name: "depfile",
310 prop: `
311 out: ["out"],
312 depfile: true,
313 cmd: "echo foo > $(out) && touch $(depfile)",
314 `,
315 expect: "echo foo > __SBOX_OUT_FILES__ && touch __SBOX_DEPFILE__",
316 },
317 {
318 name: "gendir",
319 prop: `
320 out: ["out"],
321 cmd: "echo foo > $(genDir)/foo && cp $(genDir)/foo $(out)",
322 `,
323 expect: "echo foo > __SBOX_OUT_DIR__/foo && cp __SBOX_OUT_DIR__/foo __SBOX_OUT_FILES__",
324 },
325
326 {
327 name: "error empty location",
328 prop: `
329 out: ["out"],
330 cmd: "$(location) > $(out)",
331 `,
332 err: "at least one `tools` or `tool_files` is required if $(location) is used",
333 },
334 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700335 name: "error empty location no files",
336 prop: `
337 tool_files: [":empty"],
338 out: ["out"],
339 cmd: "$(location) > $(out)",
340 `,
341 err: `default label ":empty" has no files`,
342 },
343 {
344 name: "error empty location multiple files",
345 prop: `
346 tool_files: [":tool_files"],
347 out: ["out"],
348 cmd: "$(location) > $(out)",
349 `,
350 err: `default label ":tool_files" has multiple files`,
351 },
352 {
Colin Cross2a076922018-10-04 23:28:25 -0700353 name: "error location",
354 prop: `
355 out: ["out"],
356 cmd: "echo foo > $(location missing)",
357 `,
358 err: `unknown location label "missing"`,
359 },
360 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700361 name: "error locations",
362 prop: `
363 out: ["out"],
364 cmd: "echo foo > $(locations missing)",
365 `,
366 err: `unknown locations label "missing"`,
367 },
368 {
369 name: "error location no files",
370 prop: `
371 out: ["out"],
372 srcs: [":empty"],
373 cmd: "echo $(location :empty) > $(out)",
374 `,
375 err: `label ":empty" has no files`,
376 },
377 {
378 name: "error locations no files",
379 prop: `
380 out: ["out"],
381 srcs: [":empty"],
382 cmd: "echo $(locations :empty) > $(out)",
383 `,
384 err: `label ":empty" has no files`,
385 },
386 {
387 name: "error location multiple files",
388 prop: `
389 out: ["out"],
390 srcs: [":ins"],
391 cmd: "echo $(location :ins) > $(out)",
392 `,
393 err: `label ":ins" has multiple files`,
394 },
395 {
Colin Cross2a076922018-10-04 23:28:25 -0700396 name: "error variable",
397 prop: `
398 out: ["out"],
399 srcs: ["in1"],
400 cmd: "echo $(foo) > $(out)",
401 `,
402 err: `unknown variable '$(foo)'`,
403 },
404 {
405 name: "error depfile",
406 prop: `
407 out: ["out"],
408 cmd: "echo foo > $(out) && touch $(depfile)",
409 `,
410 err: "$(depfile) used without depfile property",
411 },
412 {
413 name: "error no depfile",
414 prop: `
415 out: ["out"],
416 depfile: true,
417 cmd: "echo foo > $(out)",
418 `,
419 err: "specified depfile=true but did not include a reference to '${depfile}' in cmd",
420 },
421 {
422 name: "error no out",
423 prop: `
424 cmd: "echo foo > $(out)",
425 `,
426 err: "must have at least one output file",
427 },
428 }
429
430 for _, test := range testcases {
431 t.Run(test.name, func(t *testing.T) {
432 config := android.TestArchConfig(buildDir, nil)
433 bp := "genrule {\n"
434 bp += "name: \"gen\",\n"
435 bp += test.prop
436 bp += "}\n"
437
438 ctx := testContext(config, bp, nil)
439
440 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
441 if errs == nil {
442 _, errs = ctx.PrepareBuildActions(config)
443 }
444 if errs == nil && test.err != "" {
445 t.Fatalf("want error %q, got no error", test.err)
446 } else if errs != nil && test.err == "" {
447 android.FailIfErrored(t, errs)
448 } else if test.err != "" {
449 if len(errs) != 1 {
450 t.Errorf("want 1 error, got %d errors:", len(errs))
451 for _, err := range errs {
452 t.Errorf(" %s", err.Error())
453 }
454 t.FailNow()
455 }
456 if !strings.Contains(errs[0].Error(), test.err) {
457 t.Fatalf("want %q, got %q", test.err, errs[0].Error())
458 }
459 return
460 }
461
462 gen := ctx.ModuleForTests("gen", "").Module().(*Module)
463 if gen.rawCommand != "'"+test.expect+"'" {
464 t.Errorf("want %q, got %q", test.expect, gen.rawCommand)
465 }
466 })
467 }
468
469}
470
Jaewoong Jung98716bd2018-12-10 08:13:18 -0800471func TestGenruleDefaults(t *testing.T) {
472 config := android.TestArchConfig(buildDir, nil)
473 bp := `
474 genrule_defaults {
475 name: "gen_defaults1",
476 cmd: "cp $(in) $(out)",
477 }
478
479 genrule_defaults {
480 name: "gen_defaults2",
481 srcs: ["in1"],
482 }
483
484 genrule {
485 name: "gen",
486 out: ["out"],
487 defaults: ["gen_defaults1", "gen_defaults2"],
488 }
489 `
490 ctx := testContext(config, bp, nil)
491 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
492 if errs == nil {
493 _, errs = ctx.PrepareBuildActions(config)
494 }
495 if errs != nil {
496 t.Fatal(errs)
497 }
498 gen := ctx.ModuleForTests("gen", "").Module().(*Module)
499
500 expectedCmd := "'cp ${in} __SBOX_OUT_FILES__'"
501 if gen.rawCommand != expectedCmd {
502 t.Errorf("Expected cmd: %q, actual: %q", expectedCmd, gen.rawCommand)
503 }
504
505 expectedSrcs := []string{"in1"}
506 if !reflect.DeepEqual(expectedSrcs, gen.properties.Srcs) {
507 t.Errorf("Expected srcs: %q, actual: %q", expectedSrcs, gen.properties.Srcs)
508 }
509}
510
Colin Cross2a076922018-10-04 23:28:25 -0700511type testTool struct {
512 android.ModuleBase
513 outputFile android.Path
514}
515
516func toolFactory() android.Module {
517 module := &testTool{}
518 android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
519 return module
520}
521
522func (t *testTool) DepsMutator(ctx android.BottomUpMutatorContext) {}
523
524func (t *testTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
525 t.outputFile = android.PathForTesting("out", ctx.ModuleName())
526}
527
528func (t *testTool) HostToolPath() android.OptionalPath {
529 return android.OptionalPathForPath(t.outputFile)
530}
531
532var _ HostToolProvider = (*testTool)(nil)