blob: 928ba532d1151a1671518456687ae9974af87557 [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
Colin Cross8b8bec32019-11-15 13:18:43 -080036 highmem bool
37 remoteable RemoteRuleSupports
Dan Willemsen633c5022019-04-12 11:11:38 -070038 sboxOutDir WritablePath
Colin Cross0d2f40a2019-02-05 22:31:15 -080039 missingDeps []string
Colin Crossfeec25b2019-01-30 17:32:39 -080040}
41
Colin Cross758290d2019-02-01 16:42:32 -080042// NewRuleBuilder returns a newly created RuleBuilder.
43func NewRuleBuilder() *RuleBuilder {
Colin Cross5cb5b092019-02-02 21:25:18 -080044 return &RuleBuilder{
Colin Cross69f59a32019-02-15 10:39:37 -080045 temporariesSet: make(map[WritablePath]bool),
Colin Cross5cb5b092019-02-02 21:25:18 -080046 }
Colin Cross758290d2019-02-01 16:42:32 -080047}
48
49// RuleBuilderInstall is a tuple of install from and to locations.
50type RuleBuilderInstall struct {
Colin Cross69f59a32019-02-15 10:39:37 -080051 From Path
52 To string
Colin Cross758290d2019-02-01 16:42:32 -080053}
54
Colin Crossdeabb942019-02-11 14:11:09 -080055type RuleBuilderInstalls []RuleBuilderInstall
56
57// String returns the RuleBuilderInstalls in the form used by $(call copy-many-files) in Make, a space separated
58// list of from:to tuples.
59func (installs RuleBuilderInstalls) String() string {
60 sb := strings.Builder{}
61 for i, install := range installs {
62 if i != 0 {
63 sb.WriteRune(' ')
64 }
Colin Cross69f59a32019-02-15 10:39:37 -080065 sb.WriteString(install.From.String())
Colin Crossdeabb942019-02-11 14:11:09 -080066 sb.WriteRune(':')
67 sb.WriteString(install.To)
68 }
69 return sb.String()
70}
71
Colin Cross0d2f40a2019-02-05 22:31:15 -080072// MissingDeps adds modules to the list of missing dependencies. If MissingDeps
73// is called with a non-empty input, any call to Build will result in a rule
74// that will print an error listing the missing dependencies and fail.
75// MissingDeps should only be called if Config.AllowMissingDependencies() is
76// true.
77func (r *RuleBuilder) MissingDeps(missingDeps []string) {
78 r.missingDeps = append(r.missingDeps, missingDeps...)
79}
80
Colin Cross758290d2019-02-01 16:42:32 -080081// 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 -070082//
83// Restat is not compatible with Sbox()
Colin Crossfeec25b2019-01-30 17:32:39 -080084func (r *RuleBuilder) Restat() *RuleBuilder {
Dan Willemsen633c5022019-04-12 11:11:38 -070085 if r.sbox {
86 panic("Restat() is not compatible with Sbox()")
87 }
Colin Crossfeec25b2019-01-30 17:32:39 -080088 r.restat = true
89 return r
90}
91
Colin Cross8b8bec32019-11-15 13:18:43 -080092// HighMem marks the rule as a high memory rule, which will limit how many run in parallel with other high memory
93// rules.
94func (r *RuleBuilder) HighMem() *RuleBuilder {
95 r.highmem = true
96 return r
97}
98
99// Remoteable marks the rule as supporting remote execution.
100func (r *RuleBuilder) Remoteable(supports RemoteRuleSupports) *RuleBuilder {
101 r.remoteable = supports
102 return r
103}
104
Dan Willemsen633c5022019-04-12 11:11:38 -0700105// Sbox marks the rule as needing to be wrapped by sbox. The WritablePath should point to the output
106// directory that sbox will wipe. It should not be written to by any other rule. sbox will ensure
107// that all outputs have been written, and will discard any output files that were not specified.
108//
109// Sbox is not compatible with Restat()
110func (r *RuleBuilder) Sbox(outputDir WritablePath) *RuleBuilder {
111 if r.sbox {
112 panic("Sbox() may not be called more than once")
113 }
114 if len(r.commands) > 0 {
115 panic("Sbox() may not be called after Command()")
116 }
117 if r.restat {
118 panic("Sbox() is not compatible with Restat()")
119 }
120 r.sbox = true
121 r.sboxOutDir = outputDir
122 return r
123}
124
Colin Cross758290d2019-02-01 16:42:32 -0800125// Install associates an output of the rule with an install location, which can be retrieved later using
126// RuleBuilder.Installs.
Colin Cross69f59a32019-02-15 10:39:37 -0800127func (r *RuleBuilder) Install(from Path, to string) {
Colin Crossfeec25b2019-01-30 17:32:39 -0800128 r.installs = append(r.installs, RuleBuilderInstall{from, to})
129}
130
Colin Cross758290d2019-02-01 16:42:32 -0800131// Command returns a new RuleBuilderCommand for the rule. The commands will be ordered in the rule by when they were
132// created by this method. That can be mutated through their methods in any order, as long as the mutations do not
133// race with any call to Build.
Colin Crossfeec25b2019-01-30 17:32:39 -0800134func (r *RuleBuilder) Command() *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700135 command := &RuleBuilderCommand{
136 sbox: r.sbox,
137 sboxOutDir: r.sboxOutDir,
138 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800139 r.commands = append(r.commands, command)
140 return command
141}
142
Colin Cross5cb5b092019-02-02 21:25:18 -0800143// Temporary marks an output of a command as an intermediate file that will be used as an input to another command
144// in the same rule, and should not be listed in Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800145func (r *RuleBuilder) Temporary(path WritablePath) {
Colin Cross5cb5b092019-02-02 21:25:18 -0800146 r.temporariesSet[path] = true
147}
148
149// DeleteTemporaryFiles adds a command to the rule that deletes any outputs that have been marked using Temporary
150// when the rule runs. DeleteTemporaryFiles should be called after all calls to Temporary.
151func (r *RuleBuilder) DeleteTemporaryFiles() {
Colin Cross69f59a32019-02-15 10:39:37 -0800152 var temporariesList WritablePaths
Colin Cross5cb5b092019-02-02 21:25:18 -0800153
154 for intermediate := range r.temporariesSet {
155 temporariesList = append(temporariesList, intermediate)
156 }
Colin Cross69f59a32019-02-15 10:39:37 -0800157
158 sort.Slice(temporariesList, func(i, j int) bool {
159 return temporariesList[i].String() < temporariesList[j].String()
160 })
Colin Cross5cb5b092019-02-02 21:25:18 -0800161
162 r.Command().Text("rm").Flag("-f").Outputs(temporariesList)
163}
164
Colin Cross758290d2019-02-01 16:42:32 -0800165// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take input paths, such
166// as RuleBuilderCommand.Input, RuleBuilderComand.Implicit, or RuleBuilderCommand.FlagWithInput. Inputs to a command
167// that are also outputs of another command in the same RuleBuilder are filtered out.
Colin Cross69f59a32019-02-15 10:39:37 -0800168func (r *RuleBuilder) Inputs() Paths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800169 outputs := r.outputSet()
Dan Willemsen633c5022019-04-12 11:11:38 -0700170 depFiles := r.depFileSet()
Colin Crossfeec25b2019-01-30 17:32:39 -0800171
Colin Cross69f59a32019-02-15 10:39:37 -0800172 inputs := make(map[string]Path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800173 for _, c := range r.commands {
174 for _, input := range c.inputs {
Dan Willemsen633c5022019-04-12 11:11:38 -0700175 inputStr := input.String()
176 if _, isOutput := outputs[inputStr]; !isOutput {
177 if _, isDepFile := depFiles[inputStr]; !isDepFile {
178 inputs[input.String()] = input
179 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800180 }
181 }
182 }
183
Colin Cross69f59a32019-02-15 10:39:37 -0800184 var inputList Paths
185 for _, input := range inputs {
Colin Crossfeec25b2019-01-30 17:32:39 -0800186 inputList = append(inputList, input)
187 }
Colin Cross69f59a32019-02-15 10:39:37 -0800188
189 sort.Slice(inputList, func(i, j int) bool {
190 return inputList[i].String() < inputList[j].String()
191 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800192
193 return inputList
194}
195
Colin Cross69f59a32019-02-15 10:39:37 -0800196func (r *RuleBuilder) outputSet() map[string]WritablePath {
197 outputs := make(map[string]WritablePath)
Colin Crossfeec25b2019-01-30 17:32:39 -0800198 for _, c := range r.commands {
199 for _, output := range c.outputs {
Colin Cross69f59a32019-02-15 10:39:37 -0800200 outputs[output.String()] = output
Colin Crossfeec25b2019-01-30 17:32:39 -0800201 }
202 }
203 return outputs
204}
205
Colin Cross758290d2019-02-01 16:42:32 -0800206// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take output paths, such
207// as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or RuleBuilderCommand.FlagWithInput.
Colin Cross69f59a32019-02-15 10:39:37 -0800208func (r *RuleBuilder) Outputs() WritablePaths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800209 outputs := r.outputSet()
210
Colin Cross69f59a32019-02-15 10:39:37 -0800211 var outputList WritablePaths
212 for _, output := range outputs {
Colin Cross5cb5b092019-02-02 21:25:18 -0800213 if !r.temporariesSet[output] {
214 outputList = append(outputList, output)
215 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800216 }
Colin Cross69f59a32019-02-15 10:39:37 -0800217
218 sort.Slice(outputList, func(i, j int) bool {
219 return outputList[i].String() < outputList[j].String()
220 })
221
Colin Crossfeec25b2019-01-30 17:32:39 -0800222 return outputList
223}
224
Dan Willemsen633c5022019-04-12 11:11:38 -0700225func (r *RuleBuilder) depFileSet() map[string]WritablePath {
226 depFiles := make(map[string]WritablePath)
227 for _, c := range r.commands {
228 for _, depFile := range c.depFiles {
229 depFiles[depFile.String()] = depFile
230 }
231 }
232 return depFiles
233}
234
Colin Cross1d2cf042019-03-29 15:33:06 -0700235// DepFiles returns the list of paths that were passed to the RuleBuilderCommand methods that take depfile paths, such
236// as RuleBuilderCommand.DepFile or RuleBuilderCommand.FlagWithDepFile.
237func (r *RuleBuilder) DepFiles() WritablePaths {
238 var depFiles WritablePaths
239
240 for _, c := range r.commands {
241 for _, depFile := range c.depFiles {
242 depFiles = append(depFiles, depFile)
243 }
244 }
245
246 return depFiles
247}
248
Colin Cross758290d2019-02-01 16:42:32 -0800249// Installs returns the list of tuples passed to Install.
Colin Crossdeabb942019-02-11 14:11:09 -0800250func (r *RuleBuilder) Installs() RuleBuilderInstalls {
251 return append(RuleBuilderInstalls(nil), r.installs...)
Colin Crossfeec25b2019-01-30 17:32:39 -0800252}
253
Colin Cross69f59a32019-02-15 10:39:37 -0800254func (r *RuleBuilder) toolsSet() map[string]Path {
255 tools := make(map[string]Path)
Colin Cross5cb5b092019-02-02 21:25:18 -0800256 for _, c := range r.commands {
257 for _, tool := range c.tools {
Colin Cross69f59a32019-02-15 10:39:37 -0800258 tools[tool.String()] = tool
Colin Cross5cb5b092019-02-02 21:25:18 -0800259 }
260 }
261
262 return tools
263}
264
Colin Cross758290d2019-02-01 16:42:32 -0800265// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method.
Colin Cross69f59a32019-02-15 10:39:37 -0800266func (r *RuleBuilder) Tools() Paths {
Colin Cross5cb5b092019-02-02 21:25:18 -0800267 toolsSet := r.toolsSet()
268
Colin Cross69f59a32019-02-15 10:39:37 -0800269 var toolsList Paths
270 for _, tool := range toolsSet {
Colin Cross5cb5b092019-02-02 21:25:18 -0800271 toolsList = append(toolsList, tool)
Colin Crossfeec25b2019-01-30 17:32:39 -0800272 }
Colin Cross69f59a32019-02-15 10:39:37 -0800273
274 sort.Slice(toolsList, func(i, j int) bool {
275 return toolsList[i].String() < toolsList[j].String()
276 })
277
Colin Cross5cb5b092019-02-02 21:25:18 -0800278 return toolsList
Colin Crossfeec25b2019-01-30 17:32:39 -0800279}
280
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700281// RspFileInputs returns the list of paths that were passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
282func (r *RuleBuilder) RspFileInputs() Paths {
283 var rspFileInputs Paths
284 for _, c := range r.commands {
285 if c.rspFileInputs != nil {
286 if rspFileInputs != nil {
287 panic("Multiple commands in a rule may not have rsp file inputs")
288 }
289 rspFileInputs = c.rspFileInputs
290 }
291 }
292
293 return rspFileInputs
294}
295
296// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
Colin Crossfeec25b2019-01-30 17:32:39 -0800297func (r *RuleBuilder) Commands() []string {
298 var commands []string
299 for _, c := range r.commands {
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700300 commands = append(commands, c.String())
301 }
302 return commands
303}
304
305// NinjaEscapedCommands returns a slice containin the built command line after ninja escaping for each call to
306// RuleBuilder.Command.
307func (r *RuleBuilder) NinjaEscapedCommands() []string {
308 var commands []string
309 for _, c := range r.commands {
310 commands = append(commands, c.NinjaEscapedString())
Colin Crossfeec25b2019-01-30 17:32:39 -0800311 }
312 return commands
313}
314
Colin Cross758290d2019-02-01 16:42:32 -0800315// BuilderContext is a subset of ModuleContext and SingletonContext.
Colin Cross786cd6d2019-02-01 16:41:11 -0800316type BuilderContext interface {
317 PathContext
318 Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
319 Build(PackageContext, BuildParams)
320}
321
Colin Cross758290d2019-02-01 16:42:32 -0800322var _ BuilderContext = ModuleContext(nil)
323var _ BuilderContext = SingletonContext(nil)
324
Colin Cross1d2cf042019-03-29 15:33:06 -0700325func (r *RuleBuilder) depFileMergerCmd(ctx PathContext, depFiles WritablePaths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700326 return r.Command().
Colin Crossee94d6a2019-07-08 17:08:34 -0700327 BuiltTool(ctx, "dep_fixer").
Dan Willemsen633c5022019-04-12 11:11:38 -0700328 Inputs(depFiles.Paths())
Colin Cross1d2cf042019-03-29 15:33:06 -0700329}
330
Colin Cross758290d2019-02-01 16:42:32 -0800331// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
332// Outputs.
Colin Cross786cd6d2019-02-01 16:41:11 -0800333func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string, desc string) {
Colin Cross1d2cf042019-03-29 15:33:06 -0700334 name = ninjaNameEscape(name)
335
Colin Cross0d2f40a2019-02-05 22:31:15 -0800336 if len(r.missingDeps) > 0 {
337 ctx.Build(pctx, BuildParams{
338 Rule: ErrorRule,
Colin Cross69f59a32019-02-15 10:39:37 -0800339 Outputs: r.Outputs(),
Colin Cross0d2f40a2019-02-05 22:31:15 -0800340 Description: desc,
341 Args: map[string]string{
342 "error": "missing dependencies: " + strings.Join(r.missingDeps, ", "),
343 },
344 })
345 return
346 }
347
Colin Cross1d2cf042019-03-29 15:33:06 -0700348 var depFile WritablePath
349 var depFormat blueprint.Deps
350 if depFiles := r.DepFiles(); len(depFiles) > 0 {
351 depFile = depFiles[0]
352 depFormat = blueprint.DepsGCC
353 if len(depFiles) > 1 {
354 // Add a command locally that merges all depfiles together into the first depfile.
Dan Willemsen633c5022019-04-12 11:11:38 -0700355 r.depFileMergerCmd(ctx, depFiles)
356
357 if r.sbox {
358 // Check for Rel() errors, as all depfiles should be in the output dir
359 for _, path := range depFiles[1:] {
360 Rel(ctx, r.sboxOutDir.String(), path.String())
361 }
362 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700363 }
364 }
365
Dan Willemsen633c5022019-04-12 11:11:38 -0700366 tools := r.Tools()
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700367 commands := r.NinjaEscapedCommands()
Dan Willemsen633c5022019-04-12 11:11:38 -0700368 outputs := r.Outputs()
369
370 if len(commands) == 0 {
371 return
372 }
373 if len(outputs) == 0 {
374 panic("No outputs specified from any Commands")
375 }
376
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700377 commandString := strings.Join(commands, " && ")
Dan Willemsen633c5022019-04-12 11:11:38 -0700378
379 if r.sbox {
380 sboxOutputs := make([]string, len(outputs))
381 for i, output := range outputs {
382 sboxOutputs[i] = "__SBOX_OUT_DIR__/" + Rel(ctx, r.sboxOutDir.String(), output.String())
383 }
384
Dan Willemsen633c5022019-04-12 11:11:38 -0700385 commandString = proptools.ShellEscape(commandString)
386 if !strings.HasPrefix(commandString, `'`) {
387 commandString = `'` + commandString + `'`
388 }
389
390 sboxCmd := &RuleBuilderCommand{}
Colin Crossee94d6a2019-07-08 17:08:34 -0700391 sboxCmd.BuiltTool(ctx, "sbox").
Dan Willemsen633c5022019-04-12 11:11:38 -0700392 Flag("-c").Text(commandString).
393 Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(ctx).String())).
Dan Willemsenc89b6f12019-08-29 14:47:40 -0700394 Flag("--output-root").Text(r.sboxOutDir.String())
395
396 if depFile != nil {
397 sboxCmd.Flag("--depfile-out").Text(depFile.String())
398 }
399
400 sboxCmd.Flags(sboxOutputs)
Dan Willemsen633c5022019-04-12 11:11:38 -0700401
Colin Crosscfec40c2019-07-08 17:07:18 -0700402 commandString = sboxCmd.buf.String()
Dan Willemsen633c5022019-04-12 11:11:38 -0700403 tools = append(tools, sboxCmd.tools...)
404 }
405
Colin Cross1d2cf042019-03-29 15:33:06 -0700406 // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700407 // ImplicitOutputs. RuleBuilder only uses "$out" for the rsp file location, so the distinction between Outputs and
408 // ImplicitOutputs doesn't matter.
Dan Willemsen633c5022019-04-12 11:11:38 -0700409 output := outputs[0]
410 implicitOutputs := outputs[1:]
Colin Cross1d2cf042019-03-29 15:33:06 -0700411
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700412 var rspFile, rspFileContent string
413 rspFileInputs := r.RspFileInputs()
414 if rspFileInputs != nil {
415 rspFile = "$out.rsp"
416 rspFileContent = "$in"
417 }
418
Colin Cross8b8bec32019-11-15 13:18:43 -0800419 var pool blueprint.Pool
420 if ctx.Config().UseGoma() && r.remoteable&SUPPORTS_GOMA != 0 {
421 // When USE_GOMA=true is set and the rule is supported by goma, allow jobs to run outside the local pool.
422 } else if ctx.Config().UseRBE() && r.remoteable&SUPPORTS_RBE != 0 {
423 // When USE_GOMA=true is set and the rule is supported by RBE, allow jobs to run outside the local pool.
424 } else if r.highmem {
425 pool = highmemPool
426 } else if ctx.Config().UseRemoteBuild() {
427 pool = localPool
428 }
429
Dan Willemsen633c5022019-04-12 11:11:38 -0700430 ctx.Build(pctx, BuildParams{
431 Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700432 Command: commandString,
433 CommandDeps: tools.Strings(),
434 Restat: r.restat,
435 Rspfile: rspFile,
436 RspfileContent: rspFileContent,
Colin Cross8b8bec32019-11-15 13:18:43 -0800437 Pool: pool,
Dan Willemsen633c5022019-04-12 11:11:38 -0700438 }),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700439 Inputs: rspFileInputs,
Dan Willemsen633c5022019-04-12 11:11:38 -0700440 Implicits: r.Inputs(),
441 Output: output,
442 ImplicitOutputs: implicitOutputs,
443 Depfile: depFile,
444 Deps: depFormat,
445 Description: desc,
446 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800447}
448
Colin Cross758290d2019-02-01 16:42:32 -0800449// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
450// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the
451// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
452// space as a separator from the previous method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800453type RuleBuilderCommand struct {
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700454 buf strings.Builder
455 inputs Paths
456 outputs WritablePaths
457 depFiles WritablePaths
458 tools Paths
459 rspFileInputs Paths
460
461 // spans [start,end) of the command that should not be ninja escaped
462 unescapedSpans [][2]int
Dan Willemsen633c5022019-04-12 11:11:38 -0700463
464 sbox bool
465 sboxOutDir WritablePath
466}
467
468func (c *RuleBuilderCommand) addInput(path Path) string {
469 if c.sbox {
470 if rel, isRel, _ := maybeRelErr(c.sboxOutDir.String(), path.String()); isRel {
471 return "__SBOX_OUT_DIR__/" + rel
472 }
473 }
474 c.inputs = append(c.inputs, path)
475 return path.String()
476}
477
478func (c *RuleBuilderCommand) outputStr(path Path) string {
479 if c.sbox {
480 // Errors will be handled in RuleBuilder.Build where we have a context to report them
481 rel, _, _ := maybeRelErr(c.sboxOutDir.String(), path.String())
482 return "__SBOX_OUT_DIR__/" + rel
483 }
484 return path.String()
Colin Crossfeec25b2019-01-30 17:32:39 -0800485}
486
Colin Cross758290d2019-02-01 16:42:32 -0800487// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
488// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800489func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
Colin Crosscfec40c2019-07-08 17:07:18 -0700490 if c.buf.Len() > 0 {
491 c.buf.WriteByte(' ')
Colin Crossfeec25b2019-01-30 17:32:39 -0800492 }
Colin Crosscfec40c2019-07-08 17:07:18 -0700493 c.buf.WriteString(text)
Colin Crossfeec25b2019-01-30 17:32:39 -0800494 return c
495}
496
Colin Cross758290d2019-02-01 16:42:32 -0800497// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or
498// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800499func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
500 return c.Text(fmt.Sprintf(format, a...))
501}
502
Colin Cross758290d2019-02-01 16:42:32 -0800503// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the
504// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800505func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
506 return c.Text(flag)
507}
508
Colin Crossab054432019-07-15 16:13:59 -0700509// OptionalFlag adds the specified raw text to the command line if it is not nil. The text should not contain input or
510// output paths or the rule will not have them listed in its dependencies or outputs.
511func (c *RuleBuilderCommand) OptionalFlag(flag *string) *RuleBuilderCommand {
512 if flag != nil {
513 c.Text(*flag)
514 }
515
516 return c
517}
518
Colin Cross92b7d582019-03-29 15:32:51 -0700519// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the
520// rule will not have them listed in its dependencies or outputs.
521func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
522 for _, flag := range flags {
523 c.Text(flag)
524 }
525 return c
526}
527
Colin Cross758290d2019-02-01 16:42:32 -0800528// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
529// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
530// outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800531func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
532 return c.Text(flag + arg)
533}
534
Colin Crossc7ed0042019-02-11 14:11:09 -0800535// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to
536// calling FlagWithArg for argument.
537func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand {
538 for _, arg := range args {
539 c.FlagWithArg(flag, arg)
540 }
541 return c
542}
543
Roland Levillain2da5d9a2019-02-27 16:56:41 +0000544// 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 -0800545// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or
546// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800547func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
548 return c.Text(flag + strings.Join(list, sep))
549}
550
Colin Cross758290d2019-02-01 16:42:32 -0800551// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by
552// RuleBuilder.Tools.
Colin Cross69f59a32019-02-15 10:39:37 -0800553func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800554 c.tools = append(c.tools, path)
Colin Cross69f59a32019-02-15 10:39:37 -0800555 return c.Text(path.String())
Colin Crossfeec25b2019-01-30 17:32:39 -0800556}
557
Colin Crossee94d6a2019-07-08 17:08:34 -0700558// BuiltTool adds the specified tool path that was built using a host Soong module to the command line. The path will
559// be also added to the dependencies returned by RuleBuilder.Tools.
560//
561// It is equivalent to:
562// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
563func (c *RuleBuilderCommand) BuiltTool(ctx PathContext, tool string) *RuleBuilderCommand {
564 return c.Tool(ctx.Config().HostToolPath(ctx, tool))
565}
566
567// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the
568// dependencies returned by RuleBuilder.Tools.
569//
570// It is equivalent to:
571// cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
572func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
573 return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
574}
575
Colin Cross758290d2019-02-01 16:42:32 -0800576// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
577// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800578func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700579 return c.Text(c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800580}
581
Colin Cross758290d2019-02-01 16:42:32 -0800582// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
583// dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800584func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800585 for _, path := range paths {
586 c.Input(path)
587 }
588 return c
589}
590
591// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
592// command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800593func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700594 c.addInput(path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800595 return c
596}
597
Colin Cross758290d2019-02-01 16:42:32 -0800598// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
599// command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800600func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700601 for _, path := range paths {
602 c.addInput(path)
603 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800604 return c
605}
606
Colin Cross758290d2019-02-01 16:42:32 -0800607// Output adds the specified output path to the command line. The path will also be added to the outputs returned by
608// RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800609func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800610 c.outputs = append(c.outputs, path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700611 return c.Text(c.outputStr(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800612}
613
Colin Cross758290d2019-02-01 16:42:32 -0800614// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
615// the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800616func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800617 for _, path := range paths {
618 c.Output(path)
619 }
620 return c
621}
622
Dan Willemsen1945a4b2019-06-04 17:10:41 -0700623// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
624// and will be the temporary output directory managed by sbox, not the final one.
625func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand {
626 if !c.sbox {
627 panic("OutputDir only valid with Sbox")
628 }
629 return c.Text("__SBOX_OUT_DIR__")
630}
631
Colin Cross1d2cf042019-03-29 15:33:06 -0700632// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
633// line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles are added to
634// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
635func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
636 c.depFiles = append(c.depFiles, path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700637 return c.Text(c.outputStr(path))
Colin Cross1d2cf042019-03-29 15:33:06 -0700638}
639
Colin Cross758290d2019-02-01 16:42:32 -0800640// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
641// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800642func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800643 c.outputs = append(c.outputs, path)
644 return c
645}
646
Colin Cross758290d2019-02-01 16:42:32 -0800647// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
648// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800649func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800650 c.outputs = append(c.outputs, paths...)
651 return c
652}
653
Colin Cross1d2cf042019-03-29 15:33:06 -0700654// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
655// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles
656// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
657// depfiles together.
658func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand {
659 c.depFiles = append(c.depFiles, path)
660 return c
661}
662
Colin Cross758290d2019-02-01 16:42:32 -0800663// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
664// will also be added to the dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800665func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700666 return c.Text(flag + c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800667}
668
Colin Cross758290d2019-02-01 16:42:32 -0800669// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
670// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
671// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800672func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700673 strs := make([]string, len(paths))
674 for i, path := range paths {
675 strs[i] = c.addInput(path)
676 }
677 return c.FlagWithList(flag, strs, sep)
Colin Crossfeec25b2019-01-30 17:32:39 -0800678}
679
Colin Cross758290d2019-02-01 16:42:32 -0800680// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
681// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for
682// each input path.
Colin Cross69f59a32019-02-15 10:39:37 -0800683func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800684 for _, path := range paths {
685 c.FlagWithInput(flag, path)
686 }
687 return c
688}
689
690// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path
691// will also be added to the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800692func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800693 c.outputs = append(c.outputs, path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700694 return c.Text(flag + c.outputStr(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800695}
696
Colin Cross1d2cf042019-03-29 15:33:06 -0700697// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path
698// will also be added to the outputs returned by RuleBuilder.Outputs.
699func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
700 c.depFiles = append(c.depFiles, path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700701 return c.Text(flag + c.outputStr(path))
Colin Cross1d2cf042019-03-29 15:33:06 -0700702}
703
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700704// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with no separator
705// between them. The paths will be written to the rspfile.
706func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, paths Paths) *RuleBuilderCommand {
707 if c.rspFileInputs != nil {
708 panic("FlagWithRspFileInputList cannot be called if rsp file inputs have already been provided")
709 }
710
711 // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
712 // generated.
713 if paths == nil {
714 paths = Paths{}
715 }
716
717 c.rspFileInputs = paths
718
719 rspFile := "$out.rsp"
720 c.FlagWithArg(flag, rspFile)
721 c.unescapedSpans = append(c.unescapedSpans, [2]int{c.buf.Len() - len(rspFile), c.buf.Len()})
722 return c
723}
724
Colin Cross758290d2019-02-01 16:42:32 -0800725// String returns the command line.
726func (c *RuleBuilderCommand) String() string {
Colin Crosscfec40c2019-07-08 17:07:18 -0700727 return c.buf.String()
Colin Cross758290d2019-02-01 16:42:32 -0800728}
Colin Cross1d2cf042019-03-29 15:33:06 -0700729
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700730// String returns the command line.
731func (c *RuleBuilderCommand) NinjaEscapedString() string {
732 return ninjaEscapeExceptForSpans(c.String(), c.unescapedSpans)
733}
734
735func ninjaEscapeExceptForSpans(s string, spans [][2]int) string {
736 if len(spans) == 0 {
737 return proptools.NinjaEscape(s)
738 }
739
740 sb := strings.Builder{}
741 sb.Grow(len(s) * 11 / 10)
742
743 i := 0
744 for _, span := range spans {
745 sb.WriteString(proptools.NinjaEscape(s[i:span[0]]))
746 sb.WriteString(s[span[0]:span[1]])
747 i = span[1]
748 }
749 sb.WriteString(proptools.NinjaEscape(s[i:]))
750
751 return sb.String()
752}
753
Colin Cross1d2cf042019-03-29 15:33:06 -0700754func ninjaNameEscape(s string) string {
755 b := []byte(s)
756 escaped := false
757 for i, c := range b {
758 valid := (c >= 'a' && c <= 'z') ||
759 (c >= 'A' && c <= 'Z') ||
760 (c >= '0' && c <= '9') ||
761 (c == '_') ||
762 (c == '-') ||
763 (c == '.')
764 if !valid {
765 b[i] = '_'
766 escaped = true
767 }
768 }
769 if escaped {
770 s = string(b)
771 }
772 return s
773}