genrule_test.go 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137
  1. // 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. package genrule
  15. import (
  16. "fmt"
  17. "os"
  18. "regexp"
  19. "testing"
  20. "android/soong/android"
  21. "github.com/google/blueprint/proptools"
  22. )
  23. func TestMain(m *testing.M) {
  24. os.Exit(m.Run())
  25. }
  26. var prepareForGenRuleTest = android.GroupFixturePreparers(
  27. android.PrepareForTestWithArchMutator,
  28. android.PrepareForTestWithDefaults,
  29. android.PrepareForTestWithFilegroup,
  30. PrepareForTestWithGenRuleBuildComponents,
  31. android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
  32. android.RegisterPrebuiltMutators(ctx)
  33. ctx.RegisterModuleType("tool", toolFactory)
  34. ctx.RegisterModuleType("prebuilt_tool", prebuiltToolFactory)
  35. ctx.RegisterModuleType("output", outputProducerFactory)
  36. ctx.RegisterModuleType("use_source", useSourceFactory)
  37. }),
  38. android.FixtureMergeMockFs(android.MockFS{
  39. "tool": nil,
  40. "tool_file1": nil,
  41. "tool_file2": nil,
  42. "in1": nil,
  43. "in2": nil,
  44. "in1.txt": nil,
  45. "in2.txt": nil,
  46. "in3.txt": nil,
  47. }),
  48. )
  49. func testGenruleBp() string {
  50. return `
  51. tool {
  52. name: "tool",
  53. }
  54. filegroup {
  55. name: "tool_files",
  56. srcs: [
  57. "tool_file1",
  58. "tool_file2",
  59. ],
  60. }
  61. filegroup {
  62. name: "1tool_file",
  63. srcs: [
  64. "tool_file1",
  65. ],
  66. }
  67. filegroup {
  68. name: "ins",
  69. srcs: [
  70. "in1",
  71. "in2",
  72. ],
  73. }
  74. filegroup {
  75. name: "1in",
  76. srcs: [
  77. "in1",
  78. ],
  79. }
  80. filegroup {
  81. name: "empty",
  82. }
  83. `
  84. }
  85. func TestGenruleCmd(t *testing.T) {
  86. testcases := []struct {
  87. name string
  88. moduleName string
  89. prop string
  90. allowMissingDependencies bool
  91. err string
  92. expect string
  93. }{
  94. {
  95. name: "empty location tool",
  96. prop: `
  97. tools: ["tool"],
  98. out: ["out"],
  99. cmd: "$(location) > $(out)",
  100. `,
  101. expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
  102. },
  103. {
  104. name: "empty location tool2",
  105. prop: `
  106. tools: [":tool"],
  107. out: ["out"],
  108. cmd: "$(location) > $(out)",
  109. `,
  110. expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
  111. },
  112. {
  113. name: "empty location tool file",
  114. prop: `
  115. tool_files: ["tool_file1"],
  116. out: ["out"],
  117. cmd: "$(location) > $(out)",
  118. `,
  119. expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
  120. },
  121. {
  122. name: "empty location tool file fg",
  123. prop: `
  124. tool_files: [":1tool_file"],
  125. out: ["out"],
  126. cmd: "$(location) > $(out)",
  127. `,
  128. expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
  129. },
  130. {
  131. name: "empty location tool and tool file",
  132. prop: `
  133. tools: ["tool"],
  134. tool_files: ["tool_file1"],
  135. out: ["out"],
  136. cmd: "$(location) > $(out)",
  137. `,
  138. expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
  139. },
  140. {
  141. name: "tool",
  142. prop: `
  143. tools: ["tool"],
  144. out: ["out"],
  145. cmd: "$(location tool) > $(out)",
  146. `,
  147. expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
  148. },
  149. {
  150. name: "tool2",
  151. prop: `
  152. tools: [":tool"],
  153. out: ["out"],
  154. cmd: "$(location :tool) > $(out)",
  155. `,
  156. expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
  157. },
  158. {
  159. name: "tool file",
  160. prop: `
  161. tool_files: ["tool_file1"],
  162. out: ["out"],
  163. cmd: "$(location tool_file1) > $(out)",
  164. `,
  165. expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
  166. },
  167. {
  168. name: "tool file fg",
  169. prop: `
  170. tool_files: [":1tool_file"],
  171. out: ["out"],
  172. cmd: "$(location :1tool_file) > $(out)",
  173. `,
  174. expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
  175. },
  176. {
  177. name: "tool files",
  178. prop: `
  179. tool_files: [":tool_files"],
  180. out: ["out"],
  181. cmd: "$(locations :tool_files) > $(out)",
  182. `,
  183. expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 __SBOX_SANDBOX_DIR__/tools/src/tool_file2 > __SBOX_SANDBOX_DIR__/out/out",
  184. },
  185. {
  186. name: "in1",
  187. prop: `
  188. srcs: ["in1"],
  189. out: ["out"],
  190. cmd: "cat $(in) > $(out)",
  191. `,
  192. expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
  193. },
  194. {
  195. name: "in1 fg",
  196. prop: `
  197. srcs: [":1in"],
  198. out: ["out"],
  199. cmd: "cat $(in) > $(out)",
  200. `,
  201. expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
  202. },
  203. {
  204. name: "ins",
  205. prop: `
  206. srcs: ["in1", "in2"],
  207. out: ["out"],
  208. cmd: "cat $(in) > $(out)",
  209. `,
  210. expect: "cat in1 in2 > __SBOX_SANDBOX_DIR__/out/out",
  211. },
  212. {
  213. name: "ins fg",
  214. prop: `
  215. srcs: [":ins"],
  216. out: ["out"],
  217. cmd: "cat $(in) > $(out)",
  218. `,
  219. expect: "cat in1 in2 > __SBOX_SANDBOX_DIR__/out/out",
  220. },
  221. {
  222. name: "location in1",
  223. prop: `
  224. srcs: ["in1"],
  225. out: ["out"],
  226. cmd: "cat $(location in1) > $(out)",
  227. `,
  228. expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
  229. },
  230. {
  231. name: "location in1 fg",
  232. prop: `
  233. srcs: [":1in"],
  234. out: ["out"],
  235. cmd: "cat $(location :1in) > $(out)",
  236. `,
  237. expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
  238. },
  239. {
  240. name: "location ins",
  241. prop: `
  242. srcs: ["in1", "in2"],
  243. out: ["out"],
  244. cmd: "cat $(location in1) > $(out)",
  245. `,
  246. expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
  247. },
  248. {
  249. name: "location ins fg",
  250. prop: `
  251. srcs: [":ins"],
  252. out: ["out"],
  253. cmd: "cat $(locations :ins) > $(out)",
  254. `,
  255. expect: "cat in1 in2 > __SBOX_SANDBOX_DIR__/out/out",
  256. },
  257. {
  258. name: "outs",
  259. prop: `
  260. out: ["out", "out2"],
  261. cmd: "echo foo > $(out)",
  262. `,
  263. expect: "echo foo > __SBOX_SANDBOX_DIR__/out/out __SBOX_SANDBOX_DIR__/out/out2",
  264. },
  265. {
  266. name: "location out",
  267. prop: `
  268. out: ["out", "out2"],
  269. cmd: "echo foo > $(location out2)",
  270. `,
  271. expect: "echo foo > __SBOX_SANDBOX_DIR__/out/out2",
  272. },
  273. {
  274. name: "depfile",
  275. moduleName: "depfile_allowed_for_test",
  276. prop: `
  277. out: ["out"],
  278. depfile: true,
  279. cmd: "echo foo > $(out) && touch $(depfile)",
  280. `,
  281. expect: "echo foo > __SBOX_SANDBOX_DIR__/out/out && touch __SBOX_DEPFILE__",
  282. },
  283. {
  284. name: "gendir",
  285. prop: `
  286. out: ["out"],
  287. cmd: "echo foo > $(genDir)/foo && cp $(genDir)/foo $(out)",
  288. `,
  289. expect: "echo foo > __SBOX_SANDBOX_DIR__/out/foo && cp __SBOX_SANDBOX_DIR__/out/foo __SBOX_SANDBOX_DIR__/out/out",
  290. },
  291. {
  292. name: "$",
  293. prop: `
  294. out: ["out"],
  295. cmd: "echo $$ > $(out)",
  296. `,
  297. expect: "echo $ > __SBOX_SANDBOX_DIR__/out/out",
  298. },
  299. {
  300. name: "error empty location",
  301. prop: `
  302. out: ["out"],
  303. cmd: "$(location) > $(out)",
  304. `,
  305. err: "at least one `tools` or `tool_files` is required if $(location) is used",
  306. },
  307. {
  308. name: "error empty location no files",
  309. prop: `
  310. tool_files: [":empty"],
  311. out: ["out"],
  312. cmd: "$(location) > $(out)",
  313. `,
  314. err: `default label ":empty" has no files`,
  315. },
  316. {
  317. name: "error empty location multiple files",
  318. prop: `
  319. tool_files: [":tool_files"],
  320. out: ["out"],
  321. cmd: "$(location) > $(out)",
  322. `,
  323. err: `default label ":tool_files" has multiple files`,
  324. },
  325. {
  326. name: "error location",
  327. prop: `
  328. out: ["out"],
  329. cmd: "echo foo > $(location missing)",
  330. `,
  331. err: `unknown location label "missing" is not in srcs, out, tools or tool_files.`,
  332. },
  333. {
  334. name: "error locations",
  335. prop: `
  336. out: ["out"],
  337. cmd: "echo foo > $(locations missing)",
  338. `,
  339. err: `unknown locations label "missing" is not in srcs, out, tools or tool_files`,
  340. },
  341. {
  342. name: "error location no files",
  343. prop: `
  344. out: ["out"],
  345. srcs: [":empty"],
  346. cmd: "echo $(location :empty) > $(out)",
  347. `,
  348. err: `label ":empty" has no files`,
  349. },
  350. {
  351. name: "error locations no files",
  352. prop: `
  353. out: ["out"],
  354. srcs: [":empty"],
  355. cmd: "echo $(locations :empty) > $(out)",
  356. `,
  357. err: `label ":empty" has no files`,
  358. },
  359. {
  360. name: "error location multiple files",
  361. prop: `
  362. out: ["out"],
  363. srcs: [":ins"],
  364. cmd: "echo $(location :ins) > $(out)",
  365. `,
  366. err: `label ":ins" has multiple files`,
  367. },
  368. {
  369. name: "error variable",
  370. prop: `
  371. out: ["out"],
  372. srcs: ["in1"],
  373. cmd: "echo $(foo) > $(out)",
  374. `,
  375. err: `unknown variable '$(foo)'`,
  376. },
  377. {
  378. name: "error depfile",
  379. prop: `
  380. out: ["out"],
  381. cmd: "echo foo > $(out) && touch $(depfile)",
  382. `,
  383. err: "$(depfile) used without depfile property",
  384. },
  385. {
  386. name: "error no depfile",
  387. moduleName: "depfile_allowed_for_test",
  388. prop: `
  389. out: ["out"],
  390. depfile: true,
  391. cmd: "echo foo > $(out)",
  392. `,
  393. err: "specified depfile=true but did not include a reference to '${depfile}' in cmd",
  394. },
  395. {
  396. name: "error no out",
  397. prop: `
  398. cmd: "echo foo > $(out)",
  399. `,
  400. err: "must have at least one output file",
  401. },
  402. {
  403. name: "srcs allow missing dependencies",
  404. prop: `
  405. srcs: [":missing"],
  406. out: ["out"],
  407. cmd: "cat $(location :missing) > $(out)",
  408. `,
  409. allowMissingDependencies: true,
  410. expect: "cat '***missing srcs :missing***' > __SBOX_SANDBOX_DIR__/out/out",
  411. },
  412. {
  413. name: "tool allow missing dependencies",
  414. prop: `
  415. tools: [":missing"],
  416. out: ["out"],
  417. cmd: "$(location :missing) > $(out)",
  418. `,
  419. allowMissingDependencies: true,
  420. expect: "'***missing tool :missing***' > __SBOX_SANDBOX_DIR__/out/out",
  421. },
  422. }
  423. for _, test := range testcases {
  424. t.Run(test.name, func(t *testing.T) {
  425. moduleName := "gen"
  426. if test.moduleName != "" {
  427. moduleName = test.moduleName
  428. }
  429. bp := fmt.Sprintf(`
  430. genrule {
  431. name: "%s",
  432. %s
  433. }`, moduleName, test.prop)
  434. var expectedErrors []string
  435. if test.err != "" {
  436. expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
  437. }
  438. result := android.GroupFixturePreparers(
  439. prepareForGenRuleTest,
  440. android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
  441. variables.Allow_missing_dependencies = proptools.BoolPtr(test.allowMissingDependencies)
  442. }),
  443. android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
  444. variables.GenruleSandboxing = proptools.BoolPtr(true)
  445. }),
  446. android.FixtureModifyContext(func(ctx *android.TestContext) {
  447. ctx.SetAllowMissingDependencies(test.allowMissingDependencies)
  448. }),
  449. ).
  450. ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
  451. RunTestWithBp(t, testGenruleBp()+bp)
  452. if expectedErrors != nil {
  453. return
  454. }
  455. gen := result.Module(moduleName, "").(*Module)
  456. android.AssertStringEquals(t, "raw commands", test.expect, gen.rawCommands[0])
  457. })
  458. }
  459. }
  460. func TestGenruleHashInputs(t *testing.T) {
  461. // The basic idea here is to verify that the sbox command (which is
  462. // in the Command field of the generate rule) contains a hash of the
  463. // inputs, but only if $(in) is not referenced in the genrule cmd
  464. // property.
  465. // By including a hash of the inputs, we cause the rule to re-run if
  466. // the list of inputs changes (because the sbox command changes).
  467. // However, if the genrule cmd property already contains $(in), then
  468. // the dependency is already expressed, so we don't need to include the
  469. // hash in that case.
  470. bp := `
  471. genrule {
  472. name: "hash0",
  473. srcs: ["in1.txt", "in2.txt"],
  474. out: ["out"],
  475. cmd: "echo foo > $(out)",
  476. }
  477. genrule {
  478. name: "hash1",
  479. srcs: ["*.txt"],
  480. out: ["out"],
  481. cmd: "echo bar > $(out)",
  482. }
  483. genrule {
  484. name: "hash2",
  485. srcs: ["*.txt"],
  486. out: ["out"],
  487. cmd: "echo $(in) > $(out)",
  488. }
  489. `
  490. testcases := []struct {
  491. name string
  492. expectedHash string
  493. }{
  494. {
  495. name: "hash0",
  496. // sha256 value obtained from: echo -en 'in1.txt\nin2.txt' | sha256sum
  497. expectedHash: "18da75b9b1cc74b09e365b4ca2e321b5d618f438cc632b387ad9dc2ab4b20e9d",
  498. },
  499. {
  500. name: "hash1",
  501. // sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
  502. expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
  503. },
  504. {
  505. name: "hash2",
  506. // sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
  507. expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
  508. },
  509. }
  510. result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
  511. for _, test := range testcases {
  512. t.Run(test.name, func(t *testing.T) {
  513. gen := result.ModuleForTests(test.name, "")
  514. manifest := android.RuleBuilderSboxProtoForTests(t, gen.Output("genrule.sbox.textproto"))
  515. hash := manifest.Commands[0].GetInputHash()
  516. android.AssertStringEquals(t, "hash", test.expectedHash, hash)
  517. })
  518. }
  519. }
  520. func TestGenSrcs(t *testing.T) {
  521. testcases := []struct {
  522. name string
  523. prop string
  524. allowMissingDependencies bool
  525. err string
  526. cmds []string
  527. deps []string
  528. files []string
  529. }{
  530. {
  531. name: "gensrcs",
  532. prop: `
  533. tools: ["tool"],
  534. srcs: ["in1.txt", "in2.txt"],
  535. cmd: "$(location) $(in) > $(out)",
  536. `,
  537. cmds: []string{
  538. "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'",
  539. },
  540. deps: []string{
  541. "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
  542. "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
  543. },
  544. files: []string{
  545. "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
  546. "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
  547. },
  548. },
  549. {
  550. name: "shards",
  551. prop: `
  552. tools: ["tool"],
  553. srcs: ["in1.txt", "in2.txt", "in3.txt"],
  554. cmd: "$(location) $(in) > $(out)",
  555. shard_size: 2,
  556. `,
  557. cmds: []string{
  558. "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'",
  559. "bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in3.txt > __SBOX_SANDBOX_DIR__/out/in3.h'",
  560. },
  561. deps: []string{
  562. "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
  563. "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
  564. "out/soong/.intermediates/gen/gen/gensrcs/in3.h",
  565. },
  566. files: []string{
  567. "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
  568. "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
  569. "out/soong/.intermediates/gen/gen/gensrcs/in3.h",
  570. },
  571. },
  572. }
  573. for _, test := range testcases {
  574. t.Run(test.name, func(t *testing.T) {
  575. bp := "gensrcs {\n"
  576. bp += `name: "gen",` + "\n"
  577. bp += `output_extension: "h",` + "\n"
  578. bp += test.prop
  579. bp += "}\n"
  580. var expectedErrors []string
  581. if test.err != "" {
  582. expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
  583. }
  584. result := prepareForGenRuleTest.
  585. ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
  586. RunTestWithBp(t, testGenruleBp()+bp)
  587. if expectedErrors != nil {
  588. return
  589. }
  590. gen := result.Module("gen", "").(*Module)
  591. android.AssertDeepEquals(t, "cmd", test.cmds, gen.rawCommands)
  592. android.AssertPathsRelativeToTopEquals(t, "deps", test.deps, gen.outputDeps)
  593. android.AssertPathsRelativeToTopEquals(t, "files", test.files, gen.outputFiles)
  594. })
  595. }
  596. }
  597. func TestGenruleAllowlistingDepfile(t *testing.T) {
  598. tests := []struct {
  599. name string
  600. prop string
  601. err string
  602. moduleName string
  603. }{
  604. {
  605. name: `error when module is not allowlisted`,
  606. prop: `
  607. depfile: true,
  608. cmd: "cat $(in) > $(out) && cat $(depfile)",
  609. `,
  610. err: "depfile: Deprecated to ensure the module type is convertible to Bazel",
  611. },
  612. {
  613. name: `no error when module is allowlisted`,
  614. prop: `
  615. depfile: true,
  616. cmd: "cat $(in) > $(out) && cat $(depfile)",
  617. `,
  618. moduleName: `depfile_allowed_for_test`,
  619. },
  620. }
  621. for _, test := range tests {
  622. t.Run(test.name, func(t *testing.T) {
  623. moduleName := "foo"
  624. if test.moduleName != "" {
  625. moduleName = test.moduleName
  626. }
  627. bp := fmt.Sprintf(`
  628. gensrcs {
  629. name: "%s",
  630. srcs: ["data.txt"],
  631. %s
  632. }`, moduleName, test.prop)
  633. var expectedErrors []string
  634. if test.err != "" {
  635. expectedErrors = append(expectedErrors, test.err)
  636. }
  637. android.GroupFixturePreparers(
  638. prepareForGenRuleTest,
  639. android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
  640. variables.GenruleSandboxing = proptools.BoolPtr(true)
  641. }),
  642. ).
  643. ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
  644. RunTestWithBp(t, bp)
  645. })
  646. }
  647. }
  648. func TestGenruleDefaults(t *testing.T) {
  649. bp := `
  650. genrule_defaults {
  651. name: "gen_defaults1",
  652. cmd: "cp $(in) $(out)",
  653. }
  654. genrule_defaults {
  655. name: "gen_defaults2",
  656. srcs: ["in1"],
  657. }
  658. genrule {
  659. name: "gen",
  660. out: ["out"],
  661. defaults: ["gen_defaults1", "gen_defaults2"],
  662. }
  663. `
  664. result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
  665. gen := result.Module("gen", "").(*Module)
  666. expectedCmd := "cp in1 __SBOX_SANDBOX_DIR__/out/out"
  667. android.AssertStringEquals(t, "cmd", expectedCmd, gen.rawCommands[0])
  668. expectedSrcs := []string{"in1"}
  669. android.AssertDeepEquals(t, "srcs", expectedSrcs, gen.properties.Srcs)
  670. }
  671. func TestGenruleAllowMissingDependencies(t *testing.T) {
  672. bp := `
  673. output {
  674. name: "disabled",
  675. enabled: false,
  676. }
  677. genrule {
  678. name: "gen",
  679. srcs: [
  680. ":disabled",
  681. ],
  682. out: ["out"],
  683. cmd: "cat $(in) > $(out)",
  684. }
  685. `
  686. result := android.GroupFixturePreparers(
  687. prepareForGenRuleTest,
  688. android.FixtureModifyConfigAndContext(
  689. func(config android.Config, ctx *android.TestContext) {
  690. config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
  691. ctx.SetAllowMissingDependencies(true)
  692. })).RunTestWithBp(t, bp)
  693. gen := result.ModuleForTests("gen", "").Output("out")
  694. if gen.Rule != android.ErrorRule {
  695. t.Errorf("Expected missing dependency error rule for gen, got %q", gen.Rule.String())
  696. }
  697. }
  698. func TestGenruleOutputFiles(t *testing.T) {
  699. bp := `
  700. genrule {
  701. name: "gen",
  702. out: ["foo", "sub/bar"],
  703. cmd: "echo foo > $(location foo) && echo bar > $(location sub/bar)",
  704. }
  705. use_source {
  706. name: "gen_foo",
  707. srcs: [":gen{foo}"],
  708. }
  709. use_source {
  710. name: "gen_bar",
  711. srcs: [":gen{sub/bar}"],
  712. }
  713. use_source {
  714. name: "gen_all",
  715. srcs: [":gen"],
  716. }
  717. `
  718. result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
  719. android.AssertPathsRelativeToTopEquals(t,
  720. "genrule.tag with output",
  721. []string{"out/soong/.intermediates/gen/gen/foo"},
  722. result.ModuleForTests("gen_foo", "").Module().(*useSource).srcs)
  723. android.AssertPathsRelativeToTopEquals(t,
  724. "genrule.tag with output in subdir",
  725. []string{"out/soong/.intermediates/gen/gen/sub/bar"},
  726. result.ModuleForTests("gen_bar", "").Module().(*useSource).srcs)
  727. android.AssertPathsRelativeToTopEquals(t,
  728. "genrule.tag with all",
  729. []string{"out/soong/.intermediates/gen/gen/foo", "out/soong/.intermediates/gen/gen/sub/bar"},
  730. result.ModuleForTests("gen_all", "").Module().(*useSource).srcs)
  731. }
  732. func TestGenSrcsWithNonRootAndroidBpOutputFiles(t *testing.T) {
  733. result := android.GroupFixturePreparers(
  734. prepareForGenRuleTest,
  735. android.FixtureMergeMockFs(android.MockFS{
  736. "external-protos/path/Android.bp": []byte(`
  737. filegroup {
  738. name: "external-protos",
  739. srcs: ["baz/baz.proto", "bar.proto"],
  740. }
  741. `),
  742. "package-dir/Android.bp": []byte(`
  743. gensrcs {
  744. name: "module-name",
  745. cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
  746. srcs: [
  747. "src/foo.proto",
  748. ":external-protos",
  749. ],
  750. output_extension: "proto.h",
  751. }
  752. `),
  753. }),
  754. ).RunTest(t)
  755. exportedIncludeDir := "out/soong/.intermediates/package-dir/module-name/gen/gensrcs"
  756. gen := result.Module("module-name", "").(*Module)
  757. android.AssertPathsRelativeToTopEquals(
  758. t,
  759. "include path",
  760. []string{exportedIncludeDir},
  761. gen.exportedIncludeDirs,
  762. )
  763. android.AssertPathsRelativeToTopEquals(
  764. t,
  765. "files",
  766. []string{
  767. exportedIncludeDir + "/package-dir/src/foo.proto.h",
  768. exportedIncludeDir + "/external-protos/path/baz/baz.proto.h",
  769. exportedIncludeDir + "/external-protos/path/bar.proto.h",
  770. },
  771. gen.outputFiles,
  772. )
  773. }
  774. func TestGenSrcsWithSrcsFromExternalPackage(t *testing.T) {
  775. bp := `
  776. gensrcs {
  777. name: "module-name",
  778. cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
  779. srcs: [
  780. ":external-protos",
  781. ],
  782. output_extension: "proto.h",
  783. }
  784. `
  785. result := android.GroupFixturePreparers(
  786. prepareForGenRuleTest,
  787. android.FixtureMergeMockFs(android.MockFS{
  788. "external-protos/path/Android.bp": []byte(`
  789. filegroup {
  790. name: "external-protos",
  791. srcs: ["foo/foo.proto", "bar.proto"],
  792. }
  793. `),
  794. }),
  795. ).RunTestWithBp(t, bp)
  796. exportedIncludeDir := "out/soong/.intermediates/module-name/gen/gensrcs"
  797. gen := result.Module("module-name", "").(*Module)
  798. android.AssertPathsRelativeToTopEquals(
  799. t,
  800. "include path",
  801. []string{exportedIncludeDir},
  802. gen.exportedIncludeDirs,
  803. )
  804. android.AssertPathsRelativeToTopEquals(
  805. t,
  806. "files",
  807. []string{
  808. exportedIncludeDir + "/external-protos/path/foo/foo.proto.h",
  809. exportedIncludeDir + "/external-protos/path/bar.proto.h",
  810. },
  811. gen.outputFiles,
  812. )
  813. }
  814. func TestPrebuiltTool(t *testing.T) {
  815. testcases := []struct {
  816. name string
  817. bp string
  818. expectedToolName string
  819. }{
  820. {
  821. name: "source only",
  822. bp: `
  823. tool { name: "tool" }
  824. `,
  825. expectedToolName: "bin/tool",
  826. },
  827. {
  828. name: "prebuilt only",
  829. bp: `
  830. prebuilt_tool { name: "tool" }
  831. `,
  832. expectedToolName: "prebuilt_bin/tool",
  833. },
  834. {
  835. name: "source preferred",
  836. bp: `
  837. tool { name: "tool" }
  838. prebuilt_tool { name: "tool" }
  839. `,
  840. expectedToolName: "bin/tool",
  841. },
  842. {
  843. name: "prebuilt preferred",
  844. bp: `
  845. tool { name: "tool" }
  846. prebuilt_tool { name: "tool", prefer: true }
  847. `,
  848. expectedToolName: "prebuilt_bin/prebuilt_tool",
  849. },
  850. {
  851. name: "source disabled",
  852. bp: `
  853. tool { name: "tool", enabled: false }
  854. prebuilt_tool { name: "tool" }
  855. `,
  856. expectedToolName: "prebuilt_bin/prebuilt_tool",
  857. },
  858. }
  859. for _, test := range testcases {
  860. t.Run(test.name, func(t *testing.T) {
  861. result := prepareForGenRuleTest.RunTestWithBp(t, test.bp+`
  862. genrule {
  863. name: "gen",
  864. tools: ["tool"],
  865. out: ["foo"],
  866. cmd: "$(location tool)",
  867. }
  868. `)
  869. gen := result.Module("gen", "").(*Module)
  870. expectedCmd := "__SBOX_SANDBOX_DIR__/tools/out/" + test.expectedToolName
  871. android.AssertStringEquals(t, "command", expectedCmd, gen.rawCommands[0])
  872. })
  873. }
  874. }
  875. func TestGenruleWithBazel(t *testing.T) {
  876. bp := `
  877. genrule {
  878. name: "foo",
  879. out: ["one.txt", "two.txt"],
  880. bazel_module: { label: "//foo/bar:bar" },
  881. }
  882. `
  883. result := android.GroupFixturePreparers(
  884. prepareForGenRuleTest, android.FixtureModifyConfig(func(config android.Config) {
  885. config.BazelContext = android.MockBazelContext{
  886. OutputBaseDir: "outputbase",
  887. LabelToOutputFiles: map[string][]string{
  888. "//foo/bar:bar": []string{"bazelone.txt", "bazeltwo.txt"}}}
  889. })).RunTestWithBp(t, testGenruleBp()+bp)
  890. gen := result.Module("foo", "").(*Module)
  891. expectedOutputFiles := []string{"outputbase/execroot/__main__/bazelone.txt",
  892. "outputbase/execroot/__main__/bazeltwo.txt"}
  893. android.AssertDeepEquals(t, "output files", expectedOutputFiles, gen.outputFiles.Strings())
  894. android.AssertDeepEquals(t, "output deps", expectedOutputFiles, gen.outputDeps.Strings())
  895. }
  896. func TestGenruleWithGlobPaths(t *testing.T) {
  897. testcases := []struct {
  898. name string
  899. bp string
  900. additionalFiles android.MockFS
  901. expectedCmd string
  902. }{
  903. {
  904. name: "single file in directory with $ sign",
  905. bp: `
  906. genrule {
  907. name: "gen",
  908. srcs: ["inn*.txt"],
  909. out: ["out.txt"],
  910. cmd: "cp $(in) $(out)",
  911. }
  912. `,
  913. additionalFiles: android.MockFS{"inn$1.txt": nil},
  914. expectedCmd: "cp 'inn$1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
  915. },
  916. {
  917. name: "multiple file in directory with $ sign",
  918. bp: `
  919. genrule {
  920. name: "gen",
  921. srcs: ["inn*.txt"],
  922. out: ["."],
  923. cmd: "cp $(in) $(out)",
  924. }
  925. `,
  926. additionalFiles: android.MockFS{"inn$1.txt": nil, "inn$2.txt": nil},
  927. expectedCmd: "cp 'inn$1.txt' 'inn$2.txt' __SBOX_SANDBOX_DIR__/out",
  928. },
  929. {
  930. name: "file in directory with other shell unsafe character",
  931. bp: `
  932. genrule {
  933. name: "gen",
  934. srcs: ["inn*.txt"],
  935. out: ["out.txt"],
  936. cmd: "cp $(in) $(out)",
  937. }
  938. `,
  939. additionalFiles: android.MockFS{"inn@1.txt": nil},
  940. expectedCmd: "cp 'inn@1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
  941. },
  942. {
  943. name: "glob location param with filepath containing $",
  944. bp: `
  945. genrule {
  946. name: "gen",
  947. srcs: ["**/inn*"],
  948. out: ["."],
  949. cmd: "cp $(in) $(location **/inn*)",
  950. }
  951. `,
  952. additionalFiles: android.MockFS{"a/inn$1.txt": nil},
  953. expectedCmd: "cp 'a/inn$1.txt' 'a/inn$1.txt'",
  954. },
  955. {
  956. name: "glob locations param with filepath containing $",
  957. bp: `
  958. genrule {
  959. name: "gen",
  960. tool_files: ["**/inn*"],
  961. out: ["out.txt"],
  962. cmd: "cp $(locations **/inn*) $(out)",
  963. }
  964. `,
  965. additionalFiles: android.MockFS{"a/inn$1.txt": nil},
  966. expectedCmd: "cp '__SBOX_SANDBOX_DIR__/tools/src/a/inn$1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
  967. },
  968. }
  969. for _, test := range testcases {
  970. t.Run(test.name, func(t *testing.T) {
  971. result := android.GroupFixturePreparers(
  972. prepareForGenRuleTest,
  973. android.FixtureMergeMockFs(test.additionalFiles),
  974. ).RunTestWithBp(t, test.bp)
  975. gen := result.Module("gen", "").(*Module)
  976. android.AssertStringEquals(t, "command", test.expectedCmd, gen.rawCommands[0])
  977. })
  978. }
  979. }
  980. type testTool struct {
  981. android.ModuleBase
  982. outputFile android.Path
  983. }
  984. func toolFactory() android.Module {
  985. module := &testTool{}
  986. android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
  987. return module
  988. }
  989. func (t *testTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
  990. t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
  991. }
  992. func (t *testTool) HostToolPath() android.OptionalPath {
  993. return android.OptionalPathForPath(t.outputFile)
  994. }
  995. type prebuiltTestTool struct {
  996. android.ModuleBase
  997. prebuilt android.Prebuilt
  998. testTool
  999. }
  1000. func (p *prebuiltTestTool) Name() string {
  1001. return p.prebuilt.Name(p.ModuleBase.Name())
  1002. }
  1003. func (p *prebuiltTestTool) Prebuilt() *android.Prebuilt {
  1004. return &p.prebuilt
  1005. }
  1006. func (t *prebuiltTestTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
  1007. t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "prebuilt_bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
  1008. }
  1009. func prebuiltToolFactory() android.Module {
  1010. module := &prebuiltTestTool{}
  1011. android.InitPrebuiltModuleWithoutSrcs(module)
  1012. android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
  1013. return module
  1014. }
  1015. var _ android.HostToolProvider = (*testTool)(nil)
  1016. var _ android.HostToolProvider = (*prebuiltTestTool)(nil)
  1017. type testOutputProducer struct {
  1018. android.ModuleBase
  1019. outputFile android.Path
  1020. }
  1021. func outputProducerFactory() android.Module {
  1022. module := &testOutputProducer{}
  1023. android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
  1024. return module
  1025. }
  1026. func (t *testOutputProducer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
  1027. t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
  1028. }
  1029. func (t *testOutputProducer) OutputFiles(tag string) (android.Paths, error) {
  1030. return android.Paths{t.outputFile}, nil
  1031. }
  1032. var _ android.OutputFileProducer = (*testOutputProducer)(nil)
  1033. type useSource struct {
  1034. android.ModuleBase
  1035. props struct {
  1036. Srcs []string `android:"path"`
  1037. }
  1038. srcs android.Paths
  1039. }
  1040. func (s *useSource) GenerateAndroidBuildActions(ctx android.ModuleContext) {
  1041. s.srcs = android.PathsForModuleSrc(ctx, s.props.Srcs)
  1042. }
  1043. func useSourceFactory() android.Module {
  1044. module := &useSource{}
  1045. module.AddProperties(&module.props)
  1046. android.InitAndroidModule(module)
  1047. return module
  1048. }