blob: 399dfdb91fa00dddbad49e3579834459b0739f83 [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
25 "github.com/google/blueprint"
26 "github.com/google/blueprint/proptools"
Dan Willemsen4591b642021-05-24 14:24:12 -070027 "google.golang.org/protobuf/encoding/prototext"
28 "google.golang.org/protobuf/proto"
Dan Willemsen633c5022019-04-12 11:11:38 -070029
Colin Crosse16ce362020-11-12 08:29:30 -080030 "android/soong/cmd/sbox/sbox_proto"
Colin Crossef972742021-03-12 17:24:45 -080031 "android/soong/remoteexec"
Colin Crosse55bd422021-03-23 13:44:30 -070032 "android/soong/response"
Dan Willemsen633c5022019-04-12 11:11:38 -070033 "android/soong/shared"
Colin Crossfeec25b2019-01-30 17:32:39 -080034)
35
Colin Crosse16ce362020-11-12 08:29:30 -080036const sboxSandboxBaseDir = "__SBOX_SANDBOX_DIR__"
37const sboxOutSubDir = "out"
Colin Crossba9e4032020-11-24 16:32:22 -080038const sboxToolsSubDir = "tools"
Colin Crosse16ce362020-11-12 08:29:30 -080039const sboxOutDir = sboxSandboxBaseDir + "/" + sboxOutSubDir
Colin Cross3d680512020-11-13 16:23:53 -080040
Colin Cross758290d2019-02-01 16:42:32 -080041// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
42// graph.
Colin Crossfeec25b2019-01-30 17:32:39 -080043type RuleBuilder struct {
Colin Crossf1a035e2020-11-16 17:32:30 -080044 pctx PackageContext
45 ctx BuilderContext
46
Colin Crosse16ce362020-11-12 08:29:30 -080047 commands []*RuleBuilderCommand
48 installs RuleBuilderInstalls
49 temporariesSet map[WritablePath]bool
50 restat bool
51 sbox bool
52 highmem bool
53 remoteable RemoteRuleSupports
Colin Crossef972742021-03-12 17:24:45 -080054 rbeParams *remoteexec.REParams
Colin Crossf1a035e2020-11-16 17:32:30 -080055 outDir WritablePath
Spandan Dasaf4ccaa2023-06-29 01:15:51 +000056 sboxOutSubDir string
Colin Crossba9e4032020-11-24 16:32:22 -080057 sboxTools bool
Colin Crossab020a72021-03-12 17:52:23 -080058 sboxInputs bool
Colin Crosse16ce362020-11-12 08:29:30 -080059 sboxManifestPath WritablePath
60 missingDeps []string
Colin Crossfeec25b2019-01-30 17:32:39 -080061}
62
Colin Cross758290d2019-02-01 16:42:32 -080063// NewRuleBuilder returns a newly created RuleBuilder.
Colin Crossf1a035e2020-11-16 17:32:30 -080064func NewRuleBuilder(pctx PackageContext, ctx BuilderContext) *RuleBuilder {
Colin Cross5cb5b092019-02-02 21:25:18 -080065 return &RuleBuilder{
Colin Crossf1a035e2020-11-16 17:32:30 -080066 pctx: pctx,
67 ctx: ctx,
Colin Cross69f59a32019-02-15 10:39:37 -080068 temporariesSet: make(map[WritablePath]bool),
Spandan Dasaf4ccaa2023-06-29 01:15:51 +000069 sboxOutSubDir: sboxOutSubDir,
Colin Cross5cb5b092019-02-02 21:25:18 -080070 }
Colin Cross758290d2019-02-01 16:42:32 -080071}
72
Spandan Dasaf4ccaa2023-06-29 01:15:51 +000073// SetSboxOutDirDirAsEmpty sets the out subdirectory to an empty string
74// This is useful for sandboxing actions that change the execution root to a path in out/ (e.g mixed builds)
75// For such actions, SetSboxOutDirDirAsEmpty ensures that the path does not become $SBOX_SANDBOX_DIR/out/out/bazel/output/execroot/__main__/...
76func (rb *RuleBuilder) SetSboxOutDirDirAsEmpty() *RuleBuilder {
77 rb.sboxOutSubDir = ""
78 return rb
79}
80
Colin Cross758290d2019-02-01 16:42:32 -080081// RuleBuilderInstall is a tuple of install from and to locations.
82type RuleBuilderInstall struct {
Colin Cross69f59a32019-02-15 10:39:37 -080083 From Path
84 To string
Colin Cross758290d2019-02-01 16:42:32 -080085}
86
Colin Crossdeabb942019-02-11 14:11:09 -080087type RuleBuilderInstalls []RuleBuilderInstall
88
89// String returns the RuleBuilderInstalls in the form used by $(call copy-many-files) in Make, a space separated
90// list of from:to tuples.
91func (installs RuleBuilderInstalls) String() string {
92 sb := strings.Builder{}
93 for i, install := range installs {
94 if i != 0 {
95 sb.WriteRune(' ')
96 }
Colin Cross69f59a32019-02-15 10:39:37 -080097 sb.WriteString(install.From.String())
Colin Crossdeabb942019-02-11 14:11:09 -080098 sb.WriteRune(':')
99 sb.WriteString(install.To)
100 }
101 return sb.String()
102}
103
Colin Cross0d2f40a2019-02-05 22:31:15 -0800104// MissingDeps adds modules to the list of missing dependencies. If MissingDeps
105// is called with a non-empty input, any call to Build will result in a rule
106// that will print an error listing the missing dependencies and fail.
107// MissingDeps should only be called if Config.AllowMissingDependencies() is
108// true.
109func (r *RuleBuilder) MissingDeps(missingDeps []string) {
110 r.missingDeps = append(r.missingDeps, missingDeps...)
111}
112
Colin Cross758290d2019-02-01 16:42:32 -0800113// Restat marks the rule as a restat rule, which will be passed to ModuleContext.Rule in BuildParams.Restat.
Colin Crossfeec25b2019-01-30 17:32:39 -0800114func (r *RuleBuilder) Restat() *RuleBuilder {
115 r.restat = true
116 return r
117}
118
Colin Cross8b8bec32019-11-15 13:18:43 -0800119// HighMem marks the rule as a high memory rule, which will limit how many run in parallel with other high memory
120// rules.
121func (r *RuleBuilder) HighMem() *RuleBuilder {
122 r.highmem = true
123 return r
124}
125
126// Remoteable marks the rule as supporting remote execution.
127func (r *RuleBuilder) Remoteable(supports RemoteRuleSupports) *RuleBuilder {
128 r.remoteable = supports
129 return r
130}
131
Colin Crossef972742021-03-12 17:24:45 -0800132// Rewrapper marks the rule as running inside rewrapper using the given params in order to support
133// running on RBE. During RuleBuilder.Build the params will be combined with the inputs, outputs
134// and tools known to RuleBuilder to prepend an appropriate rewrapper command line to the rule's
135// command line.
136func (r *RuleBuilder) Rewrapper(params *remoteexec.REParams) *RuleBuilder {
137 if !r.sboxInputs {
138 panic(fmt.Errorf("RuleBuilder.Rewrapper must be called after RuleBuilder.SandboxInputs"))
139 }
140 r.rbeParams = params
141 return r
142}
143
Colin Crosse16ce362020-11-12 08:29:30 -0800144// Sbox marks the rule as needing to be wrapped by sbox. The outputDir should point to the output
145// directory that sbox will wipe. It should not be written to by any other rule. manifestPath should
146// point to a location where sbox's manifest will be written and must be outside outputDir. sbox
147// will ensure that all outputs have been written, and will discard any output files that were not
148// specified.
Colin Crosse16ce362020-11-12 08:29:30 -0800149func (r *RuleBuilder) Sbox(outputDir WritablePath, manifestPath WritablePath) *RuleBuilder {
Dan Willemsen633c5022019-04-12 11:11:38 -0700150 if r.sbox {
151 panic("Sbox() may not be called more than once")
152 }
153 if len(r.commands) > 0 {
154 panic("Sbox() may not be called after Command()")
155 }
Dan Willemsen633c5022019-04-12 11:11:38 -0700156 r.sbox = true
Colin Crossf1a035e2020-11-16 17:32:30 -0800157 r.outDir = outputDir
Colin Crosse16ce362020-11-12 08:29:30 -0800158 r.sboxManifestPath = manifestPath
Dan Willemsen633c5022019-04-12 11:11:38 -0700159 return r
160}
161
Colin Crossba9e4032020-11-24 16:32:22 -0800162// SandboxTools enables tool sandboxing for the rule by copying any referenced tools into the
163// sandbox.
164func (r *RuleBuilder) SandboxTools() *RuleBuilder {
165 if !r.sbox {
166 panic("SandboxTools() must be called after Sbox()")
167 }
168 if len(r.commands) > 0 {
169 panic("SandboxTools() may not be called after Command()")
170 }
171 r.sboxTools = true
172 return r
173}
174
Colin Crossab020a72021-03-12 17:52:23 -0800175// SandboxInputs enables input sandboxing for the rule by copying any referenced inputs into the
176// sandbox. It also implies SandboxTools().
177//
178// Sandboxing inputs requires RuleBuilder to be aware of all references to input paths. Paths
179// that are passed to RuleBuilder outside of the methods that expect inputs, for example
180// FlagWithArg, must use RuleBuilderCommand.PathForInput to translate the path to one that matches
181// the sandbox layout.
182func (r *RuleBuilder) SandboxInputs() *RuleBuilder {
183 if !r.sbox {
184 panic("SandboxInputs() must be called after Sbox()")
185 }
186 if len(r.commands) > 0 {
187 panic("SandboxInputs() may not be called after Command()")
188 }
189 r.sboxTools = true
190 r.sboxInputs = true
191 return r
192}
193
Colin Cross758290d2019-02-01 16:42:32 -0800194// Install associates an output of the rule with an install location, which can be retrieved later using
195// RuleBuilder.Installs.
Colin Cross69f59a32019-02-15 10:39:37 -0800196func (r *RuleBuilder) Install(from Path, to string) {
Colin Crossfeec25b2019-01-30 17:32:39 -0800197 r.installs = append(r.installs, RuleBuilderInstall{from, to})
198}
199
Colin Cross758290d2019-02-01 16:42:32 -0800200// Command returns a new RuleBuilderCommand for the rule. The commands will be ordered in the rule by when they were
201// created by this method. That can be mutated through their methods in any order, as long as the mutations do not
202// race with any call to Build.
Colin Crossfeec25b2019-01-30 17:32:39 -0800203func (r *RuleBuilder) Command() *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700204 command := &RuleBuilderCommand{
Colin Crossf1a035e2020-11-16 17:32:30 -0800205 rule: r,
Dan Willemsen633c5022019-04-12 11:11:38 -0700206 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800207 r.commands = append(r.commands, command)
208 return command
209}
210
Colin Cross5cb5b092019-02-02 21:25:18 -0800211// Temporary marks an output of a command as an intermediate file that will be used as an input to another command
212// in the same rule, and should not be listed in Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800213func (r *RuleBuilder) Temporary(path WritablePath) {
Colin Cross5cb5b092019-02-02 21:25:18 -0800214 r.temporariesSet[path] = true
215}
216
217// DeleteTemporaryFiles adds a command to the rule that deletes any outputs that have been marked using Temporary
218// when the rule runs. DeleteTemporaryFiles should be called after all calls to Temporary.
219func (r *RuleBuilder) DeleteTemporaryFiles() {
Colin Cross69f59a32019-02-15 10:39:37 -0800220 var temporariesList WritablePaths
Colin Cross5cb5b092019-02-02 21:25:18 -0800221
222 for intermediate := range r.temporariesSet {
223 temporariesList = append(temporariesList, intermediate)
224 }
Colin Cross69f59a32019-02-15 10:39:37 -0800225
226 sort.Slice(temporariesList, func(i, j int) bool {
227 return temporariesList[i].String() < temporariesList[j].String()
228 })
Colin Cross5cb5b092019-02-02 21:25:18 -0800229
230 r.Command().Text("rm").Flag("-f").Outputs(temporariesList)
231}
232
Colin Crossda71eda2020-02-21 16:55:19 -0800233// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
Colin Cross3d680512020-11-13 16:23:53 -0800234// input paths, such as RuleBuilderCommand.Input, RuleBuilderCommand.Implicit, or
Colin Crossda71eda2020-02-21 16:55:19 -0800235// RuleBuilderCommand.FlagWithInput. Inputs to a command that are also outputs of another command
236// in the same RuleBuilder are filtered out. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800237func (r *RuleBuilder) Inputs() Paths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800238 outputs := r.outputSet()
Dan Willemsen633c5022019-04-12 11:11:38 -0700239 depFiles := r.depFileSet()
Colin Crossfeec25b2019-01-30 17:32:39 -0800240
Colin Cross69f59a32019-02-15 10:39:37 -0800241 inputs := make(map[string]Path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800242 for _, c := range r.commands {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400243 for _, input := range append(c.inputs, c.implicits...) {
Dan Willemsen633c5022019-04-12 11:11:38 -0700244 inputStr := input.String()
245 if _, isOutput := outputs[inputStr]; !isOutput {
246 if _, isDepFile := depFiles[inputStr]; !isDepFile {
247 inputs[input.String()] = input
248 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800249 }
250 }
251 }
252
Colin Cross69f59a32019-02-15 10:39:37 -0800253 var inputList Paths
254 for _, input := range inputs {
Colin Crossfeec25b2019-01-30 17:32:39 -0800255 inputList = append(inputList, input)
256 }
Colin Cross69f59a32019-02-15 10:39:37 -0800257
258 sort.Slice(inputList, func(i, j int) bool {
259 return inputList[i].String() < inputList[j].String()
260 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800261
262 return inputList
263}
264
Colin Crossda71eda2020-02-21 16:55:19 -0800265// OrderOnlys returns the list of paths that were passed to the RuleBuilderCommand.OrderOnly or
266// RuleBuilderCommand.OrderOnlys. The list is sorted and duplicates removed.
267func (r *RuleBuilder) OrderOnlys() Paths {
268 orderOnlys := make(map[string]Path)
269 for _, c := range r.commands {
270 for _, orderOnly := range c.orderOnlys {
271 orderOnlys[orderOnly.String()] = orderOnly
272 }
273 }
274
275 var orderOnlyList Paths
276 for _, orderOnly := range orderOnlys {
277 orderOnlyList = append(orderOnlyList, orderOnly)
278 }
279
280 sort.Slice(orderOnlyList, func(i, j int) bool {
281 return orderOnlyList[i].String() < orderOnlyList[j].String()
282 })
283
284 return orderOnlyList
285}
286
Colin Crossae89abe2021-04-21 11:45:23 -0700287// Validations returns the list of paths that were passed to RuleBuilderCommand.Validation or
288// RuleBuilderCommand.Validations. The list is sorted and duplicates removed.
289func (r *RuleBuilder) Validations() Paths {
290 validations := make(map[string]Path)
291 for _, c := range r.commands {
292 for _, validation := range c.validations {
293 validations[validation.String()] = validation
294 }
295 }
296
297 var validationList Paths
298 for _, validation := range validations {
299 validationList = append(validationList, validation)
300 }
301
302 sort.Slice(validationList, func(i, j int) bool {
303 return validationList[i].String() < validationList[j].String()
304 })
305
306 return validationList
307}
308
Colin Cross69f59a32019-02-15 10:39:37 -0800309func (r *RuleBuilder) outputSet() map[string]WritablePath {
310 outputs := make(map[string]WritablePath)
Colin Crossfeec25b2019-01-30 17:32:39 -0800311 for _, c := range r.commands {
312 for _, output := range c.outputs {
Colin Cross69f59a32019-02-15 10:39:37 -0800313 outputs[output.String()] = output
Colin Crossfeec25b2019-01-30 17:32:39 -0800314 }
315 }
316 return outputs
317}
318
Colin Crossda71eda2020-02-21 16:55:19 -0800319// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
320// output paths, such as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or
321// RuleBuilderCommand.FlagWithInput. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800322func (r *RuleBuilder) Outputs() WritablePaths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800323 outputs := r.outputSet()
324
Colin Cross69f59a32019-02-15 10:39:37 -0800325 var outputList WritablePaths
326 for _, output := range outputs {
Colin Cross5cb5b092019-02-02 21:25:18 -0800327 if !r.temporariesSet[output] {
328 outputList = append(outputList, output)
329 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800330 }
Colin Cross69f59a32019-02-15 10:39:37 -0800331
332 sort.Slice(outputList, func(i, j int) bool {
333 return outputList[i].String() < outputList[j].String()
334 })
335
Colin Crossfeec25b2019-01-30 17:32:39 -0800336 return outputList
337}
338
Jingwen Chence679d22020-09-23 04:30:02 +0000339func (r *RuleBuilder) symlinkOutputSet() map[string]WritablePath {
340 symlinkOutputs := make(map[string]WritablePath)
341 for _, c := range r.commands {
342 for _, symlinkOutput := range c.symlinkOutputs {
343 symlinkOutputs[symlinkOutput.String()] = symlinkOutput
344 }
345 }
346 return symlinkOutputs
347}
348
349// SymlinkOutputs returns the list of paths that the executor (Ninja) would
350// verify, after build edge completion, that:
351//
352// 1) Created output symlinks match the list of paths in this list exactly (no more, no fewer)
353// 2) Created output files are *not* declared in this list.
354//
355// These symlink outputs are expected to be a subset of outputs or implicit
356// outputs, or they would fail validation at build param construction time
357// later, to support other non-rule-builder approaches for constructing
358// statements.
359func (r *RuleBuilder) SymlinkOutputs() WritablePaths {
360 symlinkOutputs := r.symlinkOutputSet()
361
362 var symlinkOutputList WritablePaths
363 for _, symlinkOutput := range symlinkOutputs {
364 symlinkOutputList = append(symlinkOutputList, symlinkOutput)
365 }
366
367 sort.Slice(symlinkOutputList, func(i, j int) bool {
368 return symlinkOutputList[i].String() < symlinkOutputList[j].String()
369 })
370
371 return symlinkOutputList
372}
373
Dan Willemsen633c5022019-04-12 11:11:38 -0700374func (r *RuleBuilder) depFileSet() map[string]WritablePath {
375 depFiles := make(map[string]WritablePath)
376 for _, c := range r.commands {
377 for _, depFile := range c.depFiles {
378 depFiles[depFile.String()] = depFile
379 }
380 }
381 return depFiles
382}
383
Colin Cross1d2cf042019-03-29 15:33:06 -0700384// DepFiles returns the list of paths that were passed to the RuleBuilderCommand methods that take depfile paths, such
385// as RuleBuilderCommand.DepFile or RuleBuilderCommand.FlagWithDepFile.
386func (r *RuleBuilder) DepFiles() WritablePaths {
387 var depFiles WritablePaths
388
389 for _, c := range r.commands {
390 for _, depFile := range c.depFiles {
391 depFiles = append(depFiles, depFile)
392 }
393 }
394
395 return depFiles
396}
397
Colin Cross758290d2019-02-01 16:42:32 -0800398// Installs returns the list of tuples passed to Install.
Colin Crossdeabb942019-02-11 14:11:09 -0800399func (r *RuleBuilder) Installs() RuleBuilderInstalls {
400 return append(RuleBuilderInstalls(nil), r.installs...)
Colin Crossfeec25b2019-01-30 17:32:39 -0800401}
402
Colin Cross69f59a32019-02-15 10:39:37 -0800403func (r *RuleBuilder) toolsSet() map[string]Path {
404 tools := make(map[string]Path)
Colin Cross5cb5b092019-02-02 21:25:18 -0800405 for _, c := range r.commands {
406 for _, tool := range c.tools {
Colin Cross69f59a32019-02-15 10:39:37 -0800407 tools[tool.String()] = tool
Colin Cross5cb5b092019-02-02 21:25:18 -0800408 }
409 }
410
411 return tools
412}
413
Colin Crossda71eda2020-02-21 16:55:19 -0800414// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method. The
415// list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800416func (r *RuleBuilder) Tools() Paths {
Colin Cross5cb5b092019-02-02 21:25:18 -0800417 toolsSet := r.toolsSet()
418
Colin Cross69f59a32019-02-15 10:39:37 -0800419 var toolsList Paths
420 for _, tool := range toolsSet {
Colin Cross5cb5b092019-02-02 21:25:18 -0800421 toolsList = append(toolsList, tool)
Colin Crossfeec25b2019-01-30 17:32:39 -0800422 }
Colin Cross69f59a32019-02-15 10:39:37 -0800423
424 sort.Slice(toolsList, func(i, j int) bool {
425 return toolsList[i].String() < toolsList[j].String()
426 })
427
Colin Cross5cb5b092019-02-02 21:25:18 -0800428 return toolsList
Colin Crossfeec25b2019-01-30 17:32:39 -0800429}
430
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700431// RspFileInputs returns the list of paths that were passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
432func (r *RuleBuilder) RspFileInputs() Paths {
433 var rspFileInputs Paths
434 for _, c := range r.commands {
Colin Crossce3a51d2021-03-19 16:22:12 -0700435 for _, rspFile := range c.rspFiles {
436 rspFileInputs = append(rspFileInputs, rspFile.paths...)
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700437 }
438 }
439
440 return rspFileInputs
441}
442
Colin Crossce3a51d2021-03-19 16:22:12 -0700443func (r *RuleBuilder) rspFiles() []rspFileAndPaths {
444 var rspFiles []rspFileAndPaths
Colin Cross70c47412021-03-12 17:48:14 -0800445 for _, c := range r.commands {
Colin Crossce3a51d2021-03-19 16:22:12 -0700446 rspFiles = append(rspFiles, c.rspFiles...)
Colin Cross70c47412021-03-12 17:48:14 -0800447 }
448
Colin Crossce3a51d2021-03-19 16:22:12 -0700449 return rspFiles
Colin Cross70c47412021-03-12 17:48:14 -0800450}
451
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700452// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
Colin Crossfeec25b2019-01-30 17:32:39 -0800453func (r *RuleBuilder) Commands() []string {
454 var commands []string
455 for _, c := range r.commands {
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700456 commands = append(commands, c.String())
457 }
458 return commands
459}
460
Colin Cross758290d2019-02-01 16:42:32 -0800461// BuilderContext is a subset of ModuleContext and SingletonContext.
Colin Cross786cd6d2019-02-01 16:41:11 -0800462type BuilderContext interface {
463 PathContext
464 Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
465 Build(PackageContext, BuildParams)
466}
467
Colin Cross758290d2019-02-01 16:42:32 -0800468var _ BuilderContext = ModuleContext(nil)
469var _ BuilderContext = SingletonContext(nil)
470
Colin Crossf1a035e2020-11-16 17:32:30 -0800471func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700472 return r.Command().
Colin Cross9b698b62021-12-22 09:55:32 -0800473 builtToolWithoutDeps("dep_fixer").
Dan Willemsen633c5022019-04-12 11:11:38 -0700474 Inputs(depFiles.Paths())
Colin Cross1d2cf042019-03-29 15:33:06 -0700475}
476
Sam Delmerico285b66a2023-09-25 12:13:17 +0000477// BuildWithNinjaVars adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
478// Outputs. This function will not escape Ninja variables, so it may be used to write sandbox manifests using Ninja variables.
479func (r *RuleBuilder) BuildWithUnescapedNinjaVars(name string, desc string) {
480 r.build(name, desc, false)
481}
482
Colin Cross758290d2019-02-01 16:42:32 -0800483// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
484// Outputs.
Colin Crossf1a035e2020-11-16 17:32:30 -0800485func (r *RuleBuilder) Build(name string, desc string) {
Sam Delmerico285b66a2023-09-25 12:13:17 +0000486 r.build(name, desc, true)
487}
488
489func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString bool) {
Colin Cross1d2cf042019-03-29 15:33:06 -0700490 name = ninjaNameEscape(name)
491
Colin Cross0d2f40a2019-02-05 22:31:15 -0800492 if len(r.missingDeps) > 0 {
Sam Delmerico285b66a2023-09-25 12:13:17 +0000493 r.ctx.Build(r.pctx, BuildParams{
Colin Cross0d2f40a2019-02-05 22:31:15 -0800494 Rule: ErrorRule,
Colin Cross69f59a32019-02-15 10:39:37 -0800495 Outputs: r.Outputs(),
Colin Cross0d2f40a2019-02-05 22:31:15 -0800496 Description: desc,
497 Args: map[string]string{
498 "error": "missing dependencies: " + strings.Join(r.missingDeps, ", "),
499 },
500 })
501 return
502 }
503
Colin Cross1d2cf042019-03-29 15:33:06 -0700504 var depFile WritablePath
505 var depFormat blueprint.Deps
506 if depFiles := r.DepFiles(); len(depFiles) > 0 {
507 depFile = depFiles[0]
508 depFormat = blueprint.DepsGCC
509 if len(depFiles) > 1 {
510 // Add a command locally that merges all depfiles together into the first depfile.
Colin Crossf1a035e2020-11-16 17:32:30 -0800511 r.depFileMergerCmd(depFiles)
Dan Willemsen633c5022019-04-12 11:11:38 -0700512
513 if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800514 // Check for Rel() errors, as all depfiles should be in the output dir. Errors
515 // will be reported to the ctx.
Dan Willemsen633c5022019-04-12 11:11:38 -0700516 for _, path := range depFiles[1:] {
Colin Crossf1a035e2020-11-16 17:32:30 -0800517 Rel(r.ctx, r.outDir.String(), path.String())
Dan Willemsen633c5022019-04-12 11:11:38 -0700518 }
519 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700520 }
521 }
522
Dan Willemsen633c5022019-04-12 11:11:38 -0700523 tools := r.Tools()
Colin Crossb70a1a92021-03-12 17:51:32 -0800524 commands := r.Commands()
Dan Willemsen633c5022019-04-12 11:11:38 -0700525 outputs := r.Outputs()
Colin Cross3d680512020-11-13 16:23:53 -0800526 inputs := r.Inputs()
Colin Crossce3a51d2021-03-19 16:22:12 -0700527 rspFiles := r.rspFiles()
Dan Willemsen633c5022019-04-12 11:11:38 -0700528
529 if len(commands) == 0 {
530 return
531 }
532 if len(outputs) == 0 {
533 panic("No outputs specified from any Commands")
534 }
535
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700536 commandString := strings.Join(commands, " && ")
Dan Willemsen633c5022019-04-12 11:11:38 -0700537
538 if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800539 // If running the command inside sbox, write the rule data out to an sbox
540 // manifest.textproto.
541 manifest := sbox_proto.Manifest{}
542 command := sbox_proto.Command{}
543 manifest.Commands = append(manifest.Commands, &command)
544 command.Command = proto.String(commandString)
Colin Cross151b9ff2020-11-12 08:29:30 -0800545
Colin Cross619b9ab2020-11-20 18:44:31 +0000546 if depFile != nil {
Colin Crosse16ce362020-11-12 08:29:30 -0800547 manifest.OutputDepfile = proto.String(depFile.String())
Colin Cross619b9ab2020-11-20 18:44:31 +0000548 }
549
Colin Crossba9e4032020-11-24 16:32:22 -0800550 // If sandboxing tools is enabled, add copy rules to the manifest to copy each tool
551 // into the sbox directory.
552 if r.sboxTools {
553 for _, tool := range tools {
554 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
555 From: proto.String(tool.String()),
556 To: proto.String(sboxPathForToolRel(r.ctx, tool)),
557 })
558 }
559 for _, c := range r.commands {
560 for _, tool := range c.packagedTools {
561 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
562 From: proto.String(tool.srcPath.String()),
563 To: proto.String(sboxPathForPackagedToolRel(tool)),
564 Executable: proto.Bool(tool.executable),
565 })
566 tools = append(tools, tool.srcPath)
567 }
568 }
569 }
570
Colin Crossab020a72021-03-12 17:52:23 -0800571 // If sandboxing inputs is enabled, add copy rules to the manifest to copy each input
572 // into the sbox directory.
573 if r.sboxInputs {
574 for _, input := range inputs {
575 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
576 From: proto.String(input.String()),
577 To: proto.String(r.sboxPathForInputRel(input)),
578 })
579 }
580
Colin Crossce3a51d2021-03-19 16:22:12 -0700581 // If using rsp files copy them and their contents into the sbox directory with
582 // the appropriate path mappings.
583 for _, rspFile := range rspFiles {
Colin Crosse55bd422021-03-23 13:44:30 -0700584 command.RspFiles = append(command.RspFiles, &sbox_proto.RspFile{
Colin Crossce3a51d2021-03-19 16:22:12 -0700585 File: proto.String(rspFile.file.String()),
Colin Crosse55bd422021-03-23 13:44:30 -0700586 // These have to match the logic in sboxPathForInputRel
587 PathMappings: []*sbox_proto.PathMapping{
588 {
589 From: proto.String(r.outDir.String()),
590 To: proto.String(sboxOutSubDir),
591 },
592 {
Cole Fauste8561c62023-11-30 17:26:37 -0800593 From: proto.String(r.ctx.Config().OutDir()),
Colin Crosse55bd422021-03-23 13:44:30 -0700594 To: proto.String(sboxOutSubDir),
595 },
596 },
Colin Crossab020a72021-03-12 17:52:23 -0800597 })
598 }
599
600 command.Chdir = proto.Bool(true)
601 }
602
Colin Crosse16ce362020-11-12 08:29:30 -0800603 // Add copy rules to the manifest to copy each output file from the sbox directory.
Colin Crossba9e4032020-11-24 16:32:22 -0800604 // to the output directory after running the commands.
Spandan Das33e30972023-07-13 21:19:12 +0000605 for _, output := range outputs {
Colin Crossf1a035e2020-11-16 17:32:30 -0800606 rel := Rel(r.ctx, r.outDir.String(), output.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800607 command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{
Spandan Dasaf4ccaa2023-06-29 01:15:51 +0000608 From: proto.String(filepath.Join(r.sboxOutSubDir, rel)),
Colin Crosse16ce362020-11-12 08:29:30 -0800609 To: proto.String(output.String()),
610 })
611 }
Colin Cross619b9ab2020-11-20 18:44:31 +0000612
Colin Cross5334edd2021-03-11 17:18:21 -0800613 // Outputs that were marked Temporary will not be checked that they are in the output
614 // directory by the loop above, check them here.
615 for path := range r.temporariesSet {
616 Rel(r.ctx, r.outDir.String(), path.String())
617 }
618
Colin Crosse16ce362020-11-12 08:29:30 -0800619 // Add a hash of the list of input files to the manifest so that the textproto file
620 // changes when the list of input files changes and causes the sbox rule that
621 // depends on it to rerun.
622 command.InputHash = proto.String(hashSrcFiles(inputs))
Colin Cross619b9ab2020-11-20 18:44:31 +0000623
Colin Crosse16ce362020-11-12 08:29:30 -0800624 // Verify that the manifest textproto is not inside the sbox output directory, otherwise
625 // it will get deleted when the sbox rule clears its output directory.
Colin Crossf1a035e2020-11-16 17:32:30 -0800626 _, manifestInOutDir := MaybeRel(r.ctx, r.outDir.String(), r.sboxManifestPath.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800627 if manifestInOutDir {
Colin Crossf1a035e2020-11-16 17:32:30 -0800628 ReportPathErrorf(r.ctx, "sbox rule %q manifestPath %q must not be in outputDir %q",
629 name, r.sboxManifestPath.String(), r.outDir.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800630 }
631
Paul Duffin4a3a0a52023-10-12 15:01:29 +0100632 // Create a rule to write the manifest as textproto. Pretty print it by indenting and
633 // splitting across multiple lines.
634 pbText, err := prototext.MarshalOptions{Indent: " "}.Marshal(&manifest)
Dan Willemsen4591b642021-05-24 14:24:12 -0700635 if err != nil {
636 ReportPathErrorf(r.ctx, "sbox manifest failed to marshal: %q", err)
637 }
Sam Delmerico285b66a2023-09-25 12:13:17 +0000638 if ninjaEscapeCommandString {
639 WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText))
640 } else {
641 // We need to have a rule to write files that is
642 // defined on the RuleBuilder's pctx in order to
643 // write Ninja variables in the string.
644 // The WriteFileRule function above rule can only write
645 // raw strings because it is defined on the android
646 // package's pctx, and it can't access variables defined
647 // in another context.
648 r.ctx.Build(r.pctx, BuildParams{
649 Rule: r.ctx.Rule(r.pctx, "unescapedWriteFile", blueprint.RuleParams{
650 Command: `rm -rf ${out} && cat ${out}.rsp > ${out}`,
651 Rspfile: "${out}.rsp",
652 RspfileContent: "${content}",
653 Description: "write file",
654 }, "content"),
655 Output: r.sboxManifestPath,
656 Description: "write sbox manifest " + r.sboxManifestPath.Base(),
657 Args: map[string]string{
658 "content": string(pbText),
659 },
660 })
661 }
Colin Crosse16ce362020-11-12 08:29:30 -0800662
663 // Generate a new string to use as the command line of the sbox rule. This uses
664 // a RuleBuilderCommand as a convenience method of building the command line, then
665 // converts it to a string to replace commandString.
Colin Crossf1a035e2020-11-16 17:32:30 -0800666 sboxCmd := &RuleBuilderCommand{
667 rule: &RuleBuilder{
668 ctx: r.ctx,
669 },
670 }
Colin Cross9b698b62021-12-22 09:55:32 -0800671 sboxCmd.builtToolWithoutDeps("sbox").
Colin Crosse52c2ac2022-03-28 17:03:35 -0700672 FlagWithArg("--sandbox-path ", shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
673 FlagWithArg("--output-dir ", r.outDir.String()).
674 FlagWithInput("--manifest ", r.sboxManifestPath)
675
676 if r.restat {
677 sboxCmd.Flag("--write-if-changed")
678 }
Colin Crosse16ce362020-11-12 08:29:30 -0800679
680 // Replace the command string, and add the sbox tool and manifest textproto to the
681 // dependencies of the final sbox rule.
Colin Crosscfec40c2019-07-08 17:07:18 -0700682 commandString = sboxCmd.buf.String()
Dan Willemsen633c5022019-04-12 11:11:38 -0700683 tools = append(tools, sboxCmd.tools...)
Colin Crosse16ce362020-11-12 08:29:30 -0800684 inputs = append(inputs, sboxCmd.inputs...)
Colin Crossef972742021-03-12 17:24:45 -0800685
686 if r.rbeParams != nil {
Colin Crosse55bd422021-03-23 13:44:30 -0700687 // RBE needs a list of input files to copy to the remote builder. For inputs already
688 // listed in an rsp file, pass the rsp file directly to rewrapper. For the rest,
689 // create a new rsp file to pass to rewrapper.
690 var remoteRspFiles Paths
691 var remoteInputs Paths
692
693 remoteInputs = append(remoteInputs, inputs...)
694 remoteInputs = append(remoteInputs, tools...)
695
Colin Crossce3a51d2021-03-19 16:22:12 -0700696 for _, rspFile := range rspFiles {
697 remoteInputs = append(remoteInputs, rspFile.file)
698 remoteRspFiles = append(remoteRspFiles, rspFile.file)
Colin Crossef972742021-03-12 17:24:45 -0800699 }
Colin Crosse55bd422021-03-23 13:44:30 -0700700
701 if len(remoteInputs) > 0 {
702 inputsListFile := r.sboxManifestPath.ReplaceExtension(r.ctx, "rbe_inputs.list")
703 writeRspFileRule(r.ctx, inputsListFile, remoteInputs)
704 remoteRspFiles = append(remoteRspFiles, inputsListFile)
705 // Add the new rsp file as an extra input to the rule.
706 inputs = append(inputs, inputsListFile)
707 }
Colin Crossef972742021-03-12 17:24:45 -0800708
709 r.rbeParams.OutputFiles = outputs.Strings()
Colin Crosse55bd422021-03-23 13:44:30 -0700710 r.rbeParams.RSPFiles = remoteRspFiles.Strings()
Colin Crossef972742021-03-12 17:24:45 -0800711 rewrapperCommand := r.rbeParams.NoVarTemplate(r.ctx.Config().RBEWrapper())
712 commandString = rewrapperCommand + " bash -c '" + strings.ReplaceAll(commandString, `'`, `'\''`) + "'"
713 }
Colin Cross3d680512020-11-13 16:23:53 -0800714 } else {
715 // If not using sbox the rule will run the command directly, put the hash of the
716 // list of input files in a comment at the end of the command line to ensure ninja
717 // reruns the rule when the list of input files changes.
718 commandString += " # hash of input list: " + hashSrcFiles(inputs)
Dan Willemsen633c5022019-04-12 11:11:38 -0700719 }
720
Colin Cross1d2cf042019-03-29 15:33:06 -0700721 // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
Colin Cross70c47412021-03-12 17:48:14 -0800722 // ImplicitOutputs. RuleBuilder doesn't use "$out", so the distinction between Outputs and
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700723 // ImplicitOutputs doesn't matter.
Dan Willemsen633c5022019-04-12 11:11:38 -0700724 output := outputs[0]
725 implicitOutputs := outputs[1:]
Colin Cross1d2cf042019-03-29 15:33:06 -0700726
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700727 var rspFile, rspFileContent string
Colin Crossce3a51d2021-03-19 16:22:12 -0700728 var rspFileInputs Paths
729 if len(rspFiles) > 0 {
730 // The first rsp files uses Ninja's rsp file support for the rule
731 rspFile = rspFiles[0].file.String()
Colin Crosse55bd422021-03-23 13:44:30 -0700732 // Use "$in" for rspFileContent to avoid duplicating the list of files in the dependency
733 // list and in the contents of the rsp file. Inputs to the rule that are not in the
734 // rsp file will be listed in Implicits instead of Inputs so they don't show up in "$in".
735 rspFileContent = "$in"
Colin Crossce3a51d2021-03-19 16:22:12 -0700736 rspFileInputs = append(rspFileInputs, rspFiles[0].paths...)
737
738 for _, rspFile := range rspFiles[1:] {
739 // Any additional rsp files need an extra rule to write the file.
740 writeRspFileRule(r.ctx, rspFile.file, rspFile.paths)
741 // The main rule needs to depend on the inputs listed in the extra rsp file.
742 inputs = append(inputs, rspFile.paths...)
743 // The main rule needs to depend on the extra rsp file.
744 inputs = append(inputs, rspFile.file)
745 }
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700746 }
747
Colin Cross8b8bec32019-11-15 13:18:43 -0800748 var pool blueprint.Pool
Colin Crossf1a035e2020-11-16 17:32:30 -0800749 if r.ctx.Config().UseGoma() && r.remoteable.Goma {
Colin Cross8b8bec32019-11-15 13:18:43 -0800750 // 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 -0800751 } else if r.ctx.Config().UseRBE() && r.remoteable.RBE {
Ramy Medhat944839a2020-03-31 22:14:52 -0400752 // When USE_RBE=true is set and the rule is supported by RBE, use the remotePool.
753 pool = remotePool
Colin Cross8b8bec32019-11-15 13:18:43 -0800754 } else if r.highmem {
755 pool = highmemPool
Colin Crossf1a035e2020-11-16 17:32:30 -0800756 } else if r.ctx.Config().UseRemoteBuild() {
Colin Cross8b8bec32019-11-15 13:18:43 -0800757 pool = localPool
758 }
759
Sam Delmericod46f6c82023-09-25 12:13:17 +0000760 if ninjaEscapeCommandString {
761 commandString = proptools.NinjaEscape(commandString)
762 }
763
Colin Crossf1a035e2020-11-16 17:32:30 -0800764 r.ctx.Build(r.pctx, BuildParams{
Sam Delmerico285b66a2023-09-25 12:13:17 +0000765 Rule: r.ctx.Rule(r.pctx, name, blueprint.RuleParams{
Sam Delmericod46f6c82023-09-25 12:13:17 +0000766 Command: commandString,
Colin Cross45029782021-03-16 16:49:52 -0700767 CommandDeps: proptools.NinjaEscapeList(tools.Strings()),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700768 Restat: r.restat,
Colin Cross45029782021-03-16 16:49:52 -0700769 Rspfile: proptools.NinjaEscape(rspFile),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700770 RspfileContent: rspFileContent,
Colin Cross8b8bec32019-11-15 13:18:43 -0800771 Pool: pool,
Dan Willemsen633c5022019-04-12 11:11:38 -0700772 }),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700773 Inputs: rspFileInputs,
Colin Cross3d680512020-11-13 16:23:53 -0800774 Implicits: inputs,
Colin Crossda6401b2021-04-21 11:32:19 -0700775 OrderOnly: r.OrderOnlys(),
Colin Crossae89abe2021-04-21 11:45:23 -0700776 Validations: r.Validations(),
Dan Willemsen633c5022019-04-12 11:11:38 -0700777 Output: output,
778 ImplicitOutputs: implicitOutputs,
Jingwen Chence679d22020-09-23 04:30:02 +0000779 SymlinkOutputs: r.SymlinkOutputs(),
Dan Willemsen633c5022019-04-12 11:11:38 -0700780 Depfile: depFile,
781 Deps: depFormat,
782 Description: desc,
783 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800784}
785
Colin Cross758290d2019-02-01 16:42:32 -0800786// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
787// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the
788// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
789// space as a separator from the previous method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800790type RuleBuilderCommand struct {
Colin Crossf1a035e2020-11-16 17:32:30 -0800791 rule *RuleBuilder
792
Jingwen Chence679d22020-09-23 04:30:02 +0000793 buf strings.Builder
794 inputs Paths
795 implicits Paths
796 orderOnlys Paths
Colin Crossae89abe2021-04-21 11:45:23 -0700797 validations Paths
Jingwen Chence679d22020-09-23 04:30:02 +0000798 outputs WritablePaths
799 symlinkOutputs WritablePaths
800 depFiles WritablePaths
801 tools Paths
Colin Crossba9e4032020-11-24 16:32:22 -0800802 packagedTools []PackagingSpec
Colin Crossce3a51d2021-03-19 16:22:12 -0700803 rspFiles []rspFileAndPaths
804}
805
806type rspFileAndPaths struct {
807 file WritablePath
808 paths Paths
Dan Willemsen633c5022019-04-12 11:11:38 -0700809}
810
Paul Duffin3866b892021-10-04 11:24:48 +0100811func checkPathNotNil(path Path) {
812 if path == nil {
813 panic("rule_builder paths cannot be nil")
814 }
815}
816
Dan Willemsen633c5022019-04-12 11:11:38 -0700817func (c *RuleBuilderCommand) addInput(path Path) string {
Paul Duffin3866b892021-10-04 11:24:48 +0100818 checkPathNotNil(path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700819 c.inputs = append(c.inputs, path)
Colin Crossab020a72021-03-12 17:52:23 -0800820 return c.PathForInput(path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700821}
822
Colin Crossab020a72021-03-12 17:52:23 -0800823func (c *RuleBuilderCommand) addImplicit(path Path) {
Paul Duffin3866b892021-10-04 11:24:48 +0100824 checkPathNotNil(path)
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400825 c.implicits = append(c.implicits, path)
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400826}
827
Colin Crossda71eda2020-02-21 16:55:19 -0800828func (c *RuleBuilderCommand) addOrderOnly(path Path) {
Paul Duffin3866b892021-10-04 11:24:48 +0100829 checkPathNotNil(path)
Colin Crossda71eda2020-02-21 16:55:19 -0800830 c.orderOnlys = append(c.orderOnlys, path)
831}
832
Colin Crossab020a72021-03-12 17:52:23 -0800833// PathForInput takes an input path and returns the appropriate path to use on the command line. If
834// sbox was enabled via a call to RuleBuilder.Sbox() and the path was an output path it returns a
835// path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
836// original path.
837func (c *RuleBuilderCommand) PathForInput(path Path) string {
838 if c.rule.sbox {
839 rel, inSandbox := c.rule._sboxPathForInputRel(path)
840 if inSandbox {
841 rel = filepath.Join(sboxSandboxBaseDir, rel)
842 }
843 return rel
844 }
845 return path.String()
846}
847
848// PathsForInputs takes a list of input paths and returns the appropriate paths to use on the
849// command line. If sbox was enabled via a call to RuleBuilder.Sbox() a path was an output path, it
850// returns the path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it
851// returns the original paths.
852func (c *RuleBuilderCommand) PathsForInputs(paths Paths) []string {
853 ret := make([]string, len(paths))
854 for i, path := range paths {
855 ret[i] = c.PathForInput(path)
856 }
857 return ret
858}
859
Colin Crossf1a035e2020-11-16 17:32:30 -0800860// PathForOutput takes an output path and returns the appropriate path to use on the command
861// line. If sbox was enabled via a call to RuleBuilder.Sbox(), it returns a path with the
862// placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
863// original path.
864func (c *RuleBuilderCommand) PathForOutput(path WritablePath) string {
865 if c.rule.sbox {
866 // Errors will be handled in RuleBuilder.Build where we have a context to report them
867 rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
868 return filepath.Join(sboxOutDir, rel)
Dan Willemsen633c5022019-04-12 11:11:38 -0700869 }
870 return path.String()
Colin Crossfeec25b2019-01-30 17:32:39 -0800871}
872
Colin Crossba9e4032020-11-24 16:32:22 -0800873func sboxPathForToolRel(ctx BuilderContext, path Path) string {
874 // Errors will be handled in RuleBuilder.Build where we have a context to report them
Cole Faust3b703f32023-10-16 13:30:51 -0700875 toolDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "")
Colin Cross790ef352021-10-25 19:15:55 -0700876 relOutSoong, isRelOutSoong, _ := maybeRelErr(toolDir.String(), path.String())
877 if isRelOutSoong {
878 // The tool is in the Soong output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
879 return filepath.Join(sboxToolsSubDir, "out", relOutSoong)
Colin Crossba9e4032020-11-24 16:32:22 -0800880 }
881 // The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
882 return filepath.Join(sboxToolsSubDir, "src", path.String())
883}
884
Colin Crossab020a72021-03-12 17:52:23 -0800885func (r *RuleBuilder) _sboxPathForInputRel(path Path) (rel string, inSandbox bool) {
886 // Errors will be handled in RuleBuilder.Build where we have a context to report them
887 rel, isRelSboxOut, _ := maybeRelErr(r.outDir.String(), path.String())
888 if isRelSboxOut {
889 return filepath.Join(sboxOutSubDir, rel), true
890 }
891 if r.sboxInputs {
892 // When sandboxing inputs all inputs have to be copied into the sandbox. Input files that
893 // are outputs of other rules could be an arbitrary absolute path if OUT_DIR is set, so they
894 // will be copied to relative paths under __SBOX_OUT_DIR__/out.
Cole Fauste8561c62023-11-30 17:26:37 -0800895 rel, isRelOut, _ := maybeRelErr(r.ctx.Config().OutDir(), path.String())
Colin Crossab020a72021-03-12 17:52:23 -0800896 if isRelOut {
897 return filepath.Join(sboxOutSubDir, rel), true
898 }
899 }
900 return path.String(), false
901}
902
903func (r *RuleBuilder) sboxPathForInputRel(path Path) string {
904 rel, _ := r._sboxPathForInputRel(path)
905 return rel
906}
907
908func (r *RuleBuilder) sboxPathsForInputsRel(paths Paths) []string {
909 ret := make([]string, len(paths))
910 for i, path := range paths {
911 ret[i] = r.sboxPathForInputRel(path)
912 }
913 return ret
914}
915
Colin Crossba9e4032020-11-24 16:32:22 -0800916func sboxPathForPackagedToolRel(spec PackagingSpec) string {
917 return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage)
918}
919
Colin Crossd11cf622021-03-23 22:30:35 -0700920// PathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
921// tool after copying it into the sandbox. This can be used on the RuleBuilder command line to
922// reference the tool.
923func (c *RuleBuilderCommand) PathForPackagedTool(spec PackagingSpec) string {
924 if !c.rule.sboxTools {
925 panic("PathForPackagedTool() requires SandboxTools()")
926 }
927
928 return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
929}
930
Colin Crossba9e4032020-11-24 16:32:22 -0800931// PathForTool takes a path to a tool, which may be an output file or a source file, and returns
932// the corresponding path for the tool in the sbox sandbox if sbox is enabled, or the original path
933// if it is not. This can be used on the RuleBuilder command line to reference the tool.
934func (c *RuleBuilderCommand) PathForTool(path Path) string {
935 if c.rule.sbox && c.rule.sboxTools {
936 return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path))
937 }
938 return path.String()
939}
940
Colin Crossd11cf622021-03-23 22:30:35 -0700941// PathsForTools takes a list of paths to tools, which may be output files or source files, and
942// returns the corresponding paths for the tools in the sbox sandbox if sbox is enabled, or the
943// original paths if it is not. This can be used on the RuleBuilder command line to reference the tool.
944func (c *RuleBuilderCommand) PathsForTools(paths Paths) []string {
945 if c.rule.sbox && c.rule.sboxTools {
946 var ret []string
947 for _, path := range paths {
948 ret = append(ret, filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path)))
949 }
950 return ret
951 }
952 return paths.Strings()
953}
954
Colin Crossba9e4032020-11-24 16:32:22 -0800955// PackagedTool adds the specified tool path to the command line. It can only be used with tool
956// sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox.
957func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand {
958 if !c.rule.sboxTools {
959 panic("PackagedTool() requires SandboxTools()")
960 }
961
962 c.packagedTools = append(c.packagedTools, spec)
963 c.Text(sboxPathForPackagedToolRel(spec))
964 return c
965}
966
967// ImplicitPackagedTool copies the specified tool into the sandbox without modifying the command
968// line. It can only be used with tool sandboxing enabled by SandboxTools().
969func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuilderCommand {
970 if !c.rule.sboxTools {
971 panic("ImplicitPackagedTool() requires SandboxTools()")
972 }
973
974 c.packagedTools = append(c.packagedTools, spec)
975 return c
976}
977
978// ImplicitPackagedTools copies the specified tools into the sandbox without modifying the command
979// line. It can only be used with tool sandboxing enabled by SandboxTools().
980func (c *RuleBuilderCommand) ImplicitPackagedTools(specs []PackagingSpec) *RuleBuilderCommand {
981 if !c.rule.sboxTools {
982 panic("ImplicitPackagedTools() requires SandboxTools()")
983 }
984
985 c.packagedTools = append(c.packagedTools, specs...)
986 return c
987}
988
Colin Cross758290d2019-02-01 16:42:32 -0800989// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
990// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800991func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
Colin Crosscfec40c2019-07-08 17:07:18 -0700992 if c.buf.Len() > 0 {
993 c.buf.WriteByte(' ')
Colin Crossfeec25b2019-01-30 17:32:39 -0800994 }
Colin Crosscfec40c2019-07-08 17:07:18 -0700995 c.buf.WriteString(text)
Colin Crossfeec25b2019-01-30 17:32:39 -0800996 return c
997}
998
Colin Cross758290d2019-02-01 16:42:32 -0800999// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or
1000// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001001func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
1002 return c.Text(fmt.Sprintf(format, a...))
1003}
1004
Colin Cross758290d2019-02-01 16:42:32 -08001005// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the
1006// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001007func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
1008 return c.Text(flag)
1009}
1010
Colin Crossab054432019-07-15 16:13:59 -07001011// OptionalFlag adds the specified raw text to the command line if it is not nil. The text should not contain input or
1012// output paths or the rule will not have them listed in its dependencies or outputs.
1013func (c *RuleBuilderCommand) OptionalFlag(flag *string) *RuleBuilderCommand {
1014 if flag != nil {
1015 c.Text(*flag)
1016 }
1017
1018 return c
1019}
1020
Colin Cross92b7d582019-03-29 15:32:51 -07001021// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the
1022// rule will not have them listed in its dependencies or outputs.
1023func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
1024 for _, flag := range flags {
1025 c.Text(flag)
1026 }
1027 return c
1028}
1029
Colin Cross758290d2019-02-01 16:42:32 -08001030// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
1031// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
1032// outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001033func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
1034 return c.Text(flag + arg)
1035}
1036
Colin Crossc7ed0042019-02-11 14:11:09 -08001037// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to
1038// calling FlagWithArg for argument.
1039func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand {
1040 for _, arg := range args {
1041 c.FlagWithArg(flag, arg)
1042 }
1043 return c
1044}
1045
Roland Levillain2da5d9a2019-02-27 16:56:41 +00001046// 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 -08001047// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or
1048// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001049func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
1050 return c.Text(flag + strings.Join(list, sep))
1051}
1052
Colin Cross758290d2019-02-01 16:42:32 -08001053// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by
1054// RuleBuilder.Tools.
Colin Cross69f59a32019-02-15 10:39:37 -08001055func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001056 checkPathNotNil(path)
Colin Crossfeec25b2019-01-30 17:32:39 -08001057 c.tools = append(c.tools, path)
Colin Crossba9e4032020-11-24 16:32:22 -08001058 return c.Text(c.PathForTool(path))
1059}
1060
1061// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
1062func (c *RuleBuilderCommand) ImplicitTool(path Path) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001063 checkPathNotNil(path)
Colin Crossba9e4032020-11-24 16:32:22 -08001064 c.tools = append(c.tools, path)
1065 return c
1066}
1067
1068// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
1069func (c *RuleBuilderCommand) ImplicitTools(paths Paths) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001070 for _, path := range paths {
1071 c.ImplicitTool(path)
1072 }
Colin Crossba9e4032020-11-24 16:32:22 -08001073 return c
Colin Crossfeec25b2019-01-30 17:32:39 -08001074}
1075
Colin Crossee94d6a2019-07-08 17:08:34 -07001076// BuiltTool adds the specified tool path that was built using a host Soong module to the command line. The path will
1077// be also added to the dependencies returned by RuleBuilder.Tools.
1078//
1079// It is equivalent to:
Colin Crossd079e0b2022-08-16 10:27:33 -07001080//
1081// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
Colin Crossf1a035e2020-11-16 17:32:30 -08001082func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
Colin Cross9b698b62021-12-22 09:55:32 -08001083 if c.rule.ctx.Config().UseHostMusl() {
1084 // If the host is using musl, assume that the tool was built against musl libc and include
1085 // libc_musl.so in the sandbox.
1086 // TODO(ccross): if we supported adding new dependencies during GenerateAndroidBuildActions
1087 // this could be a dependency + TransitivePackagingSpecs.
1088 c.ImplicitTool(c.rule.ctx.Config().HostJNIToolPath(c.rule.ctx, "libc_musl"))
1089 }
1090 return c.builtToolWithoutDeps(tool)
1091}
1092
1093// builtToolWithoutDeps is similar to BuiltTool, but doesn't add any dependencies. It is used
1094// internally by RuleBuilder for helper tools that are known to be compiled statically.
1095func (c *RuleBuilderCommand) builtToolWithoutDeps(tool string) *RuleBuilderCommand {
Colin Crossf1a035e2020-11-16 17:32:30 -08001096 return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
Colin Crossee94d6a2019-07-08 17:08:34 -07001097}
1098
1099// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the
1100// dependencies returned by RuleBuilder.Tools.
1101//
1102// It is equivalent to:
Colin Crossd079e0b2022-08-16 10:27:33 -07001103//
1104// cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
Colin Crossee94d6a2019-07-08 17:08:34 -07001105func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
1106 return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
1107}
1108
Colin Cross758290d2019-02-01 16:42:32 -08001109// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
1110// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001111func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001112 return c.Text(c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001113}
1114
Colin Cross758290d2019-02-01 16:42:32 -08001115// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
1116// dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001117func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001118 for _, path := range paths {
1119 c.Input(path)
1120 }
1121 return c
1122}
1123
1124// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
1125// command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001126func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001127 c.addImplicit(path)
Colin Crossfeec25b2019-01-30 17:32:39 -08001128 return c
1129}
1130
Colin Cross758290d2019-02-01 16:42:32 -08001131// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
1132// command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001133func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001134 for _, path := range paths {
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001135 c.addImplicit(path)
Dan Willemsen633c5022019-04-12 11:11:38 -07001136 }
Colin Crossfeec25b2019-01-30 17:32:39 -08001137 return c
1138}
1139
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001140// GetImplicits returns the command's implicit inputs.
1141func (c *RuleBuilderCommand) GetImplicits() Paths {
1142 return c.implicits
1143}
1144
Colin Crossda71eda2020-02-21 16:55:19 -08001145// OrderOnly adds the specified input path to the dependencies returned by RuleBuilder.OrderOnlys
1146// without modifying the command line.
1147func (c *RuleBuilderCommand) OrderOnly(path Path) *RuleBuilderCommand {
1148 c.addOrderOnly(path)
1149 return c
1150}
1151
1152// OrderOnlys adds the specified input paths to the dependencies returned by RuleBuilder.OrderOnlys
1153// without modifying the command line.
1154func (c *RuleBuilderCommand) OrderOnlys(paths Paths) *RuleBuilderCommand {
1155 for _, path := range paths {
1156 c.addOrderOnly(path)
1157 }
1158 return c
1159}
1160
Colin Crossae89abe2021-04-21 11:45:23 -07001161// Validation adds the specified input path to the validation dependencies by
1162// RuleBuilder.Validations without modifying the command line.
1163func (c *RuleBuilderCommand) Validation(path Path) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001164 checkPathNotNil(path)
Colin Crossae89abe2021-04-21 11:45:23 -07001165 c.validations = append(c.validations, path)
1166 return c
1167}
1168
1169// Validations adds the specified input paths to the validation dependencies by
1170// RuleBuilder.Validations without modifying the command line.
1171func (c *RuleBuilderCommand) Validations(paths Paths) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001172 for _, path := range paths {
1173 c.Validation(path)
1174 }
Colin Crossae89abe2021-04-21 11:45:23 -07001175 return c
1176}
1177
Colin Cross758290d2019-02-01 16:42:32 -08001178// Output adds the specified output path to the command line. The path will also be added to the outputs returned by
1179// RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001180func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001181 checkPathNotNil(path)
Colin Crossfeec25b2019-01-30 17:32:39 -08001182 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001183 return c.Text(c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001184}
1185
Colin Cross758290d2019-02-01 16:42:32 -08001186// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
1187// the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001188func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001189 for _, path := range paths {
1190 c.Output(path)
1191 }
1192 return c
1193}
1194
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001195// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
1196// and will be the temporary output directory managed by sbox, not the final one.
1197func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand {
Colin Crossf1a035e2020-11-16 17:32:30 -08001198 if !c.rule.sbox {
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001199 panic("OutputDir only valid with Sbox")
1200 }
Colin Cross3d680512020-11-13 16:23:53 -08001201 return c.Text(sboxOutDir)
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001202}
1203
Colin Cross1d2cf042019-03-29 15:33:06 -07001204// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
1205// line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles are added to
1206// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
1207func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001208 checkPathNotNil(path)
Colin Cross1d2cf042019-03-29 15:33:06 -07001209 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001210 return c.Text(c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -07001211}
1212
Colin Cross758290d2019-02-01 16:42:32 -08001213// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
1214// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001215func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001216 c.outputs = append(c.outputs, path)
1217 return c
1218}
1219
Colin Cross758290d2019-02-01 16:42:32 -08001220// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
1221// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001222func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001223 c.outputs = append(c.outputs, paths...)
1224 return c
1225}
1226
Jingwen Chence679d22020-09-23 04:30:02 +00001227// ImplicitSymlinkOutput declares the specified path as an implicit output that
1228// will be a symlink instead of a regular file. Does not modify the command
1229// line.
1230func (c *RuleBuilderCommand) ImplicitSymlinkOutput(path WritablePath) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001231 checkPathNotNil(path)
Jingwen Chence679d22020-09-23 04:30:02 +00001232 c.symlinkOutputs = append(c.symlinkOutputs, path)
1233 return c.ImplicitOutput(path)
1234}
1235
1236// ImplicitSymlinkOutputs declares the specified paths as implicit outputs that
1237// will be a symlinks instead of regular files. Does not modify the command
1238// line.
1239func (c *RuleBuilderCommand) ImplicitSymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
1240 for _, path := range paths {
1241 c.ImplicitSymlinkOutput(path)
1242 }
1243 return c
1244}
1245
1246// SymlinkOutput declares the specified path as an output that will be a symlink
1247// instead of a regular file. Modifies the command line.
1248func (c *RuleBuilderCommand) SymlinkOutput(path WritablePath) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001249 checkPathNotNil(path)
Jingwen Chence679d22020-09-23 04:30:02 +00001250 c.symlinkOutputs = append(c.symlinkOutputs, path)
1251 return c.Output(path)
1252}
1253
1254// SymlinkOutputsl declares the specified paths as outputs that will be symlinks
1255// instead of regular files. Modifies the command line.
1256func (c *RuleBuilderCommand) SymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
1257 for _, path := range paths {
1258 c.SymlinkOutput(path)
1259 }
1260 return c
1261}
1262
Colin Cross1d2cf042019-03-29 15:33:06 -07001263// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
1264// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles
1265// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
1266// depfiles together.
1267func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand {
1268 c.depFiles = append(c.depFiles, path)
1269 return c
1270}
1271
Colin Cross758290d2019-02-01 16:42:32 -08001272// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
1273// will also be added to the dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001274func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001275 return c.Text(flag + c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001276}
1277
Colin Cross758290d2019-02-01 16:42:32 -08001278// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
1279// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
1280// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001281func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001282 strs := make([]string, len(paths))
1283 for i, path := range paths {
1284 strs[i] = c.addInput(path)
1285 }
1286 return c.FlagWithList(flag, strs, sep)
Colin Crossfeec25b2019-01-30 17:32:39 -08001287}
1288
Colin Cross758290d2019-02-01 16:42:32 -08001289// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
1290// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for
1291// each input path.
Colin Cross69f59a32019-02-15 10:39:37 -08001292func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001293 for _, path := range paths {
1294 c.FlagWithInput(flag, path)
1295 }
1296 return c
1297}
1298
1299// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path
1300// will also be added to the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001301func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001302 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001303 return c.Text(flag + c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001304}
1305
Colin Cross1d2cf042019-03-29 15:33:06 -07001306// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path
1307// will also be added to the outputs returned by RuleBuilder.Outputs.
1308func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
1309 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001310 return c.Text(flag + c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -07001311}
1312
Colin Crossce3a51d2021-03-19 16:22:12 -07001313// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with
1314// no separator between them. The paths will be written to the rspfile. If sbox is enabled, the
1315// rspfile must be outside the sbox directory. The first use of FlagWithRspFileInputList in any
1316// RuleBuilderCommand of a RuleBuilder will use Ninja's rsp file support for the rule, additional
1317// uses will result in an auxiliary rules to write the rspFile contents.
Colin Cross70c47412021-03-12 17:48:14 -08001318func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, rspFile WritablePath, paths Paths) *RuleBuilderCommand {
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001319 // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
1320 // generated.
1321 if paths == nil {
1322 paths = Paths{}
1323 }
1324
Colin Crossce3a51d2021-03-19 16:22:12 -07001325 c.rspFiles = append(c.rspFiles, rspFileAndPaths{rspFile, paths})
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001326
Colin Cross70c47412021-03-12 17:48:14 -08001327 if c.rule.sbox {
1328 if _, isRel, _ := maybeRelErr(c.rule.outDir.String(), rspFile.String()); isRel {
1329 panic(fmt.Errorf("FlagWithRspFileInputList rspfile %q must not be inside out dir %q",
1330 rspFile.String(), c.rule.outDir.String()))
1331 }
1332 }
1333
Colin Crossab020a72021-03-12 17:52:23 -08001334 c.FlagWithArg(flag, c.PathForInput(rspFile))
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001335 return c
1336}
1337
Colin Cross758290d2019-02-01 16:42:32 -08001338// String returns the command line.
1339func (c *RuleBuilderCommand) String() string {
Colin Crosscfec40c2019-07-08 17:07:18 -07001340 return c.buf.String()
Colin Cross758290d2019-02-01 16:42:32 -08001341}
Colin Cross1d2cf042019-03-29 15:33:06 -07001342
Colin Crosse16ce362020-11-12 08:29:30 -08001343// RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox()
1344// and returns sbox testproto generated by the RuleBuilder.
Colin Crossf61d03d2023-11-02 16:56:39 -07001345func RuleBuilderSboxProtoForTests(t *testing.T, ctx *TestContext, params TestingBuildParams) *sbox_proto.Manifest {
Colin Crosse16ce362020-11-12 08:29:30 -08001346 t.Helper()
Colin Crossf61d03d2023-11-02 16:56:39 -07001347 content := ContentFromFileRuleForTests(t, ctx, params)
Colin Crosse16ce362020-11-12 08:29:30 -08001348 manifest := sbox_proto.Manifest{}
Dan Willemsen4591b642021-05-24 14:24:12 -07001349 err := prototext.Unmarshal([]byte(content), &manifest)
Colin Crosse16ce362020-11-12 08:29:30 -08001350 if err != nil {
1351 t.Fatalf("failed to unmarshal manifest: %s", err.Error())
1352 }
1353 return &manifest
1354}
1355
Colin Cross1d2cf042019-03-29 15:33:06 -07001356func ninjaNameEscape(s string) string {
1357 b := []byte(s)
1358 escaped := false
1359 for i, c := range b {
1360 valid := (c >= 'a' && c <= 'z') ||
1361 (c >= 'A' && c <= 'Z') ||
1362 (c >= '0' && c <= '9') ||
1363 (c == '_') ||
1364 (c == '-') ||
1365 (c == '.')
1366 if !valid {
1367 b[i] = '_'
1368 escaped = true
1369 }
1370 }
1371 if escaped {
1372 s = string(b)
1373 }
1374 return s
1375}
Colin Cross3d680512020-11-13 16:23:53 -08001376
1377// hashSrcFiles returns a hash of the list of source files. It is used to ensure the command line
1378// or the sbox textproto manifest change even if the input files are not listed on the command line.
1379func hashSrcFiles(srcFiles Paths) string {
1380 h := sha256.New()
1381 srcFileList := strings.Join(srcFiles.Strings(), "\n")
1382 h.Write([]byte(srcFileList))
1383 return fmt.Sprintf("%x", h.Sum(nil))
1384}
Colin Crossf1a035e2020-11-16 17:32:30 -08001385
1386// BuilderContextForTesting returns a BuilderContext for the given config that can be used for tests
1387// that need to call methods that take a BuilderContext.
1388func BuilderContextForTesting(config Config) BuilderContext {
1389 pathCtx := PathContextForTesting(config)
1390 return builderContextForTests{
1391 PathContext: pathCtx,
1392 }
1393}
1394
1395type builderContextForTests struct {
1396 PathContext
1397}
1398
1399func (builderContextForTests) Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule {
1400 return nil
1401}
1402func (builderContextForTests) Build(PackageContext, BuildParams) {}
Colin Crossef972742021-03-12 17:24:45 -08001403
Colin Crosse55bd422021-03-23 13:44:30 -07001404func writeRspFileRule(ctx BuilderContext, rspFile WritablePath, paths Paths) {
1405 buf := &strings.Builder{}
1406 err := response.WriteRspFile(buf, paths.Strings())
1407 if err != nil {
1408 // There should never be I/O errors writing to a bytes.Buffer.
1409 panic(err)
Colin Crossef972742021-03-12 17:24:45 -08001410 }
Colin Crosse55bd422021-03-23 13:44:30 -07001411 WriteFileRule(ctx, rspFile, buf.String())
Colin Crossef972742021-03-12 17:24:45 -08001412}