blob: f190750d13df4503c6d0d376cd03119a1321edbb [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"
Liz Kammer796921d2023-07-11 08:21:41 -040021 "strconv"
Cole Faust78f3c3a2024-08-15 17:19:34 -070022 "strings"
Colin Cross2a076922018-10-04 23:28:25 -070023 "testing"
24
25 "android/soong/android"
Colin Crossba71a3f2019-03-18 12:12:48 -070026
Cole Faustf966e382024-10-08 16:17:56 -070027 "github.com/google/blueprint"
Colin Crossba71a3f2019-03-18 12:12:48 -070028 "github.com/google/blueprint/proptools"
Colin Cross2a076922018-10-04 23:28:25 -070029)
30
Colin Cross2a076922018-10-04 23:28:25 -070031func TestMain(m *testing.M) {
Paul Duffine66946b2021-03-16 12:38:33 +000032 os.Exit(m.Run())
Colin Cross2a076922018-10-04 23:28:25 -070033}
34
Paul Duffin89648f92021-03-20 00:36:55 +000035var prepareForGenRuleTest = android.GroupFixturePreparers(
Paul Duffin672cb9f2021-03-03 02:30:37 +000036 android.PrepareForTestWithArchMutator,
37 android.PrepareForTestWithDefaults,
Paul Duffin672cb9f2021-03-03 02:30:37 +000038 android.PrepareForTestWithFilegroup,
39 PrepareForTestWithGenRuleBuildComponents,
40 android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
Martin Stjernholmdbd814d2022-01-12 23:18:30 +000041 android.RegisterPrebuiltMutators(ctx)
Paul Duffin672cb9f2021-03-03 02:30:37 +000042 ctx.RegisterModuleType("tool", toolFactory)
Martin Stjernholmdbd814d2022-01-12 23:18:30 +000043 ctx.RegisterModuleType("prebuilt_tool", prebuiltToolFactory)
Colin Crossfa65cee2021-03-22 17:05:59 -070044 ctx.RegisterModuleType("output", outputProducerFactory)
Jooyung Han8c7e3ed2021-06-28 17:35:58 +090045 ctx.RegisterModuleType("use_source", useSourceFactory)
Paul Duffin672cb9f2021-03-03 02:30:37 +000046 }),
47 android.FixtureMergeMockFs(android.MockFS{
48 "tool": nil,
49 "tool_file1": nil,
50 "tool_file2": nil,
51 "in1": nil,
52 "in2": nil,
53 "in1.txt": nil,
54 "in2.txt": nil,
55 "in3.txt": nil,
56 }),
57)
Martin Stjernholm710ec3a2020-01-16 15:12:04 +000058
Paul Duffin672cb9f2021-03-03 02:30:37 +000059func testGenruleBp() string {
60 return `
Colin Cross2a076922018-10-04 23:28:25 -070061 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 `
Colin Cross2a076922018-10-04 23:28:25 -070099}
100
101func TestGenruleCmd(t *testing.T) {
102 testcases := []struct {
Yu Liu6a7940c2023-05-09 17:12:22 -0700103 name string
104 moduleName string
105 prop string
Colin Cross2a076922018-10-04 23:28:25 -0700106
Colin Crossba71a3f2019-03-18 12:12:48 -0700107 allowMissingDependencies bool
108
Colin Cross2a076922018-10-04 23:28:25 -0700109 err string
110 expect string
111 }{
112 {
113 name: "empty location tool",
114 prop: `
115 tools: ["tool"],
116 out: ["out"],
117 cmd: "$(location) > $(out)",
118 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800119 expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700120 },
121 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700122 name: "empty location tool2",
123 prop: `
124 tools: [":tool"],
125 out: ["out"],
126 cmd: "$(location) > $(out)",
127 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800128 expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross08f15ab2018-10-04 23:29:14 -0700129 },
130 {
Colin Cross2a076922018-10-04 23:28:25 -0700131 name: "empty location tool file",
132 prop: `
133 tool_files: ["tool_file1"],
134 out: ["out"],
135 cmd: "$(location) > $(out)",
136 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800137 expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700138 },
139 {
140 name: "empty location tool file fg",
141 prop: `
142 tool_files: [":1tool_file"],
143 out: ["out"],
144 cmd: "$(location) > $(out)",
145 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800146 expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700147 },
148 {
149 name: "empty location tool and tool file",
150 prop: `
151 tools: ["tool"],
152 tool_files: ["tool_file1"],
153 out: ["out"],
154 cmd: "$(location) > $(out)",
155 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800156 expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700157 },
158 {
159 name: "tool",
160 prop: `
161 tools: ["tool"],
162 out: ["out"],
163 cmd: "$(location tool) > $(out)",
164 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800165 expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700166 },
167 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700168 name: "tool2",
169 prop: `
170 tools: [":tool"],
171 out: ["out"],
172 cmd: "$(location :tool) > $(out)",
173 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800174 expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross08f15ab2018-10-04 23:29:14 -0700175 },
176 {
Colin Cross2a076922018-10-04 23:28:25 -0700177 name: "tool file",
178 prop: `
179 tool_files: ["tool_file1"],
180 out: ["out"],
181 cmd: "$(location tool_file1) > $(out)",
182 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800183 expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700184 },
185 {
186 name: "tool file fg",
187 prop: `
188 tool_files: [":1tool_file"],
189 out: ["out"],
Colin Cross08f15ab2018-10-04 23:29:14 -0700190 cmd: "$(location :1tool_file) > $(out)",
Colin Cross2a076922018-10-04 23:28:25 -0700191 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800192 expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700193 },
194 {
195 name: "tool files",
196 prop: `
197 tool_files: [":tool_files"],
198 out: ["out"],
Colin Cross08f15ab2018-10-04 23:29:14 -0700199 cmd: "$(locations :tool_files) > $(out)",
Colin Cross2a076922018-10-04 23:28:25 -0700200 `,
Colin Crossba9e4032020-11-24 16:32:22 -0800201 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 -0700202 },
203 {
204 name: "in1",
205 prop: `
206 srcs: ["in1"],
207 out: ["out"],
208 cmd: "cat $(in) > $(out)",
209 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800210 expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700211 },
212 {
213 name: "in1 fg",
214 prop: `
215 srcs: [":1in"],
216 out: ["out"],
217 cmd: "cat $(in) > $(out)",
218 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800219 expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700220 },
221 {
222 name: "ins",
223 prop: `
224 srcs: ["in1", "in2"],
225 out: ["out"],
226 cmd: "cat $(in) > $(out)",
227 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800228 expect: "cat in1 in2 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700229 },
230 {
231 name: "ins fg",
232 prop: `
233 srcs: [":ins"],
234 out: ["out"],
235 cmd: "cat $(in) > $(out)",
236 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800237 expect: "cat in1 in2 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross2a076922018-10-04 23:28:25 -0700238 },
239 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700240 name: "location in1",
241 prop: `
242 srcs: ["in1"],
243 out: ["out"],
244 cmd: "cat $(location in1) > $(out)",
245 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800246 expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross08f15ab2018-10-04 23:29:14 -0700247 },
248 {
249 name: "location in1 fg",
250 prop: `
251 srcs: [":1in"],
252 out: ["out"],
253 cmd: "cat $(location :1in) > $(out)",
254 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800255 expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross08f15ab2018-10-04 23:29:14 -0700256 },
257 {
258 name: "location ins",
259 prop: `
260 srcs: ["in1", "in2"],
261 out: ["out"],
262 cmd: "cat $(location in1) > $(out)",
263 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800264 expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross08f15ab2018-10-04 23:29:14 -0700265 },
266 {
267 name: "location ins fg",
268 prop: `
269 srcs: [":ins"],
270 out: ["out"],
271 cmd: "cat $(locations :ins) > $(out)",
272 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800273 expect: "cat in1 in2 > __SBOX_SANDBOX_DIR__/out/out",
Colin Cross08f15ab2018-10-04 23:29:14 -0700274 },
275 {
Colin Cross2a076922018-10-04 23:28:25 -0700276 name: "outs",
277 prop: `
278 out: ["out", "out2"],
279 cmd: "echo foo > $(out)",
280 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800281 expect: "echo foo > __SBOX_SANDBOX_DIR__/out/out __SBOX_SANDBOX_DIR__/out/out2",
Colin Cross2a076922018-10-04 23:28:25 -0700282 },
283 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700284 name: "location out",
285 prop: `
286 out: ["out", "out2"],
287 cmd: "echo foo > $(location out2)",
288 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800289 expect: "echo foo > __SBOX_SANDBOX_DIR__/out/out2",
Colin Cross08f15ab2018-10-04 23:29:14 -0700290 },
291 {
Colin Cross2a076922018-10-04 23:28:25 -0700292 name: "gendir",
293 prop: `
294 out: ["out"],
295 cmd: "echo foo > $(genDir)/foo && cp $(genDir)/foo $(out)",
296 `,
Colin Crosse16ce362020-11-12 08:29:30 -0800297 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 -0700298 },
Colin Cross70c47412021-03-12 17:48:14 -0800299 {
300 name: "$",
301 prop: `
302 out: ["out"],
303 cmd: "echo $$ > $(out)",
304 `,
305 expect: "echo $ > __SBOX_SANDBOX_DIR__/out/out",
306 },
Colin Cross2a076922018-10-04 23:28:25 -0700307
308 {
309 name: "error empty location",
310 prop: `
311 out: ["out"],
312 cmd: "$(location) > $(out)",
313 `,
314 err: "at least one `tools` or `tool_files` is required if $(location) is used",
315 },
316 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700317 name: "error empty location no files",
318 prop: `
319 tool_files: [":empty"],
320 out: ["out"],
321 cmd: "$(location) > $(out)",
322 `,
323 err: `default label ":empty" has no files`,
324 },
325 {
326 name: "error empty location multiple files",
327 prop: `
328 tool_files: [":tool_files"],
329 out: ["out"],
330 cmd: "$(location) > $(out)",
331 `,
332 err: `default label ":tool_files" has multiple files`,
333 },
334 {
Colin Cross2a076922018-10-04 23:28:25 -0700335 name: "error location",
336 prop: `
337 out: ["out"],
338 cmd: "echo foo > $(location missing)",
339 `,
Anton Hanssonbebf5262022-02-23 11:42:38 +0000340 err: `unknown location label "missing" is not in srcs, out, tools or tool_files.`,
Colin Cross2a076922018-10-04 23:28:25 -0700341 },
342 {
Colin Cross08f15ab2018-10-04 23:29:14 -0700343 name: "error locations",
344 prop: `
345 out: ["out"],
346 cmd: "echo foo > $(locations missing)",
347 `,
Anton Hanssonbebf5262022-02-23 11:42:38 +0000348 err: `unknown locations label "missing" is not in srcs, out, tools or tool_files`,
Colin Cross08f15ab2018-10-04 23:29:14 -0700349 },
350 {
351 name: "error location no files",
352 prop: `
353 out: ["out"],
354 srcs: [":empty"],
355 cmd: "echo $(location :empty) > $(out)",
356 `,
357 err: `label ":empty" has no files`,
358 },
359 {
360 name: "error locations no files",
361 prop: `
362 out: ["out"],
363 srcs: [":empty"],
364 cmd: "echo $(locations :empty) > $(out)",
365 `,
366 err: `label ":empty" has no files`,
367 },
368 {
369 name: "error location multiple files",
370 prop: `
371 out: ["out"],
372 srcs: [":ins"],
373 cmd: "echo $(location :ins) > $(out)",
374 `,
375 err: `label ":ins" has multiple files`,
376 },
377 {
Colin Cross2a076922018-10-04 23:28:25 -0700378 name: "error variable",
379 prop: `
380 out: ["out"],
381 srcs: ["in1"],
382 cmd: "echo $(foo) > $(out)",
383 `,
384 err: `unknown variable '$(foo)'`,
385 },
386 {
Colin Cross2a076922018-10-04 23:28:25 -0700387 name: "error no out",
388 prop: `
389 cmd: "echo foo > $(out)",
390 `,
391 err: "must have at least one output file",
392 },
Colin Crossba71a3f2019-03-18 12:12:48 -0700393 {
394 name: "srcs allow missing dependencies",
395 prop: `
396 srcs: [":missing"],
397 out: ["out"],
398 cmd: "cat $(location :missing) > $(out)",
399 `,
400
401 allowMissingDependencies: true,
402
Jihoon Kangc170af42022-08-20 05:26:38 +0000403 expect: "cat '***missing srcs :missing***' > __SBOX_SANDBOX_DIR__/out/out",
Colin Crossba71a3f2019-03-18 12:12:48 -0700404 },
405 {
406 name: "tool allow missing dependencies",
407 prop: `
408 tools: [":missing"],
409 out: ["out"],
410 cmd: "$(location :missing) > $(out)",
411 `,
412
413 allowMissingDependencies: true,
414
Jihoon Kangc170af42022-08-20 05:26:38 +0000415 expect: "'***missing tool :missing***' > __SBOX_SANDBOX_DIR__/out/out",
Colin Crossba71a3f2019-03-18 12:12:48 -0700416 },
Colin Cross2a076922018-10-04 23:28:25 -0700417 }
418
419 for _, test := range testcases {
420 t.Run(test.name, func(t *testing.T) {
Yu Liu6a7940c2023-05-09 17:12:22 -0700421 moduleName := "gen"
422 if test.moduleName != "" {
423 moduleName = test.moduleName
424 }
425 bp := fmt.Sprintf(`
426 genrule {
427 name: "%s",
428 %s
429 }`, moduleName, test.prop)
Paul Duffin672cb9f2021-03-03 02:30:37 +0000430 var expectedErrors []string
431 if test.err != "" {
432 expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
Colin Cross2a076922018-10-04 23:28:25 -0700433 }
Paul Duffin672cb9f2021-03-03 02:30:37 +0000434
Paul Duffin89648f92021-03-20 00:36:55 +0000435 result := android.GroupFixturePreparers(
436 prepareForGenRuleTest,
Paul Duffin672cb9f2021-03-03 02:30:37 +0000437 android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
438 variables.Allow_missing_dependencies = proptools.BoolPtr(test.allowMissingDependencies)
439 }),
Yu Liu6a7940c2023-05-09 17:12:22 -0700440 android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
441 variables.GenruleSandboxing = proptools.BoolPtr(true)
442 }),
Paul Duffin672cb9f2021-03-03 02:30:37 +0000443 android.FixtureModifyContext(func(ctx *android.TestContext) {
444 ctx.SetAllowMissingDependencies(test.allowMissingDependencies)
445 }),
446 ).
447 ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
448 RunTestWithBp(t, testGenruleBp()+bp)
449
450 if expectedErrors != nil {
Colin Cross2a076922018-10-04 23:28:25 -0700451 return
452 }
453
Yu Liu6a7940c2023-05-09 17:12:22 -0700454 gen := result.Module(moduleName, "").(*Module)
Paul Duffine84b1332021-03-12 11:59:43 +0000455 android.AssertStringEquals(t, "raw commands", test.expect, gen.rawCommands[0])
Colin Cross2a076922018-10-04 23:28:25 -0700456 })
457 }
Colin Cross1a527682019-09-23 15:55:30 -0700458}
459
Bill Peckhamc087be12020-02-13 15:55:10 -0800460func TestGenruleHashInputs(t *testing.T) {
461
462 // The basic idea here is to verify that the sbox command (which is
463 // in the Command field of the generate rule) contains a hash of the
464 // inputs, but only if $(in) is not referenced in the genrule cmd
465 // property.
466
467 // By including a hash of the inputs, we cause the rule to re-run if
468 // the list of inputs changes (because the sbox command changes).
469
470 // However, if the genrule cmd property already contains $(in), then
471 // the dependency is already expressed, so we don't need to include the
472 // hash in that case.
473
474 bp := `
475 genrule {
476 name: "hash0",
477 srcs: ["in1.txt", "in2.txt"],
478 out: ["out"],
479 cmd: "echo foo > $(out)",
480 }
481 genrule {
482 name: "hash1",
483 srcs: ["*.txt"],
484 out: ["out"],
485 cmd: "echo bar > $(out)",
486 }
487 genrule {
488 name: "hash2",
489 srcs: ["*.txt"],
490 out: ["out"],
491 cmd: "echo $(in) > $(out)",
492 }
493 `
494 testcases := []struct {
495 name string
496 expectedHash string
497 }{
498 {
499 name: "hash0",
Colin Cross3d680512020-11-13 16:23:53 -0800500 // sha256 value obtained from: echo -en 'in1.txt\nin2.txt' | sha256sum
501 expectedHash: "18da75b9b1cc74b09e365b4ca2e321b5d618f438cc632b387ad9dc2ab4b20e9d",
Bill Peckhamc087be12020-02-13 15:55:10 -0800502 },
503 {
504 name: "hash1",
Colin Cross3d680512020-11-13 16:23:53 -0800505 // sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
506 expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
Bill Peckhamc087be12020-02-13 15:55:10 -0800507 },
508 {
509 name: "hash2",
Colin Cross3d680512020-11-13 16:23:53 -0800510 // sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
511 expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
Bill Peckhamc087be12020-02-13 15:55:10 -0800512 },
513 }
514
Paul Duffin89648f92021-03-20 00:36:55 +0000515 result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
Bill Peckhamc087be12020-02-13 15:55:10 -0800516
517 for _, test := range testcases {
518 t.Run(test.name, func(t *testing.T) {
Paul Duffine84b1332021-03-12 11:59:43 +0000519 gen := result.ModuleForTests(test.name, "")
Colin Crossf61d03d2023-11-02 16:56:39 -0700520 manifest := android.RuleBuilderSboxProtoForTests(t, result.TestContext, gen.Output("genrule.sbox.textproto"))
Colin Crosse16ce362020-11-12 08:29:30 -0800521 hash := manifest.Commands[0].GetInputHash()
Bill Peckhamc087be12020-02-13 15:55:10 -0800522
Paul Duffine84b1332021-03-12 11:59:43 +0000523 android.AssertStringEquals(t, "hash", test.expectedHash, hash)
Bill Peckhamc087be12020-02-13 15:55:10 -0800524 })
525 }
526}
527
Colin Cross1a527682019-09-23 15:55:30 -0700528func TestGenSrcs(t *testing.T) {
529 testcases := []struct {
530 name string
531 prop string
532
533 allowMissingDependencies bool
534
Liz Kammer796921d2023-07-11 08:21:41 -0400535 err string
536 cmds []string
537 deps []string
538 files []string
539 shards int
540 inputs []string
Colin Cross1a527682019-09-23 15:55:30 -0700541 }{
542 {
543 name: "gensrcs",
544 prop: `
545 tools: ["tool"],
546 srcs: ["in1.txt", "in2.txt"],
547 cmd: "$(location) $(in) > $(out)",
548 `,
549 cmds: []string{
Colin Crossba9e4032020-11-24 16:32:22 -0800550 "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 -0700551 },
Paul Duffine66946b2021-03-16 12:38:33 +0000552 deps: []string{
553 "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
554 "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
555 },
556 files: []string{
557 "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
558 "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
559 },
Colin Cross1a527682019-09-23 15:55:30 -0700560 },
561 {
562 name: "shards",
563 prop: `
564 tools: ["tool"],
565 srcs: ["in1.txt", "in2.txt", "in3.txt"],
566 cmd: "$(location) $(in) > $(out)",
567 shard_size: 2,
568 `,
569 cmds: []string{
Colin Crossba9e4032020-11-24 16:32:22 -0800570 "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'",
571 "bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in3.txt > __SBOX_SANDBOX_DIR__/out/in3.h'",
Colin Cross1a527682019-09-23 15:55:30 -0700572 },
Paul Duffine66946b2021-03-16 12:38:33 +0000573 deps: []string{
574 "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
575 "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
576 "out/soong/.intermediates/gen/gen/gensrcs/in3.h",
577 },
578 files: []string{
579 "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
580 "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
581 "out/soong/.intermediates/gen/gen/gensrcs/in3.h",
582 },
Colin Cross1a527682019-09-23 15:55:30 -0700583 },
Liz Kammer81fec182023-06-09 13:33:45 -0400584 {
585 name: "data",
586 prop: `
587 tools: ["tool"],
588 srcs: ["in1.txt", "in2.txt", "in3.txt"],
589 cmd: "$(location) $(in) --extra_input=$(location baz.txt) > $(out)",
590 data: ["baz.txt"],
591 shard_size: 2,
592 `,
593 cmds: []string{
594 "bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in1.txt --extra_input=baz.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in2.txt --extra_input=baz.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
595 "bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in3.txt --extra_input=baz.txt > __SBOX_SANDBOX_DIR__/out/in3.h'",
596 },
597 deps: []string{
598 "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
599 "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
600 "out/soong/.intermediates/gen/gen/gensrcs/in3.h",
601 },
602 files: []string{
603 "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
604 "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
605 "out/soong/.intermediates/gen/gen/gensrcs/in3.h",
606 },
Liz Kammer796921d2023-07-11 08:21:41 -0400607 shards: 2,
608 inputs: []string{
609 "baz.txt",
610 },
Liz Kammer81fec182023-06-09 13:33:45 -0400611 },
Colin Cross1a527682019-09-23 15:55:30 -0700612 }
613
Liz Kammer796921d2023-07-11 08:21:41 -0400614 checkInputs := func(t *testing.T, rule android.TestingBuildParams, inputs []string) {
615 t.Helper()
616 if len(inputs) == 0 {
617 return
618 }
619 inputBaseNames := map[string]bool{}
620 for _, f := range rule.Implicits {
621 inputBaseNames[f.Base()] = true
622 }
623 for _, f := range inputs {
624 if _, ok := inputBaseNames[f]; !ok {
625 t.Errorf("Expected to find input file %q for %q, but did not", f, rule.Description)
626 }
627 }
628 }
629
Colin Cross1a527682019-09-23 15:55:30 -0700630 for _, test := range testcases {
631 t.Run(test.name, func(t *testing.T) {
Colin Cross1a527682019-09-23 15:55:30 -0700632 bp := "gensrcs {\n"
633 bp += `name: "gen",` + "\n"
634 bp += `output_extension: "h",` + "\n"
635 bp += test.prop
636 bp += "}\n"
637
Paul Duffin672cb9f2021-03-03 02:30:37 +0000638 var expectedErrors []string
639 if test.err != "" {
640 expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
Colin Cross1a527682019-09-23 15:55:30 -0700641 }
Paul Duffin672cb9f2021-03-03 02:30:37 +0000642
Paul Duffin89648f92021-03-20 00:36:55 +0000643 result := prepareForGenRuleTest.
Paul Duffin672cb9f2021-03-03 02:30:37 +0000644 ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
645 RunTestWithBp(t, testGenruleBp()+bp)
646
Liz Kammer796921d2023-07-11 08:21:41 -0400647 mod := result.ModuleForTests("gen", "")
Paul Duffin672cb9f2021-03-03 02:30:37 +0000648 if expectedErrors != nil {
Colin Cross1a527682019-09-23 15:55:30 -0700649 return
650 }
651
Liz Kammer796921d2023-07-11 08:21:41 -0400652 if test.shards > 0 {
653 for i := 0; i < test.shards; i++ {
654 r := mod.Rule("generator" + strconv.Itoa(i))
655 checkInputs(t, r, test.inputs)
656 }
657 } else {
658 r := mod.Rule("generator")
659 checkInputs(t, r, test.inputs)
660 }
661
Paul Duffin672cb9f2021-03-03 02:30:37 +0000662 gen := result.Module("gen", "").(*Module)
Paul Duffine84b1332021-03-12 11:59:43 +0000663 android.AssertDeepEquals(t, "cmd", test.cmds, gen.rawCommands)
Colin Cross1a527682019-09-23 15:55:30 -0700664
Paul Duffine66946b2021-03-16 12:38:33 +0000665 android.AssertPathsRelativeToTopEquals(t, "deps", test.deps, gen.outputDeps)
Colin Cross1a527682019-09-23 15:55:30 -0700666
Paul Duffine66946b2021-03-16 12:38:33 +0000667 android.AssertPathsRelativeToTopEquals(t, "files", test.files, gen.outputFiles)
Colin Cross1a527682019-09-23 15:55:30 -0700668 })
669 }
Colin Cross2a076922018-10-04 23:28:25 -0700670}
671
Jaewoong Jung98716bd2018-12-10 08:13:18 -0800672func TestGenruleDefaults(t *testing.T) {
Jaewoong Jung98716bd2018-12-10 08:13:18 -0800673 bp := `
674 genrule_defaults {
675 name: "gen_defaults1",
676 cmd: "cp $(in) $(out)",
677 }
678
679 genrule_defaults {
680 name: "gen_defaults2",
681 srcs: ["in1"],
682 }
683
684 genrule {
685 name: "gen",
686 out: ["out"],
687 defaults: ["gen_defaults1", "gen_defaults2"],
688 }
689 `
Paul Duffin672cb9f2021-03-03 02:30:37 +0000690
Paul Duffin89648f92021-03-20 00:36:55 +0000691 result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
Paul Duffin672cb9f2021-03-03 02:30:37 +0000692
693 gen := result.Module("gen", "").(*Module)
Jaewoong Jung98716bd2018-12-10 08:13:18 -0800694
Colin Crosse16ce362020-11-12 08:29:30 -0800695 expectedCmd := "cp in1 __SBOX_SANDBOX_DIR__/out/out"
Paul Duffine84b1332021-03-12 11:59:43 +0000696 android.AssertStringEquals(t, "cmd", expectedCmd, gen.rawCommands[0])
Jaewoong Jung98716bd2018-12-10 08:13:18 -0800697
Cole Faustf966e382024-10-08 16:17:56 -0700698 srcsFileProvider, ok := android.OtherModuleProvider(result.TestContext, gen, blueprint.SrcsFileProviderKey)
699 if !ok {
700 t.Fatal("Expected genrule to have a SrcsFileProviderData, but did not")
701 }
Jaewoong Jung98716bd2018-12-10 08:13:18 -0800702 expectedSrcs := []string{"in1"}
Cole Faustf966e382024-10-08 16:17:56 -0700703 android.AssertDeepEquals(t, "srcs", expectedSrcs, srcsFileProvider.SrcPaths)
Jaewoong Jung98716bd2018-12-10 08:13:18 -0800704}
705
Colin Crossfa65cee2021-03-22 17:05:59 -0700706func TestGenruleAllowMissingDependencies(t *testing.T) {
707 bp := `
708 output {
709 name: "disabled",
710 enabled: false,
711 }
712
713 genrule {
714 name: "gen",
715 srcs: [
716 ":disabled",
717 ],
718 out: ["out"],
719 cmd: "cat $(in) > $(out)",
720 }
721 `
Paul Duffin79abe572021-03-29 02:16:14 +0100722 result := android.GroupFixturePreparers(
723 prepareForGenRuleTest,
Colin Crossfa65cee2021-03-22 17:05:59 -0700724 android.FixtureModifyConfigAndContext(
725 func(config android.Config, ctx *android.TestContext) {
726 config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
727 ctx.SetAllowMissingDependencies(true)
728 })).RunTestWithBp(t, bp)
729
730 gen := result.ModuleForTests("gen", "").Output("out")
731 if gen.Rule != android.ErrorRule {
732 t.Errorf("Expected missing dependency error rule for gen, got %q", gen.Rule.String())
733 }
734}
735
Jooyung Han8c7e3ed2021-06-28 17:35:58 +0900736func TestGenruleOutputFiles(t *testing.T) {
737 bp := `
738 genrule {
739 name: "gen",
740 out: ["foo", "sub/bar"],
741 cmd: "echo foo > $(location foo) && echo bar > $(location sub/bar)",
742 }
743 use_source {
744 name: "gen_foo",
745 srcs: [":gen{foo}"],
746 }
747 use_source {
748 name: "gen_bar",
749 srcs: [":gen{sub/bar}"],
750 }
751 use_source {
752 name: "gen_all",
753 srcs: [":gen"],
754 }
755 `
756
757 result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
758 android.AssertPathsRelativeToTopEquals(t,
759 "genrule.tag with output",
760 []string{"out/soong/.intermediates/gen/gen/foo"},
761 result.ModuleForTests("gen_foo", "").Module().(*useSource).srcs)
762 android.AssertPathsRelativeToTopEquals(t,
763 "genrule.tag with output in subdir",
764 []string{"out/soong/.intermediates/gen/gen/sub/bar"},
765 result.ModuleForTests("gen_bar", "").Module().(*useSource).srcs)
766 android.AssertPathsRelativeToTopEquals(t,
767 "genrule.tag with all",
768 []string{"out/soong/.intermediates/gen/gen/foo", "out/soong/.intermediates/gen/gen/sub/bar"},
769 result.ModuleForTests("gen_all", "").Module().(*useSource).srcs)
770}
771
Liz Kammerd38c87c2023-07-17 09:58:50 -0400772func TestGenruleInterface(t *testing.T) {
773 result := android.GroupFixturePreparers(
774 prepareForGenRuleTest,
775 android.FixtureMergeMockFs(android.MockFS{
776 "package-dir/Android.bp": []byte(`
777 genrule {
778 name: "module-name",
779 cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
780 srcs: [
781 "src/foo.proto",
782 ],
783 out: ["proto.h", "bar/proto.h"],
784 export_include_dirs: [".", "bar"],
785 }
786 `),
787 }),
788 ).RunTest(t)
789
790 exportedIncludeDirs := []string{
791 "out/soong/.intermediates/package-dir/module-name/gen/package-dir",
792 "out/soong/.intermediates/package-dir/module-name/gen",
793 "out/soong/.intermediates/package-dir/module-name/gen/package-dir/bar",
794 "out/soong/.intermediates/package-dir/module-name/gen/bar",
795 }
796 gen := result.Module("module-name", "").(*Module)
797
798 android.AssertPathsRelativeToTopEquals(
799 t,
800 "include path",
801 exportedIncludeDirs,
802 gen.GeneratedHeaderDirs(),
803 )
804 android.AssertPathsRelativeToTopEquals(
805 t,
806 "files",
807 []string{
808 "out/soong/.intermediates/package-dir/module-name/gen/proto.h",
809 "out/soong/.intermediates/package-dir/module-name/gen/bar/proto.h",
810 },
811 gen.GeneratedSourceFiles(),
812 )
813}
814
Vinh Tran370e08c2022-09-23 18:09:01 -0400815func TestGenSrcsWithNonRootAndroidBpOutputFiles(t *testing.T) {
816 result := android.GroupFixturePreparers(
817 prepareForGenRuleTest,
818 android.FixtureMergeMockFs(android.MockFS{
819 "external-protos/path/Android.bp": []byte(`
820 filegroup {
821 name: "external-protos",
822 srcs: ["baz/baz.proto", "bar.proto"],
823 }
824 `),
825 "package-dir/Android.bp": []byte(`
826 gensrcs {
827 name: "module-name",
828 cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
829 srcs: [
830 "src/foo.proto",
831 ":external-protos",
832 ],
833 output_extension: "proto.h",
834 }
835 `),
836 }),
837 ).RunTest(t)
838
839 exportedIncludeDir := "out/soong/.intermediates/package-dir/module-name/gen/gensrcs"
840 gen := result.Module("module-name", "").(*Module)
841
842 android.AssertPathsRelativeToTopEquals(
843 t,
844 "include path",
845 []string{exportedIncludeDir},
846 gen.exportedIncludeDirs,
847 )
848 android.AssertPathsRelativeToTopEquals(
849 t,
850 "files",
851 []string{
852 exportedIncludeDir + "/package-dir/src/foo.proto.h",
853 exportedIncludeDir + "/external-protos/path/baz/baz.proto.h",
854 exportedIncludeDir + "/external-protos/path/bar.proto.h",
855 },
856 gen.outputFiles,
857 )
858}
859
860func TestGenSrcsWithSrcsFromExternalPackage(t *testing.T) {
861 bp := `
862 gensrcs {
863 name: "module-name",
864 cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
865 srcs: [
866 ":external-protos",
867 ],
868 output_extension: "proto.h",
869 }
870 `
871 result := android.GroupFixturePreparers(
872 prepareForGenRuleTest,
873 android.FixtureMergeMockFs(android.MockFS{
874 "external-protos/path/Android.bp": []byte(`
875 filegroup {
876 name: "external-protos",
877 srcs: ["foo/foo.proto", "bar.proto"],
878 }
879 `),
880 }),
881 ).RunTestWithBp(t, bp)
882
883 exportedIncludeDir := "out/soong/.intermediates/module-name/gen/gensrcs"
884 gen := result.Module("module-name", "").(*Module)
885
886 android.AssertPathsRelativeToTopEquals(
887 t,
888 "include path",
889 []string{exportedIncludeDir},
890 gen.exportedIncludeDirs,
891 )
892 android.AssertPathsRelativeToTopEquals(
893 t,
894 "files",
895 []string{
896 exportedIncludeDir + "/external-protos/path/foo/foo.proto.h",
897 exportedIncludeDir + "/external-protos/path/bar.proto.h",
898 },
899 gen.outputFiles,
900 )
901}
902
yangbill6d032dd2024-04-18 03:05:49 +0000903func TestGenSrcsWithTrimExtAndOutpuExtension(t *testing.T) {
904 result := android.GroupFixturePreparers(
905 prepareForGenRuleTest,
906 android.FixtureMergeMockFs(android.MockFS{
907 "external-protos/path/Android.bp": []byte(`
908 filegroup {
909 name: "external-protos",
910 srcs: [
911 "baz.a.b.c.proto/baz.a.b.c.proto",
912 "bar.a.b.c.proto",
913 "qux.ext.a.b.c.proto",
914 ],
915 }
916 `),
917 "package-dir/Android.bp": []byte(`
918 gensrcs {
919 name: "module-name",
920 cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
921 srcs: [
922 "src/foo.a.b.c.proto",
923 ":external-protos",
924 ],
925
926 trim_extension: ".a.b.c.proto",
927 output_extension: "proto.h",
928 }
929 `),
930 }),
931 ).RunTest(t)
932
933 exportedIncludeDir := "out/soong/.intermediates/package-dir/module-name/gen/gensrcs"
934 gen := result.Module("module-name", "").(*Module)
935
936 android.AssertPathsRelativeToTopEquals(
937 t,
938 "include path",
939 []string{exportedIncludeDir},
940 gen.exportedIncludeDirs,
941 )
942 android.AssertPathsRelativeToTopEquals(
943 t,
944 "files",
945 []string{
946 exportedIncludeDir + "/package-dir/src/foo.proto.h",
947 exportedIncludeDir + "/external-protos/path/baz.a.b.c.proto/baz.proto.h",
948 exportedIncludeDir + "/external-protos/path/bar.proto.h",
949 exportedIncludeDir + "/external-protos/path/qux.ext.proto.h",
950 },
951 gen.outputFiles,
952 )
953}
954
955func TestGenSrcsWithTrimExtButNoOutpuExtension(t *testing.T) {
956 result := android.GroupFixturePreparers(
957 prepareForGenRuleTest,
958 android.FixtureMergeMockFs(android.MockFS{
959 "external-protos/path/Android.bp": []byte(`
960 filegroup {
961 name: "external-protos",
962 srcs: [
963 "baz.a.b.c.proto/baz.a.b.c.proto",
964 "bar.a.b.c.proto",
965 "qux.ext.a.b.c.proto",
966 ],
967 }
968 `),
969 "package-dir/Android.bp": []byte(`
970 gensrcs {
971 name: "module-name",
972 cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
973 srcs: [
974 "src/foo.a.b.c.proto",
975 ":external-protos",
976 ],
977
978 trim_extension: ".a.b.c.proto",
979 }
980 `),
981 }),
982 ).RunTest(t)
983
984 exportedIncludeDir := "out/soong/.intermediates/package-dir/module-name/gen/gensrcs"
985 gen := result.Module("module-name", "").(*Module)
986
987 android.AssertPathsRelativeToTopEquals(
988 t,
989 "include path",
990 []string{exportedIncludeDir},
991 gen.exportedIncludeDirs,
992 )
993 android.AssertPathsRelativeToTopEquals(
994 t,
995 "files",
996 []string{
997 exportedIncludeDir + "/package-dir/src/foo",
998 exportedIncludeDir + "/external-protos/path/baz.a.b.c.proto/baz",
999 exportedIncludeDir + "/external-protos/path/bar",
1000 exportedIncludeDir + "/external-protos/path/qux.ext",
1001 },
1002 gen.outputFiles,
1003 )
1004}
1005
1006func TestGenSrcsWithOutpuExtension(t *testing.T) {
1007 result := android.GroupFixturePreparers(
1008 prepareForGenRuleTest,
1009 android.FixtureMergeMockFs(android.MockFS{
1010 "external-protos/path/Android.bp": []byte(`
1011 filegroup {
1012 name: "external-protos",
1013 srcs: ["baz/baz.a.b.c.proto", "bar.a.b.c.proto"],
1014 }
1015 `),
1016 "package-dir/Android.bp": []byte(`
1017 gensrcs {
1018 name: "module-name",
1019 cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
1020 srcs: [
1021 "src/foo.a.b.c.proto",
1022 ":external-protos",
1023 ],
1024
1025 output_extension: "proto.h",
1026 }
1027 `),
1028 }),
1029 ).RunTest(t)
1030
1031 exportedIncludeDir := "out/soong/.intermediates/package-dir/module-name/gen/gensrcs"
1032 gen := result.Module("module-name", "").(*Module)
1033
1034 android.AssertPathsRelativeToTopEquals(
1035 t,
1036 "include path",
1037 []string{exportedIncludeDir},
1038 gen.exportedIncludeDirs,
1039 )
1040 android.AssertPathsRelativeToTopEquals(
1041 t,
1042 "files",
1043 []string{
1044 exportedIncludeDir + "/package-dir/src/foo.a.b.c.proto.h",
1045 exportedIncludeDir + "/external-protos/path/baz/baz.a.b.c.proto.h",
1046 exportedIncludeDir + "/external-protos/path/bar.a.b.c.proto.h",
1047 },
1048 gen.outputFiles,
1049 )
1050}
1051
Martin Stjernholmdbd814d2022-01-12 23:18:30 +00001052func TestPrebuiltTool(t *testing.T) {
1053 testcases := []struct {
1054 name string
1055 bp string
1056 expectedToolName string
1057 }{
1058 {
1059 name: "source only",
1060 bp: `
1061 tool { name: "tool" }
1062 `,
1063 expectedToolName: "bin/tool",
1064 },
1065 {
1066 name: "prebuilt only",
1067 bp: `
1068 prebuilt_tool { name: "tool" }
1069 `,
1070 expectedToolName: "prebuilt_bin/tool",
1071 },
1072 {
1073 name: "source preferred",
1074 bp: `
1075 tool { name: "tool" }
1076 prebuilt_tool { name: "tool" }
1077 `,
1078 expectedToolName: "bin/tool",
1079 },
1080 {
1081 name: "prebuilt preferred",
1082 bp: `
1083 tool { name: "tool" }
1084 prebuilt_tool { name: "tool", prefer: true }
1085 `,
1086 expectedToolName: "prebuilt_bin/prebuilt_tool",
1087 },
1088 {
1089 name: "source disabled",
1090 bp: `
1091 tool { name: "tool", enabled: false }
1092 prebuilt_tool { name: "tool" }
1093 `,
1094 expectedToolName: "prebuilt_bin/prebuilt_tool",
1095 },
1096 }
1097
1098 for _, test := range testcases {
1099 t.Run(test.name, func(t *testing.T) {
1100 result := prepareForGenRuleTest.RunTestWithBp(t, test.bp+`
1101 genrule {
1102 name: "gen",
1103 tools: ["tool"],
1104 out: ["foo"],
1105 cmd: "$(location tool)",
1106 }
1107 `)
1108 gen := result.Module("gen", "").(*Module)
1109 expectedCmd := "__SBOX_SANDBOX_DIR__/tools/out/" + test.expectedToolName
1110 android.AssertStringEquals(t, "command", expectedCmd, gen.rawCommands[0])
1111 })
1112 }
1113}
1114
Jihoon Kangc170af42022-08-20 05:26:38 +00001115func TestGenruleWithGlobPaths(t *testing.T) {
1116 testcases := []struct {
1117 name string
1118 bp string
1119 additionalFiles android.MockFS
1120 expectedCmd string
1121 }{
1122 {
1123 name: "single file in directory with $ sign",
1124 bp: `
1125 genrule {
1126 name: "gen",
1127 srcs: ["inn*.txt"],
1128 out: ["out.txt"],
1129 cmd: "cp $(in) $(out)",
1130 }
1131 `,
1132 additionalFiles: android.MockFS{"inn$1.txt": nil},
1133 expectedCmd: "cp 'inn$1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
1134 },
1135 {
1136 name: "multiple file in directory with $ sign",
1137 bp: `
1138 genrule {
1139 name: "gen",
1140 srcs: ["inn*.txt"],
1141 out: ["."],
1142 cmd: "cp $(in) $(out)",
1143 }
1144 `,
1145 additionalFiles: android.MockFS{"inn$1.txt": nil, "inn$2.txt": nil},
1146 expectedCmd: "cp 'inn$1.txt' 'inn$2.txt' __SBOX_SANDBOX_DIR__/out",
1147 },
1148 {
1149 name: "file in directory with other shell unsafe character",
1150 bp: `
1151 genrule {
1152 name: "gen",
1153 srcs: ["inn*.txt"],
1154 out: ["out.txt"],
1155 cmd: "cp $(in) $(out)",
1156 }
1157 `,
1158 additionalFiles: android.MockFS{"inn@1.txt": nil},
1159 expectedCmd: "cp 'inn@1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
1160 },
1161 {
1162 name: "glob location param with filepath containing $",
1163 bp: `
1164 genrule {
1165 name: "gen",
1166 srcs: ["**/inn*"],
1167 out: ["."],
1168 cmd: "cp $(in) $(location **/inn*)",
1169 }
1170 `,
1171 additionalFiles: android.MockFS{"a/inn$1.txt": nil},
1172 expectedCmd: "cp 'a/inn$1.txt' 'a/inn$1.txt'",
1173 },
1174 {
1175 name: "glob locations param with filepath containing $",
1176 bp: `
1177 genrule {
1178 name: "gen",
1179 tool_files: ["**/inn*"],
1180 out: ["out.txt"],
1181 cmd: "cp $(locations **/inn*) $(out)",
1182 }
1183 `,
1184 additionalFiles: android.MockFS{"a/inn$1.txt": nil},
1185 expectedCmd: "cp '__SBOX_SANDBOX_DIR__/tools/src/a/inn$1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
1186 },
1187 }
1188
1189 for _, test := range testcases {
1190 t.Run(test.name, func(t *testing.T) {
1191 result := android.GroupFixturePreparers(
1192 prepareForGenRuleTest,
1193 android.FixtureMergeMockFs(test.additionalFiles),
1194 ).RunTestWithBp(t, test.bp)
1195 gen := result.Module("gen", "").(*Module)
1196 android.AssertStringEquals(t, "command", test.expectedCmd, gen.rawCommands[0])
1197 })
1198 }
1199}
1200
Cole Faust78f3c3a2024-08-15 17:19:34 -07001201func TestGenruleUsesOrderOnlyBuildNumberFile(t *testing.T) {
1202 testCases := []struct {
1203 name string
1204 bp string
1205 fs android.MockFS
1206 expectedError string
1207 expectedCommand string
1208 }{
1209 {
1210 name: "not allowed when not in allowlist",
1211 fs: android.MockFS{
1212 "foo/Android.bp": []byte(`
1213genrule {
1214 name: "gen",
1215 uses_order_only_build_number_file: true,
1216 cmd: "cp $(build_number_file) $(out)",
1217 out: ["out.txt"],
1218}
1219`),
1220 },
1221 expectedError: `Only allowlisted modules may use uses_order_only_build_number_file: true`,
1222 },
1223 {
1224 name: "normal",
1225 fs: android.MockFS{
1226 "build/soong/tests/Android.bp": []byte(`
1227genrule {
1228 name: "gen",
1229 uses_order_only_build_number_file: true,
1230 cmd: "cp $(build_number_file) $(out)",
1231 out: ["out.txt"],
1232}
1233`),
1234 },
1235 expectedCommand: `cp BUILD_NUMBER_FILE __SBOX_SANDBOX_DIR__/out/out.txt`,
1236 },
1237 }
1238
1239 for _, tc := range testCases {
1240 t.Run(tc.name, func(t *testing.T) {
1241 fixtures := android.GroupFixturePreparers(
1242 prepareForGenRuleTest,
1243 android.PrepareForTestWithVisibility,
1244 android.FixtureMergeMockFs(tc.fs),
1245 android.FixtureModifyConfigAndContext(func(config android.Config, ctx *android.TestContext) {
1246 config.TestProductVariables.BuildNumberFile = proptools.StringPtr("build_number.txt")
1247 }),
1248 )
1249 if tc.expectedError != "" {
1250 fixtures = fixtures.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(tc.expectedError))
1251 }
1252 result := fixtures.RunTest(t)
1253
1254 if tc.expectedError == "" {
1255 tc.expectedCommand = strings.ReplaceAll(tc.expectedCommand, "BUILD_NUMBER_FILE", result.Config.SoongOutDir()+"/build_number.txt")
1256 gen := result.Module("gen", "").(*Module)
1257 android.AssertStringEquals(t, "raw commands", tc.expectedCommand, gen.rawCommands[0])
1258 }
1259 })
1260 }
1261}
1262
Colin Cross2a076922018-10-04 23:28:25 -07001263type testTool struct {
1264 android.ModuleBase
1265 outputFile android.Path
1266}
1267
1268func toolFactory() android.Module {
1269 module := &testTool{}
1270 android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
1271 return module
1272}
1273
Colin Cross2a076922018-10-04 23:28:25 -07001274func (t *testTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
Colin Crossba9e4032020-11-24 16:32:22 -08001275 t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
Colin Cross2a076922018-10-04 23:28:25 -07001276}
1277
1278func (t *testTool) HostToolPath() android.OptionalPath {
1279 return android.OptionalPathForPath(t.outputFile)
1280}
1281
Martin Stjernholmdbd814d2022-01-12 23:18:30 +00001282type prebuiltTestTool struct {
1283 android.ModuleBase
1284 prebuilt android.Prebuilt
1285 testTool
1286}
1287
1288func (p *prebuiltTestTool) Name() string {
1289 return p.prebuilt.Name(p.ModuleBase.Name())
1290}
1291
1292func (p *prebuiltTestTool) Prebuilt() *android.Prebuilt {
1293 return &p.prebuilt
1294}
1295
1296func (t *prebuiltTestTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
1297 t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "prebuilt_bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
1298}
1299
1300func prebuiltToolFactory() android.Module {
1301 module := &prebuiltTestTool{}
1302 android.InitPrebuiltModuleWithoutSrcs(module)
1303 android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
1304 return module
1305}
1306
Colin Crossfe17f6f2019-03-28 19:30:56 -07001307var _ android.HostToolProvider = (*testTool)(nil)
Martin Stjernholmdbd814d2022-01-12 23:18:30 +00001308var _ android.HostToolProvider = (*prebuiltTestTool)(nil)
Colin Crossfa65cee2021-03-22 17:05:59 -07001309
1310type testOutputProducer struct {
1311 android.ModuleBase
1312 outputFile android.Path
1313}
1314
1315func outputProducerFactory() android.Module {
1316 module := &testOutputProducer{}
1317 android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
1318 return module
1319}
1320
1321func (t *testOutputProducer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
1322 t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
1323}
1324
Jooyung Han8c7e3ed2021-06-28 17:35:58 +09001325type useSource struct {
1326 android.ModuleBase
1327 props struct {
1328 Srcs []string `android:"path"`
1329 }
1330 srcs android.Paths
1331}
1332
1333func (s *useSource) GenerateAndroidBuildActions(ctx android.ModuleContext) {
1334 s.srcs = android.PathsForModuleSrc(ctx, s.props.Srcs)
1335}
1336
1337func useSourceFactory() android.Module {
1338 module := &useSource{}
1339 module.AddProperties(&module.props)
1340 android.InitAndroidModule(module)
1341 return module
1342}