blob: cc0bfa6af6971b4ccbb55620b4f83f06a54bcf35 [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"
Colin Crossba9e4032020-11-24 16:32:22 -080035const sboxToolsSubDir = "tools"
Colin Crosse16ce362020-11-12 08:29:30 -080036const sboxOutDir = sboxSandboxBaseDir + "/" + sboxOutSubDir
Colin Cross3d680512020-11-13 16:23:53 -080037
Colin Cross758290d2019-02-01 16:42:32 -080038// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
39// graph.
Colin Crossfeec25b2019-01-30 17:32:39 -080040type RuleBuilder struct {
Colin Crossf1a035e2020-11-16 17:32:30 -080041 pctx PackageContext
42 ctx BuilderContext
43
Colin Crosse16ce362020-11-12 08:29:30 -080044 commands []*RuleBuilderCommand
45 installs RuleBuilderInstalls
46 temporariesSet map[WritablePath]bool
47 restat bool
48 sbox bool
49 highmem bool
50 remoteable RemoteRuleSupports
Colin Crossf1a035e2020-11-16 17:32:30 -080051 outDir WritablePath
Colin Crossba9e4032020-11-24 16:32:22 -080052 sboxTools bool
Colin Crosse16ce362020-11-12 08:29:30 -080053 sboxManifestPath WritablePath
54 missingDeps []string
Colin Crossfeec25b2019-01-30 17:32:39 -080055}
56
Colin Cross758290d2019-02-01 16:42:32 -080057// NewRuleBuilder returns a newly created RuleBuilder.
Colin Crossf1a035e2020-11-16 17:32:30 -080058func NewRuleBuilder(pctx PackageContext, ctx BuilderContext) *RuleBuilder {
Colin Cross5cb5b092019-02-02 21:25:18 -080059 return &RuleBuilder{
Colin Crossf1a035e2020-11-16 17:32:30 -080060 pctx: pctx,
61 ctx: ctx,
Colin Cross69f59a32019-02-15 10:39:37 -080062 temporariesSet: make(map[WritablePath]bool),
Colin Cross5cb5b092019-02-02 21:25:18 -080063 }
Colin Cross758290d2019-02-01 16:42:32 -080064}
65
66// RuleBuilderInstall is a tuple of install from and to locations.
67type RuleBuilderInstall struct {
Colin Cross69f59a32019-02-15 10:39:37 -080068 From Path
69 To string
Colin Cross758290d2019-02-01 16:42:32 -080070}
71
Colin Crossdeabb942019-02-11 14:11:09 -080072type RuleBuilderInstalls []RuleBuilderInstall
73
74// String returns the RuleBuilderInstalls in the form used by $(call copy-many-files) in Make, a space separated
75// list of from:to tuples.
76func (installs RuleBuilderInstalls) String() string {
77 sb := strings.Builder{}
78 for i, install := range installs {
79 if i != 0 {
80 sb.WriteRune(' ')
81 }
Colin Cross69f59a32019-02-15 10:39:37 -080082 sb.WriteString(install.From.String())
Colin Crossdeabb942019-02-11 14:11:09 -080083 sb.WriteRune(':')
84 sb.WriteString(install.To)
85 }
86 return sb.String()
87}
88
Colin Cross0d2f40a2019-02-05 22:31:15 -080089// MissingDeps adds modules to the list of missing dependencies. If MissingDeps
90// is called with a non-empty input, any call to Build will result in a rule
91// that will print an error listing the missing dependencies and fail.
92// MissingDeps should only be called if Config.AllowMissingDependencies() is
93// true.
94func (r *RuleBuilder) MissingDeps(missingDeps []string) {
95 r.missingDeps = append(r.missingDeps, missingDeps...)
96}
97
Colin Cross758290d2019-02-01 16:42:32 -080098// 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 -070099//
100// Restat is not compatible with Sbox()
Colin Crossfeec25b2019-01-30 17:32:39 -0800101func (r *RuleBuilder) Restat() *RuleBuilder {
Dan Willemsen633c5022019-04-12 11:11:38 -0700102 if r.sbox {
103 panic("Restat() is not compatible with Sbox()")
104 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800105 r.restat = true
106 return r
107}
108
Colin Cross8b8bec32019-11-15 13:18:43 -0800109// HighMem marks the rule as a high memory rule, which will limit how many run in parallel with other high memory
110// rules.
111func (r *RuleBuilder) HighMem() *RuleBuilder {
112 r.highmem = true
113 return r
114}
115
116// Remoteable marks the rule as supporting remote execution.
117func (r *RuleBuilder) Remoteable(supports RemoteRuleSupports) *RuleBuilder {
118 r.remoteable = supports
119 return r
120}
121
Colin Crosse16ce362020-11-12 08:29:30 -0800122// Sbox marks the rule as needing to be wrapped by sbox. The outputDir should point to the output
123// directory that sbox will wipe. It should not be written to by any other rule. manifestPath should
124// point to a location where sbox's manifest will be written and must be outside outputDir. sbox
125// will ensure that all outputs have been written, and will discard any output files that were not
126// specified.
Dan Willemsen633c5022019-04-12 11:11:38 -0700127//
128// Sbox is not compatible with Restat()
Colin Crosse16ce362020-11-12 08:29:30 -0800129func (r *RuleBuilder) Sbox(outputDir WritablePath, manifestPath WritablePath) *RuleBuilder {
Dan Willemsen633c5022019-04-12 11:11:38 -0700130 if r.sbox {
131 panic("Sbox() may not be called more than once")
132 }
133 if len(r.commands) > 0 {
134 panic("Sbox() may not be called after Command()")
135 }
136 if r.restat {
137 panic("Sbox() is not compatible with Restat()")
138 }
139 r.sbox = true
Colin Crossf1a035e2020-11-16 17:32:30 -0800140 r.outDir = outputDir
Colin Crosse16ce362020-11-12 08:29:30 -0800141 r.sboxManifestPath = manifestPath
Dan Willemsen633c5022019-04-12 11:11:38 -0700142 return r
143}
144
Colin Crossba9e4032020-11-24 16:32:22 -0800145// SandboxTools enables tool sandboxing for the rule by copying any referenced tools into the
146// sandbox.
147func (r *RuleBuilder) SandboxTools() *RuleBuilder {
148 if !r.sbox {
149 panic("SandboxTools() must be called after Sbox()")
150 }
151 if len(r.commands) > 0 {
152 panic("SandboxTools() may not be called after Command()")
153 }
154 r.sboxTools = true
155 return r
156}
157
Colin Cross758290d2019-02-01 16:42:32 -0800158// Install associates an output of the rule with an install location, which can be retrieved later using
159// RuleBuilder.Installs.
Colin Cross69f59a32019-02-15 10:39:37 -0800160func (r *RuleBuilder) Install(from Path, to string) {
Colin Crossfeec25b2019-01-30 17:32:39 -0800161 r.installs = append(r.installs, RuleBuilderInstall{from, to})
162}
163
Colin Cross758290d2019-02-01 16:42:32 -0800164// Command returns a new RuleBuilderCommand for the rule. The commands will be ordered in the rule by when they were
165// created by this method. That can be mutated through their methods in any order, as long as the mutations do not
166// race with any call to Build.
Colin Crossfeec25b2019-01-30 17:32:39 -0800167func (r *RuleBuilder) Command() *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700168 command := &RuleBuilderCommand{
Colin Crossf1a035e2020-11-16 17:32:30 -0800169 rule: r,
Dan Willemsen633c5022019-04-12 11:11:38 -0700170 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800171 r.commands = append(r.commands, command)
172 return command
173}
174
Colin Cross5cb5b092019-02-02 21:25:18 -0800175// Temporary marks an output of a command as an intermediate file that will be used as an input to another command
176// in the same rule, and should not be listed in Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800177func (r *RuleBuilder) Temporary(path WritablePath) {
Colin Cross5cb5b092019-02-02 21:25:18 -0800178 r.temporariesSet[path] = true
179}
180
181// DeleteTemporaryFiles adds a command to the rule that deletes any outputs that have been marked using Temporary
182// when the rule runs. DeleteTemporaryFiles should be called after all calls to Temporary.
183func (r *RuleBuilder) DeleteTemporaryFiles() {
Colin Cross69f59a32019-02-15 10:39:37 -0800184 var temporariesList WritablePaths
Colin Cross5cb5b092019-02-02 21:25:18 -0800185
186 for intermediate := range r.temporariesSet {
187 temporariesList = append(temporariesList, intermediate)
188 }
Colin Cross69f59a32019-02-15 10:39:37 -0800189
190 sort.Slice(temporariesList, func(i, j int) bool {
191 return temporariesList[i].String() < temporariesList[j].String()
192 })
Colin Cross5cb5b092019-02-02 21:25:18 -0800193
194 r.Command().Text("rm").Flag("-f").Outputs(temporariesList)
195}
196
Colin Crossda71eda2020-02-21 16:55:19 -0800197// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
Colin Cross3d680512020-11-13 16:23:53 -0800198// input paths, such as RuleBuilderCommand.Input, RuleBuilderCommand.Implicit, or
Colin Crossda71eda2020-02-21 16:55:19 -0800199// RuleBuilderCommand.FlagWithInput. Inputs to a command that are also outputs of another command
200// in the same RuleBuilder are filtered out. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800201func (r *RuleBuilder) Inputs() Paths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800202 outputs := r.outputSet()
Dan Willemsen633c5022019-04-12 11:11:38 -0700203 depFiles := r.depFileSet()
Colin Crossfeec25b2019-01-30 17:32:39 -0800204
Colin Cross69f59a32019-02-15 10:39:37 -0800205 inputs := make(map[string]Path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800206 for _, c := range r.commands {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400207 for _, input := range append(c.inputs, c.implicits...) {
Dan Willemsen633c5022019-04-12 11:11:38 -0700208 inputStr := input.String()
209 if _, isOutput := outputs[inputStr]; !isOutput {
210 if _, isDepFile := depFiles[inputStr]; !isDepFile {
211 inputs[input.String()] = input
212 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800213 }
214 }
215 }
216
Colin Cross69f59a32019-02-15 10:39:37 -0800217 var inputList Paths
218 for _, input := range inputs {
Colin Crossfeec25b2019-01-30 17:32:39 -0800219 inputList = append(inputList, input)
220 }
Colin Cross69f59a32019-02-15 10:39:37 -0800221
222 sort.Slice(inputList, func(i, j int) bool {
223 return inputList[i].String() < inputList[j].String()
224 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800225
226 return inputList
227}
228
Colin Crossda71eda2020-02-21 16:55:19 -0800229// OrderOnlys returns the list of paths that were passed to the RuleBuilderCommand.OrderOnly or
230// RuleBuilderCommand.OrderOnlys. The list is sorted and duplicates removed.
231func (r *RuleBuilder) OrderOnlys() Paths {
232 orderOnlys := make(map[string]Path)
233 for _, c := range r.commands {
234 for _, orderOnly := range c.orderOnlys {
235 orderOnlys[orderOnly.String()] = orderOnly
236 }
237 }
238
239 var orderOnlyList Paths
240 for _, orderOnly := range orderOnlys {
241 orderOnlyList = append(orderOnlyList, orderOnly)
242 }
243
244 sort.Slice(orderOnlyList, func(i, j int) bool {
245 return orderOnlyList[i].String() < orderOnlyList[j].String()
246 })
247
248 return orderOnlyList
249}
250
Colin Cross69f59a32019-02-15 10:39:37 -0800251func (r *RuleBuilder) outputSet() map[string]WritablePath {
252 outputs := make(map[string]WritablePath)
Colin Crossfeec25b2019-01-30 17:32:39 -0800253 for _, c := range r.commands {
254 for _, output := range c.outputs {
Colin Cross69f59a32019-02-15 10:39:37 -0800255 outputs[output.String()] = output
Colin Crossfeec25b2019-01-30 17:32:39 -0800256 }
257 }
258 return outputs
259}
260
Colin Crossda71eda2020-02-21 16:55:19 -0800261// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
262// output paths, such as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or
263// RuleBuilderCommand.FlagWithInput. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800264func (r *RuleBuilder) Outputs() WritablePaths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800265 outputs := r.outputSet()
266
Colin Cross69f59a32019-02-15 10:39:37 -0800267 var outputList WritablePaths
268 for _, output := range outputs {
Colin Cross5cb5b092019-02-02 21:25:18 -0800269 if !r.temporariesSet[output] {
270 outputList = append(outputList, output)
271 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800272 }
Colin Cross69f59a32019-02-15 10:39:37 -0800273
274 sort.Slice(outputList, func(i, j int) bool {
275 return outputList[i].String() < outputList[j].String()
276 })
277
Colin Crossfeec25b2019-01-30 17:32:39 -0800278 return outputList
279}
280
Jingwen Chence679d22020-09-23 04:30:02 +0000281func (r *RuleBuilder) symlinkOutputSet() map[string]WritablePath {
282 symlinkOutputs := make(map[string]WritablePath)
283 for _, c := range r.commands {
284 for _, symlinkOutput := range c.symlinkOutputs {
285 symlinkOutputs[symlinkOutput.String()] = symlinkOutput
286 }
287 }
288 return symlinkOutputs
289}
290
291// SymlinkOutputs returns the list of paths that the executor (Ninja) would
292// verify, after build edge completion, that:
293//
294// 1) Created output symlinks match the list of paths in this list exactly (no more, no fewer)
295// 2) Created output files are *not* declared in this list.
296//
297// These symlink outputs are expected to be a subset of outputs or implicit
298// outputs, or they would fail validation at build param construction time
299// later, to support other non-rule-builder approaches for constructing
300// statements.
301func (r *RuleBuilder) SymlinkOutputs() WritablePaths {
302 symlinkOutputs := r.symlinkOutputSet()
303
304 var symlinkOutputList WritablePaths
305 for _, symlinkOutput := range symlinkOutputs {
306 symlinkOutputList = append(symlinkOutputList, symlinkOutput)
307 }
308
309 sort.Slice(symlinkOutputList, func(i, j int) bool {
310 return symlinkOutputList[i].String() < symlinkOutputList[j].String()
311 })
312
313 return symlinkOutputList
314}
315
Dan Willemsen633c5022019-04-12 11:11:38 -0700316func (r *RuleBuilder) depFileSet() map[string]WritablePath {
317 depFiles := make(map[string]WritablePath)
318 for _, c := range r.commands {
319 for _, depFile := range c.depFiles {
320 depFiles[depFile.String()] = depFile
321 }
322 }
323 return depFiles
324}
325
Colin Cross1d2cf042019-03-29 15:33:06 -0700326// DepFiles returns the list of paths that were passed to the RuleBuilderCommand methods that take depfile paths, such
327// as RuleBuilderCommand.DepFile or RuleBuilderCommand.FlagWithDepFile.
328func (r *RuleBuilder) DepFiles() WritablePaths {
329 var depFiles WritablePaths
330
331 for _, c := range r.commands {
332 for _, depFile := range c.depFiles {
333 depFiles = append(depFiles, depFile)
334 }
335 }
336
337 return depFiles
338}
339
Colin Cross758290d2019-02-01 16:42:32 -0800340// Installs returns the list of tuples passed to Install.
Colin Crossdeabb942019-02-11 14:11:09 -0800341func (r *RuleBuilder) Installs() RuleBuilderInstalls {
342 return append(RuleBuilderInstalls(nil), r.installs...)
Colin Crossfeec25b2019-01-30 17:32:39 -0800343}
344
Colin Cross69f59a32019-02-15 10:39:37 -0800345func (r *RuleBuilder) toolsSet() map[string]Path {
346 tools := make(map[string]Path)
Colin Cross5cb5b092019-02-02 21:25:18 -0800347 for _, c := range r.commands {
348 for _, tool := range c.tools {
Colin Cross69f59a32019-02-15 10:39:37 -0800349 tools[tool.String()] = tool
Colin Cross5cb5b092019-02-02 21:25:18 -0800350 }
351 }
352
353 return tools
354}
355
Colin Crossda71eda2020-02-21 16:55:19 -0800356// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method. The
357// list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800358func (r *RuleBuilder) Tools() Paths {
Colin Cross5cb5b092019-02-02 21:25:18 -0800359 toolsSet := r.toolsSet()
360
Colin Cross69f59a32019-02-15 10:39:37 -0800361 var toolsList Paths
362 for _, tool := range toolsSet {
Colin Cross5cb5b092019-02-02 21:25:18 -0800363 toolsList = append(toolsList, tool)
Colin Crossfeec25b2019-01-30 17:32:39 -0800364 }
Colin Cross69f59a32019-02-15 10:39:37 -0800365
366 sort.Slice(toolsList, func(i, j int) bool {
367 return toolsList[i].String() < toolsList[j].String()
368 })
369
Colin Cross5cb5b092019-02-02 21:25:18 -0800370 return toolsList
Colin Crossfeec25b2019-01-30 17:32:39 -0800371}
372
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700373// RspFileInputs returns the list of paths that were passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
374func (r *RuleBuilder) RspFileInputs() Paths {
375 var rspFileInputs Paths
376 for _, c := range r.commands {
377 if c.rspFileInputs != nil {
378 if rspFileInputs != nil {
379 panic("Multiple commands in a rule may not have rsp file inputs")
380 }
381 rspFileInputs = c.rspFileInputs
382 }
383 }
384
385 return rspFileInputs
386}
387
Colin Cross70c47412021-03-12 17:48:14 -0800388// RspFile returns the path to the rspfile that was passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
389func (r *RuleBuilder) RspFile() WritablePath {
390 var rspFile WritablePath
391 for _, c := range r.commands {
392 if c.rspFile != nil {
393 if rspFile != nil {
394 panic("Multiple commands in a rule may not have rsp file inputs")
395 }
396 rspFile = c.rspFile
397 }
398 }
399
400 return rspFile
401}
402
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700403// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
Colin Crossfeec25b2019-01-30 17:32:39 -0800404func (r *RuleBuilder) Commands() []string {
405 var commands []string
406 for _, c := range r.commands {
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700407 commands = append(commands, c.String())
408 }
409 return commands
410}
411
Colin Cross3d680512020-11-13 16:23:53 -0800412// NinjaEscapedCommands returns a slice containing the built command line after ninja escaping for each call to
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700413// RuleBuilder.Command.
414func (r *RuleBuilder) NinjaEscapedCommands() []string {
415 var commands []string
416 for _, c := range r.commands {
417 commands = append(commands, c.NinjaEscapedString())
Colin Crossfeec25b2019-01-30 17:32:39 -0800418 }
419 return commands
420}
421
Colin Cross758290d2019-02-01 16:42:32 -0800422// BuilderContext is a subset of ModuleContext and SingletonContext.
Colin Cross786cd6d2019-02-01 16:41:11 -0800423type BuilderContext interface {
424 PathContext
425 Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
426 Build(PackageContext, BuildParams)
427}
428
Colin Cross758290d2019-02-01 16:42:32 -0800429var _ BuilderContext = ModuleContext(nil)
430var _ BuilderContext = SingletonContext(nil)
431
Colin Crossf1a035e2020-11-16 17:32:30 -0800432func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700433 return r.Command().
Colin Crossf1a035e2020-11-16 17:32:30 -0800434 BuiltTool("dep_fixer").
Dan Willemsen633c5022019-04-12 11:11:38 -0700435 Inputs(depFiles.Paths())
Colin Cross1d2cf042019-03-29 15:33:06 -0700436}
437
Colin Cross758290d2019-02-01 16:42:32 -0800438// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
439// Outputs.
Colin Crossf1a035e2020-11-16 17:32:30 -0800440func (r *RuleBuilder) Build(name string, desc string) {
Colin Cross1d2cf042019-03-29 15:33:06 -0700441 name = ninjaNameEscape(name)
442
Colin Cross0d2f40a2019-02-05 22:31:15 -0800443 if len(r.missingDeps) > 0 {
Colin Crossf1a035e2020-11-16 17:32:30 -0800444 r.ctx.Build(pctx, BuildParams{
Colin Cross0d2f40a2019-02-05 22:31:15 -0800445 Rule: ErrorRule,
Colin Cross69f59a32019-02-15 10:39:37 -0800446 Outputs: r.Outputs(),
Colin Crossda71eda2020-02-21 16:55:19 -0800447 OrderOnly: r.OrderOnlys(),
Colin Cross0d2f40a2019-02-05 22:31:15 -0800448 Description: desc,
449 Args: map[string]string{
450 "error": "missing dependencies: " + strings.Join(r.missingDeps, ", "),
451 },
452 })
453 return
454 }
455
Colin Cross1d2cf042019-03-29 15:33:06 -0700456 var depFile WritablePath
457 var depFormat blueprint.Deps
458 if depFiles := r.DepFiles(); len(depFiles) > 0 {
459 depFile = depFiles[0]
460 depFormat = blueprint.DepsGCC
461 if len(depFiles) > 1 {
462 // Add a command locally that merges all depfiles together into the first depfile.
Colin Crossf1a035e2020-11-16 17:32:30 -0800463 r.depFileMergerCmd(depFiles)
Dan Willemsen633c5022019-04-12 11:11:38 -0700464
465 if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800466 // Check for Rel() errors, as all depfiles should be in the output dir. Errors
467 // will be reported to the ctx.
Dan Willemsen633c5022019-04-12 11:11:38 -0700468 for _, path := range depFiles[1:] {
Colin Crossf1a035e2020-11-16 17:32:30 -0800469 Rel(r.ctx, r.outDir.String(), path.String())
Dan Willemsen633c5022019-04-12 11:11:38 -0700470 }
471 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700472 }
473 }
474
Dan Willemsen633c5022019-04-12 11:11:38 -0700475 tools := r.Tools()
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700476 commands := r.NinjaEscapedCommands()
Dan Willemsen633c5022019-04-12 11:11:38 -0700477 outputs := r.Outputs()
Colin Cross3d680512020-11-13 16:23:53 -0800478 inputs := r.Inputs()
Colin Cross70c47412021-03-12 17:48:14 -0800479 rspFileInputs := r.RspFileInputs()
480 rspFilePath := r.RspFile()
Dan Willemsen633c5022019-04-12 11:11:38 -0700481
482 if len(commands) == 0 {
483 return
484 }
485 if len(outputs) == 0 {
486 panic("No outputs specified from any Commands")
487 }
488
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700489 commandString := strings.Join(commands, " && ")
Dan Willemsen633c5022019-04-12 11:11:38 -0700490
491 if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800492 // If running the command inside sbox, write the rule data out to an sbox
493 // manifest.textproto.
494 manifest := sbox_proto.Manifest{}
495 command := sbox_proto.Command{}
496 manifest.Commands = append(manifest.Commands, &command)
497 command.Command = proto.String(commandString)
Colin Cross151b9ff2020-11-12 08:29:30 -0800498
Colin Cross619b9ab2020-11-20 18:44:31 +0000499 if depFile != nil {
Colin Crosse16ce362020-11-12 08:29:30 -0800500 manifest.OutputDepfile = proto.String(depFile.String())
Colin Cross619b9ab2020-11-20 18:44:31 +0000501 }
502
Colin Crossba9e4032020-11-24 16:32:22 -0800503 // If sandboxing tools is enabled, add copy rules to the manifest to copy each tool
504 // into the sbox directory.
505 if r.sboxTools {
506 for _, tool := range tools {
507 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
508 From: proto.String(tool.String()),
509 To: proto.String(sboxPathForToolRel(r.ctx, tool)),
510 })
511 }
512 for _, c := range r.commands {
513 for _, tool := range c.packagedTools {
514 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
515 From: proto.String(tool.srcPath.String()),
516 To: proto.String(sboxPathForPackagedToolRel(tool)),
517 Executable: proto.Bool(tool.executable),
518 })
519 tools = append(tools, tool.srcPath)
520 }
521 }
522 }
523
Colin Crosse16ce362020-11-12 08:29:30 -0800524 // Add copy rules to the manifest to copy each output file from the sbox directory.
Colin Crossba9e4032020-11-24 16:32:22 -0800525 // to the output directory after running the commands.
Colin Crosse16ce362020-11-12 08:29:30 -0800526 sboxOutputs := make([]string, len(outputs))
527 for i, output := range outputs {
Colin Crossf1a035e2020-11-16 17:32:30 -0800528 rel := Rel(r.ctx, r.outDir.String(), output.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800529 sboxOutputs[i] = filepath.Join(sboxOutDir, rel)
530 command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{
531 From: proto.String(filepath.Join(sboxOutSubDir, rel)),
532 To: proto.String(output.String()),
533 })
534 }
Colin Cross619b9ab2020-11-20 18:44:31 +0000535
Colin Crosse16ce362020-11-12 08:29:30 -0800536 // Add a hash of the list of input files to the manifest so that the textproto file
537 // changes when the list of input files changes and causes the sbox rule that
538 // depends on it to rerun.
539 command.InputHash = proto.String(hashSrcFiles(inputs))
Colin Cross619b9ab2020-11-20 18:44:31 +0000540
Colin Crosse16ce362020-11-12 08:29:30 -0800541 // Verify that the manifest textproto is not inside the sbox output directory, otherwise
542 // it will get deleted when the sbox rule clears its output directory.
Colin Crossf1a035e2020-11-16 17:32:30 -0800543 _, manifestInOutDir := MaybeRel(r.ctx, r.outDir.String(), r.sboxManifestPath.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800544 if manifestInOutDir {
Colin Crossf1a035e2020-11-16 17:32:30 -0800545 ReportPathErrorf(r.ctx, "sbox rule %q manifestPath %q must not be in outputDir %q",
546 name, r.sboxManifestPath.String(), r.outDir.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800547 }
548
549 // Create a rule to write the manifest as a the textproto.
Colin Crossf1a035e2020-11-16 17:32:30 -0800550 WriteFileRule(r.ctx, r.sboxManifestPath, proto.MarshalTextString(&manifest))
Colin Crosse16ce362020-11-12 08:29:30 -0800551
552 // Generate a new string to use as the command line of the sbox rule. This uses
553 // a RuleBuilderCommand as a convenience method of building the command line, then
554 // converts it to a string to replace commandString.
Colin Crossf1a035e2020-11-16 17:32:30 -0800555 sboxCmd := &RuleBuilderCommand{
556 rule: &RuleBuilder{
557 ctx: r.ctx,
558 },
559 }
560 sboxCmd.Text("rm -rf").Output(r.outDir)
Colin Crosse16ce362020-11-12 08:29:30 -0800561 sboxCmd.Text("&&")
Colin Crossf1a035e2020-11-16 17:32:30 -0800562 sboxCmd.BuiltTool("sbox").
563 Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
Colin Crosse16ce362020-11-12 08:29:30 -0800564 Flag("--manifest").Input(r.sboxManifestPath)
565
566 // Replace the command string, and add the sbox tool and manifest textproto to the
567 // dependencies of the final sbox rule.
Colin Crosscfec40c2019-07-08 17:07:18 -0700568 commandString = sboxCmd.buf.String()
Dan Willemsen633c5022019-04-12 11:11:38 -0700569 tools = append(tools, sboxCmd.tools...)
Colin Crosse16ce362020-11-12 08:29:30 -0800570 inputs = append(inputs, sboxCmd.inputs...)
Colin Cross3d680512020-11-13 16:23:53 -0800571 } else {
572 // If not using sbox the rule will run the command directly, put the hash of the
573 // list of input files in a comment at the end of the command line to ensure ninja
574 // reruns the rule when the list of input files changes.
575 commandString += " # hash of input list: " + hashSrcFiles(inputs)
Dan Willemsen633c5022019-04-12 11:11:38 -0700576 }
577
Colin Cross1d2cf042019-03-29 15:33:06 -0700578 // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
Colin Cross70c47412021-03-12 17:48:14 -0800579 // ImplicitOutputs. RuleBuilder doesn't use "$out", so the distinction between Outputs and
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700580 // ImplicitOutputs doesn't matter.
Dan Willemsen633c5022019-04-12 11:11:38 -0700581 output := outputs[0]
582 implicitOutputs := outputs[1:]
Colin Cross1d2cf042019-03-29 15:33:06 -0700583
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700584 var rspFile, rspFileContent string
Colin Cross70c47412021-03-12 17:48:14 -0800585 if rspFilePath != nil {
586 rspFile = rspFilePath.String()
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700587 rspFileContent = "$in"
588 }
589
Colin Cross8b8bec32019-11-15 13:18:43 -0800590 var pool blueprint.Pool
Colin Crossf1a035e2020-11-16 17:32:30 -0800591 if r.ctx.Config().UseGoma() && r.remoteable.Goma {
Colin Cross8b8bec32019-11-15 13:18:43 -0800592 // When USE_GOMA=true is set and the rule is supported by goma, allow jobs to run outside the local pool.
Colin Crossf1a035e2020-11-16 17:32:30 -0800593 } else if r.ctx.Config().UseRBE() && r.remoteable.RBE {
Ramy Medhat944839a2020-03-31 22:14:52 -0400594 // When USE_RBE=true is set and the rule is supported by RBE, use the remotePool.
595 pool = remotePool
Colin Cross8b8bec32019-11-15 13:18:43 -0800596 } else if r.highmem {
597 pool = highmemPool
Colin Crossf1a035e2020-11-16 17:32:30 -0800598 } else if r.ctx.Config().UseRemoteBuild() {
Colin Cross8b8bec32019-11-15 13:18:43 -0800599 pool = localPool
600 }
601
Colin Crossf1a035e2020-11-16 17:32:30 -0800602 r.ctx.Build(r.pctx, BuildParams{
603 Rule: r.ctx.Rule(pctx, name, blueprint.RuleParams{
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700604 Command: commandString,
Colin Cross45029782021-03-16 16:49:52 -0700605 CommandDeps: proptools.NinjaEscapeList(tools.Strings()),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700606 Restat: r.restat,
Colin Cross45029782021-03-16 16:49:52 -0700607 Rspfile: proptools.NinjaEscape(rspFile),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700608 RspfileContent: rspFileContent,
Colin Cross8b8bec32019-11-15 13:18:43 -0800609 Pool: pool,
Dan Willemsen633c5022019-04-12 11:11:38 -0700610 }),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700611 Inputs: rspFileInputs,
Colin Cross3d680512020-11-13 16:23:53 -0800612 Implicits: inputs,
Dan Willemsen633c5022019-04-12 11:11:38 -0700613 Output: output,
614 ImplicitOutputs: implicitOutputs,
Jingwen Chence679d22020-09-23 04:30:02 +0000615 SymlinkOutputs: r.SymlinkOutputs(),
Dan Willemsen633c5022019-04-12 11:11:38 -0700616 Depfile: depFile,
617 Deps: depFormat,
618 Description: desc,
619 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800620}
621
Colin Cross758290d2019-02-01 16:42:32 -0800622// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
623// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the
624// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
625// space as a separator from the previous method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800626type RuleBuilderCommand struct {
Colin Crossf1a035e2020-11-16 17:32:30 -0800627 rule *RuleBuilder
628
Jingwen Chence679d22020-09-23 04:30:02 +0000629 buf strings.Builder
630 inputs Paths
631 implicits Paths
632 orderOnlys Paths
633 outputs WritablePaths
634 symlinkOutputs WritablePaths
635 depFiles WritablePaths
636 tools Paths
Colin Crossba9e4032020-11-24 16:32:22 -0800637 packagedTools []PackagingSpec
Jingwen Chence679d22020-09-23 04:30:02 +0000638 rspFileInputs Paths
Colin Cross70c47412021-03-12 17:48:14 -0800639 rspFile WritablePath
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700640
641 // spans [start,end) of the command that should not be ninja escaped
642 unescapedSpans [][2]int
Dan Willemsen633c5022019-04-12 11:11:38 -0700643}
644
645func (c *RuleBuilderCommand) addInput(path Path) string {
Colin Crossf1a035e2020-11-16 17:32:30 -0800646 if c.rule.sbox {
647 if rel, isRel, _ := maybeRelErr(c.rule.outDir.String(), path.String()); isRel {
Colin Cross3d680512020-11-13 16:23:53 -0800648 return filepath.Join(sboxOutDir, rel)
Dan Willemsen633c5022019-04-12 11:11:38 -0700649 }
650 }
651 c.inputs = append(c.inputs, path)
652 return path.String()
653}
654
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400655func (c *RuleBuilderCommand) addImplicit(path Path) string {
Colin Crossf1a035e2020-11-16 17:32:30 -0800656 if c.rule.sbox {
657 if rel, isRel, _ := maybeRelErr(c.rule.outDir.String(), path.String()); isRel {
Colin Cross3d680512020-11-13 16:23:53 -0800658 return filepath.Join(sboxOutDir, rel)
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400659 }
660 }
661 c.implicits = append(c.implicits, path)
662 return path.String()
663}
664
Colin Crossda71eda2020-02-21 16:55:19 -0800665func (c *RuleBuilderCommand) addOrderOnly(path Path) {
666 c.orderOnlys = append(c.orderOnlys, path)
667}
668
Colin Crossf1a035e2020-11-16 17:32:30 -0800669// PathForOutput takes an output path and returns the appropriate path to use on the command
670// line. If sbox was enabled via a call to RuleBuilder.Sbox(), it returns a path with the
671// placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
672// original path.
673func (c *RuleBuilderCommand) PathForOutput(path WritablePath) string {
674 if c.rule.sbox {
675 // Errors will be handled in RuleBuilder.Build where we have a context to report them
676 rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
677 return filepath.Join(sboxOutDir, rel)
Dan Willemsen633c5022019-04-12 11:11:38 -0700678 }
679 return path.String()
Colin Crossfeec25b2019-01-30 17:32:39 -0800680}
681
Colin Crossba9e4032020-11-24 16:32:22 -0800682// SboxPathForTool takes a path to a tool, which may be an output file or a source file, and returns
683// the corresponding path for the tool in the sbox sandbox. It assumes that sandboxing and tool
684// sandboxing are enabled.
685func SboxPathForTool(ctx BuilderContext, path Path) string {
686 return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(ctx, path))
687}
688
689func sboxPathForToolRel(ctx BuilderContext, path Path) string {
690 // Errors will be handled in RuleBuilder.Build where we have a context to report them
691 relOut, isRelOut, _ := maybeRelErr(PathForOutput(ctx, "host", ctx.Config().PrebuiltOS()).String(), path.String())
692 if isRelOut {
693 // The tool is in the output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
694 return filepath.Join(sboxToolsSubDir, "out", relOut)
695 }
696 // The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
697 return filepath.Join(sboxToolsSubDir, "src", path.String())
698}
699
700// SboxPathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
701// tool after copying it into the sandbox. This can be used on the RuleBuilder command line to
702// reference the tool.
703func SboxPathForPackagedTool(spec PackagingSpec) string {
704 return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
705}
706
707func sboxPathForPackagedToolRel(spec PackagingSpec) string {
708 return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage)
709}
710
711// PathForTool takes a path to a tool, which may be an output file or a source file, and returns
712// the corresponding path for the tool in the sbox sandbox if sbox is enabled, or the original path
713// if it is not. This can be used on the RuleBuilder command line to reference the tool.
714func (c *RuleBuilderCommand) PathForTool(path Path) string {
715 if c.rule.sbox && c.rule.sboxTools {
716 return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path))
717 }
718 return path.String()
719}
720
721// PackagedTool adds the specified tool path to the command line. It can only be used with tool
722// sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox.
723func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand {
724 if !c.rule.sboxTools {
725 panic("PackagedTool() requires SandboxTools()")
726 }
727
728 c.packagedTools = append(c.packagedTools, spec)
729 c.Text(sboxPathForPackagedToolRel(spec))
730 return c
731}
732
733// ImplicitPackagedTool copies the specified tool into the sandbox without modifying the command
734// line. It can only be used with tool sandboxing enabled by SandboxTools().
735func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuilderCommand {
736 if !c.rule.sboxTools {
737 panic("ImplicitPackagedTool() requires SandboxTools()")
738 }
739
740 c.packagedTools = append(c.packagedTools, spec)
741 return c
742}
743
744// ImplicitPackagedTools copies the specified tools into the sandbox without modifying the command
745// line. It can only be used with tool sandboxing enabled by SandboxTools().
746func (c *RuleBuilderCommand) ImplicitPackagedTools(specs []PackagingSpec) *RuleBuilderCommand {
747 if !c.rule.sboxTools {
748 panic("ImplicitPackagedTools() requires SandboxTools()")
749 }
750
751 c.packagedTools = append(c.packagedTools, specs...)
752 return c
753}
754
Colin Cross758290d2019-02-01 16:42:32 -0800755// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
756// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800757func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
Colin Crosscfec40c2019-07-08 17:07:18 -0700758 if c.buf.Len() > 0 {
759 c.buf.WriteByte(' ')
Colin Crossfeec25b2019-01-30 17:32:39 -0800760 }
Colin Crosscfec40c2019-07-08 17:07:18 -0700761 c.buf.WriteString(text)
Colin Crossfeec25b2019-01-30 17:32:39 -0800762 return c
763}
764
Colin Cross758290d2019-02-01 16:42:32 -0800765// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or
766// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800767func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
768 return c.Text(fmt.Sprintf(format, a...))
769}
770
Colin Cross758290d2019-02-01 16:42:32 -0800771// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the
772// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800773func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
774 return c.Text(flag)
775}
776
Colin Crossab054432019-07-15 16:13:59 -0700777// OptionalFlag adds the specified raw text to the command line if it is not nil. The text should not contain input or
778// output paths or the rule will not have them listed in its dependencies or outputs.
779func (c *RuleBuilderCommand) OptionalFlag(flag *string) *RuleBuilderCommand {
780 if flag != nil {
781 c.Text(*flag)
782 }
783
784 return c
785}
786
Colin Cross92b7d582019-03-29 15:32:51 -0700787// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the
788// rule will not have them listed in its dependencies or outputs.
789func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
790 for _, flag := range flags {
791 c.Text(flag)
792 }
793 return c
794}
795
Colin Cross758290d2019-02-01 16:42:32 -0800796// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
797// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
798// outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800799func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
800 return c.Text(flag + arg)
801}
802
Colin Crossc7ed0042019-02-11 14:11:09 -0800803// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to
804// calling FlagWithArg for argument.
805func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand {
806 for _, arg := range args {
807 c.FlagWithArg(flag, arg)
808 }
809 return c
810}
811
Roland Levillain2da5d9a2019-02-27 16:56:41 +0000812// 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 -0800813// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or
814// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800815func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
816 return c.Text(flag + strings.Join(list, sep))
817}
818
Colin Cross758290d2019-02-01 16:42:32 -0800819// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by
820// RuleBuilder.Tools.
Colin Cross69f59a32019-02-15 10:39:37 -0800821func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800822 c.tools = append(c.tools, path)
Colin Crossba9e4032020-11-24 16:32:22 -0800823 return c.Text(c.PathForTool(path))
824}
825
826// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
827func (c *RuleBuilderCommand) ImplicitTool(path Path) *RuleBuilderCommand {
828 c.tools = append(c.tools, path)
829 return c
830}
831
832// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
833func (c *RuleBuilderCommand) ImplicitTools(paths Paths) *RuleBuilderCommand {
834 c.tools = append(c.tools, paths...)
835 return c
Colin Crossfeec25b2019-01-30 17:32:39 -0800836}
837
Colin Crossee94d6a2019-07-08 17:08:34 -0700838// BuiltTool adds the specified tool path that was built using a host Soong module to the command line. The path will
839// be also added to the dependencies returned by RuleBuilder.Tools.
840//
841// It is equivalent to:
842// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
Colin Crossf1a035e2020-11-16 17:32:30 -0800843func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
844 return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
Colin Crossee94d6a2019-07-08 17:08:34 -0700845}
846
847// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the
848// dependencies returned by RuleBuilder.Tools.
849//
850// It is equivalent to:
851// cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
852func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
853 return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
854}
855
Colin Cross758290d2019-02-01 16:42:32 -0800856// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
857// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800858func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700859 return c.Text(c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800860}
861
Colin Cross758290d2019-02-01 16:42:32 -0800862// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
863// dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800864func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800865 for _, path := range paths {
866 c.Input(path)
867 }
868 return c
869}
870
871// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
872// command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800873func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400874 c.addImplicit(path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800875 return c
876}
877
Colin Cross758290d2019-02-01 16:42:32 -0800878// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
879// command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800880func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700881 for _, path := range paths {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400882 c.addImplicit(path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700883 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800884 return c
885}
886
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400887// GetImplicits returns the command's implicit inputs.
888func (c *RuleBuilderCommand) GetImplicits() Paths {
889 return c.implicits
890}
891
Colin Crossda71eda2020-02-21 16:55:19 -0800892// OrderOnly adds the specified input path to the dependencies returned by RuleBuilder.OrderOnlys
893// without modifying the command line.
894func (c *RuleBuilderCommand) OrderOnly(path Path) *RuleBuilderCommand {
895 c.addOrderOnly(path)
896 return c
897}
898
899// OrderOnlys adds the specified input paths to the dependencies returned by RuleBuilder.OrderOnlys
900// without modifying the command line.
901func (c *RuleBuilderCommand) OrderOnlys(paths Paths) *RuleBuilderCommand {
902 for _, path := range paths {
903 c.addOrderOnly(path)
904 }
905 return c
906}
907
Colin Cross758290d2019-02-01 16:42:32 -0800908// Output adds the specified output path to the command line. The path will also be added to the outputs returned by
909// RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800910func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800911 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -0800912 return c.Text(c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800913}
914
Colin Cross758290d2019-02-01 16:42:32 -0800915// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
916// the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800917func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800918 for _, path := range paths {
919 c.Output(path)
920 }
921 return c
922}
923
Dan Willemsen1945a4b2019-06-04 17:10:41 -0700924// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
925// and will be the temporary output directory managed by sbox, not the final one.
926func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand {
Colin Crossf1a035e2020-11-16 17:32:30 -0800927 if !c.rule.sbox {
Dan Willemsen1945a4b2019-06-04 17:10:41 -0700928 panic("OutputDir only valid with Sbox")
929 }
Colin Cross3d680512020-11-13 16:23:53 -0800930 return c.Text(sboxOutDir)
Dan Willemsen1945a4b2019-06-04 17:10:41 -0700931}
932
Colin Cross1d2cf042019-03-29 15:33:06 -0700933// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
934// line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles are added to
935// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
936func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
937 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -0800938 return c.Text(c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -0700939}
940
Colin Cross758290d2019-02-01 16:42:32 -0800941// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
942// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800943func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800944 c.outputs = append(c.outputs, path)
945 return c
946}
947
Colin Cross758290d2019-02-01 16:42:32 -0800948// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
949// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800950func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800951 c.outputs = append(c.outputs, paths...)
952 return c
953}
954
Jingwen Chence679d22020-09-23 04:30:02 +0000955// ImplicitSymlinkOutput declares the specified path as an implicit output that
956// will be a symlink instead of a regular file. Does not modify the command
957// line.
958func (c *RuleBuilderCommand) ImplicitSymlinkOutput(path WritablePath) *RuleBuilderCommand {
959 c.symlinkOutputs = append(c.symlinkOutputs, path)
960 return c.ImplicitOutput(path)
961}
962
963// ImplicitSymlinkOutputs declares the specified paths as implicit outputs that
964// will be a symlinks instead of regular files. Does not modify the command
965// line.
966func (c *RuleBuilderCommand) ImplicitSymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
967 for _, path := range paths {
968 c.ImplicitSymlinkOutput(path)
969 }
970 return c
971}
972
973// SymlinkOutput declares the specified path as an output that will be a symlink
974// instead of a regular file. Modifies the command line.
975func (c *RuleBuilderCommand) SymlinkOutput(path WritablePath) *RuleBuilderCommand {
976 c.symlinkOutputs = append(c.symlinkOutputs, path)
977 return c.Output(path)
978}
979
980// SymlinkOutputsl declares the specified paths as outputs that will be symlinks
981// instead of regular files. Modifies the command line.
982func (c *RuleBuilderCommand) SymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
983 for _, path := range paths {
984 c.SymlinkOutput(path)
985 }
986 return c
987}
988
Colin Cross1d2cf042019-03-29 15:33:06 -0700989// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
990// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles
991// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
992// depfiles together.
993func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand {
994 c.depFiles = append(c.depFiles, path)
995 return c
996}
997
Colin Cross758290d2019-02-01 16:42:32 -0800998// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
999// will also be added to the dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001000func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001001 return c.Text(flag + c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001002}
1003
Colin Cross758290d2019-02-01 16:42:32 -08001004// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
1005// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
1006// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001007func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001008 strs := make([]string, len(paths))
1009 for i, path := range paths {
1010 strs[i] = c.addInput(path)
1011 }
1012 return c.FlagWithList(flag, strs, sep)
Colin Crossfeec25b2019-01-30 17:32:39 -08001013}
1014
Colin Cross758290d2019-02-01 16:42:32 -08001015// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
1016// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for
1017// each input path.
Colin Cross69f59a32019-02-15 10:39:37 -08001018func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001019 for _, path := range paths {
1020 c.FlagWithInput(flag, path)
1021 }
1022 return c
1023}
1024
1025// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path
1026// will also be added to the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001027func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001028 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001029 return c.Text(flag + c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001030}
1031
Colin Cross1d2cf042019-03-29 15:33:06 -07001032// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path
1033// will also be added to the outputs returned by RuleBuilder.Outputs.
1034func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
1035 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001036 return c.Text(flag + c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -07001037}
1038
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001039// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with no separator
Colin Cross70c47412021-03-12 17:48:14 -08001040// between them. The paths will be written to the rspfile. If sbox is enabled, the rspfile must
1041// be outside the sbox directory.
1042func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, rspFile WritablePath, paths Paths) *RuleBuilderCommand {
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001043 if c.rspFileInputs != nil {
1044 panic("FlagWithRspFileInputList cannot be called if rsp file inputs have already been provided")
1045 }
1046
1047 // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
1048 // generated.
1049 if paths == nil {
1050 paths = Paths{}
1051 }
1052
1053 c.rspFileInputs = paths
Colin Cross70c47412021-03-12 17:48:14 -08001054 c.rspFile = rspFile
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001055
Colin Cross70c47412021-03-12 17:48:14 -08001056 if c.rule.sbox {
1057 if _, isRel, _ := maybeRelErr(c.rule.outDir.String(), rspFile.String()); isRel {
1058 panic(fmt.Errorf("FlagWithRspFileInputList rspfile %q must not be inside out dir %q",
1059 rspFile.String(), c.rule.outDir.String()))
1060 }
1061 }
1062
1063 c.FlagWithArg(flag, rspFile.String())
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001064 return c
1065}
1066
Colin Cross758290d2019-02-01 16:42:32 -08001067// String returns the command line.
1068func (c *RuleBuilderCommand) String() string {
Colin Crosscfec40c2019-07-08 17:07:18 -07001069 return c.buf.String()
Colin Cross758290d2019-02-01 16:42:32 -08001070}
Colin Cross1d2cf042019-03-29 15:33:06 -07001071
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001072// String returns the command line.
1073func (c *RuleBuilderCommand) NinjaEscapedString() string {
1074 return ninjaEscapeExceptForSpans(c.String(), c.unescapedSpans)
1075}
1076
Colin Crosse16ce362020-11-12 08:29:30 -08001077// RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox()
1078// and returns sbox testproto generated by the RuleBuilder.
1079func RuleBuilderSboxProtoForTests(t *testing.T, params TestingBuildParams) *sbox_proto.Manifest {
1080 t.Helper()
1081 content := ContentFromFileRuleForTests(t, params)
1082 manifest := sbox_proto.Manifest{}
1083 err := proto.UnmarshalText(content, &manifest)
1084 if err != nil {
1085 t.Fatalf("failed to unmarshal manifest: %s", err.Error())
1086 }
1087 return &manifest
1088}
1089
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001090func ninjaEscapeExceptForSpans(s string, spans [][2]int) string {
1091 if len(spans) == 0 {
1092 return proptools.NinjaEscape(s)
1093 }
1094
1095 sb := strings.Builder{}
1096 sb.Grow(len(s) * 11 / 10)
1097
1098 i := 0
1099 for _, span := range spans {
1100 sb.WriteString(proptools.NinjaEscape(s[i:span[0]]))
1101 sb.WriteString(s[span[0]:span[1]])
1102 i = span[1]
1103 }
1104 sb.WriteString(proptools.NinjaEscape(s[i:]))
1105
1106 return sb.String()
1107}
1108
Colin Cross1d2cf042019-03-29 15:33:06 -07001109func ninjaNameEscape(s string) string {
1110 b := []byte(s)
1111 escaped := false
1112 for i, c := range b {
1113 valid := (c >= 'a' && c <= 'z') ||
1114 (c >= 'A' && c <= 'Z') ||
1115 (c >= '0' && c <= '9') ||
1116 (c == '_') ||
1117 (c == '-') ||
1118 (c == '.')
1119 if !valid {
1120 b[i] = '_'
1121 escaped = true
1122 }
1123 }
1124 if escaped {
1125 s = string(b)
1126 }
1127 return s
1128}
Colin Cross3d680512020-11-13 16:23:53 -08001129
1130// hashSrcFiles returns a hash of the list of source files. It is used to ensure the command line
1131// or the sbox textproto manifest change even if the input files are not listed on the command line.
1132func hashSrcFiles(srcFiles Paths) string {
1133 h := sha256.New()
1134 srcFileList := strings.Join(srcFiles.Strings(), "\n")
1135 h.Write([]byte(srcFileList))
1136 return fmt.Sprintf("%x", h.Sum(nil))
1137}
Colin Crossf1a035e2020-11-16 17:32:30 -08001138
1139// BuilderContextForTesting returns a BuilderContext for the given config that can be used for tests
1140// that need to call methods that take a BuilderContext.
1141func BuilderContextForTesting(config Config) BuilderContext {
1142 pathCtx := PathContextForTesting(config)
1143 return builderContextForTests{
1144 PathContext: pathCtx,
1145 }
1146}
1147
1148type builderContextForTests struct {
1149 PathContext
1150}
1151
1152func (builderContextForTests) Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule {
1153 return nil
1154}
1155func (builderContextForTests) Build(PackageContext, BuildParams) {}