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