blob: 4813d7a10ca4abb74a5f8f5d7e70ee1fcd4dd755 [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
Colin Crosse16ce362020-11-12 08:29:30 -080025 "github.com/golang/protobuf/proto"
Colin Crossfeec25b2019-01-30 17:32:39 -080026 "github.com/google/blueprint"
27 "github.com/google/blueprint/proptools"
Dan Willemsen633c5022019-04-12 11:11:38 -070028
Colin Crosse16ce362020-11-12 08:29:30 -080029 "android/soong/cmd/sbox/sbox_proto"
Colin Crossef972742021-03-12 17:24:45 -080030 "android/soong/remoteexec"
Colin Crosse55bd422021-03-23 13:44:30 -070031 "android/soong/response"
Dan Willemsen633c5022019-04-12 11:11:38 -070032 "android/soong/shared"
Colin Crossfeec25b2019-01-30 17:32:39 -080033)
34
Colin Crosse16ce362020-11-12 08:29:30 -080035const sboxSandboxBaseDir = "__SBOX_SANDBOX_DIR__"
36const sboxOutSubDir = "out"
Colin Crossba9e4032020-11-24 16:32:22 -080037const sboxToolsSubDir = "tools"
Colin Crosse16ce362020-11-12 08:29:30 -080038const sboxOutDir = sboxSandboxBaseDir + "/" + sboxOutSubDir
Colin Cross3d680512020-11-13 16:23:53 -080039
Colin Cross758290d2019-02-01 16:42:32 -080040// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
41// graph.
Colin Crossfeec25b2019-01-30 17:32:39 -080042type RuleBuilder struct {
Colin Crossf1a035e2020-11-16 17:32:30 -080043 pctx PackageContext
44 ctx BuilderContext
45
Colin Crosse16ce362020-11-12 08:29:30 -080046 commands []*RuleBuilderCommand
47 installs RuleBuilderInstalls
48 temporariesSet map[WritablePath]bool
49 restat bool
50 sbox bool
51 highmem bool
52 remoteable RemoteRuleSupports
Colin Crossef972742021-03-12 17:24:45 -080053 rbeParams *remoteexec.REParams
Colin Crossf1a035e2020-11-16 17:32:30 -080054 outDir WritablePath
Colin Crossba9e4032020-11-24 16:32:22 -080055 sboxTools bool
Colin Crossab020a72021-03-12 17:52:23 -080056 sboxInputs bool
Colin Crosse16ce362020-11-12 08:29:30 -080057 sboxManifestPath WritablePath
58 missingDeps []string
Colin Crossfeec25b2019-01-30 17:32:39 -080059}
60
Colin Cross758290d2019-02-01 16:42:32 -080061// NewRuleBuilder returns a newly created RuleBuilder.
Colin Crossf1a035e2020-11-16 17:32:30 -080062func NewRuleBuilder(pctx PackageContext, ctx BuilderContext) *RuleBuilder {
Colin Cross5cb5b092019-02-02 21:25:18 -080063 return &RuleBuilder{
Colin Crossf1a035e2020-11-16 17:32:30 -080064 pctx: pctx,
65 ctx: ctx,
Colin Cross69f59a32019-02-15 10:39:37 -080066 temporariesSet: make(map[WritablePath]bool),
Colin Cross5cb5b092019-02-02 21:25:18 -080067 }
Colin Cross758290d2019-02-01 16:42:32 -080068}
69
70// RuleBuilderInstall is a tuple of install from and to locations.
71type RuleBuilderInstall struct {
Colin Cross69f59a32019-02-15 10:39:37 -080072 From Path
73 To string
Colin Cross758290d2019-02-01 16:42:32 -080074}
75
Colin Crossdeabb942019-02-11 14:11:09 -080076type RuleBuilderInstalls []RuleBuilderInstall
77
78// String returns the RuleBuilderInstalls in the form used by $(call copy-many-files) in Make, a space separated
79// list of from:to tuples.
80func (installs RuleBuilderInstalls) String() string {
81 sb := strings.Builder{}
82 for i, install := range installs {
83 if i != 0 {
84 sb.WriteRune(' ')
85 }
Colin Cross69f59a32019-02-15 10:39:37 -080086 sb.WriteString(install.From.String())
Colin Crossdeabb942019-02-11 14:11:09 -080087 sb.WriteRune(':')
88 sb.WriteString(install.To)
89 }
90 return sb.String()
91}
92
Colin Cross0d2f40a2019-02-05 22:31:15 -080093// MissingDeps adds modules to the list of missing dependencies. If MissingDeps
94// is called with a non-empty input, any call to Build will result in a rule
95// that will print an error listing the missing dependencies and fail.
96// MissingDeps should only be called if Config.AllowMissingDependencies() is
97// true.
98func (r *RuleBuilder) MissingDeps(missingDeps []string) {
99 r.missingDeps = append(r.missingDeps, missingDeps...)
100}
101
Colin Cross758290d2019-02-01 16:42:32 -0800102// Restat marks the rule as a restat rule, which will be passed to ModuleContext.Rule in BuildParams.Restat.
Dan Willemsen633c5022019-04-12 11:11:38 -0700103//
104// Restat is not compatible with Sbox()
Colin Crossfeec25b2019-01-30 17:32:39 -0800105func (r *RuleBuilder) Restat() *RuleBuilder {
Dan Willemsen633c5022019-04-12 11:11:38 -0700106 if r.sbox {
107 panic("Restat() is not compatible with Sbox()")
108 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800109 r.restat = true
110 return r
111}
112
Colin Cross8b8bec32019-11-15 13:18:43 -0800113// HighMem marks the rule as a high memory rule, which will limit how many run in parallel with other high memory
114// rules.
115func (r *RuleBuilder) HighMem() *RuleBuilder {
116 r.highmem = true
117 return r
118}
119
120// Remoteable marks the rule as supporting remote execution.
121func (r *RuleBuilder) Remoteable(supports RemoteRuleSupports) *RuleBuilder {
122 r.remoteable = supports
123 return r
124}
125
Colin Crossef972742021-03-12 17:24:45 -0800126// Rewrapper marks the rule as running inside rewrapper using the given params in order to support
127// running on RBE. During RuleBuilder.Build the params will be combined with the inputs, outputs
128// and tools known to RuleBuilder to prepend an appropriate rewrapper command line to the rule's
129// command line.
130func (r *RuleBuilder) Rewrapper(params *remoteexec.REParams) *RuleBuilder {
131 if !r.sboxInputs {
132 panic(fmt.Errorf("RuleBuilder.Rewrapper must be called after RuleBuilder.SandboxInputs"))
133 }
134 r.rbeParams = params
135 return r
136}
137
Colin Crosse16ce362020-11-12 08:29:30 -0800138// Sbox marks the rule as needing to be wrapped by sbox. The outputDir should point to the output
139// directory that sbox will wipe. It should not be written to by any other rule. manifestPath should
140// point to a location where sbox's manifest will be written and must be outside outputDir. sbox
141// will ensure that all outputs have been written, and will discard any output files that were not
142// specified.
Dan Willemsen633c5022019-04-12 11:11:38 -0700143//
144// Sbox is not compatible with Restat()
Colin Crosse16ce362020-11-12 08:29:30 -0800145func (r *RuleBuilder) Sbox(outputDir WritablePath, manifestPath WritablePath) *RuleBuilder {
Dan Willemsen633c5022019-04-12 11:11:38 -0700146 if r.sbox {
147 panic("Sbox() may not be called more than once")
148 }
149 if len(r.commands) > 0 {
150 panic("Sbox() may not be called after Command()")
151 }
152 if r.restat {
153 panic("Sbox() is not compatible with Restat()")
154 }
155 r.sbox = true
Colin Crossf1a035e2020-11-16 17:32:30 -0800156 r.outDir = outputDir
Colin Crosse16ce362020-11-12 08:29:30 -0800157 r.sboxManifestPath = manifestPath
Dan Willemsen633c5022019-04-12 11:11:38 -0700158 return r
159}
160
Colin Crossba9e4032020-11-24 16:32:22 -0800161// SandboxTools enables tool sandboxing for the rule by copying any referenced tools into the
162// sandbox.
163func (r *RuleBuilder) SandboxTools() *RuleBuilder {
164 if !r.sbox {
165 panic("SandboxTools() must be called after Sbox()")
166 }
167 if len(r.commands) > 0 {
168 panic("SandboxTools() may not be called after Command()")
169 }
170 r.sboxTools = true
171 return r
172}
173
Colin Crossab020a72021-03-12 17:52:23 -0800174// SandboxInputs enables input sandboxing for the rule by copying any referenced inputs into the
175// sandbox. It also implies SandboxTools().
176//
177// Sandboxing inputs requires RuleBuilder to be aware of all references to input paths. Paths
178// that are passed to RuleBuilder outside of the methods that expect inputs, for example
179// FlagWithArg, must use RuleBuilderCommand.PathForInput to translate the path to one that matches
180// the sandbox layout.
181func (r *RuleBuilder) SandboxInputs() *RuleBuilder {
182 if !r.sbox {
183 panic("SandboxInputs() must be called after Sbox()")
184 }
185 if len(r.commands) > 0 {
186 panic("SandboxInputs() may not be called after Command()")
187 }
188 r.sboxTools = true
189 r.sboxInputs = true
190 return r
191}
192
Colin Cross758290d2019-02-01 16:42:32 -0800193// Install associates an output of the rule with an install location, which can be retrieved later using
194// RuleBuilder.Installs.
Colin Cross69f59a32019-02-15 10:39:37 -0800195func (r *RuleBuilder) Install(from Path, to string) {
Colin Crossfeec25b2019-01-30 17:32:39 -0800196 r.installs = append(r.installs, RuleBuilderInstall{from, to})
197}
198
Colin Cross758290d2019-02-01 16:42:32 -0800199// Command returns a new RuleBuilderCommand for the rule. The commands will be ordered in the rule by when they were
200// created by this method. That can be mutated through their methods in any order, as long as the mutations do not
201// race with any call to Build.
Colin Crossfeec25b2019-01-30 17:32:39 -0800202func (r *RuleBuilder) Command() *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700203 command := &RuleBuilderCommand{
Colin Crossf1a035e2020-11-16 17:32:30 -0800204 rule: r,
Dan Willemsen633c5022019-04-12 11:11:38 -0700205 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800206 r.commands = append(r.commands, command)
207 return command
208}
209
Colin Cross5cb5b092019-02-02 21:25:18 -0800210// Temporary marks an output of a command as an intermediate file that will be used as an input to another command
211// in the same rule, and should not be listed in Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800212func (r *RuleBuilder) Temporary(path WritablePath) {
Colin Cross5cb5b092019-02-02 21:25:18 -0800213 r.temporariesSet[path] = true
214}
215
216// DeleteTemporaryFiles adds a command to the rule that deletes any outputs that have been marked using Temporary
217// when the rule runs. DeleteTemporaryFiles should be called after all calls to Temporary.
218func (r *RuleBuilder) DeleteTemporaryFiles() {
Colin Cross69f59a32019-02-15 10:39:37 -0800219 var temporariesList WritablePaths
Colin Cross5cb5b092019-02-02 21:25:18 -0800220
221 for intermediate := range r.temporariesSet {
222 temporariesList = append(temporariesList, intermediate)
223 }
Colin Cross69f59a32019-02-15 10:39:37 -0800224
225 sort.Slice(temporariesList, func(i, j int) bool {
226 return temporariesList[i].String() < temporariesList[j].String()
227 })
Colin Cross5cb5b092019-02-02 21:25:18 -0800228
229 r.Command().Text("rm").Flag("-f").Outputs(temporariesList)
230}
231
Colin Crossda71eda2020-02-21 16:55:19 -0800232// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
Colin Cross3d680512020-11-13 16:23:53 -0800233// input paths, such as RuleBuilderCommand.Input, RuleBuilderCommand.Implicit, or
Colin Crossda71eda2020-02-21 16:55:19 -0800234// RuleBuilderCommand.FlagWithInput. Inputs to a command that are also outputs of another command
235// in the same RuleBuilder are filtered out. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800236func (r *RuleBuilder) Inputs() Paths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800237 outputs := r.outputSet()
Dan Willemsen633c5022019-04-12 11:11:38 -0700238 depFiles := r.depFileSet()
Colin Crossfeec25b2019-01-30 17:32:39 -0800239
Colin Cross69f59a32019-02-15 10:39:37 -0800240 inputs := make(map[string]Path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800241 for _, c := range r.commands {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400242 for _, input := range append(c.inputs, c.implicits...) {
Dan Willemsen633c5022019-04-12 11:11:38 -0700243 inputStr := input.String()
244 if _, isOutput := outputs[inputStr]; !isOutput {
245 if _, isDepFile := depFiles[inputStr]; !isDepFile {
246 inputs[input.String()] = input
247 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800248 }
249 }
250 }
251
Colin Cross69f59a32019-02-15 10:39:37 -0800252 var inputList Paths
253 for _, input := range inputs {
Colin Crossfeec25b2019-01-30 17:32:39 -0800254 inputList = append(inputList, input)
255 }
Colin Cross69f59a32019-02-15 10:39:37 -0800256
257 sort.Slice(inputList, func(i, j int) bool {
258 return inputList[i].String() < inputList[j].String()
259 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800260
261 return inputList
262}
263
Colin Crossda71eda2020-02-21 16:55:19 -0800264// OrderOnlys returns the list of paths that were passed to the RuleBuilderCommand.OrderOnly or
265// RuleBuilderCommand.OrderOnlys. The list is sorted and duplicates removed.
266func (r *RuleBuilder) OrderOnlys() Paths {
267 orderOnlys := make(map[string]Path)
268 for _, c := range r.commands {
269 for _, orderOnly := range c.orderOnlys {
270 orderOnlys[orderOnly.String()] = orderOnly
271 }
272 }
273
274 var orderOnlyList Paths
275 for _, orderOnly := range orderOnlys {
276 orderOnlyList = append(orderOnlyList, orderOnly)
277 }
278
279 sort.Slice(orderOnlyList, func(i, j int) bool {
280 return orderOnlyList[i].String() < orderOnlyList[j].String()
281 })
282
283 return orderOnlyList
284}
285
Colin Cross69f59a32019-02-15 10:39:37 -0800286func (r *RuleBuilder) outputSet() map[string]WritablePath {
287 outputs := make(map[string]WritablePath)
Colin Crossfeec25b2019-01-30 17:32:39 -0800288 for _, c := range r.commands {
289 for _, output := range c.outputs {
Colin Cross69f59a32019-02-15 10:39:37 -0800290 outputs[output.String()] = output
Colin Crossfeec25b2019-01-30 17:32:39 -0800291 }
292 }
293 return outputs
294}
295
Colin Crossda71eda2020-02-21 16:55:19 -0800296// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
297// output paths, such as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or
298// RuleBuilderCommand.FlagWithInput. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800299func (r *RuleBuilder) Outputs() WritablePaths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800300 outputs := r.outputSet()
301
Colin Cross69f59a32019-02-15 10:39:37 -0800302 var outputList WritablePaths
303 for _, output := range outputs {
Colin Cross5cb5b092019-02-02 21:25:18 -0800304 if !r.temporariesSet[output] {
305 outputList = append(outputList, output)
306 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800307 }
Colin Cross69f59a32019-02-15 10:39:37 -0800308
309 sort.Slice(outputList, func(i, j int) bool {
310 return outputList[i].String() < outputList[j].String()
311 })
312
Colin Crossfeec25b2019-01-30 17:32:39 -0800313 return outputList
314}
315
Jingwen Chence679d22020-09-23 04:30:02 +0000316func (r *RuleBuilder) symlinkOutputSet() map[string]WritablePath {
317 symlinkOutputs := make(map[string]WritablePath)
318 for _, c := range r.commands {
319 for _, symlinkOutput := range c.symlinkOutputs {
320 symlinkOutputs[symlinkOutput.String()] = symlinkOutput
321 }
322 }
323 return symlinkOutputs
324}
325
326// SymlinkOutputs returns the list of paths that the executor (Ninja) would
327// verify, after build edge completion, that:
328//
329// 1) Created output symlinks match the list of paths in this list exactly (no more, no fewer)
330// 2) Created output files are *not* declared in this list.
331//
332// These symlink outputs are expected to be a subset of outputs or implicit
333// outputs, or they would fail validation at build param construction time
334// later, to support other non-rule-builder approaches for constructing
335// statements.
336func (r *RuleBuilder) SymlinkOutputs() WritablePaths {
337 symlinkOutputs := r.symlinkOutputSet()
338
339 var symlinkOutputList WritablePaths
340 for _, symlinkOutput := range symlinkOutputs {
341 symlinkOutputList = append(symlinkOutputList, symlinkOutput)
342 }
343
344 sort.Slice(symlinkOutputList, func(i, j int) bool {
345 return symlinkOutputList[i].String() < symlinkOutputList[j].String()
346 })
347
348 return symlinkOutputList
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 {
412 if c.rspFileInputs != nil {
413 if rspFileInputs != nil {
414 panic("Multiple commands in a rule may not have rsp file inputs")
415 }
416 rspFileInputs = c.rspFileInputs
417 }
418 }
419
420 return rspFileInputs
421}
422
Colin Cross70c47412021-03-12 17:48:14 -0800423// RspFile returns the path to the rspfile that was passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
424func (r *RuleBuilder) RspFile() WritablePath {
425 var rspFile WritablePath
426 for _, c := range r.commands {
427 if c.rspFile != nil {
428 if rspFile != nil {
429 panic("Multiple commands in a rule may not have rsp file inputs")
430 }
431 rspFile = c.rspFile
432 }
433 }
434
435 return rspFile
436}
437
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700438// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
Colin Crossfeec25b2019-01-30 17:32:39 -0800439func (r *RuleBuilder) Commands() []string {
440 var commands []string
441 for _, c := range r.commands {
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700442 commands = append(commands, c.String())
443 }
444 return commands
445}
446
Colin Cross758290d2019-02-01 16:42:32 -0800447// BuilderContext is a subset of ModuleContext and SingletonContext.
Colin Cross786cd6d2019-02-01 16:41:11 -0800448type BuilderContext interface {
449 PathContext
450 Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
451 Build(PackageContext, BuildParams)
452}
453
Colin Cross758290d2019-02-01 16:42:32 -0800454var _ BuilderContext = ModuleContext(nil)
455var _ BuilderContext = SingletonContext(nil)
456
Colin Crossf1a035e2020-11-16 17:32:30 -0800457func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700458 return r.Command().
Colin Crossf1a035e2020-11-16 17:32:30 -0800459 BuiltTool("dep_fixer").
Dan Willemsen633c5022019-04-12 11:11:38 -0700460 Inputs(depFiles.Paths())
Colin Cross1d2cf042019-03-29 15:33:06 -0700461}
462
Colin Cross758290d2019-02-01 16:42:32 -0800463// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
464// Outputs.
Colin Crossf1a035e2020-11-16 17:32:30 -0800465func (r *RuleBuilder) Build(name string, desc string) {
Colin Cross1d2cf042019-03-29 15:33:06 -0700466 name = ninjaNameEscape(name)
467
Colin Cross0d2f40a2019-02-05 22:31:15 -0800468 if len(r.missingDeps) > 0 {
Colin Crossf1a035e2020-11-16 17:32:30 -0800469 r.ctx.Build(pctx, BuildParams{
Colin Cross0d2f40a2019-02-05 22:31:15 -0800470 Rule: ErrorRule,
Colin Cross69f59a32019-02-15 10:39:37 -0800471 Outputs: r.Outputs(),
Colin Crossda71eda2020-02-21 16:55:19 -0800472 OrderOnly: r.OrderOnlys(),
Colin Cross0d2f40a2019-02-05 22:31:15 -0800473 Description: desc,
474 Args: map[string]string{
475 "error": "missing dependencies: " + strings.Join(r.missingDeps, ", "),
476 },
477 })
478 return
479 }
480
Colin Cross1d2cf042019-03-29 15:33:06 -0700481 var depFile WritablePath
482 var depFormat blueprint.Deps
483 if depFiles := r.DepFiles(); len(depFiles) > 0 {
484 depFile = depFiles[0]
485 depFormat = blueprint.DepsGCC
486 if len(depFiles) > 1 {
487 // Add a command locally that merges all depfiles together into the first depfile.
Colin Crossf1a035e2020-11-16 17:32:30 -0800488 r.depFileMergerCmd(depFiles)
Dan Willemsen633c5022019-04-12 11:11:38 -0700489
490 if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800491 // Check for Rel() errors, as all depfiles should be in the output dir. Errors
492 // will be reported to the ctx.
Dan Willemsen633c5022019-04-12 11:11:38 -0700493 for _, path := range depFiles[1:] {
Colin Crossf1a035e2020-11-16 17:32:30 -0800494 Rel(r.ctx, r.outDir.String(), path.String())
Dan Willemsen633c5022019-04-12 11:11:38 -0700495 }
496 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700497 }
498 }
499
Dan Willemsen633c5022019-04-12 11:11:38 -0700500 tools := r.Tools()
Colin Crossb70a1a92021-03-12 17:51:32 -0800501 commands := r.Commands()
Dan Willemsen633c5022019-04-12 11:11:38 -0700502 outputs := r.Outputs()
Colin Cross3d680512020-11-13 16:23:53 -0800503 inputs := r.Inputs()
Colin Cross70c47412021-03-12 17:48:14 -0800504 rspFileInputs := r.RspFileInputs()
505 rspFilePath := r.RspFile()
Dan Willemsen633c5022019-04-12 11:11:38 -0700506
507 if len(commands) == 0 {
508 return
509 }
510 if len(outputs) == 0 {
511 panic("No outputs specified from any Commands")
512 }
513
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700514 commandString := strings.Join(commands, " && ")
Dan Willemsen633c5022019-04-12 11:11:38 -0700515
516 if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800517 // If running the command inside sbox, write the rule data out to an sbox
518 // manifest.textproto.
519 manifest := sbox_proto.Manifest{}
520 command := sbox_proto.Command{}
521 manifest.Commands = append(manifest.Commands, &command)
522 command.Command = proto.String(commandString)
Colin Cross151b9ff2020-11-12 08:29:30 -0800523
Colin Cross619b9ab2020-11-20 18:44:31 +0000524 if depFile != nil {
Colin Crosse16ce362020-11-12 08:29:30 -0800525 manifest.OutputDepfile = proto.String(depFile.String())
Colin Cross619b9ab2020-11-20 18:44:31 +0000526 }
527
Colin Crossba9e4032020-11-24 16:32:22 -0800528 // If sandboxing tools is enabled, add copy rules to the manifest to copy each tool
529 // into the sbox directory.
530 if r.sboxTools {
531 for _, tool := range tools {
532 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
533 From: proto.String(tool.String()),
534 To: proto.String(sboxPathForToolRel(r.ctx, tool)),
535 })
536 }
537 for _, c := range r.commands {
538 for _, tool := range c.packagedTools {
539 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
540 From: proto.String(tool.srcPath.String()),
541 To: proto.String(sboxPathForPackagedToolRel(tool)),
542 Executable: proto.Bool(tool.executable),
543 })
544 tools = append(tools, tool.srcPath)
545 }
546 }
547 }
548
Colin Crossab020a72021-03-12 17:52:23 -0800549 // If sandboxing inputs is enabled, add copy rules to the manifest to copy each input
550 // into the sbox directory.
551 if r.sboxInputs {
552 for _, input := range inputs {
553 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
554 From: proto.String(input.String()),
555 To: proto.String(r.sboxPathForInputRel(input)),
556 })
557 }
558
559 // If using an rsp file copy it into the sbox directory.
560 if rspFilePath != nil {
Colin Crosse55bd422021-03-23 13:44:30 -0700561 command.RspFiles = append(command.RspFiles, &sbox_proto.RspFile{
562 File: proto.String(rspFilePath.String()),
563 // These have to match the logic in sboxPathForInputRel
564 PathMappings: []*sbox_proto.PathMapping{
565 {
566 From: proto.String(r.outDir.String()),
567 To: proto.String(sboxOutSubDir),
568 },
569 {
570 From: proto.String(PathForOutput(r.ctx).String()),
571 To: proto.String(sboxOutSubDir),
572 },
573 },
Colin Crossab020a72021-03-12 17:52:23 -0800574 })
575 }
576
577 command.Chdir = proto.Bool(true)
578 }
579
Colin Crosse16ce362020-11-12 08:29:30 -0800580 // Add copy rules to the manifest to copy each output file from the sbox directory.
Colin Crossba9e4032020-11-24 16:32:22 -0800581 // to the output directory after running the commands.
Colin Crosse16ce362020-11-12 08:29:30 -0800582 sboxOutputs := make([]string, len(outputs))
583 for i, output := range outputs {
Colin Crossf1a035e2020-11-16 17:32:30 -0800584 rel := Rel(r.ctx, r.outDir.String(), output.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800585 sboxOutputs[i] = filepath.Join(sboxOutDir, rel)
586 command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{
587 From: proto.String(filepath.Join(sboxOutSubDir, rel)),
588 To: proto.String(output.String()),
589 })
590 }
Colin Cross619b9ab2020-11-20 18:44:31 +0000591
Colin Cross5334edd2021-03-11 17:18:21 -0800592 // Outputs that were marked Temporary will not be checked that they are in the output
593 // directory by the loop above, check them here.
594 for path := range r.temporariesSet {
595 Rel(r.ctx, r.outDir.String(), path.String())
596 }
597
Colin Crosse16ce362020-11-12 08:29:30 -0800598 // Add a hash of the list of input files to the manifest so that the textproto file
599 // changes when the list of input files changes and causes the sbox rule that
600 // depends on it to rerun.
601 command.InputHash = proto.String(hashSrcFiles(inputs))
Colin Cross619b9ab2020-11-20 18:44:31 +0000602
Colin Crosse16ce362020-11-12 08:29:30 -0800603 // Verify that the manifest textproto is not inside the sbox output directory, otherwise
604 // it will get deleted when the sbox rule clears its output directory.
Colin Crossf1a035e2020-11-16 17:32:30 -0800605 _, manifestInOutDir := MaybeRel(r.ctx, r.outDir.String(), r.sboxManifestPath.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800606 if manifestInOutDir {
Colin Crossf1a035e2020-11-16 17:32:30 -0800607 ReportPathErrorf(r.ctx, "sbox rule %q manifestPath %q must not be in outputDir %q",
608 name, r.sboxManifestPath.String(), r.outDir.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800609 }
610
611 // Create a rule to write the manifest as a the textproto.
Colin Cross1c217fd2021-03-12 17:24:18 -0800612 WriteFileRule(r.ctx, r.sboxManifestPath, proto.MarshalTextString(&manifest))
Colin Crosse16ce362020-11-12 08:29:30 -0800613
614 // Generate a new string to use as the command line of the sbox rule. This uses
615 // a RuleBuilderCommand as a convenience method of building the command line, then
616 // converts it to a string to replace commandString.
Colin Crossf1a035e2020-11-16 17:32:30 -0800617 sboxCmd := &RuleBuilderCommand{
618 rule: &RuleBuilder{
619 ctx: r.ctx,
620 },
621 }
622 sboxCmd.Text("rm -rf").Output(r.outDir)
Colin Crosse16ce362020-11-12 08:29:30 -0800623 sboxCmd.Text("&&")
Colin Crossf1a035e2020-11-16 17:32:30 -0800624 sboxCmd.BuiltTool("sbox").
625 Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
Colin Crosse16ce362020-11-12 08:29:30 -0800626 Flag("--manifest").Input(r.sboxManifestPath)
627
628 // Replace the command string, and add the sbox tool and manifest textproto to the
629 // dependencies of the final sbox rule.
Colin Crosscfec40c2019-07-08 17:07:18 -0700630 commandString = sboxCmd.buf.String()
Dan Willemsen633c5022019-04-12 11:11:38 -0700631 tools = append(tools, sboxCmd.tools...)
Colin Crosse16ce362020-11-12 08:29:30 -0800632 inputs = append(inputs, sboxCmd.inputs...)
Colin Crossef972742021-03-12 17:24:45 -0800633
634 if r.rbeParams != nil {
Colin Crosse55bd422021-03-23 13:44:30 -0700635 // RBE needs a list of input files to copy to the remote builder. For inputs already
636 // listed in an rsp file, pass the rsp file directly to rewrapper. For the rest,
637 // create a new rsp file to pass to rewrapper.
638 var remoteRspFiles Paths
639 var remoteInputs Paths
640
641 remoteInputs = append(remoteInputs, inputs...)
642 remoteInputs = append(remoteInputs, tools...)
643
Colin Crossef972742021-03-12 17:24:45 -0800644 if rspFilePath != nil {
Colin Crosse55bd422021-03-23 13:44:30 -0700645 remoteInputs = append(remoteInputs, rspFilePath)
646 remoteRspFiles = append(remoteRspFiles, rspFilePath)
Colin Crossef972742021-03-12 17:24:45 -0800647 }
Colin Crosse55bd422021-03-23 13:44:30 -0700648
649 if len(remoteInputs) > 0 {
650 inputsListFile := r.sboxManifestPath.ReplaceExtension(r.ctx, "rbe_inputs.list")
651 writeRspFileRule(r.ctx, inputsListFile, remoteInputs)
652 remoteRspFiles = append(remoteRspFiles, inputsListFile)
653 // Add the new rsp file as an extra input to the rule.
654 inputs = append(inputs, inputsListFile)
655 }
Colin Crossef972742021-03-12 17:24:45 -0800656
657 r.rbeParams.OutputFiles = outputs.Strings()
Colin Crosse55bd422021-03-23 13:44:30 -0700658 r.rbeParams.RSPFiles = remoteRspFiles.Strings()
Colin Crossef972742021-03-12 17:24:45 -0800659 rewrapperCommand := r.rbeParams.NoVarTemplate(r.ctx.Config().RBEWrapper())
660 commandString = rewrapperCommand + " bash -c '" + strings.ReplaceAll(commandString, `'`, `'\''`) + "'"
661 }
Colin Cross3d680512020-11-13 16:23:53 -0800662 } else {
663 // If not using sbox the rule will run the command directly, put the hash of the
664 // list of input files in a comment at the end of the command line to ensure ninja
665 // reruns the rule when the list of input files changes.
666 commandString += " # hash of input list: " + hashSrcFiles(inputs)
Dan Willemsen633c5022019-04-12 11:11:38 -0700667 }
668
Colin Cross1d2cf042019-03-29 15:33:06 -0700669 // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
Colin Cross70c47412021-03-12 17:48:14 -0800670 // ImplicitOutputs. RuleBuilder doesn't use "$out", so the distinction between Outputs and
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700671 // ImplicitOutputs doesn't matter.
Dan Willemsen633c5022019-04-12 11:11:38 -0700672 output := outputs[0]
673 implicitOutputs := outputs[1:]
Colin Cross1d2cf042019-03-29 15:33:06 -0700674
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700675 var rspFile, rspFileContent string
Colin Cross70c47412021-03-12 17:48:14 -0800676 if rspFilePath != nil {
677 rspFile = rspFilePath.String()
Colin Crosse55bd422021-03-23 13:44:30 -0700678 // Use "$in" for rspFileContent to avoid duplicating the list of files in the dependency
679 // list and in the contents of the rsp file. Inputs to the rule that are not in the
680 // rsp file will be listed in Implicits instead of Inputs so they don't show up in "$in".
681 rspFileContent = "$in"
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700682 }
683
Colin Cross8b8bec32019-11-15 13:18:43 -0800684 var pool blueprint.Pool
Colin Crossf1a035e2020-11-16 17:32:30 -0800685 if r.ctx.Config().UseGoma() && r.remoteable.Goma {
Colin Cross8b8bec32019-11-15 13:18:43 -0800686 // 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 -0800687 } else if r.ctx.Config().UseRBE() && r.remoteable.RBE {
Ramy Medhat944839a2020-03-31 22:14:52 -0400688 // When USE_RBE=true is set and the rule is supported by RBE, use the remotePool.
689 pool = remotePool
Colin Cross8b8bec32019-11-15 13:18:43 -0800690 } else if r.highmem {
691 pool = highmemPool
Colin Crossf1a035e2020-11-16 17:32:30 -0800692 } else if r.ctx.Config().UseRemoteBuild() {
Colin Cross8b8bec32019-11-15 13:18:43 -0800693 pool = localPool
694 }
695
Colin Crossf1a035e2020-11-16 17:32:30 -0800696 r.ctx.Build(r.pctx, BuildParams{
697 Rule: r.ctx.Rule(pctx, name, blueprint.RuleParams{
Colin Crossb70a1a92021-03-12 17:51:32 -0800698 Command: proptools.NinjaEscape(commandString),
Colin Cross45029782021-03-16 16:49:52 -0700699 CommandDeps: proptools.NinjaEscapeList(tools.Strings()),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700700 Restat: r.restat,
Colin Cross45029782021-03-16 16:49:52 -0700701 Rspfile: proptools.NinjaEscape(rspFile),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700702 RspfileContent: rspFileContent,
Colin Cross8b8bec32019-11-15 13:18:43 -0800703 Pool: pool,
Dan Willemsen633c5022019-04-12 11:11:38 -0700704 }),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700705 Inputs: rspFileInputs,
Colin Cross3d680512020-11-13 16:23:53 -0800706 Implicits: inputs,
Dan Willemsen633c5022019-04-12 11:11:38 -0700707 Output: output,
708 ImplicitOutputs: implicitOutputs,
Jingwen Chence679d22020-09-23 04:30:02 +0000709 SymlinkOutputs: r.SymlinkOutputs(),
Dan Willemsen633c5022019-04-12 11:11:38 -0700710 Depfile: depFile,
711 Deps: depFormat,
712 Description: desc,
713 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800714}
715
Colin Cross758290d2019-02-01 16:42:32 -0800716// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
717// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the
718// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
719// space as a separator from the previous method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800720type RuleBuilderCommand struct {
Colin Crossf1a035e2020-11-16 17:32:30 -0800721 rule *RuleBuilder
722
Jingwen Chence679d22020-09-23 04:30:02 +0000723 buf strings.Builder
724 inputs Paths
725 implicits Paths
726 orderOnlys Paths
727 outputs WritablePaths
728 symlinkOutputs WritablePaths
729 depFiles WritablePaths
730 tools Paths
Colin Crossba9e4032020-11-24 16:32:22 -0800731 packagedTools []PackagingSpec
Jingwen Chence679d22020-09-23 04:30:02 +0000732 rspFileInputs Paths
Colin Cross70c47412021-03-12 17:48:14 -0800733 rspFile WritablePath
Dan Willemsen633c5022019-04-12 11:11:38 -0700734}
735
736func (c *RuleBuilderCommand) addInput(path Path) string {
Dan Willemsen633c5022019-04-12 11:11:38 -0700737 c.inputs = append(c.inputs, path)
Colin Crossab020a72021-03-12 17:52:23 -0800738 return c.PathForInput(path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700739}
740
Colin Crossab020a72021-03-12 17:52:23 -0800741func (c *RuleBuilderCommand) addImplicit(path Path) {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400742 c.implicits = append(c.implicits, path)
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400743}
744
Colin Crossda71eda2020-02-21 16:55:19 -0800745func (c *RuleBuilderCommand) addOrderOnly(path Path) {
746 c.orderOnlys = append(c.orderOnlys, path)
747}
748
Colin Crossab020a72021-03-12 17:52:23 -0800749// PathForInput takes an input path and returns the appropriate path to use on the command line. If
750// sbox was enabled via a call to RuleBuilder.Sbox() and the path was an output path it returns a
751// path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
752// original path.
753func (c *RuleBuilderCommand) PathForInput(path Path) string {
754 if c.rule.sbox {
755 rel, inSandbox := c.rule._sboxPathForInputRel(path)
756 if inSandbox {
757 rel = filepath.Join(sboxSandboxBaseDir, rel)
758 }
759 return rel
760 }
761 return path.String()
762}
763
764// PathsForInputs takes a list of input paths and returns the appropriate paths to use on the
765// command line. If sbox was enabled via a call to RuleBuilder.Sbox() a path was an output path, it
766// returns the path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it
767// returns the original paths.
768func (c *RuleBuilderCommand) PathsForInputs(paths Paths) []string {
769 ret := make([]string, len(paths))
770 for i, path := range paths {
771 ret[i] = c.PathForInput(path)
772 }
773 return ret
774}
775
Colin Crossf1a035e2020-11-16 17:32:30 -0800776// PathForOutput takes an output path and returns the appropriate path to use on the command
777// line. If sbox was enabled via a call to RuleBuilder.Sbox(), it returns a path with the
778// placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
779// original path.
780func (c *RuleBuilderCommand) PathForOutput(path WritablePath) string {
781 if c.rule.sbox {
782 // Errors will be handled in RuleBuilder.Build where we have a context to report them
783 rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
784 return filepath.Join(sboxOutDir, rel)
Dan Willemsen633c5022019-04-12 11:11:38 -0700785 }
786 return path.String()
Colin Crossfeec25b2019-01-30 17:32:39 -0800787}
788
Colin Crossba9e4032020-11-24 16:32:22 -0800789// SboxPathForTool takes a path to a tool, which may be an output file or a source file, and returns
790// the corresponding path for the tool in the sbox sandbox. It assumes that sandboxing and tool
791// sandboxing are enabled.
792func SboxPathForTool(ctx BuilderContext, path Path) string {
793 return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(ctx, path))
794}
795
796func sboxPathForToolRel(ctx BuilderContext, path Path) string {
797 // Errors will be handled in RuleBuilder.Build where we have a context to report them
798 relOut, isRelOut, _ := maybeRelErr(PathForOutput(ctx, "host", ctx.Config().PrebuiltOS()).String(), path.String())
799 if isRelOut {
800 // The tool is in the output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
801 return filepath.Join(sboxToolsSubDir, "out", relOut)
802 }
803 // The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
804 return filepath.Join(sboxToolsSubDir, "src", path.String())
805}
806
Colin Crossab020a72021-03-12 17:52:23 -0800807func (r *RuleBuilder) _sboxPathForInputRel(path Path) (rel string, inSandbox bool) {
808 // Errors will be handled in RuleBuilder.Build where we have a context to report them
809 rel, isRelSboxOut, _ := maybeRelErr(r.outDir.String(), path.String())
810 if isRelSboxOut {
811 return filepath.Join(sboxOutSubDir, rel), true
812 }
813 if r.sboxInputs {
814 // When sandboxing inputs all inputs have to be copied into the sandbox. Input files that
815 // are outputs of other rules could be an arbitrary absolute path if OUT_DIR is set, so they
816 // will be copied to relative paths under __SBOX_OUT_DIR__/out.
817 rel, isRelOut, _ := maybeRelErr(PathForOutput(r.ctx).String(), path.String())
818 if isRelOut {
819 return filepath.Join(sboxOutSubDir, rel), true
820 }
821 }
822 return path.String(), false
823}
824
825func (r *RuleBuilder) sboxPathForInputRel(path Path) string {
826 rel, _ := r._sboxPathForInputRel(path)
827 return rel
828}
829
830func (r *RuleBuilder) sboxPathsForInputsRel(paths Paths) []string {
831 ret := make([]string, len(paths))
832 for i, path := range paths {
833 ret[i] = r.sboxPathForInputRel(path)
834 }
835 return ret
836}
837
Colin Crossba9e4032020-11-24 16:32:22 -0800838// SboxPathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
839// tool after copying it into the sandbox. This can be used on the RuleBuilder command line to
840// reference the tool.
841func SboxPathForPackagedTool(spec PackagingSpec) string {
842 return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
843}
844
845func sboxPathForPackagedToolRel(spec PackagingSpec) string {
846 return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage)
847}
848
849// PathForTool takes a path to a tool, which may be an output file or a source file, and returns
850// the corresponding path for the tool in the sbox sandbox if sbox is enabled, or the original path
851// if it is not. This can be used on the RuleBuilder command line to reference the tool.
852func (c *RuleBuilderCommand) PathForTool(path Path) string {
853 if c.rule.sbox && c.rule.sboxTools {
854 return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path))
855 }
856 return path.String()
857}
858
859// PackagedTool adds the specified tool path to the command line. It can only be used with tool
860// sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox.
861func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand {
862 if !c.rule.sboxTools {
863 panic("PackagedTool() requires SandboxTools()")
864 }
865
866 c.packagedTools = append(c.packagedTools, spec)
867 c.Text(sboxPathForPackagedToolRel(spec))
868 return c
869}
870
871// ImplicitPackagedTool copies the specified tool into the sandbox without modifying the command
872// line. It can only be used with tool sandboxing enabled by SandboxTools().
873func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuilderCommand {
874 if !c.rule.sboxTools {
875 panic("ImplicitPackagedTool() requires SandboxTools()")
876 }
877
878 c.packagedTools = append(c.packagedTools, spec)
879 return c
880}
881
882// ImplicitPackagedTools copies the specified tools into the sandbox without modifying the command
883// line. It can only be used with tool sandboxing enabled by SandboxTools().
884func (c *RuleBuilderCommand) ImplicitPackagedTools(specs []PackagingSpec) *RuleBuilderCommand {
885 if !c.rule.sboxTools {
886 panic("ImplicitPackagedTools() requires SandboxTools()")
887 }
888
889 c.packagedTools = append(c.packagedTools, specs...)
890 return c
891}
892
Colin Cross758290d2019-02-01 16:42:32 -0800893// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
894// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800895func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
Colin Crosscfec40c2019-07-08 17:07:18 -0700896 if c.buf.Len() > 0 {
897 c.buf.WriteByte(' ')
Colin Crossfeec25b2019-01-30 17:32:39 -0800898 }
Colin Crosscfec40c2019-07-08 17:07:18 -0700899 c.buf.WriteString(text)
Colin Crossfeec25b2019-01-30 17:32:39 -0800900 return c
901}
902
Colin Cross758290d2019-02-01 16:42:32 -0800903// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or
904// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800905func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
906 return c.Text(fmt.Sprintf(format, a...))
907}
908
Colin Cross758290d2019-02-01 16:42:32 -0800909// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the
910// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800911func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
912 return c.Text(flag)
913}
914
Colin Crossab054432019-07-15 16:13:59 -0700915// OptionalFlag adds the specified raw text to the command line if it is not nil. The text should not contain input or
916// output paths or the rule will not have them listed in its dependencies or outputs.
917func (c *RuleBuilderCommand) OptionalFlag(flag *string) *RuleBuilderCommand {
918 if flag != nil {
919 c.Text(*flag)
920 }
921
922 return c
923}
924
Colin Cross92b7d582019-03-29 15:32:51 -0700925// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the
926// rule will not have them listed in its dependencies or outputs.
927func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
928 for _, flag := range flags {
929 c.Text(flag)
930 }
931 return c
932}
933
Colin Cross758290d2019-02-01 16:42:32 -0800934// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
935// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
936// outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800937func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
938 return c.Text(flag + arg)
939}
940
Colin Crossc7ed0042019-02-11 14:11:09 -0800941// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to
942// calling FlagWithArg for argument.
943func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand {
944 for _, arg := range args {
945 c.FlagWithArg(flag, arg)
946 }
947 return c
948}
949
Roland Levillain2da5d9a2019-02-27 16:56:41 +0000950// 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 -0800951// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or
952// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800953func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
954 return c.Text(flag + strings.Join(list, sep))
955}
956
Colin Cross758290d2019-02-01 16:42:32 -0800957// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by
958// RuleBuilder.Tools.
Colin Cross69f59a32019-02-15 10:39:37 -0800959func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800960 c.tools = append(c.tools, path)
Colin Crossba9e4032020-11-24 16:32:22 -0800961 return c.Text(c.PathForTool(path))
962}
963
964// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
965func (c *RuleBuilderCommand) ImplicitTool(path Path) *RuleBuilderCommand {
966 c.tools = append(c.tools, path)
967 return c
968}
969
970// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
971func (c *RuleBuilderCommand) ImplicitTools(paths Paths) *RuleBuilderCommand {
972 c.tools = append(c.tools, paths...)
973 return c
Colin Crossfeec25b2019-01-30 17:32:39 -0800974}
975
Colin Crossee94d6a2019-07-08 17:08:34 -0700976// BuiltTool adds the specified tool path that was built using a host Soong module to the command line. The path will
977// be also added to the dependencies returned by RuleBuilder.Tools.
978//
979// It is equivalent to:
980// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
Colin Crossf1a035e2020-11-16 17:32:30 -0800981func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
982 return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
Colin Crossee94d6a2019-07-08 17:08:34 -0700983}
984
985// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the
986// dependencies returned by RuleBuilder.Tools.
987//
988// It is equivalent to:
989// cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
990func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
991 return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
992}
993
Colin Cross758290d2019-02-01 16:42:32 -0800994// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
995// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800996func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700997 return c.Text(c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800998}
999
Colin Cross758290d2019-02-01 16:42:32 -08001000// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
1001// dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001002func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001003 for _, path := range paths {
1004 c.Input(path)
1005 }
1006 return c
1007}
1008
1009// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
1010// command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001011func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001012 c.addImplicit(path)
Colin Crossfeec25b2019-01-30 17:32:39 -08001013 return c
1014}
1015
Colin Cross758290d2019-02-01 16:42:32 -08001016// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
1017// command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001018func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001019 for _, path := range paths {
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001020 c.addImplicit(path)
Dan Willemsen633c5022019-04-12 11:11:38 -07001021 }
Colin Crossfeec25b2019-01-30 17:32:39 -08001022 return c
1023}
1024
Ramy Medhat2f99eec2020-06-13 17:38:27 -04001025// GetImplicits returns the command's implicit inputs.
1026func (c *RuleBuilderCommand) GetImplicits() Paths {
1027 return c.implicits
1028}
1029
Colin Crossda71eda2020-02-21 16:55:19 -08001030// OrderOnly adds the specified input path to the dependencies returned by RuleBuilder.OrderOnlys
1031// without modifying the command line.
1032func (c *RuleBuilderCommand) OrderOnly(path Path) *RuleBuilderCommand {
1033 c.addOrderOnly(path)
1034 return c
1035}
1036
1037// OrderOnlys adds the specified input paths to the dependencies returned by RuleBuilder.OrderOnlys
1038// without modifying the command line.
1039func (c *RuleBuilderCommand) OrderOnlys(paths Paths) *RuleBuilderCommand {
1040 for _, path := range paths {
1041 c.addOrderOnly(path)
1042 }
1043 return c
1044}
1045
Colin Cross758290d2019-02-01 16:42:32 -08001046// Output adds the specified output path to the command line. The path will also be added to the outputs returned by
1047// RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001048func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001049 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001050 return c.Text(c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001051}
1052
Colin Cross758290d2019-02-01 16:42:32 -08001053// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
1054// the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001055func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001056 for _, path := range paths {
1057 c.Output(path)
1058 }
1059 return c
1060}
1061
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001062// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
1063// and will be the temporary output directory managed by sbox, not the final one.
1064func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand {
Colin Crossf1a035e2020-11-16 17:32:30 -08001065 if !c.rule.sbox {
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001066 panic("OutputDir only valid with Sbox")
1067 }
Colin Cross3d680512020-11-13 16:23:53 -08001068 return c.Text(sboxOutDir)
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001069}
1070
Colin Cross1d2cf042019-03-29 15:33:06 -07001071// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
1072// line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles are added to
1073// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
1074func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
1075 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001076 return c.Text(c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -07001077}
1078
Colin Cross758290d2019-02-01 16:42:32 -08001079// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
1080// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001081func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001082 c.outputs = append(c.outputs, path)
1083 return c
1084}
1085
Colin Cross758290d2019-02-01 16:42:32 -08001086// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
1087// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001088func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001089 c.outputs = append(c.outputs, paths...)
1090 return c
1091}
1092
Jingwen Chence679d22020-09-23 04:30:02 +00001093// ImplicitSymlinkOutput declares the specified path as an implicit output that
1094// will be a symlink instead of a regular file. Does not modify the command
1095// line.
1096func (c *RuleBuilderCommand) ImplicitSymlinkOutput(path WritablePath) *RuleBuilderCommand {
1097 c.symlinkOutputs = append(c.symlinkOutputs, path)
1098 return c.ImplicitOutput(path)
1099}
1100
1101// ImplicitSymlinkOutputs declares the specified paths as implicit outputs that
1102// will be a symlinks instead of regular files. Does not modify the command
1103// line.
1104func (c *RuleBuilderCommand) ImplicitSymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
1105 for _, path := range paths {
1106 c.ImplicitSymlinkOutput(path)
1107 }
1108 return c
1109}
1110
1111// SymlinkOutput declares the specified path as an output that will be a symlink
1112// instead of a regular file. Modifies the command line.
1113func (c *RuleBuilderCommand) SymlinkOutput(path WritablePath) *RuleBuilderCommand {
1114 c.symlinkOutputs = append(c.symlinkOutputs, path)
1115 return c.Output(path)
1116}
1117
1118// SymlinkOutputsl declares the specified paths as outputs that will be symlinks
1119// instead of regular files. Modifies the command line.
1120func (c *RuleBuilderCommand) SymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
1121 for _, path := range paths {
1122 c.SymlinkOutput(path)
1123 }
1124 return c
1125}
1126
Colin Cross1d2cf042019-03-29 15:33:06 -07001127// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
1128// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles
1129// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
1130// depfiles together.
1131func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand {
1132 c.depFiles = append(c.depFiles, path)
1133 return c
1134}
1135
Colin Cross758290d2019-02-01 16:42:32 -08001136// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
1137// will also be added to the dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001138func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001139 return c.Text(flag + c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001140}
1141
Colin Cross758290d2019-02-01 16:42:32 -08001142// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
1143// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
1144// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001145func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001146 strs := make([]string, len(paths))
1147 for i, path := range paths {
1148 strs[i] = c.addInput(path)
1149 }
1150 return c.FlagWithList(flag, strs, sep)
Colin Crossfeec25b2019-01-30 17:32:39 -08001151}
1152
Colin Cross758290d2019-02-01 16:42:32 -08001153// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
1154// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for
1155// each input path.
Colin Cross69f59a32019-02-15 10:39:37 -08001156func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001157 for _, path := range paths {
1158 c.FlagWithInput(flag, path)
1159 }
1160 return c
1161}
1162
1163// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path
1164// will also be added to the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001165func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001166 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001167 return c.Text(flag + c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001168}
1169
Colin Cross1d2cf042019-03-29 15:33:06 -07001170// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path
1171// will also be added to the outputs returned by RuleBuilder.Outputs.
1172func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
1173 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001174 return c.Text(flag + c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -07001175}
1176
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001177// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with no separator
Colin Cross70c47412021-03-12 17:48:14 -08001178// between them. The paths will be written to the rspfile. If sbox is enabled, the rspfile must
1179// be outside the sbox directory.
1180func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, rspFile WritablePath, paths Paths) *RuleBuilderCommand {
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001181 if c.rspFileInputs != nil {
1182 panic("FlagWithRspFileInputList cannot be called if rsp file inputs have already been provided")
1183 }
1184
1185 // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
1186 // generated.
1187 if paths == nil {
1188 paths = Paths{}
1189 }
1190
1191 c.rspFileInputs = paths
Colin Cross70c47412021-03-12 17:48:14 -08001192 c.rspFile = rspFile
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001193
Colin Cross70c47412021-03-12 17:48:14 -08001194 if c.rule.sbox {
1195 if _, isRel, _ := maybeRelErr(c.rule.outDir.String(), rspFile.String()); isRel {
1196 panic(fmt.Errorf("FlagWithRspFileInputList rspfile %q must not be inside out dir %q",
1197 rspFile.String(), c.rule.outDir.String()))
1198 }
1199 }
1200
Colin Crossab020a72021-03-12 17:52:23 -08001201 c.FlagWithArg(flag, c.PathForInput(rspFile))
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001202 return c
1203}
1204
Colin Cross758290d2019-02-01 16:42:32 -08001205// String returns the command line.
1206func (c *RuleBuilderCommand) String() string {
Colin Crosscfec40c2019-07-08 17:07:18 -07001207 return c.buf.String()
Colin Cross758290d2019-02-01 16:42:32 -08001208}
Colin Cross1d2cf042019-03-29 15:33:06 -07001209
Colin Crosse16ce362020-11-12 08:29:30 -08001210// RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox()
1211// and returns sbox testproto generated by the RuleBuilder.
1212func RuleBuilderSboxProtoForTests(t *testing.T, params TestingBuildParams) *sbox_proto.Manifest {
1213 t.Helper()
1214 content := ContentFromFileRuleForTests(t, params)
1215 manifest := sbox_proto.Manifest{}
1216 err := proto.UnmarshalText(content, &manifest)
1217 if err != nil {
1218 t.Fatalf("failed to unmarshal manifest: %s", err.Error())
1219 }
1220 return &manifest
1221}
1222
Colin Cross1d2cf042019-03-29 15:33:06 -07001223func ninjaNameEscape(s string) string {
1224 b := []byte(s)
1225 escaped := false
1226 for i, c := range b {
1227 valid := (c >= 'a' && c <= 'z') ||
1228 (c >= 'A' && c <= 'Z') ||
1229 (c >= '0' && c <= '9') ||
1230 (c == '_') ||
1231 (c == '-') ||
1232 (c == '.')
1233 if !valid {
1234 b[i] = '_'
1235 escaped = true
1236 }
1237 }
1238 if escaped {
1239 s = string(b)
1240 }
1241 return s
1242}
Colin Cross3d680512020-11-13 16:23:53 -08001243
1244// hashSrcFiles returns a hash of the list of source files. It is used to ensure the command line
1245// or the sbox textproto manifest change even if the input files are not listed on the command line.
1246func hashSrcFiles(srcFiles Paths) string {
1247 h := sha256.New()
1248 srcFileList := strings.Join(srcFiles.Strings(), "\n")
1249 h.Write([]byte(srcFileList))
1250 return fmt.Sprintf("%x", h.Sum(nil))
1251}
Colin Crossf1a035e2020-11-16 17:32:30 -08001252
1253// BuilderContextForTesting returns a BuilderContext for the given config that can be used for tests
1254// that need to call methods that take a BuilderContext.
1255func BuilderContextForTesting(config Config) BuilderContext {
1256 pathCtx := PathContextForTesting(config)
1257 return builderContextForTests{
1258 PathContext: pathCtx,
1259 }
1260}
1261
1262type builderContextForTests struct {
1263 PathContext
1264}
1265
1266func (builderContextForTests) Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule {
1267 return nil
1268}
1269func (builderContextForTests) Build(PackageContext, BuildParams) {}
Colin Crossef972742021-03-12 17:24:45 -08001270
Colin Crosse55bd422021-03-23 13:44:30 -07001271func writeRspFileRule(ctx BuilderContext, rspFile WritablePath, paths Paths) {
1272 buf := &strings.Builder{}
1273 err := response.WriteRspFile(buf, paths.Strings())
1274 if err != nil {
1275 // There should never be I/O errors writing to a bytes.Buffer.
1276 panic(err)
Colin Crossef972742021-03-12 17:24:45 -08001277 }
Colin Crosse55bd422021-03-23 13:44:30 -07001278 WriteFileRule(ctx, rspFile, buf.String())
Colin Crossef972742021-03-12 17:24:45 -08001279}