blob: 0d8e2b7c99a0489be6300eca52b43b4109c44f90 [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"
Colin Crossef972742021-03-12 17:24:45 -080030 "android/soong/remoteexec"
Dan Willemsen633c5022019-04-12 11:11:38 -070031 "android/soong/shared"
Colin Crossfeec25b2019-01-30 17:32:39 -080032)
33
Colin Crosse16ce362020-11-12 08:29:30 -080034const sboxSandboxBaseDir = "__SBOX_SANDBOX_DIR__"
35const sboxOutSubDir = "out"
Colin Crossba9e4032020-11-24 16:32:22 -080036const sboxToolsSubDir = "tools"
Colin Crosse16ce362020-11-12 08:29:30 -080037const sboxOutDir = sboxSandboxBaseDir + "/" + sboxOutSubDir
Colin Cross3d680512020-11-13 16:23:53 -080038
Colin Cross758290d2019-02-01 16:42:32 -080039// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
40// graph.
Colin Crossfeec25b2019-01-30 17:32:39 -080041type RuleBuilder struct {
Colin Crossf1a035e2020-11-16 17:32:30 -080042 pctx PackageContext
43 ctx BuilderContext
44
Colin Crosse16ce362020-11-12 08:29:30 -080045 commands []*RuleBuilderCommand
46 installs RuleBuilderInstalls
47 temporariesSet map[WritablePath]bool
48 restat bool
49 sbox bool
50 highmem bool
51 remoteable RemoteRuleSupports
Colin Crossef972742021-03-12 17:24:45 -080052 rbeParams *remoteexec.REParams
Colin Crossf1a035e2020-11-16 17:32:30 -080053 outDir WritablePath
Colin Crossba9e4032020-11-24 16:32:22 -080054 sboxTools bool
Colin Crossab020a72021-03-12 17:52:23 -080055 sboxInputs bool
Colin Crosse16ce362020-11-12 08:29:30 -080056 sboxManifestPath WritablePath
57 missingDeps []string
Colin Crossfeec25b2019-01-30 17:32:39 -080058}
59
Colin Cross758290d2019-02-01 16:42:32 -080060// NewRuleBuilder returns a newly created RuleBuilder.
Colin Crossf1a035e2020-11-16 17:32:30 -080061func NewRuleBuilder(pctx PackageContext, ctx BuilderContext) *RuleBuilder {
Colin Cross5cb5b092019-02-02 21:25:18 -080062 return &RuleBuilder{
Colin Crossf1a035e2020-11-16 17:32:30 -080063 pctx: pctx,
64 ctx: ctx,
Colin Cross69f59a32019-02-15 10:39:37 -080065 temporariesSet: make(map[WritablePath]bool),
Colin Cross5cb5b092019-02-02 21:25:18 -080066 }
Colin Cross758290d2019-02-01 16:42:32 -080067}
68
69// RuleBuilderInstall is a tuple of install from and to locations.
70type RuleBuilderInstall struct {
Colin Cross69f59a32019-02-15 10:39:37 -080071 From Path
72 To string
Colin Cross758290d2019-02-01 16:42:32 -080073}
74
Colin Crossdeabb942019-02-11 14:11:09 -080075type RuleBuilderInstalls []RuleBuilderInstall
76
77// String returns the RuleBuilderInstalls in the form used by $(call copy-many-files) in Make, a space separated
78// list of from:to tuples.
79func (installs RuleBuilderInstalls) String() string {
80 sb := strings.Builder{}
81 for i, install := range installs {
82 if i != 0 {
83 sb.WriteRune(' ')
84 }
Colin Cross69f59a32019-02-15 10:39:37 -080085 sb.WriteString(install.From.String())
Colin Crossdeabb942019-02-11 14:11:09 -080086 sb.WriteRune(':')
87 sb.WriteString(install.To)
88 }
89 return sb.String()
90}
91
Colin Cross0d2f40a2019-02-05 22:31:15 -080092// MissingDeps adds modules to the list of missing dependencies. If MissingDeps
93// is called with a non-empty input, any call to Build will result in a rule
94// that will print an error listing the missing dependencies and fail.
95// MissingDeps should only be called if Config.AllowMissingDependencies() is
96// true.
97func (r *RuleBuilder) MissingDeps(missingDeps []string) {
98 r.missingDeps = append(r.missingDeps, missingDeps...)
99}
100
Colin Cross758290d2019-02-01 16:42:32 -0800101// 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 -0700102//
103// Restat is not compatible with Sbox()
Colin Crossfeec25b2019-01-30 17:32:39 -0800104func (r *RuleBuilder) Restat() *RuleBuilder {
Dan Willemsen633c5022019-04-12 11:11:38 -0700105 if r.sbox {
106 panic("Restat() is not compatible with Sbox()")
107 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800108 r.restat = true
109 return r
110}
111
Colin Cross8b8bec32019-11-15 13:18:43 -0800112// HighMem marks the rule as a high memory rule, which will limit how many run in parallel with other high memory
113// rules.
114func (r *RuleBuilder) HighMem() *RuleBuilder {
115 r.highmem = true
116 return r
117}
118
119// Remoteable marks the rule as supporting remote execution.
120func (r *RuleBuilder) Remoteable(supports RemoteRuleSupports) *RuleBuilder {
121 r.remoteable = supports
122 return r
123}
124
Colin Crossef972742021-03-12 17:24:45 -0800125// Rewrapper marks the rule as running inside rewrapper using the given params in order to support
126// running on RBE. During RuleBuilder.Build the params will be combined with the inputs, outputs
127// and tools known to RuleBuilder to prepend an appropriate rewrapper command line to the rule's
128// command line.
129func (r *RuleBuilder) Rewrapper(params *remoteexec.REParams) *RuleBuilder {
130 if !r.sboxInputs {
131 panic(fmt.Errorf("RuleBuilder.Rewrapper must be called after RuleBuilder.SandboxInputs"))
132 }
133 r.rbeParams = params
134 return r
135}
136
Colin Crosse16ce362020-11-12 08:29:30 -0800137// Sbox marks the rule as needing to be wrapped by sbox. The outputDir should point to the output
138// directory that sbox will wipe. It should not be written to by any other rule. manifestPath should
139// point to a location where sbox's manifest will be written and must be outside outputDir. sbox
140// will ensure that all outputs have been written, and will discard any output files that were not
141// specified.
Dan Willemsen633c5022019-04-12 11:11:38 -0700142//
143// Sbox is not compatible with Restat()
Colin Crosse16ce362020-11-12 08:29:30 -0800144func (r *RuleBuilder) Sbox(outputDir WritablePath, manifestPath WritablePath) *RuleBuilder {
Dan Willemsen633c5022019-04-12 11:11:38 -0700145 if r.sbox {
146 panic("Sbox() may not be called more than once")
147 }
148 if len(r.commands) > 0 {
149 panic("Sbox() may not be called after Command()")
150 }
151 if r.restat {
152 panic("Sbox() is not compatible with Restat()")
153 }
154 r.sbox = true
Colin Crossf1a035e2020-11-16 17:32:30 -0800155 r.outDir = outputDir
Colin Crosse16ce362020-11-12 08:29:30 -0800156 r.sboxManifestPath = manifestPath
Dan Willemsen633c5022019-04-12 11:11:38 -0700157 return r
158}
159
Colin Crossba9e4032020-11-24 16:32:22 -0800160// SandboxTools enables tool sandboxing for the rule by copying any referenced tools into the
161// sandbox.
162func (r *RuleBuilder) SandboxTools() *RuleBuilder {
163 if !r.sbox {
164 panic("SandboxTools() must be called after Sbox()")
165 }
166 if len(r.commands) > 0 {
167 panic("SandboxTools() may not be called after Command()")
168 }
169 r.sboxTools = true
170 return r
171}
172
Colin Crossab020a72021-03-12 17:52:23 -0800173// SandboxInputs enables input sandboxing for the rule by copying any referenced inputs into the
174// sandbox. It also implies SandboxTools().
175//
176// Sandboxing inputs requires RuleBuilder to be aware of all references to input paths. Paths
177// that are passed to RuleBuilder outside of the methods that expect inputs, for example
178// FlagWithArg, must use RuleBuilderCommand.PathForInput to translate the path to one that matches
179// the sandbox layout.
180func (r *RuleBuilder) SandboxInputs() *RuleBuilder {
181 if !r.sbox {
182 panic("SandboxInputs() must be called after Sbox()")
183 }
184 if len(r.commands) > 0 {
185 panic("SandboxInputs() may not be called after Command()")
186 }
187 r.sboxTools = true
188 r.sboxInputs = true
189 return r
190}
191
Colin Cross758290d2019-02-01 16:42:32 -0800192// Install associates an output of the rule with an install location, which can be retrieved later using
193// RuleBuilder.Installs.
Colin Cross69f59a32019-02-15 10:39:37 -0800194func (r *RuleBuilder) Install(from Path, to string) {
Colin Crossfeec25b2019-01-30 17:32:39 -0800195 r.installs = append(r.installs, RuleBuilderInstall{from, to})
196}
197
Colin Cross758290d2019-02-01 16:42:32 -0800198// Command returns a new RuleBuilderCommand for the rule. The commands will be ordered in the rule by when they were
199// created by this method. That can be mutated through their methods in any order, as long as the mutations do not
200// race with any call to Build.
Colin Crossfeec25b2019-01-30 17:32:39 -0800201func (r *RuleBuilder) Command() *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700202 command := &RuleBuilderCommand{
Colin Crossf1a035e2020-11-16 17:32:30 -0800203 rule: r,
Dan Willemsen633c5022019-04-12 11:11:38 -0700204 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800205 r.commands = append(r.commands, command)
206 return command
207}
208
Colin Cross5cb5b092019-02-02 21:25:18 -0800209// Temporary marks an output of a command as an intermediate file that will be used as an input to another command
210// in the same rule, and should not be listed in Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800211func (r *RuleBuilder) Temporary(path WritablePath) {
Colin Cross5cb5b092019-02-02 21:25:18 -0800212 r.temporariesSet[path] = true
213}
214
215// DeleteTemporaryFiles adds a command to the rule that deletes any outputs that have been marked using Temporary
216// when the rule runs. DeleteTemporaryFiles should be called after all calls to Temporary.
217func (r *RuleBuilder) DeleteTemporaryFiles() {
Colin Cross69f59a32019-02-15 10:39:37 -0800218 var temporariesList WritablePaths
Colin Cross5cb5b092019-02-02 21:25:18 -0800219
220 for intermediate := range r.temporariesSet {
221 temporariesList = append(temporariesList, intermediate)
222 }
Colin Cross69f59a32019-02-15 10:39:37 -0800223
224 sort.Slice(temporariesList, func(i, j int) bool {
225 return temporariesList[i].String() < temporariesList[j].String()
226 })
Colin Cross5cb5b092019-02-02 21:25:18 -0800227
228 r.Command().Text("rm").Flag("-f").Outputs(temporariesList)
229}
230
Colin Crossda71eda2020-02-21 16:55:19 -0800231// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
Colin Cross3d680512020-11-13 16:23:53 -0800232// input paths, such as RuleBuilderCommand.Input, RuleBuilderCommand.Implicit, or
Colin Crossda71eda2020-02-21 16:55:19 -0800233// RuleBuilderCommand.FlagWithInput. Inputs to a command that are also outputs of another command
234// in the same RuleBuilder are filtered out. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800235func (r *RuleBuilder) Inputs() Paths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800236 outputs := r.outputSet()
Dan Willemsen633c5022019-04-12 11:11:38 -0700237 depFiles := r.depFileSet()
Colin Crossfeec25b2019-01-30 17:32:39 -0800238
Colin Cross69f59a32019-02-15 10:39:37 -0800239 inputs := make(map[string]Path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800240 for _, c := range r.commands {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400241 for _, input := range append(c.inputs, c.implicits...) {
Dan Willemsen633c5022019-04-12 11:11:38 -0700242 inputStr := input.String()
243 if _, isOutput := outputs[inputStr]; !isOutput {
244 if _, isDepFile := depFiles[inputStr]; !isDepFile {
245 inputs[input.String()] = input
246 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800247 }
248 }
249 }
250
Colin Cross69f59a32019-02-15 10:39:37 -0800251 var inputList Paths
252 for _, input := range inputs {
Colin Crossfeec25b2019-01-30 17:32:39 -0800253 inputList = append(inputList, input)
254 }
Colin Cross69f59a32019-02-15 10:39:37 -0800255
256 sort.Slice(inputList, func(i, j int) bool {
257 return inputList[i].String() < inputList[j].String()
258 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800259
260 return inputList
261}
262
Colin Crossda71eda2020-02-21 16:55:19 -0800263// OrderOnlys returns the list of paths that were passed to the RuleBuilderCommand.OrderOnly or
264// RuleBuilderCommand.OrderOnlys. The list is sorted and duplicates removed.
265func (r *RuleBuilder) OrderOnlys() Paths {
266 orderOnlys := make(map[string]Path)
267 for _, c := range r.commands {
268 for _, orderOnly := range c.orderOnlys {
269 orderOnlys[orderOnly.String()] = orderOnly
270 }
271 }
272
273 var orderOnlyList Paths
274 for _, orderOnly := range orderOnlys {
275 orderOnlyList = append(orderOnlyList, orderOnly)
276 }
277
278 sort.Slice(orderOnlyList, func(i, j int) bool {
279 return orderOnlyList[i].String() < orderOnlyList[j].String()
280 })
281
282 return orderOnlyList
283}
284
Colin Cross69f59a32019-02-15 10:39:37 -0800285func (r *RuleBuilder) outputSet() map[string]WritablePath {
286 outputs := make(map[string]WritablePath)
Colin Crossfeec25b2019-01-30 17:32:39 -0800287 for _, c := range r.commands {
288 for _, output := range c.outputs {
Colin Cross69f59a32019-02-15 10:39:37 -0800289 outputs[output.String()] = output
Colin Crossfeec25b2019-01-30 17:32:39 -0800290 }
291 }
292 return outputs
293}
294
Colin Crossda71eda2020-02-21 16:55:19 -0800295// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
296// output paths, such as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or
297// RuleBuilderCommand.FlagWithInput. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800298func (r *RuleBuilder) Outputs() WritablePaths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800299 outputs := r.outputSet()
300
Colin Cross69f59a32019-02-15 10:39:37 -0800301 var outputList WritablePaths
302 for _, output := range outputs {
Colin Cross5cb5b092019-02-02 21:25:18 -0800303 if !r.temporariesSet[output] {
304 outputList = append(outputList, output)
305 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800306 }
Colin Cross69f59a32019-02-15 10:39:37 -0800307
308 sort.Slice(outputList, func(i, j int) bool {
309 return outputList[i].String() < outputList[j].String()
310 })
311
Colin Crossfeec25b2019-01-30 17:32:39 -0800312 return outputList
313}
314
Jingwen Chence679d22020-09-23 04:30:02 +0000315func (r *RuleBuilder) symlinkOutputSet() map[string]WritablePath {
316 symlinkOutputs := make(map[string]WritablePath)
317 for _, c := range r.commands {
318 for _, symlinkOutput := range c.symlinkOutputs {
319 symlinkOutputs[symlinkOutput.String()] = symlinkOutput
320 }
321 }
322 return symlinkOutputs
323}
324
325// SymlinkOutputs returns the list of paths that the executor (Ninja) would
326// verify, after build edge completion, that:
327//
328// 1) Created output symlinks match the list of paths in this list exactly (no more, no fewer)
329// 2) Created output files are *not* declared in this list.
330//
331// These symlink outputs are expected to be a subset of outputs or implicit
332// outputs, or they would fail validation at build param construction time
333// later, to support other non-rule-builder approaches for constructing
334// statements.
335func (r *RuleBuilder) SymlinkOutputs() WritablePaths {
336 symlinkOutputs := r.symlinkOutputSet()
337
338 var symlinkOutputList WritablePaths
339 for _, symlinkOutput := range symlinkOutputs {
340 symlinkOutputList = append(symlinkOutputList, symlinkOutput)
341 }
342
343 sort.Slice(symlinkOutputList, func(i, j int) bool {
344 return symlinkOutputList[i].String() < symlinkOutputList[j].String()
345 })
346
347 return symlinkOutputList
348}
349
Dan Willemsen633c5022019-04-12 11:11:38 -0700350func (r *RuleBuilder) depFileSet() map[string]WritablePath {
351 depFiles := make(map[string]WritablePath)
352 for _, c := range r.commands {
353 for _, depFile := range c.depFiles {
354 depFiles[depFile.String()] = depFile
355 }
356 }
357 return depFiles
358}
359
Colin Cross1d2cf042019-03-29 15:33:06 -0700360// DepFiles returns the list of paths that were passed to the RuleBuilderCommand methods that take depfile paths, such
361// as RuleBuilderCommand.DepFile or RuleBuilderCommand.FlagWithDepFile.
362func (r *RuleBuilder) DepFiles() WritablePaths {
363 var depFiles WritablePaths
364
365 for _, c := range r.commands {
366 for _, depFile := range c.depFiles {
367 depFiles = append(depFiles, depFile)
368 }
369 }
370
371 return depFiles
372}
373
Colin Cross758290d2019-02-01 16:42:32 -0800374// Installs returns the list of tuples passed to Install.
Colin Crossdeabb942019-02-11 14:11:09 -0800375func (r *RuleBuilder) Installs() RuleBuilderInstalls {
376 return append(RuleBuilderInstalls(nil), r.installs...)
Colin Crossfeec25b2019-01-30 17:32:39 -0800377}
378
Colin Cross69f59a32019-02-15 10:39:37 -0800379func (r *RuleBuilder) toolsSet() map[string]Path {
380 tools := make(map[string]Path)
Colin Cross5cb5b092019-02-02 21:25:18 -0800381 for _, c := range r.commands {
382 for _, tool := range c.tools {
Colin Cross69f59a32019-02-15 10:39:37 -0800383 tools[tool.String()] = tool
Colin Cross5cb5b092019-02-02 21:25:18 -0800384 }
385 }
386
387 return tools
388}
389
Colin Crossda71eda2020-02-21 16:55:19 -0800390// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method. The
391// list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800392func (r *RuleBuilder) Tools() Paths {
Colin Cross5cb5b092019-02-02 21:25:18 -0800393 toolsSet := r.toolsSet()
394
Colin Cross69f59a32019-02-15 10:39:37 -0800395 var toolsList Paths
396 for _, tool := range toolsSet {
Colin Cross5cb5b092019-02-02 21:25:18 -0800397 toolsList = append(toolsList, tool)
Colin Crossfeec25b2019-01-30 17:32:39 -0800398 }
Colin Cross69f59a32019-02-15 10:39:37 -0800399
400 sort.Slice(toolsList, func(i, j int) bool {
401 return toolsList[i].String() < toolsList[j].String()
402 })
403
Colin Cross5cb5b092019-02-02 21:25:18 -0800404 return toolsList
Colin Crossfeec25b2019-01-30 17:32:39 -0800405}
406
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700407// RspFileInputs returns the list of paths that were passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
408func (r *RuleBuilder) RspFileInputs() Paths {
409 var rspFileInputs Paths
410 for _, c := range r.commands {
411 if c.rspFileInputs != nil {
412 if rspFileInputs != nil {
413 panic("Multiple commands in a rule may not have rsp file inputs")
414 }
415 rspFileInputs = c.rspFileInputs
416 }
417 }
418
419 return rspFileInputs
420}
421
Colin Cross70c47412021-03-12 17:48:14 -0800422// RspFile returns the path to the rspfile that was passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
423func (r *RuleBuilder) RspFile() WritablePath {
424 var rspFile WritablePath
425 for _, c := range r.commands {
426 if c.rspFile != nil {
427 if rspFile != nil {
428 panic("Multiple commands in a rule may not have rsp file inputs")
429 }
430 rspFile = c.rspFile
431 }
432 }
433
434 return rspFile
435}
436
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700437// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
Colin Crossfeec25b2019-01-30 17:32:39 -0800438func (r *RuleBuilder) Commands() []string {
439 var commands []string
440 for _, c := range r.commands {
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700441 commands = append(commands, c.String())
442 }
443 return commands
444}
445
Colin Cross758290d2019-02-01 16:42:32 -0800446// BuilderContext is a subset of ModuleContext and SingletonContext.
Colin Cross786cd6d2019-02-01 16:41:11 -0800447type BuilderContext interface {
448 PathContext
449 Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
450 Build(PackageContext, BuildParams)
451}
452
Colin Cross758290d2019-02-01 16:42:32 -0800453var _ BuilderContext = ModuleContext(nil)
454var _ BuilderContext = SingletonContext(nil)
455
Colin Crossf1a035e2020-11-16 17:32:30 -0800456func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700457 return r.Command().
Colin Crossf1a035e2020-11-16 17:32:30 -0800458 BuiltTool("dep_fixer").
Dan Willemsen633c5022019-04-12 11:11:38 -0700459 Inputs(depFiles.Paths())
Colin Cross1d2cf042019-03-29 15:33:06 -0700460}
461
Colin Crossab020a72021-03-12 17:52:23 -0800462// composeRspFileContent returns a string that will serve as the contents of the rsp file to pass
463// the listed input files to the command running in the sandbox.
464func (r *RuleBuilder) composeRspFileContent(rspFileInputs Paths) string {
465 if r.sboxInputs {
466 if len(rspFileInputs) > 0 {
467 // When SandboxInputs is used the paths need to be rewritten to be relative to the sandbox
468 // directory so that they are valid after sbox chdirs into the sandbox directory.
469 return proptools.NinjaEscape(strings.Join(r.sboxPathsForInputsRel(rspFileInputs), " "))
470 } else {
471 // If the list of inputs is empty fall back to "$in" so that the rspfilecontent Ninja
472 // variable is set to something non-empty, otherwise ninja will complain. The inputs
473 // will be empty (all the non-rspfile inputs are implicits), so $in will evaluate to
474 // an empty string.
475 return "$in"
476 }
477 } else {
478 return "$in"
479 }
480}
481
Colin Cross758290d2019-02-01 16:42:32 -0800482// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
483// Outputs.
Colin Crossf1a035e2020-11-16 17:32:30 -0800484func (r *RuleBuilder) Build(name string, desc string) {
Colin Cross1d2cf042019-03-29 15:33:06 -0700485 name = ninjaNameEscape(name)
486
Colin Cross0d2f40a2019-02-05 22:31:15 -0800487 if len(r.missingDeps) > 0 {
Colin Crossf1a035e2020-11-16 17:32:30 -0800488 r.ctx.Build(pctx, BuildParams{
Colin Cross0d2f40a2019-02-05 22:31:15 -0800489 Rule: ErrorRule,
Colin Cross69f59a32019-02-15 10:39:37 -0800490 Outputs: r.Outputs(),
Colin Crossda71eda2020-02-21 16:55:19 -0800491 OrderOnly: r.OrderOnlys(),
Colin Cross0d2f40a2019-02-05 22:31:15 -0800492 Description: desc,
493 Args: map[string]string{
494 "error": "missing dependencies: " + strings.Join(r.missingDeps, ", "),
495 },
496 })
497 return
498 }
499
Colin Cross1d2cf042019-03-29 15:33:06 -0700500 var depFile WritablePath
501 var depFormat blueprint.Deps
502 if depFiles := r.DepFiles(); len(depFiles) > 0 {
503 depFile = depFiles[0]
504 depFormat = blueprint.DepsGCC
505 if len(depFiles) > 1 {
506 // Add a command locally that merges all depfiles together into the first depfile.
Colin Crossf1a035e2020-11-16 17:32:30 -0800507 r.depFileMergerCmd(depFiles)
Dan Willemsen633c5022019-04-12 11:11:38 -0700508
509 if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800510 // Check for Rel() errors, as all depfiles should be in the output dir. Errors
511 // will be reported to the ctx.
Dan Willemsen633c5022019-04-12 11:11:38 -0700512 for _, path := range depFiles[1:] {
Colin Crossf1a035e2020-11-16 17:32:30 -0800513 Rel(r.ctx, r.outDir.String(), path.String())
Dan Willemsen633c5022019-04-12 11:11:38 -0700514 }
515 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700516 }
517 }
518
Dan Willemsen633c5022019-04-12 11:11:38 -0700519 tools := r.Tools()
Colin Crossb70a1a92021-03-12 17:51:32 -0800520 commands := r.Commands()
Dan Willemsen633c5022019-04-12 11:11:38 -0700521 outputs := r.Outputs()
Colin Cross3d680512020-11-13 16:23:53 -0800522 inputs := r.Inputs()
Colin Cross70c47412021-03-12 17:48:14 -0800523 rspFileInputs := r.RspFileInputs()
524 rspFilePath := r.RspFile()
Dan Willemsen633c5022019-04-12 11:11:38 -0700525
526 if len(commands) == 0 {
527 return
528 }
529 if len(outputs) == 0 {
530 panic("No outputs specified from any Commands")
531 }
532
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700533 commandString := strings.Join(commands, " && ")
Dan Willemsen633c5022019-04-12 11:11:38 -0700534
535 if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800536 // If running the command inside sbox, write the rule data out to an sbox
537 // manifest.textproto.
538 manifest := sbox_proto.Manifest{}
539 command := sbox_proto.Command{}
540 manifest.Commands = append(manifest.Commands, &command)
541 command.Command = proto.String(commandString)
Colin Cross151b9ff2020-11-12 08:29:30 -0800542
Colin Cross619b9ab2020-11-20 18:44:31 +0000543 if depFile != nil {
Colin Crosse16ce362020-11-12 08:29:30 -0800544 manifest.OutputDepfile = proto.String(depFile.String())
Colin Cross619b9ab2020-11-20 18:44:31 +0000545 }
546
Colin Crossba9e4032020-11-24 16:32:22 -0800547 // If sandboxing tools is enabled, add copy rules to the manifest to copy each tool
548 // into the sbox directory.
549 if r.sboxTools {
550 for _, tool := range tools {
551 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
552 From: proto.String(tool.String()),
553 To: proto.String(sboxPathForToolRel(r.ctx, tool)),
554 })
555 }
556 for _, c := range r.commands {
557 for _, tool := range c.packagedTools {
558 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
559 From: proto.String(tool.srcPath.String()),
560 To: proto.String(sboxPathForPackagedToolRel(tool)),
561 Executable: proto.Bool(tool.executable),
562 })
563 tools = append(tools, tool.srcPath)
564 }
565 }
566 }
567
Colin Crossab020a72021-03-12 17:52:23 -0800568 // If sandboxing inputs is enabled, add copy rules to the manifest to copy each input
569 // into the sbox directory.
570 if r.sboxInputs {
571 for _, input := range inputs {
572 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
573 From: proto.String(input.String()),
574 To: proto.String(r.sboxPathForInputRel(input)),
575 })
576 }
577
578 // If using an rsp file copy it into the sbox directory.
579 if rspFilePath != nil {
580 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
581 From: proto.String(rspFilePath.String()),
582 To: proto.String(r.sboxPathForInputRel(rspFilePath)),
583 })
584 }
585
586 command.Chdir = proto.Bool(true)
587 }
588
Colin Crosse16ce362020-11-12 08:29:30 -0800589 // Add copy rules to the manifest to copy each output file from the sbox directory.
Colin Crossba9e4032020-11-24 16:32:22 -0800590 // to the output directory after running the commands.
Colin Crosse16ce362020-11-12 08:29:30 -0800591 sboxOutputs := make([]string, len(outputs))
592 for i, output := range outputs {
Colin Crossf1a035e2020-11-16 17:32:30 -0800593 rel := Rel(r.ctx, r.outDir.String(), output.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800594 sboxOutputs[i] = filepath.Join(sboxOutDir, rel)
595 command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{
596 From: proto.String(filepath.Join(sboxOutSubDir, rel)),
597 To: proto.String(output.String()),
598 })
599 }
Colin Cross619b9ab2020-11-20 18:44:31 +0000600
Colin Cross5334edd2021-03-11 17:18:21 -0800601 // Outputs that were marked Temporary will not be checked that they are in the output
602 // directory by the loop above, check them here.
603 for path := range r.temporariesSet {
604 Rel(r.ctx, r.outDir.String(), path.String())
605 }
606
Colin Crosse16ce362020-11-12 08:29:30 -0800607 // Add a hash of the list of input files to the manifest so that the textproto file
608 // changes when the list of input files changes and causes the sbox rule that
609 // depends on it to rerun.
610 command.InputHash = proto.String(hashSrcFiles(inputs))
Colin Cross619b9ab2020-11-20 18:44:31 +0000611
Colin Crosse16ce362020-11-12 08:29:30 -0800612 // Verify that the manifest textproto is not inside the sbox output directory, otherwise
613 // it will get deleted when the sbox rule clears its output directory.
Colin Crossf1a035e2020-11-16 17:32:30 -0800614 _, manifestInOutDir := MaybeRel(r.ctx, r.outDir.String(), r.sboxManifestPath.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800615 if manifestInOutDir {
Colin Crossf1a035e2020-11-16 17:32:30 -0800616 ReportPathErrorf(r.ctx, "sbox rule %q manifestPath %q must not be in outputDir %q",
617 name, r.sboxManifestPath.String(), r.outDir.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800618 }
619
620 // Create a rule to write the manifest as a the textproto.
Colin Cross1c217fd2021-03-12 17:24:18 -0800621 WriteFileRule(r.ctx, r.sboxManifestPath, proto.MarshalTextString(&manifest))
Colin Crosse16ce362020-11-12 08:29:30 -0800622
623 // Generate a new string to use as the command line of the sbox rule. This uses
624 // a RuleBuilderCommand as a convenience method of building the command line, then
625 // converts it to a string to replace commandString.
Colin Crossf1a035e2020-11-16 17:32:30 -0800626 sboxCmd := &RuleBuilderCommand{
627 rule: &RuleBuilder{
628 ctx: r.ctx,
629 },
630 }
631 sboxCmd.Text("rm -rf").Output(r.outDir)
Colin Crosse16ce362020-11-12 08:29:30 -0800632 sboxCmd.Text("&&")
Colin Crossf1a035e2020-11-16 17:32:30 -0800633 sboxCmd.BuiltTool("sbox").
634 Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
Colin Crosse16ce362020-11-12 08:29:30 -0800635 Flag("--manifest").Input(r.sboxManifestPath)
636
637 // Replace the command string, and add the sbox tool and manifest textproto to the
638 // dependencies of the final sbox rule.
Colin Crosscfec40c2019-07-08 17:07:18 -0700639 commandString = sboxCmd.buf.String()
Dan Willemsen633c5022019-04-12 11:11:38 -0700640 tools = append(tools, sboxCmd.tools...)
Colin Crosse16ce362020-11-12 08:29:30 -0800641 inputs = append(inputs, sboxCmd.inputs...)
Colin Crossef972742021-03-12 17:24:45 -0800642
643 if r.rbeParams != nil {
644 var remoteInputs []string
645 remoteInputs = append(remoteInputs, inputs.Strings()...)
646 remoteInputs = append(remoteInputs, tools.Strings()...)
647 remoteInputs = append(remoteInputs, rspFileInputs.Strings()...)
648 if rspFilePath != nil {
649 remoteInputs = append(remoteInputs, rspFilePath.String())
650 }
651 inputsListFile := r.sboxManifestPath.ReplaceExtension(r.ctx, "rbe_inputs.list")
652 inputsListContents := rspFileForInputs(remoteInputs)
653 WriteFileRule(r.ctx, inputsListFile, inputsListContents)
654 inputs = append(inputs, inputsListFile)
655
656 r.rbeParams.OutputFiles = outputs.Strings()
657 r.rbeParams.RSPFile = inputsListFile.String()
658 rewrapperCommand := r.rbeParams.NoVarTemplate(r.ctx.Config().RBEWrapper())
659 commandString = rewrapperCommand + " bash -c '" + strings.ReplaceAll(commandString, `'`, `'\''`) + "'"
660 }
Colin Cross3d680512020-11-13 16:23:53 -0800661 } else {
662 // If not using sbox the rule will run the command directly, put the hash of the
663 // list of input files in a comment at the end of the command line to ensure ninja
664 // reruns the rule when the list of input files changes.
665 commandString += " # hash of input list: " + hashSrcFiles(inputs)
Dan Willemsen633c5022019-04-12 11:11:38 -0700666 }
667
Colin Cross1d2cf042019-03-29 15:33:06 -0700668 // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
Colin Cross70c47412021-03-12 17:48:14 -0800669 // ImplicitOutputs. RuleBuilder doesn't use "$out", so the distinction between Outputs and
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700670 // ImplicitOutputs doesn't matter.
Dan Willemsen633c5022019-04-12 11:11:38 -0700671 output := outputs[0]
672 implicitOutputs := outputs[1:]
Colin Cross1d2cf042019-03-29 15:33:06 -0700673
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700674 var rspFile, rspFileContent string
Colin Cross70c47412021-03-12 17:48:14 -0800675 if rspFilePath != nil {
676 rspFile = rspFilePath.String()
Colin Crossab020a72021-03-12 17:52:23 -0800677 rspFileContent = r.composeRspFileContent(rspFileInputs)
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700678 }
679
Colin Cross8b8bec32019-11-15 13:18:43 -0800680 var pool blueprint.Pool
Colin Crossf1a035e2020-11-16 17:32:30 -0800681 if r.ctx.Config().UseGoma() && r.remoteable.Goma {
Colin Cross8b8bec32019-11-15 13:18:43 -0800682 // 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 -0800683 } else if r.ctx.Config().UseRBE() && r.remoteable.RBE {
Ramy Medhat944839a2020-03-31 22:14:52 -0400684 // When USE_RBE=true is set and the rule is supported by RBE, use the remotePool.
685 pool = remotePool
Colin Cross8b8bec32019-11-15 13:18:43 -0800686 } else if r.highmem {
687 pool = highmemPool
Colin Crossf1a035e2020-11-16 17:32:30 -0800688 } else if r.ctx.Config().UseRemoteBuild() {
Colin Cross8b8bec32019-11-15 13:18:43 -0800689 pool = localPool
690 }
691
Colin Crossf1a035e2020-11-16 17:32:30 -0800692 r.ctx.Build(r.pctx, BuildParams{
693 Rule: r.ctx.Rule(pctx, name, blueprint.RuleParams{
Colin Crossb70a1a92021-03-12 17:51:32 -0800694 Command: proptools.NinjaEscape(commandString),
Colin Cross45029782021-03-16 16:49:52 -0700695 CommandDeps: proptools.NinjaEscapeList(tools.Strings()),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700696 Restat: r.restat,
Colin Cross45029782021-03-16 16:49:52 -0700697 Rspfile: proptools.NinjaEscape(rspFile),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700698 RspfileContent: rspFileContent,
Colin Cross8b8bec32019-11-15 13:18:43 -0800699 Pool: pool,
Dan Willemsen633c5022019-04-12 11:11:38 -0700700 }),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700701 Inputs: rspFileInputs,
Colin Cross3d680512020-11-13 16:23:53 -0800702 Implicits: inputs,
Dan Willemsen633c5022019-04-12 11:11:38 -0700703 Output: output,
704 ImplicitOutputs: implicitOutputs,
Jingwen Chence679d22020-09-23 04:30:02 +0000705 SymlinkOutputs: r.SymlinkOutputs(),
Dan Willemsen633c5022019-04-12 11:11:38 -0700706 Depfile: depFile,
707 Deps: depFormat,
708 Description: desc,
709 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800710}
711
Colin Cross758290d2019-02-01 16:42:32 -0800712// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
713// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the
714// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
715// space as a separator from the previous method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800716type RuleBuilderCommand struct {
Colin Crossf1a035e2020-11-16 17:32:30 -0800717 rule *RuleBuilder
718
Jingwen Chence679d22020-09-23 04:30:02 +0000719 buf strings.Builder
720 inputs Paths
721 implicits Paths
722 orderOnlys Paths
723 outputs WritablePaths
724 symlinkOutputs WritablePaths
725 depFiles WritablePaths
726 tools Paths
Colin Crossba9e4032020-11-24 16:32:22 -0800727 packagedTools []PackagingSpec
Jingwen Chence679d22020-09-23 04:30:02 +0000728 rspFileInputs Paths
Colin Cross70c47412021-03-12 17:48:14 -0800729 rspFile WritablePath
Dan Willemsen633c5022019-04-12 11:11:38 -0700730}
731
732func (c *RuleBuilderCommand) addInput(path Path) string {
Dan Willemsen633c5022019-04-12 11:11:38 -0700733 c.inputs = append(c.inputs, path)
Colin Crossab020a72021-03-12 17:52:23 -0800734 return c.PathForInput(path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700735}
736
Colin Crossab020a72021-03-12 17:52:23 -0800737func (c *RuleBuilderCommand) addImplicit(path Path) {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400738 c.implicits = append(c.implicits, path)
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400739}
740
Colin Crossda71eda2020-02-21 16:55:19 -0800741func (c *RuleBuilderCommand) addOrderOnly(path Path) {
742 c.orderOnlys = append(c.orderOnlys, path)
743}
744
Colin Crossab020a72021-03-12 17:52:23 -0800745// PathForInput takes an input path and returns the appropriate path to use on the command line. If
746// sbox was enabled via a call to RuleBuilder.Sbox() and the path was an output path it returns a
747// path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
748// original path.
749func (c *RuleBuilderCommand) PathForInput(path Path) string {
750 if c.rule.sbox {
751 rel, inSandbox := c.rule._sboxPathForInputRel(path)
752 if inSandbox {
753 rel = filepath.Join(sboxSandboxBaseDir, rel)
754 }
755 return rel
756 }
757 return path.String()
758}
759
760// PathsForInputs takes a list of input paths and returns the appropriate paths to use on the
761// command line. If sbox was enabled via a call to RuleBuilder.Sbox() a path was an output path, it
762// returns the path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it
763// returns the original paths.
764func (c *RuleBuilderCommand) PathsForInputs(paths Paths) []string {
765 ret := make([]string, len(paths))
766 for i, path := range paths {
767 ret[i] = c.PathForInput(path)
768 }
769 return ret
770}
771
Colin Crossf1a035e2020-11-16 17:32:30 -0800772// PathForOutput takes an output path and returns the appropriate path to use on the command
773// line. If sbox was enabled via a call to RuleBuilder.Sbox(), it returns a path with the
774// placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
775// original path.
776func (c *RuleBuilderCommand) PathForOutput(path WritablePath) string {
777 if c.rule.sbox {
778 // Errors will be handled in RuleBuilder.Build where we have a context to report them
779 rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
780 return filepath.Join(sboxOutDir, rel)
Dan Willemsen633c5022019-04-12 11:11:38 -0700781 }
782 return path.String()
Colin Crossfeec25b2019-01-30 17:32:39 -0800783}
784
Colin Crossba9e4032020-11-24 16:32:22 -0800785// SboxPathForTool takes a path to a tool, which may be an output file or a source file, and returns
786// the corresponding path for the tool in the sbox sandbox. It assumes that sandboxing and tool
787// sandboxing are enabled.
788func SboxPathForTool(ctx BuilderContext, path Path) string {
789 return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(ctx, path))
790}
791
792func sboxPathForToolRel(ctx BuilderContext, path Path) string {
793 // Errors will be handled in RuleBuilder.Build where we have a context to report them
794 relOut, isRelOut, _ := maybeRelErr(PathForOutput(ctx, "host", ctx.Config().PrebuiltOS()).String(), path.String())
795 if isRelOut {
796 // The tool is in the output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
797 return filepath.Join(sboxToolsSubDir, "out", relOut)
798 }
799 // The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
800 return filepath.Join(sboxToolsSubDir, "src", path.String())
801}
802
Colin Crossab020a72021-03-12 17:52:23 -0800803func (r *RuleBuilder) _sboxPathForInputRel(path Path) (rel string, inSandbox bool) {
804 // Errors will be handled in RuleBuilder.Build where we have a context to report them
805 rel, isRelSboxOut, _ := maybeRelErr(r.outDir.String(), path.String())
806 if isRelSboxOut {
807 return filepath.Join(sboxOutSubDir, rel), true
808 }
809 if r.sboxInputs {
810 // When sandboxing inputs all inputs have to be copied into the sandbox. Input files that
811 // are outputs of other rules could be an arbitrary absolute path if OUT_DIR is set, so they
812 // will be copied to relative paths under __SBOX_OUT_DIR__/out.
813 rel, isRelOut, _ := maybeRelErr(PathForOutput(r.ctx).String(), path.String())
814 if isRelOut {
815 return filepath.Join(sboxOutSubDir, rel), true
816 }
817 }
818 return path.String(), false
819}
820
821func (r *RuleBuilder) sboxPathForInputRel(path Path) string {
822 rel, _ := r._sboxPathForInputRel(path)
823 return rel
824}
825
826func (r *RuleBuilder) sboxPathsForInputsRel(paths Paths) []string {
827 ret := make([]string, len(paths))
828 for i, path := range paths {
829 ret[i] = r.sboxPathForInputRel(path)
830 }
831 return ret
832}
833
Colin Crossba9e4032020-11-24 16:32:22 -0800834// SboxPathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
835// tool after copying it into the sandbox. This can be used on the RuleBuilder command line to
836// reference the tool.
837func SboxPathForPackagedTool(spec PackagingSpec) string {
838 return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
839}
840
841func sboxPathForPackagedToolRel(spec PackagingSpec) string {
842 return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage)
843}
844
845// PathForTool takes a path to a tool, which may be an output file or a source file, and returns
846// the corresponding path for the tool in the sbox sandbox if sbox is enabled, or the original path
847// if it is not. This can be used on the RuleBuilder command line to reference the tool.
848func (c *RuleBuilderCommand) PathForTool(path Path) string {
849 if c.rule.sbox && c.rule.sboxTools {
850 return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path))
851 }
852 return path.String()
853}
854
855// PackagedTool adds the specified tool path to the command line. It can only be used with tool
856// sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox.
857func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand {
858 if !c.rule.sboxTools {
859 panic("PackagedTool() requires SandboxTools()")
860 }
861
862 c.packagedTools = append(c.packagedTools, spec)
863 c.Text(sboxPathForPackagedToolRel(spec))
864 return c
865}
866
867// ImplicitPackagedTool copies the specified tool into the sandbox without modifying the command
868// line. It can only be used with tool sandboxing enabled by SandboxTools().
869func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuilderCommand {
870 if !c.rule.sboxTools {
871 panic("ImplicitPackagedTool() requires SandboxTools()")
872 }
873
874 c.packagedTools = append(c.packagedTools, spec)
875 return c
876}
877
878// ImplicitPackagedTools copies the specified tools into the sandbox without modifying the command
879// line. It can only be used with tool sandboxing enabled by SandboxTools().
880func (c *RuleBuilderCommand) ImplicitPackagedTools(specs []PackagingSpec) *RuleBuilderCommand {
881 if !c.rule.sboxTools {
882 panic("ImplicitPackagedTools() requires SandboxTools()")
883 }
884
885 c.packagedTools = append(c.packagedTools, specs...)
886 return c
887}
888
Colin Cross758290d2019-02-01 16:42:32 -0800889// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
890// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800891func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
Colin Crosscfec40c2019-07-08 17:07:18 -0700892 if c.buf.Len() > 0 {
893 c.buf.WriteByte(' ')
Colin Crossfeec25b2019-01-30 17:32:39 -0800894 }
Colin Crosscfec40c2019-07-08 17:07:18 -0700895 c.buf.WriteString(text)
Colin Crossfeec25b2019-01-30 17:32:39 -0800896 return c
897}
898
Colin Cross758290d2019-02-01 16:42:32 -0800899// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or
900// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800901func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
902 return c.Text(fmt.Sprintf(format, a...))
903}
904
Colin Cross758290d2019-02-01 16:42:32 -0800905// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the
906// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800907func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
908 return c.Text(flag)
909}
910
Colin Crossab054432019-07-15 16:13:59 -0700911// OptionalFlag adds the specified raw text to the command line if it is not nil. The text should not contain input or
912// output paths or the rule will not have them listed in its dependencies or outputs.
913func (c *RuleBuilderCommand) OptionalFlag(flag *string) *RuleBuilderCommand {
914 if flag != nil {
915 c.Text(*flag)
916 }
917
918 return c
919}
920
Colin Cross92b7d582019-03-29 15:32:51 -0700921// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the
922// rule will not have them listed in its dependencies or outputs.
923func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
924 for _, flag := range flags {
925 c.Text(flag)
926 }
927 return c
928}
929
Colin Cross758290d2019-02-01 16:42:32 -0800930// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
931// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
932// outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800933func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
934 return c.Text(flag + arg)
935}
936
Colin Crossc7ed0042019-02-11 14:11:09 -0800937// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to
938// calling FlagWithArg for argument.
939func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand {
940 for _, arg := range args {
941 c.FlagWithArg(flag, arg)
942 }
943 return c
944}
945
Roland Levillain2da5d9a2019-02-27 16:56:41 +0000946// 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 -0800947// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or
948// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800949func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
950 return c.Text(flag + strings.Join(list, sep))
951}
952
Colin Cross758290d2019-02-01 16:42:32 -0800953// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by
954// RuleBuilder.Tools.
Colin Cross69f59a32019-02-15 10:39:37 -0800955func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800956 c.tools = append(c.tools, path)
Colin Crossba9e4032020-11-24 16:32:22 -0800957 return c.Text(c.PathForTool(path))
958}
959
960// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
961func (c *RuleBuilderCommand) ImplicitTool(path Path) *RuleBuilderCommand {
962 c.tools = append(c.tools, path)
963 return c
964}
965
966// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
967func (c *RuleBuilderCommand) ImplicitTools(paths Paths) *RuleBuilderCommand {
968 c.tools = append(c.tools, paths...)
969 return c
Colin Crossfeec25b2019-01-30 17:32:39 -0800970}
971
Colin Crossee94d6a2019-07-08 17:08:34 -0700972// BuiltTool adds the specified tool path that was built using a host Soong module to the command line. The path will
973// be also added to the dependencies returned by RuleBuilder.Tools.
974//
975// It is equivalent to:
976// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
Colin Crossf1a035e2020-11-16 17:32:30 -0800977func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
978 return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
Colin Crossee94d6a2019-07-08 17:08:34 -0700979}
980
981// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the
982// dependencies returned by RuleBuilder.Tools.
983//
984// It is equivalent to:
985// cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
986func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
987 return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
988}
989
Colin Cross758290d2019-02-01 16:42:32 -0800990// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
991// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800992func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700993 return c.Text(c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800994}
995
Colin Cross758290d2019-02-01 16:42:32 -0800996// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
997// dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800998func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800999 for _, path := range paths {
1000 c.Input(path)
1001 }
1002 return c
1003}
1004
1005// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
1006// command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001007func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001008 c.addImplicit(path)
Colin Crossfeec25b2019-01-30 17:32:39 -08001009 return c
1010}
1011
Colin Cross758290d2019-02-01 16:42:32 -08001012// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
1013// command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001014func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001015 for _, path := range paths {
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001016 c.addImplicit(path)
Dan Willemsen633c5022019-04-12 11:11:38 -07001017 }
Colin Crossfeec25b2019-01-30 17:32:39 -08001018 return c
1019}
1020
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001021// GetImplicits returns the command's implicit inputs.
1022func (c *RuleBuilderCommand) GetImplicits() Paths {
1023 return c.implicits
1024}
1025
Colin Crossda71eda2020-02-21 16:55:19 -08001026// OrderOnly adds the specified input path to the dependencies returned by RuleBuilder.OrderOnlys
1027// without modifying the command line.
1028func (c *RuleBuilderCommand) OrderOnly(path Path) *RuleBuilderCommand {
1029 c.addOrderOnly(path)
1030 return c
1031}
1032
1033// OrderOnlys adds the specified input paths to the dependencies returned by RuleBuilder.OrderOnlys
1034// without modifying the command line.
1035func (c *RuleBuilderCommand) OrderOnlys(paths Paths) *RuleBuilderCommand {
1036 for _, path := range paths {
1037 c.addOrderOnly(path)
1038 }
1039 return c
1040}
1041
Colin Cross758290d2019-02-01 16:42:32 -08001042// Output adds the specified output path to the command line. The path will also be added to the outputs returned by
1043// RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001044func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001045 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001046 return c.Text(c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001047}
1048
Colin Cross758290d2019-02-01 16:42:32 -08001049// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
1050// the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001051func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001052 for _, path := range paths {
1053 c.Output(path)
1054 }
1055 return c
1056}
1057
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001058// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
1059// and will be the temporary output directory managed by sbox, not the final one.
1060func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand {
Colin Crossf1a035e2020-11-16 17:32:30 -08001061 if !c.rule.sbox {
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001062 panic("OutputDir only valid with Sbox")
1063 }
Colin Cross3d680512020-11-13 16:23:53 -08001064 return c.Text(sboxOutDir)
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001065}
1066
Colin Cross1d2cf042019-03-29 15:33:06 -07001067// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
1068// line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles are added to
1069// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
1070func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
1071 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001072 return c.Text(c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -07001073}
1074
Colin Cross758290d2019-02-01 16:42:32 -08001075// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
1076// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001077func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001078 c.outputs = append(c.outputs, path)
1079 return c
1080}
1081
Colin Cross758290d2019-02-01 16:42:32 -08001082// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
1083// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001084func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001085 c.outputs = append(c.outputs, paths...)
1086 return c
1087}
1088
Jingwen Chence679d22020-09-23 04:30:02 +00001089// ImplicitSymlinkOutput declares the specified path as an implicit output that
1090// will be a symlink instead of a regular file. Does not modify the command
1091// line.
1092func (c *RuleBuilderCommand) ImplicitSymlinkOutput(path WritablePath) *RuleBuilderCommand {
1093 c.symlinkOutputs = append(c.symlinkOutputs, path)
1094 return c.ImplicitOutput(path)
1095}
1096
1097// ImplicitSymlinkOutputs declares the specified paths as implicit outputs that
1098// will be a symlinks instead of regular files. Does not modify the command
1099// line.
1100func (c *RuleBuilderCommand) ImplicitSymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
1101 for _, path := range paths {
1102 c.ImplicitSymlinkOutput(path)
1103 }
1104 return c
1105}
1106
1107// SymlinkOutput declares the specified path as an output that will be a symlink
1108// instead of a regular file. Modifies the command line.
1109func (c *RuleBuilderCommand) SymlinkOutput(path WritablePath) *RuleBuilderCommand {
1110 c.symlinkOutputs = append(c.symlinkOutputs, path)
1111 return c.Output(path)
1112}
1113
1114// SymlinkOutputsl declares the specified paths as outputs that will be symlinks
1115// instead of regular files. Modifies the command line.
1116func (c *RuleBuilderCommand) SymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
1117 for _, path := range paths {
1118 c.SymlinkOutput(path)
1119 }
1120 return c
1121}
1122
Colin Cross1d2cf042019-03-29 15:33:06 -07001123// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
1124// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles
1125// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
1126// depfiles together.
1127func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand {
1128 c.depFiles = append(c.depFiles, path)
1129 return c
1130}
1131
Colin Cross758290d2019-02-01 16:42:32 -08001132// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
1133// will also be added to the dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001134func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001135 return c.Text(flag + c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001136}
1137
Colin Cross758290d2019-02-01 16:42:32 -08001138// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
1139// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
1140// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001141func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001142 strs := make([]string, len(paths))
1143 for i, path := range paths {
1144 strs[i] = c.addInput(path)
1145 }
1146 return c.FlagWithList(flag, strs, sep)
Colin Crossfeec25b2019-01-30 17:32:39 -08001147}
1148
Colin Cross758290d2019-02-01 16:42:32 -08001149// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
1150// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for
1151// each input path.
Colin Cross69f59a32019-02-15 10:39:37 -08001152func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001153 for _, path := range paths {
1154 c.FlagWithInput(flag, path)
1155 }
1156 return c
1157}
1158
1159// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path
1160// will also be added to the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001161func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001162 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001163 return c.Text(flag + c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001164}
1165
Colin Cross1d2cf042019-03-29 15:33:06 -07001166// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path
1167// will also be added to the outputs returned by RuleBuilder.Outputs.
1168func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
1169 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001170 return c.Text(flag + c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -07001171}
1172
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001173// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with no separator
Colin Cross70c47412021-03-12 17:48:14 -08001174// between them. The paths will be written to the rspfile. If sbox is enabled, the rspfile must
1175// be outside the sbox directory.
1176func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, rspFile WritablePath, paths Paths) *RuleBuilderCommand {
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001177 if c.rspFileInputs != nil {
1178 panic("FlagWithRspFileInputList cannot be called if rsp file inputs have already been provided")
1179 }
1180
1181 // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
1182 // generated.
1183 if paths == nil {
1184 paths = Paths{}
1185 }
1186
1187 c.rspFileInputs = paths
Colin Cross70c47412021-03-12 17:48:14 -08001188 c.rspFile = rspFile
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001189
Colin Cross70c47412021-03-12 17:48:14 -08001190 if c.rule.sbox {
1191 if _, isRel, _ := maybeRelErr(c.rule.outDir.String(), rspFile.String()); isRel {
1192 panic(fmt.Errorf("FlagWithRspFileInputList rspfile %q must not be inside out dir %q",
1193 rspFile.String(), c.rule.outDir.String()))
1194 }
1195 }
1196
Colin Crossab020a72021-03-12 17:52:23 -08001197 c.FlagWithArg(flag, c.PathForInput(rspFile))
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001198 return c
1199}
1200
Colin Cross758290d2019-02-01 16:42:32 -08001201// String returns the command line.
1202func (c *RuleBuilderCommand) String() string {
Colin Crosscfec40c2019-07-08 17:07:18 -07001203 return c.buf.String()
Colin Cross758290d2019-02-01 16:42:32 -08001204}
Colin Cross1d2cf042019-03-29 15:33:06 -07001205
Colin Crosse16ce362020-11-12 08:29:30 -08001206// RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox()
1207// and returns sbox testproto generated by the RuleBuilder.
1208func RuleBuilderSboxProtoForTests(t *testing.T, params TestingBuildParams) *sbox_proto.Manifest {
1209 t.Helper()
1210 content := ContentFromFileRuleForTests(t, params)
1211 manifest := sbox_proto.Manifest{}
1212 err := proto.UnmarshalText(content, &manifest)
1213 if err != nil {
1214 t.Fatalf("failed to unmarshal manifest: %s", err.Error())
1215 }
1216 return &manifest
1217}
1218
Colin Cross1d2cf042019-03-29 15:33:06 -07001219func ninjaNameEscape(s string) string {
1220 b := []byte(s)
1221 escaped := false
1222 for i, c := range b {
1223 valid := (c >= 'a' && c <= 'z') ||
1224 (c >= 'A' && c <= 'Z') ||
1225 (c >= '0' && c <= '9') ||
1226 (c == '_') ||
1227 (c == '-') ||
1228 (c == '.')
1229 if !valid {
1230 b[i] = '_'
1231 escaped = true
1232 }
1233 }
1234 if escaped {
1235 s = string(b)
1236 }
1237 return s
1238}
Colin Cross3d680512020-11-13 16:23:53 -08001239
1240// hashSrcFiles returns a hash of the list of source files. It is used to ensure the command line
1241// or the sbox textproto manifest change even if the input files are not listed on the command line.
1242func hashSrcFiles(srcFiles Paths) string {
1243 h := sha256.New()
1244 srcFileList := strings.Join(srcFiles.Strings(), "\n")
1245 h.Write([]byte(srcFileList))
1246 return fmt.Sprintf("%x", h.Sum(nil))
1247}
Colin Crossf1a035e2020-11-16 17:32:30 -08001248
1249// BuilderContextForTesting returns a BuilderContext for the given config that can be used for tests
1250// that need to call methods that take a BuilderContext.
1251func BuilderContextForTesting(config Config) BuilderContext {
1252 pathCtx := PathContextForTesting(config)
1253 return builderContextForTests{
1254 PathContext: pathCtx,
1255 }
1256}
1257
1258type builderContextForTests struct {
1259 PathContext
1260}
1261
1262func (builderContextForTests) Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule {
1263 return nil
1264}
1265func (builderContextForTests) Build(PackageContext, BuildParams) {}
Colin Crossef972742021-03-12 17:24:45 -08001266
1267func rspFileForInputs(paths []string) string {
1268 s := strings.Builder{}
1269 for i, path := range paths {
1270 if i != 0 {
1271 s.WriteByte(' ')
1272 }
1273 s.WriteString(proptools.ShellEscape(path))
1274 }
1275 return s.String()
1276}