genrule.go 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920
  1. // Copyright 2015 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. // A genrule module takes a list of source files ("srcs" property), an optional
  15. // list of tools ("tools" property), and a command line ("cmd" property), to
  16. // generate output files ("out" property).
  17. package genrule
  18. import (
  19. "fmt"
  20. "io"
  21. "path/filepath"
  22. "strconv"
  23. "strings"
  24. "github.com/google/blueprint"
  25. "github.com/google/blueprint/bootstrap"
  26. "github.com/google/blueprint/proptools"
  27. "android/soong/android"
  28. "android/soong/bazel"
  29. )
  30. func init() {
  31. RegisterGenruleBuildComponents(android.InitRegistrationContext)
  32. }
  33. // Test fixture preparer that will register most genrule build components.
  34. //
  35. // Singletons and mutators should only be added here if they are needed for a majority of genrule
  36. // module types, otherwise they should be added under a separate preparer to allow them to be
  37. // selected only when needed to reduce test execution time.
  38. //
  39. // Module types do not have much of an overhead unless they are used so this should include as many
  40. // module types as possible. The exceptions are those module types that require mutators and/or
  41. // singletons in order to function in which case they should be kept together in a separate
  42. // preparer.
  43. var PrepareForTestWithGenRuleBuildComponents = android.GroupFixturePreparers(
  44. android.FixtureRegisterWithContext(RegisterGenruleBuildComponents),
  45. )
  46. // Prepare a fixture to use all genrule module types, mutators and singletons fully.
  47. //
  48. // This should only be used by tests that want to run with as much of the build enabled as possible.
  49. var PrepareForIntegrationTestWithGenrule = android.GroupFixturePreparers(
  50. PrepareForTestWithGenRuleBuildComponents,
  51. )
  52. func RegisterGenruleBuildComponents(ctx android.RegistrationContext) {
  53. ctx.RegisterModuleType("genrule_defaults", defaultsFactory)
  54. ctx.RegisterModuleType("gensrcs", GenSrcsFactory)
  55. ctx.RegisterModuleType("genrule", GenRuleFactory)
  56. ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
  57. ctx.BottomUp("genrule_tool_deps", toolDepsMutator).Parallel()
  58. })
  59. android.DepsBp2BuildMutators(RegisterGenruleBp2BuildDeps)
  60. android.RegisterBp2BuildMutator("genrule", GenruleBp2Build)
  61. }
  62. func RegisterGenruleBp2BuildDeps(ctx android.RegisterMutatorsContext) {
  63. ctx.BottomUp("genrule_tool_deps", toolDepsMutator)
  64. }
  65. var (
  66. pctx = android.NewPackageContext("android/soong/genrule")
  67. // Used by gensrcs when there is more than 1 shard to merge the outputs
  68. // of each shard into a zip file.
  69. gensrcsMerge = pctx.AndroidStaticRule("gensrcsMerge", blueprint.RuleParams{
  70. Command: "${soongZip} -o ${tmpZip} @${tmpZip}.rsp && ${zipSync} -d ${genDir} ${tmpZip}",
  71. CommandDeps: []string{"${soongZip}", "${zipSync}"},
  72. Rspfile: "${tmpZip}.rsp",
  73. RspfileContent: "${zipArgs}",
  74. }, "tmpZip", "genDir", "zipArgs")
  75. )
  76. func init() {
  77. pctx.Import("android/soong/android")
  78. pctx.HostBinToolVariable("soongZip", "soong_zip")
  79. pctx.HostBinToolVariable("zipSync", "zipsync")
  80. }
  81. type SourceFileGenerator interface {
  82. GeneratedSourceFiles() android.Paths
  83. GeneratedHeaderDirs() android.Paths
  84. GeneratedDeps() android.Paths
  85. }
  86. // Alias for android.HostToolProvider
  87. // Deprecated: use android.HostToolProvider instead.
  88. type HostToolProvider interface {
  89. android.HostToolProvider
  90. }
  91. type hostToolDependencyTag struct {
  92. blueprint.BaseDependencyTag
  93. label string
  94. }
  95. type generatorProperties struct {
  96. // The command to run on one or more input files. Cmd supports substitution of a few variables
  97. //
  98. // Available variables for substitution:
  99. //
  100. // $(location): the path to the first entry in tools or tool_files
  101. // $(location <label>): the path to the tool, tool_file, input or output with name <label>
  102. // $(in): one or more input files
  103. // $(out): a single output file
  104. // $(depfile): a file to which dependencies will be written, if the depfile property is set to true
  105. // $(genDir): the sandbox directory for this tool; contains $(out)
  106. // $$: a literal $
  107. Cmd *string
  108. // Enable reading a file containing dependencies in gcc format after the command completes
  109. Depfile *bool
  110. // name of the modules (if any) that produces the host executable. Leave empty for
  111. // prebuilts or scripts that do not need a module to build them.
  112. Tools []string
  113. // Local file that is used as the tool
  114. Tool_files []string `android:"path"`
  115. // List of directories to export generated headers from
  116. Export_include_dirs []string
  117. // list of input files
  118. Srcs []string `android:"path,arch_variant"`
  119. // input files to exclude
  120. Exclude_srcs []string `android:"path,arch_variant"`
  121. }
  122. type Module struct {
  123. android.ModuleBase
  124. android.DefaultableModuleBase
  125. android.BazelModuleBase
  126. android.ApexModuleBase
  127. // For other packages to make their own genrules with extra
  128. // properties
  129. Extra interface{}
  130. android.ImageInterface
  131. properties generatorProperties
  132. // For the different tasks that genrule and gensrc generate. genrule will
  133. // generate 1 task, and gensrc will generate 1 or more tasks based on the
  134. // number of shards the input files are sharded into.
  135. taskGenerator taskFunc
  136. rule blueprint.Rule
  137. rawCommands []string
  138. exportedIncludeDirs android.Paths
  139. outputFiles android.Paths
  140. outputDeps android.Paths
  141. subName string
  142. subDir string
  143. // Collect the module directory for IDE info in java/jdeps.go.
  144. modulePaths []string
  145. }
  146. type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask
  147. type generateTask struct {
  148. in android.Paths
  149. out android.WritablePaths
  150. depFile android.WritablePath
  151. copyTo android.WritablePaths // For gensrcs to set on gensrcsMerge rule.
  152. genDir android.WritablePath
  153. extraTools android.Paths // dependencies on tools used by the generator
  154. cmd string
  155. // For gensrsc sharding.
  156. shard int
  157. shards int
  158. }
  159. func (g *Module) GeneratedSourceFiles() android.Paths {
  160. return g.outputFiles
  161. }
  162. func (g *Module) Srcs() android.Paths {
  163. return append(android.Paths{}, g.outputFiles...)
  164. }
  165. func (g *Module) GeneratedHeaderDirs() android.Paths {
  166. return g.exportedIncludeDirs
  167. }
  168. func (g *Module) GeneratedDeps() android.Paths {
  169. return g.outputDeps
  170. }
  171. func toolDepsMutator(ctx android.BottomUpMutatorContext) {
  172. if g, ok := ctx.Module().(*Module); ok {
  173. for _, tool := range g.properties.Tools {
  174. tag := hostToolDependencyTag{label: tool}
  175. if m := android.SrcIsModule(tool); m != "" {
  176. tool = m
  177. }
  178. ctx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), tag, tool)
  179. }
  180. }
  181. }
  182. // Returns true if information was available from Bazel, false if bazel invocation still needs to occur.
  183. func (c *Module) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
  184. bazelCtx := ctx.Config().BazelContext
  185. filePaths, ok := bazelCtx.GetOutputFiles(label, ctx.Arch().ArchType)
  186. if ok {
  187. var bazelOutputFiles android.Paths
  188. for _, bazelOutputFile := range filePaths {
  189. bazelOutputFiles = append(bazelOutputFiles, android.PathForBazelOut(ctx, bazelOutputFile))
  190. }
  191. c.outputFiles = bazelOutputFiles
  192. c.outputDeps = bazelOutputFiles
  193. }
  194. return ok
  195. }
  196. func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
  197. g.subName = ctx.ModuleSubDir()
  198. // Collect the module directory for IDE info in java/jdeps.go.
  199. g.modulePaths = append(g.modulePaths, ctx.ModuleDir())
  200. if len(g.properties.Export_include_dirs) > 0 {
  201. for _, dir := range g.properties.Export_include_dirs {
  202. g.exportedIncludeDirs = append(g.exportedIncludeDirs,
  203. android.PathForModuleGen(ctx, g.subDir, ctx.ModuleDir(), dir))
  204. }
  205. } else {
  206. g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForModuleGen(ctx, g.subDir))
  207. }
  208. locationLabels := map[string]location{}
  209. firstLabel := ""
  210. addLocationLabel := func(label string, loc location) {
  211. if firstLabel == "" {
  212. firstLabel = label
  213. }
  214. if _, exists := locationLabels[label]; !exists {
  215. locationLabels[label] = loc
  216. } else {
  217. ctx.ModuleErrorf("multiple labels for %q, %q and %q",
  218. label, locationLabels[label], loc)
  219. }
  220. }
  221. var tools android.Paths
  222. var packagedTools []android.PackagingSpec
  223. if len(g.properties.Tools) > 0 {
  224. seenTools := make(map[string]bool)
  225. ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
  226. switch tag := ctx.OtherModuleDependencyTag(module).(type) {
  227. case hostToolDependencyTag:
  228. tool := ctx.OtherModuleName(module)
  229. switch t := module.(type) {
  230. case android.HostToolProvider:
  231. // A HostToolProvider provides the path to a tool, which will be copied
  232. // into the sandbox.
  233. if !t.(android.Module).Enabled() {
  234. if ctx.Config().AllowMissingDependencies() {
  235. ctx.AddMissingDependencies([]string{tool})
  236. } else {
  237. ctx.ModuleErrorf("depends on disabled module %q", tool)
  238. }
  239. return
  240. }
  241. path := t.HostToolPath()
  242. if !path.Valid() {
  243. ctx.ModuleErrorf("host tool %q missing output file", tool)
  244. return
  245. }
  246. if specs := t.TransitivePackagingSpecs(); specs != nil {
  247. // If the HostToolProvider has PackgingSpecs, which are definitions of the
  248. // required relative locations of the tool and its dependencies, use those
  249. // instead. They will be copied to those relative locations in the sbox
  250. // sandbox.
  251. packagedTools = append(packagedTools, specs...)
  252. // Assume that the first PackagingSpec of the module is the tool.
  253. addLocationLabel(tag.label, packagedToolLocation{specs[0]})
  254. } else {
  255. tools = append(tools, path.Path())
  256. addLocationLabel(tag.label, toolLocation{android.Paths{path.Path()}})
  257. }
  258. case bootstrap.GoBinaryTool:
  259. // A GoBinaryTool provides the install path to a tool, which will be copied.
  260. if s, err := filepath.Rel(android.PathForOutput(ctx).String(), t.InstallPath()); err == nil {
  261. toolPath := android.PathForOutput(ctx, s)
  262. tools = append(tools, toolPath)
  263. addLocationLabel(tag.label, toolLocation{android.Paths{toolPath}})
  264. } else {
  265. ctx.ModuleErrorf("cannot find path for %q: %v", tool, err)
  266. return
  267. }
  268. default:
  269. ctx.ModuleErrorf("%q is not a host tool provider", tool)
  270. return
  271. }
  272. seenTools[tag.label] = true
  273. }
  274. })
  275. // If AllowMissingDependencies is enabled, the build will not have stopped when
  276. // AddFarVariationDependencies was called on a missing tool, which will result in nonsensical
  277. // "cmd: unknown location label ..." errors later. Add a placeholder file to the local label.
  278. // The command that uses this placeholder file will never be executed because the rule will be
  279. // replaced with an android.Error rule reporting the missing dependencies.
  280. if ctx.Config().AllowMissingDependencies() {
  281. for _, tool := range g.properties.Tools {
  282. if !seenTools[tool] {
  283. addLocationLabel(tool, errorLocation{"***missing tool " + tool + "***"})
  284. }
  285. }
  286. }
  287. }
  288. if ctx.Failed() {
  289. return
  290. }
  291. for _, toolFile := range g.properties.Tool_files {
  292. paths := android.PathsForModuleSrc(ctx, []string{toolFile})
  293. tools = append(tools, paths...)
  294. addLocationLabel(toolFile, toolLocation{paths})
  295. }
  296. var srcFiles android.Paths
  297. for _, in := range g.properties.Srcs {
  298. paths, missingDeps := android.PathsAndMissingDepsForModuleSrcExcludes(ctx, []string{in}, g.properties.Exclude_srcs)
  299. if len(missingDeps) > 0 {
  300. if !ctx.Config().AllowMissingDependencies() {
  301. panic(fmt.Errorf("should never get here, the missing dependencies %q should have been reported in DepsMutator",
  302. missingDeps))
  303. }
  304. // If AllowMissingDependencies is enabled, the build will not have stopped when
  305. // the dependency was added on a missing SourceFileProducer module, which will result in nonsensical
  306. // "cmd: label ":..." has no files" errors later. Add a placeholder file to the local label.
  307. // The command that uses this placeholder file will never be executed because the rule will be
  308. // replaced with an android.Error rule reporting the missing dependencies.
  309. ctx.AddMissingDependencies(missingDeps)
  310. addLocationLabel(in, errorLocation{"***missing srcs " + in + "***"})
  311. } else {
  312. srcFiles = append(srcFiles, paths...)
  313. addLocationLabel(in, inputLocation{paths})
  314. }
  315. }
  316. var copyFrom android.Paths
  317. var outputFiles android.WritablePaths
  318. var zipArgs strings.Builder
  319. // Generate tasks, either from genrule or gensrcs.
  320. for _, task := range g.taskGenerator(ctx, String(g.properties.Cmd), srcFiles) {
  321. if len(task.out) == 0 {
  322. ctx.ModuleErrorf("must have at least one output file")
  323. return
  324. }
  325. // Pick a unique path outside the task.genDir for the sbox manifest textproto,
  326. // a unique rule name, and the user-visible description.
  327. manifestName := "genrule.sbox.textproto"
  328. desc := "generate"
  329. name := "generator"
  330. if task.shards > 0 {
  331. manifestName = "genrule_" + strconv.Itoa(task.shard) + ".sbox.textproto"
  332. desc += " " + strconv.Itoa(task.shard)
  333. name += strconv.Itoa(task.shard)
  334. } else if len(task.out) == 1 {
  335. desc += " " + task.out[0].Base()
  336. }
  337. manifestPath := android.PathForModuleOut(ctx, manifestName)
  338. // Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox.
  339. rule := android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath).SandboxTools()
  340. cmd := rule.Command()
  341. for _, out := range task.out {
  342. addLocationLabel(out.Rel(), outputLocation{out})
  343. }
  344. referencedDepfile := false
  345. rawCommand, err := android.Expand(task.cmd, func(name string) (string, error) {
  346. // report the error directly without returning an error to android.Expand to catch multiple errors in a
  347. // single run
  348. reportError := func(fmt string, args ...interface{}) (string, error) {
  349. ctx.PropertyErrorf("cmd", fmt, args...)
  350. return "SOONG_ERROR", nil
  351. }
  352. switch name {
  353. case "location":
  354. if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 {
  355. return reportError("at least one `tools` or `tool_files` is required if $(location) is used")
  356. }
  357. loc := locationLabels[firstLabel]
  358. paths := loc.Paths(cmd)
  359. if len(paths) == 0 {
  360. return reportError("default label %q has no files", firstLabel)
  361. } else if len(paths) > 1 {
  362. return reportError("default label %q has multiple files, use $(locations %s) to reference it",
  363. firstLabel, firstLabel)
  364. }
  365. return paths[0], nil
  366. case "in":
  367. return strings.Join(cmd.PathsForInputs(srcFiles), " "), nil
  368. case "out":
  369. var sandboxOuts []string
  370. for _, out := range task.out {
  371. sandboxOuts = append(sandboxOuts, cmd.PathForOutput(out))
  372. }
  373. return strings.Join(sandboxOuts, " "), nil
  374. case "depfile":
  375. referencedDepfile = true
  376. if !Bool(g.properties.Depfile) {
  377. return reportError("$(depfile) used without depfile property")
  378. }
  379. return "__SBOX_DEPFILE__", nil
  380. case "genDir":
  381. return cmd.PathForOutput(task.genDir), nil
  382. default:
  383. if strings.HasPrefix(name, "location ") {
  384. label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
  385. if loc, ok := locationLabels[label]; ok {
  386. paths := loc.Paths(cmd)
  387. if len(paths) == 0 {
  388. return reportError("label %q has no files", label)
  389. } else if len(paths) > 1 {
  390. return reportError("label %q has multiple files, use $(locations %s) to reference it",
  391. label, label)
  392. }
  393. return paths[0], nil
  394. } else {
  395. return reportError("unknown location label %q", label)
  396. }
  397. } else if strings.HasPrefix(name, "locations ") {
  398. label := strings.TrimSpace(strings.TrimPrefix(name, "locations "))
  399. if loc, ok := locationLabels[label]; ok {
  400. paths := loc.Paths(cmd)
  401. if len(paths) == 0 {
  402. return reportError("label %q has no files", label)
  403. }
  404. return strings.Join(paths, " "), nil
  405. } else {
  406. return reportError("unknown locations label %q", label)
  407. }
  408. } else {
  409. return reportError("unknown variable '$(%s)'", name)
  410. }
  411. }
  412. })
  413. if err != nil {
  414. ctx.PropertyErrorf("cmd", "%s", err.Error())
  415. return
  416. }
  417. if Bool(g.properties.Depfile) && !referencedDepfile {
  418. ctx.PropertyErrorf("cmd", "specified depfile=true but did not include a reference to '${depfile}' in cmd")
  419. return
  420. }
  421. g.rawCommands = append(g.rawCommands, rawCommand)
  422. cmd.Text(rawCommand)
  423. cmd.ImplicitOutputs(task.out)
  424. cmd.Implicits(task.in)
  425. cmd.ImplicitTools(tools)
  426. cmd.ImplicitTools(task.extraTools)
  427. cmd.ImplicitPackagedTools(packagedTools)
  428. if Bool(g.properties.Depfile) {
  429. cmd.ImplicitDepFile(task.depFile)
  430. }
  431. // Create the rule to run the genrule command inside sbox.
  432. rule.Build(name, desc)
  433. if len(task.copyTo) > 0 {
  434. // If copyTo is set, multiple shards need to be copied into a single directory.
  435. // task.out contains the per-shard paths, and copyTo contains the corresponding
  436. // final path. The files need to be copied into the final directory by a
  437. // single rule so it can remove the directory before it starts to ensure no
  438. // old files remain. zipsync already does this, so build up zipArgs that
  439. // zip all the per-shard directories into a single zip.
  440. outputFiles = append(outputFiles, task.copyTo...)
  441. copyFrom = append(copyFrom, task.out.Paths()...)
  442. zipArgs.WriteString(" -C " + task.genDir.String())
  443. zipArgs.WriteString(android.JoinWithPrefix(task.out.Strings(), " -f "))
  444. } else {
  445. outputFiles = append(outputFiles, task.out...)
  446. }
  447. }
  448. if len(copyFrom) > 0 {
  449. // Create a rule that zips all the per-shard directories into a single zip and then
  450. // uses zipsync to unzip it into the final directory.
  451. ctx.Build(pctx, android.BuildParams{
  452. Rule: gensrcsMerge,
  453. Implicits: copyFrom,
  454. Outputs: outputFiles,
  455. Description: "merge shards",
  456. Args: map[string]string{
  457. "zipArgs": zipArgs.String(),
  458. "tmpZip": android.PathForModuleGen(ctx, g.subDir+".zip").String(),
  459. "genDir": android.PathForModuleGen(ctx, g.subDir).String(),
  460. },
  461. })
  462. }
  463. g.outputFiles = outputFiles.Paths()
  464. bazelModuleLabel := g.GetBazelLabel(ctx, g)
  465. bazelActionsUsed := false
  466. if ctx.Config().BazelContext.BazelEnabled() && len(bazelModuleLabel) > 0 {
  467. bazelActionsUsed = g.generateBazelBuildActions(ctx, bazelModuleLabel)
  468. }
  469. if !bazelActionsUsed {
  470. // For <= 6 outputs, just embed those directly in the users. Right now, that covers >90% of
  471. // the genrules on AOSP. That will make things simpler to look at the graph in the common
  472. // case. For larger sets of outputs, inject a phony target in between to limit ninja file
  473. // growth.
  474. if len(g.outputFiles) <= 6 {
  475. g.outputDeps = g.outputFiles
  476. } else {
  477. phonyFile := android.PathForModuleGen(ctx, "genrule-phony")
  478. ctx.Build(pctx, android.BuildParams{
  479. Rule: blueprint.Phony,
  480. Output: phonyFile,
  481. Inputs: g.outputFiles,
  482. })
  483. g.outputDeps = android.Paths{phonyFile}
  484. }
  485. }
  486. }
  487. // Collect information for opening IDE project files in java/jdeps.go.
  488. func (g *Module) IDEInfo(dpInfo *android.IdeInfo) {
  489. dpInfo.Srcs = append(dpInfo.Srcs, g.Srcs().Strings()...)
  490. for _, src := range g.properties.Srcs {
  491. if strings.HasPrefix(src, ":") {
  492. src = strings.Trim(src, ":")
  493. dpInfo.Deps = append(dpInfo.Deps, src)
  494. }
  495. }
  496. dpInfo.Paths = append(dpInfo.Paths, g.modulePaths...)
  497. }
  498. func (g *Module) AndroidMk() android.AndroidMkData {
  499. return android.AndroidMkData{
  500. Class: "ETC",
  501. OutputFile: android.OptionalPathForPath(g.outputFiles[0]),
  502. SubName: g.subName,
  503. Extra: []android.AndroidMkExtraFunc{
  504. func(w io.Writer, outputFile android.Path) {
  505. fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
  506. },
  507. },
  508. Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
  509. android.WriteAndroidMkData(w, data)
  510. if data.SubName != "" {
  511. fmt.Fprintln(w, ".PHONY:", name)
  512. fmt.Fprintln(w, name, ":", name+g.subName)
  513. }
  514. },
  515. }
  516. }
  517. var _ android.ApexModule = (*Module)(nil)
  518. // Implements android.ApexModule
  519. func (g *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
  520. sdkVersion android.ApiLevel) error {
  521. // Because generated outputs are checked by client modules(e.g. cc_library, ...)
  522. // we can safely ignore the check here.
  523. return nil
  524. }
  525. func generatorFactory(taskGenerator taskFunc, props ...interface{}) *Module {
  526. module := &Module{
  527. taskGenerator: taskGenerator,
  528. }
  529. module.AddProperties(props...)
  530. module.AddProperties(&module.properties)
  531. module.ImageInterface = noopImageInterface{}
  532. return module
  533. }
  534. type noopImageInterface struct{}
  535. func (x noopImageInterface) ImageMutatorBegin(android.BaseModuleContext) {}
  536. func (x noopImageInterface) CoreVariantNeeded(android.BaseModuleContext) bool { return false }
  537. func (x noopImageInterface) RamdiskVariantNeeded(android.BaseModuleContext) bool { return false }
  538. func (x noopImageInterface) VendorRamdiskVariantNeeded(android.BaseModuleContext) bool { return false }
  539. func (x noopImageInterface) RecoveryVariantNeeded(android.BaseModuleContext) bool { return false }
  540. func (x noopImageInterface) ExtraImageVariations(ctx android.BaseModuleContext) []string { return nil }
  541. func (x noopImageInterface) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
  542. }
  543. func NewGenSrcs() *Module {
  544. properties := &genSrcsProperties{}
  545. // finalSubDir is the name of the subdirectory that output files will be generated into.
  546. // It is used so that per-shard directories can be placed alongside it an then finally
  547. // merged into it.
  548. const finalSubDir = "gensrcs"
  549. taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask {
  550. shardSize := defaultShardSize
  551. if s := properties.Shard_size; s != nil {
  552. shardSize = int(*s)
  553. }
  554. // gensrcs rules can easily hit command line limits by repeating the command for
  555. // every input file. Shard the input files into groups.
  556. shards := android.ShardPaths(srcFiles, shardSize)
  557. var generateTasks []generateTask
  558. for i, shard := range shards {
  559. var commands []string
  560. var outFiles android.WritablePaths
  561. var commandDepFiles []string
  562. var copyTo android.WritablePaths
  563. // When sharding is enabled (i.e. len(shards) > 1), the sbox rules for each
  564. // shard will be write to their own directories and then be merged together
  565. // into finalSubDir. If sharding is not enabled (i.e. len(shards) == 1),
  566. // the sbox rule will write directly to finalSubDir.
  567. genSubDir := finalSubDir
  568. if len(shards) > 1 {
  569. genSubDir = strconv.Itoa(i)
  570. }
  571. genDir := android.PathForModuleGen(ctx, genSubDir)
  572. // TODO(ccross): this RuleBuilder is a hack to be able to call
  573. // rule.Command().PathForOutput. Replace this with passing the rule into the
  574. // generator.
  575. rule := android.NewRuleBuilder(pctx, ctx).Sbox(genDir, nil).SandboxTools()
  576. for _, in := range shard {
  577. outFile := android.GenPathWithExt(ctx, finalSubDir, in, String(properties.Output_extension))
  578. // If sharding is enabled, then outFile is the path to the output file in
  579. // the shard directory, and copyTo is the path to the output file in the
  580. // final directory.
  581. if len(shards) > 1 {
  582. shardFile := android.GenPathWithExt(ctx, genSubDir, in, String(properties.Output_extension))
  583. copyTo = append(copyTo, outFile)
  584. outFile = shardFile
  585. }
  586. outFiles = append(outFiles, outFile)
  587. // pre-expand the command line to replace $in and $out with references to
  588. // a single input and output file.
  589. command, err := android.Expand(rawCommand, func(name string) (string, error) {
  590. switch name {
  591. case "in":
  592. return in.String(), nil
  593. case "out":
  594. return rule.Command().PathForOutput(outFile), nil
  595. case "depfile":
  596. // Generate a depfile for each output file. Store the list for
  597. // later in order to combine them all into a single depfile.
  598. depFile := rule.Command().PathForOutput(outFile.ReplaceExtension(ctx, "d"))
  599. commandDepFiles = append(commandDepFiles, depFile)
  600. return depFile, nil
  601. default:
  602. return "$(" + name + ")", nil
  603. }
  604. })
  605. if err != nil {
  606. ctx.PropertyErrorf("cmd", err.Error())
  607. }
  608. // escape the command in case for example it contains '#', an odd number of '"', etc
  609. command = fmt.Sprintf("bash -c %v", proptools.ShellEscape(command))
  610. commands = append(commands, command)
  611. }
  612. fullCommand := strings.Join(commands, " && ")
  613. var outputDepfile android.WritablePath
  614. var extraTools android.Paths
  615. if len(commandDepFiles) > 0 {
  616. // Each command wrote to a depfile, but ninja can only handle one
  617. // depfile per rule. Use the dep_fixer tool at the end of the
  618. // command to combine all the depfiles into a single output depfile.
  619. outputDepfile = android.PathForModuleGen(ctx, genSubDir, "gensrcs.d")
  620. depFixerTool := ctx.Config().HostToolPath(ctx, "dep_fixer")
  621. fullCommand += fmt.Sprintf(" && %s -o $(depfile) %s",
  622. rule.Command().PathForTool(depFixerTool),
  623. strings.Join(commandDepFiles, " "))
  624. extraTools = append(extraTools, depFixerTool)
  625. }
  626. generateTasks = append(generateTasks, generateTask{
  627. in: shard,
  628. out: outFiles,
  629. depFile: outputDepfile,
  630. copyTo: copyTo,
  631. genDir: genDir,
  632. cmd: fullCommand,
  633. shard: i,
  634. shards: len(shards),
  635. extraTools: extraTools,
  636. })
  637. }
  638. return generateTasks
  639. }
  640. g := generatorFactory(taskGenerator, properties)
  641. g.subDir = finalSubDir
  642. return g
  643. }
  644. func GenSrcsFactory() android.Module {
  645. m := NewGenSrcs()
  646. android.InitAndroidModule(m)
  647. return m
  648. }
  649. type genSrcsProperties struct {
  650. // extension that will be substituted for each output file
  651. Output_extension *string
  652. // maximum number of files that will be passed on a single command line.
  653. Shard_size *int64
  654. }
  655. const defaultShardSize = 50
  656. func NewGenRule() *Module {
  657. properties := &genRuleProperties{}
  658. taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask {
  659. outs := make(android.WritablePaths, len(properties.Out))
  660. var depFile android.WritablePath
  661. for i, out := range properties.Out {
  662. outPath := android.PathForModuleGen(ctx, out)
  663. if i == 0 {
  664. depFile = outPath.ReplaceExtension(ctx, "d")
  665. }
  666. outs[i] = outPath
  667. }
  668. return []generateTask{{
  669. in: srcFiles,
  670. out: outs,
  671. depFile: depFile,
  672. genDir: android.PathForModuleGen(ctx),
  673. cmd: rawCommand,
  674. }}
  675. }
  676. return generatorFactory(taskGenerator, properties)
  677. }
  678. func GenRuleFactory() android.Module {
  679. m := NewGenRule()
  680. android.InitAndroidModule(m)
  681. android.InitDefaultableModule(m)
  682. android.InitBazelModule(m)
  683. return m
  684. }
  685. type genRuleProperties struct {
  686. // names of the output files that will be generated
  687. Out []string `android:"arch_variant"`
  688. }
  689. type bazelGenruleAttributes struct {
  690. Srcs bazel.LabelList
  691. Outs []string
  692. Tools bazel.LabelList
  693. Cmd string
  694. }
  695. type bazelGenrule struct {
  696. android.BazelTargetModuleBase
  697. bazelGenruleAttributes
  698. }
  699. func BazelGenruleFactory() android.Module {
  700. module := &bazelGenrule{}
  701. module.AddProperties(&module.bazelGenruleAttributes)
  702. android.InitBazelTargetModule(module)
  703. return module
  704. }
  705. func GenruleBp2Build(ctx android.TopDownMutatorContext) {
  706. m, ok := ctx.Module().(*Module)
  707. if !ok || !m.ConvertWithBp2build(ctx) {
  708. return
  709. }
  710. if ctx.ModuleType() != "genrule" {
  711. // Not a regular genrule. Could be a cc_genrule or java_genrule.
  712. return
  713. }
  714. // Bazel only has the "tools" attribute.
  715. tools := android.BazelLabelForModuleDeps(ctx, m.properties.Tools)
  716. tool_files := android.BazelLabelForModuleSrc(ctx, m.properties.Tool_files)
  717. tools.Append(tool_files)
  718. srcs := android.BazelLabelForModuleSrc(ctx, m.properties.Srcs)
  719. var allReplacements bazel.LabelList
  720. allReplacements.Append(tools)
  721. allReplacements.Append(srcs)
  722. // Replace in and out variables with $< and $@
  723. var cmd string
  724. if m.properties.Cmd != nil {
  725. cmd = strings.Replace(*m.properties.Cmd, "$(in)", "$(SRCS)", -1)
  726. cmd = strings.Replace(cmd, "$(out)", "$(OUTS)", -1)
  727. cmd = strings.Replace(cmd, "$(genDir)", "$(GENDIR)", -1)
  728. if len(tools.Includes) > 0 {
  729. cmd = strings.Replace(cmd, "$(location)", fmt.Sprintf("$(location %s)", tools.Includes[0].Label), -1)
  730. cmd = strings.Replace(cmd, "$(locations)", fmt.Sprintf("$(locations %s)", tools.Includes[0].Label), -1)
  731. }
  732. for _, l := range allReplacements.Includes {
  733. bpLoc := fmt.Sprintf("$(location %s)", l.Bp_text)
  734. bpLocs := fmt.Sprintf("$(locations %s)", l.Bp_text)
  735. bazelLoc := fmt.Sprintf("$(location %s)", l.Label)
  736. bazelLocs := fmt.Sprintf("$(locations %s)", l.Label)
  737. cmd = strings.Replace(cmd, bpLoc, bazelLoc, -1)
  738. cmd = strings.Replace(cmd, bpLocs, bazelLocs, -1)
  739. }
  740. }
  741. // The Out prop is not in an immediately accessible field
  742. // in the Module struct, so use GetProperties and cast it
  743. // to the known struct prop.
  744. var outs []string
  745. for _, propIntf := range m.GetProperties() {
  746. if props, ok := propIntf.(*genRuleProperties); ok {
  747. outs = props.Out
  748. break
  749. }
  750. }
  751. attrs := &bazelGenruleAttributes{
  752. Srcs: srcs,
  753. Outs: outs,
  754. Cmd: cmd,
  755. Tools: tools,
  756. }
  757. props := bazel.BazelTargetModuleProperties{
  758. Rule_class: "genrule",
  759. }
  760. // Create the BazelTargetModule.
  761. ctx.CreateBazelTargetModule(BazelGenruleFactory, m.Name(), props, attrs)
  762. }
  763. func (m *bazelGenrule) Name() string {
  764. return m.BaseModuleName()
  765. }
  766. func (m *bazelGenrule) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
  767. var Bool = proptools.Bool
  768. var String = proptools.String
  769. //
  770. // Defaults
  771. //
  772. type Defaults struct {
  773. android.ModuleBase
  774. android.DefaultsModuleBase
  775. }
  776. func defaultsFactory() android.Module {
  777. return DefaultsFactory()
  778. }
  779. func DefaultsFactory(props ...interface{}) android.Module {
  780. module := &Defaults{}
  781. module.AddProperties(props...)
  782. module.AddProperties(
  783. &generatorProperties{},
  784. &genRuleProperties{},
  785. )
  786. android.InitDefaultsModule(module)
  787. return module
  788. }