genrule_test.go 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161
  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. name: "data",
  574. prop: `
  575. tools: ["tool"],
  576. srcs: ["in1.txt", "in2.txt", "in3.txt"],
  577. cmd: "$(location) $(in) --extra_input=$(location baz.txt) > $(out)",
  578. data: ["baz.txt"],
  579. shard_size: 2,
  580. `,
  581. cmds: []string{
  582. "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'",
  583. "bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in3.txt --extra_input=baz.txt > __SBOX_SANDBOX_DIR__/out/in3.h'",
  584. },
  585. deps: []string{
  586. "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
  587. "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
  588. "out/soong/.intermediates/gen/gen/gensrcs/in3.h",
  589. },
  590. files: []string{
  591. "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
  592. "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
  593. "out/soong/.intermediates/gen/gen/gensrcs/in3.h",
  594. },
  595. },
  596. }
  597. for _, test := range testcases {
  598. t.Run(test.name, func(t *testing.T) {
  599. bp := "gensrcs {\n"
  600. bp += `name: "gen",` + "\n"
  601. bp += `output_extension: "h",` + "\n"
  602. bp += test.prop
  603. bp += "}\n"
  604. var expectedErrors []string
  605. if test.err != "" {
  606. expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
  607. }
  608. result := prepareForGenRuleTest.
  609. ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
  610. RunTestWithBp(t, testGenruleBp()+bp)
  611. if expectedErrors != nil {
  612. return
  613. }
  614. gen := result.Module("gen", "").(*Module)
  615. android.AssertDeepEquals(t, "cmd", test.cmds, gen.rawCommands)
  616. android.AssertPathsRelativeToTopEquals(t, "deps", test.deps, gen.outputDeps)
  617. android.AssertPathsRelativeToTopEquals(t, "files", test.files, gen.outputFiles)
  618. })
  619. }
  620. }
  621. func TestGenruleAllowlistingDepfile(t *testing.T) {
  622. tests := []struct {
  623. name string
  624. prop string
  625. err string
  626. moduleName string
  627. }{
  628. {
  629. name: `error when module is not allowlisted`,
  630. prop: `
  631. depfile: true,
  632. cmd: "cat $(in) > $(out) && cat $(depfile)",
  633. `,
  634. err: "depfile: Deprecated to ensure the module type is convertible to Bazel",
  635. },
  636. {
  637. name: `no error when module is allowlisted`,
  638. prop: `
  639. depfile: true,
  640. cmd: "cat $(in) > $(out) && cat $(depfile)",
  641. `,
  642. moduleName: `depfile_allowed_for_test`,
  643. },
  644. }
  645. for _, test := range tests {
  646. t.Run(test.name, func(t *testing.T) {
  647. moduleName := "foo"
  648. if test.moduleName != "" {
  649. moduleName = test.moduleName
  650. }
  651. bp := fmt.Sprintf(`
  652. gensrcs {
  653. name: "%s",
  654. srcs: ["data.txt"],
  655. %s
  656. }`, moduleName, test.prop)
  657. var expectedErrors []string
  658. if test.err != "" {
  659. expectedErrors = append(expectedErrors, test.err)
  660. }
  661. android.GroupFixturePreparers(
  662. prepareForGenRuleTest,
  663. android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
  664. variables.GenruleSandboxing = proptools.BoolPtr(true)
  665. }),
  666. ).
  667. ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
  668. RunTestWithBp(t, bp)
  669. })
  670. }
  671. }
  672. func TestGenruleDefaults(t *testing.T) {
  673. bp := `
  674. genrule_defaults {
  675. name: "gen_defaults1",
  676. cmd: "cp $(in) $(out)",
  677. }
  678. genrule_defaults {
  679. name: "gen_defaults2",
  680. srcs: ["in1"],
  681. }
  682. genrule {
  683. name: "gen",
  684. out: ["out"],
  685. defaults: ["gen_defaults1", "gen_defaults2"],
  686. }
  687. `
  688. result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
  689. gen := result.Module("gen", "").(*Module)
  690. expectedCmd := "cp in1 __SBOX_SANDBOX_DIR__/out/out"
  691. android.AssertStringEquals(t, "cmd", expectedCmd, gen.rawCommands[0])
  692. expectedSrcs := []string{"in1"}
  693. android.AssertDeepEquals(t, "srcs", expectedSrcs, gen.properties.Srcs)
  694. }
  695. func TestGenruleAllowMissingDependencies(t *testing.T) {
  696. bp := `
  697. output {
  698. name: "disabled",
  699. enabled: false,
  700. }
  701. genrule {
  702. name: "gen",
  703. srcs: [
  704. ":disabled",
  705. ],
  706. out: ["out"],
  707. cmd: "cat $(in) > $(out)",
  708. }
  709. `
  710. result := android.GroupFixturePreparers(
  711. prepareForGenRuleTest,
  712. android.FixtureModifyConfigAndContext(
  713. func(config android.Config, ctx *android.TestContext) {
  714. config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
  715. ctx.SetAllowMissingDependencies(true)
  716. })).RunTestWithBp(t, bp)
  717. gen := result.ModuleForTests("gen", "").Output("out")
  718. if gen.Rule != android.ErrorRule {
  719. t.Errorf("Expected missing dependency error rule for gen, got %q", gen.Rule.String())
  720. }
  721. }
  722. func TestGenruleOutputFiles(t *testing.T) {
  723. bp := `
  724. genrule {
  725. name: "gen",
  726. out: ["foo", "sub/bar"],
  727. cmd: "echo foo > $(location foo) && echo bar > $(location sub/bar)",
  728. }
  729. use_source {
  730. name: "gen_foo",
  731. srcs: [":gen{foo}"],
  732. }
  733. use_source {
  734. name: "gen_bar",
  735. srcs: [":gen{sub/bar}"],
  736. }
  737. use_source {
  738. name: "gen_all",
  739. srcs: [":gen"],
  740. }
  741. `
  742. result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
  743. android.AssertPathsRelativeToTopEquals(t,
  744. "genrule.tag with output",
  745. []string{"out/soong/.intermediates/gen/gen/foo"},
  746. result.ModuleForTests("gen_foo", "").Module().(*useSource).srcs)
  747. android.AssertPathsRelativeToTopEquals(t,
  748. "genrule.tag with output in subdir",
  749. []string{"out/soong/.intermediates/gen/gen/sub/bar"},
  750. result.ModuleForTests("gen_bar", "").Module().(*useSource).srcs)
  751. android.AssertPathsRelativeToTopEquals(t,
  752. "genrule.tag with all",
  753. []string{"out/soong/.intermediates/gen/gen/foo", "out/soong/.intermediates/gen/gen/sub/bar"},
  754. result.ModuleForTests("gen_all", "").Module().(*useSource).srcs)
  755. }
  756. func TestGenSrcsWithNonRootAndroidBpOutputFiles(t *testing.T) {
  757. result := android.GroupFixturePreparers(
  758. prepareForGenRuleTest,
  759. android.FixtureMergeMockFs(android.MockFS{
  760. "external-protos/path/Android.bp": []byte(`
  761. filegroup {
  762. name: "external-protos",
  763. srcs: ["baz/baz.proto", "bar.proto"],
  764. }
  765. `),
  766. "package-dir/Android.bp": []byte(`
  767. gensrcs {
  768. name: "module-name",
  769. cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
  770. srcs: [
  771. "src/foo.proto",
  772. ":external-protos",
  773. ],
  774. output_extension: "proto.h",
  775. }
  776. `),
  777. }),
  778. ).RunTest(t)
  779. exportedIncludeDir := "out/soong/.intermediates/package-dir/module-name/gen/gensrcs"
  780. gen := result.Module("module-name", "").(*Module)
  781. android.AssertPathsRelativeToTopEquals(
  782. t,
  783. "include path",
  784. []string{exportedIncludeDir},
  785. gen.exportedIncludeDirs,
  786. )
  787. android.AssertPathsRelativeToTopEquals(
  788. t,
  789. "files",
  790. []string{
  791. exportedIncludeDir + "/package-dir/src/foo.proto.h",
  792. exportedIncludeDir + "/external-protos/path/baz/baz.proto.h",
  793. exportedIncludeDir + "/external-protos/path/bar.proto.h",
  794. },
  795. gen.outputFiles,
  796. )
  797. }
  798. func TestGenSrcsWithSrcsFromExternalPackage(t *testing.T) {
  799. bp := `
  800. gensrcs {
  801. name: "module-name",
  802. cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
  803. srcs: [
  804. ":external-protos",
  805. ],
  806. output_extension: "proto.h",
  807. }
  808. `
  809. result := android.GroupFixturePreparers(
  810. prepareForGenRuleTest,
  811. android.FixtureMergeMockFs(android.MockFS{
  812. "external-protos/path/Android.bp": []byte(`
  813. filegroup {
  814. name: "external-protos",
  815. srcs: ["foo/foo.proto", "bar.proto"],
  816. }
  817. `),
  818. }),
  819. ).RunTestWithBp(t, bp)
  820. exportedIncludeDir := "out/soong/.intermediates/module-name/gen/gensrcs"
  821. gen := result.Module("module-name", "").(*Module)
  822. android.AssertPathsRelativeToTopEquals(
  823. t,
  824. "include path",
  825. []string{exportedIncludeDir},
  826. gen.exportedIncludeDirs,
  827. )
  828. android.AssertPathsRelativeToTopEquals(
  829. t,
  830. "files",
  831. []string{
  832. exportedIncludeDir + "/external-protos/path/foo/foo.proto.h",
  833. exportedIncludeDir + "/external-protos/path/bar.proto.h",
  834. },
  835. gen.outputFiles,
  836. )
  837. }
  838. func TestPrebuiltTool(t *testing.T) {
  839. testcases := []struct {
  840. name string
  841. bp string
  842. expectedToolName string
  843. }{
  844. {
  845. name: "source only",
  846. bp: `
  847. tool { name: "tool" }
  848. `,
  849. expectedToolName: "bin/tool",
  850. },
  851. {
  852. name: "prebuilt only",
  853. bp: `
  854. prebuilt_tool { name: "tool" }
  855. `,
  856. expectedToolName: "prebuilt_bin/tool",
  857. },
  858. {
  859. name: "source preferred",
  860. bp: `
  861. tool { name: "tool" }
  862. prebuilt_tool { name: "tool" }
  863. `,
  864. expectedToolName: "bin/tool",
  865. },
  866. {
  867. name: "prebuilt preferred",
  868. bp: `
  869. tool { name: "tool" }
  870. prebuilt_tool { name: "tool", prefer: true }
  871. `,
  872. expectedToolName: "prebuilt_bin/prebuilt_tool",
  873. },
  874. {
  875. name: "source disabled",
  876. bp: `
  877. tool { name: "tool", enabled: false }
  878. prebuilt_tool { name: "tool" }
  879. `,
  880. expectedToolName: "prebuilt_bin/prebuilt_tool",
  881. },
  882. }
  883. for _, test := range testcases {
  884. t.Run(test.name, func(t *testing.T) {
  885. result := prepareForGenRuleTest.RunTestWithBp(t, test.bp+`
  886. genrule {
  887. name: "gen",
  888. tools: ["tool"],
  889. out: ["foo"],
  890. cmd: "$(location tool)",
  891. }
  892. `)
  893. gen := result.Module("gen", "").(*Module)
  894. expectedCmd := "__SBOX_SANDBOX_DIR__/tools/out/" + test.expectedToolName
  895. android.AssertStringEquals(t, "command", expectedCmd, gen.rawCommands[0])
  896. })
  897. }
  898. }
  899. func TestGenruleWithBazel(t *testing.T) {
  900. bp := `
  901. genrule {
  902. name: "foo",
  903. out: ["one.txt", "two.txt"],
  904. bazel_module: { label: "//foo/bar:bar" },
  905. }
  906. `
  907. result := android.GroupFixturePreparers(
  908. prepareForGenRuleTest, android.FixtureModifyConfig(func(config android.Config) {
  909. config.BazelContext = android.MockBazelContext{
  910. OutputBaseDir: "outputbase",
  911. LabelToOutputFiles: map[string][]string{
  912. "//foo/bar:bar": []string{"bazelone.txt", "bazeltwo.txt"}}}
  913. })).RunTestWithBp(t, testGenruleBp()+bp)
  914. gen := result.Module("foo", "").(*Module)
  915. expectedOutputFiles := []string{"outputbase/execroot/__main__/bazelone.txt",
  916. "outputbase/execroot/__main__/bazeltwo.txt"}
  917. android.AssertDeepEquals(t, "output files", expectedOutputFiles, gen.outputFiles.Strings())
  918. android.AssertDeepEquals(t, "output deps", expectedOutputFiles, gen.outputDeps.Strings())
  919. }
  920. func TestGenruleWithGlobPaths(t *testing.T) {
  921. testcases := []struct {
  922. name string
  923. bp string
  924. additionalFiles android.MockFS
  925. expectedCmd string
  926. }{
  927. {
  928. name: "single file in directory with $ sign",
  929. bp: `
  930. genrule {
  931. name: "gen",
  932. srcs: ["inn*.txt"],
  933. out: ["out.txt"],
  934. cmd: "cp $(in) $(out)",
  935. }
  936. `,
  937. additionalFiles: android.MockFS{"inn$1.txt": nil},
  938. expectedCmd: "cp 'inn$1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
  939. },
  940. {
  941. name: "multiple file in directory with $ sign",
  942. bp: `
  943. genrule {
  944. name: "gen",
  945. srcs: ["inn*.txt"],
  946. out: ["."],
  947. cmd: "cp $(in) $(out)",
  948. }
  949. `,
  950. additionalFiles: android.MockFS{"inn$1.txt": nil, "inn$2.txt": nil},
  951. expectedCmd: "cp 'inn$1.txt' 'inn$2.txt' __SBOX_SANDBOX_DIR__/out",
  952. },
  953. {
  954. name: "file in directory with other shell unsafe character",
  955. bp: `
  956. genrule {
  957. name: "gen",
  958. srcs: ["inn*.txt"],
  959. out: ["out.txt"],
  960. cmd: "cp $(in) $(out)",
  961. }
  962. `,
  963. additionalFiles: android.MockFS{"inn@1.txt": nil},
  964. expectedCmd: "cp 'inn@1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
  965. },
  966. {
  967. name: "glob location param with filepath containing $",
  968. bp: `
  969. genrule {
  970. name: "gen",
  971. srcs: ["**/inn*"],
  972. out: ["."],
  973. cmd: "cp $(in) $(location **/inn*)",
  974. }
  975. `,
  976. additionalFiles: android.MockFS{"a/inn$1.txt": nil},
  977. expectedCmd: "cp 'a/inn$1.txt' 'a/inn$1.txt'",
  978. },
  979. {
  980. name: "glob locations param with filepath containing $",
  981. bp: `
  982. genrule {
  983. name: "gen",
  984. tool_files: ["**/inn*"],
  985. out: ["out.txt"],
  986. cmd: "cp $(locations **/inn*) $(out)",
  987. }
  988. `,
  989. additionalFiles: android.MockFS{"a/inn$1.txt": nil},
  990. expectedCmd: "cp '__SBOX_SANDBOX_DIR__/tools/src/a/inn$1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
  991. },
  992. }
  993. for _, test := range testcases {
  994. t.Run(test.name, func(t *testing.T) {
  995. result := android.GroupFixturePreparers(
  996. prepareForGenRuleTest,
  997. android.FixtureMergeMockFs(test.additionalFiles),
  998. ).RunTestWithBp(t, test.bp)
  999. gen := result.Module("gen", "").(*Module)
  1000. android.AssertStringEquals(t, "command", test.expectedCmd, gen.rawCommands[0])
  1001. })
  1002. }
  1003. }
  1004. type testTool struct {
  1005. android.ModuleBase
  1006. outputFile android.Path
  1007. }
  1008. func toolFactory() android.Module {
  1009. module := &testTool{}
  1010. android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
  1011. return module
  1012. }
  1013. func (t *testTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
  1014. t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
  1015. }
  1016. func (t *testTool) HostToolPath() android.OptionalPath {
  1017. return android.OptionalPathForPath(t.outputFile)
  1018. }
  1019. type prebuiltTestTool struct {
  1020. android.ModuleBase
  1021. prebuilt android.Prebuilt
  1022. testTool
  1023. }
  1024. func (p *prebuiltTestTool) Name() string {
  1025. return p.prebuilt.Name(p.ModuleBase.Name())
  1026. }
  1027. func (p *prebuiltTestTool) Prebuilt() *android.Prebuilt {
  1028. return &p.prebuilt
  1029. }
  1030. func (t *prebuiltTestTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
  1031. t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "prebuilt_bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
  1032. }
  1033. func prebuiltToolFactory() android.Module {
  1034. module := &prebuiltTestTool{}
  1035. android.InitPrebuiltModuleWithoutSrcs(module)
  1036. android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
  1037. return module
  1038. }
  1039. var _ android.HostToolProvider = (*testTool)(nil)
  1040. var _ android.HostToolProvider = (*prebuiltTestTool)(nil)
  1041. type testOutputProducer struct {
  1042. android.ModuleBase
  1043. outputFile android.Path
  1044. }
  1045. func outputProducerFactory() android.Module {
  1046. module := &testOutputProducer{}
  1047. android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
  1048. return module
  1049. }
  1050. func (t *testOutputProducer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
  1051. t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
  1052. }
  1053. func (t *testOutputProducer) OutputFiles(tag string) (android.Paths, error) {
  1054. return android.Paths{t.outputFile}, nil
  1055. }
  1056. var _ android.OutputFileProducer = (*testOutputProducer)(nil)
  1057. type useSource struct {
  1058. android.ModuleBase
  1059. props struct {
  1060. Srcs []string `android:"path"`
  1061. }
  1062. srcs android.Paths
  1063. }
  1064. func (s *useSource) GenerateAndroidBuildActions(ctx android.ModuleContext) {
  1065. s.srcs = android.PathsForModuleSrc(ctx, s.props.Srcs)
  1066. }
  1067. func useSourceFactory() android.Module {
  1068. module := &useSource{}
  1069. module.AddProperties(&module.props)
  1070. android.InitAndroidModule(module)
  1071. return module
  1072. }