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