blob: 01fe6d8eaa2c9a06e4ea4a09c07a5ec56b720f06 [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 {
Cole Faust7d345122025-03-06 12:45:53 -0800663 if tool.srcPath != nil {
664 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
665 From: proto.String(tool.srcPath.String()),
666 To: proto.String(sboxPathForPackagedToolRel(tool)),
667 Executable: proto.Bool(tool.executable),
668 })
669 tools = append(tools, tool.srcPath)
670 } else if tool.SymlinkTarget() == "" {
671 // We ignore symlinks for now, could be added later if needed
672 panic("Expected tool packagingSpec to either be a file or symlink")
673 }
Colin Crossba9e4032020-11-24 16:32:22 -0800674 }
675 }
676 }
677
Colin Crossab020a72021-03-12 17:52:23 -0800678 // If sandboxing inputs is enabled, add copy rules to the manifest to copy each input
679 // into the sbox directory.
680 if r.sboxInputs {
681 for _, input := range inputs {
682 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
683 From: proto.String(input.String()),
684 To: proto.String(r.sboxPathForInputRel(input)),
685 })
686 }
Cole Faust78f3c3a2024-08-15 17:19:34 -0700687 for _, input := range r.OrderOnlys() {
688 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
689 From: proto.String(input.String()),
690 To: proto.String(r.sboxPathForInputRel(input)),
691 })
692 }
Colin Crossab020a72021-03-12 17:52:23 -0800693
Colin Crossce3a51d2021-03-19 16:22:12 -0700694 // If using rsp files copy them and their contents into the sbox directory with
695 // the appropriate path mappings.
696 for _, rspFile := range rspFiles {
Colin Crosse55bd422021-03-23 13:44:30 -0700697 command.RspFiles = append(command.RspFiles, &sbox_proto.RspFile{
Colin Crossce3a51d2021-03-19 16:22:12 -0700698 File: proto.String(rspFile.file.String()),
Colin Crosse55bd422021-03-23 13:44:30 -0700699 // These have to match the logic in sboxPathForInputRel
700 PathMappings: []*sbox_proto.PathMapping{
701 {
702 From: proto.String(r.outDir.String()),
703 To: proto.String(sboxOutSubDir),
704 },
705 {
Cole Fauste8561c62023-11-30 17:26:37 -0800706 From: proto.String(r.ctx.Config().OutDir()),
Colin Crosse55bd422021-03-23 13:44:30 -0700707 To: proto.String(sboxOutSubDir),
708 },
709 },
Colin Crossab020a72021-03-12 17:52:23 -0800710 })
711 }
712
Cole Faust63ea1f92024-08-27 11:42:26 -0700713 // Only allow the build to access certain environment variables
714 command.DontInheritEnv = proto.Bool(true)
715 command.Env = r.ctx.Config().Once(sandboxEnvOnceKey, func() interface{} {
716 // The list of allowed variables was found by running builds of all
717 // genrules and seeing what failed
718 var result []*sbox_proto.EnvironmentVariable
719 inheritedVars := []string{
720 "PATH",
721 "JAVA_HOME",
722 "TMPDIR",
723 // Allow RBE variables because the art tests invoke RBE manually
724 "RBE_log_dir",
725 "RBE_platform",
726 "RBE_server_address",
727 // TODO: RBE_exec_root is set to the absolute path to the root of the source
728 // tree, which we don't want sandboxed actions to find. Remap it to ".".
729 "RBE_exec_root",
730 }
731 for _, v := range inheritedVars {
732 result = append(result, &sbox_proto.EnvironmentVariable{
733 Name: proto.String(v),
734 State: &sbox_proto.EnvironmentVariable_Inherit{
735 Inherit: true,
736 },
737 })
738 }
739 // Set OUT_DIR to the relative path of the sandboxed out directory.
740 // Otherwise, OUT_DIR will be inherited from the rest of the build,
741 // which will allow scripts to escape the sandbox if OUT_DIR is an
742 // absolute path.
743 result = append(result, &sbox_proto.EnvironmentVariable{
744 Name: proto.String("OUT_DIR"),
745 State: &sbox_proto.EnvironmentVariable_Value{
746 Value: sboxOutSubDir,
747 },
748 })
749 return result
750 }).([]*sbox_proto.EnvironmentVariable)
Colin Crossab020a72021-03-12 17:52:23 -0800751 command.Chdir = proto.Bool(true)
752 }
753
Colin Crosse16ce362020-11-12 08:29:30 -0800754 // Add copy rules to the manifest to copy each output file from the sbox directory.
Colin Crossba9e4032020-11-24 16:32:22 -0800755 // to the output directory after running the commands.
Spandan Das33e30972023-07-13 21:19:12 +0000756 for _, output := range outputs {
Colin Crossf1a035e2020-11-16 17:32:30 -0800757 rel := Rel(r.ctx, r.outDir.String(), output.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800758 command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{
Spandan Dasaf4ccaa2023-06-29 01:15:51 +0000759 From: proto.String(filepath.Join(r.sboxOutSubDir, rel)),
Colin Crosse16ce362020-11-12 08:29:30 -0800760 To: proto.String(output.String()),
761 })
762 }
Colin Cross619b9ab2020-11-20 18:44:31 +0000763
Colin Cross5334edd2021-03-11 17:18:21 -0800764 // Outputs that were marked Temporary will not be checked that they are in the output
765 // directory by the loop above, check them here.
766 for path := range r.temporariesSet {
767 Rel(r.ctx, r.outDir.String(), path.String())
768 }
769
Colin Crosse16ce362020-11-12 08:29:30 -0800770 // Add a hash of the list of input files to the manifest so that the textproto file
771 // changes when the list of input files changes and causes the sbox rule that
772 // depends on it to rerun.
773 command.InputHash = proto.String(hashSrcFiles(inputs))
Colin Cross619b9ab2020-11-20 18:44:31 +0000774
Colin Crosse16ce362020-11-12 08:29:30 -0800775 // Verify that the manifest textproto is not inside the sbox output directory, otherwise
776 // it will get deleted when the sbox rule clears its output directory.
Colin Crossf1a035e2020-11-16 17:32:30 -0800777 _, manifestInOutDir := MaybeRel(r.ctx, r.outDir.String(), r.sboxManifestPath.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800778 if manifestInOutDir {
Colin Crossf1a035e2020-11-16 17:32:30 -0800779 ReportPathErrorf(r.ctx, "sbox rule %q manifestPath %q must not be in outputDir %q",
780 name, r.sboxManifestPath.String(), r.outDir.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800781 }
782
Paul Duffin4a3a0a52023-10-12 15:01:29 +0100783 // Create a rule to write the manifest as textproto. Pretty print it by indenting and
784 // splitting across multiple lines.
785 pbText, err := prototext.MarshalOptions{Indent: " "}.Marshal(&manifest)
Dan Willemsen4591b642021-05-24 14:24:12 -0700786 if err != nil {
787 ReportPathErrorf(r.ctx, "sbox manifest failed to marshal: %q", err)
788 }
Cole Faust2f3791f2024-11-25 15:49:57 -0800789 WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText))
Colin Crosse16ce362020-11-12 08:29:30 -0800790
791 // Generate a new string to use as the command line of the sbox rule. This uses
792 // a RuleBuilderCommand as a convenience method of building the command line, then
793 // converts it to a string to replace commandString.
Colin Crossf1a035e2020-11-16 17:32:30 -0800794 sboxCmd := &RuleBuilderCommand{
795 rule: &RuleBuilder{
796 ctx: r.ctx,
797 },
798 }
Colin Cross9b698b62021-12-22 09:55:32 -0800799 sboxCmd.builtToolWithoutDeps("sbox").
Colin Crosse52c2ac2022-03-28 17:03:35 -0700800 FlagWithArg("--sandbox-path ", shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
801 FlagWithArg("--output-dir ", r.outDir.String()).
802 FlagWithInput("--manifest ", r.sboxManifestPath)
803
804 if r.restat {
805 sboxCmd.Flag("--write-if-changed")
806 }
Colin Crosse16ce362020-11-12 08:29:30 -0800807
808 // Replace the command string, and add the sbox tool and manifest textproto to the
809 // dependencies of the final sbox rule.
Colin Crosscfec40c2019-07-08 17:07:18 -0700810 commandString = sboxCmd.buf.String()
Dan Willemsen633c5022019-04-12 11:11:38 -0700811 tools = append(tools, sboxCmd.tools...)
Colin Crosse16ce362020-11-12 08:29:30 -0800812 inputs = append(inputs, sboxCmd.inputs...)
Colin Crossef972742021-03-12 17:24:45 -0800813
814 if r.rbeParams != nil {
Colin Crosse55bd422021-03-23 13:44:30 -0700815 // RBE needs a list of input files to copy to the remote builder. For inputs already
816 // listed in an rsp file, pass the rsp file directly to rewrapper. For the rest,
817 // create a new rsp file to pass to rewrapper.
818 var remoteRspFiles Paths
819 var remoteInputs Paths
820
821 remoteInputs = append(remoteInputs, inputs...)
822 remoteInputs = append(remoteInputs, tools...)
823
Colin Crossce3a51d2021-03-19 16:22:12 -0700824 for _, rspFile := range rspFiles {
825 remoteInputs = append(remoteInputs, rspFile.file)
826 remoteRspFiles = append(remoteRspFiles, rspFile.file)
Colin Crossef972742021-03-12 17:24:45 -0800827 }
Colin Crosse55bd422021-03-23 13:44:30 -0700828
829 if len(remoteInputs) > 0 {
830 inputsListFile := r.sboxManifestPath.ReplaceExtension(r.ctx, "rbe_inputs.list")
831 writeRspFileRule(r.ctx, inputsListFile, remoteInputs)
832 remoteRspFiles = append(remoteRspFiles, inputsListFile)
833 // Add the new rsp file as an extra input to the rule.
834 inputs = append(inputs, inputsListFile)
835 }
Colin Crossef972742021-03-12 17:24:45 -0800836
837 r.rbeParams.OutputFiles = outputs.Strings()
Colin Crosse55bd422021-03-23 13:44:30 -0700838 r.rbeParams.RSPFiles = remoteRspFiles.Strings()
Colin Crossef972742021-03-12 17:24:45 -0800839 rewrapperCommand := r.rbeParams.NoVarTemplate(r.ctx.Config().RBEWrapper())
840 commandString = rewrapperCommand + " bash -c '" + strings.ReplaceAll(commandString, `'`, `'\''`) + "'"
841 }
Dan Willemsen633c5022019-04-12 11:11:38 -0700842 }
843
Colin Cross1d2cf042019-03-29 15:33:06 -0700844 // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
Colin Cross70c47412021-03-12 17:48:14 -0800845 // ImplicitOutputs. RuleBuilder doesn't use "$out", so the distinction between Outputs and
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700846 // ImplicitOutputs doesn't matter.
Dan Willemsen633c5022019-04-12 11:11:38 -0700847 output := outputs[0]
848 implicitOutputs := outputs[1:]
Colin Cross1d2cf042019-03-29 15:33:06 -0700849
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700850 var rspFile, rspFileContent string
Colin Crossce3a51d2021-03-19 16:22:12 -0700851 var rspFileInputs Paths
852 if len(rspFiles) > 0 {
853 // The first rsp files uses Ninja's rsp file support for the rule
854 rspFile = rspFiles[0].file.String()
Colin Crosse55bd422021-03-23 13:44:30 -0700855 // Use "$in" for rspFileContent to avoid duplicating the list of files in the dependency
856 // list and in the contents of the rsp file. Inputs to the rule that are not in the
857 // rsp file will be listed in Implicits instead of Inputs so they don't show up in "$in".
858 rspFileContent = "$in"
Colin Crossce3a51d2021-03-19 16:22:12 -0700859 rspFileInputs = append(rspFileInputs, rspFiles[0].paths...)
860
861 for _, rspFile := range rspFiles[1:] {
862 // Any additional rsp files need an extra rule to write the file.
863 writeRspFileRule(r.ctx, rspFile.file, rspFile.paths)
864 // The main rule needs to depend on the inputs listed in the extra rsp file.
865 inputs = append(inputs, rspFile.paths...)
866 // The main rule needs to depend on the extra rsp file.
867 inputs = append(inputs, rspFile.file)
868 }
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700869 }
870
Colin Cross8b8bec32019-11-15 13:18:43 -0800871 var pool blueprint.Pool
Colin Crossf1a035e2020-11-16 17:32:30 -0800872 if r.ctx.Config().UseGoma() && r.remoteable.Goma {
Colin Cross8b8bec32019-11-15 13:18:43 -0800873 // 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 -0800874 } else if r.ctx.Config().UseRBE() && r.remoteable.RBE {
Ramy Medhat944839a2020-03-31 22:14:52 -0400875 // When USE_RBE=true is set and the rule is supported by RBE, use the remotePool.
876 pool = remotePool
Colin Cross8b8bec32019-11-15 13:18:43 -0800877 } else if r.highmem {
878 pool = highmemPool
Colin Crossf1a035e2020-11-16 17:32:30 -0800879 } else if r.ctx.Config().UseRemoteBuild() {
Colin Cross8b8bec32019-11-15 13:18:43 -0800880 pool = localPool
881 }
882
Cole Faustd7556eb2024-12-02 13:18:58 -0800883 // If the command length is getting close to linux's maximum, dump it to a file, which allows
884 // for longer commands.
885 if len(commandString) > 100000 {
886 hasher := sha256.New()
887 hasher.Write([]byte(output.String()))
888 script := PathForOutput(r.ctx, "rule_builder_scripts", fmt.Sprintf("%x.sh", hasher.Sum(nil)))
889 commandString = "set -eu\n\n" + commandString + "\n"
890 WriteExecutableFileRuleVerbatim(r.ctx, script, commandString)
891 inputs = append(inputs, script)
892 commandString = script.String()
893 }
894
Cole Faust2f3791f2024-11-25 15:49:57 -0800895 commandString = proptools.NinjaEscape(commandString)
Sam Delmericod46f6c82023-09-25 12:13:17 +0000896
Devin Mooreb6cc64f2024-07-15 22:31:24 +0000897 args_vars := make([]string, len(r.args))
898 i := 0
899 for k, _ := range r.args {
900 args_vars[i] = k
901 i++
902 }
Colin Crossf1a035e2020-11-16 17:32:30 -0800903 r.ctx.Build(r.pctx, BuildParams{
Sam Delmerico285b66a2023-09-25 12:13:17 +0000904 Rule: r.ctx.Rule(r.pctx, name, blueprint.RuleParams{
Sam Delmericod46f6c82023-09-25 12:13:17 +0000905 Command: commandString,
Colin Cross45029782021-03-16 16:49:52 -0700906 CommandDeps: proptools.NinjaEscapeList(tools.Strings()),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700907 Restat: r.restat,
Colin Cross45029782021-03-16 16:49:52 -0700908 Rspfile: proptools.NinjaEscape(rspFile),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700909 RspfileContent: rspFileContent,
Colin Cross8b8bec32019-11-15 13:18:43 -0800910 Pool: pool,
Devin Mooreb6cc64f2024-07-15 22:31:24 +0000911 }, args_vars...),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700912 Inputs: rspFileInputs,
Colin Cross3d680512020-11-13 16:23:53 -0800913 Implicits: inputs,
Colin Crossda6401b2021-04-21 11:32:19 -0700914 OrderOnly: r.OrderOnlys(),
Colin Crossae89abe2021-04-21 11:45:23 -0700915 Validations: r.Validations(),
Dan Willemsen633c5022019-04-12 11:11:38 -0700916 Output: output,
917 ImplicitOutputs: implicitOutputs,
918 Depfile: depFile,
919 Deps: depFormat,
920 Description: desc,
Devin Mooreb6cc64f2024-07-15 22:31:24 +0000921 Args: r.args,
Dan Willemsen633c5022019-04-12 11:11:38 -0700922 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800923}
924
Colin Cross758290d2019-02-01 16:42:32 -0800925// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
926// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the
927// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
928// space as a separator from the previous method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800929type RuleBuilderCommand struct {
Colin Crossf1a035e2020-11-16 17:32:30 -0800930 rule *RuleBuilder
931
Inseob Kim93036a52024-10-25 17:02:21 +0900932 buf strings.Builder
933 inputs Paths
934 implicits Paths
935 orderOnlys Paths
936 validations Paths
937 outputs WritablePaths
938 depFiles WritablePaths
939 tools Paths
940 packagedTools []PackagingSpec
941 rspFiles []rspFileAndPaths
942 implicitDirectories DirectoryPaths
Colin Crossce3a51d2021-03-19 16:22:12 -0700943}
944
945type rspFileAndPaths struct {
946 file WritablePath
947 paths Paths
Dan Willemsen633c5022019-04-12 11:11:38 -0700948}
949
Paul Duffin3866b892021-10-04 11:24:48 +0100950func checkPathNotNil(path Path) {
951 if path == nil {
952 panic("rule_builder paths cannot be nil")
953 }
954}
955
Dan Willemsen633c5022019-04-12 11:11:38 -0700956func (c *RuleBuilderCommand) addInput(path Path) string {
Paul Duffin3866b892021-10-04 11:24:48 +0100957 checkPathNotNil(path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700958 c.inputs = append(c.inputs, path)
Colin Crossab020a72021-03-12 17:52:23 -0800959 return c.PathForInput(path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700960}
961
Colin Crossab020a72021-03-12 17:52:23 -0800962func (c *RuleBuilderCommand) addImplicit(path Path) {
Paul Duffin3866b892021-10-04 11:24:48 +0100963 checkPathNotNil(path)
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400964 c.implicits = append(c.implicits, path)
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400965}
966
Inseob Kim93036a52024-10-25 17:02:21 +0900967func (c *RuleBuilderCommand) addImplicitDirectory(path DirectoryPath) {
968 c.implicitDirectories = append(c.implicitDirectories, path)
969}
970
Colin Crossda71eda2020-02-21 16:55:19 -0800971func (c *RuleBuilderCommand) addOrderOnly(path Path) {
Paul Duffin3866b892021-10-04 11:24:48 +0100972 checkPathNotNil(path)
Colin Crossda71eda2020-02-21 16:55:19 -0800973 c.orderOnlys = append(c.orderOnlys, path)
974}
975
Colin Crossab020a72021-03-12 17:52:23 -0800976// PathForInput takes an input path and returns the appropriate path to use on the command line. If
977// sbox was enabled via a call to RuleBuilder.Sbox() and the path was an output path it returns a
978// path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
979// original path.
980func (c *RuleBuilderCommand) PathForInput(path Path) string {
981 if c.rule.sbox {
982 rel, inSandbox := c.rule._sboxPathForInputRel(path)
983 if inSandbox {
984 rel = filepath.Join(sboxSandboxBaseDir, rel)
985 }
986 return rel
Inseob Kimf7cd03e2024-09-06 17:25:00 +0900987 } else if c.rule.nsjail {
988 return c.rule.nsjailPathForInputRel(path)
Colin Crossab020a72021-03-12 17:52:23 -0800989 }
990 return path.String()
991}
992
993// PathsForInputs takes a list of input paths and returns the appropriate paths to use on the
994// command line. If sbox was enabled via a call to RuleBuilder.Sbox() a path was an output path, it
995// returns the path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it
996// returns the original paths.
997func (c *RuleBuilderCommand) PathsForInputs(paths Paths) []string {
998 ret := make([]string, len(paths))
999 for i, path := range paths {
1000 ret[i] = c.PathForInput(path)
1001 }
1002 return ret
1003}
1004
Colin Crossf1a035e2020-11-16 17:32:30 -08001005// PathForOutput takes an output path and returns the appropriate path to use on the command
1006// line. If sbox was enabled via a call to RuleBuilder.Sbox(), it returns a path with the
1007// placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
1008// original path.
1009func (c *RuleBuilderCommand) PathForOutput(path WritablePath) string {
1010 if c.rule.sbox {
1011 // Errors will be handled in RuleBuilder.Build where we have a context to report them
1012 rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
1013 return filepath.Join(sboxOutDir, rel)
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001014 } else if c.rule.nsjail {
1015 // Errors will be handled in RuleBuilder.Build where we have a context to report them
1016 rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
1017 return filepath.Join(nsjailOutDir, rel)
Dan Willemsen633c5022019-04-12 11:11:38 -07001018 }
1019 return path.String()
Colin Crossfeec25b2019-01-30 17:32:39 -08001020}
1021
Colin Crossba9e4032020-11-24 16:32:22 -08001022func sboxPathForToolRel(ctx BuilderContext, path Path) string {
1023 // Errors will be handled in RuleBuilder.Build where we have a context to report them
Cole Faust3b703f32023-10-16 13:30:51 -07001024 toolDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "")
Colin Cross790ef352021-10-25 19:15:55 -07001025 relOutSoong, isRelOutSoong, _ := maybeRelErr(toolDir.String(), path.String())
1026 if isRelOutSoong {
1027 // The tool is in the Soong output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
1028 return filepath.Join(sboxToolsSubDir, "out", relOutSoong)
Colin Crossba9e4032020-11-24 16:32:22 -08001029 }
1030 // The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
1031 return filepath.Join(sboxToolsSubDir, "src", path.String())
1032}
1033
Colin Crossab020a72021-03-12 17:52:23 -08001034func (r *RuleBuilder) _sboxPathForInputRel(path Path) (rel string, inSandbox bool) {
1035 // Errors will be handled in RuleBuilder.Build where we have a context to report them
1036 rel, isRelSboxOut, _ := maybeRelErr(r.outDir.String(), path.String())
1037 if isRelSboxOut {
1038 return filepath.Join(sboxOutSubDir, rel), true
1039 }
1040 if r.sboxInputs {
1041 // When sandboxing inputs all inputs have to be copied into the sandbox. Input files that
1042 // are outputs of other rules could be an arbitrary absolute path if OUT_DIR is set, so they
1043 // will be copied to relative paths under __SBOX_OUT_DIR__/out.
Cole Fauste8561c62023-11-30 17:26:37 -08001044 rel, isRelOut, _ := maybeRelErr(r.ctx.Config().OutDir(), path.String())
Colin Crossab020a72021-03-12 17:52:23 -08001045 if isRelOut {
1046 return filepath.Join(sboxOutSubDir, rel), true
1047 }
1048 }
1049 return path.String(), false
1050}
1051
1052func (r *RuleBuilder) sboxPathForInputRel(path Path) string {
1053 rel, _ := r._sboxPathForInputRel(path)
1054 return rel
1055}
1056
1057func (r *RuleBuilder) sboxPathsForInputsRel(paths Paths) []string {
1058 ret := make([]string, len(paths))
1059 for i, path := range paths {
1060 ret[i] = r.sboxPathForInputRel(path)
1061 }
1062 return ret
1063}
1064
Colin Crossba9e4032020-11-24 16:32:22 -08001065func sboxPathForPackagedToolRel(spec PackagingSpec) string {
1066 return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage)
1067}
1068
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001069func nsjailPathForToolRel(ctx BuilderContext, path Path) string {
1070 // Errors will be handled in RuleBuilder.Build where we have a context to report them
1071 toolDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "")
1072 relOutSoong, isRelOutSoong, _ := maybeRelErr(toolDir.String(), path.String())
1073 if isRelOutSoong {
1074 // The tool is in the Soong output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
1075 return filepath.Join(nsjailToolsSubDir, "out", relOutSoong)
1076 }
1077 // The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
1078 return filepath.Join(nsjailToolsSubDir, "src", path.String())
1079}
1080
1081func (r *RuleBuilder) nsjailPathForInputRel(path Path) string {
1082 rel, isRelSboxOut, _ := maybeRelErr(r.outDir.String(), path.String())
1083 if isRelSboxOut {
1084 return filepath.Join(nsjailOutDir, rel)
1085 }
1086 return path.String()
1087}
1088
1089func (r *RuleBuilder) nsjailPathsForInputsRel(paths Paths) []string {
1090 ret := make([]string, len(paths))
1091 for i, path := range paths {
1092 ret[i] = r.nsjailPathForInputRel(path)
1093 }
1094 return ret
1095}
1096
1097func nsjailPathForPackagedToolRel(spec PackagingSpec) string {
1098 return filepath.Join(nsjailToolsSubDir, "out", spec.relPathInPackage)
1099}
1100
Colin Crossd11cf622021-03-23 22:30:35 -07001101// PathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
1102// tool after copying it into the sandbox. This can be used on the RuleBuilder command line to
1103// reference the tool.
1104func (c *RuleBuilderCommand) PathForPackagedTool(spec PackagingSpec) string {
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001105 if c.rule.sboxTools {
1106 return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
1107 } else if c.rule.nsjail {
1108 return nsjailPathForPackagedToolRel(spec)
1109 } else {
1110 panic("PathForPackagedTool() requires SandboxTools() or Nsjail()")
Colin Crossd11cf622021-03-23 22:30:35 -07001111 }
Colin Crossd11cf622021-03-23 22:30:35 -07001112}
1113
Colin Crossba9e4032020-11-24 16:32:22 -08001114// PathForTool takes a path to a tool, which may be an output file or a source file, and returns
1115// the corresponding path for the tool in the sbox sandbox if sbox is enabled, or the original path
1116// if it is not. This can be used on the RuleBuilder command line to reference the tool.
1117func (c *RuleBuilderCommand) PathForTool(path Path) string {
1118 if c.rule.sbox && c.rule.sboxTools {
1119 return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path))
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001120 } else if c.rule.nsjail {
1121 return nsjailPathForToolRel(c.rule.ctx, path)
Colin Crossba9e4032020-11-24 16:32:22 -08001122 }
1123 return path.String()
1124}
1125
Colin Crossd11cf622021-03-23 22:30:35 -07001126// PathsForTools takes a list of paths to tools, which may be output files or source files, and
1127// returns the corresponding paths for the tools in the sbox sandbox if sbox is enabled, or the
1128// original paths if it is not. This can be used on the RuleBuilder command line to reference the tool.
1129func (c *RuleBuilderCommand) PathsForTools(paths Paths) []string {
1130 if c.rule.sbox && c.rule.sboxTools {
1131 var ret []string
1132 for _, path := range paths {
1133 ret = append(ret, filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path)))
1134 }
1135 return ret
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001136 } else if c.rule.nsjail {
1137 var ret []string
1138 for _, path := range paths {
1139 ret = append(ret, nsjailPathForToolRel(c.rule.ctx, path))
1140 }
1141 return ret
Colin Crossd11cf622021-03-23 22:30:35 -07001142 }
1143 return paths.Strings()
1144}
1145
Colin Crossba9e4032020-11-24 16:32:22 -08001146// PackagedTool adds the specified tool path to the command line. It can only be used with tool
1147// sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox.
1148func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand {
Colin Crossba9e4032020-11-24 16:32:22 -08001149 c.packagedTools = append(c.packagedTools, spec)
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001150 if c.rule.sboxTools {
1151 c.Text(sboxPathForPackagedToolRel(spec))
1152 } else if c.rule.nsjail {
1153 c.Text(nsjailPathForPackagedToolRel(spec))
1154 } else {
1155 panic("PackagedTool() requires SandboxTools() or Nsjail()")
1156 }
Colin Crossba9e4032020-11-24 16:32:22 -08001157 return c
1158}
1159
1160// ImplicitPackagedTool copies the specified tool into the sandbox without modifying the command
1161// line. It can only be used with tool sandboxing enabled by SandboxTools().
1162func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuilderCommand {
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001163 if !c.rule.sboxTools && !c.rule.nsjail {
1164 panic("ImplicitPackagedTool() requires SandboxTools() or Nsjail()")
Colin Crossba9e4032020-11-24 16:32:22 -08001165 }
1166
1167 c.packagedTools = append(c.packagedTools, spec)
1168 return c
1169}
1170
1171// ImplicitPackagedTools copies the specified tools into the sandbox without modifying the command
1172// line. It can only be used with tool sandboxing enabled by SandboxTools().
1173func (c *RuleBuilderCommand) ImplicitPackagedTools(specs []PackagingSpec) *RuleBuilderCommand {
Inseob Kimf7cd03e2024-09-06 17:25:00 +09001174 if !c.rule.sboxTools && !c.rule.nsjail {
1175 panic("ImplicitPackagedTools() requires SandboxTools() or Nsjail()")
Colin Crossba9e4032020-11-24 16:32:22 -08001176 }
1177
1178 c.packagedTools = append(c.packagedTools, specs...)
1179 return c
1180}
1181
Colin Cross758290d2019-02-01 16:42:32 -08001182// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
1183// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001184func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
Colin Crosscfec40c2019-07-08 17:07:18 -07001185 if c.buf.Len() > 0 {
1186 c.buf.WriteByte(' ')
Colin Crossfeec25b2019-01-30 17:32:39 -08001187 }
Colin Crosscfec40c2019-07-08 17:07:18 -07001188 c.buf.WriteString(text)
Colin Crossfeec25b2019-01-30 17:32:39 -08001189 return c
1190}
1191
Colin Cross758290d2019-02-01 16:42:32 -08001192// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or
1193// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001194func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
Cole Faust2006e892025-03-05 10:26:04 -08001195 if c.buf.Len() > 0 {
1196 c.buf.WriteByte(' ')
1197 }
1198 fmt.Fprintf(&c.buf, format, a...)
1199 return c
Colin Crossfeec25b2019-01-30 17:32:39 -08001200}
1201
Colin Cross758290d2019-02-01 16:42:32 -08001202// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the
1203// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001204func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
1205 return c.Text(flag)
1206}
1207
Colin Crossab054432019-07-15 16:13:59 -07001208// OptionalFlag adds the specified raw text to the command line if it is not nil. The text should not contain input or
1209// output paths or the rule will not have them listed in its dependencies or outputs.
1210func (c *RuleBuilderCommand) OptionalFlag(flag *string) *RuleBuilderCommand {
1211 if flag != nil {
1212 c.Text(*flag)
1213 }
1214
1215 return c
1216}
1217
Colin Cross92b7d582019-03-29 15:32:51 -07001218// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the
1219// rule will not have them listed in its dependencies or outputs.
1220func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
1221 for _, flag := range flags {
1222 c.Text(flag)
1223 }
1224 return c
1225}
1226
Colin Cross758290d2019-02-01 16:42:32 -08001227// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
1228// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
1229// outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001230func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
1231 return c.Text(flag + arg)
1232}
1233
Colin Crossc7ed0042019-02-11 14:11:09 -08001234// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to
1235// calling FlagWithArg for argument.
1236func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand {
1237 for _, arg := range args {
1238 c.FlagWithArg(flag, arg)
1239 }
1240 return c
1241}
1242
Roland Levillain2da5d9a2019-02-27 16:56:41 +00001243// 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 -08001244// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or
1245// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001246func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
1247 return c.Text(flag + strings.Join(list, sep))
1248}
1249
Colin Cross758290d2019-02-01 16:42:32 -08001250// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by
1251// RuleBuilder.Tools.
Colin Cross69f59a32019-02-15 10:39:37 -08001252func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001253 checkPathNotNil(path)
Colin Crossfeec25b2019-01-30 17:32:39 -08001254 c.tools = append(c.tools, path)
Colin Crossba9e4032020-11-24 16:32:22 -08001255 return c.Text(c.PathForTool(path))
1256}
1257
1258// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
1259func (c *RuleBuilderCommand) ImplicitTool(path Path) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001260 checkPathNotNil(path)
Colin Crossba9e4032020-11-24 16:32:22 -08001261 c.tools = append(c.tools, path)
1262 return c
1263}
1264
1265// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
1266func (c *RuleBuilderCommand) ImplicitTools(paths Paths) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001267 for _, path := range paths {
1268 c.ImplicitTool(path)
1269 }
Colin Crossba9e4032020-11-24 16:32:22 -08001270 return c
Colin Crossfeec25b2019-01-30 17:32:39 -08001271}
1272
Colin Crossee94d6a2019-07-08 17:08:34 -07001273// BuiltTool adds the specified tool path that was built using a host Soong module to the command line. The path will
1274// be also added to the dependencies returned by RuleBuilder.Tools.
1275//
1276// It is equivalent to:
Colin Crossd079e0b2022-08-16 10:27:33 -07001277//
1278// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
Colin Crossf1a035e2020-11-16 17:32:30 -08001279func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
Colin Cross9b698b62021-12-22 09:55:32 -08001280 if c.rule.ctx.Config().UseHostMusl() {
1281 // If the host is using musl, assume that the tool was built against musl libc and include
1282 // libc_musl.so in the sandbox.
1283 // TODO(ccross): if we supported adding new dependencies during GenerateAndroidBuildActions
1284 // this could be a dependency + TransitivePackagingSpecs.
1285 c.ImplicitTool(c.rule.ctx.Config().HostJNIToolPath(c.rule.ctx, "libc_musl"))
1286 }
1287 return c.builtToolWithoutDeps(tool)
1288}
1289
1290// builtToolWithoutDeps is similar to BuiltTool, but doesn't add any dependencies. It is used
1291// internally by RuleBuilder for helper tools that are known to be compiled statically.
1292func (c *RuleBuilderCommand) builtToolWithoutDeps(tool string) *RuleBuilderCommand {
Colin Crossf1a035e2020-11-16 17:32:30 -08001293 return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
Colin Crossee94d6a2019-07-08 17:08:34 -07001294}
1295
1296// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the
1297// dependencies returned by RuleBuilder.Tools.
1298//
1299// It is equivalent to:
Colin Crossd079e0b2022-08-16 10:27:33 -07001300//
1301// cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
Colin Crossee94d6a2019-07-08 17:08:34 -07001302func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
1303 return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
1304}
1305
Colin Cross758290d2019-02-01 16:42:32 -08001306// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
1307// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001308func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001309 return c.Text(c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001310}
1311
Colin Cross758290d2019-02-01 16:42:32 -08001312// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
1313// dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001314func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001315 for _, path := range paths {
1316 c.Input(path)
1317 }
1318 return c
1319}
1320
1321// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
1322// command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001323func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001324 c.addImplicit(path)
Colin Crossfeec25b2019-01-30 17:32:39 -08001325 return c
1326}
1327
Colin Cross758290d2019-02-01 16:42:32 -08001328// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
1329// command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001330func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001331 for _, path := range paths {
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001332 c.addImplicit(path)
Dan Willemsen633c5022019-04-12 11:11:38 -07001333 }
Colin Crossfeec25b2019-01-30 17:32:39 -08001334 return c
1335}
1336
Inseob Kim93036a52024-10-25 17:02:21 +09001337// ImplicitDirectory adds the specified input directory to the dependencies without modifying the
1338// command line. Added directories will be bind-mounted for the nsjail.
1339func (c *RuleBuilderCommand) ImplicitDirectory(path DirectoryPath) *RuleBuilderCommand {
1340 if !c.rule.nsjail {
1341 panic("ImplicitDirectory() must be called after Nsjail()")
1342 }
1343 c.addImplicitDirectory(path)
1344 return c
1345}
1346
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001347// GetImplicits returns the command's implicit inputs.
1348func (c *RuleBuilderCommand) GetImplicits() Paths {
1349 return c.implicits
1350}
1351
Colin Crossda71eda2020-02-21 16:55:19 -08001352// OrderOnly adds the specified input path to the dependencies returned by RuleBuilder.OrderOnlys
1353// without modifying the command line.
1354func (c *RuleBuilderCommand) OrderOnly(path Path) *RuleBuilderCommand {
1355 c.addOrderOnly(path)
1356 return c
1357}
1358
1359// OrderOnlys adds the specified input paths to the dependencies returned by RuleBuilder.OrderOnlys
1360// without modifying the command line.
1361func (c *RuleBuilderCommand) OrderOnlys(paths Paths) *RuleBuilderCommand {
1362 for _, path := range paths {
1363 c.addOrderOnly(path)
1364 }
1365 return c
1366}
1367
Colin Crossae89abe2021-04-21 11:45:23 -07001368// Validation adds the specified input path to the validation dependencies by
1369// RuleBuilder.Validations without modifying the command line.
1370func (c *RuleBuilderCommand) Validation(path Path) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001371 checkPathNotNil(path)
Colin Crossae89abe2021-04-21 11:45:23 -07001372 c.validations = append(c.validations, path)
1373 return c
1374}
1375
1376// Validations adds the specified input paths to the validation dependencies by
1377// RuleBuilder.Validations without modifying the command line.
1378func (c *RuleBuilderCommand) Validations(paths Paths) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001379 for _, path := range paths {
1380 c.Validation(path)
1381 }
Colin Crossae89abe2021-04-21 11:45:23 -07001382 return c
1383}
1384
Colin Cross758290d2019-02-01 16:42:32 -08001385// Output adds the specified output path to the command line. The path will also be added to the outputs returned by
1386// RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001387func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001388 checkPathNotNil(path)
Colin Crossfeec25b2019-01-30 17:32:39 -08001389 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001390 return c.Text(c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001391}
1392
Colin Cross758290d2019-02-01 16:42:32 -08001393// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
1394// the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001395func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001396 for _, path := range paths {
1397 c.Output(path)
1398 }
1399 return c
1400}
1401
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001402// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
1403// and will be the temporary output directory managed by sbox, not the final one.
Anas Sulaimanb4dff132024-02-07 21:58:46 +00001404func (c *RuleBuilderCommand) OutputDir(subPathComponents ...string) *RuleBuilderCommand {
Colin Crossf1a035e2020-11-16 17:32:30 -08001405 if !c.rule.sbox {
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001406 panic("OutputDir only valid with Sbox")
1407 }
Anas Sulaimanb4dff132024-02-07 21:58:46 +00001408 path := sboxOutDir
1409 if len(subPathComponents) > 0 {
1410 path = filepath.Join(append([]string{sboxOutDir}, subPathComponents...)...)
1411 }
1412 return c.Text(path)
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001413}
1414
Colin Cross1d2cf042019-03-29 15:33:06 -07001415// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
1416// line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles are added to
1417// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
1418func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001419 checkPathNotNil(path)
Colin Cross1d2cf042019-03-29 15:33:06 -07001420 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001421 return c.Text(c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -07001422}
1423
Colin Cross758290d2019-02-01 16:42:32 -08001424// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
1425// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001426func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001427 c.outputs = append(c.outputs, path)
1428 return c
1429}
1430
Colin Cross758290d2019-02-01 16:42:32 -08001431// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
1432// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001433func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001434 c.outputs = append(c.outputs, paths...)
1435 return c
1436}
1437
Colin Cross1d2cf042019-03-29 15:33:06 -07001438// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
1439// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles
1440// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
1441// depfiles together.
1442func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand {
1443 c.depFiles = append(c.depFiles, path)
1444 return c
1445}
1446
Colin Cross758290d2019-02-01 16:42:32 -08001447// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
1448// will also be added to the dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001449func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001450 return c.Text(flag + c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001451}
1452
Colin Cross758290d2019-02-01 16:42:32 -08001453// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
1454// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
1455// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001456func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001457 strs := make([]string, len(paths))
1458 for i, path := range paths {
1459 strs[i] = c.addInput(path)
1460 }
1461 return c.FlagWithList(flag, strs, sep)
Colin Crossfeec25b2019-01-30 17:32:39 -08001462}
1463
Colin Cross758290d2019-02-01 16:42:32 -08001464// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
1465// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for
1466// each input path.
Colin Cross69f59a32019-02-15 10:39:37 -08001467func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001468 for _, path := range paths {
1469 c.FlagWithInput(flag, path)
1470 }
1471 return c
1472}
1473
1474// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path
1475// will also be added to the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001476func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001477 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001478 return c.Text(flag + c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001479}
1480
Colin Cross1d2cf042019-03-29 15:33:06 -07001481// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path
1482// will also be added to the outputs returned by RuleBuilder.Outputs.
1483func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
1484 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001485 return c.Text(flag + c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -07001486}
1487
Colin Crossce3a51d2021-03-19 16:22:12 -07001488// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with
1489// no separator between them. The paths will be written to the rspfile. If sbox is enabled, the
1490// rspfile must be outside the sbox directory. The first use of FlagWithRspFileInputList in any
1491// RuleBuilderCommand of a RuleBuilder will use Ninja's rsp file support for the rule, additional
1492// uses will result in an auxiliary rules to write the rspFile contents.
Colin Cross70c47412021-03-12 17:48:14 -08001493func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, rspFile WritablePath, paths Paths) *RuleBuilderCommand {
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001494 // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
1495 // generated.
1496 if paths == nil {
1497 paths = Paths{}
1498 }
1499
Colin Crossce3a51d2021-03-19 16:22:12 -07001500 c.rspFiles = append(c.rspFiles, rspFileAndPaths{rspFile, paths})
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001501
Colin Cross70c47412021-03-12 17:48:14 -08001502 if c.rule.sbox {
1503 if _, isRel, _ := maybeRelErr(c.rule.outDir.String(), rspFile.String()); isRel {
1504 panic(fmt.Errorf("FlagWithRspFileInputList rspfile %q must not be inside out dir %q",
1505 rspFile.String(), c.rule.outDir.String()))
1506 }
1507 }
1508
Colin Crossab020a72021-03-12 17:52:23 -08001509 c.FlagWithArg(flag, c.PathForInput(rspFile))
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001510 return c
1511}
1512
Colin Cross758290d2019-02-01 16:42:32 -08001513// String returns the command line.
1514func (c *RuleBuilderCommand) String() string {
Colin Crosscfec40c2019-07-08 17:07:18 -07001515 return c.buf.String()
Colin Cross758290d2019-02-01 16:42:32 -08001516}
Colin Cross1d2cf042019-03-29 15:33:06 -07001517
Colin Crosse16ce362020-11-12 08:29:30 -08001518// RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox()
1519// and returns sbox testproto generated by the RuleBuilder.
Colin Crossf61d03d2023-11-02 16:56:39 -07001520func RuleBuilderSboxProtoForTests(t *testing.T, ctx *TestContext, params TestingBuildParams) *sbox_proto.Manifest {
Colin Crosse16ce362020-11-12 08:29:30 -08001521 t.Helper()
Colin Crossf61d03d2023-11-02 16:56:39 -07001522 content := ContentFromFileRuleForTests(t, ctx, params)
Colin Crosse16ce362020-11-12 08:29:30 -08001523 manifest := sbox_proto.Manifest{}
Dan Willemsen4591b642021-05-24 14:24:12 -07001524 err := prototext.Unmarshal([]byte(content), &manifest)
Colin Crosse16ce362020-11-12 08:29:30 -08001525 if err != nil {
1526 t.Fatalf("failed to unmarshal manifest: %s", err.Error())
1527 }
1528 return &manifest
1529}
1530
Colin Cross1d2cf042019-03-29 15:33:06 -07001531func ninjaNameEscape(s string) string {
1532 b := []byte(s)
1533 escaped := false
1534 for i, c := range b {
1535 valid := (c >= 'a' && c <= 'z') ||
1536 (c >= 'A' && c <= 'Z') ||
1537 (c >= '0' && c <= '9') ||
1538 (c == '_') ||
1539 (c == '-') ||
1540 (c == '.')
1541 if !valid {
1542 b[i] = '_'
1543 escaped = true
1544 }
1545 }
1546 if escaped {
1547 s = string(b)
1548 }
1549 return s
1550}
Colin Cross3d680512020-11-13 16:23:53 -08001551
1552// hashSrcFiles returns a hash of the list of source files. It is used to ensure the command line
1553// or the sbox textproto manifest change even if the input files are not listed on the command line.
1554func hashSrcFiles(srcFiles Paths) string {
1555 h := sha256.New()
1556 srcFileList := strings.Join(srcFiles.Strings(), "\n")
1557 h.Write([]byte(srcFileList))
1558 return fmt.Sprintf("%x", h.Sum(nil))
1559}
Colin Crossf1a035e2020-11-16 17:32:30 -08001560
1561// BuilderContextForTesting returns a BuilderContext for the given config that can be used for tests
1562// that need to call methods that take a BuilderContext.
1563func BuilderContextForTesting(config Config) BuilderContext {
1564 pathCtx := PathContextForTesting(config)
1565 return builderContextForTests{
1566 PathContext: pathCtx,
1567 }
1568}
1569
1570type builderContextForTests struct {
1571 PathContext
1572}
1573
1574func (builderContextForTests) Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule {
1575 return nil
1576}
1577func (builderContextForTests) Build(PackageContext, BuildParams) {}
Colin Crossef972742021-03-12 17:24:45 -08001578
Colin Crosse55bd422021-03-23 13:44:30 -07001579func writeRspFileRule(ctx BuilderContext, rspFile WritablePath, paths Paths) {
1580 buf := &strings.Builder{}
1581 err := response.WriteRspFile(buf, paths.Strings())
1582 if err != nil {
1583 // There should never be I/O errors writing to a bytes.Buffer.
1584 panic(err)
Colin Crossef972742021-03-12 17:24:45 -08001585 }
Colin Crosse55bd422021-03-23 13:44:30 -07001586 WriteFileRule(ctx, rspFile, buf.String())
Colin Crossef972742021-03-12 17:24:45 -08001587}