blob: 3efe9f8c609ba43ba14b68bd742d224df06ff0a2 [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"
Colin Crosse16ce362020-11-12 08:29:30 -080023 "testing"
Colin Crossfeec25b2019-01-30 17:32:39 -080024
Colin Crosse16ce362020-11-12 08:29:30 -080025 "github.com/golang/protobuf/proto"
Colin Crossfeec25b2019-01-30 17:32:39 -080026 "github.com/google/blueprint"
27 "github.com/google/blueprint/proptools"
Dan Willemsen633c5022019-04-12 11:11:38 -070028
Colin Crosse16ce362020-11-12 08:29:30 -080029 "android/soong/cmd/sbox/sbox_proto"
Dan Willemsen633c5022019-04-12 11:11:38 -070030 "android/soong/shared"
Colin Crossfeec25b2019-01-30 17:32:39 -080031)
32
Colin Crosse16ce362020-11-12 08:29:30 -080033const sboxSandboxBaseDir = "__SBOX_SANDBOX_DIR__"
34const sboxOutSubDir = "out"
35const sboxOutDir = sboxSandboxBaseDir + "/" + sboxOutSubDir
Colin Cross3d680512020-11-13 16:23:53 -080036
Colin Cross758290d2019-02-01 16:42:32 -080037// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
38// graph.
Colin Crossfeec25b2019-01-30 17:32:39 -080039type RuleBuilder struct {
Colin Crosse16ce362020-11-12 08:29:30 -080040 commands []*RuleBuilderCommand
41 installs RuleBuilderInstalls
42 temporariesSet map[WritablePath]bool
43 restat bool
44 sbox bool
45 highmem bool
46 remoteable RemoteRuleSupports
47 sboxOutDir WritablePath
48 sboxManifestPath WritablePath
49 missingDeps []string
Colin Crossfeec25b2019-01-30 17:32:39 -080050}
51
Colin Cross758290d2019-02-01 16:42:32 -080052// NewRuleBuilder returns a newly created RuleBuilder.
53func NewRuleBuilder() *RuleBuilder {
Colin Cross5cb5b092019-02-02 21:25:18 -080054 return &RuleBuilder{
Colin Cross69f59a32019-02-15 10:39:37 -080055 temporariesSet: make(map[WritablePath]bool),
Colin Cross5cb5b092019-02-02 21:25:18 -080056 }
Colin Cross758290d2019-02-01 16:42:32 -080057}
58
59// RuleBuilderInstall is a tuple of install from and to locations.
60type RuleBuilderInstall struct {
Colin Cross69f59a32019-02-15 10:39:37 -080061 From Path
62 To string
Colin Cross758290d2019-02-01 16:42:32 -080063}
64
Colin Crossdeabb942019-02-11 14:11:09 -080065type RuleBuilderInstalls []RuleBuilderInstall
66
67// String returns the RuleBuilderInstalls in the form used by $(call copy-many-files) in Make, a space separated
68// list of from:to tuples.
69func (installs RuleBuilderInstalls) String() string {
70 sb := strings.Builder{}
71 for i, install := range installs {
72 if i != 0 {
73 sb.WriteRune(' ')
74 }
Colin Cross69f59a32019-02-15 10:39:37 -080075 sb.WriteString(install.From.String())
Colin Crossdeabb942019-02-11 14:11:09 -080076 sb.WriteRune(':')
77 sb.WriteString(install.To)
78 }
79 return sb.String()
80}
81
Colin Cross0d2f40a2019-02-05 22:31:15 -080082// MissingDeps adds modules to the list of missing dependencies. If MissingDeps
83// is called with a non-empty input, any call to Build will result in a rule
84// that will print an error listing the missing dependencies and fail.
85// MissingDeps should only be called if Config.AllowMissingDependencies() is
86// true.
87func (r *RuleBuilder) MissingDeps(missingDeps []string) {
88 r.missingDeps = append(r.missingDeps, missingDeps...)
89}
90
Colin Cross758290d2019-02-01 16:42:32 -080091// 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 -070092//
93// Restat is not compatible with Sbox()
Colin Crossfeec25b2019-01-30 17:32:39 -080094func (r *RuleBuilder) Restat() *RuleBuilder {
Dan Willemsen633c5022019-04-12 11:11:38 -070095 if r.sbox {
96 panic("Restat() is not compatible with Sbox()")
97 }
Colin Crossfeec25b2019-01-30 17:32:39 -080098 r.restat = true
99 return r
100}
101
Colin Cross8b8bec32019-11-15 13:18:43 -0800102// HighMem marks the rule as a high memory rule, which will limit how many run in parallel with other high memory
103// rules.
104func (r *RuleBuilder) HighMem() *RuleBuilder {
105 r.highmem = true
106 return r
107}
108
109// Remoteable marks the rule as supporting remote execution.
110func (r *RuleBuilder) Remoteable(supports RemoteRuleSupports) *RuleBuilder {
111 r.remoteable = supports
112 return r
113}
114
Colin Crosse16ce362020-11-12 08:29:30 -0800115// Sbox marks the rule as needing to be wrapped by sbox. The outputDir should point to the output
116// directory that sbox will wipe. It should not be written to by any other rule. manifestPath should
117// point to a location where sbox's manifest will be written and must be outside outputDir. sbox
118// will ensure that all outputs have been written, and will discard any output files that were not
119// specified.
Dan Willemsen633c5022019-04-12 11:11:38 -0700120//
121// Sbox is not compatible with Restat()
Colin Crosse16ce362020-11-12 08:29:30 -0800122func (r *RuleBuilder) Sbox(outputDir WritablePath, manifestPath WritablePath) *RuleBuilder {
Dan Willemsen633c5022019-04-12 11:11:38 -0700123 if r.sbox {
124 panic("Sbox() may not be called more than once")
125 }
126 if len(r.commands) > 0 {
127 panic("Sbox() may not be called after Command()")
128 }
129 if r.restat {
130 panic("Sbox() is not compatible with Restat()")
131 }
132 r.sbox = true
133 r.sboxOutDir = outputDir
Colin Crosse16ce362020-11-12 08:29:30 -0800134 r.sboxManifestPath = manifestPath
Dan Willemsen633c5022019-04-12 11:11:38 -0700135 return r
136}
137
Colin Cross758290d2019-02-01 16:42:32 -0800138// Install associates an output of the rule with an install location, which can be retrieved later using
139// RuleBuilder.Installs.
Colin Cross69f59a32019-02-15 10:39:37 -0800140func (r *RuleBuilder) Install(from Path, to string) {
Colin Crossfeec25b2019-01-30 17:32:39 -0800141 r.installs = append(r.installs, RuleBuilderInstall{from, to})
142}
143
Colin Cross758290d2019-02-01 16:42:32 -0800144// Command returns a new RuleBuilderCommand for the rule. The commands will be ordered in the rule by when they were
145// created by this method. That can be mutated through their methods in any order, as long as the mutations do not
146// race with any call to Build.
Colin Crossfeec25b2019-01-30 17:32:39 -0800147func (r *RuleBuilder) Command() *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700148 command := &RuleBuilderCommand{
Colin Cross3d680512020-11-13 16:23:53 -0800149 sbox: r.sbox,
150 outDir: r.sboxOutDir,
Dan Willemsen633c5022019-04-12 11:11:38 -0700151 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800152 r.commands = append(r.commands, command)
153 return command
154}
155
Colin Cross5cb5b092019-02-02 21:25:18 -0800156// Temporary marks an output of a command as an intermediate file that will be used as an input to another command
157// in the same rule, and should not be listed in Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800158func (r *RuleBuilder) Temporary(path WritablePath) {
Colin Cross5cb5b092019-02-02 21:25:18 -0800159 r.temporariesSet[path] = true
160}
161
162// DeleteTemporaryFiles adds a command to the rule that deletes any outputs that have been marked using Temporary
163// when the rule runs. DeleteTemporaryFiles should be called after all calls to Temporary.
164func (r *RuleBuilder) DeleteTemporaryFiles() {
Colin Cross69f59a32019-02-15 10:39:37 -0800165 var temporariesList WritablePaths
Colin Cross5cb5b092019-02-02 21:25:18 -0800166
167 for intermediate := range r.temporariesSet {
168 temporariesList = append(temporariesList, intermediate)
169 }
Colin Cross69f59a32019-02-15 10:39:37 -0800170
171 sort.Slice(temporariesList, func(i, j int) bool {
172 return temporariesList[i].String() < temporariesList[j].String()
173 })
Colin Cross5cb5b092019-02-02 21:25:18 -0800174
175 r.Command().Text("rm").Flag("-f").Outputs(temporariesList)
176}
177
Colin Crossda71eda2020-02-21 16:55:19 -0800178// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
Colin Cross3d680512020-11-13 16:23:53 -0800179// input paths, such as RuleBuilderCommand.Input, RuleBuilderCommand.Implicit, or
Colin Crossda71eda2020-02-21 16:55:19 -0800180// RuleBuilderCommand.FlagWithInput. Inputs to a command that are also outputs of another command
181// in the same RuleBuilder are filtered out. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800182func (r *RuleBuilder) Inputs() Paths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800183 outputs := r.outputSet()
Dan Willemsen633c5022019-04-12 11:11:38 -0700184 depFiles := r.depFileSet()
Colin Crossfeec25b2019-01-30 17:32:39 -0800185
Colin Cross69f59a32019-02-15 10:39:37 -0800186 inputs := make(map[string]Path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800187 for _, c := range r.commands {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400188 for _, input := range append(c.inputs, c.implicits...) {
Dan Willemsen633c5022019-04-12 11:11:38 -0700189 inputStr := input.String()
190 if _, isOutput := outputs[inputStr]; !isOutput {
191 if _, isDepFile := depFiles[inputStr]; !isDepFile {
192 inputs[input.String()] = input
193 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800194 }
195 }
196 }
197
Colin Cross69f59a32019-02-15 10:39:37 -0800198 var inputList Paths
199 for _, input := range inputs {
Colin Crossfeec25b2019-01-30 17:32:39 -0800200 inputList = append(inputList, input)
201 }
Colin Cross69f59a32019-02-15 10:39:37 -0800202
203 sort.Slice(inputList, func(i, j int) bool {
204 return inputList[i].String() < inputList[j].String()
205 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800206
207 return inputList
208}
209
Colin Crossda71eda2020-02-21 16:55:19 -0800210// OrderOnlys returns the list of paths that were passed to the RuleBuilderCommand.OrderOnly or
211// RuleBuilderCommand.OrderOnlys. The list is sorted and duplicates removed.
212func (r *RuleBuilder) OrderOnlys() Paths {
213 orderOnlys := make(map[string]Path)
214 for _, c := range r.commands {
215 for _, orderOnly := range c.orderOnlys {
216 orderOnlys[orderOnly.String()] = orderOnly
217 }
218 }
219
220 var orderOnlyList Paths
221 for _, orderOnly := range orderOnlys {
222 orderOnlyList = append(orderOnlyList, orderOnly)
223 }
224
225 sort.Slice(orderOnlyList, func(i, j int) bool {
226 return orderOnlyList[i].String() < orderOnlyList[j].String()
227 })
228
229 return orderOnlyList
230}
231
Colin Cross69f59a32019-02-15 10:39:37 -0800232func (r *RuleBuilder) outputSet() map[string]WritablePath {
233 outputs := make(map[string]WritablePath)
Colin Crossfeec25b2019-01-30 17:32:39 -0800234 for _, c := range r.commands {
235 for _, output := range c.outputs {
Colin Cross69f59a32019-02-15 10:39:37 -0800236 outputs[output.String()] = output
Colin Crossfeec25b2019-01-30 17:32:39 -0800237 }
238 }
239 return outputs
240}
241
Colin Crossda71eda2020-02-21 16:55:19 -0800242// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
243// output paths, such as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or
244// RuleBuilderCommand.FlagWithInput. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800245func (r *RuleBuilder) Outputs() WritablePaths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800246 outputs := r.outputSet()
247
Colin Cross69f59a32019-02-15 10:39:37 -0800248 var outputList WritablePaths
249 for _, output := range outputs {
Colin Cross5cb5b092019-02-02 21:25:18 -0800250 if !r.temporariesSet[output] {
251 outputList = append(outputList, output)
252 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800253 }
Colin Cross69f59a32019-02-15 10:39:37 -0800254
255 sort.Slice(outputList, func(i, j int) bool {
256 return outputList[i].String() < outputList[j].String()
257 })
258
Colin Crossfeec25b2019-01-30 17:32:39 -0800259 return outputList
260}
261
Jingwen Chence679d22020-09-23 04:30:02 +0000262func (r *RuleBuilder) symlinkOutputSet() map[string]WritablePath {
263 symlinkOutputs := make(map[string]WritablePath)
264 for _, c := range r.commands {
265 for _, symlinkOutput := range c.symlinkOutputs {
266 symlinkOutputs[symlinkOutput.String()] = symlinkOutput
267 }
268 }
269 return symlinkOutputs
270}
271
272// SymlinkOutputs returns the list of paths that the executor (Ninja) would
273// verify, after build edge completion, that:
274//
275// 1) Created output symlinks match the list of paths in this list exactly (no more, no fewer)
276// 2) Created output files are *not* declared in this list.
277//
278// These symlink outputs are expected to be a subset of outputs or implicit
279// outputs, or they would fail validation at build param construction time
280// later, to support other non-rule-builder approaches for constructing
281// statements.
282func (r *RuleBuilder) SymlinkOutputs() WritablePaths {
283 symlinkOutputs := r.symlinkOutputSet()
284
285 var symlinkOutputList WritablePaths
286 for _, symlinkOutput := range symlinkOutputs {
287 symlinkOutputList = append(symlinkOutputList, symlinkOutput)
288 }
289
290 sort.Slice(symlinkOutputList, func(i, j int) bool {
291 return symlinkOutputList[i].String() < symlinkOutputList[j].String()
292 })
293
294 return symlinkOutputList
295}
296
Dan Willemsen633c5022019-04-12 11:11:38 -0700297func (r *RuleBuilder) depFileSet() map[string]WritablePath {
298 depFiles := make(map[string]WritablePath)
299 for _, c := range r.commands {
300 for _, depFile := range c.depFiles {
301 depFiles[depFile.String()] = depFile
302 }
303 }
304 return depFiles
305}
306
Colin Cross1d2cf042019-03-29 15:33:06 -0700307// DepFiles returns the list of paths that were passed to the RuleBuilderCommand methods that take depfile paths, such
308// as RuleBuilderCommand.DepFile or RuleBuilderCommand.FlagWithDepFile.
309func (r *RuleBuilder) DepFiles() WritablePaths {
310 var depFiles WritablePaths
311
312 for _, c := range r.commands {
313 for _, depFile := range c.depFiles {
314 depFiles = append(depFiles, depFile)
315 }
316 }
317
318 return depFiles
319}
320
Colin Cross758290d2019-02-01 16:42:32 -0800321// Installs returns the list of tuples passed to Install.
Colin Crossdeabb942019-02-11 14:11:09 -0800322func (r *RuleBuilder) Installs() RuleBuilderInstalls {
323 return append(RuleBuilderInstalls(nil), r.installs...)
Colin Crossfeec25b2019-01-30 17:32:39 -0800324}
325
Colin Cross69f59a32019-02-15 10:39:37 -0800326func (r *RuleBuilder) toolsSet() map[string]Path {
327 tools := make(map[string]Path)
Colin Cross5cb5b092019-02-02 21:25:18 -0800328 for _, c := range r.commands {
329 for _, tool := range c.tools {
Colin Cross69f59a32019-02-15 10:39:37 -0800330 tools[tool.String()] = tool
Colin Cross5cb5b092019-02-02 21:25:18 -0800331 }
332 }
333
334 return tools
335}
336
Colin Crossda71eda2020-02-21 16:55:19 -0800337// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method. The
338// list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800339func (r *RuleBuilder) Tools() Paths {
Colin Cross5cb5b092019-02-02 21:25:18 -0800340 toolsSet := r.toolsSet()
341
Colin Cross69f59a32019-02-15 10:39:37 -0800342 var toolsList Paths
343 for _, tool := range toolsSet {
Colin Cross5cb5b092019-02-02 21:25:18 -0800344 toolsList = append(toolsList, tool)
Colin Crossfeec25b2019-01-30 17:32:39 -0800345 }
Colin Cross69f59a32019-02-15 10:39:37 -0800346
347 sort.Slice(toolsList, func(i, j int) bool {
348 return toolsList[i].String() < toolsList[j].String()
349 })
350
Colin Cross5cb5b092019-02-02 21:25:18 -0800351 return toolsList
Colin Crossfeec25b2019-01-30 17:32:39 -0800352}
353
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700354// RspFileInputs returns the list of paths that were passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
355func (r *RuleBuilder) RspFileInputs() Paths {
356 var rspFileInputs Paths
357 for _, c := range r.commands {
358 if c.rspFileInputs != nil {
359 if rspFileInputs != nil {
360 panic("Multiple commands in a rule may not have rsp file inputs")
361 }
362 rspFileInputs = c.rspFileInputs
363 }
364 }
365
366 return rspFileInputs
367}
368
369// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
Colin Crossfeec25b2019-01-30 17:32:39 -0800370func (r *RuleBuilder) Commands() []string {
371 var commands []string
372 for _, c := range r.commands {
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700373 commands = append(commands, c.String())
374 }
375 return commands
376}
377
Colin Cross3d680512020-11-13 16:23:53 -0800378// NinjaEscapedCommands returns a slice containing the built command line after ninja escaping for each call to
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700379// RuleBuilder.Command.
380func (r *RuleBuilder) NinjaEscapedCommands() []string {
381 var commands []string
382 for _, c := range r.commands {
383 commands = append(commands, c.NinjaEscapedString())
Colin Crossfeec25b2019-01-30 17:32:39 -0800384 }
385 return commands
386}
387
Colin Cross758290d2019-02-01 16:42:32 -0800388// BuilderContext is a subset of ModuleContext and SingletonContext.
Colin Cross786cd6d2019-02-01 16:41:11 -0800389type BuilderContext interface {
390 PathContext
391 Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
392 Build(PackageContext, BuildParams)
393}
394
Colin Cross758290d2019-02-01 16:42:32 -0800395var _ BuilderContext = ModuleContext(nil)
396var _ BuilderContext = SingletonContext(nil)
397
Colin Cross1d2cf042019-03-29 15:33:06 -0700398func (r *RuleBuilder) depFileMergerCmd(ctx PathContext, depFiles WritablePaths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700399 return r.Command().
Colin Crossee94d6a2019-07-08 17:08:34 -0700400 BuiltTool(ctx, "dep_fixer").
Dan Willemsen633c5022019-04-12 11:11:38 -0700401 Inputs(depFiles.Paths())
Colin Cross1d2cf042019-03-29 15:33:06 -0700402}
403
Colin Cross758290d2019-02-01 16:42:32 -0800404// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
405// Outputs.
Colin Cross786cd6d2019-02-01 16:41:11 -0800406func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string, desc string) {
Colin Cross1d2cf042019-03-29 15:33:06 -0700407 name = ninjaNameEscape(name)
408
Colin Cross0d2f40a2019-02-05 22:31:15 -0800409 if len(r.missingDeps) > 0 {
410 ctx.Build(pctx, BuildParams{
411 Rule: ErrorRule,
Colin Cross69f59a32019-02-15 10:39:37 -0800412 Outputs: r.Outputs(),
Colin Crossda71eda2020-02-21 16:55:19 -0800413 OrderOnly: r.OrderOnlys(),
Colin Cross0d2f40a2019-02-05 22:31:15 -0800414 Description: desc,
415 Args: map[string]string{
416 "error": "missing dependencies: " + strings.Join(r.missingDeps, ", "),
417 },
418 })
419 return
420 }
421
Colin Cross1d2cf042019-03-29 15:33:06 -0700422 var depFile WritablePath
423 var depFormat blueprint.Deps
424 if depFiles := r.DepFiles(); len(depFiles) > 0 {
425 depFile = depFiles[0]
426 depFormat = blueprint.DepsGCC
427 if len(depFiles) > 1 {
428 // Add a command locally that merges all depfiles together into the first depfile.
Dan Willemsen633c5022019-04-12 11:11:38 -0700429 r.depFileMergerCmd(ctx, depFiles)
430
431 if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800432 // Check for Rel() errors, as all depfiles should be in the output dir. Errors
433 // will be reported to the ctx.
Dan Willemsen633c5022019-04-12 11:11:38 -0700434 for _, path := range depFiles[1:] {
435 Rel(ctx, r.sboxOutDir.String(), path.String())
436 }
437 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700438 }
439 }
440
Dan Willemsen633c5022019-04-12 11:11:38 -0700441 tools := r.Tools()
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700442 commands := r.NinjaEscapedCommands()
Dan Willemsen633c5022019-04-12 11:11:38 -0700443 outputs := r.Outputs()
Colin Cross3d680512020-11-13 16:23:53 -0800444 inputs := r.Inputs()
Dan Willemsen633c5022019-04-12 11:11:38 -0700445
446 if len(commands) == 0 {
447 return
448 }
449 if len(outputs) == 0 {
450 panic("No outputs specified from any Commands")
451 }
452
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700453 commandString := strings.Join(commands, " && ")
Dan Willemsen633c5022019-04-12 11:11:38 -0700454
455 if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800456 // If running the command inside sbox, write the rule data out to an sbox
457 // manifest.textproto.
458 manifest := sbox_proto.Manifest{}
459 command := sbox_proto.Command{}
460 manifest.Commands = append(manifest.Commands, &command)
461 command.Command = proto.String(commandString)
Colin Cross151b9ff2020-11-12 08:29:30 -0800462
Colin Cross619b9ab2020-11-20 18:44:31 +0000463 if depFile != nil {
Colin Crosse16ce362020-11-12 08:29:30 -0800464 manifest.OutputDepfile = proto.String(depFile.String())
Colin Cross619b9ab2020-11-20 18:44:31 +0000465 }
466
Colin Crosse16ce362020-11-12 08:29:30 -0800467 // Add copy rules to the manifest to copy each output file from the sbox directory.
468 // to the output directory.
469 sboxOutputs := make([]string, len(outputs))
470 for i, output := range outputs {
471 rel := Rel(ctx, r.sboxOutDir.String(), output.String())
472 sboxOutputs[i] = filepath.Join(sboxOutDir, rel)
473 command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{
474 From: proto.String(filepath.Join(sboxOutSubDir, rel)),
475 To: proto.String(output.String()),
476 })
477 }
Colin Cross619b9ab2020-11-20 18:44:31 +0000478
Colin Crosse16ce362020-11-12 08:29:30 -0800479 // Add a hash of the list of input files to the manifest so that the textproto file
480 // changes when the list of input files changes and causes the sbox rule that
481 // depends on it to rerun.
482 command.InputHash = proto.String(hashSrcFiles(inputs))
Colin Cross619b9ab2020-11-20 18:44:31 +0000483
Colin Crosse16ce362020-11-12 08:29:30 -0800484 // Verify that the manifest textproto is not inside the sbox output directory, otherwise
485 // it will get deleted when the sbox rule clears its output directory.
486 _, manifestInOutDir := MaybeRel(ctx, r.sboxOutDir.String(), r.sboxManifestPath.String())
487 if manifestInOutDir {
488 ReportPathErrorf(ctx, "sbox rule %q manifestPath %q must not be in outputDir %q",
489 name, r.sboxManifestPath.String(), r.sboxOutDir.String())
490 }
491
492 // Create a rule to write the manifest as a the textproto.
493 WriteFileRule(ctx, r.sboxManifestPath, proto.MarshalTextString(&manifest))
494
495 // Generate a new string to use as the command line of the sbox rule. This uses
496 // a RuleBuilderCommand as a convenience method of building the command line, then
497 // converts it to a string to replace commandString.
498 sboxCmd := &RuleBuilderCommand{}
499 sboxCmd.Text("rm -rf").Output(r.sboxOutDir)
500 sboxCmd.Text("&&")
501 sboxCmd.BuiltTool(ctx, "sbox").
502 Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(ctx).String())).
503 Flag("--manifest").Input(r.sboxManifestPath)
504
505 // Replace the command string, and add the sbox tool and manifest textproto to the
506 // dependencies of the final sbox rule.
Colin Crosscfec40c2019-07-08 17:07:18 -0700507 commandString = sboxCmd.buf.String()
Dan Willemsen633c5022019-04-12 11:11:38 -0700508 tools = append(tools, sboxCmd.tools...)
Colin Crosse16ce362020-11-12 08:29:30 -0800509 inputs = append(inputs, sboxCmd.inputs...)
Colin Cross3d680512020-11-13 16:23:53 -0800510 } else {
511 // If not using sbox the rule will run the command directly, put the hash of the
512 // list of input files in a comment at the end of the command line to ensure ninja
513 // reruns the rule when the list of input files changes.
514 commandString += " # hash of input list: " + hashSrcFiles(inputs)
Dan Willemsen633c5022019-04-12 11:11:38 -0700515 }
516
Colin Cross1d2cf042019-03-29 15:33:06 -0700517 // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700518 // ImplicitOutputs. RuleBuilder only uses "$out" for the rsp file location, so the distinction between Outputs and
519 // ImplicitOutputs doesn't matter.
Dan Willemsen633c5022019-04-12 11:11:38 -0700520 output := outputs[0]
521 implicitOutputs := outputs[1:]
Colin Cross1d2cf042019-03-29 15:33:06 -0700522
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700523 var rspFile, rspFileContent string
524 rspFileInputs := r.RspFileInputs()
525 if rspFileInputs != nil {
526 rspFile = "$out.rsp"
527 rspFileContent = "$in"
528 }
529
Colin Cross8b8bec32019-11-15 13:18:43 -0800530 var pool blueprint.Pool
Ramy Medhat8ea054a2020-01-27 14:19:44 -0500531 if ctx.Config().UseGoma() && r.remoteable.Goma {
Colin Cross8b8bec32019-11-15 13:18:43 -0800532 // 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 -0500533 } else if ctx.Config().UseRBE() && r.remoteable.RBE {
Ramy Medhat944839a2020-03-31 22:14:52 -0400534 // When USE_RBE=true is set and the rule is supported by RBE, use the remotePool.
535 pool = remotePool
Colin Cross8b8bec32019-11-15 13:18:43 -0800536 } else if r.highmem {
537 pool = highmemPool
538 } else if ctx.Config().UseRemoteBuild() {
539 pool = localPool
540 }
541
Dan Willemsen633c5022019-04-12 11:11:38 -0700542 ctx.Build(pctx, BuildParams{
543 Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700544 Command: commandString,
545 CommandDeps: tools.Strings(),
546 Restat: r.restat,
547 Rspfile: rspFile,
548 RspfileContent: rspFileContent,
Colin Cross8b8bec32019-11-15 13:18:43 -0800549 Pool: pool,
Dan Willemsen633c5022019-04-12 11:11:38 -0700550 }),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700551 Inputs: rspFileInputs,
Colin Cross3d680512020-11-13 16:23:53 -0800552 Implicits: inputs,
Dan Willemsen633c5022019-04-12 11:11:38 -0700553 Output: output,
554 ImplicitOutputs: implicitOutputs,
Jingwen Chence679d22020-09-23 04:30:02 +0000555 SymlinkOutputs: r.SymlinkOutputs(),
Dan Willemsen633c5022019-04-12 11:11:38 -0700556 Depfile: depFile,
557 Deps: depFormat,
558 Description: desc,
559 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800560}
561
Colin Cross758290d2019-02-01 16:42:32 -0800562// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
563// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the
564// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
565// space as a separator from the previous method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800566type RuleBuilderCommand struct {
Jingwen Chence679d22020-09-23 04:30:02 +0000567 buf strings.Builder
568 inputs Paths
569 implicits Paths
570 orderOnlys Paths
571 outputs WritablePaths
572 symlinkOutputs WritablePaths
573 depFiles WritablePaths
574 tools Paths
575 rspFileInputs Paths
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700576
577 // spans [start,end) of the command that should not be ninja escaped
578 unescapedSpans [][2]int
Dan Willemsen633c5022019-04-12 11:11:38 -0700579
Colin Cross3d680512020-11-13 16:23:53 -0800580 sbox bool
581 // outDir is the directory that will contain the output files of the rules. sbox will copy
582 // the output files from the sandbox directory to this directory when it finishes.
583 outDir WritablePath
Dan Willemsen633c5022019-04-12 11:11:38 -0700584}
585
586func (c *RuleBuilderCommand) addInput(path Path) string {
587 if c.sbox {
Colin Cross3d680512020-11-13 16:23:53 -0800588 if rel, isRel, _ := maybeRelErr(c.outDir.String(), path.String()); isRel {
589 return filepath.Join(sboxOutDir, rel)
Dan Willemsen633c5022019-04-12 11:11:38 -0700590 }
591 }
592 c.inputs = append(c.inputs, path)
593 return path.String()
594}
595
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400596func (c *RuleBuilderCommand) addImplicit(path Path) string {
597 if c.sbox {
Colin Cross3d680512020-11-13 16:23:53 -0800598 if rel, isRel, _ := maybeRelErr(c.outDir.String(), path.String()); isRel {
599 return filepath.Join(sboxOutDir, rel)
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400600 }
601 }
602 c.implicits = append(c.implicits, path)
603 return path.String()
604}
605
Colin Crossda71eda2020-02-21 16:55:19 -0800606func (c *RuleBuilderCommand) addOrderOnly(path Path) {
607 c.orderOnlys = append(c.orderOnlys, path)
608}
609
Colin Cross3d680512020-11-13 16:23:53 -0800610func (c *RuleBuilderCommand) outputStr(path WritablePath) string {
Dan Willemsen633c5022019-04-12 11:11:38 -0700611 if c.sbox {
Colin Cross3d680512020-11-13 16:23:53 -0800612 return SboxPathForOutput(path, c.outDir)
Dan Willemsen633c5022019-04-12 11:11:38 -0700613 }
614 return path.String()
Colin Crossfeec25b2019-01-30 17:32:39 -0800615}
616
Colin Cross3d680512020-11-13 16:23:53 -0800617// SboxPathForOutput takes an output path and the out directory passed to RuleBuilder.Sbox(),
618// and returns the corresponding path for the output in the sbox sandbox. This can be used
619// on the RuleBuilder command line to reference the output.
620func SboxPathForOutput(path WritablePath, outDir WritablePath) string {
621 // Errors will be handled in RuleBuilder.Build where we have a context to report them
622 rel, _, _ := maybeRelErr(outDir.String(), path.String())
623 return filepath.Join(sboxOutDir, rel)
624}
625
Colin Cross758290d2019-02-01 16:42:32 -0800626// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
627// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800628func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
Colin Crosscfec40c2019-07-08 17:07:18 -0700629 if c.buf.Len() > 0 {
630 c.buf.WriteByte(' ')
Colin Crossfeec25b2019-01-30 17:32:39 -0800631 }
Colin Crosscfec40c2019-07-08 17:07:18 -0700632 c.buf.WriteString(text)
Colin Crossfeec25b2019-01-30 17:32:39 -0800633 return c
634}
635
Colin Cross758290d2019-02-01 16:42:32 -0800636// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or
637// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800638func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
639 return c.Text(fmt.Sprintf(format, a...))
640}
641
Colin Cross758290d2019-02-01 16:42:32 -0800642// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the
643// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800644func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
645 return c.Text(flag)
646}
647
Colin Crossab054432019-07-15 16:13:59 -0700648// OptionalFlag adds the specified raw text to the command line if it is not nil. The text should not contain input or
649// output paths or the rule will not have them listed in its dependencies or outputs.
650func (c *RuleBuilderCommand) OptionalFlag(flag *string) *RuleBuilderCommand {
651 if flag != nil {
652 c.Text(*flag)
653 }
654
655 return c
656}
657
Colin Cross92b7d582019-03-29 15:32:51 -0700658// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the
659// rule will not have them listed in its dependencies or outputs.
660func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
661 for _, flag := range flags {
662 c.Text(flag)
663 }
664 return c
665}
666
Colin Cross758290d2019-02-01 16:42:32 -0800667// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
668// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
669// outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800670func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
671 return c.Text(flag + arg)
672}
673
Colin Crossc7ed0042019-02-11 14:11:09 -0800674// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to
675// calling FlagWithArg for argument.
676func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand {
677 for _, arg := range args {
678 c.FlagWithArg(flag, arg)
679 }
680 return c
681}
682
Roland Levillain2da5d9a2019-02-27 16:56:41 +0000683// 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 -0800684// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or
685// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800686func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
687 return c.Text(flag + strings.Join(list, sep))
688}
689
Colin Cross758290d2019-02-01 16:42:32 -0800690// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by
691// RuleBuilder.Tools.
Colin Cross69f59a32019-02-15 10:39:37 -0800692func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800693 c.tools = append(c.tools, path)
Colin Cross69f59a32019-02-15 10:39:37 -0800694 return c.Text(path.String())
Colin Crossfeec25b2019-01-30 17:32:39 -0800695}
696
Colin Crossee94d6a2019-07-08 17:08:34 -0700697// BuiltTool adds the specified tool path that was built using a host Soong module to the command line. The path will
698// be also added to the dependencies returned by RuleBuilder.Tools.
699//
700// It is equivalent to:
701// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
702func (c *RuleBuilderCommand) BuiltTool(ctx PathContext, tool string) *RuleBuilderCommand {
703 return c.Tool(ctx.Config().HostToolPath(ctx, tool))
704}
705
706// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the
707// dependencies returned by RuleBuilder.Tools.
708//
709// It is equivalent to:
710// cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
711func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
712 return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
713}
714
Colin Cross758290d2019-02-01 16:42:32 -0800715// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
716// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800717func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700718 return c.Text(c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800719}
720
Colin Cross758290d2019-02-01 16:42:32 -0800721// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
722// dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800723func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800724 for _, path := range paths {
725 c.Input(path)
726 }
727 return c
728}
729
730// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
731// command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800732func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400733 c.addImplicit(path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800734 return c
735}
736
Colin Cross758290d2019-02-01 16:42:32 -0800737// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
738// command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800739func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700740 for _, path := range paths {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400741 c.addImplicit(path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700742 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800743 return c
744}
745
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400746// GetImplicits returns the command's implicit inputs.
747func (c *RuleBuilderCommand) GetImplicits() Paths {
748 return c.implicits
749}
750
Colin Crossda71eda2020-02-21 16:55:19 -0800751// OrderOnly adds the specified input path to the dependencies returned by RuleBuilder.OrderOnlys
752// without modifying the command line.
753func (c *RuleBuilderCommand) OrderOnly(path Path) *RuleBuilderCommand {
754 c.addOrderOnly(path)
755 return c
756}
757
758// OrderOnlys adds the specified input paths to the dependencies returned by RuleBuilder.OrderOnlys
759// without modifying the command line.
760func (c *RuleBuilderCommand) OrderOnlys(paths Paths) *RuleBuilderCommand {
761 for _, path := range paths {
762 c.addOrderOnly(path)
763 }
764 return c
765}
766
Colin Cross758290d2019-02-01 16:42:32 -0800767// Output adds the specified output path to the command line. The path will also be added to the outputs returned by
768// RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800769func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800770 c.outputs = append(c.outputs, path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700771 return c.Text(c.outputStr(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800772}
773
Colin Cross758290d2019-02-01 16:42:32 -0800774// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
775// the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800776func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800777 for _, path := range paths {
778 c.Output(path)
779 }
780 return c
781}
782
Dan Willemsen1945a4b2019-06-04 17:10:41 -0700783// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
784// and will be the temporary output directory managed by sbox, not the final one.
785func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand {
786 if !c.sbox {
787 panic("OutputDir only valid with Sbox")
788 }
Colin Cross3d680512020-11-13 16:23:53 -0800789 return c.Text(sboxOutDir)
Dan Willemsen1945a4b2019-06-04 17:10:41 -0700790}
791
Colin Cross1d2cf042019-03-29 15:33:06 -0700792// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
793// line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles are added to
794// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
795func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
796 c.depFiles = append(c.depFiles, path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700797 return c.Text(c.outputStr(path))
Colin Cross1d2cf042019-03-29 15:33:06 -0700798}
799
Colin Cross758290d2019-02-01 16:42:32 -0800800// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
801// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800802func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800803 c.outputs = append(c.outputs, path)
804 return c
805}
806
Colin Cross758290d2019-02-01 16:42:32 -0800807// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
808// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800809func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800810 c.outputs = append(c.outputs, paths...)
811 return c
812}
813
Jingwen Chence679d22020-09-23 04:30:02 +0000814// ImplicitSymlinkOutput declares the specified path as an implicit output that
815// will be a symlink instead of a regular file. Does not modify the command
816// line.
817func (c *RuleBuilderCommand) ImplicitSymlinkOutput(path WritablePath) *RuleBuilderCommand {
818 c.symlinkOutputs = append(c.symlinkOutputs, path)
819 return c.ImplicitOutput(path)
820}
821
822// ImplicitSymlinkOutputs declares the specified paths as implicit outputs that
823// will be a symlinks instead of regular files. Does not modify the command
824// line.
825func (c *RuleBuilderCommand) ImplicitSymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
826 for _, path := range paths {
827 c.ImplicitSymlinkOutput(path)
828 }
829 return c
830}
831
832// SymlinkOutput declares the specified path as an output that will be a symlink
833// instead of a regular file. Modifies the command line.
834func (c *RuleBuilderCommand) SymlinkOutput(path WritablePath) *RuleBuilderCommand {
835 c.symlinkOutputs = append(c.symlinkOutputs, path)
836 return c.Output(path)
837}
838
839// SymlinkOutputsl declares the specified paths as outputs that will be symlinks
840// instead of regular files. Modifies the command line.
841func (c *RuleBuilderCommand) SymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
842 for _, path := range paths {
843 c.SymlinkOutput(path)
844 }
845 return c
846}
847
Colin Cross1d2cf042019-03-29 15:33:06 -0700848// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
849// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles
850// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
851// depfiles together.
852func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand {
853 c.depFiles = append(c.depFiles, path)
854 return c
855}
856
Colin Cross758290d2019-02-01 16:42:32 -0800857// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
858// will also be added to the dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800859func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700860 return c.Text(flag + c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800861}
862
Colin Cross758290d2019-02-01 16:42:32 -0800863// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
864// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
865// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800866func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700867 strs := make([]string, len(paths))
868 for i, path := range paths {
869 strs[i] = c.addInput(path)
870 }
871 return c.FlagWithList(flag, strs, sep)
Colin Crossfeec25b2019-01-30 17:32:39 -0800872}
873
Colin Cross758290d2019-02-01 16:42:32 -0800874// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
875// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for
876// each input path.
Colin Cross69f59a32019-02-15 10:39:37 -0800877func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800878 for _, path := range paths {
879 c.FlagWithInput(flag, path)
880 }
881 return c
882}
883
884// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path
885// will also be added to the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800886func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800887 c.outputs = append(c.outputs, path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700888 return c.Text(flag + c.outputStr(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800889}
890
Colin Cross1d2cf042019-03-29 15:33:06 -0700891// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path
892// will also be added to the outputs returned by RuleBuilder.Outputs.
893func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
894 c.depFiles = append(c.depFiles, path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700895 return c.Text(flag + c.outputStr(path))
Colin Cross1d2cf042019-03-29 15:33:06 -0700896}
897
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700898// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with no separator
899// between them. The paths will be written to the rspfile.
900func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, paths Paths) *RuleBuilderCommand {
901 if c.rspFileInputs != nil {
902 panic("FlagWithRspFileInputList cannot be called if rsp file inputs have already been provided")
903 }
904
905 // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
906 // generated.
907 if paths == nil {
908 paths = Paths{}
909 }
910
911 c.rspFileInputs = paths
912
913 rspFile := "$out.rsp"
914 c.FlagWithArg(flag, rspFile)
915 c.unescapedSpans = append(c.unescapedSpans, [2]int{c.buf.Len() - len(rspFile), c.buf.Len()})
916 return c
917}
918
Colin Cross758290d2019-02-01 16:42:32 -0800919// String returns the command line.
920func (c *RuleBuilderCommand) String() string {
Colin Crosscfec40c2019-07-08 17:07:18 -0700921 return c.buf.String()
Colin Cross758290d2019-02-01 16:42:32 -0800922}
Colin Cross1d2cf042019-03-29 15:33:06 -0700923
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700924// String returns the command line.
925func (c *RuleBuilderCommand) NinjaEscapedString() string {
926 return ninjaEscapeExceptForSpans(c.String(), c.unescapedSpans)
927}
928
Colin Crosse16ce362020-11-12 08:29:30 -0800929// RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox()
930// and returns sbox testproto generated by the RuleBuilder.
931func RuleBuilderSboxProtoForTests(t *testing.T, params TestingBuildParams) *sbox_proto.Manifest {
932 t.Helper()
933 content := ContentFromFileRuleForTests(t, params)
934 manifest := sbox_proto.Manifest{}
935 err := proto.UnmarshalText(content, &manifest)
936 if err != nil {
937 t.Fatalf("failed to unmarshal manifest: %s", err.Error())
938 }
939 return &manifest
940}
941
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700942func ninjaEscapeExceptForSpans(s string, spans [][2]int) string {
943 if len(spans) == 0 {
944 return proptools.NinjaEscape(s)
945 }
946
947 sb := strings.Builder{}
948 sb.Grow(len(s) * 11 / 10)
949
950 i := 0
951 for _, span := range spans {
952 sb.WriteString(proptools.NinjaEscape(s[i:span[0]]))
953 sb.WriteString(s[span[0]:span[1]])
954 i = span[1]
955 }
956 sb.WriteString(proptools.NinjaEscape(s[i:]))
957
958 return sb.String()
959}
960
Colin Cross1d2cf042019-03-29 15:33:06 -0700961func ninjaNameEscape(s string) string {
962 b := []byte(s)
963 escaped := false
964 for i, c := range b {
965 valid := (c >= 'a' && c <= 'z') ||
966 (c >= 'A' && c <= 'Z') ||
967 (c >= '0' && c <= '9') ||
968 (c == '_') ||
969 (c == '-') ||
970 (c == '.')
971 if !valid {
972 b[i] = '_'
973 escaped = true
974 }
975 }
976 if escaped {
977 s = string(b)
978 }
979 return s
980}
Colin Cross3d680512020-11-13 16:23:53 -0800981
982// hashSrcFiles returns a hash of the list of source files. It is used to ensure the command line
983// or the sbox textproto manifest change even if the input files are not listed on the command line.
984func hashSrcFiles(srcFiles Paths) string {
985 h := sha256.New()
986 srcFileList := strings.Join(srcFiles.Strings(), "\n")
987 h.Write([]byte(srcFileList))
988 return fmt.Sprintf("%x", h.Sum(nil))
989}