blob: db56c3f2952cfba35f9ad59ac6b7bd72e320114c [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
Inseob Kim7195b062024-11-29 15:40:49 +090066 nsjailKeepGendir bool
Inseob Kimf7cd03e2024-09-06 17:25:00 +090067 nsjailBasePath WritablePath
68 nsjailImplicits Paths
Colin Crossfeec25b2019-01-30 17:32:39 -080069}
70
Colin Cross758290d2019-02-01 16:42:32 -080071// NewRuleBuilder returns a newly created RuleBuilder.
Colin Crossf1a035e2020-11-16 17:32:30 -080072func NewRuleBuilder(pctx PackageContext, ctx BuilderContext) *RuleBuilder {
Colin Cross5cb5b092019-02-02 21:25:18 -080073 return &RuleBuilder{
Colin Crossf1a035e2020-11-16 17:32:30 -080074 pctx: pctx,
75 ctx: ctx,
Colin Cross69f59a32019-02-15 10:39:37 -080076 temporariesSet: make(map[WritablePath]bool),
Spandan Dasaf4ccaa2023-06-29 01:15:51 +000077 sboxOutSubDir: sboxOutSubDir,
Colin Cross5cb5b092019-02-02 21:25:18 -080078 }
Colin Cross758290d2019-02-01 16:42:32 -080079}
80
Spandan Dasaf4ccaa2023-06-29 01:15:51 +000081// SetSboxOutDirDirAsEmpty sets the out subdirectory to an empty string
82// This is useful for sandboxing actions that change the execution root to a path in out/ (e.g mixed builds)
83// For such actions, SetSboxOutDirDirAsEmpty ensures that the path does not become $SBOX_SANDBOX_DIR/out/out/bazel/output/execroot/__main__/...
84func (rb *RuleBuilder) SetSboxOutDirDirAsEmpty() *RuleBuilder {
85 rb.sboxOutSubDir = ""
86 return rb
87}
88
Devin Mooreb6cc64f2024-07-15 22:31:24 +000089// Set the phony_output argument.
90// This causes the output files to be ignored.
91// If the output isn't created, it's not treated as an error.
92// The build rule is run every time whether or not the output is created.
93func (rb *RuleBuilder) SetPhonyOutput() {
94 if rb.args == nil {
95 rb.args = make(map[string]string)
96 }
97 rb.args["phony_output"] = "true"
98}
99
Colin Cross758290d2019-02-01 16:42:32 -0800100// RuleBuilderInstall is a tuple of install from and to locations.
101type RuleBuilderInstall struct {
Colin Cross69f59a32019-02-15 10:39:37 -0800102 From Path
103 To string
Colin Cross758290d2019-02-01 16:42:32 -0800104}
105
Colin Crossdeabb942019-02-11 14:11:09 -0800106type RuleBuilderInstalls []RuleBuilderInstall
107
108// String returns the RuleBuilderInstalls in the form used by $(call copy-many-files) in Make, a space separated
109// list of from:to tuples.
110func (installs RuleBuilderInstalls) String() string {
111 sb := strings.Builder{}
112 for i, install := range installs {
113 if i != 0 {
114 sb.WriteRune(' ')
115 }
Colin Cross69f59a32019-02-15 10:39:37 -0800116 sb.WriteString(install.From.String())
Colin Crossdeabb942019-02-11 14:11:09 -0800117 sb.WriteRune(':')
118 sb.WriteString(install.To)
119 }
120 return sb.String()
121}
122
Colin Cross0d2f40a2019-02-05 22:31:15 -0800123// MissingDeps adds modules to the list of missing dependencies. If MissingDeps
124// is called with a non-empty input, any call to Build will result in a rule
125// that will print an error listing the missing dependencies and fail.
126// MissingDeps should only be called if Config.AllowMissingDependencies() is
127// true.
128func (r *RuleBuilder) MissingDeps(missingDeps []string) {
129 r.missingDeps = append(r.missingDeps, missingDeps...)
130}
131
Colin Cross758290d2019-02-01 16:42:32 -0800132// 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 -0800133func (r *RuleBuilder) Restat() *RuleBuilder {
134 r.restat = true
135 return r
136}
137
Colin Cross8b8bec32019-11-15 13:18:43 -0800138// HighMem marks the rule as a high memory rule, which will limit how many run in parallel with other high memory
139// rules.
140func (r *RuleBuilder) HighMem() *RuleBuilder {
141 r.highmem = true
142 return r
143}
144
145// Remoteable marks the rule as supporting remote execution.
146func (r *RuleBuilder) Remoteable(supports RemoteRuleSupports) *RuleBuilder {
147 r.remoteable = supports
148 return r
149}
150
Colin Crossef972742021-03-12 17:24:45 -0800151// Rewrapper marks the rule as running inside rewrapper using the given params in order to support
152// running on RBE. During RuleBuilder.Build the params will be combined with the inputs, outputs
153// and tools known to RuleBuilder to prepend an appropriate rewrapper command line to the rule's
154// command line.
155func (r *RuleBuilder) Rewrapper(params *remoteexec.REParams) *RuleBuilder {
156 if !r.sboxInputs {
157 panic(fmt.Errorf("RuleBuilder.Rewrapper must be called after RuleBuilder.SandboxInputs"))
158 }
159 r.rbeParams = params
160 return r
161}
162
Colin Crosse16ce362020-11-12 08:29:30 -0800163// Sbox marks the rule as needing to be wrapped by sbox. The outputDir should point to the output
164// directory that sbox will wipe. It should not be written to by any other rule. manifestPath should
165// point to a location where sbox's manifest will be written and must be outside outputDir. sbox
166// will ensure that all outputs have been written, and will discard any output files that were not
167// specified.
Colin Crosse16ce362020-11-12 08:29:30 -0800168func (r *RuleBuilder) Sbox(outputDir WritablePath, manifestPath WritablePath) *RuleBuilder {
Dan Willemsen633c5022019-04-12 11:11:38 -0700169 if r.sbox {
170 panic("Sbox() may not be called more than once")
171 }
172 if len(r.commands) > 0 {
173 panic("Sbox() may not be called after Command()")
174 }
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900175 if r.nsjail {
176 panic("Sbox() may not be called after Nsjail()")
177 }
Dan Willemsen633c5022019-04-12 11:11:38 -0700178 r.sbox = true
Colin Crossf1a035e2020-11-16 17:32:30 -0800179 r.outDir = outputDir
Colin Crosse16ce362020-11-12 08:29:30 -0800180 r.sboxManifestPath = manifestPath
Dan Willemsen633c5022019-04-12 11:11:38 -0700181 return r
182}
183
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900184// Nsjail marks the rule as needing to be wrapped by nsjail. The outputDir should point to the
185// output directory that nsjail will mount to out/. It should not be written to by any other rule.
186// baseDir should point to a location where nsjail will mount to /nsjail_build_sandbox, which will
187// be the working directory of the command.
188func (r *RuleBuilder) Nsjail(outputDir WritablePath, baseDir WritablePath) *RuleBuilder {
189 if len(r.commands) > 0 {
190 panic("Nsjail() may not be called after Command()")
191 }
192 if r.sbox {
193 panic("Nsjail() may not be called after Sbox()")
194 }
195 r.nsjail = true
196 r.outDir = outputDir
197 r.nsjailBasePath = baseDir
198 return r
199}
200
201// NsjailImplicits adds implicit inputs that are not directly mounted. This is useful when
202// the rule mounts directories, as files within those directories can be globbed and
203// tracked as dependencies with NsjailImplicits().
204func (r *RuleBuilder) NsjailImplicits(inputs Paths) *RuleBuilder {
205 if !r.nsjail {
206 panic("NsjailImplicits() must be called after Nsjail()")
207 }
208 r.nsjailImplicits = append(r.nsjailImplicits, inputs...)
209 return r
210}
211
Inseob Kim7195b062024-11-29 15:40:49 +0900212// By default, nsjail rules truncate outputDir and baseDir before running commands, similar to Sbox
213// rules which always run commands in a fresh sandbox. Calling NsjailKeepGendir keeps outputDir and
214// baseDir as-is, leaving previous artifacts. This is useful when the rules support incremental
215// builds.
216func (r *RuleBuilder) NsjailKeepGendir() *RuleBuilder {
217 if !r.nsjail {
218 panic("NsjailKeepGendir() must be called after Nsjail()")
219 }
220 r.nsjailKeepGendir = true
221 return r
222}
223
Colin Crossba9e4032020-11-24 16:32:22 -0800224// SandboxTools enables tool sandboxing for the rule by copying any referenced tools into the
225// sandbox.
226func (r *RuleBuilder) SandboxTools() *RuleBuilder {
227 if !r.sbox {
228 panic("SandboxTools() must be called after Sbox()")
229 }
230 if len(r.commands) > 0 {
231 panic("SandboxTools() may not be called after Command()")
232 }
233 r.sboxTools = true
234 return r
235}
236
Colin Crossab020a72021-03-12 17:52:23 -0800237// SandboxInputs enables input sandboxing for the rule by copying any referenced inputs into the
238// sandbox. It also implies SandboxTools().
239//
240// Sandboxing inputs requires RuleBuilder to be aware of all references to input paths. Paths
241// that are passed to RuleBuilder outside of the methods that expect inputs, for example
242// FlagWithArg, must use RuleBuilderCommand.PathForInput to translate the path to one that matches
243// the sandbox layout.
244func (r *RuleBuilder) SandboxInputs() *RuleBuilder {
245 if !r.sbox {
246 panic("SandboxInputs() must be called after Sbox()")
247 }
248 if len(r.commands) > 0 {
249 panic("SandboxInputs() may not be called after Command()")
250 }
251 r.sboxTools = true
252 r.sboxInputs = true
253 return r
254}
255
Colin Cross758290d2019-02-01 16:42:32 -0800256// Install associates an output of the rule with an install location, which can be retrieved later using
257// RuleBuilder.Installs.
Colin Cross69f59a32019-02-15 10:39:37 -0800258func (r *RuleBuilder) Install(from Path, to string) {
Colin Crossfeec25b2019-01-30 17:32:39 -0800259 r.installs = append(r.installs, RuleBuilderInstall{from, to})
260}
261
Colin Cross758290d2019-02-01 16:42:32 -0800262// Command returns a new RuleBuilderCommand for the rule. The commands will be ordered in the rule by when they were
263// created by this method. That can be mutated through their methods in any order, as long as the mutations do not
264// race with any call to Build.
Colin Crossfeec25b2019-01-30 17:32:39 -0800265func (r *RuleBuilder) Command() *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700266 command := &RuleBuilderCommand{
Colin Crossf1a035e2020-11-16 17:32:30 -0800267 rule: r,
Dan Willemsen633c5022019-04-12 11:11:38 -0700268 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800269 r.commands = append(r.commands, command)
270 return command
271}
272
Colin Cross5cb5b092019-02-02 21:25:18 -0800273// Temporary marks an output of a command as an intermediate file that will be used as an input to another command
274// in the same rule, and should not be listed in Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800275func (r *RuleBuilder) Temporary(path WritablePath) {
Colin Cross5cb5b092019-02-02 21:25:18 -0800276 r.temporariesSet[path] = true
277}
278
279// DeleteTemporaryFiles adds a command to the rule that deletes any outputs that have been marked using Temporary
280// when the rule runs. DeleteTemporaryFiles should be called after all calls to Temporary.
281func (r *RuleBuilder) DeleteTemporaryFiles() {
Colin Cross69f59a32019-02-15 10:39:37 -0800282 var temporariesList WritablePaths
Colin Cross5cb5b092019-02-02 21:25:18 -0800283
284 for intermediate := range r.temporariesSet {
285 temporariesList = append(temporariesList, intermediate)
286 }
Colin Cross69f59a32019-02-15 10:39:37 -0800287
288 sort.Slice(temporariesList, func(i, j int) bool {
289 return temporariesList[i].String() < temporariesList[j].String()
290 })
Colin Cross5cb5b092019-02-02 21:25:18 -0800291
292 r.Command().Text("rm").Flag("-f").Outputs(temporariesList)
293}
294
Colin Crossda71eda2020-02-21 16:55:19 -0800295// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
Colin Cross3d680512020-11-13 16:23:53 -0800296// input paths, such as RuleBuilderCommand.Input, RuleBuilderCommand.Implicit, or
Colin Crossda71eda2020-02-21 16:55:19 -0800297// RuleBuilderCommand.FlagWithInput. Inputs to a command that are also outputs of another command
298// in the same RuleBuilder are filtered out. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800299func (r *RuleBuilder) Inputs() Paths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800300 outputs := r.outputSet()
Dan Willemsen633c5022019-04-12 11:11:38 -0700301 depFiles := r.depFileSet()
Colin Crossfeec25b2019-01-30 17:32:39 -0800302
Colin Cross69f59a32019-02-15 10:39:37 -0800303 inputs := make(map[string]Path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800304 for _, c := range r.commands {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400305 for _, input := range append(c.inputs, c.implicits...) {
Dan Willemsen633c5022019-04-12 11:11:38 -0700306 inputStr := input.String()
307 if _, isOutput := outputs[inputStr]; !isOutput {
308 if _, isDepFile := depFiles[inputStr]; !isDepFile {
309 inputs[input.String()] = input
310 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800311 }
312 }
313 }
314
Colin Cross69f59a32019-02-15 10:39:37 -0800315 var inputList Paths
316 for _, input := range inputs {
Colin Crossfeec25b2019-01-30 17:32:39 -0800317 inputList = append(inputList, input)
318 }
Colin Cross69f59a32019-02-15 10:39:37 -0800319
320 sort.Slice(inputList, func(i, j int) bool {
321 return inputList[i].String() < inputList[j].String()
322 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800323
324 return inputList
325}
326
Colin Crossda71eda2020-02-21 16:55:19 -0800327// OrderOnlys returns the list of paths that were passed to the RuleBuilderCommand.OrderOnly or
328// RuleBuilderCommand.OrderOnlys. The list is sorted and duplicates removed.
329func (r *RuleBuilder) OrderOnlys() Paths {
330 orderOnlys := make(map[string]Path)
331 for _, c := range r.commands {
332 for _, orderOnly := range c.orderOnlys {
333 orderOnlys[orderOnly.String()] = orderOnly
334 }
335 }
336
337 var orderOnlyList Paths
338 for _, orderOnly := range orderOnlys {
339 orderOnlyList = append(orderOnlyList, orderOnly)
340 }
341
342 sort.Slice(orderOnlyList, func(i, j int) bool {
343 return orderOnlyList[i].String() < orderOnlyList[j].String()
344 })
345
346 return orderOnlyList
347}
348
Colin Crossae89abe2021-04-21 11:45:23 -0700349// Validations returns the list of paths that were passed to RuleBuilderCommand.Validation or
350// RuleBuilderCommand.Validations. The list is sorted and duplicates removed.
351func (r *RuleBuilder) Validations() Paths {
352 validations := make(map[string]Path)
353 for _, c := range r.commands {
354 for _, validation := range c.validations {
355 validations[validation.String()] = validation
356 }
357 }
358
359 var validationList Paths
360 for _, validation := range validations {
361 validationList = append(validationList, validation)
362 }
363
364 sort.Slice(validationList, func(i, j int) bool {
365 return validationList[i].String() < validationList[j].String()
366 })
367
368 return validationList
369}
370
Colin Cross69f59a32019-02-15 10:39:37 -0800371func (r *RuleBuilder) outputSet() map[string]WritablePath {
372 outputs := make(map[string]WritablePath)
Colin Crossfeec25b2019-01-30 17:32:39 -0800373 for _, c := range r.commands {
374 for _, output := range c.outputs {
Colin Cross69f59a32019-02-15 10:39:37 -0800375 outputs[output.String()] = output
Colin Crossfeec25b2019-01-30 17:32:39 -0800376 }
377 }
378 return outputs
379}
380
Colin Crossda71eda2020-02-21 16:55:19 -0800381// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
382// output paths, such as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or
383// RuleBuilderCommand.FlagWithInput. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800384func (r *RuleBuilder) Outputs() WritablePaths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800385 outputs := r.outputSet()
386
Colin Cross69f59a32019-02-15 10:39:37 -0800387 var outputList WritablePaths
388 for _, output := range outputs {
Colin Cross5cb5b092019-02-02 21:25:18 -0800389 if !r.temporariesSet[output] {
390 outputList = append(outputList, output)
391 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800392 }
Colin Cross69f59a32019-02-15 10:39:37 -0800393
394 sort.Slice(outputList, func(i, j int) bool {
395 return outputList[i].String() < outputList[j].String()
396 })
397
Colin Crossfeec25b2019-01-30 17:32:39 -0800398 return outputList
399}
400
Dan Willemsen633c5022019-04-12 11:11:38 -0700401func (r *RuleBuilder) depFileSet() map[string]WritablePath {
402 depFiles := make(map[string]WritablePath)
403 for _, c := range r.commands {
404 for _, depFile := range c.depFiles {
405 depFiles[depFile.String()] = depFile
406 }
407 }
408 return depFiles
409}
410
Colin Cross1d2cf042019-03-29 15:33:06 -0700411// DepFiles returns the list of paths that were passed to the RuleBuilderCommand methods that take depfile paths, such
412// as RuleBuilderCommand.DepFile or RuleBuilderCommand.FlagWithDepFile.
413func (r *RuleBuilder) DepFiles() WritablePaths {
414 var depFiles WritablePaths
415
416 for _, c := range r.commands {
417 for _, depFile := range c.depFiles {
418 depFiles = append(depFiles, depFile)
419 }
420 }
421
422 return depFiles
423}
424
Colin Cross758290d2019-02-01 16:42:32 -0800425// Installs returns the list of tuples passed to Install.
Colin Crossdeabb942019-02-11 14:11:09 -0800426func (r *RuleBuilder) Installs() RuleBuilderInstalls {
427 return append(RuleBuilderInstalls(nil), r.installs...)
Colin Crossfeec25b2019-01-30 17:32:39 -0800428}
429
Colin Cross69f59a32019-02-15 10:39:37 -0800430func (r *RuleBuilder) toolsSet() map[string]Path {
431 tools := make(map[string]Path)
Colin Cross5cb5b092019-02-02 21:25:18 -0800432 for _, c := range r.commands {
433 for _, tool := range c.tools {
Colin Cross69f59a32019-02-15 10:39:37 -0800434 tools[tool.String()] = tool
Colin Cross5cb5b092019-02-02 21:25:18 -0800435 }
436 }
437
438 return tools
439}
440
Colin Crossda71eda2020-02-21 16:55:19 -0800441// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method. The
442// list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800443func (r *RuleBuilder) Tools() Paths {
Colin Cross5cb5b092019-02-02 21:25:18 -0800444 toolsSet := r.toolsSet()
445
Colin Cross69f59a32019-02-15 10:39:37 -0800446 var toolsList Paths
447 for _, tool := range toolsSet {
Colin Cross5cb5b092019-02-02 21:25:18 -0800448 toolsList = append(toolsList, tool)
Colin Crossfeec25b2019-01-30 17:32:39 -0800449 }
Colin Cross69f59a32019-02-15 10:39:37 -0800450
451 sort.Slice(toolsList, func(i, j int) bool {
452 return toolsList[i].String() < toolsList[j].String()
453 })
454
Colin Cross5cb5b092019-02-02 21:25:18 -0800455 return toolsList
Colin Crossfeec25b2019-01-30 17:32:39 -0800456}
457
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700458// RspFileInputs returns the list of paths that were passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
459func (r *RuleBuilder) RspFileInputs() Paths {
460 var rspFileInputs Paths
461 for _, c := range r.commands {
Colin Crossce3a51d2021-03-19 16:22:12 -0700462 for _, rspFile := range c.rspFiles {
463 rspFileInputs = append(rspFileInputs, rspFile.paths...)
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700464 }
465 }
466
467 return rspFileInputs
468}
469
Colin Crossce3a51d2021-03-19 16:22:12 -0700470func (r *RuleBuilder) rspFiles() []rspFileAndPaths {
471 var rspFiles []rspFileAndPaths
Colin Cross70c47412021-03-12 17:48:14 -0800472 for _, c := range r.commands {
Colin Crossce3a51d2021-03-19 16:22:12 -0700473 rspFiles = append(rspFiles, c.rspFiles...)
Colin Cross70c47412021-03-12 17:48:14 -0800474 }
475
Colin Crossce3a51d2021-03-19 16:22:12 -0700476 return rspFiles
Colin Cross70c47412021-03-12 17:48:14 -0800477}
478
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700479// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
Colin Crossfeec25b2019-01-30 17:32:39 -0800480func (r *RuleBuilder) Commands() []string {
481 var commands []string
482 for _, c := range r.commands {
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700483 commands = append(commands, c.String())
484 }
485 return commands
486}
487
Colin Cross758290d2019-02-01 16:42:32 -0800488// BuilderContext is a subset of ModuleContext and SingletonContext.
Colin Cross786cd6d2019-02-01 16:41:11 -0800489type BuilderContext interface {
490 PathContext
491 Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
492 Build(PackageContext, BuildParams)
493}
494
Colin Cross758290d2019-02-01 16:42:32 -0800495var _ BuilderContext = ModuleContext(nil)
496var _ BuilderContext = SingletonContext(nil)
497
Colin Crossf1a035e2020-11-16 17:32:30 -0800498func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700499 return r.Command().
Colin Cross9b698b62021-12-22 09:55:32 -0800500 builtToolWithoutDeps("dep_fixer").
Dan Willemsen633c5022019-04-12 11:11:38 -0700501 Inputs(depFiles.Paths())
Colin Cross1d2cf042019-03-29 15:33:06 -0700502}
503
Colin Cross758290d2019-02-01 16:42:32 -0800504// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
505// Outputs.
Colin Crossf1a035e2020-11-16 17:32:30 -0800506func (r *RuleBuilder) Build(name string, desc string) {
Cole Faust2f3791f2024-11-25 15:49:57 -0800507 r.build(name, desc)
Sam Delmerico285b66a2023-09-25 12:13:17 +0000508}
509
Cole Faust63ea1f92024-08-27 11:42:26 -0700510var sandboxEnvOnceKey = NewOnceKey("sandbox_environment_variables")
511
Cole Faust2f3791f2024-11-25 15:49:57 -0800512func (r *RuleBuilder) build(name string, desc string) {
Colin Cross1d2cf042019-03-29 15:33:06 -0700513 name = ninjaNameEscape(name)
514
Colin Cross0d2f40a2019-02-05 22:31:15 -0800515 if len(r.missingDeps) > 0 {
Sam Delmerico285b66a2023-09-25 12:13:17 +0000516 r.ctx.Build(r.pctx, BuildParams{
Colin Cross0d2f40a2019-02-05 22:31:15 -0800517 Rule: ErrorRule,
Colin Cross69f59a32019-02-15 10:39:37 -0800518 Outputs: r.Outputs(),
Colin Cross0d2f40a2019-02-05 22:31:15 -0800519 Description: desc,
520 Args: map[string]string{
521 "error": "missing dependencies: " + strings.Join(r.missingDeps, ", "),
522 },
523 })
524 return
525 }
526
Colin Cross1d2cf042019-03-29 15:33:06 -0700527 var depFile WritablePath
528 var depFormat blueprint.Deps
529 if depFiles := r.DepFiles(); len(depFiles) > 0 {
530 depFile = depFiles[0]
531 depFormat = blueprint.DepsGCC
532 if len(depFiles) > 1 {
533 // Add a command locally that merges all depfiles together into the first depfile.
Colin Crossf1a035e2020-11-16 17:32:30 -0800534 r.depFileMergerCmd(depFiles)
Dan Willemsen633c5022019-04-12 11:11:38 -0700535
536 if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800537 // Check for Rel() errors, as all depfiles should be in the output dir. Errors
538 // will be reported to the ctx.
Dan Willemsen633c5022019-04-12 11:11:38 -0700539 for _, path := range depFiles[1:] {
Colin Crossf1a035e2020-11-16 17:32:30 -0800540 Rel(r.ctx, r.outDir.String(), path.String())
Dan Willemsen633c5022019-04-12 11:11:38 -0700541 }
542 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700543 }
544 }
545
Dan Willemsen633c5022019-04-12 11:11:38 -0700546 tools := r.Tools()
Colin Crossb70a1a92021-03-12 17:51:32 -0800547 commands := r.Commands()
Dan Willemsen633c5022019-04-12 11:11:38 -0700548 outputs := r.Outputs()
Colin Cross3d680512020-11-13 16:23:53 -0800549 inputs := r.Inputs()
Colin Crossce3a51d2021-03-19 16:22:12 -0700550 rspFiles := r.rspFiles()
Dan Willemsen633c5022019-04-12 11:11:38 -0700551
552 if len(commands) == 0 {
553 return
554 }
555 if len(outputs) == 0 {
556 panic("No outputs specified from any Commands")
557 }
558
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700559 commandString := strings.Join(commands, " && ")
Dan Willemsen633c5022019-04-12 11:11:38 -0700560
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900561 if !r.sbox {
562 // If not using sbox the rule will run the command directly, put the hash of the
563 // list of input files in a comment at the end of the command line to ensure ninja
564 // reruns the rule when the list of input files changes.
565 commandString += " # hash of input list: " + hashSrcFiles(inputs)
566 }
567
568 if r.nsjail {
569 var nsjailCmd strings.Builder
570 nsjailPath := r.ctx.Config().PrebuiltBuildTool(r.ctx, "nsjail")
Inseob Kim7195b062024-11-29 15:40:49 +0900571 if !r.nsjailKeepGendir {
572 nsjailCmd.WriteString("rm -rf ")
573 nsjailCmd.WriteString(r.nsjailBasePath.String())
574 nsjailCmd.WriteRune(' ')
575 nsjailCmd.WriteString(r.outDir.String())
576 nsjailCmd.WriteString(" && ")
577 }
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900578 nsjailCmd.WriteString("mkdir -p ")
579 nsjailCmd.WriteString(r.nsjailBasePath.String())
Inseob Kim7195b062024-11-29 15:40:49 +0900580 nsjailCmd.WriteRune(' ')
581 nsjailCmd.WriteString(r.outDir.String())
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900582 nsjailCmd.WriteString(" && ")
583 nsjailCmd.WriteString(nsjailPath.String())
584 nsjailCmd.WriteRune(' ')
585 nsjailCmd.WriteString("-B $PWD/")
586 nsjailCmd.WriteString(r.nsjailBasePath.String())
587 nsjailCmd.WriteString(":nsjail_build_sandbox")
588
589 // out is mounted to $(genDir).
590 nsjailCmd.WriteString(" -B $PWD/")
591 nsjailCmd.WriteString(r.outDir.String())
592 nsjailCmd.WriteString(":nsjail_build_sandbox/out")
593
Inseob Kim93036a52024-10-25 17:02:21 +0900594 addBindMount := func(src, dst string) {
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900595 nsjailCmd.WriteString(" -R $PWD/")
Inseob Kim93036a52024-10-25 17:02:21 +0900596 nsjailCmd.WriteString(src)
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900597 nsjailCmd.WriteString(":nsjail_build_sandbox/")
Inseob Kim93036a52024-10-25 17:02:21 +0900598 nsjailCmd.WriteString(dst)
599 }
600
601 for _, input := range inputs {
602 addBindMount(input.String(), r.nsjailPathForInputRel(input))
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900603 }
604 for _, tool := range tools {
Inseob Kim93036a52024-10-25 17:02:21 +0900605 addBindMount(tool.String(), nsjailPathForToolRel(r.ctx, tool))
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900606 }
607 inputs = append(inputs, tools...)
608 for _, c := range r.commands {
Inseob Kim93036a52024-10-25 17:02:21 +0900609 for _, directory := range c.implicitDirectories {
610 addBindMount(directory.String(), directory.String())
611 // TODO(b/375551969): Add implicitDirectories to BuildParams, rather than relying on implicits
612 inputs = append(inputs, SourcePath{basePath: directory.base()})
613 }
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900614 for _, tool := range c.packagedTools {
Inseob Kim93036a52024-10-25 17:02:21 +0900615 addBindMount(tool.srcPath.String(), nsjailPathForPackagedToolRel(tool))
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900616 inputs = append(inputs, tool.srcPath)
617 }
618 }
619
620 // These five directories are necessary to run native host tools like /bin/bash and py3-cmd.
621 nsjailCmd.WriteString(" -R /bin")
622 nsjailCmd.WriteString(" -R /lib")
623 nsjailCmd.WriteString(" -R /lib64")
624 nsjailCmd.WriteString(" -R /dev")
625 nsjailCmd.WriteString(" -R /usr")
626
627 nsjailCmd.WriteString(" -m none:/tmp:tmpfs:size=1073741824") // 1GB, should be enough
628 nsjailCmd.WriteString(" -D nsjail_build_sandbox")
629 nsjailCmd.WriteString(" --disable_rlimits")
Haamed Gheibic128dd72024-11-13 13:27:53 -0800630 nsjailCmd.WriteString(" --skip_setsid") // ABFS relies on process-groups to track file operations
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900631 nsjailCmd.WriteString(" -q")
632 nsjailCmd.WriteString(" -- ")
633 nsjailCmd.WriteString("/bin/bash -c ")
634 nsjailCmd.WriteString(proptools.ShellEscape(commandString))
635
636 commandString = nsjailCmd.String()
637
638 inputs = append(inputs, nsjailPath)
639 inputs = append(inputs, r.nsjailImplicits...)
640 } else if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800641 // If running the command inside sbox, write the rule data out to an sbox
642 // manifest.textproto.
643 manifest := sbox_proto.Manifest{}
644 command := sbox_proto.Command{}
645 manifest.Commands = append(manifest.Commands, &command)
646 command.Command = proto.String(commandString)
Colin Cross151b9ff2020-11-12 08:29:30 -0800647
Colin Cross619b9ab2020-11-20 18:44:31 +0000648 if depFile != nil {
Colin Crosse16ce362020-11-12 08:29:30 -0800649 manifest.OutputDepfile = proto.String(depFile.String())
Colin Cross619b9ab2020-11-20 18:44:31 +0000650 }
651
Colin Crossba9e4032020-11-24 16:32:22 -0800652 // If sandboxing tools is enabled, add copy rules to the manifest to copy each tool
653 // into the sbox directory.
654 if r.sboxTools {
655 for _, tool := range tools {
656 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
657 From: proto.String(tool.String()),
658 To: proto.String(sboxPathForToolRel(r.ctx, tool)),
659 })
660 }
661 for _, c := range r.commands {
662 for _, tool := range c.packagedTools {
663 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
664 From: proto.String(tool.srcPath.String()),
665 To: proto.String(sboxPathForPackagedToolRel(tool)),
666 Executable: proto.Bool(tool.executable),
667 })
668 tools = append(tools, tool.srcPath)
669 }
670 }
671 }
672
Colin Crossab020a72021-03-12 17:52:23 -0800673 // If sandboxing inputs is enabled, add copy rules to the manifest to copy each input
674 // into the sbox directory.
675 if r.sboxInputs {
676 for _, input := range inputs {
677 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
678 From: proto.String(input.String()),
679 To: proto.String(r.sboxPathForInputRel(input)),
680 })
681 }
Cole Faust78f3c3a2024-08-15 17:19:34 -0700682 for _, input := range r.OrderOnlys() {
683 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
684 From: proto.String(input.String()),
685 To: proto.String(r.sboxPathForInputRel(input)),
686 })
687 }
Colin Crossab020a72021-03-12 17:52:23 -0800688
Colin Crossce3a51d2021-03-19 16:22:12 -0700689 // If using rsp files copy them and their contents into the sbox directory with
690 // the appropriate path mappings.
691 for _, rspFile := range rspFiles {
Colin Crosse55bd422021-03-23 13:44:30 -0700692 command.RspFiles = append(command.RspFiles, &sbox_proto.RspFile{
Colin Crossce3a51d2021-03-19 16:22:12 -0700693 File: proto.String(rspFile.file.String()),
Colin Crosse55bd422021-03-23 13:44:30 -0700694 // These have to match the logic in sboxPathForInputRel
695 PathMappings: []*sbox_proto.PathMapping{
696 {
697 From: proto.String(r.outDir.String()),
698 To: proto.String(sboxOutSubDir),
699 },
700 {
Cole Fauste8561c62023-11-30 17:26:37 -0800701 From: proto.String(r.ctx.Config().OutDir()),
Colin Crosse55bd422021-03-23 13:44:30 -0700702 To: proto.String(sboxOutSubDir),
703 },
704 },
Colin Crossab020a72021-03-12 17:52:23 -0800705 })
706 }
707
Cole Faust63ea1f92024-08-27 11:42:26 -0700708 // Only allow the build to access certain environment variables
709 command.DontInheritEnv = proto.Bool(true)
710 command.Env = r.ctx.Config().Once(sandboxEnvOnceKey, func() interface{} {
711 // The list of allowed variables was found by running builds of all
712 // genrules and seeing what failed
713 var result []*sbox_proto.EnvironmentVariable
714 inheritedVars := []string{
715 "PATH",
716 "JAVA_HOME",
717 "TMPDIR",
718 // Allow RBE variables because the art tests invoke RBE manually
719 "RBE_log_dir",
720 "RBE_platform",
721 "RBE_server_address",
722 // TODO: RBE_exec_root is set to the absolute path to the root of the source
723 // tree, which we don't want sandboxed actions to find. Remap it to ".".
724 "RBE_exec_root",
725 }
726 for _, v := range inheritedVars {
727 result = append(result, &sbox_proto.EnvironmentVariable{
728 Name: proto.String(v),
729 State: &sbox_proto.EnvironmentVariable_Inherit{
730 Inherit: true,
731 },
732 })
733 }
734 // Set OUT_DIR to the relative path of the sandboxed out directory.
735 // Otherwise, OUT_DIR will be inherited from the rest of the build,
736 // which will allow scripts to escape the sandbox if OUT_DIR is an
737 // absolute path.
738 result = append(result, &sbox_proto.EnvironmentVariable{
739 Name: proto.String("OUT_DIR"),
740 State: &sbox_proto.EnvironmentVariable_Value{
741 Value: sboxOutSubDir,
742 },
743 })
744 return result
745 }).([]*sbox_proto.EnvironmentVariable)
Colin Crossab020a72021-03-12 17:52:23 -0800746 command.Chdir = proto.Bool(true)
747 }
748
Colin Crosse16ce362020-11-12 08:29:30 -0800749 // Add copy rules to the manifest to copy each output file from the sbox directory.
Colin Crossba9e4032020-11-24 16:32:22 -0800750 // to the output directory after running the commands.
Spandan Das33e30972023-07-13 21:19:12 +0000751 for _, output := range outputs {
Colin Crossf1a035e2020-11-16 17:32:30 -0800752 rel := Rel(r.ctx, r.outDir.String(), output.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800753 command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{
Spandan Dasaf4ccaa2023-06-29 01:15:51 +0000754 From: proto.String(filepath.Join(r.sboxOutSubDir, rel)),
Colin Crosse16ce362020-11-12 08:29:30 -0800755 To: proto.String(output.String()),
756 })
757 }
Colin Cross619b9ab2020-11-20 18:44:31 +0000758
Colin Cross5334edd2021-03-11 17:18:21 -0800759 // Outputs that were marked Temporary will not be checked that they are in the output
760 // directory by the loop above, check them here.
761 for path := range r.temporariesSet {
762 Rel(r.ctx, r.outDir.String(), path.String())
763 }
764
Colin Crosse16ce362020-11-12 08:29:30 -0800765 // Add a hash of the list of input files to the manifest so that the textproto file
766 // changes when the list of input files changes and causes the sbox rule that
767 // depends on it to rerun.
768 command.InputHash = proto.String(hashSrcFiles(inputs))
Colin Cross619b9ab2020-11-20 18:44:31 +0000769
Colin Crosse16ce362020-11-12 08:29:30 -0800770 // Verify that the manifest textproto is not inside the sbox output directory, otherwise
771 // it will get deleted when the sbox rule clears its output directory.
Colin Crossf1a035e2020-11-16 17:32:30 -0800772 _, manifestInOutDir := MaybeRel(r.ctx, r.outDir.String(), r.sboxManifestPath.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800773 if manifestInOutDir {
Colin Crossf1a035e2020-11-16 17:32:30 -0800774 ReportPathErrorf(r.ctx, "sbox rule %q manifestPath %q must not be in outputDir %q",
775 name, r.sboxManifestPath.String(), r.outDir.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800776 }
777
Paul Duffin4a3a0a52023-10-12 15:01:29 +0100778 // Create a rule to write the manifest as textproto. Pretty print it by indenting and
779 // splitting across multiple lines.
780 pbText, err := prototext.MarshalOptions{Indent: " "}.Marshal(&manifest)
Dan Willemsen4591b642021-05-24 14:24:12 -0700781 if err != nil {
782 ReportPathErrorf(r.ctx, "sbox manifest failed to marshal: %q", err)
783 }
Cole Faust2f3791f2024-11-25 15:49:57 -0800784 WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText))
Colin Crosse16ce362020-11-12 08:29:30 -0800785
786 // Generate a new string to use as the command line of the sbox rule. This uses
787 // a RuleBuilderCommand as a convenience method of building the command line, then
788 // converts it to a string to replace commandString.
Colin Crossf1a035e2020-11-16 17:32:30 -0800789 sboxCmd := &RuleBuilderCommand{
790 rule: &RuleBuilder{
791 ctx: r.ctx,
792 },
793 }
Colin Cross9b698b62021-12-22 09:55:32 -0800794 sboxCmd.builtToolWithoutDeps("sbox").
Colin Crosse52c2ac2022-03-28 17:03:35 -0700795 FlagWithArg("--sandbox-path ", shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
796 FlagWithArg("--output-dir ", r.outDir.String()).
797 FlagWithInput("--manifest ", r.sboxManifestPath)
798
799 if r.restat {
800 sboxCmd.Flag("--write-if-changed")
801 }
Colin Crosse16ce362020-11-12 08:29:30 -0800802
803 // Replace the command string, and add the sbox tool and manifest textproto to the
804 // dependencies of the final sbox rule.
Colin Crosscfec40c2019-07-08 17:07:18 -0700805 commandString = sboxCmd.buf.String()
Dan Willemsen633c5022019-04-12 11:11:38 -0700806 tools = append(tools, sboxCmd.tools...)
Colin Crosse16ce362020-11-12 08:29:30 -0800807 inputs = append(inputs, sboxCmd.inputs...)
Colin Crossef972742021-03-12 17:24:45 -0800808
809 if r.rbeParams != nil {
Colin Crosse55bd422021-03-23 13:44:30 -0700810 // RBE needs a list of input files to copy to the remote builder. For inputs already
811 // listed in an rsp file, pass the rsp file directly to rewrapper. For the rest,
812 // create a new rsp file to pass to rewrapper.
813 var remoteRspFiles Paths
814 var remoteInputs Paths
815
816 remoteInputs = append(remoteInputs, inputs...)
817 remoteInputs = append(remoteInputs, tools...)
818
Colin Crossce3a51d2021-03-19 16:22:12 -0700819 for _, rspFile := range rspFiles {
820 remoteInputs = append(remoteInputs, rspFile.file)
821 remoteRspFiles = append(remoteRspFiles, rspFile.file)
Colin Crossef972742021-03-12 17:24:45 -0800822 }
Colin Crosse55bd422021-03-23 13:44:30 -0700823
824 if len(remoteInputs) > 0 {
825 inputsListFile := r.sboxManifestPath.ReplaceExtension(r.ctx, "rbe_inputs.list")
826 writeRspFileRule(r.ctx, inputsListFile, remoteInputs)
827 remoteRspFiles = append(remoteRspFiles, inputsListFile)
828 // Add the new rsp file as an extra input to the rule.
829 inputs = append(inputs, inputsListFile)
830 }
Colin Crossef972742021-03-12 17:24:45 -0800831
832 r.rbeParams.OutputFiles = outputs.Strings()
Colin Crosse55bd422021-03-23 13:44:30 -0700833 r.rbeParams.RSPFiles = remoteRspFiles.Strings()
Colin Crossef972742021-03-12 17:24:45 -0800834 rewrapperCommand := r.rbeParams.NoVarTemplate(r.ctx.Config().RBEWrapper())
835 commandString = rewrapperCommand + " bash -c '" + strings.ReplaceAll(commandString, `'`, `'\''`) + "'"
836 }
Dan Willemsen633c5022019-04-12 11:11:38 -0700837 }
838
Colin Cross1d2cf042019-03-29 15:33:06 -0700839 // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
Colin Cross70c47412021-03-12 17:48:14 -0800840 // ImplicitOutputs. RuleBuilder doesn't use "$out", so the distinction between Outputs and
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700841 // ImplicitOutputs doesn't matter.
Dan Willemsen633c5022019-04-12 11:11:38 -0700842 output := outputs[0]
843 implicitOutputs := outputs[1:]
Colin Cross1d2cf042019-03-29 15:33:06 -0700844
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700845 var rspFile, rspFileContent string
Colin Crossce3a51d2021-03-19 16:22:12 -0700846 var rspFileInputs Paths
847 if len(rspFiles) > 0 {
848 // The first rsp files uses Ninja's rsp file support for the rule
849 rspFile = rspFiles[0].file.String()
Colin Crosse55bd422021-03-23 13:44:30 -0700850 // Use "$in" for rspFileContent to avoid duplicating the list of files in the dependency
851 // list and in the contents of the rsp file. Inputs to the rule that are not in the
852 // rsp file will be listed in Implicits instead of Inputs so they don't show up in "$in".
853 rspFileContent = "$in"
Colin Crossce3a51d2021-03-19 16:22:12 -0700854 rspFileInputs = append(rspFileInputs, rspFiles[0].paths...)
855
856 for _, rspFile := range rspFiles[1:] {
857 // Any additional rsp files need an extra rule to write the file.
858 writeRspFileRule(r.ctx, rspFile.file, rspFile.paths)
859 // The main rule needs to depend on the inputs listed in the extra rsp file.
860 inputs = append(inputs, rspFile.paths...)
861 // The main rule needs to depend on the extra rsp file.
862 inputs = append(inputs, rspFile.file)
863 }
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700864 }
865
Colin Cross8b8bec32019-11-15 13:18:43 -0800866 var pool blueprint.Pool
Colin Crossf1a035e2020-11-16 17:32:30 -0800867 if r.ctx.Config().UseGoma() && r.remoteable.Goma {
Colin Cross8b8bec32019-11-15 13:18:43 -0800868 // 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 -0800869 } else if r.ctx.Config().UseRBE() && r.remoteable.RBE {
Ramy Medhat944839a2020-03-31 22:14:52 -0400870 // When USE_RBE=true is set and the rule is supported by RBE, use the remotePool.
871 pool = remotePool
Colin Cross8b8bec32019-11-15 13:18:43 -0800872 } else if r.highmem {
873 pool = highmemPool
Colin Crossf1a035e2020-11-16 17:32:30 -0800874 } else if r.ctx.Config().UseRemoteBuild() {
Colin Cross8b8bec32019-11-15 13:18:43 -0800875 pool = localPool
876 }
877
Cole Faustd7556eb2024-12-02 13:18:58 -0800878 // If the command length is getting close to linux's maximum, dump it to a file, which allows
879 // for longer commands.
880 if len(commandString) > 100000 {
881 hasher := sha256.New()
882 hasher.Write([]byte(output.String()))
883 script := PathForOutput(r.ctx, "rule_builder_scripts", fmt.Sprintf("%x.sh", hasher.Sum(nil)))
884 commandString = "set -eu\n\n" + commandString + "\n"
885 WriteExecutableFileRuleVerbatim(r.ctx, script, commandString)
886 inputs = append(inputs, script)
887 commandString = script.String()
888 }
889
Cole Faust2f3791f2024-11-25 15:49:57 -0800890 commandString = proptools.NinjaEscape(commandString)
Sam Delmericod46f6c82023-09-25 12:13:17 +0000891
Devin Mooreb6cc64f2024-07-15 22:31:24 +0000892 args_vars := make([]string, len(r.args))
893 i := 0
894 for k, _ := range r.args {
895 args_vars[i] = k
896 i++
897 }
Colin Crossf1a035e2020-11-16 17:32:30 -0800898 r.ctx.Build(r.pctx, BuildParams{
Sam Delmerico285b66a2023-09-25 12:13:17 +0000899 Rule: r.ctx.Rule(r.pctx, name, blueprint.RuleParams{
Sam Delmericod46f6c82023-09-25 12:13:17 +0000900 Command: commandString,
Colin Cross45029782021-03-16 16:49:52 -0700901 CommandDeps: proptools.NinjaEscapeList(tools.Strings()),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700902 Restat: r.restat,
Colin Cross45029782021-03-16 16:49:52 -0700903 Rspfile: proptools.NinjaEscape(rspFile),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700904 RspfileContent: rspFileContent,
Colin Cross8b8bec32019-11-15 13:18:43 -0800905 Pool: pool,
Devin Mooreb6cc64f2024-07-15 22:31:24 +0000906 }, args_vars...),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700907 Inputs: rspFileInputs,
Colin Cross3d680512020-11-13 16:23:53 -0800908 Implicits: inputs,
Colin Crossda6401b2021-04-21 11:32:19 -0700909 OrderOnly: r.OrderOnlys(),
Colin Crossae89abe2021-04-21 11:45:23 -0700910 Validations: r.Validations(),
Dan Willemsen633c5022019-04-12 11:11:38 -0700911 Output: output,
912 ImplicitOutputs: implicitOutputs,
913 Depfile: depFile,
914 Deps: depFormat,
915 Description: desc,
Devin Mooreb6cc64f2024-07-15 22:31:24 +0000916 Args: r.args,
Dan Willemsen633c5022019-04-12 11:11:38 -0700917 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800918}
919
Colin Cross758290d2019-02-01 16:42:32 -0800920// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
921// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the
922// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
923// space as a separator from the previous method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800924type RuleBuilderCommand struct {
Colin Crossf1a035e2020-11-16 17:32:30 -0800925 rule *RuleBuilder
926
Inseob Kim93036a52024-10-25 17:02:21 +0900927 buf strings.Builder
928 inputs Paths
929 implicits Paths
930 orderOnlys Paths
931 validations Paths
932 outputs WritablePaths
933 depFiles WritablePaths
934 tools Paths
935 packagedTools []PackagingSpec
936 rspFiles []rspFileAndPaths
937 implicitDirectories DirectoryPaths
Colin Crossce3a51d2021-03-19 16:22:12 -0700938}
939
940type rspFileAndPaths struct {
941 file WritablePath
942 paths Paths
Dan Willemsen633c5022019-04-12 11:11:38 -0700943}
944
Paul Duffin3866b892021-10-04 11:24:48 +0100945func checkPathNotNil(path Path) {
946 if path == nil {
947 panic("rule_builder paths cannot be nil")
948 }
949}
950
Dan Willemsen633c5022019-04-12 11:11:38 -0700951func (c *RuleBuilderCommand) addInput(path Path) string {
Paul Duffin3866b892021-10-04 11:24:48 +0100952 checkPathNotNil(path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700953 c.inputs = append(c.inputs, path)
Colin Crossab020a72021-03-12 17:52:23 -0800954 return c.PathForInput(path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700955}
956
Colin Crossab020a72021-03-12 17:52:23 -0800957func (c *RuleBuilderCommand) addImplicit(path Path) {
Paul Duffin3866b892021-10-04 11:24:48 +0100958 checkPathNotNil(path)
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400959 c.implicits = append(c.implicits, path)
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400960}
961
Inseob Kim93036a52024-10-25 17:02:21 +0900962func (c *RuleBuilderCommand) addImplicitDirectory(path DirectoryPath) {
963 c.implicitDirectories = append(c.implicitDirectories, path)
964}
965
Colin Crossda71eda2020-02-21 16:55:19 -0800966func (c *RuleBuilderCommand) addOrderOnly(path Path) {
Paul Duffin3866b892021-10-04 11:24:48 +0100967 checkPathNotNil(path)
Colin Crossda71eda2020-02-21 16:55:19 -0800968 c.orderOnlys = append(c.orderOnlys, path)
969}
970
Colin Crossab020a72021-03-12 17:52:23 -0800971// PathForInput takes an input path and returns the appropriate path to use on the command line. If
972// sbox was enabled via a call to RuleBuilder.Sbox() and the path was an output path it returns a
973// path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
974// original path.
975func (c *RuleBuilderCommand) PathForInput(path Path) string {
976 if c.rule.sbox {
977 rel, inSandbox := c.rule._sboxPathForInputRel(path)
978 if inSandbox {
979 rel = filepath.Join(sboxSandboxBaseDir, rel)
980 }
981 return rel
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900982 } else if c.rule.nsjail {
983 return c.rule.nsjailPathForInputRel(path)
Colin Crossab020a72021-03-12 17:52:23 -0800984 }
985 return path.String()
986}
987
988// PathsForInputs takes a list of input paths and returns the appropriate paths to use on the
989// command line. If sbox was enabled via a call to RuleBuilder.Sbox() a path was an output path, it
990// returns the path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it
991// returns the original paths.
992func (c *RuleBuilderCommand) PathsForInputs(paths Paths) []string {
993 ret := make([]string, len(paths))
994 for i, path := range paths {
995 ret[i] = c.PathForInput(path)
996 }
997 return ret
998}
999
Colin Crossf1a035e2020-11-16 17:32:30 -08001000// PathForOutput takes an output path and returns the appropriate path to use on the command
1001// line. If sbox was enabled via a call to RuleBuilder.Sbox(), it returns a path with the
1002// placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
1003// original path.
1004func (c *RuleBuilderCommand) PathForOutput(path WritablePath) string {
1005 if c.rule.sbox {
1006 // Errors will be handled in RuleBuilder.Build where we have a context to report them
1007 rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
1008 return filepath.Join(sboxOutDir, rel)
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001009 } else if c.rule.nsjail {
1010 // Errors will be handled in RuleBuilder.Build where we have a context to report them
1011 rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
1012 return filepath.Join(nsjailOutDir, rel)
Dan Willemsen633c5022019-04-12 11:11:38 -07001013 }
1014 return path.String()
Colin Crossfeec25b2019-01-30 17:32:39 -08001015}
1016
Colin Crossba9e4032020-11-24 16:32:22 -08001017func sboxPathForToolRel(ctx BuilderContext, path Path) string {
1018 // Errors will be handled in RuleBuilder.Build where we have a context to report them
Cole Faust3b703f32023-10-16 13:30:51 -07001019 toolDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "")
Colin Cross790ef352021-10-25 19:15:55 -07001020 relOutSoong, isRelOutSoong, _ := maybeRelErr(toolDir.String(), path.String())
1021 if isRelOutSoong {
1022 // The tool is in the Soong output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
1023 return filepath.Join(sboxToolsSubDir, "out", relOutSoong)
Colin Crossba9e4032020-11-24 16:32:22 -08001024 }
1025 // The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
1026 return filepath.Join(sboxToolsSubDir, "src", path.String())
1027}
1028
Colin Crossab020a72021-03-12 17:52:23 -08001029func (r *RuleBuilder) _sboxPathForInputRel(path Path) (rel string, inSandbox bool) {
1030 // Errors will be handled in RuleBuilder.Build where we have a context to report them
1031 rel, isRelSboxOut, _ := maybeRelErr(r.outDir.String(), path.String())
1032 if isRelSboxOut {
1033 return filepath.Join(sboxOutSubDir, rel), true
1034 }
1035 if r.sboxInputs {
1036 // When sandboxing inputs all inputs have to be copied into the sandbox. Input files that
1037 // are outputs of other rules could be an arbitrary absolute path if OUT_DIR is set, so they
1038 // will be copied to relative paths under __SBOX_OUT_DIR__/out.
Cole Fauste8561c62023-11-30 17:26:37 -08001039 rel, isRelOut, _ := maybeRelErr(r.ctx.Config().OutDir(), path.String())
Colin Crossab020a72021-03-12 17:52:23 -08001040 if isRelOut {
1041 return filepath.Join(sboxOutSubDir, rel), true
1042 }
1043 }
1044 return path.String(), false
1045}
1046
1047func (r *RuleBuilder) sboxPathForInputRel(path Path) string {
1048 rel, _ := r._sboxPathForInputRel(path)
1049 return rel
1050}
1051
1052func (r *RuleBuilder) sboxPathsForInputsRel(paths Paths) []string {
1053 ret := make([]string, len(paths))
1054 for i, path := range paths {
1055 ret[i] = r.sboxPathForInputRel(path)
1056 }
1057 return ret
1058}
1059
Colin Crossba9e4032020-11-24 16:32:22 -08001060func sboxPathForPackagedToolRel(spec PackagingSpec) string {
1061 return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage)
1062}
1063
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001064func nsjailPathForToolRel(ctx BuilderContext, path Path) string {
1065 // Errors will be handled in RuleBuilder.Build where we have a context to report them
1066 toolDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "")
1067 relOutSoong, isRelOutSoong, _ := maybeRelErr(toolDir.String(), path.String())
1068 if isRelOutSoong {
1069 // The tool is in the Soong output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
1070 return filepath.Join(nsjailToolsSubDir, "out", relOutSoong)
1071 }
1072 // The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
1073 return filepath.Join(nsjailToolsSubDir, "src", path.String())
1074}
1075
1076func (r *RuleBuilder) nsjailPathForInputRel(path Path) string {
1077 rel, isRelSboxOut, _ := maybeRelErr(r.outDir.String(), path.String())
1078 if isRelSboxOut {
1079 return filepath.Join(nsjailOutDir, rel)
1080 }
1081 return path.String()
1082}
1083
1084func (r *RuleBuilder) nsjailPathsForInputsRel(paths Paths) []string {
1085 ret := make([]string, len(paths))
1086 for i, path := range paths {
1087 ret[i] = r.nsjailPathForInputRel(path)
1088 }
1089 return ret
1090}
1091
1092func nsjailPathForPackagedToolRel(spec PackagingSpec) string {
1093 return filepath.Join(nsjailToolsSubDir, "out", spec.relPathInPackage)
1094}
1095
Colin Crossd11cf622021-03-23 22:30:35 -07001096// PathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
1097// tool after copying it into the sandbox. This can be used on the RuleBuilder command line to
1098// reference the tool.
1099func (c *RuleBuilderCommand) PathForPackagedTool(spec PackagingSpec) string {
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001100 if c.rule.sboxTools {
1101 return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
1102 } else if c.rule.nsjail {
1103 return nsjailPathForPackagedToolRel(spec)
1104 } else {
1105 panic("PathForPackagedTool() requires SandboxTools() or Nsjail()")
Colin Crossd11cf622021-03-23 22:30:35 -07001106 }
Colin Crossd11cf622021-03-23 22:30:35 -07001107}
1108
Colin Crossba9e4032020-11-24 16:32:22 -08001109// PathForTool takes a path to a tool, which may be an output file or a source file, and returns
1110// the corresponding path for the tool in the sbox sandbox if sbox is enabled, or the original path
1111// if it is not. This can be used on the RuleBuilder command line to reference the tool.
1112func (c *RuleBuilderCommand) PathForTool(path Path) string {
1113 if c.rule.sbox && c.rule.sboxTools {
1114 return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path))
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001115 } else if c.rule.nsjail {
1116 return nsjailPathForToolRel(c.rule.ctx, path)
Colin Crossba9e4032020-11-24 16:32:22 -08001117 }
1118 return path.String()
1119}
1120
Colin Crossd11cf622021-03-23 22:30:35 -07001121// PathsForTools takes a list of paths to tools, which may be output files or source files, and
1122// returns the corresponding paths for the tools in the sbox sandbox if sbox is enabled, or the
1123// original paths if it is not. This can be used on the RuleBuilder command line to reference the tool.
1124func (c *RuleBuilderCommand) PathsForTools(paths Paths) []string {
1125 if c.rule.sbox && c.rule.sboxTools {
1126 var ret []string
1127 for _, path := range paths {
1128 ret = append(ret, filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path)))
1129 }
1130 return ret
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001131 } else if c.rule.nsjail {
1132 var ret []string
1133 for _, path := range paths {
1134 ret = append(ret, nsjailPathForToolRel(c.rule.ctx, path))
1135 }
1136 return ret
Colin Crossd11cf622021-03-23 22:30:35 -07001137 }
1138 return paths.Strings()
1139}
1140
Colin Crossba9e4032020-11-24 16:32:22 -08001141// PackagedTool adds the specified tool path to the command line. It can only be used with tool
1142// sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox.
1143func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand {
Colin Crossba9e4032020-11-24 16:32:22 -08001144 c.packagedTools = append(c.packagedTools, spec)
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001145 if c.rule.sboxTools {
1146 c.Text(sboxPathForPackagedToolRel(spec))
1147 } else if c.rule.nsjail {
1148 c.Text(nsjailPathForPackagedToolRel(spec))
1149 } else {
1150 panic("PackagedTool() requires SandboxTools() or Nsjail()")
1151 }
Colin Crossba9e4032020-11-24 16:32:22 -08001152 return c
1153}
1154
1155// ImplicitPackagedTool copies the specified tool into the sandbox without modifying the command
1156// line. It can only be used with tool sandboxing enabled by SandboxTools().
1157func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuilderCommand {
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001158 if !c.rule.sboxTools && !c.rule.nsjail {
1159 panic("ImplicitPackagedTool() requires SandboxTools() or Nsjail()")
Colin Crossba9e4032020-11-24 16:32:22 -08001160 }
1161
1162 c.packagedTools = append(c.packagedTools, spec)
1163 return c
1164}
1165
1166// ImplicitPackagedTools copies the specified tools into the sandbox without modifying the command
1167// line. It can only be used with tool sandboxing enabled by SandboxTools().
1168func (c *RuleBuilderCommand) ImplicitPackagedTools(specs []PackagingSpec) *RuleBuilderCommand {
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001169 if !c.rule.sboxTools && !c.rule.nsjail {
1170 panic("ImplicitPackagedTools() requires SandboxTools() or Nsjail()")
Colin Crossba9e4032020-11-24 16:32:22 -08001171 }
1172
1173 c.packagedTools = append(c.packagedTools, specs...)
1174 return c
1175}
1176
Colin Cross758290d2019-02-01 16:42:32 -08001177// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
1178// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001179func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
Colin Crosscfec40c2019-07-08 17:07:18 -07001180 if c.buf.Len() > 0 {
1181 c.buf.WriteByte(' ')
Colin Crossfeec25b2019-01-30 17:32:39 -08001182 }
Colin Crosscfec40c2019-07-08 17:07:18 -07001183 c.buf.WriteString(text)
Colin Crossfeec25b2019-01-30 17:32:39 -08001184 return c
1185}
1186
Colin Cross758290d2019-02-01 16:42:32 -08001187// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or
1188// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001189func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
1190 return c.Text(fmt.Sprintf(format, a...))
1191}
1192
Colin Cross758290d2019-02-01 16:42:32 -08001193// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the
1194// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001195func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
1196 return c.Text(flag)
1197}
1198
Colin Crossab054432019-07-15 16:13:59 -07001199// OptionalFlag adds the specified raw text to the command line if it is not nil. The text should not contain input or
1200// output paths or the rule will not have them listed in its dependencies or outputs.
1201func (c *RuleBuilderCommand) OptionalFlag(flag *string) *RuleBuilderCommand {
1202 if flag != nil {
1203 c.Text(*flag)
1204 }
1205
1206 return c
1207}
1208
Colin Cross92b7d582019-03-29 15:32:51 -07001209// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the
1210// rule will not have them listed in its dependencies or outputs.
1211func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
1212 for _, flag := range flags {
1213 c.Text(flag)
1214 }
1215 return c
1216}
1217
Colin Cross758290d2019-02-01 16:42:32 -08001218// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
1219// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
1220// outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001221func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
1222 return c.Text(flag + arg)
1223}
1224
Colin Crossc7ed0042019-02-11 14:11:09 -08001225// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to
1226// calling FlagWithArg for argument.
1227func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand {
1228 for _, arg := range args {
1229 c.FlagWithArg(flag, arg)
1230 }
1231 return c
1232}
1233
Roland Levillain2da5d9a2019-02-27 16:56:41 +00001234// 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 -08001235// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or
1236// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001237func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
1238 return c.Text(flag + strings.Join(list, sep))
1239}
1240
Colin Cross758290d2019-02-01 16:42:32 -08001241// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by
1242// RuleBuilder.Tools.
Colin Cross69f59a32019-02-15 10:39:37 -08001243func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001244 checkPathNotNil(path)
Colin Crossfeec25b2019-01-30 17:32:39 -08001245 c.tools = append(c.tools, path)
Colin Crossba9e4032020-11-24 16:32:22 -08001246 return c.Text(c.PathForTool(path))
1247}
1248
1249// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
1250func (c *RuleBuilderCommand) ImplicitTool(path Path) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001251 checkPathNotNil(path)
Colin Crossba9e4032020-11-24 16:32:22 -08001252 c.tools = append(c.tools, path)
1253 return c
1254}
1255
1256// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
1257func (c *RuleBuilderCommand) ImplicitTools(paths Paths) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001258 for _, path := range paths {
1259 c.ImplicitTool(path)
1260 }
Colin Crossba9e4032020-11-24 16:32:22 -08001261 return c
Colin Crossfeec25b2019-01-30 17:32:39 -08001262}
1263
Colin Crossee94d6a2019-07-08 17:08:34 -07001264// BuiltTool adds the specified tool path that was built using a host Soong module to the command line. The path will
1265// be also added to the dependencies returned by RuleBuilder.Tools.
1266//
1267// It is equivalent to:
Colin Crossd079e0b2022-08-16 10:27:33 -07001268//
1269// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
Colin Crossf1a035e2020-11-16 17:32:30 -08001270func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
Colin Cross9b698b62021-12-22 09:55:32 -08001271 if c.rule.ctx.Config().UseHostMusl() {
1272 // If the host is using musl, assume that the tool was built against musl libc and include
1273 // libc_musl.so in the sandbox.
1274 // TODO(ccross): if we supported adding new dependencies during GenerateAndroidBuildActions
1275 // this could be a dependency + TransitivePackagingSpecs.
1276 c.ImplicitTool(c.rule.ctx.Config().HostJNIToolPath(c.rule.ctx, "libc_musl"))
1277 }
1278 return c.builtToolWithoutDeps(tool)
1279}
1280
1281// builtToolWithoutDeps is similar to BuiltTool, but doesn't add any dependencies. It is used
1282// internally by RuleBuilder for helper tools that are known to be compiled statically.
1283func (c *RuleBuilderCommand) builtToolWithoutDeps(tool string) *RuleBuilderCommand {
Colin Crossf1a035e2020-11-16 17:32:30 -08001284 return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
Colin Crossee94d6a2019-07-08 17:08:34 -07001285}
1286
1287// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the
1288// dependencies returned by RuleBuilder.Tools.
1289//
1290// It is equivalent to:
Colin Crossd079e0b2022-08-16 10:27:33 -07001291//
1292// cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
Colin Crossee94d6a2019-07-08 17:08:34 -07001293func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
1294 return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
1295}
1296
Colin Cross758290d2019-02-01 16:42:32 -08001297// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
1298// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001299func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001300 return c.Text(c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001301}
1302
Colin Cross758290d2019-02-01 16:42:32 -08001303// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
1304// dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001305func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001306 for _, path := range paths {
1307 c.Input(path)
1308 }
1309 return c
1310}
1311
1312// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
1313// command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001314func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001315 c.addImplicit(path)
Colin Crossfeec25b2019-01-30 17:32:39 -08001316 return c
1317}
1318
Colin Cross758290d2019-02-01 16:42:32 -08001319// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
1320// command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001321func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001322 for _, path := range paths {
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001323 c.addImplicit(path)
Dan Willemsen633c5022019-04-12 11:11:38 -07001324 }
Colin Crossfeec25b2019-01-30 17:32:39 -08001325 return c
1326}
1327
Inseob Kim93036a52024-10-25 17:02:21 +09001328// ImplicitDirectory adds the specified input directory to the dependencies without modifying the
1329// command line. Added directories will be bind-mounted for the nsjail.
1330func (c *RuleBuilderCommand) ImplicitDirectory(path DirectoryPath) *RuleBuilderCommand {
1331 if !c.rule.nsjail {
1332 panic("ImplicitDirectory() must be called after Nsjail()")
1333 }
1334 c.addImplicitDirectory(path)
1335 return c
1336}
1337
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001338// GetImplicits returns the command's implicit inputs.
1339func (c *RuleBuilderCommand) GetImplicits() Paths {
1340 return c.implicits
1341}
1342
Colin Crossda71eda2020-02-21 16:55:19 -08001343// OrderOnly adds the specified input path to the dependencies returned by RuleBuilder.OrderOnlys
1344// without modifying the command line.
1345func (c *RuleBuilderCommand) OrderOnly(path Path) *RuleBuilderCommand {
1346 c.addOrderOnly(path)
1347 return c
1348}
1349
1350// OrderOnlys adds the specified input paths to the dependencies returned by RuleBuilder.OrderOnlys
1351// without modifying the command line.
1352func (c *RuleBuilderCommand) OrderOnlys(paths Paths) *RuleBuilderCommand {
1353 for _, path := range paths {
1354 c.addOrderOnly(path)
1355 }
1356 return c
1357}
1358
Colin Crossae89abe2021-04-21 11:45:23 -07001359// Validation adds the specified input path to the validation dependencies by
1360// RuleBuilder.Validations without modifying the command line.
1361func (c *RuleBuilderCommand) Validation(path Path) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001362 checkPathNotNil(path)
Colin Crossae89abe2021-04-21 11:45:23 -07001363 c.validations = append(c.validations, path)
1364 return c
1365}
1366
1367// Validations adds the specified input paths to the validation dependencies by
1368// RuleBuilder.Validations without modifying the command line.
1369func (c *RuleBuilderCommand) Validations(paths Paths) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001370 for _, path := range paths {
1371 c.Validation(path)
1372 }
Colin Crossae89abe2021-04-21 11:45:23 -07001373 return c
1374}
1375
Colin Cross758290d2019-02-01 16:42:32 -08001376// Output adds the specified output path to the command line. The path will also be added to the outputs returned by
1377// RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001378func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001379 checkPathNotNil(path)
Colin Crossfeec25b2019-01-30 17:32:39 -08001380 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001381 return c.Text(c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001382}
1383
Colin Cross758290d2019-02-01 16:42:32 -08001384// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
1385// the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001386func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001387 for _, path := range paths {
1388 c.Output(path)
1389 }
1390 return c
1391}
1392
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001393// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
1394// and will be the temporary output directory managed by sbox, not the final one.
Anas Sulaimanb4dff132024-02-07 21:58:46 +00001395func (c *RuleBuilderCommand) OutputDir(subPathComponents ...string) *RuleBuilderCommand {
Colin Crossf1a035e2020-11-16 17:32:30 -08001396 if !c.rule.sbox {
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001397 panic("OutputDir only valid with Sbox")
1398 }
Anas Sulaimanb4dff132024-02-07 21:58:46 +00001399 path := sboxOutDir
1400 if len(subPathComponents) > 0 {
1401 path = filepath.Join(append([]string{sboxOutDir}, subPathComponents...)...)
1402 }
1403 return c.Text(path)
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001404}
1405
Colin Cross1d2cf042019-03-29 15:33:06 -07001406// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
1407// line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles are added to
1408// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
1409func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001410 checkPathNotNil(path)
Colin Cross1d2cf042019-03-29 15:33:06 -07001411 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001412 return c.Text(c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -07001413}
1414
Colin Cross758290d2019-02-01 16:42:32 -08001415// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
1416// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001417func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001418 c.outputs = append(c.outputs, path)
1419 return c
1420}
1421
Colin Cross758290d2019-02-01 16:42:32 -08001422// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
1423// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001424func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001425 c.outputs = append(c.outputs, paths...)
1426 return c
1427}
1428
Colin Cross1d2cf042019-03-29 15:33:06 -07001429// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
1430// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles
1431// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
1432// depfiles together.
1433func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand {
1434 c.depFiles = append(c.depFiles, path)
1435 return c
1436}
1437
Colin Cross758290d2019-02-01 16:42:32 -08001438// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
1439// will also be added to the dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001440func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001441 return c.Text(flag + c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001442}
1443
Colin Cross758290d2019-02-01 16:42:32 -08001444// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
1445// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
1446// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001447func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001448 strs := make([]string, len(paths))
1449 for i, path := range paths {
1450 strs[i] = c.addInput(path)
1451 }
1452 return c.FlagWithList(flag, strs, sep)
Colin Crossfeec25b2019-01-30 17:32:39 -08001453}
1454
Colin Cross758290d2019-02-01 16:42:32 -08001455// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
1456// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for
1457// each input path.
Colin Cross69f59a32019-02-15 10:39:37 -08001458func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001459 for _, path := range paths {
1460 c.FlagWithInput(flag, path)
1461 }
1462 return c
1463}
1464
1465// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path
1466// will also be added to the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001467func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001468 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001469 return c.Text(flag + c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001470}
1471
Colin Cross1d2cf042019-03-29 15:33:06 -07001472// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path
1473// will also be added to the outputs returned by RuleBuilder.Outputs.
1474func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
1475 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001476 return c.Text(flag + c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -07001477}
1478
Colin Crossce3a51d2021-03-19 16:22:12 -07001479// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with
1480// no separator between them. The paths will be written to the rspfile. If sbox is enabled, the
1481// rspfile must be outside the sbox directory. The first use of FlagWithRspFileInputList in any
1482// RuleBuilderCommand of a RuleBuilder will use Ninja's rsp file support for the rule, additional
1483// uses will result in an auxiliary rules to write the rspFile contents.
Colin Cross70c47412021-03-12 17:48:14 -08001484func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, rspFile WritablePath, paths Paths) *RuleBuilderCommand {
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001485 // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
1486 // generated.
1487 if paths == nil {
1488 paths = Paths{}
1489 }
1490
Colin Crossce3a51d2021-03-19 16:22:12 -07001491 c.rspFiles = append(c.rspFiles, rspFileAndPaths{rspFile, paths})
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001492
Colin Cross70c47412021-03-12 17:48:14 -08001493 if c.rule.sbox {
1494 if _, isRel, _ := maybeRelErr(c.rule.outDir.String(), rspFile.String()); isRel {
1495 panic(fmt.Errorf("FlagWithRspFileInputList rspfile %q must not be inside out dir %q",
1496 rspFile.String(), c.rule.outDir.String()))
1497 }
1498 }
1499
Colin Crossab020a72021-03-12 17:52:23 -08001500 c.FlagWithArg(flag, c.PathForInput(rspFile))
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001501 return c
1502}
1503
Colin Cross758290d2019-02-01 16:42:32 -08001504// String returns the command line.
1505func (c *RuleBuilderCommand) String() string {
Colin Crosscfec40c2019-07-08 17:07:18 -07001506 return c.buf.String()
Colin Cross758290d2019-02-01 16:42:32 -08001507}
Colin Cross1d2cf042019-03-29 15:33:06 -07001508
Colin Crosse16ce362020-11-12 08:29:30 -08001509// RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox()
1510// and returns sbox testproto generated by the RuleBuilder.
Colin Crossf61d03d2023-11-02 16:56:39 -07001511func RuleBuilderSboxProtoForTests(t *testing.T, ctx *TestContext, params TestingBuildParams) *sbox_proto.Manifest {
Colin Crosse16ce362020-11-12 08:29:30 -08001512 t.Helper()
Colin Crossf61d03d2023-11-02 16:56:39 -07001513 content := ContentFromFileRuleForTests(t, ctx, params)
Colin Crosse16ce362020-11-12 08:29:30 -08001514 manifest := sbox_proto.Manifest{}
Dan Willemsen4591b642021-05-24 14:24:12 -07001515 err := prototext.Unmarshal([]byte(content), &manifest)
Colin Crosse16ce362020-11-12 08:29:30 -08001516 if err != nil {
1517 t.Fatalf("failed to unmarshal manifest: %s", err.Error())
1518 }
1519 return &manifest
1520}
1521
Colin Cross1d2cf042019-03-29 15:33:06 -07001522func ninjaNameEscape(s string) string {
1523 b := []byte(s)
1524 escaped := false
1525 for i, c := range b {
1526 valid := (c >= 'a' && c <= 'z') ||
1527 (c >= 'A' && c <= 'Z') ||
1528 (c >= '0' && c <= '9') ||
1529 (c == '_') ||
1530 (c == '-') ||
1531 (c == '.')
1532 if !valid {
1533 b[i] = '_'
1534 escaped = true
1535 }
1536 }
1537 if escaped {
1538 s = string(b)
1539 }
1540 return s
1541}
Colin Cross3d680512020-11-13 16:23:53 -08001542
1543// hashSrcFiles returns a hash of the list of source files. It is used to ensure the command line
1544// or the sbox textproto manifest change even if the input files are not listed on the command line.
1545func hashSrcFiles(srcFiles Paths) string {
1546 h := sha256.New()
1547 srcFileList := strings.Join(srcFiles.Strings(), "\n")
1548 h.Write([]byte(srcFileList))
1549 return fmt.Sprintf("%x", h.Sum(nil))
1550}
Colin Crossf1a035e2020-11-16 17:32:30 -08001551
1552// BuilderContextForTesting returns a BuilderContext for the given config that can be used for tests
1553// that need to call methods that take a BuilderContext.
1554func BuilderContextForTesting(config Config) BuilderContext {
1555 pathCtx := PathContextForTesting(config)
1556 return builderContextForTests{
1557 PathContext: pathCtx,
1558 }
1559}
1560
1561type builderContextForTests struct {
1562 PathContext
1563}
1564
1565func (builderContextForTests) Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule {
1566 return nil
1567}
1568func (builderContextForTests) Build(PackageContext, BuildParams) {}
Colin Crossef972742021-03-12 17:24:45 -08001569
Colin Crosse55bd422021-03-23 13:44:30 -07001570func writeRspFileRule(ctx BuilderContext, rspFile WritablePath, paths Paths) {
1571 buf := &strings.Builder{}
1572 err := response.WriteRspFile(buf, paths.Strings())
1573 if err != nil {
1574 // There should never be I/O errors writing to a bytes.Buffer.
1575 panic(err)
Colin Crossef972742021-03-12 17:24:45 -08001576 }
Colin Crosse55bd422021-03-23 13:44:30 -07001577 WriteFileRule(ctx, rspFile, buf.String())
Colin Crossef972742021-03-12 17:24:45 -08001578}