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