blob: 6f04672d588f3a18c973f4685b8067e986ce249b [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 (
18 "fmt"
Colin Crossfeec25b2019-01-30 17:32:39 -080019 "sort"
20 "strings"
21
22 "github.com/google/blueprint"
23 "github.com/google/blueprint/proptools"
Dan Willemsen633c5022019-04-12 11:11:38 -070024
25 "android/soong/shared"
Colin Crossfeec25b2019-01-30 17:32:39 -080026)
27
Colin Cross758290d2019-02-01 16:42:32 -080028// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
29// graph.
Colin Crossfeec25b2019-01-30 17:32:39 -080030type RuleBuilder struct {
Colin Cross5cb5b092019-02-02 21:25:18 -080031 commands []*RuleBuilderCommand
Colin Crossdeabb942019-02-11 14:11:09 -080032 installs RuleBuilderInstalls
Colin Cross69f59a32019-02-15 10:39:37 -080033 temporariesSet map[WritablePath]bool
Colin Cross5cb5b092019-02-02 21:25:18 -080034 restat bool
Dan Willemsen633c5022019-04-12 11:11:38 -070035 sbox bool
36 sboxOutDir WritablePath
Colin Cross0d2f40a2019-02-05 22:31:15 -080037 missingDeps []string
Colin Crossfeec25b2019-01-30 17:32:39 -080038}
39
Colin Cross758290d2019-02-01 16:42:32 -080040// NewRuleBuilder returns a newly created RuleBuilder.
41func NewRuleBuilder() *RuleBuilder {
Colin Cross5cb5b092019-02-02 21:25:18 -080042 return &RuleBuilder{
Colin Cross69f59a32019-02-15 10:39:37 -080043 temporariesSet: make(map[WritablePath]bool),
Colin Cross5cb5b092019-02-02 21:25:18 -080044 }
Colin Cross758290d2019-02-01 16:42:32 -080045}
46
47// RuleBuilderInstall is a tuple of install from and to locations.
48type RuleBuilderInstall struct {
Colin Cross69f59a32019-02-15 10:39:37 -080049 From Path
50 To string
Colin Cross758290d2019-02-01 16:42:32 -080051}
52
Colin Crossdeabb942019-02-11 14:11:09 -080053type RuleBuilderInstalls []RuleBuilderInstall
54
55// String returns the RuleBuilderInstalls in the form used by $(call copy-many-files) in Make, a space separated
56// list of from:to tuples.
57func (installs RuleBuilderInstalls) String() string {
58 sb := strings.Builder{}
59 for i, install := range installs {
60 if i != 0 {
61 sb.WriteRune(' ')
62 }
Colin Cross69f59a32019-02-15 10:39:37 -080063 sb.WriteString(install.From.String())
Colin Crossdeabb942019-02-11 14:11:09 -080064 sb.WriteRune(':')
65 sb.WriteString(install.To)
66 }
67 return sb.String()
68}
69
Colin Cross0d2f40a2019-02-05 22:31:15 -080070// MissingDeps adds modules to the list of missing dependencies. If MissingDeps
71// is called with a non-empty input, any call to Build will result in a rule
72// that will print an error listing the missing dependencies and fail.
73// MissingDeps should only be called if Config.AllowMissingDependencies() is
74// true.
75func (r *RuleBuilder) MissingDeps(missingDeps []string) {
76 r.missingDeps = append(r.missingDeps, missingDeps...)
77}
78
Colin Cross758290d2019-02-01 16:42:32 -080079// 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 -070080//
81// Restat is not compatible with Sbox()
Colin Crossfeec25b2019-01-30 17:32:39 -080082func (r *RuleBuilder) Restat() *RuleBuilder {
Dan Willemsen633c5022019-04-12 11:11:38 -070083 if r.sbox {
84 panic("Restat() is not compatible with Sbox()")
85 }
Colin Crossfeec25b2019-01-30 17:32:39 -080086 r.restat = true
87 return r
88}
89
Dan Willemsen633c5022019-04-12 11:11:38 -070090// Sbox marks the rule as needing to be wrapped by sbox. The WritablePath should point to the output
91// directory that sbox will wipe. It should not be written to by any other rule. sbox will ensure
92// that all outputs have been written, and will discard any output files that were not specified.
93//
94// Sbox is not compatible with Restat()
95func (r *RuleBuilder) Sbox(outputDir WritablePath) *RuleBuilder {
96 if r.sbox {
97 panic("Sbox() may not be called more than once")
98 }
99 if len(r.commands) > 0 {
100 panic("Sbox() may not be called after Command()")
101 }
102 if r.restat {
103 panic("Sbox() is not compatible with Restat()")
104 }
105 r.sbox = true
106 r.sboxOutDir = outputDir
107 return r
108}
109
Colin Cross758290d2019-02-01 16:42:32 -0800110// Install associates an output of the rule with an install location, which can be retrieved later using
111// RuleBuilder.Installs.
Colin Cross69f59a32019-02-15 10:39:37 -0800112func (r *RuleBuilder) Install(from Path, to string) {
Colin Crossfeec25b2019-01-30 17:32:39 -0800113 r.installs = append(r.installs, RuleBuilderInstall{from, to})
114}
115
Colin Cross758290d2019-02-01 16:42:32 -0800116// Command returns a new RuleBuilderCommand for the rule. The commands will be ordered in the rule by when they were
117// created by this method. That can be mutated through their methods in any order, as long as the mutations do not
118// race with any call to Build.
Colin Crossfeec25b2019-01-30 17:32:39 -0800119func (r *RuleBuilder) Command() *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700120 command := &RuleBuilderCommand{
121 sbox: r.sbox,
122 sboxOutDir: r.sboxOutDir,
123 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800124 r.commands = append(r.commands, command)
125 return command
126}
127
Colin Cross5cb5b092019-02-02 21:25:18 -0800128// Temporary marks an output of a command as an intermediate file that will be used as an input to another command
129// in the same rule, and should not be listed in Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800130func (r *RuleBuilder) Temporary(path WritablePath) {
Colin Cross5cb5b092019-02-02 21:25:18 -0800131 r.temporariesSet[path] = true
132}
133
134// DeleteTemporaryFiles adds a command to the rule that deletes any outputs that have been marked using Temporary
135// when the rule runs. DeleteTemporaryFiles should be called after all calls to Temporary.
136func (r *RuleBuilder) DeleteTemporaryFiles() {
Colin Cross69f59a32019-02-15 10:39:37 -0800137 var temporariesList WritablePaths
Colin Cross5cb5b092019-02-02 21:25:18 -0800138
139 for intermediate := range r.temporariesSet {
140 temporariesList = append(temporariesList, intermediate)
141 }
Colin Cross69f59a32019-02-15 10:39:37 -0800142
143 sort.Slice(temporariesList, func(i, j int) bool {
144 return temporariesList[i].String() < temporariesList[j].String()
145 })
Colin Cross5cb5b092019-02-02 21:25:18 -0800146
147 r.Command().Text("rm").Flag("-f").Outputs(temporariesList)
148}
149
Colin Cross758290d2019-02-01 16:42:32 -0800150// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take input paths, such
151// as RuleBuilderCommand.Input, RuleBuilderComand.Implicit, or RuleBuilderCommand.FlagWithInput. Inputs to a command
152// that are also outputs of another command in the same RuleBuilder are filtered out.
Colin Cross69f59a32019-02-15 10:39:37 -0800153func (r *RuleBuilder) Inputs() Paths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800154 outputs := r.outputSet()
Dan Willemsen633c5022019-04-12 11:11:38 -0700155 depFiles := r.depFileSet()
Colin Crossfeec25b2019-01-30 17:32:39 -0800156
Colin Cross69f59a32019-02-15 10:39:37 -0800157 inputs := make(map[string]Path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800158 for _, c := range r.commands {
159 for _, input := range c.inputs {
Dan Willemsen633c5022019-04-12 11:11:38 -0700160 inputStr := input.String()
161 if _, isOutput := outputs[inputStr]; !isOutput {
162 if _, isDepFile := depFiles[inputStr]; !isDepFile {
163 inputs[input.String()] = input
164 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800165 }
166 }
167 }
168
Colin Cross69f59a32019-02-15 10:39:37 -0800169 var inputList Paths
170 for _, input := range inputs {
Colin Crossfeec25b2019-01-30 17:32:39 -0800171 inputList = append(inputList, input)
172 }
Colin Cross69f59a32019-02-15 10:39:37 -0800173
174 sort.Slice(inputList, func(i, j int) bool {
175 return inputList[i].String() < inputList[j].String()
176 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800177
178 return inputList
179}
180
Colin Cross69f59a32019-02-15 10:39:37 -0800181func (r *RuleBuilder) outputSet() map[string]WritablePath {
182 outputs := make(map[string]WritablePath)
Colin Crossfeec25b2019-01-30 17:32:39 -0800183 for _, c := range r.commands {
184 for _, output := range c.outputs {
Colin Cross69f59a32019-02-15 10:39:37 -0800185 outputs[output.String()] = output
Colin Crossfeec25b2019-01-30 17:32:39 -0800186 }
187 }
188 return outputs
189}
190
Colin Cross758290d2019-02-01 16:42:32 -0800191// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take output paths, such
192// as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or RuleBuilderCommand.FlagWithInput.
Colin Cross69f59a32019-02-15 10:39:37 -0800193func (r *RuleBuilder) Outputs() WritablePaths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800194 outputs := r.outputSet()
195
Colin Cross69f59a32019-02-15 10:39:37 -0800196 var outputList WritablePaths
197 for _, output := range outputs {
Colin Cross5cb5b092019-02-02 21:25:18 -0800198 if !r.temporariesSet[output] {
199 outputList = append(outputList, output)
200 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800201 }
Colin Cross69f59a32019-02-15 10:39:37 -0800202
203 sort.Slice(outputList, func(i, j int) bool {
204 return outputList[i].String() < outputList[j].String()
205 })
206
Colin Crossfeec25b2019-01-30 17:32:39 -0800207 return outputList
208}
209
Dan Willemsen633c5022019-04-12 11:11:38 -0700210func (r *RuleBuilder) depFileSet() map[string]WritablePath {
211 depFiles := make(map[string]WritablePath)
212 for _, c := range r.commands {
213 for _, depFile := range c.depFiles {
214 depFiles[depFile.String()] = depFile
215 }
216 }
217 return depFiles
218}
219
Colin Cross1d2cf042019-03-29 15:33:06 -0700220// DepFiles returns the list of paths that were passed to the RuleBuilderCommand methods that take depfile paths, such
221// as RuleBuilderCommand.DepFile or RuleBuilderCommand.FlagWithDepFile.
222func (r *RuleBuilder) DepFiles() WritablePaths {
223 var depFiles WritablePaths
224
225 for _, c := range r.commands {
226 for _, depFile := range c.depFiles {
227 depFiles = append(depFiles, depFile)
228 }
229 }
230
231 return depFiles
232}
233
Colin Cross758290d2019-02-01 16:42:32 -0800234// Installs returns the list of tuples passed to Install.
Colin Crossdeabb942019-02-11 14:11:09 -0800235func (r *RuleBuilder) Installs() RuleBuilderInstalls {
236 return append(RuleBuilderInstalls(nil), r.installs...)
Colin Crossfeec25b2019-01-30 17:32:39 -0800237}
238
Colin Cross69f59a32019-02-15 10:39:37 -0800239func (r *RuleBuilder) toolsSet() map[string]Path {
240 tools := make(map[string]Path)
Colin Cross5cb5b092019-02-02 21:25:18 -0800241 for _, c := range r.commands {
242 for _, tool := range c.tools {
Colin Cross69f59a32019-02-15 10:39:37 -0800243 tools[tool.String()] = tool
Colin Cross5cb5b092019-02-02 21:25:18 -0800244 }
245 }
246
247 return tools
248}
249
Colin Cross758290d2019-02-01 16:42:32 -0800250// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method.
Colin Cross69f59a32019-02-15 10:39:37 -0800251func (r *RuleBuilder) Tools() Paths {
Colin Cross5cb5b092019-02-02 21:25:18 -0800252 toolsSet := r.toolsSet()
253
Colin Cross69f59a32019-02-15 10:39:37 -0800254 var toolsList Paths
255 for _, tool := range toolsSet {
Colin Cross5cb5b092019-02-02 21:25:18 -0800256 toolsList = append(toolsList, tool)
Colin Crossfeec25b2019-01-30 17:32:39 -0800257 }
Colin Cross69f59a32019-02-15 10:39:37 -0800258
259 sort.Slice(toolsList, func(i, j int) bool {
260 return toolsList[i].String() < toolsList[j].String()
261 })
262
Colin Cross5cb5b092019-02-02 21:25:18 -0800263 return toolsList
Colin Crossfeec25b2019-01-30 17:32:39 -0800264}
265
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700266// RspFileInputs returns the list of paths that were passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
267func (r *RuleBuilder) RspFileInputs() Paths {
268 var rspFileInputs Paths
269 for _, c := range r.commands {
270 if c.rspFileInputs != nil {
271 if rspFileInputs != nil {
272 panic("Multiple commands in a rule may not have rsp file inputs")
273 }
274 rspFileInputs = c.rspFileInputs
275 }
276 }
277
278 return rspFileInputs
279}
280
281// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
Colin Crossfeec25b2019-01-30 17:32:39 -0800282func (r *RuleBuilder) Commands() []string {
283 var commands []string
284 for _, c := range r.commands {
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700285 commands = append(commands, c.String())
286 }
287 return commands
288}
289
290// NinjaEscapedCommands returns a slice containin the built command line after ninja escaping for each call to
291// RuleBuilder.Command.
292func (r *RuleBuilder) NinjaEscapedCommands() []string {
293 var commands []string
294 for _, c := range r.commands {
295 commands = append(commands, c.NinjaEscapedString())
Colin Crossfeec25b2019-01-30 17:32:39 -0800296 }
297 return commands
298}
299
Colin Cross758290d2019-02-01 16:42:32 -0800300// BuilderContext is a subset of ModuleContext and SingletonContext.
Colin Cross786cd6d2019-02-01 16:41:11 -0800301type BuilderContext interface {
302 PathContext
303 Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
304 Build(PackageContext, BuildParams)
305}
306
Colin Cross758290d2019-02-01 16:42:32 -0800307var _ BuilderContext = ModuleContext(nil)
308var _ BuilderContext = SingletonContext(nil)
309
Colin Cross1d2cf042019-03-29 15:33:06 -0700310func (r *RuleBuilder) depFileMergerCmd(ctx PathContext, depFiles WritablePaths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700311 return r.Command().
Colin Crossee94d6a2019-07-08 17:08:34 -0700312 BuiltTool(ctx, "dep_fixer").
Dan Willemsen633c5022019-04-12 11:11:38 -0700313 Inputs(depFiles.Paths())
Colin Cross1d2cf042019-03-29 15:33:06 -0700314}
315
Colin Cross758290d2019-02-01 16:42:32 -0800316// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
317// Outputs.
Colin Cross786cd6d2019-02-01 16:41:11 -0800318func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string, desc string) {
Colin Cross1d2cf042019-03-29 15:33:06 -0700319 name = ninjaNameEscape(name)
320
Colin Cross0d2f40a2019-02-05 22:31:15 -0800321 if len(r.missingDeps) > 0 {
322 ctx.Build(pctx, BuildParams{
323 Rule: ErrorRule,
Colin Cross69f59a32019-02-15 10:39:37 -0800324 Outputs: r.Outputs(),
Colin Cross0d2f40a2019-02-05 22:31:15 -0800325 Description: desc,
326 Args: map[string]string{
327 "error": "missing dependencies: " + strings.Join(r.missingDeps, ", "),
328 },
329 })
330 return
331 }
332
Colin Cross1d2cf042019-03-29 15:33:06 -0700333 var depFile WritablePath
334 var depFormat blueprint.Deps
335 if depFiles := r.DepFiles(); len(depFiles) > 0 {
336 depFile = depFiles[0]
337 depFormat = blueprint.DepsGCC
338 if len(depFiles) > 1 {
339 // Add a command locally that merges all depfiles together into the first depfile.
Dan Willemsen633c5022019-04-12 11:11:38 -0700340 r.depFileMergerCmd(ctx, depFiles)
341
342 if r.sbox {
343 // Check for Rel() errors, as all depfiles should be in the output dir
344 for _, path := range depFiles[1:] {
345 Rel(ctx, r.sboxOutDir.String(), path.String())
346 }
347 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700348 }
349 }
350
Dan Willemsen633c5022019-04-12 11:11:38 -0700351 tools := r.Tools()
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700352 commands := r.NinjaEscapedCommands()
Dan Willemsen633c5022019-04-12 11:11:38 -0700353 outputs := r.Outputs()
354
355 if len(commands) == 0 {
356 return
357 }
358 if len(outputs) == 0 {
359 panic("No outputs specified from any Commands")
360 }
361
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700362 commandString := strings.Join(commands, " && ")
Dan Willemsen633c5022019-04-12 11:11:38 -0700363
364 if r.sbox {
365 sboxOutputs := make([]string, len(outputs))
366 for i, output := range outputs {
367 sboxOutputs[i] = "__SBOX_OUT_DIR__/" + Rel(ctx, r.sboxOutDir.String(), output.String())
368 }
369
Dan Willemsen633c5022019-04-12 11:11:38 -0700370 commandString = proptools.ShellEscape(commandString)
371 if !strings.HasPrefix(commandString, `'`) {
372 commandString = `'` + commandString + `'`
373 }
374
375 sboxCmd := &RuleBuilderCommand{}
Colin Crossee94d6a2019-07-08 17:08:34 -0700376 sboxCmd.BuiltTool(ctx, "sbox").
Dan Willemsen633c5022019-04-12 11:11:38 -0700377 Flag("-c").Text(commandString).
378 Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(ctx).String())).
Dan Willemsenc89b6f12019-08-29 14:47:40 -0700379 Flag("--output-root").Text(r.sboxOutDir.String())
380
381 if depFile != nil {
382 sboxCmd.Flag("--depfile-out").Text(depFile.String())
383 }
384
385 sboxCmd.Flags(sboxOutputs)
Dan Willemsen633c5022019-04-12 11:11:38 -0700386
Colin Crosscfec40c2019-07-08 17:07:18 -0700387 commandString = sboxCmd.buf.String()
Dan Willemsen633c5022019-04-12 11:11:38 -0700388 tools = append(tools, sboxCmd.tools...)
389 }
390
Colin Cross1d2cf042019-03-29 15:33:06 -0700391 // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700392 // ImplicitOutputs. RuleBuilder only uses "$out" for the rsp file location, so the distinction between Outputs and
393 // ImplicitOutputs doesn't matter.
Dan Willemsen633c5022019-04-12 11:11:38 -0700394 output := outputs[0]
395 implicitOutputs := outputs[1:]
Colin Cross1d2cf042019-03-29 15:33:06 -0700396
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700397 var rspFile, rspFileContent string
398 rspFileInputs := r.RspFileInputs()
399 if rspFileInputs != nil {
400 rspFile = "$out.rsp"
401 rspFileContent = "$in"
402 }
403
Dan Willemsen633c5022019-04-12 11:11:38 -0700404 ctx.Build(pctx, BuildParams{
405 Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700406 Command: commandString,
407 CommandDeps: tools.Strings(),
408 Restat: r.restat,
409 Rspfile: rspFile,
410 RspfileContent: rspFileContent,
Dan Willemsen633c5022019-04-12 11:11:38 -0700411 }),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700412 Inputs: rspFileInputs,
Dan Willemsen633c5022019-04-12 11:11:38 -0700413 Implicits: r.Inputs(),
414 Output: output,
415 ImplicitOutputs: implicitOutputs,
416 Depfile: depFile,
417 Deps: depFormat,
418 Description: desc,
419 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800420}
421
Colin Cross758290d2019-02-01 16:42:32 -0800422// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
423// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the
424// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
425// space as a separator from the previous method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800426type RuleBuilderCommand struct {
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700427 buf strings.Builder
428 inputs Paths
429 outputs WritablePaths
430 depFiles WritablePaths
431 tools Paths
432 rspFileInputs Paths
433
434 // spans [start,end) of the command that should not be ninja escaped
435 unescapedSpans [][2]int
Dan Willemsen633c5022019-04-12 11:11:38 -0700436
437 sbox bool
438 sboxOutDir WritablePath
439}
440
441func (c *RuleBuilderCommand) addInput(path Path) string {
442 if c.sbox {
443 if rel, isRel, _ := maybeRelErr(c.sboxOutDir.String(), path.String()); isRel {
444 return "__SBOX_OUT_DIR__/" + rel
445 }
446 }
447 c.inputs = append(c.inputs, path)
448 return path.String()
449}
450
451func (c *RuleBuilderCommand) outputStr(path Path) string {
452 if c.sbox {
453 // Errors will be handled in RuleBuilder.Build where we have a context to report them
454 rel, _, _ := maybeRelErr(c.sboxOutDir.String(), path.String())
455 return "__SBOX_OUT_DIR__/" + rel
456 }
457 return path.String()
Colin Crossfeec25b2019-01-30 17:32:39 -0800458}
459
Colin Cross758290d2019-02-01 16:42:32 -0800460// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
461// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800462func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
Colin Crosscfec40c2019-07-08 17:07:18 -0700463 if c.buf.Len() > 0 {
464 c.buf.WriteByte(' ')
Colin Crossfeec25b2019-01-30 17:32:39 -0800465 }
Colin Crosscfec40c2019-07-08 17:07:18 -0700466 c.buf.WriteString(text)
Colin Crossfeec25b2019-01-30 17:32:39 -0800467 return c
468}
469
Colin Cross758290d2019-02-01 16:42:32 -0800470// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or
471// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800472func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
473 return c.Text(fmt.Sprintf(format, a...))
474}
475
Colin Cross758290d2019-02-01 16:42:32 -0800476// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the
477// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800478func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
479 return c.Text(flag)
480}
481
Colin Crossab054432019-07-15 16:13:59 -0700482// OptionalFlag adds the specified raw text to the command line if it is not nil. The text should not contain input or
483// output paths or the rule will not have them listed in its dependencies or outputs.
484func (c *RuleBuilderCommand) OptionalFlag(flag *string) *RuleBuilderCommand {
485 if flag != nil {
486 c.Text(*flag)
487 }
488
489 return c
490}
491
Colin Cross92b7d582019-03-29 15:32:51 -0700492// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the
493// rule will not have them listed in its dependencies or outputs.
494func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
495 for _, flag := range flags {
496 c.Text(flag)
497 }
498 return c
499}
500
Colin Cross758290d2019-02-01 16:42:32 -0800501// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
502// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
503// outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800504func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
505 return c.Text(flag + arg)
506}
507
Colin Crossc7ed0042019-02-11 14:11:09 -0800508// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to
509// calling FlagWithArg for argument.
510func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand {
511 for _, arg := range args {
512 c.FlagWithArg(flag, arg)
513 }
514 return c
515}
516
Roland Levillain2da5d9a2019-02-27 16:56:41 +0000517// 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 -0800518// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or
519// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800520func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
521 return c.Text(flag + strings.Join(list, sep))
522}
523
Colin Cross758290d2019-02-01 16:42:32 -0800524// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by
525// RuleBuilder.Tools.
Colin Cross69f59a32019-02-15 10:39:37 -0800526func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800527 c.tools = append(c.tools, path)
Colin Cross69f59a32019-02-15 10:39:37 -0800528 return c.Text(path.String())
Colin Crossfeec25b2019-01-30 17:32:39 -0800529}
530
Colin Crossee94d6a2019-07-08 17:08:34 -0700531// BuiltTool adds the specified tool path that was built using a host Soong module to the command line. The path will
532// be also added to the dependencies returned by RuleBuilder.Tools.
533//
534// It is equivalent to:
535// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
536func (c *RuleBuilderCommand) BuiltTool(ctx PathContext, tool string) *RuleBuilderCommand {
537 return c.Tool(ctx.Config().HostToolPath(ctx, tool))
538}
539
540// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the
541// dependencies returned by RuleBuilder.Tools.
542//
543// It is equivalent to:
544// cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
545func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
546 return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
547}
548
Colin Cross758290d2019-02-01 16:42:32 -0800549// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
550// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800551func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700552 return c.Text(c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800553}
554
Colin Cross758290d2019-02-01 16:42:32 -0800555// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
556// dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800557func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800558 for _, path := range paths {
559 c.Input(path)
560 }
561 return c
562}
563
564// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
565// command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800566func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700567 c.addInput(path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800568 return c
569}
570
Colin Cross758290d2019-02-01 16:42:32 -0800571// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
572// command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800573func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700574 for _, path := range paths {
575 c.addInput(path)
576 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800577 return c
578}
579
Colin Cross758290d2019-02-01 16:42:32 -0800580// Output adds the specified output path to the command line. The path will also be added to the outputs returned by
581// RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800582func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800583 c.outputs = append(c.outputs, path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700584 return c.Text(c.outputStr(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800585}
586
Colin Cross758290d2019-02-01 16:42:32 -0800587// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
588// the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800589func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800590 for _, path := range paths {
591 c.Output(path)
592 }
593 return c
594}
595
Dan Willemsen1945a4b2019-06-04 17:10:41 -0700596// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
597// and will be the temporary output directory managed by sbox, not the final one.
598func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand {
599 if !c.sbox {
600 panic("OutputDir only valid with Sbox")
601 }
602 return c.Text("__SBOX_OUT_DIR__")
603}
604
Colin Cross1d2cf042019-03-29 15:33:06 -0700605// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
606// line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles are added to
607// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
608func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
609 c.depFiles = append(c.depFiles, path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700610 return c.Text(c.outputStr(path))
Colin Cross1d2cf042019-03-29 15:33:06 -0700611}
612
Colin Cross758290d2019-02-01 16:42:32 -0800613// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
614// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800615func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800616 c.outputs = append(c.outputs, path)
617 return c
618}
619
Colin Cross758290d2019-02-01 16:42:32 -0800620// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
621// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800622func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800623 c.outputs = append(c.outputs, paths...)
624 return c
625}
626
Colin Cross1d2cf042019-03-29 15:33:06 -0700627// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
628// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles
629// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
630// depfiles together.
631func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand {
632 c.depFiles = append(c.depFiles, path)
633 return c
634}
635
Colin Cross758290d2019-02-01 16:42:32 -0800636// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
637// will also be added to the dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800638func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700639 return c.Text(flag + c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800640}
641
Colin Cross758290d2019-02-01 16:42:32 -0800642// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
643// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
644// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800645func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700646 strs := make([]string, len(paths))
647 for i, path := range paths {
648 strs[i] = c.addInput(path)
649 }
650 return c.FlagWithList(flag, strs, sep)
Colin Crossfeec25b2019-01-30 17:32:39 -0800651}
652
Colin Cross758290d2019-02-01 16:42:32 -0800653// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
654// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for
655// each input path.
Colin Cross69f59a32019-02-15 10:39:37 -0800656func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800657 for _, path := range paths {
658 c.FlagWithInput(flag, path)
659 }
660 return c
661}
662
663// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path
664// will also be added to the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800665func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800666 c.outputs = append(c.outputs, path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700667 return c.Text(flag + c.outputStr(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800668}
669
Colin Cross1d2cf042019-03-29 15:33:06 -0700670// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path
671// will also be added to the outputs returned by RuleBuilder.Outputs.
672func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
673 c.depFiles = append(c.depFiles, path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700674 return c.Text(flag + c.outputStr(path))
Colin Cross1d2cf042019-03-29 15:33:06 -0700675}
676
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700677// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with no separator
678// between them. The paths will be written to the rspfile.
679func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, paths Paths) *RuleBuilderCommand {
680 if c.rspFileInputs != nil {
681 panic("FlagWithRspFileInputList cannot be called if rsp file inputs have already been provided")
682 }
683
684 // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
685 // generated.
686 if paths == nil {
687 paths = Paths{}
688 }
689
690 c.rspFileInputs = paths
691
692 rspFile := "$out.rsp"
693 c.FlagWithArg(flag, rspFile)
694 c.unescapedSpans = append(c.unescapedSpans, [2]int{c.buf.Len() - len(rspFile), c.buf.Len()})
695 return c
696}
697
Colin Cross758290d2019-02-01 16:42:32 -0800698// String returns the command line.
699func (c *RuleBuilderCommand) String() string {
Colin Crosscfec40c2019-07-08 17:07:18 -0700700 return c.buf.String()
Colin Cross758290d2019-02-01 16:42:32 -0800701}
Colin Cross1d2cf042019-03-29 15:33:06 -0700702
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700703// String returns the command line.
704func (c *RuleBuilderCommand) NinjaEscapedString() string {
705 return ninjaEscapeExceptForSpans(c.String(), c.unescapedSpans)
706}
707
708func ninjaEscapeExceptForSpans(s string, spans [][2]int) string {
709 if len(spans) == 0 {
710 return proptools.NinjaEscape(s)
711 }
712
713 sb := strings.Builder{}
714 sb.Grow(len(s) * 11 / 10)
715
716 i := 0
717 for _, span := range spans {
718 sb.WriteString(proptools.NinjaEscape(s[i:span[0]]))
719 sb.WriteString(s[span[0]:span[1]])
720 i = span[1]
721 }
722 sb.WriteString(proptools.NinjaEscape(s[i:]))
723
724 return sb.String()
725}
726
Colin Cross1d2cf042019-03-29 15:33:06 -0700727func ninjaNameEscape(s string) string {
728 b := []byte(s)
729 escaped := false
730 for i, c := range b {
731 valid := (c >= 'a' && c <= 'z') ||
732 (c >= 'A' && c <= 'Z') ||
733 (c >= '0' && c <= '9') ||
734 (c == '_') ||
735 (c == '-') ||
736 (c == '.')
737 if !valid {
738 b[i] = '_'
739 escaped = true
740 }
741 }
742 if escaped {
743 s = string(b)
744 }
745 return s
746}