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/proptools" 30 31 "android/soong/android" 32) 33 34func init() { 35 RegisterGenruleBuildComponents(android.InitRegistrationContext) 36} 37 38// Test fixture preparer that will register most genrule build components. 39// 40// Singletons and mutators should only be added here if they are needed for a majority of genrule 41// module types, otherwise they should be added under a separate preparer to allow them to be 42// selected only when needed to reduce test execution time. 43// 44// Module types do not have much of an overhead unless they are used so this should include as many 45// module types as possible. The exceptions are those module types that require mutators and/or 46// singletons in order to function in which case they should be kept together in a separate 47// preparer. 48var PrepareForTestWithGenRuleBuildComponents = android.GroupFixturePreparers( 49 android.FixtureRegisterWithContext(RegisterGenruleBuildComponents), 50) 51 52// Prepare a fixture to use all genrule module types, mutators and singletons fully. 53// 54// This should only be used by tests that want to run with as much of the build enabled as possible. 55var PrepareForIntegrationTestWithGenrule = android.GroupFixturePreparers( 56 PrepareForTestWithGenRuleBuildComponents, 57) 58 59func RegisterGenruleBuildComponents(ctx android.RegistrationContext) { 60 ctx.RegisterModuleType("genrule_defaults", defaultsFactory) 61 62 ctx.RegisterModuleType("gensrcs", GenSrcsFactory) 63 ctx.RegisterModuleType("genrule", GenRuleFactory) 64 65 ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) { 66 ctx.BottomUp("genrule_tool_deps", toolDepsMutator) 67 }) 68} 69 70var ( 71 pctx = android.NewPackageContext("android/soong/genrule") 72 73 // Used by gensrcs when there is more than 1 shard to merge the outputs 74 // of each shard into a zip file. 75 gensrcsMerge = pctx.AndroidStaticRule("gensrcsMerge", blueprint.RuleParams{ 76 Command: "${soongZip} -o ${tmpZip} @${tmpZip}.rsp && ${zipSync} -d ${genDir} ${tmpZip}", 77 CommandDeps: []string{"${soongZip}", "${zipSync}"}, 78 Rspfile: "${tmpZip}.rsp", 79 RspfileContent: "${zipArgs}", 80 }, "tmpZip", "genDir", "zipArgs") 81) 82 83func init() { 84 pctx.Import("android/soong/android") 85 86 pctx.HostBinToolVariable("soongZip", "soong_zip") 87 pctx.HostBinToolVariable("zipSync", "zipsync") 88} 89 90type SourceFileGenerator interface { 91 android.SourceFileGenerator 92} 93 94// Alias for android.HostToolProvider 95// Deprecated: use android.HostToolProvider instead. 96type HostToolProvider interface { 97 android.HostToolProvider 98} 99 100type hostToolDependencyTag struct { 101 blueprint.BaseDependencyTag 102 android.LicenseAnnotationToolchainDependencyTag 103 label string 104} 105 106func (t hostToolDependencyTag) AllowDisabledModuleDependency(target android.Module) bool { 107 // Allow depending on a disabled module if it's replaced by a prebuilt 108 // counterpart. We get the prebuilt through android.PrebuiltGetPreferred in 109 // GenerateAndroidBuildActions. 110 return target.IsReplacedByPrebuilt() 111} 112 113func (t hostToolDependencyTag) AllowDisabledModuleDependencyProxy( 114 ctx android.OtherModuleProviderContext, target android.ModuleProxy) bool { 115 return android.OtherModulePointerProviderOrDefault( 116 ctx, target, android.CommonModuleInfoProvider).ReplacedByPrebuilt 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 // $(genDir): the sandbox directory for this tool; contains $(out). 132 // $$: a literal $ 133 Cmd proptools.Configurable[string] `android:"replace_instead_of_append"` 134 135 // name of the modules (if any) that produces the host executable. Leave empty for 136 // prebuilts or scripts that do not need a module to build them. 137 Tools []string 138 139 // Local files that are used by the tool 140 Tool_files []string `android:"path"` 141 142 // List of directories to export generated headers from 143 Export_include_dirs []string 144 145 // list of input files 146 Srcs proptools.Configurable[[]string] `android:"path,arch_variant"` 147 148 // Same as srcs, but will add dependencies on modules via a device os variation and the device's 149 // first supported arch's variation. Can be used to add a dependency from a host genrule to 150 // a device module. 151 Device_first_srcs proptools.Configurable[[]string] `android:"path_device_first"` 152 153 // Same as srcs, but will add dependencies on modules via a device os variation and the common 154 // arch variation. Can be used to add a dependency from a host genrule to a device module. 155 Device_common_srcs proptools.Configurable[[]string] `android:"path_device_common"` 156 157 // Same as srcs, but will add dependencies on modules via a common_os os variation. 158 Common_os_srcs proptools.Configurable[[]string] `android:"path_common_os"` 159 160 // input files to exclude 161 Exclude_srcs []string `android:"path,arch_variant"` 162 163 // Enable restat to update the output only if the output is changed 164 Write_if_changed *bool 165 166 // When set to true, an additional $(build_number_file) label will be available 167 // to use in the cmd. This will be the location of a text file containing the 168 // build number. The dependency on this file will be "order-only", meaning that 169 // the genrule will not rerun when only this file changes, to avoid rerunning 170 // the genrule every build, because the build number changes every build. 171 // This also means that you should not attempt to consume the build number from 172 // the result of this genrule in another build rule. If you do, the build number 173 // in the second build rule will be stale when the second build rule rebuilds 174 // but this genrule does not. Only certain allowlisted modules are allowed to 175 // use this property, usages of the build number should be kept to the absolute 176 // minimum. Particularly no modules on the system image may include the build 177 // number. Prefer using libbuildversion via the use_version_lib property on 178 // cc modules. 179 Uses_order_only_build_number_file *bool 180} 181 182type Module struct { 183 android.ModuleBase 184 android.DefaultableModuleBase 185 android.ApexModuleBase 186 187 // For other packages to make their own genrules with extra 188 // properties 189 Extra interface{} 190 191 // CmdModifier can be set by wrappers around genrule to modify the command, for example to 192 // prefix environment variables to it. 193 CmdModifier func(ctx android.ModuleContext, cmd string) string 194 195 android.ImageInterface 196 197 properties generatorProperties 198 199 // For the different tasks that genrule and gensrc generate. genrule will 200 // generate 1 task, and gensrc will generate 1 or more tasks based on the 201 // number of shards the input files are sharded into. 202 taskGenerator taskFunc 203 204 rule blueprint.Rule 205 rawCommands []string 206 207 exportedIncludeDirs android.Paths 208 209 outputFiles android.Paths 210 outputDeps android.Paths 211 212 subName string 213 subDir string 214} 215 216type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask 217 218type generateTask struct { 219 in android.Paths 220 out android.WritablePaths 221 copyTo android.WritablePaths // For gensrcs to set on gensrcsMerge rule. 222 genDir android.WritablePath 223 extraInputs map[string][]string 224 225 cmd string 226 // For gensrsc sharding. 227 shard int 228 shards int 229 230 // For nsjail tasks 231 useNsjail bool 232 dirSrcs android.DirectoryPaths 233 keepGendir bool 234} 235 236func (g *Module) GeneratedSourceFiles() android.Paths { 237 return g.outputFiles 238} 239 240func (g *Module) Srcs() android.Paths { 241 return append(android.Paths{}, g.outputFiles...) 242} 243 244func (g *Module) GeneratedHeaderDirs() android.Paths { 245 return g.exportedIncludeDirs 246} 247 248func (g *Module) GeneratedDeps() android.Paths { 249 return g.outputDeps 250} 251 252var _ android.SourceFileProducer = (*Module)(nil) 253 254func toolDepsMutator(ctx android.BottomUpMutatorContext) { 255 if g, ok := ctx.Module().(*Module); ok { 256 for _, tool := range g.properties.Tools { 257 tag := hostToolDependencyTag{label: tool} 258 if m := android.SrcIsModule(tool); m != "" { 259 tool = m 260 } 261 ctx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), tag, tool) 262 } 263 } 264} 265 266var buildNumberAllowlistKey = android.NewOnceKey("genruleBuildNumberAllowlistKey") 267 268// This allowlist should be kept to the bare minimum, it's 269// intended for things that existed before the build number 270// was tightly controlled. Prefer using libbuildversion 271// via the use_version_lib property of cc modules. 272// This is a function instead of a global map so that 273// soong plugins cannot add entries to the allowlist 274func isModuleInBuildNumberAllowlist(ctx android.ModuleContext) bool { 275 allowlist := ctx.Config().Once(buildNumberAllowlistKey, func() interface{} { 276 // Define the allowlist as a list and then copy it into a map so that 277 // gofmt doesn't change unnecessary lines trying to align the values of the map. 278 allowlist := []string{ 279 // go/keep-sorted start 280 "build/soong/tests:gen", 281 "hardware/google/camera/common/hal/aidl_service:aidl_camera_build_version", 282 "tools/tradefederation/core:tradefed_zip", 283 "vendor/google/services/LyricCameraHAL/src/apex:com.google.pixel.camera.hal.manifest", 284 "vendor/google_tradefederation/core:gen_google_tradefed_zip", 285 // go/keep-sorted end 286 } 287 allowlistMap := make(map[string]bool, len(allowlist)) 288 for _, a := range allowlist { 289 allowlistMap[a] = true 290 } 291 return allowlistMap 292 }).(map[string]bool) 293 294 _, ok := allowlist[ctx.ModuleDir()+":"+ctx.ModuleName()] 295 return ok 296} 297 298// generateCommonBuildActions contains build action generation logic 299// common to both the mixed build case and the legacy case of genrule processing. 300// To fully support genrule in mixed builds, the contents of this function should 301// approach zero; there should be no genrule action registration done directly 302// by Soong logic in the mixed-build case. 303func (g *Module) generateCommonBuildActions(ctx android.ModuleContext) { 304 // Add the variant as a suffix to the make modules to create, so that the make modules 305 // don't conflict because make doesn't know about variants. However, this causes issues with 306 // tracking required dependencies as the required property in soong is passed straight to make 307 // without accounting for these suffixes. To make it a little easier to work with, don't use 308 // a suffix for android_common variants so that java_genrules look like regular 1-variant 309 // genrules to make. 310 if ctx.ModuleSubDir() != "android_common" { 311 g.subName = ctx.ModuleSubDir() 312 } 313 314 if len(g.properties.Export_include_dirs) > 0 { 315 for _, dir := range g.properties.Export_include_dirs { 316 g.exportedIncludeDirs = append(g.exportedIncludeDirs, 317 android.PathForModuleGen(ctx, g.subDir, ctx.ModuleDir(), dir)) 318 // Also export without ModuleDir for consistency with Export_include_dirs not being set 319 g.exportedIncludeDirs = append(g.exportedIncludeDirs, 320 android.PathForModuleGen(ctx, g.subDir, dir)) 321 } 322 } else { 323 g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForModuleGen(ctx, g.subDir)) 324 } 325 326 locationLabels := map[string]location{} 327 firstLabel := "" 328 329 addLocationLabel := func(label string, loc location) { 330 if firstLabel == "" { 331 firstLabel = label 332 } 333 if _, exists := locationLabels[label]; !exists { 334 locationLabels[label] = loc 335 } else { 336 ctx.ModuleErrorf("multiple locations for label %q: %q and %q (do you have duplicate srcs entries?)", 337 label, locationLabels[label], loc) 338 } 339 } 340 341 var tools android.Paths 342 var packagedTools []android.PackagingSpec 343 if len(g.properties.Tools) > 0 { 344 seenTools := make(map[string]bool) 345 ctx.VisitDirectDepsProxyAllowDisabled(func(proxy android.ModuleProxy) { 346 switch tag := ctx.OtherModuleDependencyTag(proxy).(type) { 347 case hostToolDependencyTag: 348 // Necessary to retrieve any prebuilt replacement for the tool, since 349 // toolDepsMutator runs too late for the prebuilt mutators to have 350 // replaced the dependency. 351 module := android.PrebuiltGetPreferred(ctx, proxy) 352 tool := ctx.OtherModuleName(module) 353 if h, ok := android.OtherModuleProvider(ctx, module, android.HostToolProviderInfoProvider); ok { 354 // A HostToolProvider provides the path to a tool, which will be copied 355 // into the sandbox. 356 if !android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider).Enabled { 357 if ctx.Config().AllowMissingDependencies() { 358 ctx.AddMissingDependencies([]string{tool}) 359 } else { 360 ctx.ModuleErrorf("depends on disabled module %q", tool) 361 } 362 return 363 } 364 path := h.HostToolPath 365 if !path.Valid() { 366 ctx.ModuleErrorf("host tool %q missing output file", tool) 367 return 368 } 369 if specs := android.OtherModuleProviderOrDefault( 370 ctx, module, android.InstallFilesProvider).TransitivePackagingSpecs.ToList(); specs != nil { 371 // If the HostToolProvider has PackgingSpecs, which are definitions of the 372 // required relative locations of the tool and its dependencies, use those 373 // instead. They will be copied to those relative locations in the sbox 374 // sandbox. 375 // Care must be taken since TransitivePackagingSpec may return device-side 376 // paths via the required property. Filter them out. 377 for i, ps := range specs { 378 if ps.Partition() != "" { 379 if i == 0 { 380 panic("first PackagingSpec is assumed to be the host-side tool") 381 } 382 continue 383 } 384 packagedTools = append(packagedTools, ps) 385 } 386 // Assume that the first PackagingSpec of the module is the tool. 387 addLocationLabel(tag.label, packagedToolLocation{specs[0]}) 388 } else { 389 tools = append(tools, path.Path()) 390 addLocationLabel(tag.label, toolLocation{android.Paths{path.Path()}}) 391 } 392 } else { 393 ctx.ModuleErrorf("%q is not a host tool provider", tool) 394 return 395 } 396 397 seenTools[tag.label] = true 398 } 399 }) 400 401 // If AllowMissingDependencies is enabled, the build will not have stopped when 402 // AddFarVariationDependencies was called on a missing tool, which will result in nonsensical 403 // "cmd: unknown location label ..." errors later. Add a placeholder file to the local label. 404 // The command that uses this placeholder file will never be executed because the rule will be 405 // replaced with an android.Error rule reporting the missing dependencies. 406 if ctx.Config().AllowMissingDependencies() { 407 for _, tool := range g.properties.Tools { 408 if !seenTools[tool] { 409 addLocationLabel(tool, errorLocation{"***missing tool " + tool + "***"}) 410 } 411 } 412 } 413 } 414 415 if ctx.Failed() { 416 return 417 } 418 419 for _, toolFile := range g.properties.Tool_files { 420 paths := android.PathsForModuleSrc(ctx, []string{toolFile}) 421 tools = append(tools, paths...) 422 addLocationLabel(toolFile, toolLocation{paths}) 423 } 424 425 addLabelsForInputs := func(propName string, include, exclude []string) android.Paths { 426 includeDirInPaths := ctx.DeviceConfig().BuildBrokenInputDir(g.Name()) 427 var srcFiles android.Paths 428 for _, in := range include { 429 paths, missingDeps := android.PathsAndMissingDepsRelativeToModuleSourceDir(android.SourceInput{ 430 Context: ctx, Paths: []string{in}, ExcludePaths: exclude, IncludeDirs: includeDirInPaths, 431 }) 432 if len(missingDeps) > 0 { 433 if !ctx.Config().AllowMissingDependencies() { 434 panic(fmt.Errorf("should never get here, the missing dependencies %q should have been reported in DepsMutator", 435 missingDeps)) 436 } 437 438 // If AllowMissingDependencies is enabled, the build will not have stopped when 439 // the dependency was added on a missing SourceFileProducer module, which will result in nonsensical 440 // "cmd: label ":..." has no files" errors later. Add a placeholder file to the local label. 441 // The command that uses this placeholder file will never be executed because the rule will be 442 // replaced with an android.Error rule reporting the missing dependencies. 443 ctx.AddMissingDependencies(missingDeps) 444 addLocationLabel(in, errorLocation{"***missing " + propName + " " + in + "***"}) 445 } else { 446 srcFiles = append(srcFiles, paths...) 447 addLocationLabel(in, inputLocation{paths}) 448 } 449 } 450 return srcFiles 451 } 452 srcs := g.properties.Srcs.GetOrDefault(ctx, nil) 453 srcFiles := addLabelsForInputs("srcs", srcs, g.properties.Exclude_srcs) 454 srcFiles = append(srcFiles, addLabelsForInputs("device_first_srcs", g.properties.Device_first_srcs.GetOrDefault(ctx, nil), nil)...) 455 srcFiles = append(srcFiles, addLabelsForInputs("device_common_srcs", g.properties.Device_common_srcs.GetOrDefault(ctx, nil), nil)...) 456 srcFiles = append(srcFiles, addLabelsForInputs("common_os_srcs", g.properties.Common_os_srcs.GetOrDefault(ctx, nil), nil)...) 457 458 var copyFrom android.Paths 459 var outputFiles android.WritablePaths 460 var zipArgs strings.Builder 461 462 cmd := g.properties.Cmd.GetOrDefault(ctx, "") 463 if g.CmdModifier != nil { 464 cmd = g.CmdModifier(ctx, cmd) 465 } 466 467 var extraInputs android.Paths 468 // Generate tasks, either from genrule or gensrcs. 469 for i, task := range g.taskGenerator(ctx, cmd, srcFiles) { 470 if len(task.out) == 0 { 471 ctx.ModuleErrorf("must have at least one output file") 472 return 473 } 474 475 // Only handle extra inputs once as these currently are the same across all tasks 476 if i == 0 { 477 for name, values := range task.extraInputs { 478 extraInputs = append(extraInputs, addLabelsForInputs(name, values, []string{})...) 479 } 480 } 481 482 // Pick a unique path outside the task.genDir for the sbox manifest textproto, 483 // a unique rule name, and the user-visible description. 484 var rule *android.RuleBuilder 485 desc := "generate" 486 name := "generator" 487 if task.useNsjail { 488 rule = android.NewRuleBuilder(pctx, ctx).Nsjail(task.genDir, android.PathForModuleOut(ctx, "nsjail_build_sandbox")) 489 if task.keepGendir { 490 rule.NsjailKeepGendir() 491 } 492 } else { 493 manifestName := "genrule.sbox.textproto" 494 if task.shards > 0 { 495 manifestName = "genrule_" + strconv.Itoa(task.shard) + ".sbox.textproto" 496 desc += " " + strconv.Itoa(task.shard) 497 name += strconv.Itoa(task.shard) 498 } else if len(task.out) == 1 { 499 desc += " " + task.out[0].Base() 500 } 501 502 manifestPath := android.PathForModuleOut(ctx, manifestName) 503 504 // Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox. 505 rule = getSandboxedRuleBuilder(ctx, android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath)) 506 } 507 if Bool(g.properties.Write_if_changed) { 508 rule.Restat() 509 } 510 cmd := rule.Command() 511 512 for _, out := range task.out { 513 addLocationLabel(out.Rel(), outputLocation{out}) 514 } 515 516 rawCommand, err := android.Expand(task.cmd, func(name string) (string, error) { 517 // report the error directly without returning an error to android.Expand to catch multiple errors in a 518 // single run 519 reportError := func(fmt string, args ...interface{}) (string, error) { 520 ctx.PropertyErrorf("cmd", fmt, args...) 521 return "SOONG_ERROR", nil 522 } 523 524 // Apply shell escape to each cases to prevent source file paths containing $ from being evaluated in shell 525 switch name { 526 case "location": 527 if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 { 528 return reportError("at least one `tools` or `tool_files` is required if $(location) is used") 529 } 530 loc := locationLabels[firstLabel] 531 paths := loc.Paths(cmd) 532 if len(paths) == 0 { 533 return reportError("default label %q has no files", firstLabel) 534 } else if len(paths) > 1 { 535 return reportError("default label %q has multiple files, use $(locations %s) to reference it", 536 firstLabel, firstLabel) 537 } 538 return proptools.ShellEscape(paths[0]), nil 539 case "in": 540 return strings.Join(proptools.ShellEscapeList(cmd.PathsForInputs(srcFiles)), " "), nil 541 case "out": 542 var sandboxOuts []string 543 for _, out := range task.out { 544 sandboxOuts = append(sandboxOuts, cmd.PathForOutput(out)) 545 } 546 return strings.Join(proptools.ShellEscapeList(sandboxOuts), " "), nil 547 case "genDir": 548 return proptools.ShellEscape(cmd.PathForOutput(task.genDir)), nil 549 case "build_number_file": 550 if !proptools.Bool(g.properties.Uses_order_only_build_number_file) { 551 return reportError("to use the $(build_number_file) label, you must set uses_order_only_build_number_file: true") 552 } 553 return proptools.ShellEscape(cmd.PathForInput(ctx.Config().BuildNumberFile(ctx))), nil 554 default: 555 if strings.HasPrefix(name, "location ") { 556 label := strings.TrimSpace(strings.TrimPrefix(name, "location ")) 557 if loc, ok := locationLabels[label]; ok { 558 paths := loc.Paths(cmd) 559 if len(paths) == 0 { 560 return reportError("label %q has no files", label) 561 } else if len(paths) > 1 { 562 return reportError("label %q has multiple files, use $(locations %s) to reference it", 563 label, label) 564 } 565 return proptools.ShellEscape(paths[0]), nil 566 } else { 567 return reportError("unknown location label %q is not in srcs, out, tools or tool_files.", label) 568 } 569 } else if strings.HasPrefix(name, "locations ") { 570 label := strings.TrimSpace(strings.TrimPrefix(name, "locations ")) 571 if loc, ok := locationLabels[label]; ok { 572 paths := loc.Paths(cmd) 573 if len(paths) == 0 { 574 return reportError("label %q has no files", label) 575 } 576 return strings.Join(proptools.ShellEscapeList(paths), " "), nil 577 } else { 578 return reportError("unknown locations label %q is not in srcs, out, tools or tool_files.", label) 579 } 580 } else { 581 return reportError("unknown variable '$(%s)'", name) 582 } 583 } 584 }) 585 586 if err != nil { 587 ctx.PropertyErrorf("cmd", "%s", err.Error()) 588 return 589 } 590 591 g.rawCommands = append(g.rawCommands, rawCommand) 592 593 cmd.Text(rawCommand) 594 cmd.Implicits(srcFiles) // need to be able to reference other srcs 595 cmd.Implicits(extraInputs) 596 cmd.ImplicitOutputs(task.out) 597 cmd.Implicits(task.in) 598 cmd.ImplicitTools(tools) 599 cmd.ImplicitPackagedTools(packagedTools) 600 if proptools.Bool(g.properties.Uses_order_only_build_number_file) { 601 if !isModuleInBuildNumberAllowlist(ctx) { 602 ctx.ModuleErrorf("Only allowlisted modules may use uses_order_only_build_number_file: true") 603 } 604 cmd.OrderOnly(ctx.Config().BuildNumberFile(ctx)) 605 } 606 607 if task.useNsjail { 608 for _, input := range task.dirSrcs { 609 cmd.ImplicitDirectory(input) 610 // TODO(b/375551969): remove glob 611 if paths, err := ctx.GlobWithDeps(filepath.Join(input.String(), "**/*"), nil); err == nil { 612 rule.NsjailImplicits(android.PathsForSource(ctx, paths)) 613 } else { 614 ctx.PropertyErrorf("dir_srcs", "can't glob %q", input.String()) 615 } 616 } 617 } 618 619 // Create the rule to run the genrule command inside sbox. 620 rule.Build(name, desc) 621 622 if len(task.copyTo) > 0 { 623 // If copyTo is set, multiple shards need to be copied into a single directory. 624 // task.out contains the per-shard paths, and copyTo contains the corresponding 625 // final path. The files need to be copied into the final directory by a 626 // single rule so it can remove the directory before it starts to ensure no 627 // old files remain. zipsync already does this, so build up zipArgs that 628 // zip all the per-shard directories into a single zip. 629 outputFiles = append(outputFiles, task.copyTo...) 630 copyFrom = append(copyFrom, task.out.Paths()...) 631 zipArgs.WriteString(" -C " + task.genDir.String()) 632 zipArgs.WriteString(android.JoinWithPrefix(task.out.Strings(), " -f ")) 633 } else { 634 outputFiles = append(outputFiles, task.out...) 635 } 636 } 637 638 if len(copyFrom) > 0 { 639 // Create a rule that zips all the per-shard directories into a single zip and then 640 // uses zipsync to unzip it into the final directory. 641 ctx.Build(pctx, android.BuildParams{ 642 Rule: gensrcsMerge, 643 Implicits: copyFrom, 644 Outputs: outputFiles, 645 Description: "merge shards", 646 Args: map[string]string{ 647 "zipArgs": zipArgs.String(), 648 "tmpZip": android.PathForModuleGen(ctx, g.subDir+".zip").String(), 649 "genDir": android.PathForModuleGen(ctx, g.subDir).String(), 650 }, 651 }) 652 } 653 654 g.outputFiles = outputFiles.Paths() 655} 656 657func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) { 658 g.generateCommonBuildActions(ctx) 659 660 // For <= 6 outputs, just embed those directly in the users. Right now, that covers >90% of 661 // the genrules on AOSP. That will make things simpler to look at the graph in the common 662 // case. For larger sets of outputs, inject a phony target in between to limit ninja file 663 // growth. 664 if len(g.outputFiles) <= 6 { 665 g.outputDeps = g.outputFiles 666 } else { 667 phonyFile := android.PathForModuleGen(ctx, "genrule-phony") 668 ctx.Build(pctx, android.BuildParams{ 669 Rule: blueprint.Phony, 670 Output: phonyFile, 671 Inputs: g.outputFiles, 672 }) 673 g.outputDeps = android.Paths{phonyFile} 674 } 675 676 g.setOutputFiles(ctx) 677 678 if ctx.Os() == android.Windows { 679 // Make doesn't support windows: 680 // https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/module_arch_supported.mk;l=66;drc=f264690860bb6ee7762784d6b7201aae057ba6f2 681 g.HideFromMake() 682 } 683} 684 685func (g *Module) setOutputFiles(ctx android.ModuleContext) { 686 if len(g.outputFiles) == 0 { 687 return 688 } 689 ctx.SetOutputFiles(g.outputFiles, "") 690 // non-empty-string-tag should match one of the outputs 691 for _, files := range g.outputFiles { 692 ctx.SetOutputFiles(android.Paths{files}, files.Rel()) 693 } 694} 695 696// Collect information for opening IDE project files in java/jdeps.go. 697func (g *Module) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) { 698 dpInfo.Srcs = append(dpInfo.Srcs, g.Srcs().Strings()...) 699 for _, src := range g.properties.Srcs.GetOrDefault(ctx, nil) { 700 if strings.HasPrefix(src, ":") { 701 src = strings.Trim(src, ":") 702 dpInfo.Deps = append(dpInfo.Deps, src) 703 } 704 } 705} 706 707func (g *Module) AndroidMk() android.AndroidMkData { 708 return android.AndroidMkData{ 709 Class: "ETC", 710 OutputFile: android.OptionalPathForPath(g.outputFiles[0]), 711 SubName: g.subName, 712 Extra: []android.AndroidMkExtraFunc{ 713 func(w io.Writer, outputFile android.Path) { 714 fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true") 715 }, 716 }, 717 Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { 718 android.WriteAndroidMkData(w, data) 719 if data.SubName != "" { 720 fmt.Fprintln(w, ".PHONY:", name) 721 fmt.Fprintln(w, name, ":", name+g.subName) 722 } 723 }, 724 } 725} 726 727var _ android.ApexModule = (*Module)(nil) 728 729// Implements android.ApexModule 730func (m *Module) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel { 731 return android.MinApiLevel 732} 733 734func generatorFactory(taskGenerator taskFunc, props ...interface{}) *Module { 735 module := &Module{ 736 taskGenerator: taskGenerator, 737 } 738 739 module.AddProperties(props...) 740 module.AddProperties(&module.properties) 741 742 module.ImageInterface = noopImageInterface{} 743 744 return module 745} 746 747type noopImageInterface struct{} 748 749func (x noopImageInterface) ImageMutatorBegin(android.ImageInterfaceContext) {} 750func (x noopImageInterface) VendorVariantNeeded(android.ImageInterfaceContext) bool { return false } 751func (x noopImageInterface) ProductVariantNeeded(android.ImageInterfaceContext) bool { return false } 752func (x noopImageInterface) CoreVariantNeeded(android.ImageInterfaceContext) bool { return false } 753func (x noopImageInterface) RamdiskVariantNeeded(android.ImageInterfaceContext) bool { return false } 754func (x noopImageInterface) VendorRamdiskVariantNeeded(android.ImageInterfaceContext) bool { 755 return false 756} 757func (x noopImageInterface) DebugRamdiskVariantNeeded(android.ImageInterfaceContext) bool { 758 return false 759} 760func (x noopImageInterface) RecoveryVariantNeeded(android.ImageInterfaceContext) bool { return false } 761func (x noopImageInterface) ExtraImageVariations(ctx android.ImageInterfaceContext) []string { 762 return nil 763} 764func (x noopImageInterface) SetImageVariation(ctx android.ImageInterfaceContext, variation string) { 765} 766 767func NewGenSrcs() *Module { 768 properties := &genSrcsProperties{} 769 770 // finalSubDir is the name of the subdirectory that output files will be generated into. 771 // It is used so that per-shard directories can be placed alongside it an then finally 772 // merged into it. 773 const finalSubDir = "gensrcs" 774 775 taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask { 776 shardSize := defaultShardSize 777 if s := properties.Shard_size; s != nil { 778 shardSize = int(*s) 779 } 780 781 // gensrcs rules can easily hit command line limits by repeating the command for 782 // every input file. Shard the input files into groups. 783 shards := android.ShardPaths(srcFiles, shardSize) 784 var generateTasks []generateTask 785 786 for i, shard := range shards { 787 var commands []string 788 var outFiles android.WritablePaths 789 var copyTo android.WritablePaths 790 791 // When sharding is enabled (i.e. len(shards) > 1), the sbox rules for each 792 // shard will be write to their own directories and then be merged together 793 // into finalSubDir. If sharding is not enabled (i.e. len(shards) == 1), 794 // the sbox rule will write directly to finalSubDir. 795 genSubDir := finalSubDir 796 if len(shards) > 1 { 797 genSubDir = strconv.Itoa(i) 798 } 799 800 genDir := android.PathForModuleGen(ctx, genSubDir) 801 // TODO(ccross): this RuleBuilder is a hack to be able to call 802 // rule.Command().PathForOutput. Replace this with passing the rule into the 803 // generator. 804 rule := getSandboxedRuleBuilder(ctx, android.NewRuleBuilder(pctx, ctx).Sbox(genDir, nil)) 805 806 for _, in := range shard { 807 outFile := android.GenPathWithExtAndTrimExt(ctx, finalSubDir, in, String(properties.Output_extension), String(properties.Trim_extension)) 808 809 // If sharding is enabled, then outFile is the path to the output file in 810 // the shard directory, and copyTo is the path to the output file in the 811 // final directory. 812 if len(shards) > 1 { 813 shardFile := android.GenPathWithExtAndTrimExt(ctx, genSubDir, in, String(properties.Output_extension), String(properties.Trim_extension)) 814 copyTo = append(copyTo, outFile) 815 outFile = shardFile 816 } 817 818 outFiles = append(outFiles, outFile) 819 820 // pre-expand the command line to replace $in and $out with references to 821 // a single input and output file. 822 command, err := android.Expand(rawCommand, func(name string) (string, error) { 823 switch name { 824 case "in": 825 return in.String(), nil 826 case "out": 827 return rule.Command().PathForOutput(outFile), nil 828 default: 829 return "$(" + name + ")", nil 830 } 831 }) 832 if err != nil { 833 ctx.PropertyErrorf("cmd", err.Error()) 834 } 835 836 // escape the command in case for example it contains '#', an odd number of '"', etc 837 command = fmt.Sprintf("bash -c %v", proptools.ShellEscape(command)) 838 commands = append(commands, command) 839 } 840 fullCommand := strings.Join(commands, " && ") 841 842 generateTasks = append(generateTasks, generateTask{ 843 in: shard, 844 out: outFiles, 845 copyTo: copyTo, 846 genDir: genDir, 847 cmd: fullCommand, 848 shard: i, 849 shards: len(shards), 850 extraInputs: map[string][]string{ 851 "data": properties.Data, 852 }, 853 }) 854 } 855 856 return generateTasks 857 } 858 859 g := generatorFactory(taskGenerator, properties) 860 g.subDir = finalSubDir 861 return g 862} 863 864func GenSrcsFactory() android.Module { 865 m := NewGenSrcs() 866 android.InitAndroidModule(m) 867 android.InitDefaultableModule(m) 868 return m 869} 870 871type genSrcsProperties struct { 872 // extension that will be substituted for each output file 873 Output_extension *string 874 875 // maximum number of files that will be passed on a single command line. 876 Shard_size *int64 877 878 // Additional files needed for build that are not tooling related. 879 Data []string `android:"path"` 880 881 // Trim the matched extension for each input file, and it should start with ".". 882 Trim_extension *string 883} 884 885const defaultShardSize = 50 886 887func NewGenRule() *Module { 888 properties := &genRuleProperties{} 889 890 taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask { 891 useNsjail := Bool(properties.Use_nsjail) 892 893 dirSrcs := android.DirectoryPathsForModuleSrc(ctx, properties.Dir_srcs) 894 if len(dirSrcs) > 0 && !useNsjail { 895 ctx.PropertyErrorf("dir_srcs", "can't use dir_srcs if use_nsjail is false") 896 return nil 897 } 898 899 keepGendir := Bool(properties.Keep_gendir) 900 if keepGendir && !useNsjail { 901 ctx.PropertyErrorf("keep_gendir", "can't use keep_gendir if use_nsjail is false") 902 return nil 903 } 904 905 outs := make(android.WritablePaths, len(properties.Out)) 906 for i, out := range properties.Out { 907 outs[i] = android.PathForModuleGen(ctx, out) 908 } 909 return []generateTask{{ 910 in: srcFiles, 911 out: outs, 912 genDir: android.PathForModuleGen(ctx), 913 cmd: rawCommand, 914 useNsjail: useNsjail, 915 dirSrcs: dirSrcs, 916 keepGendir: keepGendir, 917 }} 918 } 919 920 return generatorFactory(taskGenerator, properties) 921} 922 923func GenRuleFactory() android.Module { 924 m := NewGenRule() 925 android.InitAndroidModule(m) 926 android.InitDefaultableModule(m) 927 return m 928} 929 930type genRuleProperties struct { 931 Use_nsjail *bool 932 933 // List of input directories. Can be set only when use_nsjail is true. Currently, usage of 934 // dir_srcs is limited only to Trusty build. 935 Dir_srcs []string `android:"path"` 936 937 // If set to true, $(genDir) is not truncated. Useful when this genrule can be incrementally 938 // built. Can be set only when use_nsjail is true. 939 Keep_gendir *bool 940 941 // names of the output files that will be generated 942 Out []string `android:"arch_variant"` 943} 944 945var Bool = proptools.Bool 946var String = proptools.String 947 948// Defaults 949type Defaults struct { 950 android.ModuleBase 951 android.DefaultsModuleBase 952} 953 954func defaultsFactory() android.Module { 955 return DefaultsFactory() 956} 957 958func DefaultsFactory(props ...interface{}) android.Module { 959 module := &Defaults{} 960 961 module.AddProperties(props...) 962 module.AddProperties( 963 &generatorProperties{}, 964 &genRuleProperties{}, 965 ) 966 967 android.InitDefaultsModule(module) 968 969 return module 970} 971 972func getSandboxedRuleBuilder(ctx android.ModuleContext, r *android.RuleBuilder) *android.RuleBuilder { 973 if !ctx.DeviceConfig().GenruleSandboxing() { 974 return r.SandboxTools() 975 } 976 return r.SandboxInputs() 977} 978