• 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//  cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
1035func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
1036	if c.rule.ctx.Config().UseHostMusl() {
1037		// If the host is using musl, assume that the tool was built against musl libc and include
1038		// libc_musl.so in the sandbox.
1039		// TODO(ccross): if we supported adding new dependencies during GenerateAndroidBuildActions
1040		// this could be a dependency + TransitivePackagingSpecs.
1041		c.ImplicitTool(c.rule.ctx.Config().HostJNIToolPath(c.rule.ctx, "libc_musl"))
1042	}
1043	return c.builtToolWithoutDeps(tool)
1044}
1045
1046// builtToolWithoutDeps is similar to BuiltTool, but doesn't add any dependencies.  It is used
1047// internally by RuleBuilder for helper tools that are known to be compiled statically.
1048func (c *RuleBuilderCommand) builtToolWithoutDeps(tool string) *RuleBuilderCommand {
1049	return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
1050}
1051
1052// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools.  The path will be also added to the
1053// dependencies returned by RuleBuilder.Tools.
1054//
1055// It is equivalent to:
1056//  cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
1057func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
1058	return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
1059}
1060
1061// Input adds the specified input path to the command line.  The path will also be added to the dependencies returned by
1062// RuleBuilder.Inputs.
1063func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
1064	return c.Text(c.addInput(path))
1065}
1066
1067// Inputs adds the specified input paths to the command line, separated by spaces.  The paths will also be added to the
1068// dependencies returned by RuleBuilder.Inputs.
1069func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
1070	for _, path := range paths {
1071		c.Input(path)
1072	}
1073	return c
1074}
1075
1076// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
1077// command line.
1078func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
1079	c.addImplicit(path)
1080	return c
1081}
1082
1083// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
1084// command line.
1085func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
1086	for _, path := range paths {
1087		c.addImplicit(path)
1088	}
1089	return c
1090}
1091
1092// GetImplicits returns the command's implicit inputs.
1093func (c *RuleBuilderCommand) GetImplicits() Paths {
1094	return c.implicits
1095}
1096
1097// OrderOnly adds the specified input path to the dependencies returned by RuleBuilder.OrderOnlys
1098// without modifying the command line.
1099func (c *RuleBuilderCommand) OrderOnly(path Path) *RuleBuilderCommand {
1100	c.addOrderOnly(path)
1101	return c
1102}
1103
1104// OrderOnlys adds the specified input paths to the dependencies returned by RuleBuilder.OrderOnlys
1105// without modifying the command line.
1106func (c *RuleBuilderCommand) OrderOnlys(paths Paths) *RuleBuilderCommand {
1107	for _, path := range paths {
1108		c.addOrderOnly(path)
1109	}
1110	return c
1111}
1112
1113// Validation adds the specified input path to the validation dependencies by
1114// RuleBuilder.Validations without modifying the command line.
1115func (c *RuleBuilderCommand) Validation(path Path) *RuleBuilderCommand {
1116	checkPathNotNil(path)
1117	c.validations = append(c.validations, path)
1118	return c
1119}
1120
1121// Validations adds the specified input paths to the validation dependencies by
1122// RuleBuilder.Validations without modifying the command line.
1123func (c *RuleBuilderCommand) Validations(paths Paths) *RuleBuilderCommand {
1124	for _, path := range paths {
1125		c.Validation(path)
1126	}
1127	return c
1128}
1129
1130// Output adds the specified output path to the command line.  The path will also be added to the outputs returned by
1131// RuleBuilder.Outputs.
1132func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
1133	checkPathNotNil(path)
1134	c.outputs = append(c.outputs, path)
1135	return c.Text(c.PathForOutput(path))
1136}
1137
1138// Outputs adds the specified output paths to the command line, separated by spaces.  The paths will also be added to
1139// the outputs returned by RuleBuilder.Outputs.
1140func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
1141	for _, path := range paths {
1142		c.Output(path)
1143	}
1144	return c
1145}
1146
1147// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
1148// and will be the temporary output directory managed by sbox, not the final one.
1149func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand {
1150	if !c.rule.sbox {
1151		panic("OutputDir only valid with Sbox")
1152	}
1153	return c.Text(sboxOutDir)
1154}
1155
1156// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
1157// line, and causes RuleBuilder.Build file to set the depfile flag for ninja.  If multiple depfiles are added to
1158// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
1159func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
1160	checkPathNotNil(path)
1161	c.depFiles = append(c.depFiles, path)
1162	return c.Text(c.PathForOutput(path))
1163}
1164
1165// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
1166// the command line.
1167func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
1168	c.outputs = append(c.outputs, path)
1169	return c
1170}
1171
1172// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
1173// the command line.
1174func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
1175	c.outputs = append(c.outputs, paths...)
1176	return c
1177}
1178
1179// ImplicitSymlinkOutput declares the specified path as an implicit output that
1180// will be a symlink instead of a regular file. Does not modify the command
1181// line.
1182func (c *RuleBuilderCommand) ImplicitSymlinkOutput(path WritablePath) *RuleBuilderCommand {
1183	checkPathNotNil(path)
1184	c.symlinkOutputs = append(c.symlinkOutputs, path)
1185	return c.ImplicitOutput(path)
1186}
1187
1188// ImplicitSymlinkOutputs declares the specified paths as implicit outputs that
1189// will be a symlinks instead of regular files. Does not modify the command
1190// line.
1191func (c *RuleBuilderCommand) ImplicitSymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
1192	for _, path := range paths {
1193		c.ImplicitSymlinkOutput(path)
1194	}
1195	return c
1196}
1197
1198// SymlinkOutput declares the specified path as an output that will be a symlink
1199// instead of a regular file. Modifies the command line.
1200func (c *RuleBuilderCommand) SymlinkOutput(path WritablePath) *RuleBuilderCommand {
1201	checkPathNotNil(path)
1202	c.symlinkOutputs = append(c.symlinkOutputs, path)
1203	return c.Output(path)
1204}
1205
1206// SymlinkOutputsl declares the specified paths as outputs that will be symlinks
1207// instead of regular files. Modifies the command line.
1208func (c *RuleBuilderCommand) SymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
1209	for _, path := range paths {
1210		c.SymlinkOutput(path)
1211	}
1212	return c
1213}
1214
1215// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
1216// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja.  If multiple depfiles
1217// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
1218// depfiles together.
1219func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand {
1220	c.depFiles = append(c.depFiles, path)
1221	return c
1222}
1223
1224// FlagWithInput adds the specified flag and input path to the command line, with no separator between them.  The path
1225// will also be added to the dependencies returned by RuleBuilder.Inputs.
1226func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
1227	return c.Text(flag + c.addInput(path))
1228}
1229
1230// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
1231// and no separator between the flag and inputs.  The input paths will also be added to the dependencies returned by
1232// RuleBuilder.Inputs.
1233func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
1234	strs := make([]string, len(paths))
1235	for i, path := range paths {
1236		strs[i] = c.addInput(path)
1237	}
1238	return c.FlagWithList(flag, strs, sep)
1239}
1240
1241// FlagForEachInput adds the specified flag joined with each input path to the command line.  The input paths will also
1242// be added to the dependencies returned by RuleBuilder.Inputs.  The result is identical to calling FlagWithInput for
1243// each input path.
1244func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
1245	for _, path := range paths {
1246		c.FlagWithInput(flag, path)
1247	}
1248	return c
1249}
1250
1251// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them.  The path
1252// will also be added to the outputs returned by RuleBuilder.Outputs.
1253func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
1254	c.outputs = append(c.outputs, path)
1255	return c.Text(flag + c.PathForOutput(path))
1256}
1257
1258// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them.  The path
1259// will also be added to the outputs returned by RuleBuilder.Outputs.
1260func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
1261	c.depFiles = append(c.depFiles, path)
1262	return c.Text(flag + c.PathForOutput(path))
1263}
1264
1265// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with
1266// no separator between them.  The paths will be written to the rspfile.  If sbox is enabled, the
1267// rspfile must be outside the sbox directory.  The first use of FlagWithRspFileInputList in any
1268// RuleBuilderCommand of a RuleBuilder will use Ninja's rsp file support for the rule, additional
1269// uses will result in an auxiliary rules to write the rspFile contents.
1270func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, rspFile WritablePath, paths Paths) *RuleBuilderCommand {
1271	// Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
1272	// generated.
1273	if paths == nil {
1274		paths = Paths{}
1275	}
1276
1277	c.rspFiles = append(c.rspFiles, rspFileAndPaths{rspFile, paths})
1278
1279	if c.rule.sbox {
1280		if _, isRel, _ := maybeRelErr(c.rule.outDir.String(), rspFile.String()); isRel {
1281			panic(fmt.Errorf("FlagWithRspFileInputList rspfile %q must not be inside out dir %q",
1282				rspFile.String(), c.rule.outDir.String()))
1283		}
1284	}
1285
1286	c.FlagWithArg(flag, c.PathForInput(rspFile))
1287	return c
1288}
1289
1290// String returns the command line.
1291func (c *RuleBuilderCommand) String() string {
1292	return c.buf.String()
1293}
1294
1295// RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox()
1296// and returns sbox testproto generated by the RuleBuilder.
1297func RuleBuilderSboxProtoForTests(t *testing.T, params TestingBuildParams) *sbox_proto.Manifest {
1298	t.Helper()
1299	content := ContentFromFileRuleForTests(t, params)
1300	manifest := sbox_proto.Manifest{}
1301	err := prototext.Unmarshal([]byte(content), &manifest)
1302	if err != nil {
1303		t.Fatalf("failed to unmarshal manifest: %s", err.Error())
1304	}
1305	return &manifest
1306}
1307
1308func ninjaNameEscape(s string) string {
1309	b := []byte(s)
1310	escaped := false
1311	for i, c := range b {
1312		valid := (c >= 'a' && c <= 'z') ||
1313			(c >= 'A' && c <= 'Z') ||
1314			(c >= '0' && c <= '9') ||
1315			(c == '_') ||
1316			(c == '-') ||
1317			(c == '.')
1318		if !valid {
1319			b[i] = '_'
1320			escaped = true
1321		}
1322	}
1323	if escaped {
1324		s = string(b)
1325	}
1326	return s
1327}
1328
1329// hashSrcFiles returns a hash of the list of source files.  It is used to ensure the command line
1330// or the sbox textproto manifest change even if the input files are not listed on the command line.
1331func hashSrcFiles(srcFiles Paths) string {
1332	h := sha256.New()
1333	srcFileList := strings.Join(srcFiles.Strings(), "\n")
1334	h.Write([]byte(srcFileList))
1335	return fmt.Sprintf("%x", h.Sum(nil))
1336}
1337
1338// BuilderContextForTesting returns a BuilderContext for the given config that can be used for tests
1339// that need to call methods that take a BuilderContext.
1340func BuilderContextForTesting(config Config) BuilderContext {
1341	pathCtx := PathContextForTesting(config)
1342	return builderContextForTests{
1343		PathContext: pathCtx,
1344	}
1345}
1346
1347type builderContextForTests struct {
1348	PathContext
1349}
1350
1351func (builderContextForTests) Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule {
1352	return nil
1353}
1354func (builderContextForTests) Build(PackageContext, BuildParams) {}
1355
1356func writeRspFileRule(ctx BuilderContext, rspFile WritablePath, paths Paths) {
1357	buf := &strings.Builder{}
1358	err := response.WriteRspFile(buf, paths.Strings())
1359	if err != nil {
1360		// There should never be I/O errors writing to a bytes.Buffer.
1361		panic(err)
1362	}
1363	WriteFileRule(ctx, rspFile, buf.String())
1364}
1365