blob: cd941ccbce6f9a1ca86f3c46f34d80d3246a4a29 [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 (
Vinh Tran140d5882022-06-10 14:23:27 -040018 "fmt"
Colin Cross2a076922018-10-04 23:28:25 -070019 "os"
Paul Duffin672cb9f2021-03-03 02:30:37 +000020 "regexp"
Colin Cross2a076922018-10-04 23:28:25 -070021 "testing"
22
23 "android/soong/android"
Colin Crossba71a3f2019-03-18 12:12:48 -070024
25 "github.com/google/blueprint/proptools"
Colin Cross2a076922018-10-04 23:28:25 -070026)
27
Colin Cross2a076922018-10-04 23:28:25 -070028func TestMain(m *testing.M) {
Paul Duffine66946b2021-03-16 12:38:33 +000029 os.Exit(m.Run())
Colin Cross2a076922018-10-04 23:28:25 -070030}
31
Paul Duffin89648f92021-03-20 00:36:55 +000032var prepareForGenRuleTest = android.GroupFixturePreparers(
Paul Duffin672cb9f2021-03-03 02:30:37 +000033 android.PrepareForTestWithArchMutator,
34 android.PrepareForTestWithDefaults,
Paul Duffin672cb9f2021-03-03 02:30:37 +000035 android.PrepareForTestWithFilegroup,
36 PrepareForTestWithGenRuleBuildComponents,
37 android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
Martin Stjernholmdbd814d2022-01-12 23:18:30 +000038 android.RegisterPrebuiltMutators(ctx)
Paul Duffin672cb9f2021-03-03 02:30:37 +000039 ctx.RegisterModuleType("tool", toolFactory)
Martin Stjernholmdbd814d2022-01-12 23:18:30 +000040 ctx.RegisterModuleType("prebuilt_tool", prebuiltToolFactory)
Colin Crossfa65cee2021-03-22 17:05:59 -070041 ctx.RegisterModuleType("output", outputProducerFactory)
Jooyung Han8c7e3ed2021-06-28 17:35:58 +090042 ctx.RegisterModuleType("use_source", useSourceFactory)
Paul Duffin672cb9f2021-03-03 02:30:37 +000043 }),
44 android.FixtureMergeMockFs(android.MockFS{
45 "tool": nil,
46 "tool_file1": nil,
47 "tool_file2": nil,
48 "in1": nil,
49 "in2": nil,
50 "in1.txt": nil,
51 "in2.txt": nil,
52 "in3.txt": nil,
53 }),
54)
Martin Stjernholm710ec3a2020-01-16 15:12:04 +000055
Paul Duffin672cb9f2021-03-03 02:30:37 +000056func testGenruleBp() string {
57 return `
Colin Cross2a076922018-10-04 23:28:25 -070058 tool {
59 name: "tool",
60 }
61
62 filegroup {
63 name: "tool_files",
64 srcs: [
65 "tool_file1",
66 "tool_file2",
67 ],
68 }
69
70 filegroup {
71 name: "1tool_file",
72 srcs: [
73 "tool_file1",
74 ],
75 }
76
77 filegroup {
78 name: "ins",
79 srcs: [
80 "in1",
81 "in2",
82 ],
83 }
84
85 filegroup {
86 name: "1in",
87 srcs: [
88 "in1",
89 ],
90 }
91
92 filegroup {
93 name: "empty",
94 }
95 `
Colin Cross2a076922018-10-04 23:28:25 -070096}
97
98func TestGenruleCmd(t *testing.T) {
99 testcases := []struct {
100 name string
101 prop string
102
Colin Crossba71a3f2019-03-18 12:12:48 -0700103 allowMissingDependencies bool
104
Colin Cross2a076922018-10-04 23:28:25 -0700105 err string
106 expect string
107 }{
108 {
109 name: "empty location tool",
110 prop: `
111 tools: ["tool"],
112 out: ["out"],
113 cmd: "$(location) > $(out)",
114 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800115 expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700116 },
117 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700118 name: "empty location tool2",
119 prop: `
120 tools: [":tool"],
121 out: ["out"],
122 cmd: "$(location) > $(out)",
123 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800124 expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross08f15ab2018-10-04 23:29:14 -0700125 },
126 {
Colin Cross2a076922018-10-04 23:28:25 -0700127 name: "empty location tool file",
128 prop: `
129 tool_files: ["tool_file1"],
130 out: ["out"],
131 cmd: "$(location) > $(out)",
132 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800133 expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700134 },
135 {
136 name: "empty location tool file fg",
137 prop: `
138 tool_files: [":1tool_file"],
139 out: ["out"],
140 cmd: "$(location) > $(out)",
141 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800142 expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700143 },
144 {
145 name: "empty location tool and tool file",
146 prop: `
147 tools: ["tool"],
148 tool_files: ["tool_file1"],
149 out: ["out"],
150 cmd: "$(location) > $(out)",
151 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800152 expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700153 },
154 {
155 name: "tool",
156 prop: `
157 tools: ["tool"],
158 out: ["out"],
159 cmd: "$(location tool) > $(out)",
160 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800161 expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700162 },
163 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700164 name: "tool2",
165 prop: `
166 tools: [":tool"],
167 out: ["out"],
168 cmd: "$(location :tool) > $(out)",
169 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800170 expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross08f15ab2018-10-04 23:29:14 -0700171 },
172 {
Colin Cross2a076922018-10-04 23:28:25 -0700173 name: "tool file",
174 prop: `
175 tool_files: ["tool_file1"],
176 out: ["out"],
177 cmd: "$(location tool_file1) > $(out)",
178 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800179 expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700180 },
181 {
182 name: "tool file fg",
183 prop: `
184 tool_files: [":1tool_file"],
185 out: ["out"],
Colin Cross08f15ab2018-10-04 23:29:14 -0700186 cmd: "$(location :1tool_file) > $(out)",
Colin Cross2a076922018-10-04 23:28:25 -0700187 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800188 expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700189 },
190 {
191 name: "tool files",
192 prop: `
193 tool_files: [":tool_files"],
194 out: ["out"],
Colin Cross08f15ab2018-10-04 23:29:14 -0700195 cmd: "$(locations :tool_files) > $(out)",
Colin Cross2a076922018-10-04 23:28:25 -0700196 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800197 expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 __SBOX_SANDBOX_DIR__/tools/src/tool_file2 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700198 },
199 {
200 name: "in1",
201 prop: `
202 srcs: ["in1"],
203 out: ["out"],
204 cmd: "cat $(in) > $(out)",
205 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800206 expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700207 },
208 {
209 name: "in1 fg",
210 prop: `
211 srcs: [":1in"],
212 out: ["out"],
213 cmd: "cat $(in) > $(out)",
214 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800215 expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700216 },
217 {
218 name: "ins",
219 prop: `
220 srcs: ["in1", "in2"],
221 out: ["out"],
222 cmd: "cat $(in) > $(out)",
223 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800224 expect: "cat in1 in2 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700225 },
226 {
227 name: "ins fg",
228 prop: `
229 srcs: [":ins"],
230 out: ["out"],
231 cmd: "cat $(in) > $(out)",
232 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800233 expect: "cat in1 in2 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700234 },
235 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700236 name: "location in1",
237 prop: `
238 srcs: ["in1"],
239 out: ["out"],
240 cmd: "cat $(location in1) > $(out)",
241 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800242 expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross08f15ab2018-10-04 23:29:14 -0700243 },
244 {
245 name: "location in1 fg",
246 prop: `
247 srcs: [":1in"],
248 out: ["out"],
249 cmd: "cat $(location :1in) > $(out)",
250 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800251 expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross08f15ab2018-10-04 23:29:14 -0700252 },
253 {
254 name: "location ins",
255 prop: `
256 srcs: ["in1", "in2"],
257 out: ["out"],
258 cmd: "cat $(location in1) > $(out)",
259 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800260 expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross08f15ab2018-10-04 23:29:14 -0700261 },
262 {
263 name: "location ins fg",
264 prop: `
265 srcs: [":ins"],
266 out: ["out"],
267 cmd: "cat $(locations :ins) > $(out)",
268 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800269 expect: "cat in1 in2 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross08f15ab2018-10-04 23:29:14 -0700270 },
271 {
Colin Cross2a076922018-10-04 23:28:25 -0700272 name: "outs",
273 prop: `
274 out: ["out", "out2"],
275 cmd: "echo foo > $(out)",
276 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800277 expect: "echo foo > __SBOX_SANDBOX_DIR__/out/out __SBOX_SANDBOX_DIR__/out/out2",
Colin Cross2a076922018-10-04 23:28:25 -0700278 },
279 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700280 name: "location out",
281 prop: `
282 out: ["out", "out2"],
283 cmd: "echo foo > $(location out2)",
284 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800285 expect: "echo foo > __SBOX_SANDBOX_DIR__/out/out2",
Colin Cross08f15ab2018-10-04 23:29:14 -0700286 },
287 {
Colin Cross2a076922018-10-04 23:28:25 -0700288 name: "depfile",
289 prop: `
290 out: ["out"],
291 depfile: true,
292 cmd: "echo foo > $(out) && touch $(depfile)",
293 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800294 expect: "echo foo > __SBOX_SANDBOX_DIR__/out/out && touch __SBOX_DEPFILE__",
Colin Cross2a076922018-10-04 23:28:25 -0700295 },
296 {
297 name: "gendir",
298 prop: `
299 out: ["out"],
300 cmd: "echo foo > $(genDir)/foo && cp $(genDir)/foo $(out)",
301 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800302 expect: "echo foo > __SBOX_SANDBOX_DIR__/out/foo && cp __SBOX_SANDBOX_DIR__/out/foo __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700303 },
Colin Cross70c47412021-03-12 17:48:14 -0800304 {
305 name: "$",
306 prop: `
307 out: ["out"],
308 cmd: "echo $$ > $(out)",
309 `,
310 expect: "echo $ > __SBOX_SANDBOX_DIR__/out/out",
311 },
Colin Cross2a076922018-10-04 23:28:25 -0700312
313 {
314 name: "error empty location",
315 prop: `
316 out: ["out"],
317 cmd: "$(location) > $(out)",
318 `,
319 err: "at least one `tools` or `tool_files` is required if $(location) is used",
320 },
321 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700322 name: "error empty location no files",
323 prop: `
324 tool_files: [":empty"],
325 out: ["out"],
326 cmd: "$(location) > $(out)",
327 `,
328 err: `default label ":empty" has no files`,
329 },
330 {
331 name: "error empty location multiple files",
332 prop: `
333 tool_files: [":tool_files"],
334 out: ["out"],
335 cmd: "$(location) > $(out)",
336 `,
337 err: `default label ":tool_files" has multiple files`,
338 },
339 {
Colin Cross2a076922018-10-04 23:28:25 -0700340 name: "error location",
341 prop: `
342 out: ["out"],
343 cmd: "echo foo > $(location missing)",
344 `,
Anton Hanssonbebf5262022-02-23 11:42:38 +0000345 err: `unknown location label "missing" is not in srcs, out, tools or tool_files.`,
Colin Cross2a076922018-10-04 23:28:25 -0700346 },
347 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700348 name: "error locations",
349 prop: `
350 out: ["out"],
351 cmd: "echo foo > $(locations missing)",
352 `,
Anton Hanssonbebf5262022-02-23 11:42:38 +0000353 err: `unknown locations label "missing" is not in srcs, out, tools or tool_files`,
Colin Cross08f15ab2018-10-04 23:29:14 -0700354 },
355 {
356 name: "error location no files",
357 prop: `
358 out: ["out"],
359 srcs: [":empty"],
360 cmd: "echo $(location :empty) > $(out)",
361 `,
362 err: `label ":empty" has no files`,
363 },
364 {
365 name: "error locations no files",
366 prop: `
367 out: ["out"],
368 srcs: [":empty"],
369 cmd: "echo $(locations :empty) > $(out)",
370 `,
371 err: `label ":empty" has no files`,
372 },
373 {
374 name: "error location multiple files",
375 prop: `
376 out: ["out"],
377 srcs: [":ins"],
378 cmd: "echo $(location :ins) > $(out)",
379 `,
380 err: `label ":ins" has multiple files`,
381 },
382 {
Colin Cross2a076922018-10-04 23:28:25 -0700383 name: "error variable",
384 prop: `
385 out: ["out"],
386 srcs: ["in1"],
387 cmd: "echo $(foo) > $(out)",
388 `,
389 err: `unknown variable '$(foo)'`,
390 },
391 {
392 name: "error depfile",
393 prop: `
394 out: ["out"],
395 cmd: "echo foo > $(out) && touch $(depfile)",
396 `,
397 err: "$(depfile) used without depfile property",
398 },
399 {
400 name: "error no depfile",
401 prop: `
402 out: ["out"],
403 depfile: true,
404 cmd: "echo foo > $(out)",
405 `,
406 err: "specified depfile=true but did not include a reference to '${depfile}' in cmd",
407 },
408 {
409 name: "error no out",
410 prop: `
411 cmd: "echo foo > $(out)",
412 `,
413 err: "must have at least one output file",
414 },
Colin Crossba71a3f2019-03-18 12:12:48 -0700415 {
416 name: "srcs allow missing dependencies",
417 prop: `
418 srcs: [":missing"],
419 out: ["out"],
420 cmd: "cat $(location :missing) > $(out)",
421 `,
422
423 allowMissingDependencies: true,
424
Jihoon Kangc170af42022-08-20 05:26:38 +0000425 expect: "cat '***missing srcs :missing***' > __SBOX_SANDBOX_DIR__/out/out",
Colin Crossba71a3f2019-03-18 12:12:48 -0700426 },
427 {
428 name: "tool allow missing dependencies",
429 prop: `
430 tools: [":missing"],
431 out: ["out"],
432 cmd: "$(location :missing) > $(out)",
433 `,
434
435 allowMissingDependencies: true,
436
Jihoon Kangc170af42022-08-20 05:26:38 +0000437 expect: "'***missing tool :missing***' > __SBOX_SANDBOX_DIR__/out/out",
Colin Crossba71a3f2019-03-18 12:12:48 -0700438 },
Colin Cross2a076922018-10-04 23:28:25 -0700439 }
440
441 for _, test := range testcases {
442 t.Run(test.name, func(t *testing.T) {
Colin Cross2a076922018-10-04 23:28:25 -0700443 bp := "genrule {\n"
444 bp += "name: \"gen\",\n"
445 bp += test.prop
446 bp += "}\n"
447
Paul Duffin672cb9f2021-03-03 02:30:37 +0000448 var expectedErrors []string
449 if test.err != "" {
450 expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
Colin Cross2a076922018-10-04 23:28:25 -0700451 }
Paul Duffin672cb9f2021-03-03 02:30:37 +0000452
Paul Duffin89648f92021-03-20 00:36:55 +0000453 result := android.GroupFixturePreparers(
454 prepareForGenRuleTest,
Paul Duffin672cb9f2021-03-03 02:30:37 +0000455 android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
456 variables.Allow_missing_dependencies = proptools.BoolPtr(test.allowMissingDependencies)
457 }),
458 android.FixtureModifyContext(func(ctx *android.TestContext) {
459 ctx.SetAllowMissingDependencies(test.allowMissingDependencies)
460 }),
461 ).
462 ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
463 RunTestWithBp(t, testGenruleBp()+bp)
464
465 if expectedErrors != nil {
Colin Cross2a076922018-10-04 23:28:25 -0700466 return
467 }
468
Paul Duffin672cb9f2021-03-03 02:30:37 +0000469 gen := result.Module("gen", "").(*Module)
Paul Duffine84b1332021-03-12 11:59:43 +0000470 android.AssertStringEquals(t, "raw commands", test.expect, gen.rawCommands[0])
Colin Cross2a076922018-10-04 23:28:25 -0700471 })
472 }
Colin Cross1a527682019-09-23 15:55:30 -0700473}
474
Bill Peckhamc087be12020-02-13 15:55:10 -0800475func TestGenruleHashInputs(t *testing.T) {
476
477 // The basic idea here is to verify that the sbox command (which is
478 // in the Command field of the generate rule) contains a hash of the
479 // inputs, but only if $(in) is not referenced in the genrule cmd
480 // property.
481
482 // By including a hash of the inputs, we cause the rule to re-run if
483 // the list of inputs changes (because the sbox command changes).
484
485 // However, if the genrule cmd property already contains $(in), then
486 // the dependency is already expressed, so we don't need to include the
487 // hash in that case.
488
489 bp := `
490 genrule {
491 name: "hash0",
492 srcs: ["in1.txt", "in2.txt"],
493 out: ["out"],
494 cmd: "echo foo > $(out)",
495 }
496 genrule {
497 name: "hash1",
498 srcs: ["*.txt"],
499 out: ["out"],
500 cmd: "echo bar > $(out)",
501 }
502 genrule {
503 name: "hash2",
504 srcs: ["*.txt"],
505 out: ["out"],
506 cmd: "echo $(in) > $(out)",
507 }
508 `
509 testcases := []struct {
510 name string
511 expectedHash string
512 }{
513 {
514 name: "hash0",
Colin Cross3d680512020-11-13 16:23:53 -0800515 // sha256 value obtained from: echo -en 'in1.txt\nin2.txt' | sha256sum
516 expectedHash: "18da75b9b1cc74b09e365b4ca2e321b5d618f438cc632b387ad9dc2ab4b20e9d",
Bill Peckhamc087be12020-02-13 15:55:10 -0800517 },
518 {
519 name: "hash1",
Colin Cross3d680512020-11-13 16:23:53 -0800520 // sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
521 expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
Bill Peckhamc087be12020-02-13 15:55:10 -0800522 },
523 {
524 name: "hash2",
Colin Cross3d680512020-11-13 16:23:53 -0800525 // sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
526 expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
Bill Peckhamc087be12020-02-13 15:55:10 -0800527 },
528 }
529
Paul Duffin89648f92021-03-20 00:36:55 +0000530 result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
Bill Peckhamc087be12020-02-13 15:55:10 -0800531
532 for _, test := range testcases {
533 t.Run(test.name, func(t *testing.T) {
Paul Duffine84b1332021-03-12 11:59:43 +0000534 gen := result.ModuleForTests(test.name, "")
Colin Crosse16ce362020-11-12 08:29:30 -0800535 manifest := android.RuleBuilderSboxProtoForTests(t, gen.Output("genrule.sbox.textproto"))
536 hash := manifest.Commands[0].GetInputHash()
Bill Peckhamc087be12020-02-13 15:55:10 -0800537
Paul Duffine84b1332021-03-12 11:59:43 +0000538 android.AssertStringEquals(t, "hash", test.expectedHash, hash)
Bill Peckhamc087be12020-02-13 15:55:10 -0800539 })
540 }
541}
542
Colin Cross1a527682019-09-23 15:55:30 -0700543func TestGenSrcs(t *testing.T) {
544 testcases := []struct {
545 name string
546 prop string
547
548 allowMissingDependencies bool
549
550 err string
551 cmds []string
552 deps []string
553 files []string
554 }{
555 {
556 name: "gensrcs",
557 prop: `
558 tools: ["tool"],
559 srcs: ["in1.txt", "in2.txt"],
560 cmd: "$(location) $(in) > $(out)",
561 `,
562 cmds: []string{
Colin Crossba9e4032020-11-24 16:32:22 -0800563 "bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in1.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in2.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
Colin Cross1a527682019-09-23 15:55:30 -0700564 },
Paul Duffine66946b2021-03-16 12:38:33 +0000565 deps: []string{
566 "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
567 "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
568 },
569 files: []string{
570 "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
571 "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
572 },
Colin Cross1a527682019-09-23 15:55:30 -0700573 },
574 {
575 name: "shards",
576 prop: `
577 tools: ["tool"],
578 srcs: ["in1.txt", "in2.txt", "in3.txt"],
579 cmd: "$(location) $(in) > $(out)",
580 shard_size: 2,
581 `,
582 cmds: []string{
Colin Crossba9e4032020-11-24 16:32:22 -0800583 "bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in1.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in2.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
584 "bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in3.txt > __SBOX_SANDBOX_DIR__/out/in3.h'",
Colin Cross1a527682019-09-23 15:55:30 -0700585 },
Paul Duffine66946b2021-03-16 12:38:33 +0000586 deps: []string{
587 "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
588 "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
589 "out/soong/.intermediates/gen/gen/gensrcs/in3.h",
590 },
591 files: []string{
592 "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
593 "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
594 "out/soong/.intermediates/gen/gen/gensrcs/in3.h",
595 },
Colin Cross1a527682019-09-23 15:55:30 -0700596 },
597 }
598
599 for _, test := range testcases {
600 t.Run(test.name, func(t *testing.T) {
Colin Cross1a527682019-09-23 15:55:30 -0700601 bp := "gensrcs {\n"
602 bp += `name: "gen",` + "\n"
603 bp += `output_extension: "h",` + "\n"
604 bp += test.prop
605 bp += "}\n"
606
Paul Duffin672cb9f2021-03-03 02:30:37 +0000607 var expectedErrors []string
608 if test.err != "" {
609 expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
Colin Cross1a527682019-09-23 15:55:30 -0700610 }
Paul Duffin672cb9f2021-03-03 02:30:37 +0000611
Paul Duffin89648f92021-03-20 00:36:55 +0000612 result := prepareForGenRuleTest.
Paul Duffin672cb9f2021-03-03 02:30:37 +0000613 ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
614 RunTestWithBp(t, testGenruleBp()+bp)
615
616 if expectedErrors != nil {
Colin Cross1a527682019-09-23 15:55:30 -0700617 return
618 }
619
Paul Duffin672cb9f2021-03-03 02:30:37 +0000620 gen := result.Module("gen", "").(*Module)
Paul Duffine84b1332021-03-12 11:59:43 +0000621 android.AssertDeepEquals(t, "cmd", test.cmds, gen.rawCommands)
Colin Cross1a527682019-09-23 15:55:30 -0700622
Paul Duffine66946b2021-03-16 12:38:33 +0000623 android.AssertPathsRelativeToTopEquals(t, "deps", test.deps, gen.outputDeps)
Colin Cross1a527682019-09-23 15:55:30 -0700624
Paul Duffine66946b2021-03-16 12:38:33 +0000625 android.AssertPathsRelativeToTopEquals(t, "files", test.files, gen.outputFiles)
Colin Cross1a527682019-09-23 15:55:30 -0700626 })
627 }
Colin Cross2a076922018-10-04 23:28:25 -0700628}
629
Vinh Tran140d5882022-06-10 14:23:27 -0400630func TestGensrcsBuildBrokenDepfile(t *testing.T) {
631 tests := []struct {
632 name string
633 prop string
634 BuildBrokenDepfile *bool
635 err string
636 }{
637 {
638 name: `error when BuildBrokenDepfile is set to false`,
639 prop: `
640 depfile: true,
641 cmd: "cat $(in) > $(out) && cat $(depfile)",
642 `,
643 BuildBrokenDepfile: proptools.BoolPtr(false),
644 err: "depfile: Deprecated to ensure the module type is convertible to Bazel",
645 },
646 {
647 name: `error when BuildBrokenDepfile is not set`,
648 prop: `
649 depfile: true,
650 cmd: "cat $(in) > $(out) && cat $(depfile)",
651 `,
652 err: "depfile: Deprecated to ensure the module type is convertible to Bazel.",
653 },
654 {
655 name: `no error when BuildBrokenDepfile is explicitly set to true`,
656 prop: `
657 depfile: true,
658 cmd: "cat $(in) > $(out) && cat $(depfile)",
659 `,
660 BuildBrokenDepfile: proptools.BoolPtr(true),
661 },
662 {
663 name: `no error if depfile is not set`,
664 prop: `
665 cmd: "cat $(in) > $(out)",
666 `,
667 },
668 }
669 for _, test := range tests {
670 t.Run(test.name, func(t *testing.T) {
671 bp := fmt.Sprintf(`
672 gensrcs {
673 name: "foo",
674 srcs: ["data.txt"],
675 %s
676 }`, test.prop)
677
678 var expectedErrors []string
679 if test.err != "" {
680 expectedErrors = append(expectedErrors, test.err)
681 }
682 android.GroupFixturePreparers(
683 prepareForGenRuleTest,
684 android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
685 if test.BuildBrokenDepfile != nil {
686 variables.BuildBrokenDepfile = test.BuildBrokenDepfile
687 }
688 }),
689 ).
690 ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
691 RunTestWithBp(t, bp)
692 })
693
694 }
695}
696
Jaewoong Jung98716bd2018-12-10 08:13:18 -0800697func TestGenruleDefaults(t *testing.T) {
Jaewoong Jung98716bd2018-12-10 08:13:18 -0800698 bp := `
699 genrule_defaults {
700 name: "gen_defaults1",
701 cmd: "cp $(in) $(out)",
702 }
703
704 genrule_defaults {
705 name: "gen_defaults2",
706 srcs: ["in1"],
707 }
708
709 genrule {
710 name: "gen",
711 out: ["out"],
712 defaults: ["gen_defaults1", "gen_defaults2"],
713 }
714 `
Paul Duffin672cb9f2021-03-03 02:30:37 +0000715
Paul Duffin89648f92021-03-20 00:36:55 +0000716 result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
Paul Duffin672cb9f2021-03-03 02:30:37 +0000717
718 gen := result.Module("gen", "").(*Module)
Jaewoong Jung98716bd2018-12-10 08:13:18 -0800719
Colin Crosse16ce362020-11-12 08:29:30 -0800720 expectedCmd := "cp in1 __SBOX_SANDBOX_DIR__/out/out"
Paul Duffine84b1332021-03-12 11:59:43 +0000721 android.AssertStringEquals(t, "cmd", expectedCmd, gen.rawCommands[0])
Jaewoong Jung98716bd2018-12-10 08:13:18 -0800722
723 expectedSrcs := []string{"in1"}
Paul Duffine84b1332021-03-12 11:59:43 +0000724 android.AssertDeepEquals(t, "srcs", expectedSrcs, gen.properties.Srcs)
Jaewoong Jung98716bd2018-12-10 08:13:18 -0800725}
726
Colin Crossfa65cee2021-03-22 17:05:59 -0700727func TestGenruleAllowMissingDependencies(t *testing.T) {
728 bp := `
729 output {
730 name: "disabled",
731 enabled: false,
732 }
733
734 genrule {
735 name: "gen",
736 srcs: [
737 ":disabled",
738 ],
739 out: ["out"],
740 cmd: "cat $(in) > $(out)",
741 }
742 `
Paul Duffin79abe572021-03-29 02:16:14 +0100743 result := android.GroupFixturePreparers(
744 prepareForGenRuleTest,
Colin Crossfa65cee2021-03-22 17:05:59 -0700745 android.FixtureModifyConfigAndContext(
746 func(config android.Config, ctx *android.TestContext) {
747 config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
748 ctx.SetAllowMissingDependencies(true)
749 })).RunTestWithBp(t, bp)
750
751 gen := result.ModuleForTests("gen", "").Output("out")
752 if gen.Rule != android.ErrorRule {
753 t.Errorf("Expected missing dependency error rule for gen, got %q", gen.Rule.String())
754 }
755}
756
Jooyung Han8c7e3ed2021-06-28 17:35:58 +0900757func TestGenruleOutputFiles(t *testing.T) {
758 bp := `
759 genrule {
760 name: "gen",
761 out: ["foo", "sub/bar"],
762 cmd: "echo foo > $(location foo) && echo bar > $(location sub/bar)",
763 }
764 use_source {
765 name: "gen_foo",
766 srcs: [":gen{foo}"],
767 }
768 use_source {
769 name: "gen_bar",
770 srcs: [":gen{sub/bar}"],
771 }
772 use_source {
773 name: "gen_all",
774 srcs: [":gen"],
775 }
776 `
777
778 result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
779 android.AssertPathsRelativeToTopEquals(t,
780 "genrule.tag with output",
781 []string{"out/soong/.intermediates/gen/gen/foo"},
782 result.ModuleForTests("gen_foo", "").Module().(*useSource).srcs)
783 android.AssertPathsRelativeToTopEquals(t,
784 "genrule.tag with output in subdir",
785 []string{"out/soong/.intermediates/gen/gen/sub/bar"},
786 result.ModuleForTests("gen_bar", "").Module().(*useSource).srcs)
787 android.AssertPathsRelativeToTopEquals(t,
788 "genrule.tag with all",
789 []string{"out/soong/.intermediates/gen/gen/foo", "out/soong/.intermediates/gen/gen/sub/bar"},
790 result.ModuleForTests("gen_all", "").Module().(*useSource).srcs)
791}
792
Martin Stjernholmdbd814d2022-01-12 23:18:30 +0000793func TestPrebuiltTool(t *testing.T) {
794 testcases := []struct {
795 name string
796 bp string
797 expectedToolName string
798 }{
799 {
800 name: "source only",
801 bp: `
802 tool { name: "tool" }
803 `,
804 expectedToolName: "bin/tool",
805 },
806 {
807 name: "prebuilt only",
808 bp: `
809 prebuilt_tool { name: "tool" }
810 `,
811 expectedToolName: "prebuilt_bin/tool",
812 },
813 {
814 name: "source preferred",
815 bp: `
816 tool { name: "tool" }
817 prebuilt_tool { name: "tool" }
818 `,
819 expectedToolName: "bin/tool",
820 },
821 {
822 name: "prebuilt preferred",
823 bp: `
824 tool { name: "tool" }
825 prebuilt_tool { name: "tool", prefer: true }
826 `,
827 expectedToolName: "prebuilt_bin/prebuilt_tool",
828 },
829 {
830 name: "source disabled",
831 bp: `
832 tool { name: "tool", enabled: false }
833 prebuilt_tool { name: "tool" }
834 `,
835 expectedToolName: "prebuilt_bin/prebuilt_tool",
836 },
837 }
838
839 for _, test := range testcases {
840 t.Run(test.name, func(t *testing.T) {
841 result := prepareForGenRuleTest.RunTestWithBp(t, test.bp+`
842 genrule {
843 name: "gen",
844 tools: ["tool"],
845 out: ["foo"],
846 cmd: "$(location tool)",
847 }
848 `)
849 gen := result.Module("gen", "").(*Module)
850 expectedCmd := "__SBOX_SANDBOX_DIR__/tools/out/" + test.expectedToolName
851 android.AssertStringEquals(t, "command", expectedCmd, gen.rawCommands[0])
852 })
853 }
854}
855
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400856func TestGenruleWithBazel(t *testing.T) {
857 bp := `
858 genrule {
859 name: "foo",
860 out: ["one.txt", "two.txt"],
Chris Parsonsaa8be052020-10-14 16:22:37 -0400861 bazel_module: { label: "//foo/bar:bar" },
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400862 }
863 `
864
Paul Duffin89648f92021-03-20 00:36:55 +0000865 result := android.GroupFixturePreparers(
866 prepareForGenRuleTest, android.FixtureModifyConfig(func(config android.Config) {
867 config.BazelContext = android.MockBazelContext{
Liz Kammera92e8442021-04-07 20:25:21 -0400868 OutputBaseDir: "outputbase",
869 LabelToOutputFiles: map[string][]string{
Paul Duffin89648f92021-03-20 00:36:55 +0000870 "//foo/bar:bar": []string{"bazelone.txt", "bazeltwo.txt"}}}
871 })).RunTestWithBp(t, testGenruleBp()+bp)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400872
Paul Duffin672cb9f2021-03-03 02:30:37 +0000873 gen := result.Module("foo", "").(*Module)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400874
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500875 expectedOutputFiles := []string{"outputbase/execroot/__main__/bazelone.txt",
876 "outputbase/execroot/__main__/bazeltwo.txt"}
Paul Duffine84b1332021-03-12 11:59:43 +0000877 android.AssertDeepEquals(t, "output files", expectedOutputFiles, gen.outputFiles.Strings())
878 android.AssertDeepEquals(t, "output deps", expectedOutputFiles, gen.outputDeps.Strings())
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400879}
880
Jihoon Kangc170af42022-08-20 05:26:38 +0000881func TestGenruleWithGlobPaths(t *testing.T) {
882 testcases := []struct {
883 name string
884 bp string
885 additionalFiles android.MockFS
886 expectedCmd string
887 }{
888 {
889 name: "single file in directory with $ sign",
890 bp: `
891 genrule {
892 name: "gen",
893 srcs: ["inn*.txt"],
894 out: ["out.txt"],
895 cmd: "cp $(in) $(out)",
896 }
897 `,
898 additionalFiles: android.MockFS{"inn$1.txt": nil},
899 expectedCmd: "cp 'inn$1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
900 },
901 {
902 name: "multiple file in directory with $ sign",
903 bp: `
904 genrule {
905 name: "gen",
906 srcs: ["inn*.txt"],
907 out: ["."],
908 cmd: "cp $(in) $(out)",
909 }
910 `,
911 additionalFiles: android.MockFS{"inn$1.txt": nil, "inn$2.txt": nil},
912 expectedCmd: "cp 'inn$1.txt' 'inn$2.txt' __SBOX_SANDBOX_DIR__/out",
913 },
914 {
915 name: "file in directory with other shell unsafe character",
916 bp: `
917 genrule {
918 name: "gen",
919 srcs: ["inn*.txt"],
920 out: ["out.txt"],
921 cmd: "cp $(in) $(out)",
922 }
923 `,
924 additionalFiles: android.MockFS{"inn@1.txt": nil},
925 expectedCmd: "cp 'inn@1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
926 },
927 {
928 name: "glob location param with filepath containing $",
929 bp: `
930 genrule {
931 name: "gen",
932 srcs: ["**/inn*"],
933 out: ["."],
934 cmd: "cp $(in) $(location **/inn*)",
935 }
936 `,
937 additionalFiles: android.MockFS{"a/inn$1.txt": nil},
938 expectedCmd: "cp 'a/inn$1.txt' 'a/inn$1.txt'",
939 },
940 {
941 name: "glob locations param with filepath containing $",
942 bp: `
943 genrule {
944 name: "gen",
945 tool_files: ["**/inn*"],
946 out: ["out.txt"],
947 cmd: "cp $(locations **/inn*) $(out)",
948 }
949 `,
950 additionalFiles: android.MockFS{"a/inn$1.txt": nil},
951 expectedCmd: "cp '__SBOX_SANDBOX_DIR__/tools/src/a/inn$1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
952 },
953 }
954
955 for _, test := range testcases {
956 t.Run(test.name, func(t *testing.T) {
957 result := android.GroupFixturePreparers(
958 prepareForGenRuleTest,
959 android.FixtureMergeMockFs(test.additionalFiles),
960 ).RunTestWithBp(t, test.bp)
961 gen := result.Module("gen", "").(*Module)
962 android.AssertStringEquals(t, "command", test.expectedCmd, gen.rawCommands[0])
963 })
964 }
965}
966
Colin Cross2a076922018-10-04 23:28:25 -0700967type testTool struct {
968 android.ModuleBase
969 outputFile android.Path
970}
971
972func toolFactory() android.Module {
973 module := &testTool{}
974 android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
975 return module
976}
977
Colin Cross2a076922018-10-04 23:28:25 -0700978func (t *testTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
Colin Crossba9e4032020-11-24 16:32:22 -0800979 t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
Colin Cross2a076922018-10-04 23:28:25 -0700980}
981
982func (t *testTool) HostToolPath() android.OptionalPath {
983 return android.OptionalPathForPath(t.outputFile)
984}
985
Martin Stjernholmdbd814d2022-01-12 23:18:30 +0000986type prebuiltTestTool struct {
987 android.ModuleBase
988 prebuilt android.Prebuilt
989 testTool
990}
991
992func (p *prebuiltTestTool) Name() string {
993 return p.prebuilt.Name(p.ModuleBase.Name())
994}
995
996func (p *prebuiltTestTool) Prebuilt() *android.Prebuilt {
997 return &p.prebuilt
998}
999
1000func (t *prebuiltTestTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
1001 t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "prebuilt_bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
1002}
1003
1004func prebuiltToolFactory() android.Module {
1005 module := &prebuiltTestTool{}
1006 android.InitPrebuiltModuleWithoutSrcs(module)
1007 android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
1008 return module
1009}
1010
Colin Crossfe17f6f2019-03-28 19:30:56 -07001011var _ android.HostToolProvider = (*testTool)(nil)
Martin Stjernholmdbd814d2022-01-12 23:18:30 +00001012var _ android.HostToolProvider = (*prebuiltTestTool)(nil)
Colin Crossfa65cee2021-03-22 17:05:59 -07001013
1014type testOutputProducer struct {
1015 android.ModuleBase
1016 outputFile android.Path
1017}
1018
1019func outputProducerFactory() android.Module {
1020 module := &testOutputProducer{}
1021 android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
1022 return module
1023}
1024
1025func (t *testOutputProducer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
1026 t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
1027}
1028
1029func (t *testOutputProducer) OutputFiles(tag string) (android.Paths, error) {
1030 return android.Paths{t.outputFile}, nil
1031}
1032
1033var _ android.OutputFileProducer = (*testOutputProducer)(nil)
Jooyung Han8c7e3ed2021-06-28 17:35:58 +09001034
1035type useSource struct {
1036 android.ModuleBase
1037 props struct {
1038 Srcs []string `android:"path"`
1039 }
1040 srcs android.Paths
1041}
1042
1043func (s *useSource) GenerateAndroidBuildActions(ctx android.ModuleContext) {
1044 s.srcs = android.PathsForModuleSrc(ctx, s.props.Srcs)
1045}
1046
1047func useSourceFactory() android.Module {
1048 module := &useSource{}
1049 module.AddProperties(&module.props)
1050 android.InitAndroidModule(module)
1051 return module
1052}