blob: 18bbcab5c02ae87634276fe985a138bdb0d074be [file] [log] [blame]
Colin Crossfeec25b2019-01-30 17:32:39 -08001// Copyright 2018 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package android
16
17import (
Colin Cross3d680512020-11-13 16:23:53 -080018 "crypto/sha256"
Colin Crossfeec25b2019-01-30 17:32:39 -080019 "fmt"
Colin Cross3d680512020-11-13 16:23:53 -080020 "path/filepath"
Colin Crossfeec25b2019-01-30 17:32:39 -080021 "sort"
22 "strings"
Colin Crosse16ce362020-11-12 08:29:30 -080023 "testing"
Colin Crossfeec25b2019-01-30 17:32:39 -080024
25 "github.com/google/blueprint"
26 "github.com/google/blueprint/proptools"
Dan Willemsen4591b642021-05-24 14:24:12 -070027 "google.golang.org/protobuf/encoding/prototext"
28 "google.golang.org/protobuf/proto"
Dan Willemsen633c5022019-04-12 11:11:38 -070029
Colin Crosse16ce362020-11-12 08:29:30 -080030 "android/soong/cmd/sbox/sbox_proto"
Colin Crossef972742021-03-12 17:24:45 -080031 "android/soong/remoteexec"
Colin Crosse55bd422021-03-23 13:44:30 -070032 "android/soong/response"
Dan Willemsen633c5022019-04-12 11:11:38 -070033 "android/soong/shared"
Colin Crossfeec25b2019-01-30 17:32:39 -080034)
35
Colin Crosse16ce362020-11-12 08:29:30 -080036const sboxSandboxBaseDir = "__SBOX_SANDBOX_DIR__"
37const sboxOutSubDir = "out"
Colin Crossba9e4032020-11-24 16:32:22 -080038const sboxToolsSubDir = "tools"
Colin Crosse16ce362020-11-12 08:29:30 -080039const sboxOutDir = sboxSandboxBaseDir + "/" + sboxOutSubDir
Colin Cross3d680512020-11-13 16:23:53 -080040
Colin Cross758290d2019-02-01 16:42:32 -080041// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
42// graph.
Colin Crossfeec25b2019-01-30 17:32:39 -080043type RuleBuilder struct {
Colin Crossf1a035e2020-11-16 17:32:30 -080044 pctx PackageContext
45 ctx BuilderContext
46
Colin Crosse16ce362020-11-12 08:29:30 -080047 commands []*RuleBuilderCommand
48 installs RuleBuilderInstalls
49 temporariesSet map[WritablePath]bool
50 restat bool
51 sbox bool
52 highmem bool
53 remoteable RemoteRuleSupports
Colin Crossef972742021-03-12 17:24:45 -080054 rbeParams *remoteexec.REParams
Colin Crossf1a035e2020-11-16 17:32:30 -080055 outDir WritablePath
Spandan Dasaf4ccaa2023-06-29 01:15:51 +000056 sboxOutSubDir string
Colin Crossba9e4032020-11-24 16:32:22 -080057 sboxTools bool
Colin Crossab020a72021-03-12 17:52:23 -080058 sboxInputs bool
Colin Crosse16ce362020-11-12 08:29:30 -080059 sboxManifestPath WritablePath
60 missingDeps []string
Devin Mooreb6cc64f2024-07-15 22:31:24 +000061 args map[string]string
Colin Crossfeec25b2019-01-30 17:32:39 -080062}
63
Colin Cross758290d2019-02-01 16:42:32 -080064// NewRuleBuilder returns a newly created RuleBuilder.
Colin Crossf1a035e2020-11-16 17:32:30 -080065func NewRuleBuilder(pctx PackageContext, ctx BuilderContext) *RuleBuilder {
Colin Cross5cb5b092019-02-02 21:25:18 -080066 return &RuleBuilder{
Colin Crossf1a035e2020-11-16 17:32:30 -080067 pctx: pctx,
68 ctx: ctx,
Colin Cross69f59a32019-02-15 10:39:37 -080069 temporariesSet: make(map[WritablePath]bool),
Spandan Dasaf4ccaa2023-06-29 01:15:51 +000070 sboxOutSubDir: sboxOutSubDir,
Colin Cross5cb5b092019-02-02 21:25:18 -080071 }
Colin Cross758290d2019-02-01 16:42:32 -080072}
73
Spandan Dasaf4ccaa2023-06-29 01:15:51 +000074// SetSboxOutDirDirAsEmpty sets the out subdirectory to an empty string
75// This is useful for sandboxing actions that change the execution root to a path in out/ (e.g mixed builds)
76// For such actions, SetSboxOutDirDirAsEmpty ensures that the path does not become $SBOX_SANDBOX_DIR/out/out/bazel/output/execroot/__main__/...
77func (rb *RuleBuilder) SetSboxOutDirDirAsEmpty() *RuleBuilder {
78 rb.sboxOutSubDir = ""
79 return rb
80}
81
Devin Mooreb6cc64f2024-07-15 22:31:24 +000082// Set the phony_output argument.
83// This causes the output files to be ignored.
84// If the output isn't created, it's not treated as an error.
85// The build rule is run every time whether or not the output is created.
86func (rb *RuleBuilder) SetPhonyOutput() {
87 if rb.args == nil {
88 rb.args = make(map[string]string)
89 }
90 rb.args["phony_output"] = "true"
91}
92
Colin Cross758290d2019-02-01 16:42:32 -080093// RuleBuilderInstall is a tuple of install from and to locations.
94type RuleBuilderInstall struct {
Colin Cross69f59a32019-02-15 10:39:37 -080095 From Path
96 To string
Colin Cross758290d2019-02-01 16:42:32 -080097}
98
Colin Crossdeabb942019-02-11 14:11:09 -080099type RuleBuilderInstalls []RuleBuilderInstall
100
101// String returns the RuleBuilderInstalls in the form used by $(call copy-many-files) in Make, a space separated
102// list of from:to tuples.
103func (installs RuleBuilderInstalls) String() string {
104 sb := strings.Builder{}
105 for i, install := range installs {
106 if i != 0 {
107 sb.WriteRune(' ')
108 }
Colin Cross69f59a32019-02-15 10:39:37 -0800109 sb.WriteString(install.From.String())
Colin Crossdeabb942019-02-11 14:11:09 -0800110 sb.WriteRune(':')
111 sb.WriteString(install.To)
112 }
113 return sb.String()
114}
115
Colin Cross0d2f40a2019-02-05 22:31:15 -0800116// MissingDeps adds modules to the list of missing dependencies. If MissingDeps
117// is called with a non-empty input, any call to Build will result in a rule
118// that will print an error listing the missing dependencies and fail.
119// MissingDeps should only be called if Config.AllowMissingDependencies() is
120// true.
121func (r *RuleBuilder) MissingDeps(missingDeps []string) {
122 r.missingDeps = append(r.missingDeps, missingDeps...)
123}
124
Colin Cross758290d2019-02-01 16:42:32 -0800125// 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 -0800126func (r *RuleBuilder) Restat() *RuleBuilder {
127 r.restat = true
128 return r
129}
130
Colin Cross8b8bec32019-11-15 13:18:43 -0800131// HighMem marks the rule as a high memory rule, which will limit how many run in parallel with other high memory
132// rules.
133func (r *RuleBuilder) HighMem() *RuleBuilder {
134 r.highmem = true
135 return r
136}
137
138// Remoteable marks the rule as supporting remote execution.
139func (r *RuleBuilder) Remoteable(supports RemoteRuleSupports) *RuleBuilder {
140 r.remoteable = supports
141 return r
142}
143
Colin Crossef972742021-03-12 17:24:45 -0800144// Rewrapper marks the rule as running inside rewrapper using the given params in order to support
145// running on RBE. During RuleBuilder.Build the params will be combined with the inputs, outputs
146// and tools known to RuleBuilder to prepend an appropriate rewrapper command line to the rule's
147// command line.
148func (r *RuleBuilder) Rewrapper(params *remoteexec.REParams) *RuleBuilder {
149 if !r.sboxInputs {
150 panic(fmt.Errorf("RuleBuilder.Rewrapper must be called after RuleBuilder.SandboxInputs"))
151 }
152 r.rbeParams = params
153 return r
154}
155
Colin Crosse16ce362020-11-12 08:29:30 -0800156// Sbox marks the rule as needing to be wrapped by sbox. The outputDir should point to the output
157// directory that sbox will wipe. It should not be written to by any other rule. manifestPath should
158// point to a location where sbox's manifest will be written and must be outside outputDir. sbox
159// will ensure that all outputs have been written, and will discard any output files that were not
160// specified.
Colin Crosse16ce362020-11-12 08:29:30 -0800161func (r *RuleBuilder) Sbox(outputDir WritablePath, manifestPath WritablePath) *RuleBuilder {
Dan Willemsen633c5022019-04-12 11:11:38 -0700162 if r.sbox {
163 panic("Sbox() may not be called more than once")
164 }
165 if len(r.commands) > 0 {
166 panic("Sbox() may not be called after Command()")
167 }
Dan Willemsen633c5022019-04-12 11:11:38 -0700168 r.sbox = true
Colin Crossf1a035e2020-11-16 17:32:30 -0800169 r.outDir = outputDir
Colin Crosse16ce362020-11-12 08:29:30 -0800170 r.sboxManifestPath = manifestPath
Dan Willemsen633c5022019-04-12 11:11:38 -0700171 return r
172}
173
Colin Crossba9e4032020-11-24 16:32:22 -0800174// SandboxTools enables tool sandboxing for the rule by copying any referenced tools into the
175// sandbox.
176func (r *RuleBuilder) SandboxTools() *RuleBuilder {
177 if !r.sbox {
178 panic("SandboxTools() must be called after Sbox()")
179 }
180 if len(r.commands) > 0 {
181 panic("SandboxTools() may not be called after Command()")
182 }
183 r.sboxTools = true
184 return r
185}
186
Colin Crossab020a72021-03-12 17:52:23 -0800187// SandboxInputs enables input sandboxing for the rule by copying any referenced inputs into the
188// sandbox. It also implies SandboxTools().
189//
190// Sandboxing inputs requires RuleBuilder to be aware of all references to input paths. Paths
191// that are passed to RuleBuilder outside of the methods that expect inputs, for example
192// FlagWithArg, must use RuleBuilderCommand.PathForInput to translate the path to one that matches
193// the sandbox layout.
194func (r *RuleBuilder) SandboxInputs() *RuleBuilder {
195 if !r.sbox {
196 panic("SandboxInputs() must be called after Sbox()")
197 }
198 if len(r.commands) > 0 {
199 panic("SandboxInputs() may not be called after Command()")
200 }
201 r.sboxTools = true
202 r.sboxInputs = true
203 return r
204}
205
Colin Cross758290d2019-02-01 16:42:32 -0800206// Install associates an output of the rule with an install location, which can be retrieved later using
207// RuleBuilder.Installs.
Colin Cross69f59a32019-02-15 10:39:37 -0800208func (r *RuleBuilder) Install(from Path, to string) {
Colin Crossfeec25b2019-01-30 17:32:39 -0800209 r.installs = append(r.installs, RuleBuilderInstall{from, to})
210}
211
Colin Cross758290d2019-02-01 16:42:32 -0800212// Command returns a new RuleBuilderCommand for the rule. The commands will be ordered in the rule by when they were
213// created by this method. That can be mutated through their methods in any order, as long as the mutations do not
214// race with any call to Build.
Colin Crossfeec25b2019-01-30 17:32:39 -0800215func (r *RuleBuilder) Command() *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700216 command := &RuleBuilderCommand{
Colin Crossf1a035e2020-11-16 17:32:30 -0800217 rule: r,
Dan Willemsen633c5022019-04-12 11:11:38 -0700218 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800219 r.commands = append(r.commands, command)
220 return command
221}
222
Colin Cross5cb5b092019-02-02 21:25:18 -0800223// Temporary marks an output of a command as an intermediate file that will be used as an input to another command
224// in the same rule, and should not be listed in Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800225func (r *RuleBuilder) Temporary(path WritablePath) {
Colin Cross5cb5b092019-02-02 21:25:18 -0800226 r.temporariesSet[path] = true
227}
228
229// DeleteTemporaryFiles adds a command to the rule that deletes any outputs that have been marked using Temporary
230// when the rule runs. DeleteTemporaryFiles should be called after all calls to Temporary.
231func (r *RuleBuilder) DeleteTemporaryFiles() {
Colin Cross69f59a32019-02-15 10:39:37 -0800232 var temporariesList WritablePaths
Colin Cross5cb5b092019-02-02 21:25:18 -0800233
234 for intermediate := range r.temporariesSet {
235 temporariesList = append(temporariesList, intermediate)
236 }
Colin Cross69f59a32019-02-15 10:39:37 -0800237
238 sort.Slice(temporariesList, func(i, j int) bool {
239 return temporariesList[i].String() < temporariesList[j].String()
240 })
Colin Cross5cb5b092019-02-02 21:25:18 -0800241
242 r.Command().Text("rm").Flag("-f").Outputs(temporariesList)
243}
244
Colin Crossda71eda2020-02-21 16:55:19 -0800245// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
Colin Cross3d680512020-11-13 16:23:53 -0800246// input paths, such as RuleBuilderCommand.Input, RuleBuilderCommand.Implicit, or
Colin Crossda71eda2020-02-21 16:55:19 -0800247// RuleBuilderCommand.FlagWithInput. Inputs to a command that are also outputs of another command
248// in the same RuleBuilder are filtered out. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800249func (r *RuleBuilder) Inputs() Paths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800250 outputs := r.outputSet()
Dan Willemsen633c5022019-04-12 11:11:38 -0700251 depFiles := r.depFileSet()
Colin Crossfeec25b2019-01-30 17:32:39 -0800252
Colin Cross69f59a32019-02-15 10:39:37 -0800253 inputs := make(map[string]Path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800254 for _, c := range r.commands {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400255 for _, input := range append(c.inputs, c.implicits...) {
Dan Willemsen633c5022019-04-12 11:11:38 -0700256 inputStr := input.String()
257 if _, isOutput := outputs[inputStr]; !isOutput {
258 if _, isDepFile := depFiles[inputStr]; !isDepFile {
259 inputs[input.String()] = input
260 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800261 }
262 }
263 }
264
Colin Cross69f59a32019-02-15 10:39:37 -0800265 var inputList Paths
266 for _, input := range inputs {
Colin Crossfeec25b2019-01-30 17:32:39 -0800267 inputList = append(inputList, input)
268 }
Colin Cross69f59a32019-02-15 10:39:37 -0800269
270 sort.Slice(inputList, func(i, j int) bool {
271 return inputList[i].String() < inputList[j].String()
272 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800273
274 return inputList
275}
276
Colin Crossda71eda2020-02-21 16:55:19 -0800277// OrderOnlys returns the list of paths that were passed to the RuleBuilderCommand.OrderOnly or
278// RuleBuilderCommand.OrderOnlys. The list is sorted and duplicates removed.
279func (r *RuleBuilder) OrderOnlys() Paths {
280 orderOnlys := make(map[string]Path)
281 for _, c := range r.commands {
282 for _, orderOnly := range c.orderOnlys {
283 orderOnlys[orderOnly.String()] = orderOnly
284 }
285 }
286
287 var orderOnlyList Paths
288 for _, orderOnly := range orderOnlys {
289 orderOnlyList = append(orderOnlyList, orderOnly)
290 }
291
292 sort.Slice(orderOnlyList, func(i, j int) bool {
293 return orderOnlyList[i].String() < orderOnlyList[j].String()
294 })
295
296 return orderOnlyList
297}
298
Colin Crossae89abe2021-04-21 11:45:23 -0700299// Validations returns the list of paths that were passed to RuleBuilderCommand.Validation or
300// RuleBuilderCommand.Validations. The list is sorted and duplicates removed.
301func (r *RuleBuilder) Validations() Paths {
302 validations := make(map[string]Path)
303 for _, c := range r.commands {
304 for _, validation := range c.validations {
305 validations[validation.String()] = validation
306 }
307 }
308
309 var validationList Paths
310 for _, validation := range validations {
311 validationList = append(validationList, validation)
312 }
313
314 sort.Slice(validationList, func(i, j int) bool {
315 return validationList[i].String() < validationList[j].String()
316 })
317
318 return validationList
319}
320
Colin Cross69f59a32019-02-15 10:39:37 -0800321func (r *RuleBuilder) outputSet() map[string]WritablePath {
322 outputs := make(map[string]WritablePath)
Colin Crossfeec25b2019-01-30 17:32:39 -0800323 for _, c := range r.commands {
324 for _, output := range c.outputs {
Colin Cross69f59a32019-02-15 10:39:37 -0800325 outputs[output.String()] = output
Colin Crossfeec25b2019-01-30 17:32:39 -0800326 }
327 }
328 return outputs
329}
330
Colin Crossda71eda2020-02-21 16:55:19 -0800331// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
332// output paths, such as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or
333// RuleBuilderCommand.FlagWithInput. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800334func (r *RuleBuilder) Outputs() WritablePaths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800335 outputs := r.outputSet()
336
Colin Cross69f59a32019-02-15 10:39:37 -0800337 var outputList WritablePaths
338 for _, output := range outputs {
Colin Cross5cb5b092019-02-02 21:25:18 -0800339 if !r.temporariesSet[output] {
340 outputList = append(outputList, output)
341 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800342 }
Colin Cross69f59a32019-02-15 10:39:37 -0800343
344 sort.Slice(outputList, func(i, j int) bool {
345 return outputList[i].String() < outputList[j].String()
346 })
347
Colin Crossfeec25b2019-01-30 17:32:39 -0800348 return outputList
349}
350
Dan Willemsen633c5022019-04-12 11:11:38 -0700351func (r *RuleBuilder) depFileSet() map[string]WritablePath {
352 depFiles := make(map[string]WritablePath)
353 for _, c := range r.commands {
354 for _, depFile := range c.depFiles {
355 depFiles[depFile.String()] = depFile
356 }
357 }
358 return depFiles
359}
360
Colin Cross1d2cf042019-03-29 15:33:06 -0700361// DepFiles returns the list of paths that were passed to the RuleBuilderCommand methods that take depfile paths, such
362// as RuleBuilderCommand.DepFile or RuleBuilderCommand.FlagWithDepFile.
363func (r *RuleBuilder) DepFiles() WritablePaths {
364 var depFiles WritablePaths
365
366 for _, c := range r.commands {
367 for _, depFile := range c.depFiles {
368 depFiles = append(depFiles, depFile)
369 }
370 }
371
372 return depFiles
373}
374
Colin Cross758290d2019-02-01 16:42:32 -0800375// Installs returns the list of tuples passed to Install.
Colin Crossdeabb942019-02-11 14:11:09 -0800376func (r *RuleBuilder) Installs() RuleBuilderInstalls {
377 return append(RuleBuilderInstalls(nil), r.installs...)
Colin Crossfeec25b2019-01-30 17:32:39 -0800378}
379
Colin Cross69f59a32019-02-15 10:39:37 -0800380func (r *RuleBuilder) toolsSet() map[string]Path {
381 tools := make(map[string]Path)
Colin Cross5cb5b092019-02-02 21:25:18 -0800382 for _, c := range r.commands {
383 for _, tool := range c.tools {
Colin Cross69f59a32019-02-15 10:39:37 -0800384 tools[tool.String()] = tool
Colin Cross5cb5b092019-02-02 21:25:18 -0800385 }
386 }
387
388 return tools
389}
390
Colin Crossda71eda2020-02-21 16:55:19 -0800391// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method. The
392// list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800393func (r *RuleBuilder) Tools() Paths {
Colin Cross5cb5b092019-02-02 21:25:18 -0800394 toolsSet := r.toolsSet()
395
Colin Cross69f59a32019-02-15 10:39:37 -0800396 var toolsList Paths
397 for _, tool := range toolsSet {
Colin Cross5cb5b092019-02-02 21:25:18 -0800398 toolsList = append(toolsList, tool)
Colin Crossfeec25b2019-01-30 17:32:39 -0800399 }
Colin Cross69f59a32019-02-15 10:39:37 -0800400
401 sort.Slice(toolsList, func(i, j int) bool {
402 return toolsList[i].String() < toolsList[j].String()
403 })
404
Colin Cross5cb5b092019-02-02 21:25:18 -0800405 return toolsList
Colin Crossfeec25b2019-01-30 17:32:39 -0800406}
407
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700408// RspFileInputs returns the list of paths that were passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
409func (r *RuleBuilder) RspFileInputs() Paths {
410 var rspFileInputs Paths
411 for _, c := range r.commands {
Colin Crossce3a51d2021-03-19 16:22:12 -0700412 for _, rspFile := range c.rspFiles {
413 rspFileInputs = append(rspFileInputs, rspFile.paths...)
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700414 }
415 }
416
417 return rspFileInputs
418}
419
Colin Crossce3a51d2021-03-19 16:22:12 -0700420func (r *RuleBuilder) rspFiles() []rspFileAndPaths {
421 var rspFiles []rspFileAndPaths
Colin Cross70c47412021-03-12 17:48:14 -0800422 for _, c := range r.commands {
Colin Crossce3a51d2021-03-19 16:22:12 -0700423 rspFiles = append(rspFiles, c.rspFiles...)
Colin Cross70c47412021-03-12 17:48:14 -0800424 }
425
Colin Crossce3a51d2021-03-19 16:22:12 -0700426 return rspFiles
Colin Cross70c47412021-03-12 17:48:14 -0800427}
428
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700429// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
Colin Crossfeec25b2019-01-30 17:32:39 -0800430func (r *RuleBuilder) Commands() []string {
431 var commands []string
432 for _, c := range r.commands {
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700433 commands = append(commands, c.String())
434 }
435 return commands
436}
437
Colin Cross758290d2019-02-01 16:42:32 -0800438// BuilderContext is a subset of ModuleContext and SingletonContext.
Colin Cross786cd6d2019-02-01 16:41:11 -0800439type BuilderContext interface {
440 PathContext
441 Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
442 Build(PackageContext, BuildParams)
443}
444
Colin Cross758290d2019-02-01 16:42:32 -0800445var _ BuilderContext = ModuleContext(nil)
446var _ BuilderContext = SingletonContext(nil)
447
Colin Crossf1a035e2020-11-16 17:32:30 -0800448func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700449 return r.Command().
Colin Cross9b698b62021-12-22 09:55:32 -0800450 builtToolWithoutDeps("dep_fixer").
Dan Willemsen633c5022019-04-12 11:11:38 -0700451 Inputs(depFiles.Paths())
Colin Cross1d2cf042019-03-29 15:33:06 -0700452}
453
Sam Delmerico285b66a2023-09-25 12:13:17 +0000454// BuildWithNinjaVars adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
455// Outputs. This function will not escape Ninja variables, so it may be used to write sandbox manifests using Ninja variables.
456func (r *RuleBuilder) BuildWithUnescapedNinjaVars(name string, desc string) {
457 r.build(name, desc, false)
458}
459
Colin Cross758290d2019-02-01 16:42:32 -0800460// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
461// Outputs.
Colin Crossf1a035e2020-11-16 17:32:30 -0800462func (r *RuleBuilder) Build(name string, desc string) {
Sam Delmerico285b66a2023-09-25 12:13:17 +0000463 r.build(name, desc, true)
464}
465
Cole Faust63ea1f92024-08-27 11:42:26 -0700466var sandboxEnvOnceKey = NewOnceKey("sandbox_environment_variables")
467
Sam Delmerico285b66a2023-09-25 12:13:17 +0000468func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString bool) {
Colin Cross1d2cf042019-03-29 15:33:06 -0700469 name = ninjaNameEscape(name)
470
Colin Cross0d2f40a2019-02-05 22:31:15 -0800471 if len(r.missingDeps) > 0 {
Sam Delmerico285b66a2023-09-25 12:13:17 +0000472 r.ctx.Build(r.pctx, BuildParams{
Colin Cross0d2f40a2019-02-05 22:31:15 -0800473 Rule: ErrorRule,
Colin Cross69f59a32019-02-15 10:39:37 -0800474 Outputs: r.Outputs(),
Colin Cross0d2f40a2019-02-05 22:31:15 -0800475 Description: desc,
476 Args: map[string]string{
477 "error": "missing dependencies: " + strings.Join(r.missingDeps, ", "),
478 },
479 })
480 return
481 }
482
Colin Cross1d2cf042019-03-29 15:33:06 -0700483 var depFile WritablePath
484 var depFormat blueprint.Deps
485 if depFiles := r.DepFiles(); len(depFiles) > 0 {
486 depFile = depFiles[0]
487 depFormat = blueprint.DepsGCC
488 if len(depFiles) > 1 {
489 // Add a command locally that merges all depfiles together into the first depfile.
Colin Crossf1a035e2020-11-16 17:32:30 -0800490 r.depFileMergerCmd(depFiles)
Dan Willemsen633c5022019-04-12 11:11:38 -0700491
492 if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800493 // Check for Rel() errors, as all depfiles should be in the output dir. Errors
494 // will be reported to the ctx.
Dan Willemsen633c5022019-04-12 11:11:38 -0700495 for _, path := range depFiles[1:] {
Colin Crossf1a035e2020-11-16 17:32:30 -0800496 Rel(r.ctx, r.outDir.String(), path.String())
Dan Willemsen633c5022019-04-12 11:11:38 -0700497 }
498 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700499 }
500 }
501
Dan Willemsen633c5022019-04-12 11:11:38 -0700502 tools := r.Tools()
Colin Crossb70a1a92021-03-12 17:51:32 -0800503 commands := r.Commands()
Dan Willemsen633c5022019-04-12 11:11:38 -0700504 outputs := r.Outputs()
Colin Cross3d680512020-11-13 16:23:53 -0800505 inputs := r.Inputs()
Colin Crossce3a51d2021-03-19 16:22:12 -0700506 rspFiles := r.rspFiles()
Dan Willemsen633c5022019-04-12 11:11:38 -0700507
508 if len(commands) == 0 {
509 return
510 }
511 if len(outputs) == 0 {
512 panic("No outputs specified from any Commands")
513 }
514
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700515 commandString := strings.Join(commands, " && ")
Dan Willemsen633c5022019-04-12 11:11:38 -0700516
517 if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800518 // If running the command inside sbox, write the rule data out to an sbox
519 // manifest.textproto.
520 manifest := sbox_proto.Manifest{}
521 command := sbox_proto.Command{}
522 manifest.Commands = append(manifest.Commands, &command)
523 command.Command = proto.String(commandString)
Colin Cross151b9ff2020-11-12 08:29:30 -0800524
Colin Cross619b9ab2020-11-20 18:44:31 +0000525 if depFile != nil {
Colin Crosse16ce362020-11-12 08:29:30 -0800526 manifest.OutputDepfile = proto.String(depFile.String())
Colin Cross619b9ab2020-11-20 18:44:31 +0000527 }
528
Colin Crossba9e4032020-11-24 16:32:22 -0800529 // If sandboxing tools is enabled, add copy rules to the manifest to copy each tool
530 // into the sbox directory.
531 if r.sboxTools {
532 for _, tool := range tools {
533 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
534 From: proto.String(tool.String()),
535 To: proto.String(sboxPathForToolRel(r.ctx, tool)),
536 })
537 }
538 for _, c := range r.commands {
539 for _, tool := range c.packagedTools {
540 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
541 From: proto.String(tool.srcPath.String()),
542 To: proto.String(sboxPathForPackagedToolRel(tool)),
543 Executable: proto.Bool(tool.executable),
544 })
545 tools = append(tools, tool.srcPath)
546 }
547 }
548 }
549
Colin Crossab020a72021-03-12 17:52:23 -0800550 // If sandboxing inputs is enabled, add copy rules to the manifest to copy each input
551 // into the sbox directory.
552 if r.sboxInputs {
553 for _, input := range inputs {
554 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
555 From: proto.String(input.String()),
556 To: proto.String(r.sboxPathForInputRel(input)),
557 })
558 }
Cole Faust78f3c3a2024-08-15 17:19:34 -0700559 for _, input := range r.OrderOnlys() {
560 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
561 From: proto.String(input.String()),
562 To: proto.String(r.sboxPathForInputRel(input)),
563 })
564 }
Colin Crossab020a72021-03-12 17:52:23 -0800565
Colin Crossce3a51d2021-03-19 16:22:12 -0700566 // If using rsp files copy them and their contents into the sbox directory with
567 // the appropriate path mappings.
568 for _, rspFile := range rspFiles {
Colin Crosse55bd422021-03-23 13:44:30 -0700569 command.RspFiles = append(command.RspFiles, &sbox_proto.RspFile{
Colin Crossce3a51d2021-03-19 16:22:12 -0700570 File: proto.String(rspFile.file.String()),
Colin Crosse55bd422021-03-23 13:44:30 -0700571 // These have to match the logic in sboxPathForInputRel
572 PathMappings: []*sbox_proto.PathMapping{
573 {
574 From: proto.String(r.outDir.String()),
575 To: proto.String(sboxOutSubDir),
576 },
577 {
Cole Fauste8561c62023-11-30 17:26:37 -0800578 From: proto.String(r.ctx.Config().OutDir()),
Colin Crosse55bd422021-03-23 13:44:30 -0700579 To: proto.String(sboxOutSubDir),
580 },
581 },
Colin Crossab020a72021-03-12 17:52:23 -0800582 })
583 }
584
Cole Faust63ea1f92024-08-27 11:42:26 -0700585 // Only allow the build to access certain environment variables
586 command.DontInheritEnv = proto.Bool(true)
587 command.Env = r.ctx.Config().Once(sandboxEnvOnceKey, func() interface{} {
588 // The list of allowed variables was found by running builds of all
589 // genrules and seeing what failed
590 var result []*sbox_proto.EnvironmentVariable
591 inheritedVars := []string{
592 "PATH",
593 "JAVA_HOME",
594 "TMPDIR",
595 // Allow RBE variables because the art tests invoke RBE manually
596 "RBE_log_dir",
597 "RBE_platform",
598 "RBE_server_address",
599 // TODO: RBE_exec_root is set to the absolute path to the root of the source
600 // tree, which we don't want sandboxed actions to find. Remap it to ".".
601 "RBE_exec_root",
602 }
603 for _, v := range inheritedVars {
604 result = append(result, &sbox_proto.EnvironmentVariable{
605 Name: proto.String(v),
606 State: &sbox_proto.EnvironmentVariable_Inherit{
607 Inherit: true,
608 },
609 })
610 }
611 // Set OUT_DIR to the relative path of the sandboxed out directory.
612 // Otherwise, OUT_DIR will be inherited from the rest of the build,
613 // which will allow scripts to escape the sandbox if OUT_DIR is an
614 // absolute path.
615 result = append(result, &sbox_proto.EnvironmentVariable{
616 Name: proto.String("OUT_DIR"),
617 State: &sbox_proto.EnvironmentVariable_Value{
618 Value: sboxOutSubDir,
619 },
620 })
621 return result
622 }).([]*sbox_proto.EnvironmentVariable)
Colin Crossab020a72021-03-12 17:52:23 -0800623 command.Chdir = proto.Bool(true)
624 }
625
Colin Crosse16ce362020-11-12 08:29:30 -0800626 // Add copy rules to the manifest to copy each output file from the sbox directory.
Colin Crossba9e4032020-11-24 16:32:22 -0800627 // to the output directory after running the commands.
Spandan Das33e30972023-07-13 21:19:12 +0000628 for _, output := range outputs {
Colin Crossf1a035e2020-11-16 17:32:30 -0800629 rel := Rel(r.ctx, r.outDir.String(), output.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800630 command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{
Spandan Dasaf4ccaa2023-06-29 01:15:51 +0000631 From: proto.String(filepath.Join(r.sboxOutSubDir, rel)),
Colin Crosse16ce362020-11-12 08:29:30 -0800632 To: proto.String(output.String()),
633 })
634 }
Colin Cross619b9ab2020-11-20 18:44:31 +0000635
Colin Cross5334edd2021-03-11 17:18:21 -0800636 // Outputs that were marked Temporary will not be checked that they are in the output
637 // directory by the loop above, check them here.
638 for path := range r.temporariesSet {
639 Rel(r.ctx, r.outDir.String(), path.String())
640 }
641
Colin Crosse16ce362020-11-12 08:29:30 -0800642 // Add a hash of the list of input files to the manifest so that the textproto file
643 // changes when the list of input files changes and causes the sbox rule that
644 // depends on it to rerun.
645 command.InputHash = proto.String(hashSrcFiles(inputs))
Colin Cross619b9ab2020-11-20 18:44:31 +0000646
Colin Crosse16ce362020-11-12 08:29:30 -0800647 // Verify that the manifest textproto is not inside the sbox output directory, otherwise
648 // it will get deleted when the sbox rule clears its output directory.
Colin Crossf1a035e2020-11-16 17:32:30 -0800649 _, manifestInOutDir := MaybeRel(r.ctx, r.outDir.String(), r.sboxManifestPath.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800650 if manifestInOutDir {
Colin Crossf1a035e2020-11-16 17:32:30 -0800651 ReportPathErrorf(r.ctx, "sbox rule %q manifestPath %q must not be in outputDir %q",
652 name, r.sboxManifestPath.String(), r.outDir.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800653 }
654
Paul Duffin4a3a0a52023-10-12 15:01:29 +0100655 // Create a rule to write the manifest as textproto. Pretty print it by indenting and
656 // splitting across multiple lines.
657 pbText, err := prototext.MarshalOptions{Indent: " "}.Marshal(&manifest)
Dan Willemsen4591b642021-05-24 14:24:12 -0700658 if err != nil {
659 ReportPathErrorf(r.ctx, "sbox manifest failed to marshal: %q", err)
660 }
Sam Delmerico285b66a2023-09-25 12:13:17 +0000661 if ninjaEscapeCommandString {
662 WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText))
663 } else {
664 // We need to have a rule to write files that is
665 // defined on the RuleBuilder's pctx in order to
666 // write Ninja variables in the string.
667 // The WriteFileRule function above rule can only write
668 // raw strings because it is defined on the android
669 // package's pctx, and it can't access variables defined
670 // in another context.
671 r.ctx.Build(r.pctx, BuildParams{
672 Rule: r.ctx.Rule(r.pctx, "unescapedWriteFile", blueprint.RuleParams{
673 Command: `rm -rf ${out} && cat ${out}.rsp > ${out}`,
674 Rspfile: "${out}.rsp",
675 RspfileContent: "${content}",
676 Description: "write file",
677 }, "content"),
678 Output: r.sboxManifestPath,
679 Description: "write sbox manifest " + r.sboxManifestPath.Base(),
680 Args: map[string]string{
681 "content": string(pbText),
682 },
683 })
684 }
Colin Crosse16ce362020-11-12 08:29:30 -0800685
686 // Generate a new string to use as the command line of the sbox rule. This uses
687 // a RuleBuilderCommand as a convenience method of building the command line, then
688 // converts it to a string to replace commandString.
Colin Crossf1a035e2020-11-16 17:32:30 -0800689 sboxCmd := &RuleBuilderCommand{
690 rule: &RuleBuilder{
691 ctx: r.ctx,
692 },
693 }
Colin Cross9b698b62021-12-22 09:55:32 -0800694 sboxCmd.builtToolWithoutDeps("sbox").
Colin Crosse52c2ac2022-03-28 17:03:35 -0700695 FlagWithArg("--sandbox-path ", shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
696 FlagWithArg("--output-dir ", r.outDir.String()).
697 FlagWithInput("--manifest ", r.sboxManifestPath)
698
699 if r.restat {
700 sboxCmd.Flag("--write-if-changed")
701 }
Colin Crosse16ce362020-11-12 08:29:30 -0800702
703 // Replace the command string, and add the sbox tool and manifest textproto to the
704 // dependencies of the final sbox rule.
Colin Crosscfec40c2019-07-08 17:07:18 -0700705 commandString = sboxCmd.buf.String()
Dan Willemsen633c5022019-04-12 11:11:38 -0700706 tools = append(tools, sboxCmd.tools...)
Colin Crosse16ce362020-11-12 08:29:30 -0800707 inputs = append(inputs, sboxCmd.inputs...)
Colin Crossef972742021-03-12 17:24:45 -0800708
709 if r.rbeParams != nil {
Colin Crosse55bd422021-03-23 13:44:30 -0700710 // RBE needs a list of input files to copy to the remote builder. For inputs already
711 // listed in an rsp file, pass the rsp file directly to rewrapper. For the rest,
712 // create a new rsp file to pass to rewrapper.
713 var remoteRspFiles Paths
714 var remoteInputs Paths
715
716 remoteInputs = append(remoteInputs, inputs...)
717 remoteInputs = append(remoteInputs, tools...)
718
Colin Crossce3a51d2021-03-19 16:22:12 -0700719 for _, rspFile := range rspFiles {
720 remoteInputs = append(remoteInputs, rspFile.file)
721 remoteRspFiles = append(remoteRspFiles, rspFile.file)
Colin Crossef972742021-03-12 17:24:45 -0800722 }
Colin Crosse55bd422021-03-23 13:44:30 -0700723
724 if len(remoteInputs) > 0 {
725 inputsListFile := r.sboxManifestPath.ReplaceExtension(r.ctx, "rbe_inputs.list")
726 writeRspFileRule(r.ctx, inputsListFile, remoteInputs)
727 remoteRspFiles = append(remoteRspFiles, inputsListFile)
728 // Add the new rsp file as an extra input to the rule.
729 inputs = append(inputs, inputsListFile)
730 }
Colin Crossef972742021-03-12 17:24:45 -0800731
732 r.rbeParams.OutputFiles = outputs.Strings()
Colin Crosse55bd422021-03-23 13:44:30 -0700733 r.rbeParams.RSPFiles = remoteRspFiles.Strings()
Colin Crossef972742021-03-12 17:24:45 -0800734 rewrapperCommand := r.rbeParams.NoVarTemplate(r.ctx.Config().RBEWrapper())
735 commandString = rewrapperCommand + " bash -c '" + strings.ReplaceAll(commandString, `'`, `'\''`) + "'"
736 }
Colin Cross3d680512020-11-13 16:23:53 -0800737 } else {
738 // If not using sbox the rule will run the command directly, put the hash of the
739 // list of input files in a comment at the end of the command line to ensure ninja
740 // reruns the rule when the list of input files changes.
741 commandString += " # hash of input list: " + hashSrcFiles(inputs)
Dan Willemsen633c5022019-04-12 11:11:38 -0700742 }
743
Colin Cross1d2cf042019-03-29 15:33:06 -0700744 // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
Colin Cross70c47412021-03-12 17:48:14 -0800745 // ImplicitOutputs. RuleBuilder doesn't use "$out", so the distinction between Outputs and
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700746 // ImplicitOutputs doesn't matter.
Dan Willemsen633c5022019-04-12 11:11:38 -0700747 output := outputs[0]
748 implicitOutputs := outputs[1:]
Colin Cross1d2cf042019-03-29 15:33:06 -0700749
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700750 var rspFile, rspFileContent string
Colin Crossce3a51d2021-03-19 16:22:12 -0700751 var rspFileInputs Paths
752 if len(rspFiles) > 0 {
753 // The first rsp files uses Ninja's rsp file support for the rule
754 rspFile = rspFiles[0].file.String()
Colin Crosse55bd422021-03-23 13:44:30 -0700755 // Use "$in" for rspFileContent to avoid duplicating the list of files in the dependency
756 // list and in the contents of the rsp file. Inputs to the rule that are not in the
757 // rsp file will be listed in Implicits instead of Inputs so they don't show up in "$in".
758 rspFileContent = "$in"
Colin Crossce3a51d2021-03-19 16:22:12 -0700759 rspFileInputs = append(rspFileInputs, rspFiles[0].paths...)
760
761 for _, rspFile := range rspFiles[1:] {
762 // Any additional rsp files need an extra rule to write the file.
763 writeRspFileRule(r.ctx, rspFile.file, rspFile.paths)
764 // The main rule needs to depend on the inputs listed in the extra rsp file.
765 inputs = append(inputs, rspFile.paths...)
766 // The main rule needs to depend on the extra rsp file.
767 inputs = append(inputs, rspFile.file)
768 }
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700769 }
770
Colin Cross8b8bec32019-11-15 13:18:43 -0800771 var pool blueprint.Pool
Colin Crossf1a035e2020-11-16 17:32:30 -0800772 if r.ctx.Config().UseGoma() && r.remoteable.Goma {
Colin Cross8b8bec32019-11-15 13:18:43 -0800773 // 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 -0800774 } else if r.ctx.Config().UseRBE() && r.remoteable.RBE {
Ramy Medhat944839a2020-03-31 22:14:52 -0400775 // When USE_RBE=true is set and the rule is supported by RBE, use the remotePool.
776 pool = remotePool
Colin Cross8b8bec32019-11-15 13:18:43 -0800777 } else if r.highmem {
778 pool = highmemPool
Colin Crossf1a035e2020-11-16 17:32:30 -0800779 } else if r.ctx.Config().UseRemoteBuild() {
Colin Cross8b8bec32019-11-15 13:18:43 -0800780 pool = localPool
781 }
782
Sam Delmericod46f6c82023-09-25 12:13:17 +0000783 if ninjaEscapeCommandString {
784 commandString = proptools.NinjaEscape(commandString)
785 }
786
Devin Mooreb6cc64f2024-07-15 22:31:24 +0000787 args_vars := make([]string, len(r.args))
788 i := 0
789 for k, _ := range r.args {
790 args_vars[i] = k
791 i++
792 }
Colin Crossf1a035e2020-11-16 17:32:30 -0800793 r.ctx.Build(r.pctx, BuildParams{
Sam Delmerico285b66a2023-09-25 12:13:17 +0000794 Rule: r.ctx.Rule(r.pctx, name, blueprint.RuleParams{
Sam Delmericod46f6c82023-09-25 12:13:17 +0000795 Command: commandString,
Colin Cross45029782021-03-16 16:49:52 -0700796 CommandDeps: proptools.NinjaEscapeList(tools.Strings()),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700797 Restat: r.restat,
Colin Cross45029782021-03-16 16:49:52 -0700798 Rspfile: proptools.NinjaEscape(rspFile),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700799 RspfileContent: rspFileContent,
Colin Cross8b8bec32019-11-15 13:18:43 -0800800 Pool: pool,
Devin Mooreb6cc64f2024-07-15 22:31:24 +0000801 }, args_vars...),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700802 Inputs: rspFileInputs,
Colin Cross3d680512020-11-13 16:23:53 -0800803 Implicits: inputs,
Colin Crossda6401b2021-04-21 11:32:19 -0700804 OrderOnly: r.OrderOnlys(),
Colin Crossae89abe2021-04-21 11:45:23 -0700805 Validations: r.Validations(),
Dan Willemsen633c5022019-04-12 11:11:38 -0700806 Output: output,
807 ImplicitOutputs: implicitOutputs,
808 Depfile: depFile,
809 Deps: depFormat,
810 Description: desc,
Devin Mooreb6cc64f2024-07-15 22:31:24 +0000811 Args: r.args,
Dan Willemsen633c5022019-04-12 11:11:38 -0700812 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800813}
814
Colin Cross758290d2019-02-01 16:42:32 -0800815// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
816// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the
817// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
818// space as a separator from the previous method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800819type RuleBuilderCommand struct {
Colin Crossf1a035e2020-11-16 17:32:30 -0800820 rule *RuleBuilder
821
Cole Faust9a346f62024-01-18 20:12:02 +0000822 buf strings.Builder
823 inputs Paths
824 implicits Paths
825 orderOnlys Paths
826 validations Paths
827 outputs WritablePaths
828 depFiles WritablePaths
829 tools Paths
830 packagedTools []PackagingSpec
831 rspFiles []rspFileAndPaths
Colin Crossce3a51d2021-03-19 16:22:12 -0700832}
833
834type rspFileAndPaths struct {
835 file WritablePath
836 paths Paths
Dan Willemsen633c5022019-04-12 11:11:38 -0700837}
838
Paul Duffin3866b892021-10-04 11:24:48 +0100839func checkPathNotNil(path Path) {
840 if path == nil {
841 panic("rule_builder paths cannot be nil")
842 }
843}
844
Dan Willemsen633c5022019-04-12 11:11:38 -0700845func (c *RuleBuilderCommand) addInput(path Path) string {
Paul Duffin3866b892021-10-04 11:24:48 +0100846 checkPathNotNil(path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700847 c.inputs = append(c.inputs, path)
Colin Crossab020a72021-03-12 17:52:23 -0800848 return c.PathForInput(path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700849}
850
Colin Crossab020a72021-03-12 17:52:23 -0800851func (c *RuleBuilderCommand) addImplicit(path Path) {
Paul Duffin3866b892021-10-04 11:24:48 +0100852 checkPathNotNil(path)
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400853 c.implicits = append(c.implicits, path)
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400854}
855
Colin Crossda71eda2020-02-21 16:55:19 -0800856func (c *RuleBuilderCommand) addOrderOnly(path Path) {
Paul Duffin3866b892021-10-04 11:24:48 +0100857 checkPathNotNil(path)
Colin Crossda71eda2020-02-21 16:55:19 -0800858 c.orderOnlys = append(c.orderOnlys, path)
859}
860
Colin Crossab020a72021-03-12 17:52:23 -0800861// PathForInput takes an input path and returns the appropriate path to use on the command line. If
862// sbox was enabled via a call to RuleBuilder.Sbox() and the path was an output path it returns a
863// path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
864// original path.
865func (c *RuleBuilderCommand) PathForInput(path Path) string {
866 if c.rule.sbox {
867 rel, inSandbox := c.rule._sboxPathForInputRel(path)
868 if inSandbox {
869 rel = filepath.Join(sboxSandboxBaseDir, rel)
870 }
871 return rel
872 }
873 return path.String()
874}
875
876// PathsForInputs takes a list of input paths and returns the appropriate paths to use on the
877// command line. If sbox was enabled via a call to RuleBuilder.Sbox() a path was an output path, it
878// returns the path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it
879// returns the original paths.
880func (c *RuleBuilderCommand) PathsForInputs(paths Paths) []string {
881 ret := make([]string, len(paths))
882 for i, path := range paths {
883 ret[i] = c.PathForInput(path)
884 }
885 return ret
886}
887
Colin Crossf1a035e2020-11-16 17:32:30 -0800888// PathForOutput takes an output path and returns the appropriate path to use on the command
889// line. If sbox was enabled via a call to RuleBuilder.Sbox(), it returns a path with the
890// placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
891// original path.
892func (c *RuleBuilderCommand) PathForOutput(path WritablePath) string {
893 if c.rule.sbox {
894 // Errors will be handled in RuleBuilder.Build where we have a context to report them
895 rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
896 return filepath.Join(sboxOutDir, rel)
Dan Willemsen633c5022019-04-12 11:11:38 -0700897 }
898 return path.String()
Colin Crossfeec25b2019-01-30 17:32:39 -0800899}
900
Colin Crossba9e4032020-11-24 16:32:22 -0800901func sboxPathForToolRel(ctx BuilderContext, path Path) string {
902 // Errors will be handled in RuleBuilder.Build where we have a context to report them
Cole Faust3b703f32023-10-16 13:30:51 -0700903 toolDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "")
Colin Cross790ef352021-10-25 19:15:55 -0700904 relOutSoong, isRelOutSoong, _ := maybeRelErr(toolDir.String(), path.String())
905 if isRelOutSoong {
906 // The tool is in the Soong output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
907 return filepath.Join(sboxToolsSubDir, "out", relOutSoong)
Colin Crossba9e4032020-11-24 16:32:22 -0800908 }
909 // The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
910 return filepath.Join(sboxToolsSubDir, "src", path.String())
911}
912
Colin Crossab020a72021-03-12 17:52:23 -0800913func (r *RuleBuilder) _sboxPathForInputRel(path Path) (rel string, inSandbox bool) {
914 // Errors will be handled in RuleBuilder.Build where we have a context to report them
915 rel, isRelSboxOut, _ := maybeRelErr(r.outDir.String(), path.String())
916 if isRelSboxOut {
917 return filepath.Join(sboxOutSubDir, rel), true
918 }
919 if r.sboxInputs {
920 // When sandboxing inputs all inputs have to be copied into the sandbox. Input files that
921 // are outputs of other rules could be an arbitrary absolute path if OUT_DIR is set, so they
922 // will be copied to relative paths under __SBOX_OUT_DIR__/out.
Cole Fauste8561c62023-11-30 17:26:37 -0800923 rel, isRelOut, _ := maybeRelErr(r.ctx.Config().OutDir(), path.String())
Colin Crossab020a72021-03-12 17:52:23 -0800924 if isRelOut {
925 return filepath.Join(sboxOutSubDir, rel), true
926 }
927 }
928 return path.String(), false
929}
930
931func (r *RuleBuilder) sboxPathForInputRel(path Path) string {
932 rel, _ := r._sboxPathForInputRel(path)
933 return rel
934}
935
936func (r *RuleBuilder) sboxPathsForInputsRel(paths Paths) []string {
937 ret := make([]string, len(paths))
938 for i, path := range paths {
939 ret[i] = r.sboxPathForInputRel(path)
940 }
941 return ret
942}
943
Colin Crossba9e4032020-11-24 16:32:22 -0800944func sboxPathForPackagedToolRel(spec PackagingSpec) string {
945 return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage)
946}
947
Colin Crossd11cf622021-03-23 22:30:35 -0700948// PathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
949// tool after copying it into the sandbox. This can be used on the RuleBuilder command line to
950// reference the tool.
951func (c *RuleBuilderCommand) PathForPackagedTool(spec PackagingSpec) string {
952 if !c.rule.sboxTools {
953 panic("PathForPackagedTool() requires SandboxTools()")
954 }
955
956 return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
957}
958
Colin Crossba9e4032020-11-24 16:32:22 -0800959// PathForTool takes a path to a tool, which may be an output file or a source file, and returns
960// the corresponding path for the tool in the sbox sandbox if sbox is enabled, or the original path
961// if it is not. This can be used on the RuleBuilder command line to reference the tool.
962func (c *RuleBuilderCommand) PathForTool(path Path) string {
963 if c.rule.sbox && c.rule.sboxTools {
964 return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path))
965 }
966 return path.String()
967}
968
Colin Crossd11cf622021-03-23 22:30:35 -0700969// PathsForTools takes a list of paths to tools, which may be output files or source files, and
970// returns the corresponding paths for the tools in the sbox sandbox if sbox is enabled, or the
971// original paths if it is not. This can be used on the RuleBuilder command line to reference the tool.
972func (c *RuleBuilderCommand) PathsForTools(paths Paths) []string {
973 if c.rule.sbox && c.rule.sboxTools {
974 var ret []string
975 for _, path := range paths {
976 ret = append(ret, filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path)))
977 }
978 return ret
979 }
980 return paths.Strings()
981}
982
Colin Crossba9e4032020-11-24 16:32:22 -0800983// PackagedTool adds the specified tool path to the command line. It can only be used with tool
984// sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox.
985func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand {
986 if !c.rule.sboxTools {
987 panic("PackagedTool() requires SandboxTools()")
988 }
989
990 c.packagedTools = append(c.packagedTools, spec)
991 c.Text(sboxPathForPackagedToolRel(spec))
992 return c
993}
994
995// ImplicitPackagedTool copies the specified tool into the sandbox without modifying the command
996// line. It can only be used with tool sandboxing enabled by SandboxTools().
997func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuilderCommand {
998 if !c.rule.sboxTools {
999 panic("ImplicitPackagedTool() requires SandboxTools()")
1000 }
1001
1002 c.packagedTools = append(c.packagedTools, spec)
1003 return c
1004}
1005
1006// ImplicitPackagedTools copies the specified tools into the sandbox without modifying the command
1007// line. It can only be used with tool sandboxing enabled by SandboxTools().
1008func (c *RuleBuilderCommand) ImplicitPackagedTools(specs []PackagingSpec) *RuleBuilderCommand {
1009 if !c.rule.sboxTools {
1010 panic("ImplicitPackagedTools() requires SandboxTools()")
1011 }
1012
1013 c.packagedTools = append(c.packagedTools, specs...)
1014 return c
1015}
1016
Colin Cross758290d2019-02-01 16:42:32 -08001017// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
1018// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001019func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
Colin Crosscfec40c2019-07-08 17:07:18 -07001020 if c.buf.Len() > 0 {
1021 c.buf.WriteByte(' ')
Colin Crossfeec25b2019-01-30 17:32:39 -08001022 }
Colin Crosscfec40c2019-07-08 17:07:18 -07001023 c.buf.WriteString(text)
Colin Crossfeec25b2019-01-30 17:32:39 -08001024 return c
1025}
1026
Colin Cross758290d2019-02-01 16:42:32 -08001027// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or
1028// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001029func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
1030 return c.Text(fmt.Sprintf(format, a...))
1031}
1032
Colin Cross758290d2019-02-01 16:42:32 -08001033// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the
1034// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001035func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
1036 return c.Text(flag)
1037}
1038
Colin Crossab054432019-07-15 16:13:59 -07001039// OptionalFlag adds the specified raw text to the command line if it is not nil. The text should not contain input or
1040// output paths or the rule will not have them listed in its dependencies or outputs.
1041func (c *RuleBuilderCommand) OptionalFlag(flag *string) *RuleBuilderCommand {
1042 if flag != nil {
1043 c.Text(*flag)
1044 }
1045
1046 return c
1047}
1048
Colin Cross92b7d582019-03-29 15:32:51 -07001049// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the
1050// rule will not have them listed in its dependencies or outputs.
1051func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
1052 for _, flag := range flags {
1053 c.Text(flag)
1054 }
1055 return c
1056}
1057
Colin Cross758290d2019-02-01 16:42:32 -08001058// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
1059// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
1060// outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001061func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
1062 return c.Text(flag + arg)
1063}
1064
Colin Crossc7ed0042019-02-11 14:11:09 -08001065// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to
1066// calling FlagWithArg for argument.
1067func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand {
1068 for _, arg := range args {
1069 c.FlagWithArg(flag, arg)
1070 }
1071 return c
1072}
1073
Roland Levillain2da5d9a2019-02-27 16:56:41 +00001074// 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 -08001075// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or
1076// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -08001077func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
1078 return c.Text(flag + strings.Join(list, sep))
1079}
1080
Colin Cross758290d2019-02-01 16:42:32 -08001081// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by
1082// RuleBuilder.Tools.
Colin Cross69f59a32019-02-15 10:39:37 -08001083func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001084 checkPathNotNil(path)
Colin Crossfeec25b2019-01-30 17:32:39 -08001085 c.tools = append(c.tools, path)
Colin Crossba9e4032020-11-24 16:32:22 -08001086 return c.Text(c.PathForTool(path))
1087}
1088
1089// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
1090func (c *RuleBuilderCommand) ImplicitTool(path Path) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001091 checkPathNotNil(path)
Colin Crossba9e4032020-11-24 16:32:22 -08001092 c.tools = append(c.tools, path)
1093 return c
1094}
1095
1096// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
1097func (c *RuleBuilderCommand) ImplicitTools(paths Paths) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001098 for _, path := range paths {
1099 c.ImplicitTool(path)
1100 }
Colin Crossba9e4032020-11-24 16:32:22 -08001101 return c
Colin Crossfeec25b2019-01-30 17:32:39 -08001102}
1103
Colin Crossee94d6a2019-07-08 17:08:34 -07001104// BuiltTool adds the specified tool path that was built using a host Soong module to the command line. The path will
1105// be also added to the dependencies returned by RuleBuilder.Tools.
1106//
1107// It is equivalent to:
Colin Crossd079e0b2022-08-16 10:27:33 -07001108//
1109// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
Colin Crossf1a035e2020-11-16 17:32:30 -08001110func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
Colin Cross9b698b62021-12-22 09:55:32 -08001111 if c.rule.ctx.Config().UseHostMusl() {
1112 // If the host is using musl, assume that the tool was built against musl libc and include
1113 // libc_musl.so in the sandbox.
1114 // TODO(ccross): if we supported adding new dependencies during GenerateAndroidBuildActions
1115 // this could be a dependency + TransitivePackagingSpecs.
1116 c.ImplicitTool(c.rule.ctx.Config().HostJNIToolPath(c.rule.ctx, "libc_musl"))
1117 }
1118 return c.builtToolWithoutDeps(tool)
1119}
1120
1121// builtToolWithoutDeps is similar to BuiltTool, but doesn't add any dependencies. It is used
1122// internally by RuleBuilder for helper tools that are known to be compiled statically.
1123func (c *RuleBuilderCommand) builtToolWithoutDeps(tool string) *RuleBuilderCommand {
Colin Crossf1a035e2020-11-16 17:32:30 -08001124 return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
Colin Crossee94d6a2019-07-08 17:08:34 -07001125}
1126
1127// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the
1128// dependencies returned by RuleBuilder.Tools.
1129//
1130// It is equivalent to:
Colin Crossd079e0b2022-08-16 10:27:33 -07001131//
1132// cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
Colin Crossee94d6a2019-07-08 17:08:34 -07001133func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
1134 return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
1135}
1136
Colin Cross758290d2019-02-01 16:42:32 -08001137// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
1138// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001139func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001140 return c.Text(c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001141}
1142
Colin Cross758290d2019-02-01 16:42:32 -08001143// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
1144// dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001145func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001146 for _, path := range paths {
1147 c.Input(path)
1148 }
1149 return c
1150}
1151
1152// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
1153// command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001154func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001155 c.addImplicit(path)
Colin Crossfeec25b2019-01-30 17:32:39 -08001156 return c
1157}
1158
Colin Cross758290d2019-02-01 16:42:32 -08001159// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
1160// command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001161func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001162 for _, path := range paths {
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001163 c.addImplicit(path)
Dan Willemsen633c5022019-04-12 11:11:38 -07001164 }
Colin Crossfeec25b2019-01-30 17:32:39 -08001165 return c
1166}
1167
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001168// GetImplicits returns the command's implicit inputs.
1169func (c *RuleBuilderCommand) GetImplicits() Paths {
1170 return c.implicits
1171}
1172
Colin Crossda71eda2020-02-21 16:55:19 -08001173// OrderOnly adds the specified input path to the dependencies returned by RuleBuilder.OrderOnlys
1174// without modifying the command line.
1175func (c *RuleBuilderCommand) OrderOnly(path Path) *RuleBuilderCommand {
1176 c.addOrderOnly(path)
1177 return c
1178}
1179
1180// OrderOnlys adds the specified input paths to the dependencies returned by RuleBuilder.OrderOnlys
1181// without modifying the command line.
1182func (c *RuleBuilderCommand) OrderOnlys(paths Paths) *RuleBuilderCommand {
1183 for _, path := range paths {
1184 c.addOrderOnly(path)
1185 }
1186 return c
1187}
1188
Colin Crossae89abe2021-04-21 11:45:23 -07001189// Validation adds the specified input path to the validation dependencies by
1190// RuleBuilder.Validations without modifying the command line.
1191func (c *RuleBuilderCommand) Validation(path Path) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001192 checkPathNotNil(path)
Colin Crossae89abe2021-04-21 11:45:23 -07001193 c.validations = append(c.validations, path)
1194 return c
1195}
1196
1197// Validations adds the specified input paths to the validation dependencies by
1198// RuleBuilder.Validations without modifying the command line.
1199func (c *RuleBuilderCommand) Validations(paths Paths) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001200 for _, path := range paths {
1201 c.Validation(path)
1202 }
Colin Crossae89abe2021-04-21 11:45:23 -07001203 return c
1204}
1205
Colin Cross758290d2019-02-01 16:42:32 -08001206// Output adds the specified output path to the command line. The path will also be added to the outputs returned by
1207// RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001208func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001209 checkPathNotNil(path)
Colin Crossfeec25b2019-01-30 17:32:39 -08001210 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001211 return c.Text(c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001212}
1213
Colin Cross758290d2019-02-01 16:42:32 -08001214// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
1215// the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001216func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001217 for _, path := range paths {
1218 c.Output(path)
1219 }
1220 return c
1221}
1222
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001223// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
1224// and will be the temporary output directory managed by sbox, not the final one.
Anas Sulaimanb4dff132024-02-07 21:58:46 +00001225func (c *RuleBuilderCommand) OutputDir(subPathComponents ...string) *RuleBuilderCommand {
Colin Crossf1a035e2020-11-16 17:32:30 -08001226 if !c.rule.sbox {
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001227 panic("OutputDir only valid with Sbox")
1228 }
Anas Sulaimanb4dff132024-02-07 21:58:46 +00001229 path := sboxOutDir
1230 if len(subPathComponents) > 0 {
1231 path = filepath.Join(append([]string{sboxOutDir}, subPathComponents...)...)
1232 }
1233 return c.Text(path)
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001234}
1235
Colin Cross1d2cf042019-03-29 15:33:06 -07001236// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
1237// line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles are added to
1238// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
1239func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
Paul Duffin3866b892021-10-04 11:24:48 +01001240 checkPathNotNil(path)
Colin Cross1d2cf042019-03-29 15:33:06 -07001241 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001242 return c.Text(c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -07001243}
1244
Colin Cross758290d2019-02-01 16:42:32 -08001245// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
1246// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001247func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001248 c.outputs = append(c.outputs, path)
1249 return c
1250}
1251
Colin Cross758290d2019-02-01 16:42:32 -08001252// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
1253// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001254func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001255 c.outputs = append(c.outputs, paths...)
1256 return c
1257}
1258
Colin Cross1d2cf042019-03-29 15:33:06 -07001259// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
1260// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles
1261// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
1262// depfiles together.
1263func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand {
1264 c.depFiles = append(c.depFiles, path)
1265 return c
1266}
1267
Colin Cross758290d2019-02-01 16:42:32 -08001268// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
1269// will also be added to the dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001270func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001271 return c.Text(flag + c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001272}
1273
Colin Cross758290d2019-02-01 16:42:32 -08001274// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
1275// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
1276// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001277func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001278 strs := make([]string, len(paths))
1279 for i, path := range paths {
1280 strs[i] = c.addInput(path)
1281 }
1282 return c.FlagWithList(flag, strs, sep)
Colin Crossfeec25b2019-01-30 17:32:39 -08001283}
1284
Colin Cross758290d2019-02-01 16:42:32 -08001285// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
1286// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for
1287// each input path.
Colin Cross69f59a32019-02-15 10:39:37 -08001288func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001289 for _, path := range paths {
1290 c.FlagWithInput(flag, path)
1291 }
1292 return c
1293}
1294
1295// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path
1296// will also be added to the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001297func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001298 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001299 return c.Text(flag + c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001300}
1301
Colin Cross1d2cf042019-03-29 15:33:06 -07001302// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path
1303// will also be added to the outputs returned by RuleBuilder.Outputs.
1304func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
1305 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001306 return c.Text(flag + c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -07001307}
1308
Colin Crossce3a51d2021-03-19 16:22:12 -07001309// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with
1310// no separator between them. The paths will be written to the rspfile. If sbox is enabled, the
1311// rspfile must be outside the sbox directory. The first use of FlagWithRspFileInputList in any
1312// RuleBuilderCommand of a RuleBuilder will use Ninja's rsp file support for the rule, additional
1313// uses will result in an auxiliary rules to write the rspFile contents.
Colin Cross70c47412021-03-12 17:48:14 -08001314func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, rspFile WritablePath, paths Paths) *RuleBuilderCommand {
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001315 // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
1316 // generated.
1317 if paths == nil {
1318 paths = Paths{}
1319 }
1320
Colin Crossce3a51d2021-03-19 16:22:12 -07001321 c.rspFiles = append(c.rspFiles, rspFileAndPaths{rspFile, paths})
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001322
Colin Cross70c47412021-03-12 17:48:14 -08001323 if c.rule.sbox {
1324 if _, isRel, _ := maybeRelErr(c.rule.outDir.String(), rspFile.String()); isRel {
1325 panic(fmt.Errorf("FlagWithRspFileInputList rspfile %q must not be inside out dir %q",
1326 rspFile.String(), c.rule.outDir.String()))
1327 }
1328 }
1329
Colin Crossab020a72021-03-12 17:52:23 -08001330 c.FlagWithArg(flag, c.PathForInput(rspFile))
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001331 return c
1332}
1333
Colin Cross758290d2019-02-01 16:42:32 -08001334// String returns the command line.
1335func (c *RuleBuilderCommand) String() string {
Colin Crosscfec40c2019-07-08 17:07:18 -07001336 return c.buf.String()
Colin Cross758290d2019-02-01 16:42:32 -08001337}
Colin Cross1d2cf042019-03-29 15:33:06 -07001338
Colin Crosse16ce362020-11-12 08:29:30 -08001339// RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox()
1340// and returns sbox testproto generated by the RuleBuilder.
Colin Crossf61d03d2023-11-02 16:56:39 -07001341func RuleBuilderSboxProtoForTests(t *testing.T, ctx *TestContext, params TestingBuildParams) *sbox_proto.Manifest {
Colin Crosse16ce362020-11-12 08:29:30 -08001342 t.Helper()
Colin Crossf61d03d2023-11-02 16:56:39 -07001343 content := ContentFromFileRuleForTests(t, ctx, params)
Colin Crosse16ce362020-11-12 08:29:30 -08001344 manifest := sbox_proto.Manifest{}
Dan Willemsen4591b642021-05-24 14:24:12 -07001345 err := prototext.Unmarshal([]byte(content), &manifest)
Colin Crosse16ce362020-11-12 08:29:30 -08001346 if err != nil {
1347 t.Fatalf("failed to unmarshal manifest: %s", err.Error())
1348 }
1349 return &manifest
1350}
1351
Colin Cross1d2cf042019-03-29 15:33:06 -07001352func ninjaNameEscape(s string) string {
1353 b := []byte(s)
1354 escaped := false
1355 for i, c := range b {
1356 valid := (c >= 'a' && c <= 'z') ||
1357 (c >= 'A' && c <= 'Z') ||
1358 (c >= '0' && c <= '9') ||
1359 (c == '_') ||
1360 (c == '-') ||
1361 (c == '.')
1362 if !valid {
1363 b[i] = '_'
1364 escaped = true
1365 }
1366 }
1367 if escaped {
1368 s = string(b)
1369 }
1370 return s
1371}
Colin Cross3d680512020-11-13 16:23:53 -08001372
1373// hashSrcFiles returns a hash of the list of source files. It is used to ensure the command line
1374// or the sbox textproto manifest change even if the input files are not listed on the command line.
1375func hashSrcFiles(srcFiles Paths) string {
1376 h := sha256.New()
1377 srcFileList := strings.Join(srcFiles.Strings(), "\n")
1378 h.Write([]byte(srcFileList))
1379 return fmt.Sprintf("%x", h.Sum(nil))
1380}
Colin Crossf1a035e2020-11-16 17:32:30 -08001381
1382// BuilderContextForTesting returns a BuilderContext for the given config that can be used for tests
1383// that need to call methods that take a BuilderContext.
1384func BuilderContextForTesting(config Config) BuilderContext {
1385 pathCtx := PathContextForTesting(config)
1386 return builderContextForTests{
1387 PathContext: pathCtx,
1388 }
1389}
1390
1391type builderContextForTests struct {
1392 PathContext
1393}
1394
1395func (builderContextForTests) Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule {
1396 return nil
1397}
1398func (builderContextForTests) Build(PackageContext, BuildParams) {}
Colin Crossef972742021-03-12 17:24:45 -08001399
Colin Crosse55bd422021-03-23 13:44:30 -07001400func writeRspFileRule(ctx BuilderContext, rspFile WritablePath, paths Paths) {
1401 buf := &strings.Builder{}
1402 err := response.WriteRspFile(buf, paths.Strings())
1403 if err != nil {
1404 // There should never be I/O errors writing to a bytes.Buffer.
1405 panic(err)
Colin Crossef972742021-03-12 17:24:45 -08001406 }
Colin Crosse55bd422021-03-23 13:44:30 -07001407 WriteFileRule(ctx, rspFile, buf.String())
Colin Crossef972742021-03-12 17:24:45 -08001408}