rule_builder_test.go 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794
  1. // Copyright 2019 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 android
  15. import (
  16. "crypto/sha256"
  17. "encoding/hex"
  18. "fmt"
  19. "path/filepath"
  20. "regexp"
  21. "strings"
  22. "testing"
  23. "github.com/google/blueprint"
  24. "android/soong/shared"
  25. )
  26. func builderContext() BuilderContext {
  27. return BuilderContextForTesting(TestConfig("out", nil, "", map[string][]byte{
  28. "ld": nil,
  29. "a.o": nil,
  30. "b.o": nil,
  31. "cp": nil,
  32. "a": nil,
  33. "b": nil,
  34. "ls": nil,
  35. "ln": nil,
  36. "turbine": nil,
  37. "java": nil,
  38. "javac": nil,
  39. }))
  40. }
  41. func ExampleRuleBuilder() {
  42. ctx := builderContext()
  43. rule := NewRuleBuilder(pctx, ctx)
  44. rule.Command().
  45. Tool(PathForSource(ctx, "ld")).
  46. Inputs(PathsForTesting("a.o", "b.o")).
  47. FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
  48. rule.Command().Text("echo success")
  49. // To add the command to the build graph:
  50. // rule.Build("link", "link")
  51. fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
  52. fmt.Printf("tools: %q\n", rule.Tools())
  53. fmt.Printf("inputs: %q\n", rule.Inputs())
  54. fmt.Printf("outputs: %q\n", rule.Outputs())
  55. // Output:
  56. // commands: "ld a.o b.o -o out/soong/linked && echo success"
  57. // tools: ["ld"]
  58. // inputs: ["a.o" "b.o"]
  59. // outputs: ["out/soong/linked"]
  60. }
  61. func ExampleRuleBuilder_SymlinkOutputs() {
  62. ctx := builderContext()
  63. rule := NewRuleBuilder(pctx, ctx)
  64. rule.Command().
  65. Tool(PathForSource(ctx, "ln")).
  66. FlagWithInput("-s ", PathForTesting("a.o")).
  67. SymlinkOutput(PathForOutput(ctx, "a"))
  68. rule.Command().Text("cp out/soong/a out/soong/b").
  69. ImplicitSymlinkOutput(PathForOutput(ctx, "b"))
  70. fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
  71. fmt.Printf("tools: %q\n", rule.Tools())
  72. fmt.Printf("inputs: %q\n", rule.Inputs())
  73. fmt.Printf("outputs: %q\n", rule.Outputs())
  74. fmt.Printf("symlink_outputs: %q\n", rule.SymlinkOutputs())
  75. // Output:
  76. // commands: "ln -s a.o out/soong/a && cp out/soong/a out/soong/b"
  77. // tools: ["ln"]
  78. // inputs: ["a.o"]
  79. // outputs: ["out/soong/a" "out/soong/b"]
  80. // symlink_outputs: ["out/soong/a" "out/soong/b"]
  81. }
  82. func ExampleRuleBuilder_Temporary() {
  83. ctx := builderContext()
  84. rule := NewRuleBuilder(pctx, ctx)
  85. rule.Command().
  86. Tool(PathForSource(ctx, "cp")).
  87. Input(PathForSource(ctx, "a")).
  88. Output(PathForOutput(ctx, "b"))
  89. rule.Command().
  90. Tool(PathForSource(ctx, "cp")).
  91. Input(PathForOutput(ctx, "b")).
  92. Output(PathForOutput(ctx, "c"))
  93. rule.Temporary(PathForOutput(ctx, "b"))
  94. fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
  95. fmt.Printf("tools: %q\n", rule.Tools())
  96. fmt.Printf("inputs: %q\n", rule.Inputs())
  97. fmt.Printf("outputs: %q\n", rule.Outputs())
  98. // Output:
  99. // commands: "cp a out/soong/b && cp out/soong/b out/soong/c"
  100. // tools: ["cp"]
  101. // inputs: ["a"]
  102. // outputs: ["out/soong/c"]
  103. }
  104. func ExampleRuleBuilder_DeleteTemporaryFiles() {
  105. ctx := builderContext()
  106. rule := NewRuleBuilder(pctx, ctx)
  107. rule.Command().
  108. Tool(PathForSource(ctx, "cp")).
  109. Input(PathForSource(ctx, "a")).
  110. Output(PathForOutput(ctx, "b"))
  111. rule.Command().
  112. Tool(PathForSource(ctx, "cp")).
  113. Input(PathForOutput(ctx, "b")).
  114. Output(PathForOutput(ctx, "c"))
  115. rule.Temporary(PathForOutput(ctx, "b"))
  116. rule.DeleteTemporaryFiles()
  117. fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
  118. fmt.Printf("tools: %q\n", rule.Tools())
  119. fmt.Printf("inputs: %q\n", rule.Inputs())
  120. fmt.Printf("outputs: %q\n", rule.Outputs())
  121. // Output:
  122. // commands: "cp a out/soong/b && cp out/soong/b out/soong/c && rm -f out/soong/b"
  123. // tools: ["cp"]
  124. // inputs: ["a"]
  125. // outputs: ["out/soong/c"]
  126. }
  127. func ExampleRuleBuilder_Installs() {
  128. ctx := builderContext()
  129. rule := NewRuleBuilder(pctx, ctx)
  130. out := PathForOutput(ctx, "linked")
  131. rule.Command().
  132. Tool(PathForSource(ctx, "ld")).
  133. Inputs(PathsForTesting("a.o", "b.o")).
  134. FlagWithOutput("-o ", out)
  135. rule.Install(out, "/bin/linked")
  136. rule.Install(out, "/sbin/linked")
  137. fmt.Printf("rule.Installs().String() = %q\n", rule.Installs().String())
  138. // Output:
  139. // rule.Installs().String() = "out/soong/linked:/bin/linked out/soong/linked:/sbin/linked"
  140. }
  141. func ExampleRuleBuilderCommand() {
  142. ctx := builderContext()
  143. rule := NewRuleBuilder(pctx, ctx)
  144. // chained
  145. rule.Command().
  146. Tool(PathForSource(ctx, "ld")).
  147. Inputs(PathsForTesting("a.o", "b.o")).
  148. FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
  149. // unchained
  150. cmd := rule.Command()
  151. cmd.Tool(PathForSource(ctx, "ld"))
  152. cmd.Inputs(PathsForTesting("a.o", "b.o"))
  153. cmd.FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
  154. // mixed:
  155. cmd = rule.Command().Tool(PathForSource(ctx, "ld"))
  156. cmd.Inputs(PathsForTesting("a.o", "b.o"))
  157. cmd.FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
  158. }
  159. func ExampleRuleBuilderCommand_Flag() {
  160. ctx := builderContext()
  161. fmt.Println(NewRuleBuilder(pctx, ctx).Command().
  162. Tool(PathForSource(ctx, "ls")).Flag("-l"))
  163. // Output:
  164. // ls -l
  165. }
  166. func ExampleRuleBuilderCommand_Flags() {
  167. ctx := builderContext()
  168. fmt.Println(NewRuleBuilder(pctx, ctx).Command().
  169. Tool(PathForSource(ctx, "ls")).Flags([]string{"-l", "-a"}))
  170. // Output:
  171. // ls -l -a
  172. }
  173. func ExampleRuleBuilderCommand_FlagWithArg() {
  174. ctx := builderContext()
  175. fmt.Println(NewRuleBuilder(pctx, ctx).Command().
  176. Tool(PathForSource(ctx, "ls")).
  177. FlagWithArg("--sort=", "time"))
  178. // Output:
  179. // ls --sort=time
  180. }
  181. func ExampleRuleBuilderCommand_FlagForEachArg() {
  182. ctx := builderContext()
  183. fmt.Println(NewRuleBuilder(pctx, ctx).Command().
  184. Tool(PathForSource(ctx, "ls")).
  185. FlagForEachArg("--sort=", []string{"time", "size"}))
  186. // Output:
  187. // ls --sort=time --sort=size
  188. }
  189. func ExampleRuleBuilderCommand_FlagForEachInput() {
  190. ctx := builderContext()
  191. fmt.Println(NewRuleBuilder(pctx, ctx).Command().
  192. Tool(PathForSource(ctx, "turbine")).
  193. FlagForEachInput("--classpath ", PathsForTesting("a.jar", "b.jar")))
  194. // Output:
  195. // turbine --classpath a.jar --classpath b.jar
  196. }
  197. func ExampleRuleBuilderCommand_FlagWithInputList() {
  198. ctx := builderContext()
  199. fmt.Println(NewRuleBuilder(pctx, ctx).Command().
  200. Tool(PathForSource(ctx, "java")).
  201. FlagWithInputList("-classpath=", PathsForTesting("a.jar", "b.jar"), ":"))
  202. // Output:
  203. // java -classpath=a.jar:b.jar
  204. }
  205. func ExampleRuleBuilderCommand_FlagWithInput() {
  206. ctx := builderContext()
  207. fmt.Println(NewRuleBuilder(pctx, ctx).Command().
  208. Tool(PathForSource(ctx, "java")).
  209. FlagWithInput("-classpath=", PathForSource(ctx, "a")))
  210. // Output:
  211. // java -classpath=a
  212. }
  213. func ExampleRuleBuilderCommand_FlagWithList() {
  214. ctx := builderContext()
  215. fmt.Println(NewRuleBuilder(pctx, ctx).Command().
  216. Tool(PathForSource(ctx, "ls")).
  217. FlagWithList("--sort=", []string{"time", "size"}, ","))
  218. // Output:
  219. // ls --sort=time,size
  220. }
  221. func ExampleRuleBuilderCommand_FlagWithRspFileInputList() {
  222. ctx := builderContext()
  223. fmt.Println(NewRuleBuilder(pctx, ctx).Command().
  224. Tool(PathForSource(ctx, "javac")).
  225. FlagWithRspFileInputList("@", PathForOutput(ctx, "foo.rsp"), PathsForTesting("a.java", "b.java")).
  226. String())
  227. // Output:
  228. // javac @out/soong/foo.rsp
  229. }
  230. func ExampleRuleBuilderCommand_String() {
  231. ctx := builderContext()
  232. fmt.Println(NewRuleBuilder(pctx, ctx).Command().
  233. Text("FOO=foo").
  234. Text("echo $FOO").
  235. String())
  236. // Output:
  237. // FOO=foo echo $FOO
  238. }
  239. func TestRuleBuilder(t *testing.T) {
  240. fs := map[string][]byte{
  241. "dep_fixer": nil,
  242. "input": nil,
  243. "Implicit": nil,
  244. "Input": nil,
  245. "OrderOnly": nil,
  246. "OrderOnlys": nil,
  247. "Tool": nil,
  248. "input2": nil,
  249. "tool2": nil,
  250. "input3": nil,
  251. }
  252. pathCtx := PathContextForTesting(TestConfig("out_local", nil, "", fs))
  253. ctx := builderContextForTests{
  254. PathContext: pathCtx,
  255. }
  256. addCommands := func(rule *RuleBuilder) {
  257. cmd := rule.Command().
  258. DepFile(PathForOutput(ctx, "module/DepFile")).
  259. Flag("Flag").
  260. FlagWithArg("FlagWithArg=", "arg").
  261. FlagWithDepFile("FlagWithDepFile=", PathForOutput(ctx, "module/depfile")).
  262. FlagWithInput("FlagWithInput=", PathForSource(ctx, "input")).
  263. FlagWithOutput("FlagWithOutput=", PathForOutput(ctx, "module/output")).
  264. FlagWithRspFileInputList("FlagWithRspFileInputList=", PathForOutput(ctx, "rsp"),
  265. Paths{
  266. PathForSource(ctx, "RspInput"),
  267. PathForOutput(ctx, "other/RspOutput2"),
  268. }).
  269. Implicit(PathForSource(ctx, "Implicit")).
  270. ImplicitDepFile(PathForOutput(ctx, "module/ImplicitDepFile")).
  271. ImplicitOutput(PathForOutput(ctx, "module/ImplicitOutput")).
  272. Input(PathForSource(ctx, "Input")).
  273. Output(PathForOutput(ctx, "module/Output")).
  274. OrderOnly(PathForSource(ctx, "OrderOnly")).
  275. Validation(PathForSource(ctx, "Validation")).
  276. SymlinkOutput(PathForOutput(ctx, "module/SymlinkOutput")).
  277. ImplicitSymlinkOutput(PathForOutput(ctx, "module/ImplicitSymlinkOutput")).
  278. Text("Text").
  279. Tool(PathForSource(ctx, "Tool"))
  280. rule.Command().
  281. Text("command2").
  282. DepFile(PathForOutput(ctx, "module/depfile2")).
  283. Input(PathForSource(ctx, "input2")).
  284. Output(PathForOutput(ctx, "module/output2")).
  285. OrderOnlys(PathsForSource(ctx, []string{"OrderOnlys"})).
  286. Validations(PathsForSource(ctx, []string{"Validations"})).
  287. Tool(PathForSource(ctx, "tool2"))
  288. // Test updates to the first command after the second command has been started
  289. cmd.Text("after command2")
  290. // Test updating a command when the previous update did not replace the cmd variable
  291. cmd.Text("old cmd")
  292. // Test a command that uses the output of a previous command as an input
  293. rule.Command().
  294. Text("command3").
  295. Input(PathForSource(ctx, "input3")).
  296. Input(PathForOutput(ctx, "module/output2")).
  297. Output(PathForOutput(ctx, "module/output3")).
  298. Text(cmd.PathForInput(PathForSource(ctx, "input3"))).
  299. Text(cmd.PathForOutput(PathForOutput(ctx, "module/output2")))
  300. }
  301. wantInputs := PathsForSource(ctx, []string{"Implicit", "Input", "input", "input2", "input3"})
  302. wantRspFileInputs := Paths{PathForSource(ctx, "RspInput"),
  303. PathForOutput(ctx, "other/RspOutput2")}
  304. wantOutputs := PathsForOutput(ctx, []string{
  305. "module/ImplicitOutput", "module/ImplicitSymlinkOutput", "module/Output", "module/SymlinkOutput",
  306. "module/output", "module/output2", "module/output3"})
  307. wantDepFiles := PathsForOutput(ctx, []string{
  308. "module/DepFile", "module/depfile", "module/ImplicitDepFile", "module/depfile2"})
  309. wantTools := PathsForSource(ctx, []string{"Tool", "tool2"})
  310. wantOrderOnlys := PathsForSource(ctx, []string{"OrderOnly", "OrderOnlys"})
  311. wantValidations := PathsForSource(ctx, []string{"Validation", "Validations"})
  312. wantSymlinkOutputs := PathsForOutput(ctx, []string{
  313. "module/ImplicitSymlinkOutput", "module/SymlinkOutput"})
  314. t.Run("normal", func(t *testing.T) {
  315. rule := NewRuleBuilder(pctx, ctx)
  316. addCommands(rule)
  317. wantCommands := []string{
  318. "out_local/soong/module/DepFile Flag FlagWithArg=arg FlagWithDepFile=out_local/soong/module/depfile " +
  319. "FlagWithInput=input FlagWithOutput=out_local/soong/module/output FlagWithRspFileInputList=out_local/soong/rsp " +
  320. "Input out_local/soong/module/Output out_local/soong/module/SymlinkOutput Text Tool after command2 old cmd",
  321. "command2 out_local/soong/module/depfile2 input2 out_local/soong/module/output2 tool2",
  322. "command3 input3 out_local/soong/module/output2 out_local/soong/module/output3 input3 out_local/soong/module/output2",
  323. }
  324. wantDepMergerCommand := "out_local/soong/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer " +
  325. "out_local/soong/module/DepFile out_local/soong/module/depfile out_local/soong/module/ImplicitDepFile out_local/soong/module/depfile2"
  326. AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
  327. AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs())
  328. AssertDeepEquals(t, "rule.RspfileInputs()", wantRspFileInputs, rule.RspFileInputs())
  329. AssertDeepEquals(t, "rule.Outputs()", wantOutputs, rule.Outputs())
  330. AssertDeepEquals(t, "rule.SymlinkOutputs()", wantSymlinkOutputs, rule.SymlinkOutputs())
  331. AssertDeepEquals(t, "rule.DepFiles()", wantDepFiles, rule.DepFiles())
  332. AssertDeepEquals(t, "rule.Tools()", wantTools, rule.Tools())
  333. AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
  334. AssertDeepEquals(t, "rule.Validations()", wantValidations, rule.Validations())
  335. AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String())
  336. })
  337. t.Run("sbox", func(t *testing.T) {
  338. rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, "module"),
  339. PathForOutput(ctx, "sbox.textproto"))
  340. addCommands(rule)
  341. wantCommands := []string{
  342. "__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile " +
  343. "FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output " +
  344. "FlagWithRspFileInputList=out_local/soong/rsp Input __SBOX_SANDBOX_DIR__/out/Output " +
  345. "__SBOX_SANDBOX_DIR__/out/SymlinkOutput Text Tool after command2 old cmd",
  346. "command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 tool2",
  347. "command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3 input3 __SBOX_SANDBOX_DIR__/out/output2",
  348. }
  349. wantDepMergerCommand := "out_local/soong/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
  350. AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
  351. AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs())
  352. AssertDeepEquals(t, "rule.RspfileInputs()", wantRspFileInputs, rule.RspFileInputs())
  353. AssertDeepEquals(t, "rule.Outputs()", wantOutputs, rule.Outputs())
  354. AssertDeepEquals(t, "rule.SymlinkOutputs()", wantSymlinkOutputs, rule.SymlinkOutputs())
  355. AssertDeepEquals(t, "rule.DepFiles()", wantDepFiles, rule.DepFiles())
  356. AssertDeepEquals(t, "rule.Tools()", wantTools, rule.Tools())
  357. AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
  358. AssertDeepEquals(t, "rule.Validations()", wantValidations, rule.Validations())
  359. AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String())
  360. })
  361. t.Run("sbox tools", func(t *testing.T) {
  362. rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, "module"),
  363. PathForOutput(ctx, "sbox.textproto")).SandboxTools()
  364. addCommands(rule)
  365. wantCommands := []string{
  366. "__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile " +
  367. "FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output " +
  368. "FlagWithRspFileInputList=out_local/soong/rsp Input __SBOX_SANDBOX_DIR__/out/Output " +
  369. "__SBOX_SANDBOX_DIR__/out/SymlinkOutput Text __SBOX_SANDBOX_DIR__/tools/src/Tool after command2 old cmd",
  370. "command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/tools/src/tool2",
  371. "command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3 input3 __SBOX_SANDBOX_DIR__/out/output2",
  372. }
  373. wantDepMergerCommand := "__SBOX_SANDBOX_DIR__/tools/out/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
  374. AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
  375. AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs())
  376. AssertDeepEquals(t, "rule.RspfileInputs()", wantRspFileInputs, rule.RspFileInputs())
  377. AssertDeepEquals(t, "rule.Outputs()", wantOutputs, rule.Outputs())
  378. AssertDeepEquals(t, "rule.SymlinkOutputs()", wantSymlinkOutputs, rule.SymlinkOutputs())
  379. AssertDeepEquals(t, "rule.DepFiles()", wantDepFiles, rule.DepFiles())
  380. AssertDeepEquals(t, "rule.Tools()", wantTools, rule.Tools())
  381. AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
  382. AssertDeepEquals(t, "rule.Validations()", wantValidations, rule.Validations())
  383. AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String())
  384. })
  385. t.Run("sbox inputs", func(t *testing.T) {
  386. rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, "module"),
  387. PathForOutput(ctx, "sbox.textproto")).SandboxInputs()
  388. addCommands(rule)
  389. wantCommands := []string{
  390. "__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile " +
  391. "FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output " +
  392. "FlagWithRspFileInputList=__SBOX_SANDBOX_DIR__/out/rsp Input __SBOX_SANDBOX_DIR__/out/Output " +
  393. "__SBOX_SANDBOX_DIR__/out/SymlinkOutput Text __SBOX_SANDBOX_DIR__/tools/src/Tool after command2 old cmd",
  394. "command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/tools/src/tool2",
  395. "command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3 input3 __SBOX_SANDBOX_DIR__/out/output2",
  396. }
  397. wantDepMergerCommand := "__SBOX_SANDBOX_DIR__/tools/out/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
  398. AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
  399. AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs())
  400. AssertDeepEquals(t, "rule.RspfileInputs()", wantRspFileInputs, rule.RspFileInputs())
  401. AssertDeepEquals(t, "rule.Outputs()", wantOutputs, rule.Outputs())
  402. AssertDeepEquals(t, "rule.SymlinkOutputs()", wantSymlinkOutputs, rule.SymlinkOutputs())
  403. AssertDeepEquals(t, "rule.DepFiles()", wantDepFiles, rule.DepFiles())
  404. AssertDeepEquals(t, "rule.Tools()", wantTools, rule.Tools())
  405. AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
  406. AssertDeepEquals(t, "rule.Validations()", wantValidations, rule.Validations())
  407. AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String())
  408. })
  409. }
  410. func testRuleBuilderFactory() Module {
  411. module := &testRuleBuilderModule{}
  412. module.AddProperties(&module.properties)
  413. InitAndroidModule(module)
  414. return module
  415. }
  416. type testRuleBuilderModule struct {
  417. ModuleBase
  418. properties struct {
  419. Srcs []string
  420. Restat bool
  421. Sbox bool
  422. Sbox_inputs bool
  423. }
  424. }
  425. func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
  426. in := PathsForSource(ctx, t.properties.Srcs)
  427. implicit := PathForSource(ctx, "implicit")
  428. orderOnly := PathForSource(ctx, "orderonly")
  429. validation := PathForSource(ctx, "validation")
  430. out := PathForModuleOut(ctx, "gen", ctx.ModuleName())
  431. outDep := PathForModuleOut(ctx, "gen", ctx.ModuleName()+".d")
  432. outDir := PathForModuleOut(ctx, "gen")
  433. rspFile := PathForModuleOut(ctx, "rsp")
  434. rspFile2 := PathForModuleOut(ctx, "rsp2")
  435. rspFileContents := PathsForSource(ctx, []string{"rsp_in"})
  436. rspFileContents2 := PathsForSource(ctx, []string{"rsp_in2"})
  437. manifestPath := PathForModuleOut(ctx, "sbox.textproto")
  438. testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, out, outDep, outDir,
  439. manifestPath, t.properties.Restat, t.properties.Sbox, t.properties.Sbox_inputs,
  440. rspFile, rspFileContents, rspFile2, rspFileContents2)
  441. }
  442. type testRuleBuilderSingleton struct{}
  443. func testRuleBuilderSingletonFactory() Singleton {
  444. return &testRuleBuilderSingleton{}
  445. }
  446. func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) {
  447. in := PathsForSource(ctx, []string{"in"})
  448. implicit := PathForSource(ctx, "implicit")
  449. orderOnly := PathForSource(ctx, "orderonly")
  450. validation := PathForSource(ctx, "validation")
  451. out := PathForOutput(ctx, "singleton/gen/baz")
  452. outDep := PathForOutput(ctx, "singleton/gen/baz.d")
  453. outDir := PathForOutput(ctx, "singleton/gen")
  454. rspFile := PathForOutput(ctx, "singleton/rsp")
  455. rspFile2 := PathForOutput(ctx, "singleton/rsp2")
  456. rspFileContents := PathsForSource(ctx, []string{"rsp_in"})
  457. rspFileContents2 := PathsForSource(ctx, []string{"rsp_in2"})
  458. manifestPath := PathForOutput(ctx, "singleton/sbox.textproto")
  459. testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, out, outDep, outDir,
  460. manifestPath, true, false, false,
  461. rspFile, rspFileContents, rspFile2, rspFileContents2)
  462. }
  463. func testRuleBuilder_Build(ctx BuilderContext, in Paths, implicit, orderOnly, validation Path,
  464. out, outDep, outDir, manifestPath WritablePath,
  465. restat, sbox, sboxInputs bool,
  466. rspFile WritablePath, rspFileContents Paths, rspFile2 WritablePath, rspFileContents2 Paths) {
  467. rule := NewRuleBuilder(pctx, ctx)
  468. if sbox {
  469. rule.Sbox(outDir, manifestPath)
  470. if sboxInputs {
  471. rule.SandboxInputs()
  472. }
  473. }
  474. rule.Command().
  475. Tool(PathForSource(ctx, "cp")).
  476. Inputs(in).
  477. Implicit(implicit).
  478. OrderOnly(orderOnly).
  479. Validation(validation).
  480. Output(out).
  481. ImplicitDepFile(outDep).
  482. FlagWithRspFileInputList("@", rspFile, rspFileContents).
  483. FlagWithRspFileInputList("@", rspFile2, rspFileContents2)
  484. if restat {
  485. rule.Restat()
  486. }
  487. rule.Build("rule", "desc")
  488. }
  489. var prepareForRuleBuilderTest = FixtureRegisterWithContext(func(ctx RegistrationContext) {
  490. ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
  491. ctx.RegisterSingletonType("rule_builder_test", testRuleBuilderSingletonFactory)
  492. })
  493. func TestRuleBuilder_Build(t *testing.T) {
  494. fs := MockFS{
  495. "in": nil,
  496. "cp": nil,
  497. }
  498. bp := `
  499. rule_builder_test {
  500. name: "foo",
  501. srcs: ["in"],
  502. restat: true,
  503. }
  504. rule_builder_test {
  505. name: "foo_sbox",
  506. srcs: ["in"],
  507. sbox: true,
  508. }
  509. rule_builder_test {
  510. name: "foo_sbox_inputs",
  511. srcs: ["in"],
  512. sbox: true,
  513. sbox_inputs: true,
  514. }
  515. `
  516. result := GroupFixturePreparers(
  517. prepareForRuleBuilderTest,
  518. FixtureWithRootAndroidBp(bp),
  519. fs.AddToFixture(),
  520. ).RunTest(t)
  521. check := func(t *testing.T, params TestingBuildParams, rspFile2Params TestingBuildParams,
  522. wantCommand, wantOutput, wantDepfile, wantRspFile, wantRspFile2 string,
  523. wantRestat bool, extraImplicits, extraCmdDeps []string) {
  524. t.Helper()
  525. command := params.RuleParams.Command
  526. re := regexp.MustCompile(" # hash of input list: [a-z0-9]*$")
  527. command = re.ReplaceAllLiteralString(command, "")
  528. AssertStringEquals(t, "RuleParams.Command", wantCommand, command)
  529. wantDeps := append([]string{"cp"}, extraCmdDeps...)
  530. AssertArrayString(t, "RuleParams.CommandDeps", wantDeps, params.RuleParams.CommandDeps)
  531. AssertBoolEquals(t, "RuleParams.Restat", wantRestat, params.RuleParams.Restat)
  532. wantInputs := []string{"rsp_in"}
  533. AssertArrayString(t, "Inputs", wantInputs, params.Inputs.Strings())
  534. wantImplicits := append([]string{"implicit", "in"}, extraImplicits...)
  535. // The second rsp file and the files listed in it should be in implicits
  536. wantImplicits = append(wantImplicits, "rsp_in2", wantRspFile2)
  537. AssertPathsRelativeToTopEquals(t, "Implicits", wantImplicits, params.Implicits)
  538. wantOrderOnlys := []string{"orderonly"}
  539. AssertPathsRelativeToTopEquals(t, "OrderOnly", wantOrderOnlys, params.OrderOnly)
  540. wantValidations := []string{"validation"}
  541. AssertPathsRelativeToTopEquals(t, "Validations", wantValidations, params.Validations)
  542. wantRspFileContent := "$in"
  543. AssertStringEquals(t, "RspfileContent", wantRspFileContent, params.RuleParams.RspfileContent)
  544. AssertStringEquals(t, "Rspfile", wantRspFile, params.RuleParams.Rspfile)
  545. AssertPathRelativeToTopEquals(t, "Output", wantOutput, params.Output)
  546. if len(params.ImplicitOutputs) != 0 {
  547. t.Errorf("want ImplicitOutputs = [], got %q", params.ImplicitOutputs.Strings())
  548. }
  549. AssertPathRelativeToTopEquals(t, "Depfile", wantDepfile, params.Depfile)
  550. if params.Deps != blueprint.DepsGCC {
  551. t.Errorf("want Deps = %q, got %q", blueprint.DepsGCC, params.Deps)
  552. }
  553. rspFile2Content := ContentFromFileRuleForTests(t, rspFile2Params)
  554. AssertStringEquals(t, "rspFile2 content", "rsp_in2\n", rspFile2Content)
  555. }
  556. t.Run("module", func(t *testing.T) {
  557. outFile := "out/soong/.intermediates/foo/gen/foo"
  558. rspFile := "out/soong/.intermediates/foo/rsp"
  559. rspFile2 := "out/soong/.intermediates/foo/rsp2"
  560. module := result.ModuleForTests("foo", "")
  561. check(t, module.Rule("rule"), module.Output(rspFile2),
  562. "cp in "+outFile+" @"+rspFile+" @"+rspFile2,
  563. outFile, outFile+".d", rspFile, rspFile2, true, nil, nil)
  564. })
  565. t.Run("sbox", func(t *testing.T) {
  566. outDir := "out/soong/.intermediates/foo_sbox"
  567. sboxOutDir := filepath.Join(outDir, "gen")
  568. outFile := filepath.Join(sboxOutDir, "foo_sbox")
  569. depFile := filepath.Join(sboxOutDir, "foo_sbox.d")
  570. rspFile := filepath.Join(outDir, "rsp")
  571. rspFile2 := filepath.Join(outDir, "rsp2")
  572. manifest := filepath.Join(outDir, "sbox.textproto")
  573. sbox := filepath.Join("out", "soong", "host", result.Config.PrebuiltOS(), "bin/sbox")
  574. sandboxPath := shared.TempDirForOutDir("out/soong")
  575. cmd := sbox + ` --sandbox-path ` + sandboxPath + ` --output-dir ` + sboxOutDir + ` --manifest ` + manifest
  576. module := result.ModuleForTests("foo_sbox", "")
  577. check(t, module.Output("gen/foo_sbox"), module.Output(rspFile2),
  578. cmd, outFile, depFile, rspFile, rspFile2, false, []string{manifest}, []string{sbox})
  579. })
  580. t.Run("sbox_inputs", func(t *testing.T) {
  581. outDir := "out/soong/.intermediates/foo_sbox_inputs"
  582. sboxOutDir := filepath.Join(outDir, "gen")
  583. outFile := filepath.Join(sboxOutDir, "foo_sbox_inputs")
  584. depFile := filepath.Join(sboxOutDir, "foo_sbox_inputs.d")
  585. rspFile := filepath.Join(outDir, "rsp")
  586. rspFile2 := filepath.Join(outDir, "rsp2")
  587. manifest := filepath.Join(outDir, "sbox.textproto")
  588. sbox := filepath.Join("out", "soong", "host", result.Config.PrebuiltOS(), "bin/sbox")
  589. sandboxPath := shared.TempDirForOutDir("out/soong")
  590. cmd := sbox + ` --sandbox-path ` + sandboxPath + ` --output-dir ` + sboxOutDir + ` --manifest ` + manifest
  591. module := result.ModuleForTests("foo_sbox_inputs", "")
  592. check(t, module.Output("gen/foo_sbox_inputs"), module.Output(rspFile2),
  593. cmd, outFile, depFile, rspFile, rspFile2, false, []string{manifest}, []string{sbox})
  594. })
  595. t.Run("singleton", func(t *testing.T) {
  596. outFile := filepath.Join("out/soong/singleton/gen/baz")
  597. rspFile := filepath.Join("out/soong/singleton/rsp")
  598. rspFile2 := filepath.Join("out/soong/singleton/rsp2")
  599. singleton := result.SingletonForTests("rule_builder_test")
  600. check(t, singleton.Rule("rule"), singleton.Output(rspFile2),
  601. "cp in "+outFile+" @"+rspFile+" @"+rspFile2,
  602. outFile, outFile+".d", rspFile, rspFile2, true, nil, nil)
  603. })
  604. }
  605. func TestRuleBuilderHashInputs(t *testing.T) {
  606. // The basic idea here is to verify that the command (in the case of a
  607. // non-sbox rule) or the sbox textproto manifest contain a hash of the
  608. // inputs.
  609. // By including a hash of the inputs, we cause the rule to re-run if
  610. // the list of inputs changes because the command line or a dependency
  611. // changes.
  612. hashOf := func(s string) string {
  613. sum := sha256.Sum256([]byte(s))
  614. return hex.EncodeToString(sum[:])
  615. }
  616. bp := `
  617. rule_builder_test {
  618. name: "hash0",
  619. srcs: ["in1.txt", "in2.txt"],
  620. }
  621. rule_builder_test {
  622. name: "hash0_sbox",
  623. srcs: ["in1.txt", "in2.txt"],
  624. sbox: true,
  625. }
  626. rule_builder_test {
  627. name: "hash1",
  628. srcs: ["in1.txt", "in2.txt", "in3.txt"],
  629. }
  630. rule_builder_test {
  631. name: "hash1_sbox",
  632. srcs: ["in1.txt", "in2.txt", "in3.txt"],
  633. sbox: true,
  634. }
  635. `
  636. testcases := []struct {
  637. name string
  638. expectedHash string
  639. }{
  640. {
  641. name: "hash0",
  642. expectedHash: hashOf("implicit\nin1.txt\nin2.txt"),
  643. },
  644. {
  645. name: "hash1",
  646. expectedHash: hashOf("implicit\nin1.txt\nin2.txt\nin3.txt"),
  647. },
  648. }
  649. result := GroupFixturePreparers(
  650. prepareForRuleBuilderTest,
  651. FixtureWithRootAndroidBp(bp),
  652. ).RunTest(t)
  653. for _, test := range testcases {
  654. t.Run(test.name, func(t *testing.T) {
  655. t.Run("sbox", func(t *testing.T) {
  656. gen := result.ModuleForTests(test.name+"_sbox", "")
  657. manifest := RuleBuilderSboxProtoForTests(t, gen.Output("sbox.textproto"))
  658. hash := manifest.Commands[0].GetInputHash()
  659. AssertStringEquals(t, "hash", test.expectedHash, hash)
  660. })
  661. t.Run("", func(t *testing.T) {
  662. gen := result.ModuleForTests(test.name+"", "")
  663. command := gen.Output("gen/" + test.name).RuleParams.Command
  664. if g, w := command, " # hash of input list: "+test.expectedHash; !strings.HasSuffix(g, w) {
  665. t.Errorf("Expected command line to end with %q, got %q", w, g)
  666. }
  667. })
  668. })
  669. }
  670. }