• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2018 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 android
16
17import (
18	"crypto/sha256"
19	"fmt"
20	"path/filepath"
21	"sort"
22	"strings"
23	"testing"
24
25	"github.com/google/blueprint"
26	"github.com/google/blueprint/proptools"
27	"google.golang.org/protobuf/encoding/prototext"
28	"google.golang.org/protobuf/proto"
29
30	"android/soong/cmd/sbox/sbox_proto"
31	"android/soong/remoteexec"
32	"android/soong/response"
33	"android/soong/shared"
34)
35
36const sboxSandboxBaseDir = "__SBOX_SANDBOX_DIR__"
37const sboxOutSubDir = "out"
38const sboxToolsSubDir = "tools"
39const sboxOutDir = sboxSandboxBaseDir + "/" + sboxOutSubDir
40
41// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
42// graph.
43type RuleBuilder struct {
44	pctx PackageContext
45	ctx  BuilderContext
46
47	commands         []*RuleBuilderCommand
48	installs         RuleBuilderInstalls
49	temporariesSet   map[WritablePath]bool
50	restat           bool
51	sbox             bool
52	highmem          bool
53	remoteable       RemoteRuleSupports
54	rbeParams        *remoteexec.REParams
55	outDir           WritablePath
56	sboxTools        bool
57	sboxInputs       bool
58	sboxManifestPath WritablePath
59	missingDeps      []string
60}
61
62// NewRuleBuilder returns a newly created RuleBuilder.
63func NewRuleBuilder(pctx PackageContext, ctx BuilderContext) *RuleBuilder {
64	return &RuleBuilder{
65		pctx:           pctx,
66		ctx:            ctx,
67		temporariesSet: make(map[WritablePath]bool),
68	}
69}
70
71// RuleBuilderInstall is a tuple of install from and to locations.
72type RuleBuilderInstall struct {
73	From Path
74	To   string
75}
76
77type RuleBuilderInstalls []RuleBuilderInstall
78
79// String returns the RuleBuilderInstalls in the form used by $(call copy-many-files) in Make, a space separated
80// list of from:to tuples.
81func (installs RuleBuilderInstalls) String() string {
82	sb := strings.Builder{}
83	for i, install := range installs {
84		if i != 0 {
85			sb.WriteRune(' ')
86		}
87		sb.WriteString(install.From.String())
88		sb.WriteRune(':')
89		sb.WriteString(install.To)
90	}
91	return sb.String()
92}
93
94// MissingDeps adds modules to the list of missing dependencies.  If MissingDeps
95// is called with a non-empty input, any call to Build will result in a rule
96// that will print an error listing the missing dependencies and fail.
97// MissingDeps should only be called if Config.AllowMissingDependencies() is
98// true.
99func (r *RuleBuilder) MissingDeps(missingDeps []string) {
100	r.missingDeps = append(r.missingDeps, missingDeps...)
101}
102
103// Restat marks the rule as a restat rule, which will be passed to ModuleContext.Rule in BuildParams.Restat.
104func (r *RuleBuilder) Restat() *RuleBuilder {
105	r.restat = true
106	return r
107}
108
109// HighMem marks the rule as a high memory rule, which will limit how many run in parallel with other high memory
110// rules.
111func (r *RuleBuilder) HighMem() *RuleBuilder {
112	r.highmem = true
113	return r
114}
115
116// Remoteable marks the rule as supporting remote execution.
117func (r *RuleBuilder) Remoteable(supports RemoteRuleSupports) *RuleBuilder {
118	r.remoteable = supports
119	return r
120}
121
122// Rewrapper marks the rule as running inside rewrapper using the given params in order to support
123// running on RBE.  During RuleBuilder.Build the params will be combined with the inputs, outputs
124// and tools known to RuleBuilder to prepend an appropriate rewrapper command line to the rule's
125// command line.
126func (r *RuleBuilder) Rewrapper(params *remoteexec.REParams) *RuleBuilder {
127	if !r.sboxInputs {
128		panic(fmt.Errorf("RuleBuilder.Rewrapper must be called after RuleBuilder.SandboxInputs"))
129	}
130	r.rbeParams = params
131	return r
132}
133
134// Sbox marks the rule as needing to be wrapped by sbox. The outputDir should point to the output
135// directory that sbox will wipe. It should not be written to by any other rule. manifestPath should
136// point to a location where sbox's manifest will be written and must be outside outputDir. sbox
137// will ensure that all outputs have been written, and will discard any output files that were not
138// specified.
139func (r *RuleBuilder) Sbox(outputDir WritablePath, manifestPath WritablePath) *RuleBuilder {
140	if r.sbox {
141		panic("Sbox() may not be called more than once")
142	}
143	if len(r.commands) > 0 {
144		panic("Sbox() may not be called after Command()")
145	}
146	r.sbox = true
147	r.outDir = outputDir
148	r.sboxManifestPath = manifestPath
149	return r
150}
151
152// SandboxTools enables tool sandboxing for the rule by copying any referenced tools into the
153// sandbox.
154func (r *RuleBuilder) SandboxTools() *RuleBuilder {
155	if !r.sbox {
156		panic("SandboxTools() must be called after Sbox()")
157	}
158	if len(r.commands) > 0 {
159		panic("SandboxTools() may not be called after Command()")
160	}
161	r.sboxTools = true
162	return r
163}
164
165// SandboxInputs enables input sandboxing for the rule by copying any referenced inputs into the
166// sandbox.  It also implies SandboxTools().
167//
168// Sandboxing inputs requires RuleBuilder to be aware of all references to input paths.  Paths
169// that are passed to RuleBuilder outside of the methods that expect inputs, for example
170// FlagWithArg, must use RuleBuilderCommand.PathForInput to translate the path to one that matches
171// the sandbox layout.
172func (r *RuleBuilder) SandboxInputs() *RuleBuilder {
173	if !r.sbox {
174		panic("SandboxInputs() must be called after Sbox()")
175	}
176	if len(r.commands) > 0 {
177		panic("SandboxInputs() may not be called after Command()")
178	}
179	r.sboxTools = true
180	r.sboxInputs = true
181	return r
182}
183
184// Install associates an output of the rule with an install location, which can be retrieved later using
185// RuleBuilder.Installs.
186func (r *RuleBuilder) Install(from Path, to string) {
187	r.installs = append(r.installs, RuleBuilderInstall{from, to})
188}
189
190// Command returns a new RuleBuilderCommand for the rule.  The commands will be ordered in the rule by when they were
191// created by this method.  That can be mutated through their methods in any order, as long as the mutations do not
192// race with any call to Build.
193func (r *RuleBuilder) Command() *RuleBuilderCommand {
194	command := &RuleBuilderCommand{
195		rule: r,
196	}
197	r.commands = append(r.commands, command)
198	return command
199}
200
201// Temporary marks an output of a command as an intermediate file that will be used as an input to another command
202// in the same rule, and should not be listed in Outputs.
203func (r *RuleBuilder) Temporary(path WritablePath) {
204	r.temporariesSet[path] = true
205}
206
207// DeleteTemporaryFiles adds a command to the rule that deletes any outputs that have been marked using Temporary
208// when the rule runs.  DeleteTemporaryFiles should be called after all calls to Temporary.
209func (r *RuleBuilder) DeleteTemporaryFiles() {
210	var temporariesList WritablePaths
211
212	for intermediate := range r.temporariesSet {
213		temporariesList = append(temporariesList, intermediate)
214	}
215
216	sort.Slice(temporariesList, func(i, j int) bool {
217		return temporariesList[i].String() < temporariesList[j].String()
218	})
219
220	r.Command().Text("rm").Flag("-f").Outputs(temporariesList)
221}
222
223// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
224// input paths, such as RuleBuilderCommand.Input, RuleBuilderCommand.Implicit, or
225// RuleBuilderCommand.FlagWithInput.  Inputs to a command that are also outputs of another command
226// in the same RuleBuilder are filtered out.  The list is sorted and duplicates removed.
227func (r *RuleBuilder) Inputs() Paths {
228	outputs := r.outputSet()
229	depFiles := r.depFileSet()
230
231	inputs := make(map[string]Path)
232	for _, c := range r.commands {
233		for _, input := range append(c.inputs, c.implicits...) {
234			inputStr := input.String()
235			if _, isOutput := outputs[inputStr]; !isOutput {
236				if _, isDepFile := depFiles[inputStr]; !isDepFile {
237					inputs[input.String()] = input
238				}
239			}
240		}
241	}
242
243	var inputList Paths
244	for _, input := range inputs {
245		inputList = append(inputList, input)
246	}
247
248	sort.Slice(inputList, func(i, j int) bool {
249		return inputList[i].String() < inputList[j].String()
250	})
251
252	return inputList
253}
254
255// OrderOnlys returns the list of paths that were passed to the RuleBuilderCommand.OrderOnly or
256// RuleBuilderCommand.OrderOnlys.  The list is sorted and duplicates removed.
257func (r *RuleBuilder) OrderOnlys() Paths {
258	orderOnlys := make(map[string]Path)
259	for _, c := range r.commands {
260		for _, orderOnly := range c.orderOnlys {
261			orderOnlys[orderOnly.String()] = orderOnly
262		}
263	}
264
265	var orderOnlyList Paths
266	for _, orderOnly := range orderOnlys {
267		orderOnlyList = append(orderOnlyList, orderOnly)
268	}
269
270	sort.Slice(orderOnlyList, func(i, j int) bool {
271		return orderOnlyList[i].String() < orderOnlyList[j].String()
272	})
273
274	return orderOnlyList
275}
276
277// Validations returns the list of paths that were passed to RuleBuilderCommand.Validation or
278// RuleBuilderCommand.Validations.  The list is sorted and duplicates removed.
279func (r *RuleBuilder) Validations() Paths {
280	validations := make(map[string]Path)
281	for _, c := range r.commands {
282		for _, validation := range c.validations {
283			validations[validation.String()] = validation
284		}
285	}
286
287	var validationList Paths
288	for _, validation := range validations {
289		validationList = append(validationList, validation)
290	}
291
292	sort.Slice(validationList, func(i, j int) bool {
293		return validationList[i].String() < validationList[j].String()
294	})
295
296	return validationList
297}
298
299func (r *RuleBuilder) outputSet() map[string]WritablePath {
300	outputs := make(map[string]WritablePath)
301	for _, c := range r.commands {
302		for _, output := range c.outputs {
303			outputs[output.String()] = output
304		}
305	}
306	return outputs
307}
308
309// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
310// output paths, such as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or
311// RuleBuilderCommand.FlagWithInput.  The list is sorted and duplicates removed.
312func (r *RuleBuilder) Outputs() WritablePaths {
313	outputs := r.outputSet()
314
315	var outputList WritablePaths
316	for _, output := range outputs {
317		if !r.temporariesSet[output] {
318			outputList = append(outputList, output)
319		}
320	}
321
322	sort.Slice(outputList, func(i, j int) bool {
323		return outputList[i].String() < outputList[j].String()
324	})
325
326	return outputList
327}
328
329func (r *RuleBuilder) symlinkOutputSet() map[string]WritablePath {
330	symlinkOutputs := make(map[string]WritablePath)
331	for _, c := range r.commands {
332		for _, symlinkOutput := range c.symlinkOutputs {
333			symlinkOutputs[symlinkOutput.String()] = symlinkOutput
334		}
335	}
336	return symlinkOutputs
337}
338
339// SymlinkOutputs returns the list of paths that the executor (Ninja) would
340// verify, after build edge completion, that:
341//
342// 1) Created output symlinks match the list of paths in this list exactly (no more, no fewer)
343// 2) Created output files are *not* declared in this list.
344//
345// These symlink outputs are expected to be a subset of outputs or implicit
346// outputs, or they would fail validation at build param construction time
347// later, to support other non-rule-builder approaches for constructing
348// statements.
349func (r *RuleBuilder) SymlinkOutputs() WritablePaths {
350	symlinkOutputs := r.symlinkOutputSet()
351
352	var symlinkOutputList WritablePaths
353	for _, symlinkOutput := range symlinkOutputs {
354		symlinkOutputList = append(symlinkOutputList, symlinkOutput)
355	}
356
357	sort.Slice(symlinkOutputList, func(i, j int) bool {
358		return symlinkOutputList[i].String() < symlinkOutputList[j].String()
359	})
360
361	return symlinkOutputList
362}
363
364func (r *RuleBuilder) depFileSet() map[string]WritablePath {
365	depFiles := make(map[string]WritablePath)
366	for _, c := range r.commands {
367		for _, depFile := range c.depFiles {
368			depFiles[depFile.String()] = depFile
369		}
370	}
371	return depFiles
372}
373
374// DepFiles returns the list of paths that were passed to the RuleBuilderCommand methods that take depfile paths, such
375// as RuleBuilderCommand.DepFile or RuleBuilderCommand.FlagWithDepFile.
376func (r *RuleBuilder) DepFiles() WritablePaths {
377	var depFiles WritablePaths
378
379	for _, c := range r.commands {
380		for _, depFile := range c.depFiles {
381			depFiles = append(depFiles, depFile)
382		}
383	}
384
385	return depFiles
386}
387
388// Installs returns the list of tuples passed to Install.
389func (r *RuleBuilder) Installs() RuleBuilderInstalls {
390	return append(RuleBuilderInstalls(nil), r.installs...)
391}
392
393func (r *RuleBuilder) toolsSet() map[string]Path {
394	tools := make(map[string]Path)
395	for _, c := range r.commands {
396		for _, tool := range c.tools {
397			tools[tool.String()] = tool
398		}
399	}
400
401	return tools
402}
403
404// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method.  The
405// list is sorted and duplicates removed.
406func (r *RuleBuilder) Tools() Paths {
407	toolsSet := r.toolsSet()
408
409	var toolsList Paths
410	for _, tool := range toolsSet {
411		toolsList = append(toolsList, tool)
412	}
413
414	sort.Slice(toolsList, func(i, j int) bool {
415		return toolsList[i].String() < toolsList[j].String()
416	})
417
418	return toolsList
419}
420
421// RspFileInputs returns the list of paths that were passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
422func (r *RuleBuilder) RspFileInputs() Paths {
423	var rspFileInputs Paths
424	for _, c := range r.commands {
425		for _, rspFile := range c.rspFiles {
426			rspFileInputs = append(rspFileInputs, rspFile.paths...)
427		}
428	}
429
430	return rspFileInputs
431}
432
433func (r *RuleBuilder) rspFiles() []rspFileAndPaths {
434	var rspFiles []rspFileAndPaths
435	for _, c := range r.commands {
436		rspFiles = append(rspFiles, c.rspFiles...)
437	}
438
439	return rspFiles
440}
441
442// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
443func (r *RuleBuilder) Commands() []string {
444	var commands []string
445	for _, c := range r.commands {
446		commands = append(commands, c.String())
447	}
448	return commands
449}
450
451// BuilderContext is a subset of ModuleContext and SingletonContext.
452type BuilderContext interface {
453	PathContext
454	Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
455	Build(PackageContext, BuildParams)
456}
457
458var _ BuilderContext = ModuleContext(nil)
459var _ BuilderContext = SingletonContext(nil)
460
461func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderCommand {
462	return r.Command().
463		builtToolWithoutDeps("dep_fixer").
464		Inputs(depFiles.Paths())
465}
466
467// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
468// Outputs.
469func (r *RuleBuilder) Build(name string, desc string) {
470	name = ninjaNameEscape(name)
471
472	if len(r.missingDeps) > 0 {
473		r.ctx.Build(pctx, BuildParams{
474			Rule:        ErrorRule,
475			Outputs:     r.Outputs(),
476			Description: desc,
477			Args: map[string]string{
478				"error": "missing dependencies: " + strings.Join(r.missingDeps, ", "),
479			},
480		})
481		return
482	}
483
484	var depFile WritablePath
485	var depFormat blueprint.Deps
486	if depFiles := r.DepFiles(); len(depFiles) > 0 {
487		depFile = depFiles[0]
488		depFormat = blueprint.DepsGCC
489		if len(depFiles) > 1 {
490			// Add a command locally that merges all depfiles together into the first depfile.
491			r.depFileMergerCmd(depFiles)
492
493			if r.sbox {
494				// Check for Rel() errors, as all depfiles should be in the output dir.  Errors
495				// will be reported to the ctx.
496				for _, path := range depFiles[1:] {
497					Rel(r.ctx, r.outDir.String(), path.String())
498				}
499			}
500		}
501	}
502
503	tools := r.Tools()
504	commands := r.Commands()
505	outputs := r.Outputs()
506	inputs := r.Inputs()
507	rspFiles := r.rspFiles()
508
509	if len(commands) == 0 {
510		return
511	}
512	if len(outputs) == 0 {
513		panic("No outputs specified from any Commands")
514	}
515
516	commandString := strings.Join(commands, " && ")
517
518	if r.sbox {
519		// If running the command inside sbox, write the rule data out to an sbox
520		// manifest.textproto.
521		manifest := sbox_proto.Manifest{}
522		command := sbox_proto.Command{}
523		manifest.Commands = append(manifest.Commands, &command)
524		command.Command = proto.String(commandString)
525
526		if depFile != nil {
527			manifest.OutputDepfile = proto.String(depFile.String())
528		}
529
530		// If sandboxing tools is enabled, add copy rules to the manifest to copy each tool
531		// into the sbox directory.
532		if r.sboxTools {
533			for _, tool := range tools {
534				command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
535					From: proto.String(tool.String()),
536					To:   proto.String(sboxPathForToolRel(r.ctx, tool)),
537				})
538			}
539			for _, c := range r.commands {
540				for _, tool := range c.packagedTools {
541					command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
542						From:       proto.String(tool.srcPath.String()),
543						To:         proto.String(sboxPathForPackagedToolRel(tool)),
544						Executable: proto.Bool(tool.executable),
545					})
546					tools = append(tools, tool.srcPath)
547				}
548			}
549		}
550
551		// If sandboxing inputs is enabled, add copy rules to the manifest to copy each input
552		// into the sbox directory.
553		if r.sboxInputs {
554			for _, input := range inputs {
555				command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
556					From: proto.String(input.String()),
557					To:   proto.String(r.sboxPathForInputRel(input)),
558				})
559			}
560
561			// If using rsp files copy them and their contents into the sbox directory with
562			// the appropriate path mappings.
563			for _, rspFile := range rspFiles {
564				command.RspFiles = append(command.RspFiles, &sbox_proto.RspFile{
565					File: proto.String(rspFile.file.String()),
566					// These have to match the logic in sboxPathForInputRel
567					PathMappings: []*sbox_proto.PathMapping{
568						{
569							From: proto.String(r.outDir.String()),
570							To:   proto.String(sboxOutSubDir),
571						},
572						{
573							From: proto.String(PathForOutput(r.ctx).String()),
574							To:   proto.String(sboxOutSubDir),
575						},
576					},
577				})
578			}
579
580			command.Chdir = proto.Bool(true)
581		}
582
583		// Add copy rules to the manifest to copy each output file from the sbox directory.
584		// to the output directory after running the commands.
585		sboxOutputs := make([]string, len(outputs))
586		for i, output := range outputs {
587			rel := Rel(r.ctx, r.outDir.String(), output.String())
588			sboxOutputs[i] = filepath.Join(sboxOutDir, rel)
589			command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{
590				From: proto.String(filepath.Join(sboxOutSubDir, rel)),
591				To:   proto.String(output.String()),
592			})
593		}
594
595		// Outputs that were marked Temporary will not be checked that they are in the output
596		// directory by the loop above, check them here.
597		for path := range r.temporariesSet {
598			Rel(r.ctx, r.outDir.String(), path.String())
599		}
600
601		// Add a hash of the list of input files to the manifest so that the textproto file
602		// changes when the list of input files changes and causes the sbox rule that
603		// depends on it to rerun.
604		command.InputHash = proto.String(hashSrcFiles(inputs))
605
606		// Verify that the manifest textproto is not inside the sbox output directory, otherwise
607		// it will get deleted when the sbox rule clears its output directory.
608		_, manifestInOutDir := MaybeRel(r.ctx, r.outDir.String(), r.sboxManifestPath.String())
609		if manifestInOutDir {
610			ReportPathErrorf(r.ctx, "sbox rule %q manifestPath %q must not be in outputDir %q",
611				name, r.sboxManifestPath.String(), r.outDir.String())
612		}
613
614		// Create a rule to write the manifest as a the textproto.
615		pbText, err := prototext.Marshal(&manifest)
616		if err != nil {
617			ReportPathErrorf(r.ctx, "sbox manifest failed to marshal: %q", err)
618		}
619		WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText))
620
621		// Generate a new string to use as the command line of the sbox rule.  This uses
622		// a RuleBuilderCommand as a convenience method of building the command line, then
623		// converts it to a string to replace commandString.
624		sboxCmd := &RuleBuilderCommand{
625			rule: &RuleBuilder{
626				ctx: r.ctx,
627			},
628		}
629		sboxCmd.builtToolWithoutDeps("sbox").
630			FlagWithArg("--sandbox-path ", shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
631			FlagWithArg("--output-dir ", r.outDir.String()).
632			FlagWithInput("--manifest ", r.sboxManifestPath)
633
634		if r.restat {
635			sboxCmd.Flag("--write-if-changed")
636		}
637
638		// Replace the command string, and add the sbox tool and manifest textproto to the
639		// dependencies of the final sbox rule.
640		commandString = sboxCmd.buf.String()
641		tools = append(tools, sboxCmd.tools...)
642		inputs = append(inputs, sboxCmd.inputs...)
643
644		if r.rbeParams != nil {
645			// RBE needs a list of input files to copy to the remote builder.  For inputs already
646			// listed in an rsp file, pass the rsp file directly to rewrapper.  For the rest,
647			// create a new rsp file to pass to rewrapper.
648			var remoteRspFiles Paths
649			var remoteInputs Paths
650
651			remoteInputs = append(remoteInputs, inputs...)
652			remoteInputs = append(remoteInputs, tools...)
653
654			for _, rspFile := range rspFiles {
655				remoteInputs = append(remoteInputs, rspFile.file)
656				remoteRspFiles = append(remoteRspFiles, rspFile.file)
657			}
658
659			if len(remoteInputs) > 0 {
660				inputsListFile := r.sboxManifestPath.ReplaceExtension(r.ctx, "rbe_inputs.list")
661				writeRspFileRule(r.ctx, inputsListFile, remoteInputs)
662				remoteRspFiles = append(remoteRspFiles, inputsListFile)
663				// Add the new rsp file as an extra input to the rule.
664				inputs = append(inputs, inputsListFile)
665			}
666
667			r.rbeParams.OutputFiles = outputs.Strings()
668			r.rbeParams.RSPFiles = remoteRspFiles.Strings()
669			rewrapperCommand := r.rbeParams.NoVarTemplate(r.ctx.Config().RBEWrapper())
670			commandString = rewrapperCommand + " bash -c '" + strings.ReplaceAll(commandString, `'`, `'\''`) + "'"
671		}
672	} else {
673		// If not using sbox the rule will run the command directly, put the hash of the
674		// list of input files in a comment at the end of the command line to ensure ninja
675		// reruns the rule when the list of input files changes.
676		commandString += " # hash of input list: " + hashSrcFiles(inputs)
677	}
678
679	// Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
680	// ImplicitOutputs.  RuleBuilder doesn't use "$out", so the distinction between Outputs and
681	// ImplicitOutputs doesn't matter.
682	output := outputs[0]
683	implicitOutputs := outputs[1:]
684
685	var rspFile, rspFileContent string
686	var rspFileInputs Paths
687	if len(rspFiles) > 0 {
688		// The first rsp files uses Ninja's rsp file support for the rule
689		rspFile = rspFiles[0].file.String()
690		// Use "$in" for rspFileContent to avoid duplicating the list of files in the dependency
691		// list and in the contents of the rsp file.  Inputs to the rule that are not in the
692		// rsp file will be listed in Implicits instead of Inputs so they don't show up in "$in".
693		rspFileContent = "$in"
694		rspFileInputs = append(rspFileInputs, rspFiles[0].paths...)
695
696		for _, rspFile := range rspFiles[1:] {
697			// Any additional rsp files need an extra rule to write the file.
698			writeRspFileRule(r.ctx, rspFile.file, rspFile.paths)
699			// The main rule needs to depend on the inputs listed in the extra rsp file.
700			inputs = append(inputs, rspFile.paths...)
701			// The main rule needs to depend on the extra rsp file.
702			inputs = append(inputs, rspFile.file)
703		}
704	}
705
706	var pool blueprint.Pool
707	if r.ctx.Config().UseGoma() && r.remoteable.Goma {
708		// When USE_GOMA=true is set and the rule is supported by goma, allow jobs to run outside the local pool.
709	} else if r.ctx.Config().UseRBE() && r.remoteable.RBE {
710		// When USE_RBE=true is set and the rule is supported by RBE, use the remotePool.
711		pool = remotePool
712	} else if r.highmem {
713		pool = highmemPool
714	} else if r.ctx.Config().UseRemoteBuild() {
715		pool = localPool
716	}
717
718	r.ctx.Build(r.pctx, BuildParams{
719		Rule: r.ctx.Rule(pctx, name, blueprint.RuleParams{
720			Command:        proptools.NinjaEscape(commandString),
721			CommandDeps:    proptools.NinjaEscapeList(tools.Strings()),
722			Restat:         r.restat,
723			Rspfile:        proptools.NinjaEscape(rspFile),
724			RspfileContent: rspFileContent,
725			Pool:           pool,
726		}),
727		Inputs:          rspFileInputs,
728		Implicits:       inputs,
729		OrderOnly:       r.OrderOnlys(),
730		Validations:     r.Validations(),
731		Output:          output,
732		ImplicitOutputs: implicitOutputs,
733		SymlinkOutputs:  r.SymlinkOutputs(),
734		Depfile:         depFile,
735		Deps:            depFormat,
736		Description:     desc,
737	})
738}
739
740// RuleBuilderCommand is a builder for a command in a command line.  It can be mutated by its methods to add to the
741// command and track dependencies.  The methods mutate the RuleBuilderCommand in place, as well as return the
742// RuleBuilderCommand, so they can be used chained or unchained.  All methods that add text implicitly add a single
743// space as a separator from the previous method.
744type RuleBuilderCommand struct {
745	rule *RuleBuilder
746
747	buf            strings.Builder
748	inputs         Paths
749	implicits      Paths
750	orderOnlys     Paths
751	validations    Paths
752	outputs        WritablePaths
753	symlinkOutputs WritablePaths
754	depFiles       WritablePaths
755	tools          Paths
756	packagedTools  []PackagingSpec
757	rspFiles       []rspFileAndPaths
758}
759
760type rspFileAndPaths struct {
761	file  WritablePath
762	paths Paths
763}
764
765func checkPathNotNil(path Path) {
766	if path == nil {
767		panic("rule_builder paths cannot be nil")
768	}
769}
770
771func (c *RuleBuilderCommand) addInput(path Path) string {
772	checkPathNotNil(path)
773	c.inputs = append(c.inputs, path)
774	return c.PathForInput(path)
775}
776
777func (c *RuleBuilderCommand) addImplicit(path Path) {
778	checkPathNotNil(path)
779	c.implicits = append(c.implicits, path)
780}
781
782func (c *RuleBuilderCommand) addOrderOnly(path Path) {
783	checkPathNotNil(path)
784	c.orderOnlys = append(c.orderOnlys, path)
785}
786
787// PathForInput takes an input path and returns the appropriate path to use on the command line.  If
788// sbox was enabled via a call to RuleBuilder.Sbox() and the path was an output path it returns a
789// path with the placeholder prefix used for outputs in sbox.  If sbox is not enabled it returns the
790// original path.
791func (c *RuleBuilderCommand) PathForInput(path Path) string {
792	if c.rule.sbox {
793		rel, inSandbox := c.rule._sboxPathForInputRel(path)
794		if inSandbox {
795			rel = filepath.Join(sboxSandboxBaseDir, rel)
796		}
797		return rel
798	}
799	return path.String()
800}
801
802// PathsForInputs takes a list of input paths and returns the appropriate paths to use on the
803// command line.  If sbox was enabled via a call to RuleBuilder.Sbox() a path was an output path, it
804// returns the path with the placeholder prefix used for outputs in sbox.  If sbox is not enabled it
805// returns the original paths.
806func (c *RuleBuilderCommand) PathsForInputs(paths Paths) []string {
807	ret := make([]string, len(paths))
808	for i, path := range paths {
809		ret[i] = c.PathForInput(path)
810	}
811	return ret
812}
813
814// PathForOutput takes an output path and returns the appropriate path to use on the command
815// line.  If sbox was enabled via a call to RuleBuilder.Sbox(), it returns a path with the
816// placeholder prefix used for outputs in sbox.  If sbox is not enabled it returns the
817// original path.
818func (c *RuleBuilderCommand) PathForOutput(path WritablePath) string {
819	if c.rule.sbox {
820		// Errors will be handled in RuleBuilder.Build where we have a context to report them
821		rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
822		return filepath.Join(sboxOutDir, rel)
823	}
824	return path.String()
825}
826
827func sboxPathForToolRel(ctx BuilderContext, path Path) string {
828	// Errors will be handled in RuleBuilder.Build where we have a context to report them
829	toolDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "", false)
830	relOutSoong, isRelOutSoong, _ := maybeRelErr(toolDir.String(), path.String())
831	if isRelOutSoong {
832		// The tool is in the Soong output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
833		return filepath.Join(sboxToolsSubDir, "out", relOutSoong)
834	}
835	// The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
836	return filepath.Join(sboxToolsSubDir, "src", path.String())
837}
838
839func (r *RuleBuilder) _sboxPathForInputRel(path Path) (rel string, inSandbox bool) {
840	// Errors will be handled in RuleBuilder.Build where we have a context to report them
841	rel, isRelSboxOut, _ := maybeRelErr(r.outDir.String(), path.String())
842	if isRelSboxOut {
843		return filepath.Join(sboxOutSubDir, rel), true
844	}
845	if r.sboxInputs {
846		// When sandboxing inputs all inputs have to be copied into the sandbox.  Input files that
847		// are outputs of other rules could be an arbitrary absolute path if OUT_DIR is set, so they
848		// will be copied to relative paths under __SBOX_OUT_DIR__/out.
849		rel, isRelOut, _ := maybeRelErr(PathForOutput(r.ctx).String(), path.String())
850		if isRelOut {
851			return filepath.Join(sboxOutSubDir, rel), true
852		}
853	}
854	return path.String(), false
855}
856
857func (r *RuleBuilder) sboxPathForInputRel(path Path) string {
858	rel, _ := r._sboxPathForInputRel(path)
859	return rel
860}
861
862func (r *RuleBuilder) sboxPathsForInputsRel(paths Paths) []string {
863	ret := make([]string, len(paths))
864	for i, path := range paths {
865		ret[i] = r.sboxPathForInputRel(path)
866	}
867	return ret
868}
869
870func sboxPathForPackagedToolRel(spec PackagingSpec) string {
871	return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage)
872}
873
874// PathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
875// tool after copying it into the sandbox.  This can be used  on the RuleBuilder command line to
876// reference the tool.
877func (c *RuleBuilderCommand) PathForPackagedTool(spec PackagingSpec) string {
878	if !c.rule.sboxTools {
879		panic("PathForPackagedTool() requires SandboxTools()")
880	}
881
882	return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
883}
884
885// PathForTool takes a path to a tool, which may be an output file or a source file, and returns
886// the corresponding path for the tool in the sbox sandbox if sbox is enabled, or the original path
887// if it is not.  This can be used  on the RuleBuilder command line to reference the tool.
888func (c *RuleBuilderCommand) PathForTool(path Path) string {
889	if c.rule.sbox && c.rule.sboxTools {
890		return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path))
891	}
892	return path.String()
893}
894
895// PathsForTools takes a list of paths to tools, which may be output files or source files, and
896// returns the corresponding paths for the tools in the sbox sandbox if sbox is enabled, or the
897// original paths if it is not.  This can be used  on the RuleBuilder command line to reference the tool.
898func (c *RuleBuilderCommand) PathsForTools(paths Paths) []string {
899	if c.rule.sbox && c.rule.sboxTools {
900		var ret []string
901		for _, path := range paths {
902			ret = append(ret, filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path)))
903		}
904		return ret
905	}
906	return paths.Strings()
907}
908
909// PackagedTool adds the specified tool path to the command line.  It can only be used with tool
910// sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox.
911func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand {
912	if !c.rule.sboxTools {
913		panic("PackagedTool() requires SandboxTools()")
914	}
915
916	c.packagedTools = append(c.packagedTools, spec)
917	c.Text(sboxPathForPackagedToolRel(spec))
918	return c
919}
920
921// ImplicitPackagedTool copies the specified tool into the sandbox without modifying the command
922// line.  It can only be used with tool sandboxing enabled by SandboxTools().
923func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuilderCommand {
924	if !c.rule.sboxTools {
925		panic("ImplicitPackagedTool() requires SandboxTools()")
926	}
927
928	c.packagedTools = append(c.packagedTools, spec)
929	return c
930}
931
932// ImplicitPackagedTools copies the specified tools into the sandbox without modifying the command
933// line.  It can only be used with tool sandboxing enabled by SandboxTools().
934func (c *RuleBuilderCommand) ImplicitPackagedTools(specs []PackagingSpec) *RuleBuilderCommand {
935	if !c.rule.sboxTools {
936		panic("ImplicitPackagedTools() requires SandboxTools()")
937	}
938
939	c.packagedTools = append(c.packagedTools, specs...)
940	return c
941}
942
943// Text adds the specified raw text to the command line.  The text should not contain input or output paths or the
944// rule will not have them listed in its dependencies or outputs.
945func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
946	if c.buf.Len() > 0 {
947		c.buf.WriteByte(' ')
948	}
949	c.buf.WriteString(text)
950	return c
951}
952
953// Textf adds the specified formatted text to the command line.  The text should not contain input or output paths or
954// the rule will not have them listed in its dependencies or outputs.
955func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
956	return c.Text(fmt.Sprintf(format, a...))
957}
958
959// Flag adds the specified raw text to the command line.  The text should not contain input or output paths or the
960// rule will not have them listed in its dependencies or outputs.
961func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
962	return c.Text(flag)
963}
964
965// OptionalFlag adds the specified raw text to the command line if it is not nil.  The text should not contain input or
966// output paths or the rule will not have them listed in its dependencies or outputs.
967func (c *RuleBuilderCommand) OptionalFlag(flag *string) *RuleBuilderCommand {
968	if flag != nil {
969		c.Text(*flag)
970	}
971
972	return c
973}
974
975// Flags adds the specified raw text to the command line.  The text should not contain input or output paths or the
976// rule will not have them listed in its dependencies or outputs.
977func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
978	for _, flag := range flags {
979		c.Text(flag)
980	}
981	return c
982}
983
984// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them.  The flag
985// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
986// outputs.
987func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
988	return c.Text(flag + arg)
989}
990
991// FlagForEachArg adds the specified flag joined with each argument to the command line.  The result is identical to
992// calling FlagWithArg for argument.
993func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand {
994	for _, arg := range args {
995		c.FlagWithArg(flag, arg)
996	}
997	return c
998}
999
1000// FlagWithList adds the specified flag and list of arguments to the command line, with the arguments joined by sep
1001// and no separator between the flag and arguments.  The flag and arguments should not contain input or output paths or
1002// the rule will not have them listed in its dependencies or outputs.
1003func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
1004	return c.Text(flag + strings.Join(list, sep))
1005}
1006
1007// Tool adds the specified tool path to the command line.  The path will be also added to the dependencies returned by
1008// RuleBuilder.Tools.
1009func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
1010	checkPathNotNil(path)
1011	c.tools = append(c.tools, path)
1012	return c.Text(c.PathForTool(path))
1013}
1014
1015// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
1016func (c *RuleBuilderCommand) ImplicitTool(path Path) *RuleBuilderCommand {
1017	checkPathNotNil(path)
1018	c.tools = append(c.tools, path)
1019	return c
1020}
1021
1022// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
1023func (c *RuleBuilderCommand) ImplicitTools(paths Paths) *RuleBuilderCommand {
1024	for _, path := range paths {
1025		c.ImplicitTool(path)
1026	}
1027	return c
1028}
1029
1030// BuiltTool adds the specified tool path that was built using a host Soong module to the command line.  The path will
1031// be also added to the dependencies returned by RuleBuilder.Tools.
1032//
1033// It is equivalent to:
1034//
1035//	cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
1036func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
1037	if c.rule.ctx.Config().UseHostMusl() {
1038		// If the host is using musl, assume that the tool was built against musl libc and include
1039		// libc_musl.so in the sandbox.
1040		// TODO(ccross): if we supported adding new dependencies during GenerateAndroidBuildActions
1041		// this could be a dependency + TransitivePackagingSpecs.
1042		c.ImplicitTool(c.rule.ctx.Config().HostJNIToolPath(c.rule.ctx, "libc_musl"))
1043	}
1044	return c.builtToolWithoutDeps(tool)
1045}
1046
1047// builtToolWithoutDeps is similar to BuiltTool, but doesn't add any dependencies.  It is used
1048// internally by RuleBuilder for helper tools that are known to be compiled statically.
1049func (c *RuleBuilderCommand) builtToolWithoutDeps(tool string) *RuleBuilderCommand {
1050	return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
1051}
1052
1053// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools.  The path will be also added to the
1054// dependencies returned by RuleBuilder.Tools.
1055//
1056// It is equivalent to:
1057//
1058//	cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
1059func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
1060	return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
1061}
1062
1063// Input adds the specified input path to the command line.  The path will also be added to the dependencies returned by
1064// RuleBuilder.Inputs.
1065func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
1066	return c.Text(c.addInput(path))
1067}
1068
1069// Inputs adds the specified input paths to the command line, separated by spaces.  The paths will also be added to the
1070// dependencies returned by RuleBuilder.Inputs.
1071func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
1072	for _, path := range paths {
1073		c.Input(path)
1074	}
1075	return c
1076}
1077
1078// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
1079// command line.
1080func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
1081	c.addImplicit(path)
1082	return c
1083}
1084
1085// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
1086// command line.
1087func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
1088	for _, path := range paths {
1089		c.addImplicit(path)
1090	}
1091	return c
1092}
1093
1094// GetImplicits returns the command's implicit inputs.
1095func (c *RuleBuilderCommand) GetImplicits() Paths {
1096	return c.implicits
1097}
1098
1099// OrderOnly adds the specified input path to the dependencies returned by RuleBuilder.OrderOnlys
1100// without modifying the command line.
1101func (c *RuleBuilderCommand) OrderOnly(path Path) *RuleBuilderCommand {
1102	c.addOrderOnly(path)
1103	return c
1104}
1105
1106// OrderOnlys adds the specified input paths to the dependencies returned by RuleBuilder.OrderOnlys
1107// without modifying the command line.
1108func (c *RuleBuilderCommand) OrderOnlys(paths Paths) *RuleBuilderCommand {
1109	for _, path := range paths {
1110		c.addOrderOnly(path)
1111	}
1112	return c
1113}
1114
1115// Validation adds the specified input path to the validation dependencies by
1116// RuleBuilder.Validations without modifying the command line.
1117func (c *RuleBuilderCommand) Validation(path Path) *RuleBuilderCommand {
1118	checkPathNotNil(path)
1119	c.validations = append(c.validations, path)
1120	return c
1121}
1122
1123// Validations adds the specified input paths to the validation dependencies by
1124// RuleBuilder.Validations without modifying the command line.
1125func (c *RuleBuilderCommand) Validations(paths Paths) *RuleBuilderCommand {
1126	for _, path := range paths {
1127		c.Validation(path)
1128	}
1129	return c
1130}
1131
1132// Output adds the specified output path to the command line.  The path will also be added to the outputs returned by
1133// RuleBuilder.Outputs.
1134func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
1135	checkPathNotNil(path)
1136	c.outputs = append(c.outputs, path)
1137	return c.Text(c.PathForOutput(path))
1138}
1139
1140// Outputs adds the specified output paths to the command line, separated by spaces.  The paths will also be added to
1141// the outputs returned by RuleBuilder.Outputs.
1142func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
1143	for _, path := range paths {
1144		c.Output(path)
1145	}
1146	return c
1147}
1148
1149// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
1150// and will be the temporary output directory managed by sbox, not the final one.
1151func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand {
1152	if !c.rule.sbox {
1153		panic("OutputDir only valid with Sbox")
1154	}
1155	return c.Text(sboxOutDir)
1156}
1157
1158// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
1159// line, and causes RuleBuilder.Build file to set the depfile flag for ninja.  If multiple depfiles are added to
1160// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
1161func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
1162	checkPathNotNil(path)
1163	c.depFiles = append(c.depFiles, path)
1164	return c.Text(c.PathForOutput(path))
1165}
1166
1167// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
1168// the command line.
1169func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
1170	c.outputs = append(c.outputs, path)
1171	return c
1172}
1173
1174// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
1175// the command line.
1176func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
1177	c.outputs = append(c.outputs, paths...)
1178	return c
1179}
1180
1181// ImplicitSymlinkOutput declares the specified path as an implicit output that
1182// will be a symlink instead of a regular file. Does not modify the command
1183// line.
1184func (c *RuleBuilderCommand) ImplicitSymlinkOutput(path WritablePath) *RuleBuilderCommand {
1185	checkPathNotNil(path)
1186	c.symlinkOutputs = append(c.symlinkOutputs, path)
1187	return c.ImplicitOutput(path)
1188}
1189
1190// ImplicitSymlinkOutputs declares the specified paths as implicit outputs that
1191// will be a symlinks instead of regular files. Does not modify the command
1192// line.
1193func (c *RuleBuilderCommand) ImplicitSymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
1194	for _, path := range paths {
1195		c.ImplicitSymlinkOutput(path)
1196	}
1197	return c
1198}
1199
1200// SymlinkOutput declares the specified path as an output that will be a symlink
1201// instead of a regular file. Modifies the command line.
1202func (c *RuleBuilderCommand) SymlinkOutput(path WritablePath) *RuleBuilderCommand {
1203	checkPathNotNil(path)
1204	c.symlinkOutputs = append(c.symlinkOutputs, path)
1205	return c.Output(path)
1206}
1207
1208// SymlinkOutputsl declares the specified paths as outputs that will be symlinks
1209// instead of regular files. Modifies the command line.
1210func (c *RuleBuilderCommand) SymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
1211	for _, path := range paths {
1212		c.SymlinkOutput(path)
1213	}
1214	return c
1215}
1216
1217// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
1218// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja.  If multiple depfiles
1219// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
1220// depfiles together.
1221func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand {
1222	c.depFiles = append(c.depFiles, path)
1223	return c
1224}
1225
1226// FlagWithInput adds the specified flag and input path to the command line, with no separator between them.  The path
1227// will also be added to the dependencies returned by RuleBuilder.Inputs.
1228func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
1229	return c.Text(flag + c.addInput(path))
1230}
1231
1232// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
1233// and no separator between the flag and inputs.  The input paths will also be added to the dependencies returned by
1234// RuleBuilder.Inputs.
1235func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
1236	strs := make([]string, len(paths))
1237	for i, path := range paths {
1238		strs[i] = c.addInput(path)
1239	}
1240	return c.FlagWithList(flag, strs, sep)
1241}
1242
1243// FlagForEachInput adds the specified flag joined with each input path to the command line.  The input paths will also
1244// be added to the dependencies returned by RuleBuilder.Inputs.  The result is identical to calling FlagWithInput for
1245// each input path.
1246func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
1247	for _, path := range paths {
1248		c.FlagWithInput(flag, path)
1249	}
1250	return c
1251}
1252
1253// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them.  The path
1254// will also be added to the outputs returned by RuleBuilder.Outputs.
1255func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
1256	c.outputs = append(c.outputs, path)
1257	return c.Text(flag + c.PathForOutput(path))
1258}
1259
1260// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them.  The path
1261// will also be added to the outputs returned by RuleBuilder.Outputs.
1262func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
1263	c.depFiles = append(c.depFiles, path)
1264	return c.Text(flag + c.PathForOutput(path))
1265}
1266
1267// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with
1268// no separator between them.  The paths will be written to the rspfile.  If sbox is enabled, the
1269// rspfile must be outside the sbox directory.  The first use of FlagWithRspFileInputList in any
1270// RuleBuilderCommand of a RuleBuilder will use Ninja's rsp file support for the rule, additional
1271// uses will result in an auxiliary rules to write the rspFile contents.
1272func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, rspFile WritablePath, paths Paths) *RuleBuilderCommand {
1273	// Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
1274	// generated.
1275	if paths == nil {
1276		paths = Paths{}
1277	}
1278
1279	c.rspFiles = append(c.rspFiles, rspFileAndPaths{rspFile, paths})
1280
1281	if c.rule.sbox {
1282		if _, isRel, _ := maybeRelErr(c.rule.outDir.String(), rspFile.String()); isRel {
1283			panic(fmt.Errorf("FlagWithRspFileInputList rspfile %q must not be inside out dir %q",
1284				rspFile.String(), c.rule.outDir.String()))
1285		}
1286	}
1287
1288	c.FlagWithArg(flag, c.PathForInput(rspFile))
1289	return c
1290}
1291
1292// String returns the command line.
1293func (c *RuleBuilderCommand) String() string {
1294	return c.buf.String()
1295}
1296
1297// RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox()
1298// and returns sbox testproto generated by the RuleBuilder.
1299func RuleBuilderSboxProtoForTests(t *testing.T, params TestingBuildParams) *sbox_proto.Manifest {
1300	t.Helper()
1301	content := ContentFromFileRuleForTests(t, params)
1302	manifest := sbox_proto.Manifest{}
1303	err := prototext.Unmarshal([]byte(content), &manifest)
1304	if err != nil {
1305		t.Fatalf("failed to unmarshal manifest: %s", err.Error())
1306	}
1307	return &manifest
1308}
1309
1310func ninjaNameEscape(s string) string {
1311	b := []byte(s)
1312	escaped := false
1313	for i, c := range b {
1314		valid := (c >= 'a' && c <= 'z') ||
1315			(c >= 'A' && c <= 'Z') ||
1316			(c >= '0' && c <= '9') ||
1317			(c == '_') ||
1318			(c == '-') ||
1319			(c == '.')
1320		if !valid {
1321			b[i] = '_'
1322			escaped = true
1323		}
1324	}
1325	if escaped {
1326		s = string(b)
1327	}
1328	return s
1329}
1330
1331// hashSrcFiles returns a hash of the list of source files.  It is used to ensure the command line
1332// or the sbox textproto manifest change even if the input files are not listed on the command line.
1333func hashSrcFiles(srcFiles Paths) string {
1334	h := sha256.New()
1335	srcFileList := strings.Join(srcFiles.Strings(), "\n")
1336	h.Write([]byte(srcFileList))
1337	return fmt.Sprintf("%x", h.Sum(nil))
1338}
1339
1340// BuilderContextForTesting returns a BuilderContext for the given config that can be used for tests
1341// that need to call methods that take a BuilderContext.
1342func BuilderContextForTesting(config Config) BuilderContext {
1343	pathCtx := PathContextForTesting(config)
1344	return builderContextForTests{
1345		PathContext: pathCtx,
1346	}
1347}
1348
1349type builderContextForTests struct {
1350	PathContext
1351}
1352
1353func (builderContextForTests) Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule {
1354	return nil
1355}
1356func (builderContextForTests) Build(PackageContext, BuildParams) {}
1357
1358func writeRspFileRule(ctx BuilderContext, rspFile WritablePath, paths Paths) {
1359	buf := &strings.Builder{}
1360	err := response.WriteRspFile(buf, paths.Strings())
1361	if err != nil {
1362		// There should never be I/O errors writing to a bytes.Buffer.
1363		panic(err)
1364	}
1365	WriteFileRule(ctx, rspFile, buf.String())
1366}
1367