blob: 86418b2d5aba979903d81965f5af330928bd0404 [file] [log] [blame]
Colin Crossfeec25b2019-01-30 17:32:39 -08001// 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 (
Colin Cross3d680512020-11-13 16:23:53 -080018 "crypto/sha256"
Colin Crossfeec25b2019-01-30 17:32:39 -080019 "fmt"
Colin Cross3d680512020-11-13 16:23:53 -080020 "path/filepath"
Colin Crossfeec25b2019-01-30 17:32:39 -080021 "sort"
22 "strings"
23
24 "github.com/google/blueprint"
25 "github.com/google/blueprint/proptools"
Dan Willemsen633c5022019-04-12 11:11:38 -070026
27 "android/soong/shared"
Colin Crossfeec25b2019-01-30 17:32:39 -080028)
29
Colin Cross3d680512020-11-13 16:23:53 -080030const sboxOutDir = "__SBOX_OUT_DIR__"
31
Colin Cross758290d2019-02-01 16:42:32 -080032// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
33// graph.
Colin Crossfeec25b2019-01-30 17:32:39 -080034type RuleBuilder struct {
Colin Cross5cb5b092019-02-02 21:25:18 -080035 commands []*RuleBuilderCommand
Colin Crossdeabb942019-02-11 14:11:09 -080036 installs RuleBuilderInstalls
Colin Cross69f59a32019-02-15 10:39:37 -080037 temporariesSet map[WritablePath]bool
Colin Cross5cb5b092019-02-02 21:25:18 -080038 restat bool
Dan Willemsen633c5022019-04-12 11:11:38 -070039 sbox bool
Colin Cross8b8bec32019-11-15 13:18:43 -080040 highmem bool
41 remoteable RemoteRuleSupports
Dan Willemsen633c5022019-04-12 11:11:38 -070042 sboxOutDir WritablePath
Colin Cross0d2f40a2019-02-05 22:31:15 -080043 missingDeps []string
Colin Crossfeec25b2019-01-30 17:32:39 -080044}
45
Colin Cross758290d2019-02-01 16:42:32 -080046// NewRuleBuilder returns a newly created RuleBuilder.
47func NewRuleBuilder() *RuleBuilder {
Colin Cross5cb5b092019-02-02 21:25:18 -080048 return &RuleBuilder{
Colin Cross69f59a32019-02-15 10:39:37 -080049 temporariesSet: make(map[WritablePath]bool),
Colin Cross5cb5b092019-02-02 21:25:18 -080050 }
Colin Cross758290d2019-02-01 16:42:32 -080051}
52
53// RuleBuilderInstall is a tuple of install from and to locations.
54type RuleBuilderInstall struct {
Colin Cross69f59a32019-02-15 10:39:37 -080055 From Path
56 To string
Colin Cross758290d2019-02-01 16:42:32 -080057}
58
Colin Crossdeabb942019-02-11 14:11:09 -080059type RuleBuilderInstalls []RuleBuilderInstall
60
61// String returns the RuleBuilderInstalls in the form used by $(call copy-many-files) in Make, a space separated
62// list of from:to tuples.
63func (installs RuleBuilderInstalls) String() string {
64 sb := strings.Builder{}
65 for i, install := range installs {
66 if i != 0 {
67 sb.WriteRune(' ')
68 }
Colin Cross69f59a32019-02-15 10:39:37 -080069 sb.WriteString(install.From.String())
Colin Crossdeabb942019-02-11 14:11:09 -080070 sb.WriteRune(':')
71 sb.WriteString(install.To)
72 }
73 return sb.String()
74}
75
Colin Cross0d2f40a2019-02-05 22:31:15 -080076// MissingDeps adds modules to the list of missing dependencies. If MissingDeps
77// is called with a non-empty input, any call to Build will result in a rule
78// that will print an error listing the missing dependencies and fail.
79// MissingDeps should only be called if Config.AllowMissingDependencies() is
80// true.
81func (r *RuleBuilder) MissingDeps(missingDeps []string) {
82 r.missingDeps = append(r.missingDeps, missingDeps...)
83}
84
Colin Cross758290d2019-02-01 16:42:32 -080085// Restat marks the rule as a restat rule, which will be passed to ModuleContext.Rule in BuildParams.Restat.
Dan Willemsen633c5022019-04-12 11:11:38 -070086//
87// Restat is not compatible with Sbox()
Colin Crossfeec25b2019-01-30 17:32:39 -080088func (r *RuleBuilder) Restat() *RuleBuilder {
Dan Willemsen633c5022019-04-12 11:11:38 -070089 if r.sbox {
90 panic("Restat() is not compatible with Sbox()")
91 }
Colin Crossfeec25b2019-01-30 17:32:39 -080092 r.restat = true
93 return r
94}
95
Colin Cross8b8bec32019-11-15 13:18:43 -080096// HighMem marks the rule as a high memory rule, which will limit how many run in parallel with other high memory
97// rules.
98func (r *RuleBuilder) HighMem() *RuleBuilder {
99 r.highmem = true
100 return r
101}
102
103// Remoteable marks the rule as supporting remote execution.
104func (r *RuleBuilder) Remoteable(supports RemoteRuleSupports) *RuleBuilder {
105 r.remoteable = supports
106 return r
107}
108
Dan Willemsen633c5022019-04-12 11:11:38 -0700109// Sbox marks the rule as needing to be wrapped by sbox. The WritablePath should point to the output
110// directory that sbox will wipe. It should not be written to by any other rule. sbox will ensure
111// that all outputs have been written, and will discard any output files that were not specified.
112//
113// Sbox is not compatible with Restat()
114func (r *RuleBuilder) Sbox(outputDir WritablePath) *RuleBuilder {
115 if r.sbox {
116 panic("Sbox() may not be called more than once")
117 }
118 if len(r.commands) > 0 {
119 panic("Sbox() may not be called after Command()")
120 }
121 if r.restat {
122 panic("Sbox() is not compatible with Restat()")
123 }
124 r.sbox = true
125 r.sboxOutDir = outputDir
126 return r
127}
128
Colin Cross758290d2019-02-01 16:42:32 -0800129// Install associates an output of the rule with an install location, which can be retrieved later using
130// RuleBuilder.Installs.
Colin Cross69f59a32019-02-15 10:39:37 -0800131func (r *RuleBuilder) Install(from Path, to string) {
Colin Crossfeec25b2019-01-30 17:32:39 -0800132 r.installs = append(r.installs, RuleBuilderInstall{from, to})
133}
134
Colin Cross758290d2019-02-01 16:42:32 -0800135// Command returns a new RuleBuilderCommand for the rule. The commands will be ordered in the rule by when they were
136// created by this method. That can be mutated through their methods in any order, as long as the mutations do not
137// race with any call to Build.
Colin Crossfeec25b2019-01-30 17:32:39 -0800138func (r *RuleBuilder) Command() *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700139 command := &RuleBuilderCommand{
Colin Cross3d680512020-11-13 16:23:53 -0800140 sbox: r.sbox,
141 outDir: r.sboxOutDir,
Dan Willemsen633c5022019-04-12 11:11:38 -0700142 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800143 r.commands = append(r.commands, command)
144 return command
145}
146
Colin Cross5cb5b092019-02-02 21:25:18 -0800147// Temporary marks an output of a command as an intermediate file that will be used as an input to another command
148// in the same rule, and should not be listed in Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800149func (r *RuleBuilder) Temporary(path WritablePath) {
Colin Cross5cb5b092019-02-02 21:25:18 -0800150 r.temporariesSet[path] = true
151}
152
153// DeleteTemporaryFiles adds a command to the rule that deletes any outputs that have been marked using Temporary
154// when the rule runs. DeleteTemporaryFiles should be called after all calls to Temporary.
155func (r *RuleBuilder) DeleteTemporaryFiles() {
Colin Cross69f59a32019-02-15 10:39:37 -0800156 var temporariesList WritablePaths
Colin Cross5cb5b092019-02-02 21:25:18 -0800157
158 for intermediate := range r.temporariesSet {
159 temporariesList = append(temporariesList, intermediate)
160 }
Colin Cross69f59a32019-02-15 10:39:37 -0800161
162 sort.Slice(temporariesList, func(i, j int) bool {
163 return temporariesList[i].String() < temporariesList[j].String()
164 })
Colin Cross5cb5b092019-02-02 21:25:18 -0800165
166 r.Command().Text("rm").Flag("-f").Outputs(temporariesList)
167}
168
Colin Crossda71eda2020-02-21 16:55:19 -0800169// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
Colin Cross3d680512020-11-13 16:23:53 -0800170// input paths, such as RuleBuilderCommand.Input, RuleBuilderCommand.Implicit, or
Colin Crossda71eda2020-02-21 16:55:19 -0800171// RuleBuilderCommand.FlagWithInput. Inputs to a command that are also outputs of another command
172// in the same RuleBuilder are filtered out. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800173func (r *RuleBuilder) Inputs() Paths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800174 outputs := r.outputSet()
Dan Willemsen633c5022019-04-12 11:11:38 -0700175 depFiles := r.depFileSet()
Colin Crossfeec25b2019-01-30 17:32:39 -0800176
Colin Cross69f59a32019-02-15 10:39:37 -0800177 inputs := make(map[string]Path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800178 for _, c := range r.commands {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400179 for _, input := range append(c.inputs, c.implicits...) {
Dan Willemsen633c5022019-04-12 11:11:38 -0700180 inputStr := input.String()
181 if _, isOutput := outputs[inputStr]; !isOutput {
182 if _, isDepFile := depFiles[inputStr]; !isDepFile {
183 inputs[input.String()] = input
184 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800185 }
186 }
187 }
188
Colin Cross69f59a32019-02-15 10:39:37 -0800189 var inputList Paths
190 for _, input := range inputs {
Colin Crossfeec25b2019-01-30 17:32:39 -0800191 inputList = append(inputList, input)
192 }
Colin Cross69f59a32019-02-15 10:39:37 -0800193
194 sort.Slice(inputList, func(i, j int) bool {
195 return inputList[i].String() < inputList[j].String()
196 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800197
198 return inputList
199}
200
Colin Crossda71eda2020-02-21 16:55:19 -0800201// OrderOnlys returns the list of paths that were passed to the RuleBuilderCommand.OrderOnly or
202// RuleBuilderCommand.OrderOnlys. The list is sorted and duplicates removed.
203func (r *RuleBuilder) OrderOnlys() Paths {
204 orderOnlys := make(map[string]Path)
205 for _, c := range r.commands {
206 for _, orderOnly := range c.orderOnlys {
207 orderOnlys[orderOnly.String()] = orderOnly
208 }
209 }
210
211 var orderOnlyList Paths
212 for _, orderOnly := range orderOnlys {
213 orderOnlyList = append(orderOnlyList, orderOnly)
214 }
215
216 sort.Slice(orderOnlyList, func(i, j int) bool {
217 return orderOnlyList[i].String() < orderOnlyList[j].String()
218 })
219
220 return orderOnlyList
221}
222
Colin Cross69f59a32019-02-15 10:39:37 -0800223func (r *RuleBuilder) outputSet() map[string]WritablePath {
224 outputs := make(map[string]WritablePath)
Colin Crossfeec25b2019-01-30 17:32:39 -0800225 for _, c := range r.commands {
226 for _, output := range c.outputs {
Colin Cross69f59a32019-02-15 10:39:37 -0800227 outputs[output.String()] = output
Colin Crossfeec25b2019-01-30 17:32:39 -0800228 }
229 }
230 return outputs
231}
232
Colin Crossda71eda2020-02-21 16:55:19 -0800233// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
234// output paths, such as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or
235// RuleBuilderCommand.FlagWithInput. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800236func (r *RuleBuilder) Outputs() WritablePaths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800237 outputs := r.outputSet()
238
Colin Cross69f59a32019-02-15 10:39:37 -0800239 var outputList WritablePaths
240 for _, output := range outputs {
Colin Cross5cb5b092019-02-02 21:25:18 -0800241 if !r.temporariesSet[output] {
242 outputList = append(outputList, output)
243 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800244 }
Colin Cross69f59a32019-02-15 10:39:37 -0800245
246 sort.Slice(outputList, func(i, j int) bool {
247 return outputList[i].String() < outputList[j].String()
248 })
249
Colin Crossfeec25b2019-01-30 17:32:39 -0800250 return outputList
251}
252
Jingwen Chence679d22020-09-23 04:30:02 +0000253func (r *RuleBuilder) symlinkOutputSet() map[string]WritablePath {
254 symlinkOutputs := make(map[string]WritablePath)
255 for _, c := range r.commands {
256 for _, symlinkOutput := range c.symlinkOutputs {
257 symlinkOutputs[symlinkOutput.String()] = symlinkOutput
258 }
259 }
260 return symlinkOutputs
261}
262
263// SymlinkOutputs returns the list of paths that the executor (Ninja) would
264// verify, after build edge completion, that:
265//
266// 1) Created output symlinks match the list of paths in this list exactly (no more, no fewer)
267// 2) Created output files are *not* declared in this list.
268//
269// These symlink outputs are expected to be a subset of outputs or implicit
270// outputs, or they would fail validation at build param construction time
271// later, to support other non-rule-builder approaches for constructing
272// statements.
273func (r *RuleBuilder) SymlinkOutputs() WritablePaths {
274 symlinkOutputs := r.symlinkOutputSet()
275
276 var symlinkOutputList WritablePaths
277 for _, symlinkOutput := range symlinkOutputs {
278 symlinkOutputList = append(symlinkOutputList, symlinkOutput)
279 }
280
281 sort.Slice(symlinkOutputList, func(i, j int) bool {
282 return symlinkOutputList[i].String() < symlinkOutputList[j].String()
283 })
284
285 return symlinkOutputList
286}
287
Dan Willemsen633c5022019-04-12 11:11:38 -0700288func (r *RuleBuilder) depFileSet() map[string]WritablePath {
289 depFiles := make(map[string]WritablePath)
290 for _, c := range r.commands {
291 for _, depFile := range c.depFiles {
292 depFiles[depFile.String()] = depFile
293 }
294 }
295 return depFiles
296}
297
Colin Cross1d2cf042019-03-29 15:33:06 -0700298// DepFiles returns the list of paths that were passed to the RuleBuilderCommand methods that take depfile paths, such
299// as RuleBuilderCommand.DepFile or RuleBuilderCommand.FlagWithDepFile.
300func (r *RuleBuilder) DepFiles() WritablePaths {
301 var depFiles WritablePaths
302
303 for _, c := range r.commands {
304 for _, depFile := range c.depFiles {
305 depFiles = append(depFiles, depFile)
306 }
307 }
308
309 return depFiles
310}
311
Colin Cross758290d2019-02-01 16:42:32 -0800312// Installs returns the list of tuples passed to Install.
Colin Crossdeabb942019-02-11 14:11:09 -0800313func (r *RuleBuilder) Installs() RuleBuilderInstalls {
314 return append(RuleBuilderInstalls(nil), r.installs...)
Colin Crossfeec25b2019-01-30 17:32:39 -0800315}
316
Colin Cross69f59a32019-02-15 10:39:37 -0800317func (r *RuleBuilder) toolsSet() map[string]Path {
318 tools := make(map[string]Path)
Colin Cross5cb5b092019-02-02 21:25:18 -0800319 for _, c := range r.commands {
320 for _, tool := range c.tools {
Colin Cross69f59a32019-02-15 10:39:37 -0800321 tools[tool.String()] = tool
Colin Cross5cb5b092019-02-02 21:25:18 -0800322 }
323 }
324
325 return tools
326}
327
Colin Crossda71eda2020-02-21 16:55:19 -0800328// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method. The
329// list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800330func (r *RuleBuilder) Tools() Paths {
Colin Cross5cb5b092019-02-02 21:25:18 -0800331 toolsSet := r.toolsSet()
332
Colin Cross69f59a32019-02-15 10:39:37 -0800333 var toolsList Paths
334 for _, tool := range toolsSet {
Colin Cross5cb5b092019-02-02 21:25:18 -0800335 toolsList = append(toolsList, tool)
Colin Crossfeec25b2019-01-30 17:32:39 -0800336 }
Colin Cross69f59a32019-02-15 10:39:37 -0800337
338 sort.Slice(toolsList, func(i, j int) bool {
339 return toolsList[i].String() < toolsList[j].String()
340 })
341
Colin Cross5cb5b092019-02-02 21:25:18 -0800342 return toolsList
Colin Crossfeec25b2019-01-30 17:32:39 -0800343}
344
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700345// RspFileInputs returns the list of paths that were passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
346func (r *RuleBuilder) RspFileInputs() Paths {
347 var rspFileInputs Paths
348 for _, c := range r.commands {
349 if c.rspFileInputs != nil {
350 if rspFileInputs != nil {
351 panic("Multiple commands in a rule may not have rsp file inputs")
352 }
353 rspFileInputs = c.rspFileInputs
354 }
355 }
356
357 return rspFileInputs
358}
359
360// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
Colin Crossfeec25b2019-01-30 17:32:39 -0800361func (r *RuleBuilder) Commands() []string {
362 var commands []string
363 for _, c := range r.commands {
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700364 commands = append(commands, c.String())
365 }
366 return commands
367}
368
Colin Cross3d680512020-11-13 16:23:53 -0800369// NinjaEscapedCommands returns a slice containing the built command line after ninja escaping for each call to
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700370// RuleBuilder.Command.
371func (r *RuleBuilder) NinjaEscapedCommands() []string {
372 var commands []string
373 for _, c := range r.commands {
374 commands = append(commands, c.NinjaEscapedString())
Colin Crossfeec25b2019-01-30 17:32:39 -0800375 }
376 return commands
377}
378
Colin Cross758290d2019-02-01 16:42:32 -0800379// BuilderContext is a subset of ModuleContext and SingletonContext.
Colin Cross786cd6d2019-02-01 16:41:11 -0800380type BuilderContext interface {
381 PathContext
382 Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
383 Build(PackageContext, BuildParams)
384}
385
Colin Cross758290d2019-02-01 16:42:32 -0800386var _ BuilderContext = ModuleContext(nil)
387var _ BuilderContext = SingletonContext(nil)
388
Colin Cross1d2cf042019-03-29 15:33:06 -0700389func (r *RuleBuilder) depFileMergerCmd(ctx PathContext, depFiles WritablePaths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700390 return r.Command().
Colin Crossee94d6a2019-07-08 17:08:34 -0700391 BuiltTool(ctx, "dep_fixer").
Dan Willemsen633c5022019-04-12 11:11:38 -0700392 Inputs(depFiles.Paths())
Colin Cross1d2cf042019-03-29 15:33:06 -0700393}
394
Colin Cross758290d2019-02-01 16:42:32 -0800395// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
396// Outputs.
Colin Cross786cd6d2019-02-01 16:41:11 -0800397func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string, desc string) {
Colin Cross1d2cf042019-03-29 15:33:06 -0700398 name = ninjaNameEscape(name)
399
Colin Cross0d2f40a2019-02-05 22:31:15 -0800400 if len(r.missingDeps) > 0 {
401 ctx.Build(pctx, BuildParams{
402 Rule: ErrorRule,
Colin Cross69f59a32019-02-15 10:39:37 -0800403 Outputs: r.Outputs(),
Colin Crossda71eda2020-02-21 16:55:19 -0800404 OrderOnly: r.OrderOnlys(),
Colin Cross0d2f40a2019-02-05 22:31:15 -0800405 Description: desc,
406 Args: map[string]string{
407 "error": "missing dependencies: " + strings.Join(r.missingDeps, ", "),
408 },
409 })
410 return
411 }
412
Colin Cross1d2cf042019-03-29 15:33:06 -0700413 var depFile WritablePath
414 var depFormat blueprint.Deps
415 if depFiles := r.DepFiles(); len(depFiles) > 0 {
416 depFile = depFiles[0]
417 depFormat = blueprint.DepsGCC
418 if len(depFiles) > 1 {
419 // Add a command locally that merges all depfiles together into the first depfile.
Dan Willemsen633c5022019-04-12 11:11:38 -0700420 r.depFileMergerCmd(ctx, depFiles)
421
422 if r.sbox {
423 // Check for Rel() errors, as all depfiles should be in the output dir
424 for _, path := range depFiles[1:] {
425 Rel(ctx, r.sboxOutDir.String(), path.String())
426 }
427 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700428 }
429 }
430
Dan Willemsen633c5022019-04-12 11:11:38 -0700431 tools := r.Tools()
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700432 commands := r.NinjaEscapedCommands()
Dan Willemsen633c5022019-04-12 11:11:38 -0700433 outputs := r.Outputs()
Colin Cross3d680512020-11-13 16:23:53 -0800434 inputs := r.Inputs()
Dan Willemsen633c5022019-04-12 11:11:38 -0700435
436 if len(commands) == 0 {
437 return
438 }
439 if len(outputs) == 0 {
440 panic("No outputs specified from any Commands")
441 }
442
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700443 commandString := strings.Join(commands, " && ")
Dan Willemsen633c5022019-04-12 11:11:38 -0700444
445 if r.sbox {
446 sboxOutputs := make([]string, len(outputs))
447 for i, output := range outputs {
Colin Cross3d680512020-11-13 16:23:53 -0800448 sboxOutputs[i] = filepath.Join(sboxOutDir, Rel(ctx, r.sboxOutDir.String(), output.String()))
Dan Willemsen633c5022019-04-12 11:11:38 -0700449 }
450
Dan Willemsen633c5022019-04-12 11:11:38 -0700451 commandString = proptools.ShellEscape(commandString)
452 if !strings.HasPrefix(commandString, `'`) {
453 commandString = `'` + commandString + `'`
454 }
455
456 sboxCmd := &RuleBuilderCommand{}
Colin Crossee94d6a2019-07-08 17:08:34 -0700457 sboxCmd.BuiltTool(ctx, "sbox").
Dan Willemsen633c5022019-04-12 11:11:38 -0700458 Flag("-c").Text(commandString).
459 Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(ctx).String())).
Dan Willemsenc89b6f12019-08-29 14:47:40 -0700460 Flag("--output-root").Text(r.sboxOutDir.String())
461
462 if depFile != nil {
463 sboxCmd.Flag("--depfile-out").Text(depFile.String())
464 }
465
Colin Cross3d680512020-11-13 16:23:53 -0800466 // Add a hash of the list of input files to the xbox command line so that ninja reruns
467 // it when the list of input files changes.
468 sboxCmd.FlagWithArg("--input-hash ", hashSrcFiles(inputs))
469
Dan Willemsenc89b6f12019-08-29 14:47:40 -0700470 sboxCmd.Flags(sboxOutputs)
Dan Willemsen633c5022019-04-12 11:11:38 -0700471
Colin Crosscfec40c2019-07-08 17:07:18 -0700472 commandString = sboxCmd.buf.String()
Dan Willemsen633c5022019-04-12 11:11:38 -0700473 tools = append(tools, sboxCmd.tools...)
Colin Cross3d680512020-11-13 16:23:53 -0800474 } else {
475 // If not using sbox the rule will run the command directly, put the hash of the
476 // list of input files in a comment at the end of the command line to ensure ninja
477 // reruns the rule when the list of input files changes.
478 commandString += " # hash of input list: " + hashSrcFiles(inputs)
Dan Willemsen633c5022019-04-12 11:11:38 -0700479 }
480
Colin Cross1d2cf042019-03-29 15:33:06 -0700481 // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700482 // ImplicitOutputs. RuleBuilder only uses "$out" for the rsp file location, so the distinction between Outputs and
483 // ImplicitOutputs doesn't matter.
Dan Willemsen633c5022019-04-12 11:11:38 -0700484 output := outputs[0]
485 implicitOutputs := outputs[1:]
Colin Cross1d2cf042019-03-29 15:33:06 -0700486
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700487 var rspFile, rspFileContent string
488 rspFileInputs := r.RspFileInputs()
489 if rspFileInputs != nil {
490 rspFile = "$out.rsp"
491 rspFileContent = "$in"
492 }
493
Colin Cross8b8bec32019-11-15 13:18:43 -0800494 var pool blueprint.Pool
Ramy Medhat8ea054a2020-01-27 14:19:44 -0500495 if ctx.Config().UseGoma() && r.remoteable.Goma {
Colin Cross8b8bec32019-11-15 13:18:43 -0800496 // When USE_GOMA=true is set and the rule is supported by goma, allow jobs to run outside the local pool.
Ramy Medhat8ea054a2020-01-27 14:19:44 -0500497 } else if ctx.Config().UseRBE() && r.remoteable.RBE {
Ramy Medhat944839a2020-03-31 22:14:52 -0400498 // When USE_RBE=true is set and the rule is supported by RBE, use the remotePool.
499 pool = remotePool
Colin Cross8b8bec32019-11-15 13:18:43 -0800500 } else if r.highmem {
501 pool = highmemPool
502 } else if ctx.Config().UseRemoteBuild() {
503 pool = localPool
504 }
505
Dan Willemsen633c5022019-04-12 11:11:38 -0700506 ctx.Build(pctx, BuildParams{
507 Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700508 Command: commandString,
509 CommandDeps: tools.Strings(),
510 Restat: r.restat,
511 Rspfile: rspFile,
512 RspfileContent: rspFileContent,
Colin Cross8b8bec32019-11-15 13:18:43 -0800513 Pool: pool,
Dan Willemsen633c5022019-04-12 11:11:38 -0700514 }),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700515 Inputs: rspFileInputs,
Colin Cross3d680512020-11-13 16:23:53 -0800516 Implicits: inputs,
Dan Willemsen633c5022019-04-12 11:11:38 -0700517 Output: output,
518 ImplicitOutputs: implicitOutputs,
Jingwen Chence679d22020-09-23 04:30:02 +0000519 SymlinkOutputs: r.SymlinkOutputs(),
Dan Willemsen633c5022019-04-12 11:11:38 -0700520 Depfile: depFile,
521 Deps: depFormat,
522 Description: desc,
523 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800524}
525
Colin Cross758290d2019-02-01 16:42:32 -0800526// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
527// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the
528// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
529// space as a separator from the previous method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800530type RuleBuilderCommand struct {
Jingwen Chence679d22020-09-23 04:30:02 +0000531 buf strings.Builder
532 inputs Paths
533 implicits Paths
534 orderOnlys Paths
535 outputs WritablePaths
536 symlinkOutputs WritablePaths
537 depFiles WritablePaths
538 tools Paths
539 rspFileInputs Paths
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700540
541 // spans [start,end) of the command that should not be ninja escaped
542 unescapedSpans [][2]int
Dan Willemsen633c5022019-04-12 11:11:38 -0700543
Colin Cross3d680512020-11-13 16:23:53 -0800544 sbox bool
545 // outDir is the directory that will contain the output files of the rules. sbox will copy
546 // the output files from the sandbox directory to this directory when it finishes.
547 outDir WritablePath
Dan Willemsen633c5022019-04-12 11:11:38 -0700548}
549
550func (c *RuleBuilderCommand) addInput(path Path) string {
551 if c.sbox {
Colin Cross3d680512020-11-13 16:23:53 -0800552 if rel, isRel, _ := maybeRelErr(c.outDir.String(), path.String()); isRel {
553 return filepath.Join(sboxOutDir, rel)
Dan Willemsen633c5022019-04-12 11:11:38 -0700554 }
555 }
556 c.inputs = append(c.inputs, path)
557 return path.String()
558}
559
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400560func (c *RuleBuilderCommand) addImplicit(path Path) string {
561 if c.sbox {
Colin Cross3d680512020-11-13 16:23:53 -0800562 if rel, isRel, _ := maybeRelErr(c.outDir.String(), path.String()); isRel {
563 return filepath.Join(sboxOutDir, rel)
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400564 }
565 }
566 c.implicits = append(c.implicits, path)
567 return path.String()
568}
569
Colin Crossda71eda2020-02-21 16:55:19 -0800570func (c *RuleBuilderCommand) addOrderOnly(path Path) {
571 c.orderOnlys = append(c.orderOnlys, path)
572}
573
Colin Cross3d680512020-11-13 16:23:53 -0800574func (c *RuleBuilderCommand) outputStr(path WritablePath) string {
Dan Willemsen633c5022019-04-12 11:11:38 -0700575 if c.sbox {
Colin Cross3d680512020-11-13 16:23:53 -0800576 return SboxPathForOutput(path, c.outDir)
Dan Willemsen633c5022019-04-12 11:11:38 -0700577 }
578 return path.String()
Colin Crossfeec25b2019-01-30 17:32:39 -0800579}
580
Colin Cross3d680512020-11-13 16:23:53 -0800581// SboxPathForOutput takes an output path and the out directory passed to RuleBuilder.Sbox(),
582// and returns the corresponding path for the output in the sbox sandbox. This can be used
583// on the RuleBuilder command line to reference the output.
584func SboxPathForOutput(path WritablePath, outDir WritablePath) string {
585 // Errors will be handled in RuleBuilder.Build where we have a context to report them
586 rel, _, _ := maybeRelErr(outDir.String(), path.String())
587 return filepath.Join(sboxOutDir, rel)
588}
589
Colin Cross758290d2019-02-01 16:42:32 -0800590// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
591// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800592func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
Colin Crosscfec40c2019-07-08 17:07:18 -0700593 if c.buf.Len() > 0 {
594 c.buf.WriteByte(' ')
Colin Crossfeec25b2019-01-30 17:32:39 -0800595 }
Colin Crosscfec40c2019-07-08 17:07:18 -0700596 c.buf.WriteString(text)
Colin Crossfeec25b2019-01-30 17:32:39 -0800597 return c
598}
599
Colin Cross758290d2019-02-01 16:42:32 -0800600// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or
601// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800602func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
603 return c.Text(fmt.Sprintf(format, a...))
604}
605
Colin Cross758290d2019-02-01 16:42:32 -0800606// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the
607// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800608func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
609 return c.Text(flag)
610}
611
Colin Crossab054432019-07-15 16:13:59 -0700612// OptionalFlag adds the specified raw text to the command line if it is not nil. The text should not contain input or
613// output paths or the rule will not have them listed in its dependencies or outputs.
614func (c *RuleBuilderCommand) OptionalFlag(flag *string) *RuleBuilderCommand {
615 if flag != nil {
616 c.Text(*flag)
617 }
618
619 return c
620}
621
Colin Cross92b7d582019-03-29 15:32:51 -0700622// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the
623// rule will not have them listed in its dependencies or outputs.
624func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
625 for _, flag := range flags {
626 c.Text(flag)
627 }
628 return c
629}
630
Colin Cross758290d2019-02-01 16:42:32 -0800631// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
632// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
633// outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800634func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
635 return c.Text(flag + arg)
636}
637
Colin Crossc7ed0042019-02-11 14:11:09 -0800638// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to
639// calling FlagWithArg for argument.
640func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand {
641 for _, arg := range args {
642 c.FlagWithArg(flag, arg)
643 }
644 return c
645}
646
Roland Levillain2da5d9a2019-02-27 16:56:41 +0000647// FlagWithList adds the specified flag and list of arguments to the command line, with the arguments joined by sep
Colin Cross758290d2019-02-01 16:42:32 -0800648// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or
649// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800650func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
651 return c.Text(flag + strings.Join(list, sep))
652}
653
Colin Cross758290d2019-02-01 16:42:32 -0800654// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by
655// RuleBuilder.Tools.
Colin Cross69f59a32019-02-15 10:39:37 -0800656func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800657 c.tools = append(c.tools, path)
Colin Cross69f59a32019-02-15 10:39:37 -0800658 return c.Text(path.String())
Colin Crossfeec25b2019-01-30 17:32:39 -0800659}
660
Colin Crossee94d6a2019-07-08 17:08:34 -0700661// BuiltTool adds the specified tool path that was built using a host Soong module to the command line. The path will
662// be also added to the dependencies returned by RuleBuilder.Tools.
663//
664// It is equivalent to:
665// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
666func (c *RuleBuilderCommand) BuiltTool(ctx PathContext, tool string) *RuleBuilderCommand {
667 return c.Tool(ctx.Config().HostToolPath(ctx, tool))
668}
669
670// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the
671// dependencies returned by RuleBuilder.Tools.
672//
673// It is equivalent to:
674// cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
675func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
676 return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
677}
678
Colin Cross758290d2019-02-01 16:42:32 -0800679// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
680// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800681func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700682 return c.Text(c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800683}
684
Colin Cross758290d2019-02-01 16:42:32 -0800685// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
686// dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800687func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800688 for _, path := range paths {
689 c.Input(path)
690 }
691 return c
692}
693
694// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
695// command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800696func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400697 c.addImplicit(path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800698 return c
699}
700
Colin Cross758290d2019-02-01 16:42:32 -0800701// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
702// command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800703func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700704 for _, path := range paths {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400705 c.addImplicit(path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700706 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800707 return c
708}
709
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400710// GetImplicits returns the command's implicit inputs.
711func (c *RuleBuilderCommand) GetImplicits() Paths {
712 return c.implicits
713}
714
Colin Crossda71eda2020-02-21 16:55:19 -0800715// OrderOnly adds the specified input path to the dependencies returned by RuleBuilder.OrderOnlys
716// without modifying the command line.
717func (c *RuleBuilderCommand) OrderOnly(path Path) *RuleBuilderCommand {
718 c.addOrderOnly(path)
719 return c
720}
721
722// OrderOnlys adds the specified input paths to the dependencies returned by RuleBuilder.OrderOnlys
723// without modifying the command line.
724func (c *RuleBuilderCommand) OrderOnlys(paths Paths) *RuleBuilderCommand {
725 for _, path := range paths {
726 c.addOrderOnly(path)
727 }
728 return c
729}
730
Colin Cross758290d2019-02-01 16:42:32 -0800731// Output adds the specified output path to the command line. The path will also be added to the outputs returned by
732// RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800733func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800734 c.outputs = append(c.outputs, path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700735 return c.Text(c.outputStr(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800736}
737
Colin Cross758290d2019-02-01 16:42:32 -0800738// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
739// the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800740func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800741 for _, path := range paths {
742 c.Output(path)
743 }
744 return c
745}
746
Dan Willemsen1945a4b2019-06-04 17:10:41 -0700747// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
748// and will be the temporary output directory managed by sbox, not the final one.
749func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand {
750 if !c.sbox {
751 panic("OutputDir only valid with Sbox")
752 }
Colin Cross3d680512020-11-13 16:23:53 -0800753 return c.Text(sboxOutDir)
Dan Willemsen1945a4b2019-06-04 17:10:41 -0700754}
755
Colin Cross1d2cf042019-03-29 15:33:06 -0700756// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
757// line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles are added to
758// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
759func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
760 c.depFiles = append(c.depFiles, path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700761 return c.Text(c.outputStr(path))
Colin Cross1d2cf042019-03-29 15:33:06 -0700762}
763
Colin Cross758290d2019-02-01 16:42:32 -0800764// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
765// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800766func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800767 c.outputs = append(c.outputs, path)
768 return c
769}
770
Colin Cross758290d2019-02-01 16:42:32 -0800771// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
772// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800773func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800774 c.outputs = append(c.outputs, paths...)
775 return c
776}
777
Jingwen Chence679d22020-09-23 04:30:02 +0000778// ImplicitSymlinkOutput declares the specified path as an implicit output that
779// will be a symlink instead of a regular file. Does not modify the command
780// line.
781func (c *RuleBuilderCommand) ImplicitSymlinkOutput(path WritablePath) *RuleBuilderCommand {
782 c.symlinkOutputs = append(c.symlinkOutputs, path)
783 return c.ImplicitOutput(path)
784}
785
786// ImplicitSymlinkOutputs declares the specified paths as implicit outputs that
787// will be a symlinks instead of regular files. Does not modify the command
788// line.
789func (c *RuleBuilderCommand) ImplicitSymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
790 for _, path := range paths {
791 c.ImplicitSymlinkOutput(path)
792 }
793 return c
794}
795
796// SymlinkOutput declares the specified path as an output that will be a symlink
797// instead of a regular file. Modifies the command line.
798func (c *RuleBuilderCommand) SymlinkOutput(path WritablePath) *RuleBuilderCommand {
799 c.symlinkOutputs = append(c.symlinkOutputs, path)
800 return c.Output(path)
801}
802
803// SymlinkOutputsl declares the specified paths as outputs that will be symlinks
804// instead of regular files. Modifies the command line.
805func (c *RuleBuilderCommand) SymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
806 for _, path := range paths {
807 c.SymlinkOutput(path)
808 }
809 return c
810}
811
Colin Cross1d2cf042019-03-29 15:33:06 -0700812// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
813// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles
814// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
815// depfiles together.
816func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand {
817 c.depFiles = append(c.depFiles, path)
818 return c
819}
820
Colin Cross758290d2019-02-01 16:42:32 -0800821// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
822// will also be added to the dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800823func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700824 return c.Text(flag + c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800825}
826
Colin Cross758290d2019-02-01 16:42:32 -0800827// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
828// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
829// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800830func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700831 strs := make([]string, len(paths))
832 for i, path := range paths {
833 strs[i] = c.addInput(path)
834 }
835 return c.FlagWithList(flag, strs, sep)
Colin Crossfeec25b2019-01-30 17:32:39 -0800836}
837
Colin Cross758290d2019-02-01 16:42:32 -0800838// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
839// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for
840// each input path.
Colin Cross69f59a32019-02-15 10:39:37 -0800841func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800842 for _, path := range paths {
843 c.FlagWithInput(flag, path)
844 }
845 return c
846}
847
848// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path
849// will also be added to the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800850func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800851 c.outputs = append(c.outputs, path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700852 return c.Text(flag + c.outputStr(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800853}
854
Colin Cross1d2cf042019-03-29 15:33:06 -0700855// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path
856// will also be added to the outputs returned by RuleBuilder.Outputs.
857func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
858 c.depFiles = append(c.depFiles, path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700859 return c.Text(flag + c.outputStr(path))
Colin Cross1d2cf042019-03-29 15:33:06 -0700860}
861
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700862// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with no separator
863// between them. The paths will be written to the rspfile.
864func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, paths Paths) *RuleBuilderCommand {
865 if c.rspFileInputs != nil {
866 panic("FlagWithRspFileInputList cannot be called if rsp file inputs have already been provided")
867 }
868
869 // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
870 // generated.
871 if paths == nil {
872 paths = Paths{}
873 }
874
875 c.rspFileInputs = paths
876
877 rspFile := "$out.rsp"
878 c.FlagWithArg(flag, rspFile)
879 c.unescapedSpans = append(c.unescapedSpans, [2]int{c.buf.Len() - len(rspFile), c.buf.Len()})
880 return c
881}
882
Colin Cross758290d2019-02-01 16:42:32 -0800883// String returns the command line.
884func (c *RuleBuilderCommand) String() string {
Colin Crosscfec40c2019-07-08 17:07:18 -0700885 return c.buf.String()
Colin Cross758290d2019-02-01 16:42:32 -0800886}
Colin Cross1d2cf042019-03-29 15:33:06 -0700887
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700888// String returns the command line.
889func (c *RuleBuilderCommand) NinjaEscapedString() string {
890 return ninjaEscapeExceptForSpans(c.String(), c.unescapedSpans)
891}
892
893func ninjaEscapeExceptForSpans(s string, spans [][2]int) string {
894 if len(spans) == 0 {
895 return proptools.NinjaEscape(s)
896 }
897
898 sb := strings.Builder{}
899 sb.Grow(len(s) * 11 / 10)
900
901 i := 0
902 for _, span := range spans {
903 sb.WriteString(proptools.NinjaEscape(s[i:span[0]]))
904 sb.WriteString(s[span[0]:span[1]])
905 i = span[1]
906 }
907 sb.WriteString(proptools.NinjaEscape(s[i:]))
908
909 return sb.String()
910}
911
Colin Cross1d2cf042019-03-29 15:33:06 -0700912func ninjaNameEscape(s string) string {
913 b := []byte(s)
914 escaped := false
915 for i, c := range b {
916 valid := (c >= 'a' && c <= 'z') ||
917 (c >= 'A' && c <= 'Z') ||
918 (c >= '0' && c <= '9') ||
919 (c == '_') ||
920 (c == '-') ||
921 (c == '.')
922 if !valid {
923 b[i] = '_'
924 escaped = true
925 }
926 }
927 if escaped {
928 s = string(b)
929 }
930 return s
931}
Colin Cross3d680512020-11-13 16:23:53 -0800932
933// hashSrcFiles returns a hash of the list of source files. It is used to ensure the command line
934// or the sbox textproto manifest change even if the input files are not listed on the command line.
935func hashSrcFiles(srcFiles Paths) string {
936 h := sha256.New()
937 srcFileList := strings.Join(srcFiles.Strings(), "\n")
938 h.Write([]byte(srcFileList))
939 return fmt.Sprintf("%x", h.Sum(nil))
940}