blob: e2d81877c4df6be7e0c5b24bbaa8b19a82524642 [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"
35const sboxOutDir = sboxSandboxBaseDir + "/" + sboxOutSubDir
Colin Cross3d680512020-11-13 16:23:53 -080036
Colin Cross758290d2019-02-01 16:42:32 -080037// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
38// graph.
Colin Crossfeec25b2019-01-30 17:32:39 -080039type RuleBuilder struct {
Colin Crossf1a035e2020-11-16 17:32:30 -080040 pctx PackageContext
41 ctx BuilderContext
42
Colin Crosse16ce362020-11-12 08:29:30 -080043 commands []*RuleBuilderCommand
44 installs RuleBuilderInstalls
45 temporariesSet map[WritablePath]bool
46 restat bool
47 sbox bool
48 highmem bool
49 remoteable RemoteRuleSupports
Colin Crossf1a035e2020-11-16 17:32:30 -080050 outDir WritablePath
Colin Crosse16ce362020-11-12 08:29:30 -080051 sboxManifestPath WritablePath
52 missingDeps []string
Colin Crossfeec25b2019-01-30 17:32:39 -080053}
54
Colin Cross758290d2019-02-01 16:42:32 -080055// NewRuleBuilder returns a newly created RuleBuilder.
Colin Crossf1a035e2020-11-16 17:32:30 -080056func NewRuleBuilder(pctx PackageContext, ctx BuilderContext) *RuleBuilder {
Colin Cross5cb5b092019-02-02 21:25:18 -080057 return &RuleBuilder{
Colin Crossf1a035e2020-11-16 17:32:30 -080058 pctx: pctx,
59 ctx: ctx,
Colin Cross69f59a32019-02-15 10:39:37 -080060 temporariesSet: make(map[WritablePath]bool),
Colin Cross5cb5b092019-02-02 21:25:18 -080061 }
Colin Cross758290d2019-02-01 16:42:32 -080062}
63
64// RuleBuilderInstall is a tuple of install from and to locations.
65type RuleBuilderInstall struct {
Colin Cross69f59a32019-02-15 10:39:37 -080066 From Path
67 To string
Colin Cross758290d2019-02-01 16:42:32 -080068}
69
Colin Crossdeabb942019-02-11 14:11:09 -080070type RuleBuilderInstalls []RuleBuilderInstall
71
72// String returns the RuleBuilderInstalls in the form used by $(call copy-many-files) in Make, a space separated
73// list of from:to tuples.
74func (installs RuleBuilderInstalls) String() string {
75 sb := strings.Builder{}
76 for i, install := range installs {
77 if i != 0 {
78 sb.WriteRune(' ')
79 }
Colin Cross69f59a32019-02-15 10:39:37 -080080 sb.WriteString(install.From.String())
Colin Crossdeabb942019-02-11 14:11:09 -080081 sb.WriteRune(':')
82 sb.WriteString(install.To)
83 }
84 return sb.String()
85}
86
Colin Cross0d2f40a2019-02-05 22:31:15 -080087// MissingDeps adds modules to the list of missing dependencies. If MissingDeps
88// is called with a non-empty input, any call to Build will result in a rule
89// that will print an error listing the missing dependencies and fail.
90// MissingDeps should only be called if Config.AllowMissingDependencies() is
91// true.
92func (r *RuleBuilder) MissingDeps(missingDeps []string) {
93 r.missingDeps = append(r.missingDeps, missingDeps...)
94}
95
Colin Cross758290d2019-02-01 16:42:32 -080096// 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 -070097//
98// Restat is not compatible with Sbox()
Colin Crossfeec25b2019-01-30 17:32:39 -080099func (r *RuleBuilder) Restat() *RuleBuilder {
Dan Willemsen633c5022019-04-12 11:11:38 -0700100 if r.sbox {
101 panic("Restat() is not compatible with Sbox()")
102 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800103 r.restat = true
104 return r
105}
106
Colin Cross8b8bec32019-11-15 13:18:43 -0800107// HighMem marks the rule as a high memory rule, which will limit how many run in parallel with other high memory
108// rules.
109func (r *RuleBuilder) HighMem() *RuleBuilder {
110 r.highmem = true
111 return r
112}
113
114// Remoteable marks the rule as supporting remote execution.
115func (r *RuleBuilder) Remoteable(supports RemoteRuleSupports) *RuleBuilder {
116 r.remoteable = supports
117 return r
118}
119
Colin Crosse16ce362020-11-12 08:29:30 -0800120// Sbox marks the rule as needing to be wrapped by sbox. The outputDir should point to the output
121// directory that sbox will wipe. It should not be written to by any other rule. manifestPath should
122// point to a location where sbox's manifest will be written and must be outside outputDir. sbox
123// will ensure that all outputs have been written, and will discard any output files that were not
124// specified.
Dan Willemsen633c5022019-04-12 11:11:38 -0700125//
126// Sbox is not compatible with Restat()
Colin Crosse16ce362020-11-12 08:29:30 -0800127func (r *RuleBuilder) Sbox(outputDir WritablePath, manifestPath WritablePath) *RuleBuilder {
Dan Willemsen633c5022019-04-12 11:11:38 -0700128 if r.sbox {
129 panic("Sbox() may not be called more than once")
130 }
131 if len(r.commands) > 0 {
132 panic("Sbox() may not be called after Command()")
133 }
134 if r.restat {
135 panic("Sbox() is not compatible with Restat()")
136 }
137 r.sbox = true
Colin Crossf1a035e2020-11-16 17:32:30 -0800138 r.outDir = outputDir
Colin Crosse16ce362020-11-12 08:29:30 -0800139 r.sboxManifestPath = manifestPath
Dan Willemsen633c5022019-04-12 11:11:38 -0700140 return r
141}
142
Colin Cross758290d2019-02-01 16:42:32 -0800143// Install associates an output of the rule with an install location, which can be retrieved later using
144// RuleBuilder.Installs.
Colin Cross69f59a32019-02-15 10:39:37 -0800145func (r *RuleBuilder) Install(from Path, to string) {
Colin Crossfeec25b2019-01-30 17:32:39 -0800146 r.installs = append(r.installs, RuleBuilderInstall{from, to})
147}
148
Colin Cross758290d2019-02-01 16:42:32 -0800149// Command returns a new RuleBuilderCommand for the rule. The commands will be ordered in the rule by when they were
150// created by this method. That can be mutated through their methods in any order, as long as the mutations do not
151// race with any call to Build.
Colin Crossfeec25b2019-01-30 17:32:39 -0800152func (r *RuleBuilder) Command() *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700153 command := &RuleBuilderCommand{
Colin Crossf1a035e2020-11-16 17:32:30 -0800154 rule: r,
Dan Willemsen633c5022019-04-12 11:11:38 -0700155 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800156 r.commands = append(r.commands, command)
157 return command
158}
159
Colin Cross5cb5b092019-02-02 21:25:18 -0800160// Temporary marks an output of a command as an intermediate file that will be used as an input to another command
161// in the same rule, and should not be listed in Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800162func (r *RuleBuilder) Temporary(path WritablePath) {
Colin Cross5cb5b092019-02-02 21:25:18 -0800163 r.temporariesSet[path] = true
164}
165
166// DeleteTemporaryFiles adds a command to the rule that deletes any outputs that have been marked using Temporary
167// when the rule runs. DeleteTemporaryFiles should be called after all calls to Temporary.
168func (r *RuleBuilder) DeleteTemporaryFiles() {
Colin Cross69f59a32019-02-15 10:39:37 -0800169 var temporariesList WritablePaths
Colin Cross5cb5b092019-02-02 21:25:18 -0800170
171 for intermediate := range r.temporariesSet {
172 temporariesList = append(temporariesList, intermediate)
173 }
Colin Cross69f59a32019-02-15 10:39:37 -0800174
175 sort.Slice(temporariesList, func(i, j int) bool {
176 return temporariesList[i].String() < temporariesList[j].String()
177 })
Colin Cross5cb5b092019-02-02 21:25:18 -0800178
179 r.Command().Text("rm").Flag("-f").Outputs(temporariesList)
180}
181
Colin Crossda71eda2020-02-21 16:55:19 -0800182// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
Colin Cross3d680512020-11-13 16:23:53 -0800183// input paths, such as RuleBuilderCommand.Input, RuleBuilderCommand.Implicit, or
Colin Crossda71eda2020-02-21 16:55:19 -0800184// RuleBuilderCommand.FlagWithInput. Inputs to a command that are also outputs of another command
185// in the same RuleBuilder are filtered out. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800186func (r *RuleBuilder) Inputs() Paths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800187 outputs := r.outputSet()
Dan Willemsen633c5022019-04-12 11:11:38 -0700188 depFiles := r.depFileSet()
Colin Crossfeec25b2019-01-30 17:32:39 -0800189
Colin Cross69f59a32019-02-15 10:39:37 -0800190 inputs := make(map[string]Path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800191 for _, c := range r.commands {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400192 for _, input := range append(c.inputs, c.implicits...) {
Dan Willemsen633c5022019-04-12 11:11:38 -0700193 inputStr := input.String()
194 if _, isOutput := outputs[inputStr]; !isOutput {
195 if _, isDepFile := depFiles[inputStr]; !isDepFile {
196 inputs[input.String()] = input
197 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800198 }
199 }
200 }
201
Colin Cross69f59a32019-02-15 10:39:37 -0800202 var inputList Paths
203 for _, input := range inputs {
Colin Crossfeec25b2019-01-30 17:32:39 -0800204 inputList = append(inputList, input)
205 }
Colin Cross69f59a32019-02-15 10:39:37 -0800206
207 sort.Slice(inputList, func(i, j int) bool {
208 return inputList[i].String() < inputList[j].String()
209 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800210
211 return inputList
212}
213
Colin Crossda71eda2020-02-21 16:55:19 -0800214// OrderOnlys returns the list of paths that were passed to the RuleBuilderCommand.OrderOnly or
215// RuleBuilderCommand.OrderOnlys. The list is sorted and duplicates removed.
216func (r *RuleBuilder) OrderOnlys() Paths {
217 orderOnlys := make(map[string]Path)
218 for _, c := range r.commands {
219 for _, orderOnly := range c.orderOnlys {
220 orderOnlys[orderOnly.String()] = orderOnly
221 }
222 }
223
224 var orderOnlyList Paths
225 for _, orderOnly := range orderOnlys {
226 orderOnlyList = append(orderOnlyList, orderOnly)
227 }
228
229 sort.Slice(orderOnlyList, func(i, j int) bool {
230 return orderOnlyList[i].String() < orderOnlyList[j].String()
231 })
232
233 return orderOnlyList
234}
235
Colin Cross69f59a32019-02-15 10:39:37 -0800236func (r *RuleBuilder) outputSet() map[string]WritablePath {
237 outputs := make(map[string]WritablePath)
Colin Crossfeec25b2019-01-30 17:32:39 -0800238 for _, c := range r.commands {
239 for _, output := range c.outputs {
Colin Cross69f59a32019-02-15 10:39:37 -0800240 outputs[output.String()] = output
Colin Crossfeec25b2019-01-30 17:32:39 -0800241 }
242 }
243 return outputs
244}
245
Colin Crossda71eda2020-02-21 16:55:19 -0800246// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
247// output paths, such as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or
248// RuleBuilderCommand.FlagWithInput. The list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800249func (r *RuleBuilder) Outputs() WritablePaths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800250 outputs := r.outputSet()
251
Colin Cross69f59a32019-02-15 10:39:37 -0800252 var outputList WritablePaths
253 for _, output := range outputs {
Colin Cross5cb5b092019-02-02 21:25:18 -0800254 if !r.temporariesSet[output] {
255 outputList = append(outputList, output)
256 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800257 }
Colin Cross69f59a32019-02-15 10:39:37 -0800258
259 sort.Slice(outputList, func(i, j int) bool {
260 return outputList[i].String() < outputList[j].String()
261 })
262
Colin Crossfeec25b2019-01-30 17:32:39 -0800263 return outputList
264}
265
Jingwen Chence679d22020-09-23 04:30:02 +0000266func (r *RuleBuilder) symlinkOutputSet() map[string]WritablePath {
267 symlinkOutputs := make(map[string]WritablePath)
268 for _, c := range r.commands {
269 for _, symlinkOutput := range c.symlinkOutputs {
270 symlinkOutputs[symlinkOutput.String()] = symlinkOutput
271 }
272 }
273 return symlinkOutputs
274}
275
276// SymlinkOutputs returns the list of paths that the executor (Ninja) would
277// verify, after build edge completion, that:
278//
279// 1) Created output symlinks match the list of paths in this list exactly (no more, no fewer)
280// 2) Created output files are *not* declared in this list.
281//
282// These symlink outputs are expected to be a subset of outputs or implicit
283// outputs, or they would fail validation at build param construction time
284// later, to support other non-rule-builder approaches for constructing
285// statements.
286func (r *RuleBuilder) SymlinkOutputs() WritablePaths {
287 symlinkOutputs := r.symlinkOutputSet()
288
289 var symlinkOutputList WritablePaths
290 for _, symlinkOutput := range symlinkOutputs {
291 symlinkOutputList = append(symlinkOutputList, symlinkOutput)
292 }
293
294 sort.Slice(symlinkOutputList, func(i, j int) bool {
295 return symlinkOutputList[i].String() < symlinkOutputList[j].String()
296 })
297
298 return symlinkOutputList
299}
300
Dan Willemsen633c5022019-04-12 11:11:38 -0700301func (r *RuleBuilder) depFileSet() map[string]WritablePath {
302 depFiles := make(map[string]WritablePath)
303 for _, c := range r.commands {
304 for _, depFile := range c.depFiles {
305 depFiles[depFile.String()] = depFile
306 }
307 }
308 return depFiles
309}
310
Colin Cross1d2cf042019-03-29 15:33:06 -0700311// DepFiles returns the list of paths that were passed to the RuleBuilderCommand methods that take depfile paths, such
312// as RuleBuilderCommand.DepFile or RuleBuilderCommand.FlagWithDepFile.
313func (r *RuleBuilder) DepFiles() WritablePaths {
314 var depFiles WritablePaths
315
316 for _, c := range r.commands {
317 for _, depFile := range c.depFiles {
318 depFiles = append(depFiles, depFile)
319 }
320 }
321
322 return depFiles
323}
324
Colin Cross758290d2019-02-01 16:42:32 -0800325// Installs returns the list of tuples passed to Install.
Colin Crossdeabb942019-02-11 14:11:09 -0800326func (r *RuleBuilder) Installs() RuleBuilderInstalls {
327 return append(RuleBuilderInstalls(nil), r.installs...)
Colin Crossfeec25b2019-01-30 17:32:39 -0800328}
329
Colin Cross69f59a32019-02-15 10:39:37 -0800330func (r *RuleBuilder) toolsSet() map[string]Path {
331 tools := make(map[string]Path)
Colin Cross5cb5b092019-02-02 21:25:18 -0800332 for _, c := range r.commands {
333 for _, tool := range c.tools {
Colin Cross69f59a32019-02-15 10:39:37 -0800334 tools[tool.String()] = tool
Colin Cross5cb5b092019-02-02 21:25:18 -0800335 }
336 }
337
338 return tools
339}
340
Colin Crossda71eda2020-02-21 16:55:19 -0800341// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method. The
342// list is sorted and duplicates removed.
Colin Cross69f59a32019-02-15 10:39:37 -0800343func (r *RuleBuilder) Tools() Paths {
Colin Cross5cb5b092019-02-02 21:25:18 -0800344 toolsSet := r.toolsSet()
345
Colin Cross69f59a32019-02-15 10:39:37 -0800346 var toolsList Paths
347 for _, tool := range toolsSet {
Colin Cross5cb5b092019-02-02 21:25:18 -0800348 toolsList = append(toolsList, tool)
Colin Crossfeec25b2019-01-30 17:32:39 -0800349 }
Colin Cross69f59a32019-02-15 10:39:37 -0800350
351 sort.Slice(toolsList, func(i, j int) bool {
352 return toolsList[i].String() < toolsList[j].String()
353 })
354
Colin Cross5cb5b092019-02-02 21:25:18 -0800355 return toolsList
Colin Crossfeec25b2019-01-30 17:32:39 -0800356}
357
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700358// RspFileInputs returns the list of paths that were passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
359func (r *RuleBuilder) RspFileInputs() Paths {
360 var rspFileInputs Paths
361 for _, c := range r.commands {
362 if c.rspFileInputs != nil {
363 if rspFileInputs != nil {
364 panic("Multiple commands in a rule may not have rsp file inputs")
365 }
366 rspFileInputs = c.rspFileInputs
367 }
368 }
369
370 return rspFileInputs
371}
372
373// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
Colin Crossfeec25b2019-01-30 17:32:39 -0800374func (r *RuleBuilder) Commands() []string {
375 var commands []string
376 for _, c := range r.commands {
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700377 commands = append(commands, c.String())
378 }
379 return commands
380}
381
Colin Cross3d680512020-11-13 16:23:53 -0800382// NinjaEscapedCommands returns a slice containing the built command line after ninja escaping for each call to
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700383// RuleBuilder.Command.
384func (r *RuleBuilder) NinjaEscapedCommands() []string {
385 var commands []string
386 for _, c := range r.commands {
387 commands = append(commands, c.NinjaEscapedString())
Colin Crossfeec25b2019-01-30 17:32:39 -0800388 }
389 return commands
390}
391
Colin Cross758290d2019-02-01 16:42:32 -0800392// BuilderContext is a subset of ModuleContext and SingletonContext.
Colin Cross786cd6d2019-02-01 16:41:11 -0800393type BuilderContext interface {
394 PathContext
395 Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
396 Build(PackageContext, BuildParams)
397}
398
Colin Cross758290d2019-02-01 16:42:32 -0800399var _ BuilderContext = ModuleContext(nil)
400var _ BuilderContext = SingletonContext(nil)
401
Colin Crossf1a035e2020-11-16 17:32:30 -0800402func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700403 return r.Command().
Colin Crossf1a035e2020-11-16 17:32:30 -0800404 BuiltTool("dep_fixer").
Dan Willemsen633c5022019-04-12 11:11:38 -0700405 Inputs(depFiles.Paths())
Colin Cross1d2cf042019-03-29 15:33:06 -0700406}
407
Colin Cross758290d2019-02-01 16:42:32 -0800408// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
409// Outputs.
Colin Crossf1a035e2020-11-16 17:32:30 -0800410func (r *RuleBuilder) Build(name string, desc string) {
Colin Cross1d2cf042019-03-29 15:33:06 -0700411 name = ninjaNameEscape(name)
412
Colin Cross0d2f40a2019-02-05 22:31:15 -0800413 if len(r.missingDeps) > 0 {
Colin Crossf1a035e2020-11-16 17:32:30 -0800414 r.ctx.Build(pctx, BuildParams{
Colin Cross0d2f40a2019-02-05 22:31:15 -0800415 Rule: ErrorRule,
Colin Cross69f59a32019-02-15 10:39:37 -0800416 Outputs: r.Outputs(),
Colin Crossda71eda2020-02-21 16:55:19 -0800417 OrderOnly: r.OrderOnlys(),
Colin Cross0d2f40a2019-02-05 22:31:15 -0800418 Description: desc,
419 Args: map[string]string{
420 "error": "missing dependencies: " + strings.Join(r.missingDeps, ", "),
421 },
422 })
423 return
424 }
425
Colin Cross1d2cf042019-03-29 15:33:06 -0700426 var depFile WritablePath
427 var depFormat blueprint.Deps
428 if depFiles := r.DepFiles(); len(depFiles) > 0 {
429 depFile = depFiles[0]
430 depFormat = blueprint.DepsGCC
431 if len(depFiles) > 1 {
432 // Add a command locally that merges all depfiles together into the first depfile.
Colin Crossf1a035e2020-11-16 17:32:30 -0800433 r.depFileMergerCmd(depFiles)
Dan Willemsen633c5022019-04-12 11:11:38 -0700434
435 if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800436 // Check for Rel() errors, as all depfiles should be in the output dir. Errors
437 // will be reported to the ctx.
Dan Willemsen633c5022019-04-12 11:11:38 -0700438 for _, path := range depFiles[1:] {
Colin Crossf1a035e2020-11-16 17:32:30 -0800439 Rel(r.ctx, r.outDir.String(), path.String())
Dan Willemsen633c5022019-04-12 11:11:38 -0700440 }
441 }
Colin Cross1d2cf042019-03-29 15:33:06 -0700442 }
443 }
444
Dan Willemsen633c5022019-04-12 11:11:38 -0700445 tools := r.Tools()
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700446 commands := r.NinjaEscapedCommands()
Dan Willemsen633c5022019-04-12 11:11:38 -0700447 outputs := r.Outputs()
Colin Cross3d680512020-11-13 16:23:53 -0800448 inputs := r.Inputs()
Dan Willemsen633c5022019-04-12 11:11:38 -0700449
450 if len(commands) == 0 {
451 return
452 }
453 if len(outputs) == 0 {
454 panic("No outputs specified from any Commands")
455 }
456
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700457 commandString := strings.Join(commands, " && ")
Dan Willemsen633c5022019-04-12 11:11:38 -0700458
459 if r.sbox {
Colin Crosse16ce362020-11-12 08:29:30 -0800460 // If running the command inside sbox, write the rule data out to an sbox
461 // manifest.textproto.
462 manifest := sbox_proto.Manifest{}
463 command := sbox_proto.Command{}
464 manifest.Commands = append(manifest.Commands, &command)
465 command.Command = proto.String(commandString)
Colin Cross151b9ff2020-11-12 08:29:30 -0800466
Colin Cross619b9ab2020-11-20 18:44:31 +0000467 if depFile != nil {
Colin Crosse16ce362020-11-12 08:29:30 -0800468 manifest.OutputDepfile = proto.String(depFile.String())
Colin Cross619b9ab2020-11-20 18:44:31 +0000469 }
470
Colin Crosse16ce362020-11-12 08:29:30 -0800471 // Add copy rules to the manifest to copy each output file from the sbox directory.
472 // to the output directory.
473 sboxOutputs := make([]string, len(outputs))
474 for i, output := range outputs {
Colin Crossf1a035e2020-11-16 17:32:30 -0800475 rel := Rel(r.ctx, r.outDir.String(), output.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800476 sboxOutputs[i] = filepath.Join(sboxOutDir, rel)
477 command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{
478 From: proto.String(filepath.Join(sboxOutSubDir, rel)),
479 To: proto.String(output.String()),
480 })
481 }
Colin Cross619b9ab2020-11-20 18:44:31 +0000482
Colin Crosse16ce362020-11-12 08:29:30 -0800483 // Add a hash of the list of input files to the manifest so that the textproto file
484 // changes when the list of input files changes and causes the sbox rule that
485 // depends on it to rerun.
486 command.InputHash = proto.String(hashSrcFiles(inputs))
Colin Cross619b9ab2020-11-20 18:44:31 +0000487
Colin Crosse16ce362020-11-12 08:29:30 -0800488 // Verify that the manifest textproto is not inside the sbox output directory, otherwise
489 // it will get deleted when the sbox rule clears its output directory.
Colin Crossf1a035e2020-11-16 17:32:30 -0800490 _, manifestInOutDir := MaybeRel(r.ctx, r.outDir.String(), r.sboxManifestPath.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800491 if manifestInOutDir {
Colin Crossf1a035e2020-11-16 17:32:30 -0800492 ReportPathErrorf(r.ctx, "sbox rule %q manifestPath %q must not be in outputDir %q",
493 name, r.sboxManifestPath.String(), r.outDir.String())
Colin Crosse16ce362020-11-12 08:29:30 -0800494 }
495
496 // Create a rule to write the manifest as a the textproto.
Colin Crossf1a035e2020-11-16 17:32:30 -0800497 WriteFileRule(r.ctx, r.sboxManifestPath, proto.MarshalTextString(&manifest))
Colin Crosse16ce362020-11-12 08:29:30 -0800498
499 // Generate a new string to use as the command line of the sbox rule. This uses
500 // a RuleBuilderCommand as a convenience method of building the command line, then
501 // converts it to a string to replace commandString.
Colin Crossf1a035e2020-11-16 17:32:30 -0800502 sboxCmd := &RuleBuilderCommand{
503 rule: &RuleBuilder{
504 ctx: r.ctx,
505 },
506 }
507 sboxCmd.Text("rm -rf").Output(r.outDir)
Colin Crosse16ce362020-11-12 08:29:30 -0800508 sboxCmd.Text("&&")
Colin Crossf1a035e2020-11-16 17:32:30 -0800509 sboxCmd.BuiltTool("sbox").
510 Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
Colin Crosse16ce362020-11-12 08:29:30 -0800511 Flag("--manifest").Input(r.sboxManifestPath)
512
513 // Replace the command string, and add the sbox tool and manifest textproto to the
514 // dependencies of the final sbox rule.
Colin Crosscfec40c2019-07-08 17:07:18 -0700515 commandString = sboxCmd.buf.String()
Dan Willemsen633c5022019-04-12 11:11:38 -0700516 tools = append(tools, sboxCmd.tools...)
Colin Crosse16ce362020-11-12 08:29:30 -0800517 inputs = append(inputs, sboxCmd.inputs...)
Colin Cross3d680512020-11-13 16:23:53 -0800518 } else {
519 // If not using sbox the rule will run the command directly, put the hash of the
520 // list of input files in a comment at the end of the command line to ensure ninja
521 // reruns the rule when the list of input files changes.
522 commandString += " # hash of input list: " + hashSrcFiles(inputs)
Dan Willemsen633c5022019-04-12 11:11:38 -0700523 }
524
Colin Cross1d2cf042019-03-29 15:33:06 -0700525 // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700526 // ImplicitOutputs. RuleBuilder only uses "$out" for the rsp file location, so the distinction between Outputs and
527 // ImplicitOutputs doesn't matter.
Dan Willemsen633c5022019-04-12 11:11:38 -0700528 output := outputs[0]
529 implicitOutputs := outputs[1:]
Colin Cross1d2cf042019-03-29 15:33:06 -0700530
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700531 var rspFile, rspFileContent string
532 rspFileInputs := r.RspFileInputs()
533 if rspFileInputs != nil {
534 rspFile = "$out.rsp"
535 rspFileContent = "$in"
536 }
537
Colin Cross8b8bec32019-11-15 13:18:43 -0800538 var pool blueprint.Pool
Colin Crossf1a035e2020-11-16 17:32:30 -0800539 if r.ctx.Config().UseGoma() && r.remoteable.Goma {
Colin Cross8b8bec32019-11-15 13:18:43 -0800540 // 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 -0800541 } else if r.ctx.Config().UseRBE() && r.remoteable.RBE {
Ramy Medhat944839a2020-03-31 22:14:52 -0400542 // When USE_RBE=true is set and the rule is supported by RBE, use the remotePool.
543 pool = remotePool
Colin Cross8b8bec32019-11-15 13:18:43 -0800544 } else if r.highmem {
545 pool = highmemPool
Colin Crossf1a035e2020-11-16 17:32:30 -0800546 } else if r.ctx.Config().UseRemoteBuild() {
Colin Cross8b8bec32019-11-15 13:18:43 -0800547 pool = localPool
548 }
549
Colin Crossf1a035e2020-11-16 17:32:30 -0800550 r.ctx.Build(r.pctx, BuildParams{
551 Rule: r.ctx.Rule(pctx, name, blueprint.RuleParams{
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700552 Command: commandString,
553 CommandDeps: tools.Strings(),
554 Restat: r.restat,
555 Rspfile: rspFile,
556 RspfileContent: rspFileContent,
Colin Cross8b8bec32019-11-15 13:18:43 -0800557 Pool: pool,
Dan Willemsen633c5022019-04-12 11:11:38 -0700558 }),
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700559 Inputs: rspFileInputs,
Colin Cross3d680512020-11-13 16:23:53 -0800560 Implicits: inputs,
Dan Willemsen633c5022019-04-12 11:11:38 -0700561 Output: output,
562 ImplicitOutputs: implicitOutputs,
Jingwen Chence679d22020-09-23 04:30:02 +0000563 SymlinkOutputs: r.SymlinkOutputs(),
Dan Willemsen633c5022019-04-12 11:11:38 -0700564 Depfile: depFile,
565 Deps: depFormat,
566 Description: desc,
567 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800568}
569
Colin Cross758290d2019-02-01 16:42:32 -0800570// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
571// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the
572// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
573// space as a separator from the previous method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800574type RuleBuilderCommand struct {
Colin Crossf1a035e2020-11-16 17:32:30 -0800575 rule *RuleBuilder
576
Jingwen Chence679d22020-09-23 04:30:02 +0000577 buf strings.Builder
578 inputs Paths
579 implicits Paths
580 orderOnlys Paths
581 outputs WritablePaths
582 symlinkOutputs WritablePaths
583 depFiles WritablePaths
584 tools Paths
585 rspFileInputs Paths
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700586
587 // spans [start,end) of the command that should not be ninja escaped
588 unescapedSpans [][2]int
Dan Willemsen633c5022019-04-12 11:11:38 -0700589}
590
591func (c *RuleBuilderCommand) addInput(path Path) string {
Colin Crossf1a035e2020-11-16 17:32:30 -0800592 if c.rule.sbox {
593 if rel, isRel, _ := maybeRelErr(c.rule.outDir.String(), path.String()); isRel {
Colin Cross3d680512020-11-13 16:23:53 -0800594 return filepath.Join(sboxOutDir, rel)
Dan Willemsen633c5022019-04-12 11:11:38 -0700595 }
596 }
597 c.inputs = append(c.inputs, path)
598 return path.String()
599}
600
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400601func (c *RuleBuilderCommand) addImplicit(path Path) string {
Colin Crossf1a035e2020-11-16 17:32:30 -0800602 if c.rule.sbox {
603 if rel, isRel, _ := maybeRelErr(c.rule.outDir.String(), path.String()); isRel {
Colin Cross3d680512020-11-13 16:23:53 -0800604 return filepath.Join(sboxOutDir, rel)
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400605 }
606 }
607 c.implicits = append(c.implicits, path)
608 return path.String()
609}
610
Colin Crossda71eda2020-02-21 16:55:19 -0800611func (c *RuleBuilderCommand) addOrderOnly(path Path) {
612 c.orderOnlys = append(c.orderOnlys, path)
613}
614
Colin Crossf1a035e2020-11-16 17:32:30 -0800615// PathForOutput takes an output path and returns the appropriate path to use on the command
616// line. If sbox was enabled via a call to RuleBuilder.Sbox(), it returns a path with the
617// placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
618// original path.
619func (c *RuleBuilderCommand) PathForOutput(path WritablePath) string {
620 if c.rule.sbox {
621 // Errors will be handled in RuleBuilder.Build where we have a context to report them
622 rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
623 return filepath.Join(sboxOutDir, rel)
Dan Willemsen633c5022019-04-12 11:11:38 -0700624 }
625 return path.String()
Colin Crossfeec25b2019-01-30 17:32:39 -0800626}
627
Colin Cross758290d2019-02-01 16:42:32 -0800628// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
629// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800630func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
Colin Crosscfec40c2019-07-08 17:07:18 -0700631 if c.buf.Len() > 0 {
632 c.buf.WriteByte(' ')
Colin Crossfeec25b2019-01-30 17:32:39 -0800633 }
Colin Crosscfec40c2019-07-08 17:07:18 -0700634 c.buf.WriteString(text)
Colin Crossfeec25b2019-01-30 17:32:39 -0800635 return c
636}
637
Colin Cross758290d2019-02-01 16:42:32 -0800638// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or
639// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800640func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
641 return c.Text(fmt.Sprintf(format, a...))
642}
643
Colin Cross758290d2019-02-01 16:42:32 -0800644// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the
645// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800646func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
647 return c.Text(flag)
648}
649
Colin Crossab054432019-07-15 16:13:59 -0700650// OptionalFlag adds the specified raw text to the command line if it is not nil. The text should not contain input or
651// output paths or the rule will not have them listed in its dependencies or outputs.
652func (c *RuleBuilderCommand) OptionalFlag(flag *string) *RuleBuilderCommand {
653 if flag != nil {
654 c.Text(*flag)
655 }
656
657 return c
658}
659
Colin Cross92b7d582019-03-29 15:32:51 -0700660// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the
661// rule will not have them listed in its dependencies or outputs.
662func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
663 for _, flag := range flags {
664 c.Text(flag)
665 }
666 return c
667}
668
Colin Cross758290d2019-02-01 16:42:32 -0800669// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
670// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
671// outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800672func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
673 return c.Text(flag + arg)
674}
675
Colin Crossc7ed0042019-02-11 14:11:09 -0800676// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to
677// calling FlagWithArg for argument.
678func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand {
679 for _, arg := range args {
680 c.FlagWithArg(flag, arg)
681 }
682 return c
683}
684
Roland Levillain2da5d9a2019-02-27 16:56:41 +0000685// 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 -0800686// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or
687// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800688func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
689 return c.Text(flag + strings.Join(list, sep))
690}
691
Colin Cross758290d2019-02-01 16:42:32 -0800692// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by
693// RuleBuilder.Tools.
Colin Cross69f59a32019-02-15 10:39:37 -0800694func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800695 c.tools = append(c.tools, path)
Colin Cross69f59a32019-02-15 10:39:37 -0800696 return c.Text(path.String())
Colin Crossfeec25b2019-01-30 17:32:39 -0800697}
698
Colin Crossee94d6a2019-07-08 17:08:34 -0700699// BuiltTool adds the specified tool path that was built using a host Soong module to the command line. The path will
700// be also added to the dependencies returned by RuleBuilder.Tools.
701//
702// It is equivalent to:
703// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
Colin Crossf1a035e2020-11-16 17:32:30 -0800704func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
705 return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
Colin Crossee94d6a2019-07-08 17:08:34 -0700706}
707
708// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the
709// dependencies returned by RuleBuilder.Tools.
710//
711// It is equivalent to:
712// cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
713func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
714 return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
715}
716
Colin Cross758290d2019-02-01 16:42:32 -0800717// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
718// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800719func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700720 return c.Text(c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800721}
722
Colin Cross758290d2019-02-01 16:42:32 -0800723// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
724// dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800725func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800726 for _, path := range paths {
727 c.Input(path)
728 }
729 return c
730}
731
732// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
733// command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800734func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400735 c.addImplicit(path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800736 return c
737}
738
Colin Cross758290d2019-02-01 16:42:32 -0800739// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
740// command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800741func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700742 for _, path := range paths {
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400743 c.addImplicit(path)
Dan Willemsen633c5022019-04-12 11:11:38 -0700744 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800745 return c
746}
747
Ramy Medhat2f99eec2020-06-13 17:38:27 -0400748// GetImplicits returns the command's implicit inputs.
749func (c *RuleBuilderCommand) GetImplicits() Paths {
750 return c.implicits
751}
752
Colin Crossda71eda2020-02-21 16:55:19 -0800753// OrderOnly adds the specified input path to the dependencies returned by RuleBuilder.OrderOnlys
754// without modifying the command line.
755func (c *RuleBuilderCommand) OrderOnly(path Path) *RuleBuilderCommand {
756 c.addOrderOnly(path)
757 return c
758}
759
760// OrderOnlys adds the specified input paths to the dependencies returned by RuleBuilder.OrderOnlys
761// without modifying the command line.
762func (c *RuleBuilderCommand) OrderOnlys(paths Paths) *RuleBuilderCommand {
763 for _, path := range paths {
764 c.addOrderOnly(path)
765 }
766 return c
767}
768
Colin Cross758290d2019-02-01 16:42:32 -0800769// Output adds the specified output path to the command line. The path will also be added to the outputs returned by
770// RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800771func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800772 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -0800773 return c.Text(c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800774}
775
Colin Cross758290d2019-02-01 16:42:32 -0800776// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
777// the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800778func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800779 for _, path := range paths {
780 c.Output(path)
781 }
782 return c
783}
784
Dan Willemsen1945a4b2019-06-04 17:10:41 -0700785// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
786// and will be the temporary output directory managed by sbox, not the final one.
787func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand {
Colin Crossf1a035e2020-11-16 17:32:30 -0800788 if !c.rule.sbox {
Dan Willemsen1945a4b2019-06-04 17:10:41 -0700789 panic("OutputDir only valid with Sbox")
790 }
Colin Cross3d680512020-11-13 16:23:53 -0800791 return c.Text(sboxOutDir)
Dan Willemsen1945a4b2019-06-04 17:10:41 -0700792}
793
Colin Cross1d2cf042019-03-29 15:33:06 -0700794// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
795// line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles are added to
796// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
797func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
798 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -0800799 return c.Text(c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -0700800}
801
Colin Cross758290d2019-02-01 16:42:32 -0800802// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
803// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800804func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800805 c.outputs = append(c.outputs, path)
806 return c
807}
808
Colin Cross758290d2019-02-01 16:42:32 -0800809// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
810// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800811func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800812 c.outputs = append(c.outputs, paths...)
813 return c
814}
815
Jingwen Chence679d22020-09-23 04:30:02 +0000816// ImplicitSymlinkOutput declares the specified path as an implicit output that
817// will be a symlink instead of a regular file. Does not modify the command
818// line.
819func (c *RuleBuilderCommand) ImplicitSymlinkOutput(path WritablePath) *RuleBuilderCommand {
820 c.symlinkOutputs = append(c.symlinkOutputs, path)
821 return c.ImplicitOutput(path)
822}
823
824// ImplicitSymlinkOutputs declares the specified paths as implicit outputs that
825// will be a symlinks instead of regular files. Does not modify the command
826// line.
827func (c *RuleBuilderCommand) ImplicitSymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
828 for _, path := range paths {
829 c.ImplicitSymlinkOutput(path)
830 }
831 return c
832}
833
834// SymlinkOutput declares the specified path as an output that will be a symlink
835// instead of a regular file. Modifies the command line.
836func (c *RuleBuilderCommand) SymlinkOutput(path WritablePath) *RuleBuilderCommand {
837 c.symlinkOutputs = append(c.symlinkOutputs, path)
838 return c.Output(path)
839}
840
841// SymlinkOutputsl declares the specified paths as outputs that will be symlinks
842// instead of regular files. Modifies the command line.
843func (c *RuleBuilderCommand) SymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
844 for _, path := range paths {
845 c.SymlinkOutput(path)
846 }
847 return c
848}
849
Colin Cross1d2cf042019-03-29 15:33:06 -0700850// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
851// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles
852// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
853// depfiles together.
854func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand {
855 c.depFiles = append(c.depFiles, path)
856 return c
857}
858
Colin Cross758290d2019-02-01 16:42:32 -0800859// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
860// will also be added to the dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800861func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700862 return c.Text(flag + c.addInput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800863}
864
Colin Cross758290d2019-02-01 16:42:32 -0800865// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
866// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
867// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800868func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
Dan Willemsen633c5022019-04-12 11:11:38 -0700869 strs := make([]string, len(paths))
870 for i, path := range paths {
871 strs[i] = c.addInput(path)
872 }
873 return c.FlagWithList(flag, strs, sep)
Colin Crossfeec25b2019-01-30 17:32:39 -0800874}
875
Colin Cross758290d2019-02-01 16:42:32 -0800876// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
877// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for
878// each input path.
Colin Cross69f59a32019-02-15 10:39:37 -0800879func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800880 for _, path := range paths {
881 c.FlagWithInput(flag, path)
882 }
883 return c
884}
885
886// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path
887// will also be added to the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800888func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800889 c.outputs = append(c.outputs, path)
Colin Crossf1a035e2020-11-16 17:32:30 -0800890 return c.Text(flag + c.PathForOutput(path))
Colin Crossfeec25b2019-01-30 17:32:39 -0800891}
892
Colin Cross1d2cf042019-03-29 15:33:06 -0700893// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path
894// will also be added to the outputs returned by RuleBuilder.Outputs.
895func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
896 c.depFiles = append(c.depFiles, path)
Colin Crossf1a035e2020-11-16 17:32:30 -0800897 return c.Text(flag + c.PathForOutput(path))
Colin Cross1d2cf042019-03-29 15:33:06 -0700898}
899
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700900// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with no separator
901// between them. The paths will be written to the rspfile.
902func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, paths Paths) *RuleBuilderCommand {
903 if c.rspFileInputs != nil {
904 panic("FlagWithRspFileInputList cannot be called if rsp file inputs have already been provided")
905 }
906
907 // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
908 // generated.
909 if paths == nil {
910 paths = Paths{}
911 }
912
913 c.rspFileInputs = paths
914
915 rspFile := "$out.rsp"
916 c.FlagWithArg(flag, rspFile)
917 c.unescapedSpans = append(c.unescapedSpans, [2]int{c.buf.Len() - len(rspFile), c.buf.Len()})
918 return c
919}
920
Colin Cross758290d2019-02-01 16:42:32 -0800921// String returns the command line.
922func (c *RuleBuilderCommand) String() string {
Colin Crosscfec40c2019-07-08 17:07:18 -0700923 return c.buf.String()
Colin Cross758290d2019-02-01 16:42:32 -0800924}
Colin Cross1d2cf042019-03-29 15:33:06 -0700925
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700926// String returns the command line.
927func (c *RuleBuilderCommand) NinjaEscapedString() string {
928 return ninjaEscapeExceptForSpans(c.String(), c.unescapedSpans)
929}
930
Colin Crosse16ce362020-11-12 08:29:30 -0800931// RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox()
932// and returns sbox testproto generated by the RuleBuilder.
933func RuleBuilderSboxProtoForTests(t *testing.T, params TestingBuildParams) *sbox_proto.Manifest {
934 t.Helper()
935 content := ContentFromFileRuleForTests(t, params)
936 manifest := sbox_proto.Manifest{}
937 err := proto.UnmarshalText(content, &manifest)
938 if err != nil {
939 t.Fatalf("failed to unmarshal manifest: %s", err.Error())
940 }
941 return &manifest
942}
943
Colin Cross0cb0d7b2019-07-11 10:59:15 -0700944func ninjaEscapeExceptForSpans(s string, spans [][2]int) string {
945 if len(spans) == 0 {
946 return proptools.NinjaEscape(s)
947 }
948
949 sb := strings.Builder{}
950 sb.Grow(len(s) * 11 / 10)
951
952 i := 0
953 for _, span := range spans {
954 sb.WriteString(proptools.NinjaEscape(s[i:span[0]]))
955 sb.WriteString(s[span[0]:span[1]])
956 i = span[1]
957 }
958 sb.WriteString(proptools.NinjaEscape(s[i:]))
959
960 return sb.String()
961}
962
Colin Cross1d2cf042019-03-29 15:33:06 -0700963func ninjaNameEscape(s string) string {
964 b := []byte(s)
965 escaped := false
966 for i, c := range b {
967 valid := (c >= 'a' && c <= 'z') ||
968 (c >= 'A' && c <= 'Z') ||
969 (c >= '0' && c <= '9') ||
970 (c == '_') ||
971 (c == '-') ||
972 (c == '.')
973 if !valid {
974 b[i] = '_'
975 escaped = true
976 }
977 }
978 if escaped {
979 s = string(b)
980 }
981 return s
982}
Colin Cross3d680512020-11-13 16:23:53 -0800983
984// hashSrcFiles returns a hash of the list of source files. It is used to ensure the command line
985// or the sbox textproto manifest change even if the input files are not listed on the command line.
986func hashSrcFiles(srcFiles Paths) string {
987 h := sha256.New()
988 srcFileList := strings.Join(srcFiles.Strings(), "\n")
989 h.Write([]byte(srcFileList))
990 return fmt.Sprintf("%x", h.Sum(nil))
991}
Colin Crossf1a035e2020-11-16 17:32:30 -0800992
993// BuilderContextForTesting returns a BuilderContext for the given config that can be used for tests
994// that need to call methods that take a BuilderContext.
995func BuilderContextForTesting(config Config) BuilderContext {
996 pathCtx := PathContextForTesting(config)
997 return builderContextForTests{
998 PathContext: pathCtx,
999 }
1000}
1001
1002type builderContextForTests struct {
1003 PathContext
1004}
1005
1006func (builderContextForTests) Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule {
1007 return nil
1008}
1009func (builderContextForTests) Build(PackageContext, BuildParams) {}