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