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