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