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 15package genrule 16 17import ( 18 "fmt" 19 "io" 20 "strconv" 21 "strings" 22 23 "github.com/google/blueprint" 24 "github.com/google/blueprint/bootstrap" 25 "github.com/google/blueprint/proptools" 26 27 "android/soong/android" 28 "android/soong/shared" 29 "crypto/sha256" 30 "path/filepath" 31) 32 33func init() { 34 registerGenruleBuildComponents(android.InitRegistrationContext) 35} 36 37func registerGenruleBuildComponents(ctx android.RegistrationContext) { 38 ctx.RegisterModuleType("genrule_defaults", defaultsFactory) 39 40 ctx.RegisterModuleType("gensrcs", GenSrcsFactory) 41 ctx.RegisterModuleType("genrule", GenRuleFactory) 42 43 ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) { 44 ctx.BottomUp("genrule_tool_deps", toolDepsMutator).Parallel() 45 }) 46} 47 48var ( 49 pctx = android.NewPackageContext("android/soong/genrule") 50 51 gensrcsMerge = pctx.AndroidStaticRule("gensrcsMerge", blueprint.RuleParams{ 52 Command: "${soongZip} -o ${tmpZip} @${tmpZip}.rsp && ${zipSync} -d ${genDir} ${tmpZip}", 53 CommandDeps: []string{"${soongZip}", "${zipSync}"}, 54 Rspfile: "${tmpZip}.rsp", 55 RspfileContent: "${zipArgs}", 56 }, "tmpZip", "genDir", "zipArgs") 57) 58 59func init() { 60 pctx.Import("android/soong/android") 61 pctx.HostBinToolVariable("sboxCmd", "sbox") 62 63 pctx.HostBinToolVariable("soongZip", "soong_zip") 64 pctx.HostBinToolVariable("zipSync", "zipsync") 65} 66 67type SourceFileGenerator interface { 68 GeneratedSourceFiles() android.Paths 69 GeneratedHeaderDirs() android.Paths 70 GeneratedDeps() android.Paths 71} 72 73// Alias for android.HostToolProvider 74// Deprecated: use android.HostToolProvider instead. 75type HostToolProvider interface { 76 android.HostToolProvider 77} 78 79type hostToolDependencyTag struct { 80 blueprint.BaseDependencyTag 81 label string 82} 83 84type generatorProperties struct { 85 // The command to run on one or more input files. Cmd supports substitution of a few variables 86 // 87 // Available variables for substitution: 88 // 89 // $(location): the path to the first entry in tools or tool_files 90 // $(location <label>): the path to the tool, tool_file, input or output with name <label> 91 // $(in): one or more input files 92 // $(out): a single output file 93 // $(depfile): a file to which dependencies will be written, if the depfile property is set to true 94 // $(genDir): the sandbox directory for this tool; contains $(out) 95 // $$: a literal $ 96 Cmd *string 97 98 // Enable reading a file containing dependencies in gcc format after the command completes 99 Depfile *bool 100 101 // name of the modules (if any) that produces the host executable. Leave empty for 102 // prebuilts or scripts that do not need a module to build them. 103 Tools []string 104 105 // Local file that is used as the tool 106 Tool_files []string `android:"path"` 107 108 // List of directories to export generated headers from 109 Export_include_dirs []string 110 111 // list of input files 112 Srcs []string `android:"path,arch_variant"` 113 114 // input files to exclude 115 Exclude_srcs []string `android:"path,arch_variant"` 116} 117 118type Module struct { 119 android.ModuleBase 120 android.DefaultableModuleBase 121 android.ApexModuleBase 122 123 // For other packages to make their own genrules with extra 124 // properties 125 Extra interface{} 126 android.ImageInterface 127 128 properties generatorProperties 129 130 taskGenerator taskFunc 131 132 deps android.Paths 133 rule blueprint.Rule 134 rawCommands []string 135 136 exportedIncludeDirs android.Paths 137 138 outputFiles android.Paths 139 outputDeps android.Paths 140 141 subName string 142 subDir string 143 144 // Collect the module directory for IDE info in java/jdeps.go. 145 modulePaths []string 146} 147 148type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask 149 150type generateTask struct { 151 in android.Paths 152 out android.WritablePaths 153 copyTo android.WritablePaths 154 genDir android.WritablePath 155 sandboxOuts []string 156 cmd string 157 shard int 158 shards int 159} 160 161func (g *Module) GeneratedSourceFiles() android.Paths { 162 return g.outputFiles 163} 164 165func (g *Module) Srcs() android.Paths { 166 return append(android.Paths{}, g.outputFiles...) 167} 168 169func (g *Module) GeneratedHeaderDirs() android.Paths { 170 return g.exportedIncludeDirs 171} 172 173func (g *Module) GeneratedDeps() android.Paths { 174 return g.outputDeps 175} 176 177func toolDepsMutator(ctx android.BottomUpMutatorContext) { 178 if g, ok := ctx.Module().(*Module); ok { 179 for _, tool := range g.properties.Tools { 180 tag := hostToolDependencyTag{label: tool} 181 if m := android.SrcIsModule(tool); m != "" { 182 tool = m 183 } 184 ctx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), tag, tool) 185 } 186 } 187} 188 189func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) { 190 g.subName = ctx.ModuleSubDir() 191 192 // Collect the module directory for IDE info in java/jdeps.go. 193 g.modulePaths = append(g.modulePaths, ctx.ModuleDir()) 194 195 if len(g.properties.Export_include_dirs) > 0 { 196 for _, dir := range g.properties.Export_include_dirs { 197 g.exportedIncludeDirs = append(g.exportedIncludeDirs, 198 android.PathForModuleGen(ctx, g.subDir, ctx.ModuleDir(), dir)) 199 } 200 } else { 201 g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForModuleGen(ctx, g.subDir)) 202 } 203 204 locationLabels := map[string][]string{} 205 firstLabel := "" 206 207 addLocationLabel := func(label string, paths []string) { 208 if firstLabel == "" { 209 firstLabel = label 210 } 211 if _, exists := locationLabels[label]; !exists { 212 locationLabels[label] = paths 213 } else { 214 ctx.ModuleErrorf("multiple labels for %q, %q and %q", 215 label, strings.Join(locationLabels[label], " "), strings.Join(paths, " ")) 216 } 217 } 218 219 if len(g.properties.Tools) > 0 { 220 seenTools := make(map[string]bool) 221 222 ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) { 223 switch tag := ctx.OtherModuleDependencyTag(module).(type) { 224 case hostToolDependencyTag: 225 tool := ctx.OtherModuleName(module) 226 var path android.OptionalPath 227 228 if t, ok := module.(android.HostToolProvider); ok { 229 if !t.(android.Module).Enabled() { 230 if ctx.Config().AllowMissingDependencies() { 231 ctx.AddMissingDependencies([]string{tool}) 232 } else { 233 ctx.ModuleErrorf("depends on disabled module %q", tool) 234 } 235 break 236 } 237 path = t.HostToolPath() 238 } else if t, ok := module.(bootstrap.GoBinaryTool); ok { 239 if s, err := filepath.Rel(android.PathForOutput(ctx).String(), t.InstallPath()); err == nil { 240 path = android.OptionalPathForPath(android.PathForOutput(ctx, s)) 241 } else { 242 ctx.ModuleErrorf("cannot find path for %q: %v", tool, err) 243 break 244 } 245 } else { 246 ctx.ModuleErrorf("%q is not a host tool provider", tool) 247 break 248 } 249 250 if path.Valid() { 251 g.deps = append(g.deps, path.Path()) 252 addLocationLabel(tag.label, []string{path.Path().String()}) 253 seenTools[tag.label] = true 254 } else { 255 ctx.ModuleErrorf("host tool %q missing output file", tool) 256 } 257 } 258 }) 259 260 // If AllowMissingDependencies is enabled, the build will not have stopped when 261 // AddFarVariationDependencies was called on a missing tool, which will result in nonsensical 262 // "cmd: unknown location label ..." errors later. Add a placeholder file to the local label. 263 // The command that uses this placeholder file will never be executed because the rule will be 264 // replaced with an android.Error rule reporting the missing dependencies. 265 if ctx.Config().AllowMissingDependencies() { 266 for _, tool := range g.properties.Tools { 267 if !seenTools[tool] { 268 addLocationLabel(tool, []string{"***missing tool " + tool + "***"}) 269 } 270 } 271 } 272 } 273 274 if ctx.Failed() { 275 return 276 } 277 278 for _, toolFile := range g.properties.Tool_files { 279 paths := android.PathsForModuleSrc(ctx, []string{toolFile}) 280 g.deps = append(g.deps, paths...) 281 addLocationLabel(toolFile, paths.Strings()) 282 } 283 284 var srcFiles android.Paths 285 for _, in := range g.properties.Srcs { 286 paths, missingDeps := android.PathsAndMissingDepsForModuleSrcExcludes(ctx, []string{in}, g.properties.Exclude_srcs) 287 if len(missingDeps) > 0 { 288 if !ctx.Config().AllowMissingDependencies() { 289 panic(fmt.Errorf("should never get here, the missing dependencies %q should have been reported in DepsMutator", 290 missingDeps)) 291 } 292 293 // If AllowMissingDependencies is enabled, the build will not have stopped when 294 // the dependency was added on a missing SourceFileProducer module, which will result in nonsensical 295 // "cmd: label ":..." has no files" errors later. Add a placeholder file to the local label. 296 // The command that uses this placeholder file will never be executed because the rule will be 297 // replaced with an android.Error rule reporting the missing dependencies. 298 ctx.AddMissingDependencies(missingDeps) 299 addLocationLabel(in, []string{"***missing srcs " + in + "***"}) 300 } else { 301 srcFiles = append(srcFiles, paths...) 302 addLocationLabel(in, paths.Strings()) 303 } 304 } 305 306 var copyFrom android.Paths 307 var outputFiles android.WritablePaths 308 var zipArgs strings.Builder 309 310 for _, task := range g.taskGenerator(ctx, String(g.properties.Cmd), srcFiles) { 311 for _, out := range task.out { 312 addLocationLabel(out.Rel(), []string{filepath.Join("__SBOX_OUT_DIR__", out.Rel())}) 313 } 314 315 referencedIn := false 316 referencedDepfile := false 317 318 rawCommand, err := android.ExpandNinjaEscaped(task.cmd, func(name string) (string, bool, error) { 319 // report the error directly without returning an error to android.Expand to catch multiple errors in a 320 // single run 321 reportError := func(fmt string, args ...interface{}) (string, bool, error) { 322 ctx.PropertyErrorf("cmd", fmt, args...) 323 return "SOONG_ERROR", false, nil 324 } 325 326 switch name { 327 case "location": 328 if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 { 329 return reportError("at least one `tools` or `tool_files` is required if $(location) is used") 330 } 331 paths := locationLabels[firstLabel] 332 if len(paths) == 0 { 333 return reportError("default label %q has no files", firstLabel) 334 } else if len(paths) > 1 { 335 return reportError("default label %q has multiple files, use $(locations %s) to reference it", 336 firstLabel, firstLabel) 337 } 338 return locationLabels[firstLabel][0], false, nil 339 case "in": 340 referencedIn = true 341 return "${in}", true, nil 342 case "out": 343 return "__SBOX_OUT_FILES__", false, nil 344 case "depfile": 345 referencedDepfile = true 346 if !Bool(g.properties.Depfile) { 347 return reportError("$(depfile) used without depfile property") 348 } 349 return "__SBOX_DEPFILE__", false, nil 350 case "genDir": 351 return "__SBOX_OUT_DIR__", false, nil 352 default: 353 if strings.HasPrefix(name, "location ") { 354 label := strings.TrimSpace(strings.TrimPrefix(name, "location ")) 355 if paths, ok := locationLabels[label]; ok { 356 if len(paths) == 0 { 357 return reportError("label %q has no files", label) 358 } else if len(paths) > 1 { 359 return reportError("label %q has multiple files, use $(locations %s) to reference it", 360 label, label) 361 } 362 return paths[0], false, nil 363 } else { 364 return reportError("unknown location label %q", label) 365 } 366 } else if strings.HasPrefix(name, "locations ") { 367 label := strings.TrimSpace(strings.TrimPrefix(name, "locations ")) 368 if paths, ok := locationLabels[label]; ok { 369 if len(paths) == 0 { 370 return reportError("label %q has no files", label) 371 } 372 return strings.Join(paths, " "), false, nil 373 } else { 374 return reportError("unknown locations label %q", label) 375 } 376 } else { 377 return reportError("unknown variable '$(%s)'", name) 378 } 379 } 380 }) 381 382 if err != nil { 383 ctx.PropertyErrorf("cmd", "%s", err.Error()) 384 return 385 } 386 387 if Bool(g.properties.Depfile) && !referencedDepfile { 388 ctx.PropertyErrorf("cmd", "specified depfile=true but did not include a reference to '${depfile}' in cmd") 389 return 390 } 391 392 // tell the sbox command which directory to use as its sandbox root 393 buildDir := android.PathForOutput(ctx).String() 394 sandboxPath := shared.TempDirForOutDir(buildDir) 395 396 // recall that Sprintf replaces percent sign expressions, whereas dollar signs expressions remain as written, 397 // to be replaced later by ninja_strings.go 398 depfilePlaceholder := "" 399 if Bool(g.properties.Depfile) { 400 depfilePlaceholder = "$depfileArgs" 401 } 402 403 // Escape the command for the shell 404 rawCommand = "'" + strings.Replace(rawCommand, "'", `'\''`, -1) + "'" 405 g.rawCommands = append(g.rawCommands, rawCommand) 406 407 sandboxCommand := fmt.Sprintf("rm -rf %s && $sboxCmd --sandbox-path %s --output-root %s", 408 task.genDir, sandboxPath, task.genDir) 409 410 if !referencedIn { 411 sandboxCommand = sandboxCommand + hashSrcFiles(srcFiles) 412 } 413 414 sandboxCommand = sandboxCommand + fmt.Sprintf(" -c %s %s $allouts", 415 rawCommand, depfilePlaceholder) 416 417 ruleParams := blueprint.RuleParams{ 418 Command: sandboxCommand, 419 CommandDeps: []string{"$sboxCmd"}, 420 } 421 args := []string{"allouts"} 422 if Bool(g.properties.Depfile) { 423 ruleParams.Deps = blueprint.DepsGCC 424 args = append(args, "depfileArgs") 425 } 426 name := "generator" 427 if task.shards > 1 { 428 name += strconv.Itoa(task.shard) 429 } 430 rule := ctx.Rule(pctx, name, ruleParams, args...) 431 432 g.generateSourceFile(ctx, task, rule) 433 434 if len(task.copyTo) > 0 { 435 outputFiles = append(outputFiles, task.copyTo...) 436 copyFrom = append(copyFrom, task.out.Paths()...) 437 zipArgs.WriteString(" -C " + task.genDir.String()) 438 zipArgs.WriteString(android.JoinWithPrefix(task.out.Strings(), " -f ")) 439 } else { 440 outputFiles = append(outputFiles, task.out...) 441 } 442 } 443 444 if len(copyFrom) > 0 { 445 ctx.Build(pctx, android.BuildParams{ 446 Rule: gensrcsMerge, 447 Implicits: copyFrom, 448 Outputs: outputFiles, 449 Args: map[string]string{ 450 "zipArgs": zipArgs.String(), 451 "tmpZip": android.PathForModuleGen(ctx, g.subDir+".zip").String(), 452 "genDir": android.PathForModuleGen(ctx, g.subDir).String(), 453 }, 454 }) 455 } 456 457 g.outputFiles = outputFiles.Paths() 458 459 // For <= 6 outputs, just embed those directly in the users. Right now, that covers >90% of 460 // the genrules on AOSP. That will make things simpler to look at the graph in the common 461 // case. For larger sets of outputs, inject a phony target in between to limit ninja file 462 // growth. 463 if len(g.outputFiles) <= 6 { 464 g.outputDeps = g.outputFiles 465 } else { 466 phonyFile := android.PathForModuleGen(ctx, "genrule-phony") 467 468 ctx.Build(pctx, android.BuildParams{ 469 Rule: blueprint.Phony, 470 Output: phonyFile, 471 Inputs: g.outputFiles, 472 }) 473 474 g.outputDeps = android.Paths{phonyFile} 475 } 476 477} 478 479func hashSrcFiles(srcFiles android.Paths) string { 480 h := sha256.New() 481 for _, src := range srcFiles { 482 h.Write([]byte(src.String())) 483 } 484 return fmt.Sprintf(" --input-hash %x", h.Sum(nil)) 485} 486 487func (g *Module) generateSourceFile(ctx android.ModuleContext, task generateTask, rule blueprint.Rule) { 488 desc := "generate" 489 if len(task.out) == 0 { 490 ctx.ModuleErrorf("must have at least one output file") 491 return 492 } 493 if len(task.out) == 1 { 494 desc += " " + task.out[0].Base() 495 } 496 497 var depFile android.ModuleGenPath 498 if Bool(g.properties.Depfile) { 499 depFile = android.PathForModuleGen(ctx, task.out[0].Rel()+".d") 500 } 501 502 if task.shards > 1 { 503 desc += " " + strconv.Itoa(task.shard) 504 } 505 506 params := android.BuildParams{ 507 Rule: rule, 508 Description: desc, 509 Output: task.out[0], 510 ImplicitOutputs: task.out[1:], 511 Inputs: task.in, 512 Implicits: g.deps, 513 Args: map[string]string{ 514 "allouts": strings.Join(task.sandboxOuts, " "), 515 }, 516 } 517 if Bool(g.properties.Depfile) { 518 params.Depfile = android.PathForModuleGen(ctx, task.out[0].Rel()+".d") 519 params.Args["depfileArgs"] = "--depfile-out " + depFile.String() 520 } 521 522 ctx.Build(pctx, params) 523} 524 525// Collect information for opening IDE project files in java/jdeps.go. 526func (g *Module) IDEInfo(dpInfo *android.IdeInfo) { 527 dpInfo.Srcs = append(dpInfo.Srcs, g.Srcs().Strings()...) 528 for _, src := range g.properties.Srcs { 529 if strings.HasPrefix(src, ":") { 530 src = strings.Trim(src, ":") 531 dpInfo.Deps = append(dpInfo.Deps, src) 532 } 533 } 534 dpInfo.Paths = append(dpInfo.Paths, g.modulePaths...) 535} 536 537func (g *Module) AndroidMk() android.AndroidMkData { 538 return android.AndroidMkData{ 539 Include: "$(BUILD_PHONY_PACKAGE)", 540 Class: "FAKE", 541 OutputFile: android.OptionalPathForPath(g.outputFiles[0]), 542 SubName: g.subName, 543 Extra: []android.AndroidMkExtraFunc{ 544 func(w io.Writer, outputFile android.Path) { 545 fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES :=", strings.Join(g.outputDeps.Strings(), " ")) 546 }, 547 }, 548 Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { 549 android.WriteAndroidMkData(w, data) 550 if data.SubName != "" { 551 fmt.Fprintln(w, ".PHONY:", name) 552 fmt.Fprintln(w, name, ":", name+g.subName) 553 } 554 }, 555 } 556} 557 558func (g *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion int) error { 559 // Because generated outputs are checked by client modules(e.g. cc_library, ...) 560 // we can safely ignore the check here. 561 return nil 562} 563 564func generatorFactory(taskGenerator taskFunc, props ...interface{}) *Module { 565 module := &Module{ 566 taskGenerator: taskGenerator, 567 } 568 569 module.AddProperties(props...) 570 module.AddProperties(&module.properties) 571 572 module.ImageInterface = noopImageInterface{} 573 574 return module 575} 576 577type noopImageInterface struct{} 578 579func (x noopImageInterface) ImageMutatorBegin(android.BaseModuleContext) {} 580func (x noopImageInterface) CoreVariantNeeded(android.BaseModuleContext) bool { return false } 581func (x noopImageInterface) RamdiskVariantNeeded(android.BaseModuleContext) bool { return false } 582func (x noopImageInterface) RecoveryVariantNeeded(android.BaseModuleContext) bool { return false } 583func (x noopImageInterface) ExtraImageVariations(ctx android.BaseModuleContext) []string { return nil } 584func (x noopImageInterface) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) { 585} 586 587// replace "out" with "__SBOX_OUT_DIR__/<the value of ${out}>" 588func pathToSandboxOut(path android.Path, genDir android.Path) string { 589 relOut, err := filepath.Rel(genDir.String(), path.String()) 590 if err != nil { 591 panic(fmt.Sprintf("Could not make ${out} relative: %v", err)) 592 } 593 return filepath.Join("__SBOX_OUT_DIR__", relOut) 594 595} 596 597func NewGenSrcs() *Module { 598 properties := &genSrcsProperties{} 599 600 taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask { 601 genDir := android.PathForModuleGen(ctx, "gensrcs") 602 shardSize := defaultShardSize 603 if s := properties.Shard_size; s != nil { 604 shardSize = int(*s) 605 } 606 607 shards := android.ShardPaths(srcFiles, shardSize) 608 var generateTasks []generateTask 609 610 for i, shard := range shards { 611 var commands []string 612 var outFiles android.WritablePaths 613 var copyTo android.WritablePaths 614 var shardDir android.WritablePath 615 var sandboxOuts []string 616 617 if len(shards) > 1 { 618 shardDir = android.PathForModuleGen(ctx, strconv.Itoa(i)) 619 } else { 620 shardDir = genDir 621 } 622 623 for _, in := range shard { 624 outFile := android.GenPathWithExt(ctx, "gensrcs", in, String(properties.Output_extension)) 625 sandboxOutfile := pathToSandboxOut(outFile, genDir) 626 627 if len(shards) > 1 { 628 shardFile := android.GenPathWithExt(ctx, strconv.Itoa(i), in, String(properties.Output_extension)) 629 copyTo = append(copyTo, outFile) 630 outFile = shardFile 631 } 632 633 outFiles = append(outFiles, outFile) 634 sandboxOuts = append(sandboxOuts, sandboxOutfile) 635 636 command, err := android.Expand(rawCommand, func(name string) (string, error) { 637 switch name { 638 case "in": 639 return in.String(), nil 640 case "out": 641 return sandboxOutfile, nil 642 default: 643 return "$(" + name + ")", nil 644 } 645 }) 646 if err != nil { 647 ctx.PropertyErrorf("cmd", err.Error()) 648 } 649 650 // escape the command in case for example it contains '#', an odd number of '"', etc 651 command = fmt.Sprintf("bash -c %v", proptools.ShellEscape(command)) 652 commands = append(commands, command) 653 } 654 fullCommand := strings.Join(commands, " && ") 655 656 generateTasks = append(generateTasks, generateTask{ 657 in: shard, 658 out: outFiles, 659 copyTo: copyTo, 660 genDir: shardDir, 661 sandboxOuts: sandboxOuts, 662 cmd: fullCommand, 663 shard: i, 664 shards: len(shards), 665 }) 666 } 667 668 return generateTasks 669 } 670 671 g := generatorFactory(taskGenerator, properties) 672 g.subDir = "gensrcs" 673 return g 674} 675 676func GenSrcsFactory() android.Module { 677 m := NewGenSrcs() 678 android.InitAndroidModule(m) 679 return m 680} 681 682type genSrcsProperties struct { 683 // extension that will be substituted for each output file 684 Output_extension *string 685 686 // maximum number of files that will be passed on a single command line. 687 Shard_size *int64 688} 689 690const defaultShardSize = 100 691 692func NewGenRule() *Module { 693 properties := &genRuleProperties{} 694 695 taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask { 696 outs := make(android.WritablePaths, len(properties.Out)) 697 sandboxOuts := make([]string, len(properties.Out)) 698 genDir := android.PathForModuleGen(ctx) 699 for i, out := range properties.Out { 700 outs[i] = android.PathForModuleGen(ctx, out) 701 sandboxOuts[i] = pathToSandboxOut(outs[i], genDir) 702 } 703 return []generateTask{{ 704 in: srcFiles, 705 out: outs, 706 genDir: android.PathForModuleGen(ctx), 707 sandboxOuts: sandboxOuts, 708 cmd: rawCommand, 709 }} 710 } 711 712 return generatorFactory(taskGenerator, properties) 713} 714 715func GenRuleFactory() android.Module { 716 m := NewGenRule() 717 android.InitAndroidModule(m) 718 android.InitDefaultableModule(m) 719 return m 720} 721 722type genRuleProperties struct { 723 // names of the output files that will be generated 724 Out []string `android:"arch_variant"` 725} 726 727var Bool = proptools.Bool 728var String = proptools.String 729 730// 731// Defaults 732// 733type Defaults struct { 734 android.ModuleBase 735 android.DefaultsModuleBase 736} 737 738func defaultsFactory() android.Module { 739 return DefaultsFactory() 740} 741 742func DefaultsFactory(props ...interface{}) android.Module { 743 module := &Defaults{} 744 745 module.AddProperties(props...) 746 module.AddProperties( 747 &generatorProperties{}, 748 &genRuleProperties{}, 749 ) 750 751 android.InitDefaultsModule(module) 752 753 return module 754} 755