blob: a1573868b8feb700cde7becbd8dd3b6f09af8845 [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
Inseob Kimf7cd03e2024-09-06 17:25:00 +090041const nsjailToolsSubDir = "tools"
42const nsjailOutDir = "out"
43
Colin Cross758290d2019-02-01 16:42:32 -080044// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
45// graph.
Colin Crossfeec25b2019-01-30 17:32:39 -080046type RuleBuilder struct {
Colin Crossf1a035e2020-11-16 17:32:30 -080047 pctx PackageContext
48 ctx BuilderContext
49
Colin Crosse16ce362020-11-12 08:29:30 -080050 commands []*RuleBuilderCommand
51 installs RuleBuilderInstalls
52 temporariesSet map[WritablePath]bool
53 restat bool
54 sbox bool
55 highmem bool
56 remoteable RemoteRuleSupports
Colin Crossef972742021-03-12 17:24:45 -080057 rbeParams *remoteexec.REParams
Colin Crossf1a035e2020-11-16 17:32:30 -080058 outDir WritablePath
Spandan Dasaf4ccaa2023-06-29 01:15:51 +000059 sboxOutSubDir string
Colin Crossba9e4032020-11-24 16:32:22 -080060 sboxTools bool
Colin Crossab020a72021-03-12 17:52:23 -080061 sboxInputs bool
Colin Crosse16ce362020-11-12 08:29:30 -080062 sboxManifestPath WritablePath
63 missingDeps []string
Devin Mooreb6cc64f2024-07-15 22:31:24 +000064 args map[string]string
Inseob Kimf7cd03e2024-09-06 17:25:00 +090065 nsjail bool
66 nsjailBasePath WritablePath
67 nsjailImplicits Paths
Colin Crossfeec25b2019-01-30 17:32:39 -080068}
69
Colin Cross758290d2019-02-01 16:42:32 -080070// NewRuleBuilder returns a newly created RuleBuilder.
Colin Crossf1a035e2020-11-16 17:32:30 -080071func NewRuleBuilder(pctx PackageContext, ctx BuilderContext) *RuleBuilder {
Colin Cross5cb5b092019-02-02 21:25:18 -080072 return &RuleBuilder{
Colin Crossf1a035e2020-11-16 17:32:30 -080073 pctx: pctx,
74 ctx: ctx,
Colin Cross69f59a32019-02-15 10:39:37 -080075 temporariesSet: make(map[WritablePath]bool),
Spandan Dasaf4ccaa2023-06-29 01:15:51 +000076 sboxOutSubDir: sboxOutSubDir,
Colin Cross5cb5b092019-02-02 21:25:18 -080077 }
Colin Cross758290d2019-02-01 16:42:32 -080078}
79
Spandan Dasaf4ccaa2023-06-29 01:15:51 +000080// SetSboxOutDirDirAsEmpty sets the out subdirectory to an empty string
81// This is useful for sandboxing actions that change the execution root to a path in out/ (e.g mixed builds)
82// For such actions, SetSboxOutDirDirAsEmpty ensures that the path does not become $SBOX_SANDBOX_DIR/out/out/bazel/output/execroot/__main__/...
83func (rb *RuleBuilder) SetSboxOutDirDirAsEmpty() *RuleBuilder {
84 rb.sboxOutSubDir = ""
85 return rb
86}
87
Devin Mooreb6cc64f2024-07-15 22:31:24 +000088// Set the phony_output argument.
89// This causes the output files to be ignored.
90// If the output isn't created, it's not treated as an error.
91// The build rule is run every time whether or not the output is created.
92func (rb *RuleBuilder) SetPhonyOutput() {
93 if rb.args == nil {
94 rb.args = make(map[string]string)
95 }
96 rb.args["phony_output"] = "true"
97}
98
Colin Cross758290d2019-02-01 16:42:32 -080099// RuleBuilderInstall is a tuple of install from and to locations.
100type RuleBuilderInstall struct {
Colin Cross69f59a32019-02-15 10:39:37 -0800101 From Path
102 To string
Colin Cross758290d2019-02-01 16:42:32 -0800103}
104
Colin Crossdeabb942019-02-11 14:11:09 -0800105type RuleBuilderInstalls []RuleBuilderInstall
106
107// String returns the RuleBuilderInstalls in the form used by $(call copy-many-files) in Make, a space separated
108// list of from:to tuples.
109func (installs RuleBuilderInstalls) String() string {
110 sb := strings.Builder{}
111 for i, install := range installs {
112 if i != 0 {
113 sb.WriteRune(' ')
114 }
Colin Cross69f59a32019-02-15 10:39:37 -0800115 sb.WriteString(install.From.String())
Colin Crossdeabb942019-02-11 14:11:09 -0800116 sb.WriteRune(':')
117 sb.WriteString(install.To)
118 }
119 return sb.String()
120}
121
Colin Cross0d2f40a2019-02-05 22:31:15 -0800122// MissingDeps adds modules to the list of missing dependencies. If MissingDeps
123// is called with a non-empty input, any call to Build will result in a rule
124// that will print an error listing the missing dependencies and fail.
125// MissingDeps should only be called if Config.AllowMissingDependencies() is
126// true.
127func (r *RuleBuilder) MissingDeps(missingDeps []string) {
128 r.missingDeps = append(r.missingDeps, missingDeps...)
129}
130
Colin Cross758290d2019-02-01 16:42:32 -0800131// 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 -0800132func (r *RuleBuilder) Restat() *RuleBuilder {
133 r.restat = true
134 return r
135}
136
Colin Cross8b8bec32019-11-15 13:18:43 -0800137// HighMem marks the rule as a high memory rule, which will limit how many run in parallel with other high memory
138// rules.
139func (r *RuleBuilder) HighMem() *RuleBuilder {
140 r.highmem = true
141 return r
142}
143
144// Remoteable marks the rule as supporting remote execution.
145func (r *RuleBuilder) Remoteable(supports RemoteRuleSupports) *RuleBuilder {
146 r.remoteable = supports
147 return r
148}
149
Colin Crossef972742021-03-12 17:24:45 -0800150// Rewrapper marks the rule as running inside rewrapper using the given params in order to support
151// running on RBE. During RuleBuilder.Build the params will be combined with the inputs, outputs
152// and tools known to RuleBuilder to prepend an appropriate rewrapper command line to the rule's
153// command line.
154func (r *RuleBuilder) Rewrapper(params *remoteexec.REParams) *RuleBuilder {
155 if !r.sboxInputs {
156 panic(fmt.Errorf("RuleBuilder.Rewrapper must be called after RuleBuilder.SandboxInputs"))
157 }
158 r.rbeParams = params
159 return r
160}
161
Colin Crosse16ce362020-11-12 08:29:30 -0800162// Sbox marks the rule as needing to be wrapped by sbox. The outputDir should point to the output
163// directory that sbox will wipe. It should not be written to by any other rule. manifestPath should
164// point to a location where sbox's manifest will be written and must be outside outputDir. sbox
165// will ensure that all outputs have been written, and will discard any output files that were not
166// specified.
Colin Crosse16ce362020-11-12 08:29:30 -0800167func (r *RuleBuilder) Sbox(outputDir WritablePath, manifestPath WritablePath) *RuleBuilder {
Dan Willemsen633c5022019-04-12 11:11:38 -0700168 if r.sbox {
169 panic("Sbox() may not be called more than once")
170 }
171 if len(r.commands) > 0 {
172 panic("Sbox() may not be called after Command()")
173 }
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900174 if r.nsjail {
175 panic("Sbox() may not be called after Nsjail()")
176 }
Dan Willemsen633c5022019-04-12 11:11:38 -0700177 r.sbox = true
Colin Crossf1a035e2020-11-16 17:32:30 -0800178 r.outDir = outputDir
Colin Crosse16ce362020-11-12 08:29:30 -0800179 r.sboxManifestPath = manifestPath
Dan Willemsen633c5022019-04-12 11:11:38 -0700180 return r
181}
182
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900183// Nsjail marks the rule as needing to be wrapped by nsjail. The outputDir should point to the
184// output directory that nsjail will mount to out/. It should not be written to by any other rule.
185// baseDir should point to a location where nsjail will mount to /nsjail_build_sandbox, which will
186// be the working directory of the command.
187func (r *RuleBuilder) Nsjail(outputDir WritablePath, baseDir WritablePath) *RuleBuilder {
188 if len(r.commands) > 0 {
189 panic("Nsjail() may not be called after Command()")
190 }
191 if r.sbox {
192 panic("Nsjail() may not be called after Sbox()")
193 }
194 r.nsjail = true
195 r.outDir = outputDir
196 r.nsjailBasePath = baseDir
197 return r
198}
199
200// NsjailImplicits adds implicit inputs that are not directly mounted. This is useful when
201// the rule mounts directories, as files within those directories can be globbed and
202// tracked as dependencies with NsjailImplicits().
203func (r *RuleBuilder) NsjailImplicits(inputs Paths) *RuleBuilder {
204 if !r.nsjail {
205 panic("NsjailImplicits() must be called after Nsjail()")
206 }
207 r.nsjailImplicits = append(r.nsjailImplicits, inputs...)
208 return r
209}
210
Colin Crossba9e4032020-11-24 16:32:22 -0800211// SandboxTools enables tool sandboxing for the rule by copying any referenced tools into the
212// sandbox.
213func (r *RuleBuilder) SandboxTools() *RuleBuilder {
214 if !r.sbox {
215 panic("SandboxTools() must be called after Sbox()")
216 }
217 if len(r.commands) > 0 {
218 panic("SandboxTools() may not be called after Command()")
219 }
220 r.sboxTools = true
221 return r
222}
223
Colin Crossab020a72021-03-12 17:52:23 -0800224// SandboxInputs enables input sandboxing for the rule by copying any referenced inputs into the
225// sandbox. It also implies SandboxTools().
226//
227// Sandboxing inputs requires RuleBuilder to be aware of all references to input paths. Paths
228// that are passed to RuleBuilder outside of the methods that expect inputs, for example
229// FlagWithArg, must use RuleBuilderCommand.PathForInput to translate the path to one that matches
230// the sandbox layout.
231func (r *RuleBuilder) SandboxInputs() *RuleBuilder {
232 if !r.sbox {
233 panic("SandboxInputs() must be called after Sbox()")
234 }
235 if len(r.commands) > 0 {
236 panic("SandboxInputs() may not be called after Command()")
237 }
238 r.sboxTools = true
239 r.sboxInputs = true
240 return r
241}
242
Colin Cross758290d2019-02-01 16:42:32 -0800243// Install associates an output of the rule with an install location, which can be retrieved later using
244// RuleBuilder.Installs.
Colin Cross69f59a32019-02-15 10:39:37 -0800245func (r *RuleBuilder) Install(from Path, to string) {
Colin Crossfeec25b2019-01-30 17:32:39 -0800246 r.installs = append(r.installs, RuleBuilderInstall{from, to})
247}
248
Colin Cross758290d2019-02-01 16:42:32 -0800249// Command returns a new RuleBuilderCommand for the rule. The commands will be ordered in the rule by when they were
250// created by this method. That can be mutated through their methods in any order, as long as the mutations do not
251// race with any call to Build.
Colin Crossfeec25b2019-01-30 17:32:39 -0800252func (r *RuleBuilder) Command() *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700253 command := &RuleBuilderCommand{
Colin Crossf1a035e2020-11-16 17:32:30 -0800254 rule: r,
Dan Willemsen633c5022019-04-12 11:11:38 -0700255 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800256 r.commands = append(r.commands, command)
257 return command
258}
259
Colin Cross5cb5b092019-02-02 21:25:18 -0800260// Temporary marks an output of a command as an intermediate file that will be used as an input to another command
261// in the same rule, and should not be listed in Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800262func (r *RuleBuilder) Temporary(path WritablePath) {
Colin Cross5cb5b092019-02-02 21:25:18 -0800263 r.temporariesSet[path] = true
264}
265
266// DeleteTemporaryFiles adds a command to the rule that deletes any outputs that have been marked using Temporary
267// when the rule runs. DeleteTemporaryFiles should be called after all calls to Temporary.
268func (r *RuleBuilder) DeleteTemporaryFiles() {
Colin Cross69f59a32019-02-15 10:39:37 -0800269 var temporariesList WritablePaths
Colin Cross5cb5b092019-02-02 21:25:18 -0800270
271 for intermediate := range r.temporariesSet {
272 temporariesList = append(temporariesList, intermediate)
273 }
Colin Cross69f59a32019-02-15 10:39:37 -0800274
275 sort.Slice(temporariesList, func(i, j int) bool {
276 return temporariesList[i].String() < temporariesList[j].String()
277 })
Colin Cross5cb5b092019-02-02 21:25:18 -0800278
279 r.Command().Text("rm").Flag("-f").Outputs(temporariesList)
280}
281
Colin Crossda71eda2020-02-21 16:55:19 -0800282// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
Colin Cross3d680512020-11-13 16:23:53 -0800283// input paths, such as RuleBuilderCommand.Input, RuleBuilderCommand.Implicit, or
Colin Crossda71eda2020-02-21 16:55:19 -0800284// RuleBuilderCommand.FlagWithInput. Inputs to a command that are also outputs of another command
285// in the same RuleBuilder are filtered out. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800286func (r *RuleBuilder) Inputs() Paths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800287 outputs := r.outputSet()
Dan Willemsen633c5022019-04-12 11:11:38 -0700288 depFiles := r.depFileSet()
Colin Crossfeec25b2019-01-30 17:32:39 -0800289
Colin Cross69f59a32019-02-15 10:39:37 -0800290 inputs := make(map[string]Path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800291 for _, c := range r.commands {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400292 for _, input := range append(c.inputs, c.implicits...) {
Dan Willemsen633c5022019-04-12 11:11:38 -0700293 inputStr := input.String()
294 if _, isOutput := outputs[inputStr]; !isOutput {
295 if _, isDepFile := depFiles[inputStr]; !isDepFile {
296 inputs[input.String()] = input
297 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800298 }
299 }
300 }
301
Colin Cross69f59a32019-02-15 10:39:37 -0800302 var inputList Paths
303 for _, input := range inputs {
Colin Crossfeec25b2019-01-30 17:32:39 -0800304 inputList = append(inputList, input)
305 }
Colin Cross69f59a32019-02-15 10:39:37 -0800306
307 sort.Slice(inputList, func(i, j int) bool {
308 return inputList[i].String() < inputList[j].String()
309 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800310
311 return inputList
312}
313
Colin Crossda71eda2020-02-21 16:55:19 -0800314// OrderOnlys returns the list of paths that were passed to the RuleBuilderCommand.OrderOnly or
315// RuleBuilderCommand.OrderOnlys. The list is sorted and duplicates removed.
316func (r *RuleBuilder) OrderOnlys() Paths {
317 orderOnlys := make(map[string]Path)
318 for _, c := range r.commands {
319 for _, orderOnly := range c.orderOnlys {
320 orderOnlys[orderOnly.String()] = orderOnly
321 }
322 }
323
324 var orderOnlyList Paths
325 for _, orderOnly := range orderOnlys {
326 orderOnlyList = append(orderOnlyList, orderOnly)
327 }
328
329 sort.Slice(orderOnlyList, func(i, j int) bool {
330 return orderOnlyList[i].String() < orderOnlyList[j].String()
331 })
332
333 return orderOnlyList
334}
335
Colin Crossae89abe2021-04-21 11:45:23 -0700336// Validations returns the list of paths that were passed to RuleBuilderCommand.Validation or
337// RuleBuilderCommand.Validations. The list is sorted and duplicates removed.
338func (r *RuleBuilder) Validations() Paths {
339 validations := make(map[string]Path)
340 for _, c := range r.commands {
341 for _, validation := range c.validations {
342 validations[validation.String()] = validation
343 }
344 }
345
346 var validationList Paths
347 for _, validation := range validations {
348 validationList = append(validationList, validation)
349 }
350
351 sort.Slice(validationList, func(i, j int) bool {
352 return validationList[i].String() < validationList[j].String()
353 })
354
355 return validationList
356}
357
Colin Cross69f59a32019-02-15 10:39:37 -0800358func (r *RuleBuilder) outputSet() map[string]WritablePath {
359 outputs := make(map[string]WritablePath)
Colin Crossfeec25b2019-01-30 17:32:39 -0800360 for _, c := range r.commands {
361 for _, output := range c.outputs {
Colin Cross69f59a32019-02-15 10:39:37 -0800362 outputs[output.String()] = output
Colin Crossfeec25b2019-01-30 17:32:39 -0800363 }
364 }
365 return outputs
366}
367
Colin Crossda71eda2020-02-21 16:55:19 -0800368// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
369// output paths, such as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or
370// RuleBuilderCommand.FlagWithInput. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800371func (r *RuleBuilder) Outputs() WritablePaths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800372 outputs := r.outputSet()
373
Colin Cross69f59a32019-02-15 10:39:37 -0800374 var outputList WritablePaths
375 for _, output := range outputs {
Colin Cross5cb5b092019-02-02 21:25:18 -0800376 if !r.temporariesSet[output] {
377 outputList = append(outputList, output)
378 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800379 }
Colin Cross69f59a32019-02-15 10:39:37 -0800380
381 sort.Slice(outputList, func(i, j int) bool {
382 return outputList[i].String() < outputList[j].String()
383 })
384
Colin Crossfeec25b2019-01-30 17:32:39 -0800385 return outputList
386}
387
Dan Willemsen633c5022019-04-12 11:11:38 -0700388func (r *RuleBuilder) depFileSet() map[string]WritablePath {
389 depFiles := make(map[string]WritablePath)
390 for _, c := range r.commands {
391 for _, depFile := range c.depFiles {
392 depFiles[depFile.String()] = depFile
393 }
394 }
395 return depFiles
396}
397
Colin Cross1d2cf042019-03-29 15:33:06 -0700398// DepFiles returns the list of paths that were passed to the RuleBuilderCommand methods that take depfile paths, such
399// as RuleBuilderCommand.DepFile or RuleBuilderCommand.FlagWithDepFile.
400func (r *RuleBuilder) DepFiles() WritablePaths {
401 var depFiles WritablePaths
402
403 for _, c := range r.commands {
404 for _, depFile := range c.depFiles {
405 depFiles = append(depFiles, depFile)
406 }
407 }
408
409 return depFiles
410}
411
Colin Cross758290d2019-02-01 16:42:32 -0800412// Installs returns the list of tuples passed to Install.
Colin Crossdeabb942019-02-11 14:11:09 -0800413func (r *RuleBuilder) Installs() RuleBuilderInstalls {
414 return append(RuleBuilderInstalls(nil), r.installs...)
Colin Crossfeec25b2019-01-30 17:32:39 -0800415}
416
Colin Cross69f59a32019-02-15 10:39:37 -0800417func (r *RuleBuilder) toolsSet() map[string]Path {
418 tools := make(map[string]Path)
Colin Cross5cb5b092019-02-02 21:25:18 -0800419 for _, c := range r.commands {
420 for _, tool := range c.tools {
Colin Cross69f59a32019-02-15 10:39:37 -0800421 tools[tool.String()] = tool
Colin Cross5cb5b092019-02-02 21:25:18 -0800422 }
423 }
424
425 return tools
426}
427
Colin Crossda71eda2020-02-21 16:55:19 -0800428// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method. The
429// list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800430func (r *RuleBuilder) Tools() Paths {
Colin Cross5cb5b092019-02-02 21:25:18 -0800431 toolsSet := r.toolsSet()
432
Colin Cross69f59a32019-02-15 10:39:37 -0800433 var toolsList Paths
434 for _, tool := range toolsSet {
Colin Cross5cb5b092019-02-02 21:25:18 -0800435 toolsList = append(toolsList, tool)
Colin Crossfeec25b2019-01-30 17:32:39 -0800436 }
Colin Cross69f59a32019-02-15 10:39:37 -0800437
438 sort.Slice(toolsList, func(i, j int) bool {
439 return toolsList[i].String() < toolsList[j].String()
440 })
441
Colin Cross5cb5b092019-02-02 21:25:18 -0800442 return toolsList
Colin Crossfeec25b2019-01-30 17:32:39 -0800443}
444
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700445// RspFileInputs returns the list of paths that were passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
446func (r *RuleBuilder) RspFileInputs() Paths {
447 var rspFileInputs Paths
448 for _, c := range r.commands {
Colin Crossce3a51d2021-03-19 16:22:12 -0700449 for _, rspFile := range c.rspFiles {
450 rspFileInputs = append(rspFileInputs, rspFile.paths...)
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700451 }
452 }
453
454 return rspFileInputs
455}
456
Colin Crossce3a51d2021-03-19 16:22:12 -0700457func (r *RuleBuilder) rspFiles() []rspFileAndPaths {
458 var rspFiles []rspFileAndPaths
Colin Cross70c47412021-03-12 17:48:14 -0800459 for _, c := range r.commands {
Colin Crossce3a51d2021-03-19 16:22:12 -0700460 rspFiles = append(rspFiles, c.rspFiles...)
Colin Cross70c47412021-03-12 17:48:14 -0800461 }
462
Colin Crossce3a51d2021-03-19 16:22:12 -0700463 return rspFiles
Colin Cross70c47412021-03-12 17:48:14 -0800464}
465
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700466// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
Colin Crossfeec25b2019-01-30 17:32:39 -0800467func (r *RuleBuilder) Commands() []string {
468 var commands []string
469 for _, c := range r.commands {
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700470 commands = append(commands, c.String())
471 }
472 return commands
473}
474
Colin Cross758290d2019-02-01 16:42:32 -0800475// BuilderContext is a subset of ModuleContext and SingletonContext.
Colin Cross786cd6d2019-02-01 16:41:11 -0800476type BuilderContext interface {
477 PathContext
478 Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
479 Build(PackageContext, BuildParams)
480}
481
Colin Cross758290d2019-02-01 16:42:32 -0800482var _ BuilderContext = ModuleContext(nil)
483var _ BuilderContext = SingletonContext(nil)
484
Colin Crossf1a035e2020-11-16 17:32:30 -0800485func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700486 return r.Command().
Colin Cross9b698b62021-12-22 09:55:32 -0800487 builtToolWithoutDeps("dep_fixer").
Dan Willemsen633c5022019-04-12 11:11:38 -0700488 Inputs(depFiles.Paths())
Colin Cross1d2cf042019-03-29 15:33:06 -0700489}
490
Sam Delmerico285b66a2023-09-25 12:13:17 +0000491// BuildWithNinjaVars adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
492// Outputs. This function will not escape Ninja variables, so it may be used to write sandbox manifests using Ninja variables.
493func (r *RuleBuilder) BuildWithUnescapedNinjaVars(name string, desc string) {
494 r.build(name, desc, false)
495}
496
Colin Cross758290d2019-02-01 16:42:32 -0800497// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
498// Outputs.
Colin Crossf1a035e2020-11-16 17:32:30 -0800499func (r *RuleBuilder) Build(name string, desc string) {
Sam Delmerico285b66a2023-09-25 12:13:17 +0000500 r.build(name, desc, true)
501}
502
Cole Faust63ea1f92024-08-27 11:42:26 -0700503var sandboxEnvOnceKey = NewOnceKey("sandbox_environment_variables")
504
Sam Delmerico285b66a2023-09-25 12:13:17 +0000505func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString bool) {
Colin Cross1d2cf042019-03-29 15:33:06 -0700506 name = ninjaNameEscape(name)
507
Colin Cross0d2f40a2019-02-05 22:31:15 -0800508 if len(r.missingDeps) > 0 {
Sam Delmerico285b66a2023-09-25 12:13:17 +0000509 r.ctx.Build(r.pctx, BuildParams{
Colin Cross0d2f40a2019-02-05 22:31:15 -0800510 Rule: ErrorRule,
Colin Cross69f59a32019-02-15 10:39:37 -0800511 Outputs: r.Outputs(),
Colin Cross0d2f40a2019-02-05 22:31:15 -0800512 Description: desc,
513 Args: map[string]string{
514 "error": "missing dependencies: " + strings.Join(r.missingDeps, ", "),
515 },
516 })
517 return
518 }
519
Colin Cross1d2cf042019-03-29 15:33:06 -0700520 var depFile WritablePath
521 var depFormat blueprint.Deps
522 if depFiles := r.DepFiles(); len(depFiles) > 0 {
523 depFile = depFiles[0]
524 depFormat = blueprint.DepsGCC
525 if len(depFiles) > 1 {
526 // Add a command locally that merges all depfiles together into the first depfile.
Colin Crossf1a035e2020-11-16 17:32:30 -0800527 r.depFileMergerCmd(depFiles)
Dan Willemsen633c5022019-04-12 11:11:38 -0700528
529 if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800530 // Check for Rel() errors, as all depfiles should be in the output dir. Errors
531 // will be reported to the ctx.
Dan Willemsen633c5022019-04-12 11:11:38 -0700532 for _, path := range depFiles[1:] {
Colin Crossf1a035e2020-11-16 17:32:30 -0800533 Rel(r.ctx, r.outDir.String(), path.String())
Dan Willemsen633c5022019-04-12 11:11:38 -0700534 }
535 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700536 }
537 }
538
Dan Willemsen633c5022019-04-12 11:11:38 -0700539 tools := r.Tools()
Colin Crossb70a1a92021-03-12 17:51:32 -0800540 commands := r.Commands()
Dan Willemsen633c5022019-04-12 11:11:38 -0700541 outputs := r.Outputs()
Colin Cross3d680512020-11-13 16:23:53 -0800542 inputs := r.Inputs()
Colin Crossce3a51d2021-03-19 16:22:12 -0700543 rspFiles := r.rspFiles()
Dan Willemsen633c5022019-04-12 11:11:38 -0700544
545 if len(commands) == 0 {
546 return
547 }
548 if len(outputs) == 0 {
549 panic("No outputs specified from any Commands")
550 }
551
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700552 commandString := strings.Join(commands, " && ")
Dan Willemsen633c5022019-04-12 11:11:38 -0700553
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900554 if !r.sbox {
555 // If not using sbox the rule will run the command directly, put the hash of the
556 // list of input files in a comment at the end of the command line to ensure ninja
557 // reruns the rule when the list of input files changes.
558 commandString += " # hash of input list: " + hashSrcFiles(inputs)
559 }
560
561 if r.nsjail {
562 var nsjailCmd strings.Builder
563 nsjailPath := r.ctx.Config().PrebuiltBuildTool(r.ctx, "nsjail")
564 nsjailCmd.WriteString("mkdir -p ")
565 nsjailCmd.WriteString(r.nsjailBasePath.String())
566 nsjailCmd.WriteString(" && ")
567 nsjailCmd.WriteString(nsjailPath.String())
568 nsjailCmd.WriteRune(' ')
569 nsjailCmd.WriteString("-B $PWD/")
570 nsjailCmd.WriteString(r.nsjailBasePath.String())
571 nsjailCmd.WriteString(":nsjail_build_sandbox")
572
573 // out is mounted to $(genDir).
574 nsjailCmd.WriteString(" -B $PWD/")
575 nsjailCmd.WriteString(r.outDir.String())
576 nsjailCmd.WriteString(":nsjail_build_sandbox/out")
577
Inseob Kim93036a52024-10-25 17:02:21 +0900578 addBindMount := func(src, dst string) {
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900579 nsjailCmd.WriteString(" -R $PWD/")
Inseob Kim93036a52024-10-25 17:02:21 +0900580 nsjailCmd.WriteString(src)
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900581 nsjailCmd.WriteString(":nsjail_build_sandbox/")
Inseob Kim93036a52024-10-25 17:02:21 +0900582 nsjailCmd.WriteString(dst)
583 }
584
585 for _, input := range inputs {
586 addBindMount(input.String(), r.nsjailPathForInputRel(input))
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900587 }
588 for _, tool := range tools {
Inseob Kim93036a52024-10-25 17:02:21 +0900589 addBindMount(tool.String(), nsjailPathForToolRel(r.ctx, tool))
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900590 }
591 inputs = append(inputs, tools...)
592 for _, c := range r.commands {
Inseob Kim93036a52024-10-25 17:02:21 +0900593 for _, directory := range c.implicitDirectories {
594 addBindMount(directory.String(), directory.String())
595 // TODO(b/375551969): Add implicitDirectories to BuildParams, rather than relying on implicits
596 inputs = append(inputs, SourcePath{basePath: directory.base()})
597 }
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900598 for _, tool := range c.packagedTools {
Inseob Kim93036a52024-10-25 17:02:21 +0900599 addBindMount(tool.srcPath.String(), nsjailPathForPackagedToolRel(tool))
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900600 inputs = append(inputs, tool.srcPath)
601 }
602 }
603
604 // These five directories are necessary to run native host tools like /bin/bash and py3-cmd.
605 nsjailCmd.WriteString(" -R /bin")
606 nsjailCmd.WriteString(" -R /lib")
607 nsjailCmd.WriteString(" -R /lib64")
608 nsjailCmd.WriteString(" -R /dev")
609 nsjailCmd.WriteString(" -R /usr")
610
611 nsjailCmd.WriteString(" -m none:/tmp:tmpfs:size=1073741824") // 1GB, should be enough
612 nsjailCmd.WriteString(" -D nsjail_build_sandbox")
613 nsjailCmd.WriteString(" --disable_rlimits")
Haamed Gheibic128dd72024-11-13 13:27:53 -0800614 nsjailCmd.WriteString(" --skip_setsid") // ABFS relies on process-groups to track file operations
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900615 nsjailCmd.WriteString(" -q")
616 nsjailCmd.WriteString(" -- ")
617 nsjailCmd.WriteString("/bin/bash -c ")
618 nsjailCmd.WriteString(proptools.ShellEscape(commandString))
619
620 commandString = nsjailCmd.String()
621
622 inputs = append(inputs, nsjailPath)
623 inputs = append(inputs, r.nsjailImplicits...)
624 } else if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800625 // If running the command inside sbox, write the rule data out to an sbox
626 // manifest.textproto.
627 manifest := sbox_proto.Manifest{}
628 command := sbox_proto.Command{}
629 manifest.Commands = append(manifest.Commands, &command)
630 command.Command = proto.String(commandString)
Colin Cross151b9ff2020-11-12 08:29:30 -0800631
Colin Cross619b9ab2020-11-20 18:44:31 +0000632 if depFile != nil {
Colin Crosse16ce362020-11-12 08:29:30 -0800633 manifest.OutputDepfile = proto.String(depFile.String())
Colin Cross619b9ab2020-11-20 18:44:31 +0000634 }
635
Colin Crossba9e4032020-11-24 16:32:22 -0800636 // If sandboxing tools is enabled, add copy rules to the manifest to copy each tool
637 // into the sbox directory.
638 if r.sboxTools {
639 for _, tool := range tools {
640 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
641 From: proto.String(tool.String()),
642 To: proto.String(sboxPathForToolRel(r.ctx, tool)),
643 })
644 }
645 for _, c := range r.commands {
646 for _, tool := range c.packagedTools {
647 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
648 From: proto.String(tool.srcPath.String()),
649 To: proto.String(sboxPathForPackagedToolRel(tool)),
650 Executable: proto.Bool(tool.executable),
651 })
652 tools = append(tools, tool.srcPath)
653 }
654 }
655 }
656
Colin Crossab020a72021-03-12 17:52:23 -0800657 // If sandboxing inputs is enabled, add copy rules to the manifest to copy each input
658 // into the sbox directory.
659 if r.sboxInputs {
660 for _, input := range inputs {
661 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
662 From: proto.String(input.String()),
663 To: proto.String(r.sboxPathForInputRel(input)),
664 })
665 }
Cole Faust78f3c3a2024-08-15 17:19:34 -0700666 for _, input := range r.OrderOnlys() {
667 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
668 From: proto.String(input.String()),
669 To: proto.String(r.sboxPathForInputRel(input)),
670 })
671 }
Colin Crossab020a72021-03-12 17:52:23 -0800672
Colin Crossce3a51d2021-03-19 16:22:12 -0700673 // If using rsp files copy them and their contents into the sbox directory with
674 // the appropriate path mappings.
675 for _, rspFile := range rspFiles {
Colin Crosse55bd422021-03-23 13:44:30 -0700676 command.RspFiles = append(command.RspFiles, &sbox_proto.RspFile{
Colin Crossce3a51d2021-03-19 16:22:12 -0700677 File: proto.String(rspFile.file.String()),
Colin Crosse55bd422021-03-23 13:44:30 -0700678 // These have to match the logic in sboxPathForInputRel
679 PathMappings: []*sbox_proto.PathMapping{
680 {
681 From: proto.String(r.outDir.String()),
682 To: proto.String(sboxOutSubDir),
683 },
684 {
Cole Fauste8561c62023-11-30 17:26:37 -0800685 From: proto.String(r.ctx.Config().OutDir()),
Colin Crosse55bd422021-03-23 13:44:30 -0700686 To: proto.String(sboxOutSubDir),
687 },
688 },
Colin Crossab020a72021-03-12 17:52:23 -0800689 })
690 }
691
Cole Faust63ea1f92024-08-27 11:42:26 -0700692 // Only allow the build to access certain environment variables
693 command.DontInheritEnv = proto.Bool(true)
694 command.Env = r.ctx.Config().Once(sandboxEnvOnceKey, func() interface{} {
695 // The list of allowed variables was found by running builds of all
696 // genrules and seeing what failed
697 var result []*sbox_proto.EnvironmentVariable
698 inheritedVars := []string{
699 "PATH",
700 "JAVA_HOME",
701 "TMPDIR",
702 // Allow RBE variables because the art tests invoke RBE manually
703 "RBE_log_dir",
704 "RBE_platform",
705 "RBE_server_address",
706 // TODO: RBE_exec_root is set to the absolute path to the root of the source
707 // tree, which we don't want sandboxed actions to find. Remap it to ".".
708 "RBE_exec_root",
709 }
710 for _, v := range inheritedVars {
711 result = append(result, &sbox_proto.EnvironmentVariable{
712 Name: proto.String(v),
713 State: &sbox_proto.EnvironmentVariable_Inherit{
714 Inherit: true,
715 },
716 })
717 }
718 // Set OUT_DIR to the relative path of the sandboxed out directory.
719 // Otherwise, OUT_DIR will be inherited from the rest of the build,
720 // which will allow scripts to escape the sandbox if OUT_DIR is an
721 // absolute path.
722 result = append(result, &sbox_proto.EnvironmentVariable{
723 Name: proto.String("OUT_DIR"),
724 State: &sbox_proto.EnvironmentVariable_Value{
725 Value: sboxOutSubDir,
726 },
727 })
728 return result
729 }).([]*sbox_proto.EnvironmentVariable)
Colin Crossab020a72021-03-12 17:52:23 -0800730 command.Chdir = proto.Bool(true)
731 }
732
Colin Crosse16ce362020-11-12 08:29:30 -0800733 // Add copy rules to the manifest to copy each output file from the sbox directory.
Colin Crossba9e4032020-11-24 16:32:22 -0800734 // to the output directory after running the commands.
Spandan Das33e30972023-07-13 21:19:12 +0000735 for _, output := range outputs {
Colin Crossf1a035e2020-11-16 17:32:30 -0800736 rel := Rel(r.ctx, r.outDir.String(), output.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800737 command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{
Spandan Dasaf4ccaa2023-06-29 01:15:51 +0000738 From: proto.String(filepath.Join(r.sboxOutSubDir, rel)),
Colin Crosse16ce362020-11-12 08:29:30 -0800739 To: proto.String(output.String()),
740 })
741 }
Colin Cross619b9ab2020-11-20 18:44:31 +0000742
Colin Cross5334edd2021-03-11 17:18:21 -0800743 // Outputs that were marked Temporary will not be checked that they are in the output
744 // directory by the loop above, check them here.
745 for path := range r.temporariesSet {
746 Rel(r.ctx, r.outDir.String(), path.String())
747 }
748
Colin Crosse16ce362020-11-12 08:29:30 -0800749 // Add a hash of the list of input files to the manifest so that the textproto file
750 // changes when the list of input files changes and causes the sbox rule that
751 // depends on it to rerun.
752 command.InputHash = proto.String(hashSrcFiles(inputs))
Colin Cross619b9ab2020-11-20 18:44:31 +0000753
Colin Crosse16ce362020-11-12 08:29:30 -0800754 // Verify that the manifest textproto is not inside the sbox output directory, otherwise
755 // it will get deleted when the sbox rule clears its output directory.
Colin Crossf1a035e2020-11-16 17:32:30 -0800756 _, manifestInOutDir := MaybeRel(r.ctx, r.outDir.String(), r.sboxManifestPath.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800757 if manifestInOutDir {
Colin Crossf1a035e2020-11-16 17:32:30 -0800758 ReportPathErrorf(r.ctx, "sbox rule %q manifestPath %q must not be in outputDir %q",
759 name, r.sboxManifestPath.String(), r.outDir.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800760 }
761
Paul Duffin4a3a0a52023-10-12 15:01:29 +0100762 // Create a rule to write the manifest as textproto. Pretty print it by indenting and
763 // splitting across multiple lines.
764 pbText, err := prototext.MarshalOptions{Indent: " "}.Marshal(&manifest)
Dan Willemsen4591b642021-05-24 14:24:12 -0700765 if err != nil {
766 ReportPathErrorf(r.ctx, "sbox manifest failed to marshal: %q", err)
767 }
Sam Delmerico285b66a2023-09-25 12:13:17 +0000768 if ninjaEscapeCommandString {
769 WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText))
770 } else {
771 // We need to have a rule to write files that is
772 // defined on the RuleBuilder's pctx in order to
773 // write Ninja variables in the string.
774 // The WriteFileRule function above rule can only write
775 // raw strings because it is defined on the android
776 // package's pctx, and it can't access variables defined
777 // in another context.
778 r.ctx.Build(r.pctx, BuildParams{
779 Rule: r.ctx.Rule(r.pctx, "unescapedWriteFile", blueprint.RuleParams{
780 Command: `rm -rf ${out} && cat ${out}.rsp > ${out}`,
781 Rspfile: "${out}.rsp",
782 RspfileContent: "${content}",
783 Description: "write file",
784 }, "content"),
785 Output: r.sboxManifestPath,
786 Description: "write sbox manifest " + r.sboxManifestPath.Base(),
787 Args: map[string]string{
788 "content": string(pbText),
789 },
790 })
791 }
Colin Crosse16ce362020-11-12 08:29:30 -0800792
793 // Generate a new string to use as the command line of the sbox rule. This uses
794 // a RuleBuilderCommand as a convenience method of building the command line, then
795 // converts it to a string to replace commandString.
Colin Crossf1a035e2020-11-16 17:32:30 -0800796 sboxCmd := &RuleBuilderCommand{
797 rule: &RuleBuilder{
798 ctx: r.ctx,
799 },
800 }
Colin Cross9b698b62021-12-22 09:55:32 -0800801 sboxCmd.builtToolWithoutDeps("sbox").
Colin Crosse52c2ac2022-03-28 17:03:35 -0700802 FlagWithArg("--sandbox-path ", shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
803 FlagWithArg("--output-dir ", r.outDir.String()).
804 FlagWithInput("--manifest ", r.sboxManifestPath)
805
806 if r.restat {
807 sboxCmd.Flag("--write-if-changed")
808 }
Colin Crosse16ce362020-11-12 08:29:30 -0800809
810 // Replace the command string, and add the sbox tool and manifest textproto to the
811 // dependencies of the final sbox rule.
Colin Crosscfec40c2019-07-08 17:07:18 -0700812 commandString = sboxCmd.buf.String()
Dan Willemsen633c5022019-04-12 11:11:38 -0700813 tools = append(tools, sboxCmd.tools...)
Colin Crosse16ce362020-11-12 08:29:30 -0800814 inputs = append(inputs, sboxCmd.inputs...)
Colin Crossef972742021-03-12 17:24:45 -0800815
816 if r.rbeParams != nil {
Colin Crosse55bd422021-03-23 13:44:30 -0700817 // RBE needs a list of input files to copy to the remote builder. For inputs already
818 // listed in an rsp file, pass the rsp file directly to rewrapper. For the rest,
819 // create a new rsp file to pass to rewrapper.
820 var remoteRspFiles Paths
821 var remoteInputs Paths
822
823 remoteInputs = append(remoteInputs, inputs...)
824 remoteInputs = append(remoteInputs, tools...)
825
Colin Crossce3a51d2021-03-19 16:22:12 -0700826 for _, rspFile := range rspFiles {
827 remoteInputs = append(remoteInputs, rspFile.file)
828 remoteRspFiles = append(remoteRspFiles, rspFile.file)
Colin Crossef972742021-03-12 17:24:45 -0800829 }
Colin Crosse55bd422021-03-23 13:44:30 -0700830
831 if len(remoteInputs) > 0 {
832 inputsListFile := r.sboxManifestPath.ReplaceExtension(r.ctx, "rbe_inputs.list")
833 writeRspFileRule(r.ctx, inputsListFile, remoteInputs)
834 remoteRspFiles = append(remoteRspFiles, inputsListFile)
835 // Add the new rsp file as an extra input to the rule.
836 inputs = append(inputs, inputsListFile)
837 }
Colin Crossef972742021-03-12 17:24:45 -0800838
839 r.rbeParams.OutputFiles = outputs.Strings()
Colin Crosse55bd422021-03-23 13:44:30 -0700840 r.rbeParams.RSPFiles = remoteRspFiles.Strings()
Colin Crossef972742021-03-12 17:24:45 -0800841 rewrapperCommand := r.rbeParams.NoVarTemplate(r.ctx.Config().RBEWrapper())
842 commandString = rewrapperCommand + " bash -c '" + strings.ReplaceAll(commandString, `'`, `'\''`) + "'"
843 }
Dan Willemsen633c5022019-04-12 11:11:38 -0700844 }
845
Colin Cross1d2cf042019-03-29 15:33:06 -0700846 // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
Colin Cross70c47412021-03-12 17:48:14 -0800847 // ImplicitOutputs. RuleBuilder doesn't use "$out", so the distinction between Outputs and
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700848 // ImplicitOutputs doesn't matter.
Dan Willemsen633c5022019-04-12 11:11:38 -0700849 output := outputs[0]
850 implicitOutputs := outputs[1:]
Colin Cross1d2cf042019-03-29 15:33:06 -0700851
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700852 var rspFile, rspFileContent string
Colin Crossce3a51d2021-03-19 16:22:12 -0700853 var rspFileInputs Paths
854 if len(rspFiles) > 0 {
855 // The first rsp files uses Ninja's rsp file support for the rule
856 rspFile = rspFiles[0].file.String()
Colin Crosse55bd422021-03-23 13:44:30 -0700857 // Use "$in" for rspFileContent to avoid duplicating the list of files in the dependency
858 // list and in the contents of the rsp file. Inputs to the rule that are not in the
859 // rsp file will be listed in Implicits instead of Inputs so they don't show up in "$in".
860 rspFileContent = "$in"
Colin Crossce3a51d2021-03-19 16:22:12 -0700861 rspFileInputs = append(rspFileInputs, rspFiles[0].paths...)
862
863 for _, rspFile := range rspFiles[1:] {
864 // Any additional rsp files need an extra rule to write the file.
865 writeRspFileRule(r.ctx, rspFile.file, rspFile.paths)
866 // The main rule needs to depend on the inputs listed in the extra rsp file.
867 inputs = append(inputs, rspFile.paths...)
868 // The main rule needs to depend on the extra rsp file.
869 inputs = append(inputs, rspFile.file)
870 }
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700871 }
872
Colin Cross8b8bec32019-11-15 13:18:43 -0800873 var pool blueprint.Pool
Colin Crossf1a035e2020-11-16 17:32:30 -0800874 if r.ctx.Config().UseGoma() && r.remoteable.Goma {
Colin Cross8b8bec32019-11-15 13:18:43 -0800875 // 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 -0800876 } else if r.ctx.Config().UseRBE() && r.remoteable.RBE {
Ramy Medhat944839a2020-03-31 22:14:52 -0400877 // When USE_RBE=true is set and the rule is supported by RBE, use the remotePool.
878 pool = remotePool
Colin Cross8b8bec32019-11-15 13:18:43 -0800879 } else if r.highmem {
880 pool = highmemPool
Colin Crossf1a035e2020-11-16 17:32:30 -0800881 } else if r.ctx.Config().UseRemoteBuild() {
Colin Cross8b8bec32019-11-15 13:18:43 -0800882 pool = localPool
883 }
884
Sam Delmericod46f6c82023-09-25 12:13:17 +0000885 if ninjaEscapeCommandString {
886 commandString = proptools.NinjaEscape(commandString)
887 }
888
Devin Mooreb6cc64f2024-07-15 22:31:24 +0000889 args_vars := make([]string, len(r.args))
890 i := 0
891 for k, _ := range r.args {
892 args_vars[i] = k
893 i++
894 }
Colin Crossf1a035e2020-11-16 17:32:30 -0800895 r.ctx.Build(r.pctx, BuildParams{
Sam Delmerico285b66a2023-09-25 12:13:17 +0000896 Rule: r.ctx.Rule(r.pctx, name, blueprint.RuleParams{
Sam Delmericod46f6c82023-09-25 12:13:17 +0000897 Command: commandString,
Colin Cross45029782021-03-16 16:49:52 -0700898 CommandDeps: proptools.NinjaEscapeList(tools.Strings()),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700899 Restat: r.restat,
Colin Cross45029782021-03-16 16:49:52 -0700900 Rspfile: proptools.NinjaEscape(rspFile),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700901 RspfileContent: rspFileContent,
Colin Cross8b8bec32019-11-15 13:18:43 -0800902 Pool: pool,
Devin Mooreb6cc64f2024-07-15 22:31:24 +0000903 }, args_vars...),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700904 Inputs: rspFileInputs,
Colin Cross3d680512020-11-13 16:23:53 -0800905 Implicits: inputs,
Colin Crossda6401b2021-04-21 11:32:19 -0700906 OrderOnly: r.OrderOnlys(),
Colin Crossae89abe2021-04-21 11:45:23 -0700907 Validations: r.Validations(),
Dan Willemsen633c5022019-04-12 11:11:38 -0700908 Output: output,
909 ImplicitOutputs: implicitOutputs,
910 Depfile: depFile,
911 Deps: depFormat,
912 Description: desc,
Devin Mooreb6cc64f2024-07-15 22:31:24 +0000913 Args: r.args,
Dan Willemsen633c5022019-04-12 11:11:38 -0700914 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800915}
916
Colin Cross758290d2019-02-01 16:42:32 -0800917// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
918// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the
919// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
920// space as a separator from the previous method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800921type RuleBuilderCommand struct {
Colin Crossf1a035e2020-11-16 17:32:30 -0800922 rule *RuleBuilder
923
Inseob Kim93036a52024-10-25 17:02:21 +0900924 buf strings.Builder
925 inputs Paths
926 implicits Paths
927 orderOnlys Paths
928 validations Paths
929 outputs WritablePaths
930 depFiles WritablePaths
931 tools Paths
932 packagedTools []PackagingSpec
933 rspFiles []rspFileAndPaths
934 implicitDirectories DirectoryPaths
Colin Crossce3a51d2021-03-19 16:22:12 -0700935}
936
937type rspFileAndPaths struct {
938 file WritablePath
939 paths Paths
Dan Willemsen633c5022019-04-12 11:11:38 -0700940}
941
Paul Duffin3866b892021-10-04 11:24:48 +0100942func checkPathNotNil(path Path) {
943 if path == nil {
944 panic("rule_builder paths cannot be nil")
945 }
946}
947
Dan Willemsen633c5022019-04-12 11:11:38 -0700948func (c *RuleBuilderCommand) addInput(path Path) string {
Paul Duffin3866b892021-10-04 11:24:48 +0100949 checkPathNotNil(path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700950 c.inputs = append(c.inputs, path)
Colin Crossab020a72021-03-12 17:52:23 -0800951 return c.PathForInput(path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700952}
953
Colin Crossab020a72021-03-12 17:52:23 -0800954func (c *RuleBuilderCommand) addImplicit(path Path) {
Paul Duffin3866b892021-10-04 11:24:48 +0100955 checkPathNotNil(path)
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400956 c.implicits = append(c.implicits, path)
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400957}
958
Inseob Kim93036a52024-10-25 17:02:21 +0900959func (c *RuleBuilderCommand) addImplicitDirectory(path DirectoryPath) {
960 c.implicitDirectories = append(c.implicitDirectories, path)
961}
962
Colin Crossda71eda2020-02-21 16:55:19 -0800963func (c *RuleBuilderCommand) addOrderOnly(path Path) {
Paul Duffin3866b892021-10-04 11:24:48 +0100964 checkPathNotNil(path)
Colin Crossda71eda2020-02-21 16:55:19 -0800965 c.orderOnlys = append(c.orderOnlys, path)
966}
967
Colin Crossab020a72021-03-12 17:52:23 -0800968// PathForInput takes an input path and returns the appropriate path to use on the command line. If
969// sbox was enabled via a call to RuleBuilder.Sbox() and the path was an output path it returns a
970// path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
971// original path.
972func (c *RuleBuilderCommand) PathForInput(path Path) string {
973 if c.rule.sbox {
974 rel, inSandbox := c.rule._sboxPathForInputRel(path)
975 if inSandbox {
976 rel = filepath.Join(sboxSandboxBaseDir, rel)
977 }
978 return rel
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900979 } else if c.rule.nsjail {
980 return c.rule.nsjailPathForInputRel(path)
Colin Crossab020a72021-03-12 17:52:23 -0800981 }
982 return path.String()
983}
984
985// PathsForInputs takes a list of input paths and returns the appropriate paths to use on the
986// command line. If sbox was enabled via a call to RuleBuilder.Sbox() a path was an output path, it
987// returns the path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it
988// returns the original paths.
989func (c *RuleBuilderCommand) PathsForInputs(paths Paths) []string {
990 ret := make([]string, len(paths))
991 for i, path := range paths {
992 ret[i] = c.PathForInput(path)
993 }
994 return ret
995}
996
Colin Crossf1a035e2020-11-16 17:32:30 -0800997// PathForOutput takes an output path and returns the appropriate path to use on the command
998// line. If sbox was enabled via a call to RuleBuilder.Sbox(), it returns a path with the
999// placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
1000// original path.
1001func (c *RuleBuilderCommand) PathForOutput(path WritablePath) string {
1002 if c.rule.sbox {
1003 // Errors will be handled in RuleBuilder.Build where we have a context to report them
1004 rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
1005 return filepath.Join(sboxOutDir, rel)
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001006 } else if c.rule.nsjail {
1007 // Errors will be handled in RuleBuilder.Build where we have a context to report them
1008 rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
1009 return filepath.Join(nsjailOutDir, rel)
Dan Willemsen633c5022019-04-12 11:11:38 -07001010 }
1011 return path.String()
Colin Crossfeec25b2019-01-30 17:32:39 -08001012}
1013
Colin Crossba9e4032020-11-24 16:32:22 -08001014func sboxPathForToolRel(ctx BuilderContext, path Path) string {
1015 // Errors will be handled in RuleBuilder.Build where we have a context to report them
Cole Faust3b703f32023-10-16 13:30:51 -07001016 toolDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "")
Colin Cross790ef352021-10-25 19:15:55 -07001017 relOutSoong, isRelOutSoong, _ := maybeRelErr(toolDir.String(), path.String())
1018 if isRelOutSoong {
1019 // The tool is in the Soong output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
1020 return filepath.Join(sboxToolsSubDir, "out", relOutSoong)
Colin Crossba9e4032020-11-24 16:32:22 -08001021 }
1022 // The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
1023 return filepath.Join(sboxToolsSubDir, "src", path.String())
1024}
1025
Colin Crossab020a72021-03-12 17:52:23 -08001026func (r *RuleBuilder) _sboxPathForInputRel(path Path) (rel string, inSandbox bool) {
1027 // Errors will be handled in RuleBuilder.Build where we have a context to report them
1028 rel, isRelSboxOut, _ := maybeRelErr(r.outDir.String(), path.String())
1029 if isRelSboxOut {
1030 return filepath.Join(sboxOutSubDir, rel), true
1031 }
1032 if r.sboxInputs {
1033 // When sandboxing inputs all inputs have to be copied into the sandbox. Input files that
1034 // are outputs of other rules could be an arbitrary absolute path if OUT_DIR is set, so they
1035 // will be copied to relative paths under __SBOX_OUT_DIR__/out.
Cole Fauste8561c62023-11-30 17:26:37 -08001036 rel, isRelOut, _ := maybeRelErr(r.ctx.Config().OutDir(), path.String())
Colin Crossab020a72021-03-12 17:52:23 -08001037 if isRelOut {
1038 return filepath.Join(sboxOutSubDir, rel), true
1039 }
1040 }
1041 return path.String(), false
1042}
1043
1044func (r *RuleBuilder) sboxPathForInputRel(path Path) string {
1045 rel, _ := r._sboxPathForInputRel(path)
1046 return rel
1047}
1048
1049func (r *RuleBuilder) sboxPathsForInputsRel(paths Paths) []string {
1050 ret := make([]string, len(paths))
1051 for i, path := range paths {
1052 ret[i] = r.sboxPathForInputRel(path)
1053 }
1054 return ret
1055}
1056
Colin Crossba9e4032020-11-24 16:32:22 -08001057func sboxPathForPackagedToolRel(spec PackagingSpec) string {
1058 return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage)
1059}
1060
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001061func nsjailPathForToolRel(ctx BuilderContext, path Path) string {
1062 // Errors will be handled in RuleBuilder.Build where we have a context to report them
1063 toolDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "")
1064 relOutSoong, isRelOutSoong, _ := maybeRelErr(toolDir.String(), path.String())
1065 if isRelOutSoong {
1066 // The tool is in the Soong output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
1067 return filepath.Join(nsjailToolsSubDir, "out", relOutSoong)
1068 }
1069 // The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
1070 return filepath.Join(nsjailToolsSubDir, "src", path.String())
1071}
1072
1073func (r *RuleBuilder) nsjailPathForInputRel(path Path) string {
1074 rel, isRelSboxOut, _ := maybeRelErr(r.outDir.String(), path.String())
1075 if isRelSboxOut {
1076 return filepath.Join(nsjailOutDir, rel)
1077 }
1078 return path.String()
1079}
1080
1081func (r *RuleBuilder) nsjailPathsForInputsRel(paths Paths) []string {
1082 ret := make([]string, len(paths))
1083 for i, path := range paths {
1084 ret[i] = r.nsjailPathForInputRel(path)
1085 }
1086 return ret
1087}
1088
1089func nsjailPathForPackagedToolRel(spec PackagingSpec) string {
1090 return filepath.Join(nsjailToolsSubDir, "out", spec.relPathInPackage)
1091}
1092
Colin Crossd11cf622021-03-23 22:30:35 -07001093// PathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
1094// tool after copying it into the sandbox. This can be used on the RuleBuilder command line to
1095// reference the tool.
1096func (c *RuleBuilderCommand) PathForPackagedTool(spec PackagingSpec) string {
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001097 if c.rule.sboxTools {
1098 return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
1099 } else if c.rule.nsjail {
1100 return nsjailPathForPackagedToolRel(spec)
1101 } else {
1102 panic("PathForPackagedTool() requires SandboxTools() or Nsjail()")
Colin Crossd11cf622021-03-23 22:30:35 -07001103 }
Colin Crossd11cf622021-03-23 22:30:35 -07001104}
1105
Colin Crossba9e4032020-11-24 16:32:22 -08001106// PathForTool takes a path to a tool, which may be an output file or a source file, and returns
1107// the corresponding path for the tool in the sbox sandbox if sbox is enabled, or the original path
1108// if it is not. This can be used on the RuleBuilder command line to reference the tool.
1109func (c *RuleBuilderCommand) PathForTool(path Path) string {
1110 if c.rule.sbox && c.rule.sboxTools {
1111 return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path))
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001112 } else if c.rule.nsjail {
1113 return nsjailPathForToolRel(c.rule.ctx, path)
Colin Crossba9e4032020-11-24 16:32:22 -08001114 }
1115 return path.String()
1116}
1117
Colin Crossd11cf622021-03-23 22:30:35 -07001118// PathsForTools takes a list of paths to tools, which may be output files or source files, and
1119// returns the corresponding paths for the tools in the sbox sandbox if sbox is enabled, or the
1120// original paths if it is not. This can be used on the RuleBuilder command line to reference the tool.
1121func (c *RuleBuilderCommand) PathsForTools(paths Paths) []string {
1122 if c.rule.sbox && c.rule.sboxTools {
1123 var ret []string
1124 for _, path := range paths {
1125 ret = append(ret, filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path)))
1126 }
1127 return ret
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001128 } else if c.rule.nsjail {
1129 var ret []string
1130 for _, path := range paths {
1131 ret = append(ret, nsjailPathForToolRel(c.rule.ctx, path))
1132 }
1133 return ret
Colin Crossd11cf622021-03-23 22:30:35 -07001134 }
1135 return paths.Strings()
1136}
1137
Colin Crossba9e4032020-11-24 16:32:22 -08001138// PackagedTool adds the specified tool path to the command line. It can only be used with tool
1139// sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox.
1140func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand {
Colin Crossba9e4032020-11-24 16:32:22 -08001141 c.packagedTools = append(c.packagedTools, spec)
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001142 if c.rule.sboxTools {
1143 c.Text(sboxPathForPackagedToolRel(spec))
1144 } else if c.rule.nsjail {
1145 c.Text(nsjailPathForPackagedToolRel(spec))
1146 } else {
1147 panic("PackagedTool() requires SandboxTools() or Nsjail()")
1148 }
Colin Crossba9e4032020-11-24 16:32:22 -08001149 return c
1150}
1151
1152// ImplicitPackagedTool copies the specified tool into the sandbox without modifying the command
1153// line. It can only be used with tool sandboxing enabled by SandboxTools().
1154func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuilderCommand {
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001155 if !c.rule.sboxTools && !c.rule.nsjail {
1156 panic("ImplicitPackagedTool() requires SandboxTools() or Nsjail()")
Colin Crossba9e4032020-11-24 16:32:22 -08001157 }
1158
1159 c.packagedTools = append(c.packagedTools, spec)
1160 return c
1161}
1162
1163// ImplicitPackagedTools copies the specified tools into the sandbox without modifying the command
1164// line. It can only be used with tool sandboxing enabled by SandboxTools().
1165func (c *RuleBuilderCommand) ImplicitPackagedTools(specs []PackagingSpec) *RuleBuilderCommand {
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001166 if !c.rule.sboxTools && !c.rule.nsjail {
1167 panic("ImplicitPackagedTools() requires SandboxTools() or Nsjail()")
Colin Crossba9e4032020-11-24 16:32:22 -08001168 }
1169
1170 c.packagedTools = append(c.packagedTools, specs...)
1171 return c
1172}
1173
Colin Cross758290d2019-02-01 16:42:32 -08001174// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
1175// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001176func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
Colin Crosscfec40c2019-07-08 17:07:18 -07001177 if c.buf.Len() > 0 {
1178 c.buf.WriteByte(' ')
Colin Crossfeec25b2019-01-30 17:32:39 -08001179 }
Colin Crosscfec40c2019-07-08 17:07:18 -07001180 c.buf.WriteString(text)
Colin Crossfeec25b2019-01-30 17:32:39 -08001181 return c
1182}
1183
Colin Cross758290d2019-02-01 16:42:32 -08001184// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or
1185// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001186func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
1187 return c.Text(fmt.Sprintf(format, a...))
1188}
1189
Colin Cross758290d2019-02-01 16:42:32 -08001190// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the
1191// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001192func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
1193 return c.Text(flag)
1194}
1195
Colin Crossab054432019-07-15 16:13:59 -07001196// OptionalFlag adds the specified raw text to the command line if it is not nil. The text should not contain input or
1197// output paths or the rule will not have them listed in its dependencies or outputs.
1198func (c *RuleBuilderCommand) OptionalFlag(flag *string) *RuleBuilderCommand {
1199 if flag != nil {
1200 c.Text(*flag)
1201 }
1202
1203 return c
1204}
1205
Colin Cross92b7d582019-03-29 15:32:51 -07001206// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the
1207// rule will not have them listed in its dependencies or outputs.
1208func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
1209 for _, flag := range flags {
1210 c.Text(flag)
1211 }
1212 return c
1213}
1214
Colin Cross758290d2019-02-01 16:42:32 -08001215// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
1216// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
1217// outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001218func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
1219 return c.Text(flag + arg)
1220}
1221
Colin Crossc7ed0042019-02-11 14:11:09 -08001222// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to
1223// calling FlagWithArg for argument.
1224func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand {
1225 for _, arg := range args {
1226 c.FlagWithArg(flag, arg)
1227 }
1228 return c
1229}
1230
Roland Levillain2da5d9a2019-02-27 16:56:41 +00001231// 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 -08001232// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or
1233// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001234func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
1235 return c.Text(flag + strings.Join(list, sep))
1236}
1237
Colin Cross758290d2019-02-01 16:42:32 -08001238// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by
1239// RuleBuilder.Tools.
Colin Cross69f59a32019-02-15 10:39:37 -08001240func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001241 checkPathNotNil(path)
Colin Crossfeec25b2019-01-30 17:32:39 -08001242 c.tools = append(c.tools, path)
Colin Crossba9e4032020-11-24 16:32:22 -08001243 return c.Text(c.PathForTool(path))
1244}
1245
1246// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
1247func (c *RuleBuilderCommand) ImplicitTool(path Path) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001248 checkPathNotNil(path)
Colin Crossba9e4032020-11-24 16:32:22 -08001249 c.tools = append(c.tools, path)
1250 return c
1251}
1252
1253// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
1254func (c *RuleBuilderCommand) ImplicitTools(paths Paths) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001255 for _, path := range paths {
1256 c.ImplicitTool(path)
1257 }
Colin Crossba9e4032020-11-24 16:32:22 -08001258 return c
Colin Crossfeec25b2019-01-30 17:32:39 -08001259}
1260
Colin Crossee94d6a2019-07-08 17:08:34 -07001261// BuiltTool adds the specified tool path that was built using a host Soong module to the command line. The path will
1262// be also added to the dependencies returned by RuleBuilder.Tools.
1263//
1264// It is equivalent to:
Colin Crossd079e0b2022-08-16 10:27:33 -07001265//
1266// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
Colin Crossf1a035e2020-11-16 17:32:30 -08001267func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
Colin Cross9b698b62021-12-22 09:55:32 -08001268 if c.rule.ctx.Config().UseHostMusl() {
1269 // If the host is using musl, assume that the tool was built against musl libc and include
1270 // libc_musl.so in the sandbox.
1271 // TODO(ccross): if we supported adding new dependencies during GenerateAndroidBuildActions
1272 // this could be a dependency + TransitivePackagingSpecs.
1273 c.ImplicitTool(c.rule.ctx.Config().HostJNIToolPath(c.rule.ctx, "libc_musl"))
1274 }
1275 return c.builtToolWithoutDeps(tool)
1276}
1277
1278// builtToolWithoutDeps is similar to BuiltTool, but doesn't add any dependencies. It is used
1279// internally by RuleBuilder for helper tools that are known to be compiled statically.
1280func (c *RuleBuilderCommand) builtToolWithoutDeps(tool string) *RuleBuilderCommand {
Colin Crossf1a035e2020-11-16 17:32:30 -08001281 return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
Colin Crossee94d6a2019-07-08 17:08:34 -07001282}
1283
1284// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the
1285// dependencies returned by RuleBuilder.Tools.
1286//
1287// It is equivalent to:
Colin Crossd079e0b2022-08-16 10:27:33 -07001288//
1289// cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
Colin Crossee94d6a2019-07-08 17:08:34 -07001290func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
1291 return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
1292}
1293
Colin Cross758290d2019-02-01 16:42:32 -08001294// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
1295// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001296func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001297 return c.Text(c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001298}
1299
Colin Cross758290d2019-02-01 16:42:32 -08001300// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
1301// dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001302func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001303 for _, path := range paths {
1304 c.Input(path)
1305 }
1306 return c
1307}
1308
1309// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
1310// command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001311func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001312 c.addImplicit(path)
Colin Crossfeec25b2019-01-30 17:32:39 -08001313 return c
1314}
1315
Colin Cross758290d2019-02-01 16:42:32 -08001316// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
1317// command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001318func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001319 for _, path := range paths {
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001320 c.addImplicit(path)
Dan Willemsen633c5022019-04-12 11:11:38 -07001321 }
Colin Crossfeec25b2019-01-30 17:32:39 -08001322 return c
1323}
1324
Inseob Kim93036a52024-10-25 17:02:21 +09001325// ImplicitDirectory adds the specified input directory to the dependencies without modifying the
1326// command line. Added directories will be bind-mounted for the nsjail.
1327func (c *RuleBuilderCommand) ImplicitDirectory(path DirectoryPath) *RuleBuilderCommand {
1328 if !c.rule.nsjail {
1329 panic("ImplicitDirectory() must be called after Nsjail()")
1330 }
1331 c.addImplicitDirectory(path)
1332 return c
1333}
1334
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001335// GetImplicits returns the command's implicit inputs.
1336func (c *RuleBuilderCommand) GetImplicits() Paths {
1337 return c.implicits
1338}
1339
Colin Crossda71eda2020-02-21 16:55:19 -08001340// OrderOnly adds the specified input path to the dependencies returned by RuleBuilder.OrderOnlys
1341// without modifying the command line.
1342func (c *RuleBuilderCommand) OrderOnly(path Path) *RuleBuilderCommand {
1343 c.addOrderOnly(path)
1344 return c
1345}
1346
1347// OrderOnlys adds the specified input paths to the dependencies returned by RuleBuilder.OrderOnlys
1348// without modifying the command line.
1349func (c *RuleBuilderCommand) OrderOnlys(paths Paths) *RuleBuilderCommand {
1350 for _, path := range paths {
1351 c.addOrderOnly(path)
1352 }
1353 return c
1354}
1355
Colin Crossae89abe2021-04-21 11:45:23 -07001356// Validation adds the specified input path to the validation dependencies by
1357// RuleBuilder.Validations without modifying the command line.
1358func (c *RuleBuilderCommand) Validation(path Path) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001359 checkPathNotNil(path)
Colin Crossae89abe2021-04-21 11:45:23 -07001360 c.validations = append(c.validations, path)
1361 return c
1362}
1363
1364// Validations adds the specified input paths to the validation dependencies by
1365// RuleBuilder.Validations without modifying the command line.
1366func (c *RuleBuilderCommand) Validations(paths Paths) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001367 for _, path := range paths {
1368 c.Validation(path)
1369 }
Colin Crossae89abe2021-04-21 11:45:23 -07001370 return c
1371}
1372
Colin Cross758290d2019-02-01 16:42:32 -08001373// Output adds the specified output path to the command line. The path will also be added to the outputs returned by
1374// RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001375func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001376 checkPathNotNil(path)
Colin Crossfeec25b2019-01-30 17:32:39 -08001377 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001378 return c.Text(c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001379}
1380
Colin Cross758290d2019-02-01 16:42:32 -08001381// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
1382// the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001383func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001384 for _, path := range paths {
1385 c.Output(path)
1386 }
1387 return c
1388}
1389
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001390// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
1391// and will be the temporary output directory managed by sbox, not the final one.
Anas Sulaimanb4dff132024-02-07 21:58:46 +00001392func (c *RuleBuilderCommand) OutputDir(subPathComponents ...string) *RuleBuilderCommand {
Colin Crossf1a035e2020-11-16 17:32:30 -08001393 if !c.rule.sbox {
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001394 panic("OutputDir only valid with Sbox")
1395 }
Anas Sulaimanb4dff132024-02-07 21:58:46 +00001396 path := sboxOutDir
1397 if len(subPathComponents) > 0 {
1398 path = filepath.Join(append([]string{sboxOutDir}, subPathComponents...)...)
1399 }
1400 return c.Text(path)
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001401}
1402
Colin Cross1d2cf042019-03-29 15:33:06 -07001403// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
1404// line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles are added to
1405// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
1406func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001407 checkPathNotNil(path)
Colin Cross1d2cf042019-03-29 15:33:06 -07001408 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001409 return c.Text(c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -07001410}
1411
Colin Cross758290d2019-02-01 16:42:32 -08001412// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
1413// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001414func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001415 c.outputs = append(c.outputs, path)
1416 return c
1417}
1418
Colin Cross758290d2019-02-01 16:42:32 -08001419// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
1420// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001421func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001422 c.outputs = append(c.outputs, paths...)
1423 return c
1424}
1425
Colin Cross1d2cf042019-03-29 15:33:06 -07001426// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
1427// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles
1428// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
1429// depfiles together.
1430func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand {
1431 c.depFiles = append(c.depFiles, path)
1432 return c
1433}
1434
Colin Cross758290d2019-02-01 16:42:32 -08001435// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
1436// will also be added to the dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001437func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001438 return c.Text(flag + c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001439}
1440
Colin Cross758290d2019-02-01 16:42:32 -08001441// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
1442// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
1443// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001444func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001445 strs := make([]string, len(paths))
1446 for i, path := range paths {
1447 strs[i] = c.addInput(path)
1448 }
1449 return c.FlagWithList(flag, strs, sep)
Colin Crossfeec25b2019-01-30 17:32:39 -08001450}
1451
Colin Cross758290d2019-02-01 16:42:32 -08001452// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
1453// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for
1454// each input path.
Colin Cross69f59a32019-02-15 10:39:37 -08001455func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001456 for _, path := range paths {
1457 c.FlagWithInput(flag, path)
1458 }
1459 return c
1460}
1461
1462// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path
1463// will also be added to the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001464func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001465 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001466 return c.Text(flag + c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001467}
1468
Colin Cross1d2cf042019-03-29 15:33:06 -07001469// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path
1470// will also be added to the outputs returned by RuleBuilder.Outputs.
1471func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
1472 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001473 return c.Text(flag + c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -07001474}
1475
Colin Crossce3a51d2021-03-19 16:22:12 -07001476// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with
1477// no separator between them. The paths will be written to the rspfile. If sbox is enabled, the
1478// rspfile must be outside the sbox directory. The first use of FlagWithRspFileInputList in any
1479// RuleBuilderCommand of a RuleBuilder will use Ninja's rsp file support for the rule, additional
1480// uses will result in an auxiliary rules to write the rspFile contents.
Colin Cross70c47412021-03-12 17:48:14 -08001481func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, rspFile WritablePath, paths Paths) *RuleBuilderCommand {
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001482 // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
1483 // generated.
1484 if paths == nil {
1485 paths = Paths{}
1486 }
1487
Colin Crossce3a51d2021-03-19 16:22:12 -07001488 c.rspFiles = append(c.rspFiles, rspFileAndPaths{rspFile, paths})
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001489
Colin Cross70c47412021-03-12 17:48:14 -08001490 if c.rule.sbox {
1491 if _, isRel, _ := maybeRelErr(c.rule.outDir.String(), rspFile.String()); isRel {
1492 panic(fmt.Errorf("FlagWithRspFileInputList rspfile %q must not be inside out dir %q",
1493 rspFile.String(), c.rule.outDir.String()))
1494 }
1495 }
1496
Colin Crossab020a72021-03-12 17:52:23 -08001497 c.FlagWithArg(flag, c.PathForInput(rspFile))
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001498 return c
1499}
1500
Colin Cross758290d2019-02-01 16:42:32 -08001501// String returns the command line.
1502func (c *RuleBuilderCommand) String() string {
Colin Crosscfec40c2019-07-08 17:07:18 -07001503 return c.buf.String()
Colin Cross758290d2019-02-01 16:42:32 -08001504}
Colin Cross1d2cf042019-03-29 15:33:06 -07001505
Colin Crosse16ce362020-11-12 08:29:30 -08001506// RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox()
1507// and returns sbox testproto generated by the RuleBuilder.
Colin Crossf61d03d2023-11-02 16:56:39 -07001508func RuleBuilderSboxProtoForTests(t *testing.T, ctx *TestContext, params TestingBuildParams) *sbox_proto.Manifest {
Colin Crosse16ce362020-11-12 08:29:30 -08001509 t.Helper()
Colin Crossf61d03d2023-11-02 16:56:39 -07001510 content := ContentFromFileRuleForTests(t, ctx, params)
Colin Crosse16ce362020-11-12 08:29:30 -08001511 manifest := sbox_proto.Manifest{}
Dan Willemsen4591b642021-05-24 14:24:12 -07001512 err := prototext.Unmarshal([]byte(content), &manifest)
Colin Crosse16ce362020-11-12 08:29:30 -08001513 if err != nil {
1514 t.Fatalf("failed to unmarshal manifest: %s", err.Error())
1515 }
1516 return &manifest
1517}
1518
Colin Cross1d2cf042019-03-29 15:33:06 -07001519func ninjaNameEscape(s string) string {
1520 b := []byte(s)
1521 escaped := false
1522 for i, c := range b {
1523 valid := (c >= 'a' && c <= 'z') ||
1524 (c >= 'A' && c <= 'Z') ||
1525 (c >= '0' && c <= '9') ||
1526 (c == '_') ||
1527 (c == '-') ||
1528 (c == '.')
1529 if !valid {
1530 b[i] = '_'
1531 escaped = true
1532 }
1533 }
1534 if escaped {
1535 s = string(b)
1536 }
1537 return s
1538}
Colin Cross3d680512020-11-13 16:23:53 -08001539
1540// hashSrcFiles returns a hash of the list of source files. It is used to ensure the command line
1541// or the sbox textproto manifest change even if the input files are not listed on the command line.
1542func hashSrcFiles(srcFiles Paths) string {
1543 h := sha256.New()
1544 srcFileList := strings.Join(srcFiles.Strings(), "\n")
1545 h.Write([]byte(srcFileList))
1546 return fmt.Sprintf("%x", h.Sum(nil))
1547}
Colin Crossf1a035e2020-11-16 17:32:30 -08001548
1549// BuilderContextForTesting returns a BuilderContext for the given config that can be used for tests
1550// that need to call methods that take a BuilderContext.
1551func BuilderContextForTesting(config Config) BuilderContext {
1552 pathCtx := PathContextForTesting(config)
1553 return builderContextForTests{
1554 PathContext: pathCtx,
1555 }
1556}
1557
1558type builderContextForTests struct {
1559 PathContext
1560}
1561
1562func (builderContextForTests) Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule {
1563 return nil
1564}
1565func (builderContextForTests) Build(PackageContext, BuildParams) {}
Colin Crossef972742021-03-12 17:24:45 -08001566
Colin Crosse55bd422021-03-23 13:44:30 -07001567func writeRspFileRule(ctx BuilderContext, rspFile WritablePath, paths Paths) {
1568 buf := &strings.Builder{}
1569 err := response.WriteRspFile(buf, paths.Strings())
1570 if err != nil {
1571 // There should never be I/O errors writing to a bytes.Buffer.
1572 panic(err)
Colin Crossef972742021-03-12 17:24:45 -08001573 }
Colin Crosse55bd422021-03-23 13:44:30 -07001574 WriteFileRule(ctx, rspFile, buf.String())
Colin Crossef972742021-03-12 17:24:45 -08001575}