• 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
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