blob: cc6c9b49e975fe1599d5c02553bd1cb4cc2c9af2 [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"
Dan Willemsen633c5022019-04-12 11:11:38 -070030 "android/soong/shared"
Colin Crossfeec25b2019-01-30 17:32:39 -080031)
32
Colin Crosse16ce362020-11-12 08:29:30 -080033const sboxSandboxBaseDir = "__SBOX_SANDBOX_DIR__"
34const sboxOutSubDir = "out"
Colin Crossba9e4032020-11-24 16:32:22 -080035const sboxToolsSubDir = "tools"
Colin Crosse16ce362020-11-12 08:29:30 -080036const sboxOutDir = sboxSandboxBaseDir + "/" + sboxOutSubDir
Colin Cross3d680512020-11-13 16:23:53 -080037
Colin Cross758290d2019-02-01 16:42:32 -080038// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
39// graph.
Colin Crossfeec25b2019-01-30 17:32:39 -080040type RuleBuilder struct {
Colin Crossf1a035e2020-11-16 17:32:30 -080041 pctx PackageContext
42 ctx BuilderContext
43
Colin Crosse16ce362020-11-12 08:29:30 -080044 commands []*RuleBuilderCommand
45 installs RuleBuilderInstalls
46 temporariesSet map[WritablePath]bool
47 restat bool
48 sbox bool
49 highmem bool
50 remoteable RemoteRuleSupports
Colin Crossf1a035e2020-11-16 17:32:30 -080051 outDir WritablePath
Colin Crossba9e4032020-11-24 16:32:22 -080052 sboxTools bool
Colin Crossab020a72021-03-12 17:52:23 -080053 sboxInputs bool
Colin Crosse16ce362020-11-12 08:29:30 -080054 sboxManifestPath WritablePath
55 missingDeps []string
Colin Crossfeec25b2019-01-30 17:32:39 -080056}
57
Colin Cross758290d2019-02-01 16:42:32 -080058// NewRuleBuilder returns a newly created RuleBuilder.
Colin Crossf1a035e2020-11-16 17:32:30 -080059func NewRuleBuilder(pctx PackageContext, ctx BuilderContext) *RuleBuilder {
Colin Cross5cb5b092019-02-02 21:25:18 -080060 return &RuleBuilder{
Colin Crossf1a035e2020-11-16 17:32:30 -080061 pctx: pctx,
62 ctx: ctx,
Colin Cross69f59a32019-02-15 10:39:37 -080063 temporariesSet: make(map[WritablePath]bool),
Colin Cross5cb5b092019-02-02 21:25:18 -080064 }
Colin Cross758290d2019-02-01 16:42:32 -080065}
66
67// RuleBuilderInstall is a tuple of install from and to locations.
68type RuleBuilderInstall struct {
Colin Cross69f59a32019-02-15 10:39:37 -080069 From Path
70 To string
Colin Cross758290d2019-02-01 16:42:32 -080071}
72
Colin Crossdeabb942019-02-11 14:11:09 -080073type RuleBuilderInstalls []RuleBuilderInstall
74
75// String returns the RuleBuilderInstalls in the form used by $(call copy-many-files) in Make, a space separated
76// list of from:to tuples.
77func (installs RuleBuilderInstalls) String() string {
78 sb := strings.Builder{}
79 for i, install := range installs {
80 if i != 0 {
81 sb.WriteRune(' ')
82 }
Colin Cross69f59a32019-02-15 10:39:37 -080083 sb.WriteString(install.From.String())
Colin Crossdeabb942019-02-11 14:11:09 -080084 sb.WriteRune(':')
85 sb.WriteString(install.To)
86 }
87 return sb.String()
88}
89
Colin Cross0d2f40a2019-02-05 22:31:15 -080090// MissingDeps adds modules to the list of missing dependencies. If MissingDeps
91// is called with a non-empty input, any call to Build will result in a rule
92// that will print an error listing the missing dependencies and fail.
93// MissingDeps should only be called if Config.AllowMissingDependencies() is
94// true.
95func (r *RuleBuilder) MissingDeps(missingDeps []string) {
96 r.missingDeps = append(r.missingDeps, missingDeps...)
97}
98
Colin Cross758290d2019-02-01 16:42:32 -080099// 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 -0700100//
101// Restat is not compatible with Sbox()
Colin Crossfeec25b2019-01-30 17:32:39 -0800102func (r *RuleBuilder) Restat() *RuleBuilder {
Dan Willemsen633c5022019-04-12 11:11:38 -0700103 if r.sbox {
104 panic("Restat() is not compatible with Sbox()")
105 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800106 r.restat = true
107 return r
108}
109
Colin Cross8b8bec32019-11-15 13:18:43 -0800110// HighMem marks the rule as a high memory rule, which will limit how many run in parallel with other high memory
111// rules.
112func (r *RuleBuilder) HighMem() *RuleBuilder {
113 r.highmem = true
114 return r
115}
116
117// Remoteable marks the rule as supporting remote execution.
118func (r *RuleBuilder) Remoteable(supports RemoteRuleSupports) *RuleBuilder {
119 r.remoteable = supports
120 return r
121}
122
Colin Crosse16ce362020-11-12 08:29:30 -0800123// Sbox marks the rule as needing to be wrapped by sbox. The outputDir should point to the output
124// directory that sbox will wipe. It should not be written to by any other rule. manifestPath should
125// point to a location where sbox's manifest will be written and must be outside outputDir. sbox
126// will ensure that all outputs have been written, and will discard any output files that were not
127// specified.
Dan Willemsen633c5022019-04-12 11:11:38 -0700128//
129// Sbox is not compatible with Restat()
Colin Crosse16ce362020-11-12 08:29:30 -0800130func (r *RuleBuilder) Sbox(outputDir WritablePath, manifestPath WritablePath) *RuleBuilder {
Dan Willemsen633c5022019-04-12 11:11:38 -0700131 if r.sbox {
132 panic("Sbox() may not be called more than once")
133 }
134 if len(r.commands) > 0 {
135 panic("Sbox() may not be called after Command()")
136 }
137 if r.restat {
138 panic("Sbox() is not compatible with Restat()")
139 }
140 r.sbox = true
Colin Crossf1a035e2020-11-16 17:32:30 -0800141 r.outDir = outputDir
Colin Crosse16ce362020-11-12 08:29:30 -0800142 r.sboxManifestPath = manifestPath
Dan Willemsen633c5022019-04-12 11:11:38 -0700143 return r
144}
145
Colin Crossba9e4032020-11-24 16:32:22 -0800146// SandboxTools enables tool sandboxing for the rule by copying any referenced tools into the
147// sandbox.
148func (r *RuleBuilder) SandboxTools() *RuleBuilder {
149 if !r.sbox {
150 panic("SandboxTools() must be called after Sbox()")
151 }
152 if len(r.commands) > 0 {
153 panic("SandboxTools() may not be called after Command()")
154 }
155 r.sboxTools = true
156 return r
157}
158
Colin Crossab020a72021-03-12 17:52:23 -0800159// SandboxInputs enables input sandboxing for the rule by copying any referenced inputs into the
160// sandbox. It also implies SandboxTools().
161//
162// Sandboxing inputs requires RuleBuilder to be aware of all references to input paths. Paths
163// that are passed to RuleBuilder outside of the methods that expect inputs, for example
164// FlagWithArg, must use RuleBuilderCommand.PathForInput to translate the path to one that matches
165// the sandbox layout.
166func (r *RuleBuilder) SandboxInputs() *RuleBuilder {
167 if !r.sbox {
168 panic("SandboxInputs() must be called after Sbox()")
169 }
170 if len(r.commands) > 0 {
171 panic("SandboxInputs() may not be called after Command()")
172 }
173 r.sboxTools = true
174 r.sboxInputs = true
175 return r
176}
177
Colin Cross758290d2019-02-01 16:42:32 -0800178// Install associates an output of the rule with an install location, which can be retrieved later using
179// RuleBuilder.Installs.
Colin Cross69f59a32019-02-15 10:39:37 -0800180func (r *RuleBuilder) Install(from Path, to string) {
Colin Crossfeec25b2019-01-30 17:32:39 -0800181 r.installs = append(r.installs, RuleBuilderInstall{from, to})
182}
183
Colin Cross758290d2019-02-01 16:42:32 -0800184// Command returns a new RuleBuilderCommand for the rule. The commands will be ordered in the rule by when they were
185// created by this method. That can be mutated through their methods in any order, as long as the mutations do not
186// race with any call to Build.
Colin Crossfeec25b2019-01-30 17:32:39 -0800187func (r *RuleBuilder) Command() *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700188 command := &RuleBuilderCommand{
Colin Crossf1a035e2020-11-16 17:32:30 -0800189 rule: r,
Dan Willemsen633c5022019-04-12 11:11:38 -0700190 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800191 r.commands = append(r.commands, command)
192 return command
193}
194
Colin Cross5cb5b092019-02-02 21:25:18 -0800195// Temporary marks an output of a command as an intermediate file that will be used as an input to another command
196// in the same rule, and should not be listed in Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800197func (r *RuleBuilder) Temporary(path WritablePath) {
Colin Cross5cb5b092019-02-02 21:25:18 -0800198 r.temporariesSet[path] = true
199}
200
201// DeleteTemporaryFiles adds a command to the rule that deletes any outputs that have been marked using Temporary
202// when the rule runs. DeleteTemporaryFiles should be called after all calls to Temporary.
203func (r *RuleBuilder) DeleteTemporaryFiles() {
Colin Cross69f59a32019-02-15 10:39:37 -0800204 var temporariesList WritablePaths
Colin Cross5cb5b092019-02-02 21:25:18 -0800205
206 for intermediate := range r.temporariesSet {
207 temporariesList = append(temporariesList, intermediate)
208 }
Colin Cross69f59a32019-02-15 10:39:37 -0800209
210 sort.Slice(temporariesList, func(i, j int) bool {
211 return temporariesList[i].String() < temporariesList[j].String()
212 })
Colin Cross5cb5b092019-02-02 21:25:18 -0800213
214 r.Command().Text("rm").Flag("-f").Outputs(temporariesList)
215}
216
Colin Crossda71eda2020-02-21 16:55:19 -0800217// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
Colin Cross3d680512020-11-13 16:23:53 -0800218// input paths, such as RuleBuilderCommand.Input, RuleBuilderCommand.Implicit, or
Colin Crossda71eda2020-02-21 16:55:19 -0800219// RuleBuilderCommand.FlagWithInput. Inputs to a command that are also outputs of another command
220// in the same RuleBuilder are filtered out. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800221func (r *RuleBuilder) Inputs() Paths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800222 outputs := r.outputSet()
Dan Willemsen633c5022019-04-12 11:11:38 -0700223 depFiles := r.depFileSet()
Colin Crossfeec25b2019-01-30 17:32:39 -0800224
Colin Cross69f59a32019-02-15 10:39:37 -0800225 inputs := make(map[string]Path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800226 for _, c := range r.commands {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400227 for _, input := range append(c.inputs, c.implicits...) {
Dan Willemsen633c5022019-04-12 11:11:38 -0700228 inputStr := input.String()
229 if _, isOutput := outputs[inputStr]; !isOutput {
230 if _, isDepFile := depFiles[inputStr]; !isDepFile {
231 inputs[input.String()] = input
232 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800233 }
234 }
235 }
236
Colin Cross69f59a32019-02-15 10:39:37 -0800237 var inputList Paths
238 for _, input := range inputs {
Colin Crossfeec25b2019-01-30 17:32:39 -0800239 inputList = append(inputList, input)
240 }
Colin Cross69f59a32019-02-15 10:39:37 -0800241
242 sort.Slice(inputList, func(i, j int) bool {
243 return inputList[i].String() < inputList[j].String()
244 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800245
246 return inputList
247}
248
Colin Crossda71eda2020-02-21 16:55:19 -0800249// OrderOnlys returns the list of paths that were passed to the RuleBuilderCommand.OrderOnly or
250// RuleBuilderCommand.OrderOnlys. The list is sorted and duplicates removed.
251func (r *RuleBuilder) OrderOnlys() Paths {
252 orderOnlys := make(map[string]Path)
253 for _, c := range r.commands {
254 for _, orderOnly := range c.orderOnlys {
255 orderOnlys[orderOnly.String()] = orderOnly
256 }
257 }
258
259 var orderOnlyList Paths
260 for _, orderOnly := range orderOnlys {
261 orderOnlyList = append(orderOnlyList, orderOnly)
262 }
263
264 sort.Slice(orderOnlyList, func(i, j int) bool {
265 return orderOnlyList[i].String() < orderOnlyList[j].String()
266 })
267
268 return orderOnlyList
269}
270
Colin Cross69f59a32019-02-15 10:39:37 -0800271func (r *RuleBuilder) outputSet() map[string]WritablePath {
272 outputs := make(map[string]WritablePath)
Colin Crossfeec25b2019-01-30 17:32:39 -0800273 for _, c := range r.commands {
274 for _, output := range c.outputs {
Colin Cross69f59a32019-02-15 10:39:37 -0800275 outputs[output.String()] = output
Colin Crossfeec25b2019-01-30 17:32:39 -0800276 }
277 }
278 return outputs
279}
280
Colin Crossda71eda2020-02-21 16:55:19 -0800281// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
282// output paths, such as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or
283// RuleBuilderCommand.FlagWithInput. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800284func (r *RuleBuilder) Outputs() WritablePaths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800285 outputs := r.outputSet()
286
Colin Cross69f59a32019-02-15 10:39:37 -0800287 var outputList WritablePaths
288 for _, output := range outputs {
Colin Cross5cb5b092019-02-02 21:25:18 -0800289 if !r.temporariesSet[output] {
290 outputList = append(outputList, output)
291 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800292 }
Colin Cross69f59a32019-02-15 10:39:37 -0800293
294 sort.Slice(outputList, func(i, j int) bool {
295 return outputList[i].String() < outputList[j].String()
296 })
297
Colin Crossfeec25b2019-01-30 17:32:39 -0800298 return outputList
299}
300
Jingwen Chence679d22020-09-23 04:30:02 +0000301func (r *RuleBuilder) symlinkOutputSet() map[string]WritablePath {
302 symlinkOutputs := make(map[string]WritablePath)
303 for _, c := range r.commands {
304 for _, symlinkOutput := range c.symlinkOutputs {
305 symlinkOutputs[symlinkOutput.String()] = symlinkOutput
306 }
307 }
308 return symlinkOutputs
309}
310
311// SymlinkOutputs returns the list of paths that the executor (Ninja) would
312// verify, after build edge completion, that:
313//
314// 1) Created output symlinks match the list of paths in this list exactly (no more, no fewer)
315// 2) Created output files are *not* declared in this list.
316//
317// These symlink outputs are expected to be a subset of outputs or implicit
318// outputs, or they would fail validation at build param construction time
319// later, to support other non-rule-builder approaches for constructing
320// statements.
321func (r *RuleBuilder) SymlinkOutputs() WritablePaths {
322 symlinkOutputs := r.symlinkOutputSet()
323
324 var symlinkOutputList WritablePaths
325 for _, symlinkOutput := range symlinkOutputs {
326 symlinkOutputList = append(symlinkOutputList, symlinkOutput)
327 }
328
329 sort.Slice(symlinkOutputList, func(i, j int) bool {
330 return symlinkOutputList[i].String() < symlinkOutputList[j].String()
331 })
332
333 return symlinkOutputList
334}
335
Dan Willemsen633c5022019-04-12 11:11:38 -0700336func (r *RuleBuilder) depFileSet() map[string]WritablePath {
337 depFiles := make(map[string]WritablePath)
338 for _, c := range r.commands {
339 for _, depFile := range c.depFiles {
340 depFiles[depFile.String()] = depFile
341 }
342 }
343 return depFiles
344}
345
Colin Cross1d2cf042019-03-29 15:33:06 -0700346// DepFiles returns the list of paths that were passed to the RuleBuilderCommand methods that take depfile paths, such
347// as RuleBuilderCommand.DepFile or RuleBuilderCommand.FlagWithDepFile.
348func (r *RuleBuilder) DepFiles() WritablePaths {
349 var depFiles WritablePaths
350
351 for _, c := range r.commands {
352 for _, depFile := range c.depFiles {
353 depFiles = append(depFiles, depFile)
354 }
355 }
356
357 return depFiles
358}
359
Colin Cross758290d2019-02-01 16:42:32 -0800360// Installs returns the list of tuples passed to Install.
Colin Crossdeabb942019-02-11 14:11:09 -0800361func (r *RuleBuilder) Installs() RuleBuilderInstalls {
362 return append(RuleBuilderInstalls(nil), r.installs...)
Colin Crossfeec25b2019-01-30 17:32:39 -0800363}
364
Colin Cross69f59a32019-02-15 10:39:37 -0800365func (r *RuleBuilder) toolsSet() map[string]Path {
366 tools := make(map[string]Path)
Colin Cross5cb5b092019-02-02 21:25:18 -0800367 for _, c := range r.commands {
368 for _, tool := range c.tools {
Colin Cross69f59a32019-02-15 10:39:37 -0800369 tools[tool.String()] = tool
Colin Cross5cb5b092019-02-02 21:25:18 -0800370 }
371 }
372
373 return tools
374}
375
Colin Crossda71eda2020-02-21 16:55:19 -0800376// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method. The
377// list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800378func (r *RuleBuilder) Tools() Paths {
Colin Cross5cb5b092019-02-02 21:25:18 -0800379 toolsSet := r.toolsSet()
380
Colin Cross69f59a32019-02-15 10:39:37 -0800381 var toolsList Paths
382 for _, tool := range toolsSet {
Colin Cross5cb5b092019-02-02 21:25:18 -0800383 toolsList = append(toolsList, tool)
Colin Crossfeec25b2019-01-30 17:32:39 -0800384 }
Colin Cross69f59a32019-02-15 10:39:37 -0800385
386 sort.Slice(toolsList, func(i, j int) bool {
387 return toolsList[i].String() < toolsList[j].String()
388 })
389
Colin Cross5cb5b092019-02-02 21:25:18 -0800390 return toolsList
Colin Crossfeec25b2019-01-30 17:32:39 -0800391}
392
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700393// RspFileInputs returns the list of paths that were passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
394func (r *RuleBuilder) RspFileInputs() Paths {
395 var rspFileInputs Paths
396 for _, c := range r.commands {
397 if c.rspFileInputs != nil {
398 if rspFileInputs != nil {
399 panic("Multiple commands in a rule may not have rsp file inputs")
400 }
401 rspFileInputs = c.rspFileInputs
402 }
403 }
404
405 return rspFileInputs
406}
407
Colin Cross70c47412021-03-12 17:48:14 -0800408// RspFile returns the path to the rspfile that was passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
409func (r *RuleBuilder) RspFile() WritablePath {
410 var rspFile WritablePath
411 for _, c := range r.commands {
412 if c.rspFile != nil {
413 if rspFile != nil {
414 panic("Multiple commands in a rule may not have rsp file inputs")
415 }
416 rspFile = c.rspFile
417 }
418 }
419
420 return rspFile
421}
422
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700423// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
Colin Crossfeec25b2019-01-30 17:32:39 -0800424func (r *RuleBuilder) Commands() []string {
425 var commands []string
426 for _, c := range r.commands {
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700427 commands = append(commands, c.String())
428 }
429 return commands
430}
431
Colin Cross758290d2019-02-01 16:42:32 -0800432// BuilderContext is a subset of ModuleContext and SingletonContext.
Colin Cross786cd6d2019-02-01 16:41:11 -0800433type BuilderContext interface {
434 PathContext
435 Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
436 Build(PackageContext, BuildParams)
437}
438
Colin Cross758290d2019-02-01 16:42:32 -0800439var _ BuilderContext = ModuleContext(nil)
440var _ BuilderContext = SingletonContext(nil)
441
Colin Crossf1a035e2020-11-16 17:32:30 -0800442func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700443 return r.Command().
Colin Crossf1a035e2020-11-16 17:32:30 -0800444 BuiltTool("dep_fixer").
Dan Willemsen633c5022019-04-12 11:11:38 -0700445 Inputs(depFiles.Paths())
Colin Cross1d2cf042019-03-29 15:33:06 -0700446}
447
Colin Crossab020a72021-03-12 17:52:23 -0800448// composeRspFileContent returns a string that will serve as the contents of the rsp file to pass
449// the listed input files to the command running in the sandbox.
450func (r *RuleBuilder) composeRspFileContent(rspFileInputs Paths) string {
451 if r.sboxInputs {
452 if len(rspFileInputs) > 0 {
453 // When SandboxInputs is used the paths need to be rewritten to be relative to the sandbox
454 // directory so that they are valid after sbox chdirs into the sandbox directory.
455 return proptools.NinjaEscape(strings.Join(r.sboxPathsForInputsRel(rspFileInputs), " "))
456 } else {
457 // If the list of inputs is empty fall back to "$in" so that the rspfilecontent Ninja
458 // variable is set to something non-empty, otherwise ninja will complain. The inputs
459 // will be empty (all the non-rspfile inputs are implicits), so $in will evaluate to
460 // an empty string.
461 return "$in"
462 }
463 } else {
464 return "$in"
465 }
466}
467
Colin Cross758290d2019-02-01 16:42:32 -0800468// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
469// Outputs.
Colin Crossf1a035e2020-11-16 17:32:30 -0800470func (r *RuleBuilder) Build(name string, desc string) {
Colin Cross1d2cf042019-03-29 15:33:06 -0700471 name = ninjaNameEscape(name)
472
Colin Cross0d2f40a2019-02-05 22:31:15 -0800473 if len(r.missingDeps) > 0 {
Colin Crossf1a035e2020-11-16 17:32:30 -0800474 r.ctx.Build(pctx, BuildParams{
Colin Cross0d2f40a2019-02-05 22:31:15 -0800475 Rule: ErrorRule,
Colin Cross69f59a32019-02-15 10:39:37 -0800476 Outputs: r.Outputs(),
Colin Crossda71eda2020-02-21 16:55:19 -0800477 OrderOnly: r.OrderOnlys(),
Colin Cross0d2f40a2019-02-05 22:31:15 -0800478 Description: desc,
479 Args: map[string]string{
480 "error": "missing dependencies: " + strings.Join(r.missingDeps, ", "),
481 },
482 })
483 return
484 }
485
Colin Cross1d2cf042019-03-29 15:33:06 -0700486 var depFile WritablePath
487 var depFormat blueprint.Deps
488 if depFiles := r.DepFiles(); len(depFiles) > 0 {
489 depFile = depFiles[0]
490 depFormat = blueprint.DepsGCC
491 if len(depFiles) > 1 {
492 // Add a command locally that merges all depfiles together into the first depfile.
Colin Crossf1a035e2020-11-16 17:32:30 -0800493 r.depFileMergerCmd(depFiles)
Dan Willemsen633c5022019-04-12 11:11:38 -0700494
495 if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800496 // Check for Rel() errors, as all depfiles should be in the output dir. Errors
497 // will be reported to the ctx.
Dan Willemsen633c5022019-04-12 11:11:38 -0700498 for _, path := range depFiles[1:] {
Colin Crossf1a035e2020-11-16 17:32:30 -0800499 Rel(r.ctx, r.outDir.String(), path.String())
Dan Willemsen633c5022019-04-12 11:11:38 -0700500 }
501 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700502 }
503 }
504
Dan Willemsen633c5022019-04-12 11:11:38 -0700505 tools := r.Tools()
Colin Crossb70a1a92021-03-12 17:51:32 -0800506 commands := r.Commands()
Dan Willemsen633c5022019-04-12 11:11:38 -0700507 outputs := r.Outputs()
Colin Cross3d680512020-11-13 16:23:53 -0800508 inputs := r.Inputs()
Colin Cross70c47412021-03-12 17:48:14 -0800509 rspFileInputs := r.RspFileInputs()
510 rspFilePath := r.RspFile()
Dan Willemsen633c5022019-04-12 11:11:38 -0700511
512 if len(commands) == 0 {
513 return
514 }
515 if len(outputs) == 0 {
516 panic("No outputs specified from any Commands")
517 }
518
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700519 commandString := strings.Join(commands, " && ")
Dan Willemsen633c5022019-04-12 11:11:38 -0700520
521 if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800522 // If running the command inside sbox, write the rule data out to an sbox
523 // manifest.textproto.
524 manifest := sbox_proto.Manifest{}
525 command := sbox_proto.Command{}
526 manifest.Commands = append(manifest.Commands, &command)
527 command.Command = proto.String(commandString)
Colin Cross151b9ff2020-11-12 08:29:30 -0800528
Colin Cross619b9ab2020-11-20 18:44:31 +0000529 if depFile != nil {
Colin Crosse16ce362020-11-12 08:29:30 -0800530 manifest.OutputDepfile = proto.String(depFile.String())
Colin Cross619b9ab2020-11-20 18:44:31 +0000531 }
532
Colin Crossba9e4032020-11-24 16:32:22 -0800533 // If sandboxing tools is enabled, add copy rules to the manifest to copy each tool
534 // into the sbox directory.
535 if r.sboxTools {
536 for _, tool := range tools {
537 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
538 From: proto.String(tool.String()),
539 To: proto.String(sboxPathForToolRel(r.ctx, tool)),
540 })
541 }
542 for _, c := range r.commands {
543 for _, tool := range c.packagedTools {
544 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
545 From: proto.String(tool.srcPath.String()),
546 To: proto.String(sboxPathForPackagedToolRel(tool)),
547 Executable: proto.Bool(tool.executable),
548 })
549 tools = append(tools, tool.srcPath)
550 }
551 }
552 }
553
Colin Crossab020a72021-03-12 17:52:23 -0800554 // If sandboxing inputs is enabled, add copy rules to the manifest to copy each input
555 // into the sbox directory.
556 if r.sboxInputs {
557 for _, input := range inputs {
558 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
559 From: proto.String(input.String()),
560 To: proto.String(r.sboxPathForInputRel(input)),
561 })
562 }
563
564 // If using an rsp file copy it into the sbox directory.
565 if rspFilePath != nil {
566 command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
567 From: proto.String(rspFilePath.String()),
568 To: proto.String(r.sboxPathForInputRel(rspFilePath)),
569 })
570 }
571
572 command.Chdir = proto.Bool(true)
573 }
574
Colin Crosse16ce362020-11-12 08:29:30 -0800575 // Add copy rules to the manifest to copy each output file from the sbox directory.
Colin Crossba9e4032020-11-24 16:32:22 -0800576 // to the output directory after running the commands.
Colin Crosse16ce362020-11-12 08:29:30 -0800577 sboxOutputs := make([]string, len(outputs))
578 for i, output := range outputs {
Colin Crossf1a035e2020-11-16 17:32:30 -0800579 rel := Rel(r.ctx, r.outDir.String(), output.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800580 sboxOutputs[i] = filepath.Join(sboxOutDir, rel)
581 command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{
582 From: proto.String(filepath.Join(sboxOutSubDir, rel)),
583 To: proto.String(output.String()),
584 })
585 }
Colin Cross619b9ab2020-11-20 18:44:31 +0000586
Colin Cross5334edd2021-03-11 17:18:21 -0800587 // Outputs that were marked Temporary will not be checked that they are in the output
588 // directory by the loop above, check them here.
589 for path := range r.temporariesSet {
590 Rel(r.ctx, r.outDir.String(), path.String())
591 }
592
Colin Crosse16ce362020-11-12 08:29:30 -0800593 // Add a hash of the list of input files to the manifest so that the textproto file
594 // changes when the list of input files changes and causes the sbox rule that
595 // depends on it to rerun.
596 command.InputHash = proto.String(hashSrcFiles(inputs))
Colin Cross619b9ab2020-11-20 18:44:31 +0000597
Colin Crosse16ce362020-11-12 08:29:30 -0800598 // Verify that the manifest textproto is not inside the sbox output directory, otherwise
599 // it will get deleted when the sbox rule clears its output directory.
Colin Crossf1a035e2020-11-16 17:32:30 -0800600 _, manifestInOutDir := MaybeRel(r.ctx, r.outDir.String(), r.sboxManifestPath.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800601 if manifestInOutDir {
Colin Crossf1a035e2020-11-16 17:32:30 -0800602 ReportPathErrorf(r.ctx, "sbox rule %q manifestPath %q must not be in outputDir %q",
603 name, r.sboxManifestPath.String(), r.outDir.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800604 }
605
606 // Create a rule to write the manifest as a the textproto.
Colin Cross1c217fd2021-03-12 17:24:18 -0800607 WriteFileRule(r.ctx, r.sboxManifestPath, proto.MarshalTextString(&manifest))
Colin Crosse16ce362020-11-12 08:29:30 -0800608
609 // Generate a new string to use as the command line of the sbox rule. This uses
610 // a RuleBuilderCommand as a convenience method of building the command line, then
611 // converts it to a string to replace commandString.
Colin Crossf1a035e2020-11-16 17:32:30 -0800612 sboxCmd := &RuleBuilderCommand{
613 rule: &RuleBuilder{
614 ctx: r.ctx,
615 },
616 }
617 sboxCmd.Text("rm -rf").Output(r.outDir)
Colin Crosse16ce362020-11-12 08:29:30 -0800618 sboxCmd.Text("&&")
Colin Crossf1a035e2020-11-16 17:32:30 -0800619 sboxCmd.BuiltTool("sbox").
620 Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
Colin Crosse16ce362020-11-12 08:29:30 -0800621 Flag("--manifest").Input(r.sboxManifestPath)
622
623 // Replace the command string, and add the sbox tool and manifest textproto to the
624 // dependencies of the final sbox rule.
Colin Crosscfec40c2019-07-08 17:07:18 -0700625 commandString = sboxCmd.buf.String()
Dan Willemsen633c5022019-04-12 11:11:38 -0700626 tools = append(tools, sboxCmd.tools...)
Colin Crosse16ce362020-11-12 08:29:30 -0800627 inputs = append(inputs, sboxCmd.inputs...)
Colin Cross3d680512020-11-13 16:23:53 -0800628 } else {
629 // If not using sbox the rule will run the command directly, put the hash of the
630 // list of input files in a comment at the end of the command line to ensure ninja
631 // reruns the rule when the list of input files changes.
632 commandString += " # hash of input list: " + hashSrcFiles(inputs)
Dan Willemsen633c5022019-04-12 11:11:38 -0700633 }
634
Colin Cross1d2cf042019-03-29 15:33:06 -0700635 // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
Colin Cross70c47412021-03-12 17:48:14 -0800636 // ImplicitOutputs. RuleBuilder doesn't use "$out", so the distinction between Outputs and
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700637 // ImplicitOutputs doesn't matter.
Dan Willemsen633c5022019-04-12 11:11:38 -0700638 output := outputs[0]
639 implicitOutputs := outputs[1:]
Colin Cross1d2cf042019-03-29 15:33:06 -0700640
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700641 var rspFile, rspFileContent string
Colin Cross70c47412021-03-12 17:48:14 -0800642 if rspFilePath != nil {
643 rspFile = rspFilePath.String()
Colin Crossab020a72021-03-12 17:52:23 -0800644 rspFileContent = r.composeRspFileContent(rspFileInputs)
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700645 }
646
Colin Cross8b8bec32019-11-15 13:18:43 -0800647 var pool blueprint.Pool
Colin Crossf1a035e2020-11-16 17:32:30 -0800648 if r.ctx.Config().UseGoma() && r.remoteable.Goma {
Colin Cross8b8bec32019-11-15 13:18:43 -0800649 // 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 -0800650 } else if r.ctx.Config().UseRBE() && r.remoteable.RBE {
Ramy Medhat944839a2020-03-31 22:14:52 -0400651 // When USE_RBE=true is set and the rule is supported by RBE, use the remotePool.
652 pool = remotePool
Colin Cross8b8bec32019-11-15 13:18:43 -0800653 } else if r.highmem {
654 pool = highmemPool
Colin Crossf1a035e2020-11-16 17:32:30 -0800655 } else if r.ctx.Config().UseRemoteBuild() {
Colin Cross8b8bec32019-11-15 13:18:43 -0800656 pool = localPool
657 }
658
Colin Crossf1a035e2020-11-16 17:32:30 -0800659 r.ctx.Build(r.pctx, BuildParams{
660 Rule: r.ctx.Rule(pctx, name, blueprint.RuleParams{
Colin Crossb70a1a92021-03-12 17:51:32 -0800661 Command: proptools.NinjaEscape(commandString),
Colin Cross45029782021-03-16 16:49:52 -0700662 CommandDeps: proptools.NinjaEscapeList(tools.Strings()),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700663 Restat: r.restat,
Colin Cross45029782021-03-16 16:49:52 -0700664 Rspfile: proptools.NinjaEscape(rspFile),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700665 RspfileContent: rspFileContent,
Colin Cross8b8bec32019-11-15 13:18:43 -0800666 Pool: pool,
Dan Willemsen633c5022019-04-12 11:11:38 -0700667 }),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700668 Inputs: rspFileInputs,
Colin Cross3d680512020-11-13 16:23:53 -0800669 Implicits: inputs,
Dan Willemsen633c5022019-04-12 11:11:38 -0700670 Output: output,
671 ImplicitOutputs: implicitOutputs,
Jingwen Chence679d22020-09-23 04:30:02 +0000672 SymlinkOutputs: r.SymlinkOutputs(),
Dan Willemsen633c5022019-04-12 11:11:38 -0700673 Depfile: depFile,
674 Deps: depFormat,
675 Description: desc,
676 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800677}
678
Colin Cross758290d2019-02-01 16:42:32 -0800679// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
680// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the
681// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
682// space as a separator from the previous method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800683type RuleBuilderCommand struct {
Colin Crossf1a035e2020-11-16 17:32:30 -0800684 rule *RuleBuilder
685
Jingwen Chence679d22020-09-23 04:30:02 +0000686 buf strings.Builder
687 inputs Paths
688 implicits Paths
689 orderOnlys Paths
690 outputs WritablePaths
691 symlinkOutputs WritablePaths
692 depFiles WritablePaths
693 tools Paths
Colin Crossba9e4032020-11-24 16:32:22 -0800694 packagedTools []PackagingSpec
Jingwen Chence679d22020-09-23 04:30:02 +0000695 rspFileInputs Paths
Colin Cross70c47412021-03-12 17:48:14 -0800696 rspFile WritablePath
Dan Willemsen633c5022019-04-12 11:11:38 -0700697}
698
699func (c *RuleBuilderCommand) addInput(path Path) string {
Dan Willemsen633c5022019-04-12 11:11:38 -0700700 c.inputs = append(c.inputs, path)
Colin Crossab020a72021-03-12 17:52:23 -0800701 return c.PathForInput(path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700702}
703
Colin Crossab020a72021-03-12 17:52:23 -0800704func (c *RuleBuilderCommand) addImplicit(path Path) {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400705 c.implicits = append(c.implicits, path)
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400706}
707
Colin Crossda71eda2020-02-21 16:55:19 -0800708func (c *RuleBuilderCommand) addOrderOnly(path Path) {
709 c.orderOnlys = append(c.orderOnlys, path)
710}
711
Colin Crossab020a72021-03-12 17:52:23 -0800712// PathForInput takes an input path and returns the appropriate path to use on the command line. If
713// sbox was enabled via a call to RuleBuilder.Sbox() and the path was an output path it returns a
714// path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
715// original path.
716func (c *RuleBuilderCommand) PathForInput(path Path) string {
717 if c.rule.sbox {
718 rel, inSandbox := c.rule._sboxPathForInputRel(path)
719 if inSandbox {
720 rel = filepath.Join(sboxSandboxBaseDir, rel)
721 }
722 return rel
723 }
724 return path.String()
725}
726
727// PathsForInputs takes a list of input paths and returns the appropriate paths to use on the
728// command line. If sbox was enabled via a call to RuleBuilder.Sbox() a path was an output path, it
729// returns the path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it
730// returns the original paths.
731func (c *RuleBuilderCommand) PathsForInputs(paths Paths) []string {
732 ret := make([]string, len(paths))
733 for i, path := range paths {
734 ret[i] = c.PathForInput(path)
735 }
736 return ret
737}
738
Colin Crossf1a035e2020-11-16 17:32:30 -0800739// PathForOutput takes an output path and returns the appropriate path to use on the command
740// line. If sbox was enabled via a call to RuleBuilder.Sbox(), it returns a path with the
741// placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
742// original path.
743func (c *RuleBuilderCommand) PathForOutput(path WritablePath) string {
744 if c.rule.sbox {
745 // Errors will be handled in RuleBuilder.Build where we have a context to report them
746 rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
747 return filepath.Join(sboxOutDir, rel)
Dan Willemsen633c5022019-04-12 11:11:38 -0700748 }
749 return path.String()
Colin Crossfeec25b2019-01-30 17:32:39 -0800750}
751
Colin Crossba9e4032020-11-24 16:32:22 -0800752// SboxPathForTool takes a path to a tool, which may be an output file or a source file, and returns
753// the corresponding path for the tool in the sbox sandbox. It assumes that sandboxing and tool
754// sandboxing are enabled.
755func SboxPathForTool(ctx BuilderContext, path Path) string {
756 return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(ctx, path))
757}
758
759func sboxPathForToolRel(ctx BuilderContext, path Path) string {
760 // Errors will be handled in RuleBuilder.Build where we have a context to report them
761 relOut, isRelOut, _ := maybeRelErr(PathForOutput(ctx, "host", ctx.Config().PrebuiltOS()).String(), path.String())
762 if isRelOut {
763 // The tool is in the output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
764 return filepath.Join(sboxToolsSubDir, "out", relOut)
765 }
766 // The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
767 return filepath.Join(sboxToolsSubDir, "src", path.String())
768}
769
Colin Crossab020a72021-03-12 17:52:23 -0800770func (r *RuleBuilder) _sboxPathForInputRel(path Path) (rel string, inSandbox bool) {
771 // Errors will be handled in RuleBuilder.Build where we have a context to report them
772 rel, isRelSboxOut, _ := maybeRelErr(r.outDir.String(), path.String())
773 if isRelSboxOut {
774 return filepath.Join(sboxOutSubDir, rel), true
775 }
776 if r.sboxInputs {
777 // When sandboxing inputs all inputs have to be copied into the sandbox. Input files that
778 // are outputs of other rules could be an arbitrary absolute path if OUT_DIR is set, so they
779 // will be copied to relative paths under __SBOX_OUT_DIR__/out.
780 rel, isRelOut, _ := maybeRelErr(PathForOutput(r.ctx).String(), path.String())
781 if isRelOut {
782 return filepath.Join(sboxOutSubDir, rel), true
783 }
784 }
785 return path.String(), false
786}
787
788func (r *RuleBuilder) sboxPathForInputRel(path Path) string {
789 rel, _ := r._sboxPathForInputRel(path)
790 return rel
791}
792
793func (r *RuleBuilder) sboxPathsForInputsRel(paths Paths) []string {
794 ret := make([]string, len(paths))
795 for i, path := range paths {
796 ret[i] = r.sboxPathForInputRel(path)
797 }
798 return ret
799}
800
Colin Crossba9e4032020-11-24 16:32:22 -0800801// SboxPathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
802// tool after copying it into the sandbox. This can be used on the RuleBuilder command line to
803// reference the tool.
804func SboxPathForPackagedTool(spec PackagingSpec) string {
805 return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
806}
807
808func sboxPathForPackagedToolRel(spec PackagingSpec) string {
809 return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage)
810}
811
812// PathForTool takes a path to a tool, which may be an output file or a source file, and returns
813// the corresponding path for the tool in the sbox sandbox if sbox is enabled, or the original path
814// if it is not. This can be used on the RuleBuilder command line to reference the tool.
815func (c *RuleBuilderCommand) PathForTool(path Path) string {
816 if c.rule.sbox && c.rule.sboxTools {
817 return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path))
818 }
819 return path.String()
820}
821
822// PackagedTool adds the specified tool path to the command line. It can only be used with tool
823// sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox.
824func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand {
825 if !c.rule.sboxTools {
826 panic("PackagedTool() requires SandboxTools()")
827 }
828
829 c.packagedTools = append(c.packagedTools, spec)
830 c.Text(sboxPathForPackagedToolRel(spec))
831 return c
832}
833
834// ImplicitPackagedTool copies the specified tool into the sandbox without modifying the command
835// line. It can only be used with tool sandboxing enabled by SandboxTools().
836func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuilderCommand {
837 if !c.rule.sboxTools {
838 panic("ImplicitPackagedTool() requires SandboxTools()")
839 }
840
841 c.packagedTools = append(c.packagedTools, spec)
842 return c
843}
844
845// ImplicitPackagedTools copies the specified tools into the sandbox without modifying the command
846// line. It can only be used with tool sandboxing enabled by SandboxTools().
847func (c *RuleBuilderCommand) ImplicitPackagedTools(specs []PackagingSpec) *RuleBuilderCommand {
848 if !c.rule.sboxTools {
849 panic("ImplicitPackagedTools() requires SandboxTools()")
850 }
851
852 c.packagedTools = append(c.packagedTools, specs...)
853 return c
854}
855
Colin Cross758290d2019-02-01 16:42:32 -0800856// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
857// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800858func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
Colin Crosscfec40c2019-07-08 17:07:18 -0700859 if c.buf.Len() > 0 {
860 c.buf.WriteByte(' ')
Colin Crossfeec25b2019-01-30 17:32:39 -0800861 }
Colin Crosscfec40c2019-07-08 17:07:18 -0700862 c.buf.WriteString(text)
Colin Crossfeec25b2019-01-30 17:32:39 -0800863 return c
864}
865
Colin Cross758290d2019-02-01 16:42:32 -0800866// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or
867// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800868func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
869 return c.Text(fmt.Sprintf(format, a...))
870}
871
Colin Cross758290d2019-02-01 16:42:32 -0800872// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the
873// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800874func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
875 return c.Text(flag)
876}
877
Colin Crossab054432019-07-15 16:13:59 -0700878// OptionalFlag adds the specified raw text to the command line if it is not nil. The text should not contain input or
879// output paths or the rule will not have them listed in its dependencies or outputs.
880func (c *RuleBuilderCommand) OptionalFlag(flag *string) *RuleBuilderCommand {
881 if flag != nil {
882 c.Text(*flag)
883 }
884
885 return c
886}
887
Colin Cross92b7d582019-03-29 15:32:51 -0700888// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the
889// rule will not have them listed in its dependencies or outputs.
890func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
891 for _, flag := range flags {
892 c.Text(flag)
893 }
894 return c
895}
896
Colin Cross758290d2019-02-01 16:42:32 -0800897// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
898// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
899// outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800900func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
901 return c.Text(flag + arg)
902}
903
Colin Crossc7ed0042019-02-11 14:11:09 -0800904// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to
905// calling FlagWithArg for argument.
906func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand {
907 for _, arg := range args {
908 c.FlagWithArg(flag, arg)
909 }
910 return c
911}
912
Roland Levillain2da5d9a2019-02-27 16:56:41 +0000913// 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 -0800914// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or
915// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800916func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
917 return c.Text(flag + strings.Join(list, sep))
918}
919
Colin Cross758290d2019-02-01 16:42:32 -0800920// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by
921// RuleBuilder.Tools.
Colin Cross69f59a32019-02-15 10:39:37 -0800922func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800923 c.tools = append(c.tools, path)
Colin Crossba9e4032020-11-24 16:32:22 -0800924 return c.Text(c.PathForTool(path))
925}
926
927// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
928func (c *RuleBuilderCommand) ImplicitTool(path Path) *RuleBuilderCommand {
929 c.tools = append(c.tools, path)
930 return c
931}
932
933// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
934func (c *RuleBuilderCommand) ImplicitTools(paths Paths) *RuleBuilderCommand {
935 c.tools = append(c.tools, paths...)
936 return c
Colin Crossfeec25b2019-01-30 17:32:39 -0800937}
938
Colin Crossee94d6a2019-07-08 17:08:34 -0700939// BuiltTool adds the specified tool path that was built using a host Soong module to the command line. The path will
940// be also added to the dependencies returned by RuleBuilder.Tools.
941//
942// It is equivalent to:
943// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
Colin Crossf1a035e2020-11-16 17:32:30 -0800944func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
945 return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
Colin Crossee94d6a2019-07-08 17:08:34 -0700946}
947
948// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the
949// dependencies returned by RuleBuilder.Tools.
950//
951// It is equivalent to:
952// cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
953func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
954 return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
955}
956
Colin Cross758290d2019-02-01 16:42:32 -0800957// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
958// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800959func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700960 return c.Text(c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800961}
962
Colin Cross758290d2019-02-01 16:42:32 -0800963// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
964// dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800965func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800966 for _, path := range paths {
967 c.Input(path)
968 }
969 return c
970}
971
972// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
973// command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800974func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400975 c.addImplicit(path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800976 return c
977}
978
Colin Cross758290d2019-02-01 16:42:32 -0800979// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
980// command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800981func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700982 for _, path := range paths {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400983 c.addImplicit(path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700984 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800985 return c
986}
987
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400988// GetImplicits returns the command's implicit inputs.
989func (c *RuleBuilderCommand) GetImplicits() Paths {
990 return c.implicits
991}
992
Colin Crossda71eda2020-02-21 16:55:19 -0800993// OrderOnly adds the specified input path to the dependencies returned by RuleBuilder.OrderOnlys
994// without modifying the command line.
995func (c *RuleBuilderCommand) OrderOnly(path Path) *RuleBuilderCommand {
996 c.addOrderOnly(path)
997 return c
998}
999
1000// OrderOnlys adds the specified input paths to the dependencies returned by RuleBuilder.OrderOnlys
1001// without modifying the command line.
1002func (c *RuleBuilderCommand) OrderOnlys(paths Paths) *RuleBuilderCommand {
1003 for _, path := range paths {
1004 c.addOrderOnly(path)
1005 }
1006 return c
1007}
1008
Colin Cross758290d2019-02-01 16:42:32 -08001009// Output adds the specified output path to the command line. The path will also be added to the outputs returned by
1010// RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001011func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001012 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001013 return c.Text(c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001014}
1015
Colin Cross758290d2019-02-01 16:42:32 -08001016// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
1017// the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001018func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001019 for _, path := range paths {
1020 c.Output(path)
1021 }
1022 return c
1023}
1024
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001025// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
1026// and will be the temporary output directory managed by sbox, not the final one.
1027func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand {
Colin Crossf1a035e2020-11-16 17:32:30 -08001028 if !c.rule.sbox {
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001029 panic("OutputDir only valid with Sbox")
1030 }
Colin Cross3d680512020-11-13 16:23:53 -08001031 return c.Text(sboxOutDir)
Dan Willemsen1945a4b2019-06-04 17:10:41 -07001032}
1033
Colin Cross1d2cf042019-03-29 15:33:06 -07001034// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
1035// line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles are added to
1036// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
1037func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
1038 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001039 return c.Text(c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -07001040}
1041
Colin Cross758290d2019-02-01 16:42:32 -08001042// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
1043// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001044func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001045 c.outputs = append(c.outputs, path)
1046 return c
1047}
1048
Colin Cross758290d2019-02-01 16:42:32 -08001049// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
1050// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -08001051func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001052 c.outputs = append(c.outputs, paths...)
1053 return c
1054}
1055
Jingwen Chence679d22020-09-23 04:30:02 +00001056// ImplicitSymlinkOutput declares the specified path as an implicit output that
1057// will be a symlink instead of a regular file. Does not modify the command
1058// line.
1059func (c *RuleBuilderCommand) ImplicitSymlinkOutput(path WritablePath) *RuleBuilderCommand {
1060 c.symlinkOutputs = append(c.symlinkOutputs, path)
1061 return c.ImplicitOutput(path)
1062}
1063
1064// ImplicitSymlinkOutputs declares the specified paths as implicit outputs that
1065// will be a symlinks instead of regular files. Does not modify the command
1066// line.
1067func (c *RuleBuilderCommand) ImplicitSymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
1068 for _, path := range paths {
1069 c.ImplicitSymlinkOutput(path)
1070 }
1071 return c
1072}
1073
1074// SymlinkOutput declares the specified path as an output that will be a symlink
1075// instead of a regular file. Modifies the command line.
1076func (c *RuleBuilderCommand) SymlinkOutput(path WritablePath) *RuleBuilderCommand {
1077 c.symlinkOutputs = append(c.symlinkOutputs, path)
1078 return c.Output(path)
1079}
1080
1081// SymlinkOutputsl declares the specified paths as outputs that will be symlinks
1082// instead of regular files. Modifies the command line.
1083func (c *RuleBuilderCommand) SymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
1084 for _, path := range paths {
1085 c.SymlinkOutput(path)
1086 }
1087 return c
1088}
1089
Colin Cross1d2cf042019-03-29 15:33:06 -07001090// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
1091// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles
1092// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
1093// depfiles together.
1094func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand {
1095 c.depFiles = append(c.depFiles, path)
1096 return c
1097}
1098
Colin Cross758290d2019-02-01 16:42:32 -08001099// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
1100// will also be added to the dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001101func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001102 return c.Text(flag + c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001103}
1104
Colin Cross758290d2019-02-01 16:42:32 -08001105// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
1106// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
1107// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001108func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -07001109 strs := make([]string, len(paths))
1110 for i, path := range paths {
1111 strs[i] = c.addInput(path)
1112 }
1113 return c.FlagWithList(flag, strs, sep)
Colin Crossfeec25b2019-01-30 17:32:39 -08001114}
1115
Colin Cross758290d2019-02-01 16:42:32 -08001116// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
1117// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for
1118// each input path.
Colin Cross69f59a32019-02-15 10:39:37 -08001119func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -08001120 for _, path := range paths {
1121 c.FlagWithInput(flag, path)
1122 }
1123 return c
1124}
1125
1126// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path
1127// will also be added to the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -08001128func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -08001129 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001130 return c.Text(flag + c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -08001131}
1132
Colin Cross1d2cf042019-03-29 15:33:06 -07001133// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path
1134// will also be added to the outputs returned by RuleBuilder.Outputs.
1135func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
1136 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -08001137 return c.Text(flag + c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -07001138}
1139
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001140// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with no separator
Colin Cross70c47412021-03-12 17:48:14 -08001141// between them. The paths will be written to the rspfile. If sbox is enabled, the rspfile must
1142// be outside the sbox directory.
1143func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, rspFile WritablePath, paths Paths) *RuleBuilderCommand {
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001144 if c.rspFileInputs != nil {
1145 panic("FlagWithRspFileInputList cannot be called if rsp file inputs have already been provided")
1146 }
1147
1148 // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
1149 // generated.
1150 if paths == nil {
1151 paths = Paths{}
1152 }
1153
1154 c.rspFileInputs = paths
Colin Cross70c47412021-03-12 17:48:14 -08001155 c.rspFile = rspFile
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001156
Colin Cross70c47412021-03-12 17:48:14 -08001157 if c.rule.sbox {
1158 if _, isRel, _ := maybeRelErr(c.rule.outDir.String(), rspFile.String()); isRel {
1159 panic(fmt.Errorf("FlagWithRspFileInputList rspfile %q must not be inside out dir %q",
1160 rspFile.String(), c.rule.outDir.String()))
1161 }
1162 }
1163
Colin Crossab020a72021-03-12 17:52:23 -08001164 c.FlagWithArg(flag, c.PathForInput(rspFile))
Colin Cross0cb0d7b2019-07-11 10:59:15 -07001165 return c
1166}
1167
Colin Cross758290d2019-02-01 16:42:32 -08001168// String returns the command line.
1169func (c *RuleBuilderCommand) String() string {
Colin Crosscfec40c2019-07-08 17:07:18 -07001170 return c.buf.String()
Colin Cross758290d2019-02-01 16:42:32 -08001171}
Colin Cross1d2cf042019-03-29 15:33:06 -07001172
Colin Crosse16ce362020-11-12 08:29:30 -08001173// RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox()
1174// and returns sbox testproto generated by the RuleBuilder.
1175func RuleBuilderSboxProtoForTests(t *testing.T, params TestingBuildParams) *sbox_proto.Manifest {
1176 t.Helper()
1177 content := ContentFromFileRuleForTests(t, params)
1178 manifest := sbox_proto.Manifest{}
1179 err := proto.UnmarshalText(content, &manifest)
1180 if err != nil {
1181 t.Fatalf("failed to unmarshal manifest: %s", err.Error())
1182 }
1183 return &manifest
1184}
1185
Colin Cross1d2cf042019-03-29 15:33:06 -07001186func ninjaNameEscape(s string) string {
1187 b := []byte(s)
1188 escaped := false
1189 for i, c := range b {
1190 valid := (c >= 'a' && c <= 'z') ||
1191 (c >= 'A' && c <= 'Z') ||
1192 (c >= '0' && c <= '9') ||
1193 (c == '_') ||
1194 (c == '-') ||
1195 (c == '.')
1196 if !valid {
1197 b[i] = '_'
1198 escaped = true
1199 }
1200 }
1201 if escaped {
1202 s = string(b)
1203 }
1204 return s
1205}
Colin Cross3d680512020-11-13 16:23:53 -08001206
1207// hashSrcFiles returns a hash of the list of source files. It is used to ensure the command line
1208// or the sbox textproto manifest change even if the input files are not listed on the command line.
1209func hashSrcFiles(srcFiles Paths) string {
1210 h := sha256.New()
1211 srcFileList := strings.Join(srcFiles.Strings(), "\n")
1212 h.Write([]byte(srcFileList))
1213 return fmt.Sprintf("%x", h.Sum(nil))
1214}
Colin Crossf1a035e2020-11-16 17:32:30 -08001215
1216// BuilderContextForTesting returns a BuilderContext for the given config that can be used for tests
1217// that need to call methods that take a BuilderContext.
1218func BuilderContextForTesting(config Config) BuilderContext {
1219 pathCtx := PathContextForTesting(config)
1220 return builderContextForTests{
1221 PathContext: pathCtx,
1222 }
1223}
1224
1225type builderContextForTests struct {
1226 PathContext
1227}
1228
1229func (builderContextForTests) Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule {
1230 return nil
1231}
1232func (builderContextForTests) Build(PackageContext, BuildParams) {}