blob: a2a536652b6bf19d6f9d976fbed0c5d390d3cd2b [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 (
18 "fmt"
Colin Crossfeec25b2019-01-30 17:32:39 -080019 "sort"
20 "strings"
21
22 "github.com/google/blueprint"
23 "github.com/google/blueprint/proptools"
24)
25
Colin Cross758290d2019-02-01 16:42:32 -080026// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
27// graph.
Colin Crossfeec25b2019-01-30 17:32:39 -080028type RuleBuilder struct {
Colin Cross5cb5b092019-02-02 21:25:18 -080029 commands []*RuleBuilderCommand
Colin Crossdeabb942019-02-11 14:11:09 -080030 installs RuleBuilderInstalls
Colin Cross69f59a32019-02-15 10:39:37 -080031 temporariesSet map[WritablePath]bool
Colin Cross5cb5b092019-02-02 21:25:18 -080032 restat bool
Colin Cross0d2f40a2019-02-05 22:31:15 -080033 missingDeps []string
Colin Crossfeec25b2019-01-30 17:32:39 -080034}
35
Colin Cross758290d2019-02-01 16:42:32 -080036// NewRuleBuilder returns a newly created RuleBuilder.
37func NewRuleBuilder() *RuleBuilder {
Colin Cross5cb5b092019-02-02 21:25:18 -080038 return &RuleBuilder{
Colin Cross69f59a32019-02-15 10:39:37 -080039 temporariesSet: make(map[WritablePath]bool),
Colin Cross5cb5b092019-02-02 21:25:18 -080040 }
Colin Cross758290d2019-02-01 16:42:32 -080041}
42
43// RuleBuilderInstall is a tuple of install from and to locations.
44type RuleBuilderInstall struct {
Colin Cross69f59a32019-02-15 10:39:37 -080045 From Path
46 To string
Colin Cross758290d2019-02-01 16:42:32 -080047}
48
Colin Crossdeabb942019-02-11 14:11:09 -080049type RuleBuilderInstalls []RuleBuilderInstall
50
51// String returns the RuleBuilderInstalls in the form used by $(call copy-many-files) in Make, a space separated
52// list of from:to tuples.
53func (installs RuleBuilderInstalls) String() string {
54 sb := strings.Builder{}
55 for i, install := range installs {
56 if i != 0 {
57 sb.WriteRune(' ')
58 }
Colin Cross69f59a32019-02-15 10:39:37 -080059 sb.WriteString(install.From.String())
Colin Crossdeabb942019-02-11 14:11:09 -080060 sb.WriteRune(':')
61 sb.WriteString(install.To)
62 }
63 return sb.String()
64}
65
Colin Cross0d2f40a2019-02-05 22:31:15 -080066// MissingDeps adds modules to the list of missing dependencies. If MissingDeps
67// is called with a non-empty input, any call to Build will result in a rule
68// that will print an error listing the missing dependencies and fail.
69// MissingDeps should only be called if Config.AllowMissingDependencies() is
70// true.
71func (r *RuleBuilder) MissingDeps(missingDeps []string) {
72 r.missingDeps = append(r.missingDeps, missingDeps...)
73}
74
Colin Cross758290d2019-02-01 16:42:32 -080075// Restat marks the rule as a restat rule, which will be passed to ModuleContext.Rule in BuildParams.Restat.
Colin Crossfeec25b2019-01-30 17:32:39 -080076func (r *RuleBuilder) Restat() *RuleBuilder {
77 r.restat = true
78 return r
79}
80
Colin Cross758290d2019-02-01 16:42:32 -080081// Install associates an output of the rule with an install location, which can be retrieved later using
82// RuleBuilder.Installs.
Colin Cross69f59a32019-02-15 10:39:37 -080083func (r *RuleBuilder) Install(from Path, to string) {
Colin Crossfeec25b2019-01-30 17:32:39 -080084 r.installs = append(r.installs, RuleBuilderInstall{from, to})
85}
86
Colin Cross758290d2019-02-01 16:42:32 -080087// Command returns a new RuleBuilderCommand for the rule. The commands will be ordered in the rule by when they were
88// created by this method. That can be mutated through their methods in any order, as long as the mutations do not
89// race with any call to Build.
Colin Crossfeec25b2019-01-30 17:32:39 -080090func (r *RuleBuilder) Command() *RuleBuilderCommand {
91 command := &RuleBuilderCommand{}
92 r.commands = append(r.commands, command)
93 return command
94}
95
Colin Cross5cb5b092019-02-02 21:25:18 -080096// Temporary marks an output of a command as an intermediate file that will be used as an input to another command
97// in the same rule, and should not be listed in Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -080098func (r *RuleBuilder) Temporary(path WritablePath) {
Colin Cross5cb5b092019-02-02 21:25:18 -080099 r.temporariesSet[path] = true
100}
101
102// DeleteTemporaryFiles adds a command to the rule that deletes any outputs that have been marked using Temporary
103// when the rule runs. DeleteTemporaryFiles should be called after all calls to Temporary.
104func (r *RuleBuilder) DeleteTemporaryFiles() {
Colin Cross69f59a32019-02-15 10:39:37 -0800105 var temporariesList WritablePaths
Colin Cross5cb5b092019-02-02 21:25:18 -0800106
107 for intermediate := range r.temporariesSet {
108 temporariesList = append(temporariesList, intermediate)
109 }
Colin Cross69f59a32019-02-15 10:39:37 -0800110
111 sort.Slice(temporariesList, func(i, j int) bool {
112 return temporariesList[i].String() < temporariesList[j].String()
113 })
Colin Cross5cb5b092019-02-02 21:25:18 -0800114
115 r.Command().Text("rm").Flag("-f").Outputs(temporariesList)
116}
117
Colin Cross758290d2019-02-01 16:42:32 -0800118// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take input paths, such
119// as RuleBuilderCommand.Input, RuleBuilderComand.Implicit, or RuleBuilderCommand.FlagWithInput. Inputs to a command
120// that are also outputs of another command in the same RuleBuilder are filtered out.
Colin Cross69f59a32019-02-15 10:39:37 -0800121func (r *RuleBuilder) Inputs() Paths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800122 outputs := r.outputSet()
123
Colin Cross69f59a32019-02-15 10:39:37 -0800124 inputs := make(map[string]Path)
Colin Crossfeec25b2019-01-30 17:32:39 -0800125 for _, c := range r.commands {
126 for _, input := range c.inputs {
Colin Cross69f59a32019-02-15 10:39:37 -0800127 if _, isOutput := outputs[input.String()]; !isOutput {
128 inputs[input.String()] = input
Colin Crossfeec25b2019-01-30 17:32:39 -0800129 }
130 }
131 }
132
Colin Cross69f59a32019-02-15 10:39:37 -0800133 var inputList Paths
134 for _, input := range inputs {
Colin Crossfeec25b2019-01-30 17:32:39 -0800135 inputList = append(inputList, input)
136 }
Colin Cross69f59a32019-02-15 10:39:37 -0800137
138 sort.Slice(inputList, func(i, j int) bool {
139 return inputList[i].String() < inputList[j].String()
140 })
Colin Crossfeec25b2019-01-30 17:32:39 -0800141
142 return inputList
143}
144
Colin Cross69f59a32019-02-15 10:39:37 -0800145func (r *RuleBuilder) outputSet() map[string]WritablePath {
146 outputs := make(map[string]WritablePath)
Colin Crossfeec25b2019-01-30 17:32:39 -0800147 for _, c := range r.commands {
148 for _, output := range c.outputs {
Colin Cross69f59a32019-02-15 10:39:37 -0800149 outputs[output.String()] = output
Colin Crossfeec25b2019-01-30 17:32:39 -0800150 }
151 }
152 return outputs
153}
154
Colin Cross758290d2019-02-01 16:42:32 -0800155// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take output paths, such
156// as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or RuleBuilderCommand.FlagWithInput.
Colin Cross69f59a32019-02-15 10:39:37 -0800157func (r *RuleBuilder) Outputs() WritablePaths {
Colin Crossfeec25b2019-01-30 17:32:39 -0800158 outputs := r.outputSet()
159
Colin Cross69f59a32019-02-15 10:39:37 -0800160 var outputList WritablePaths
161 for _, output := range outputs {
Colin Cross5cb5b092019-02-02 21:25:18 -0800162 if !r.temporariesSet[output] {
163 outputList = append(outputList, output)
164 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800165 }
Colin Cross69f59a32019-02-15 10:39:37 -0800166
167 sort.Slice(outputList, func(i, j int) bool {
168 return outputList[i].String() < outputList[j].String()
169 })
170
Colin Crossfeec25b2019-01-30 17:32:39 -0800171 return outputList
172}
173
Colin Cross758290d2019-02-01 16:42:32 -0800174// Installs returns the list of tuples passed to Install.
Colin Crossdeabb942019-02-11 14:11:09 -0800175func (r *RuleBuilder) Installs() RuleBuilderInstalls {
176 return append(RuleBuilderInstalls(nil), r.installs...)
Colin Crossfeec25b2019-01-30 17:32:39 -0800177}
178
Colin Cross69f59a32019-02-15 10:39:37 -0800179func (r *RuleBuilder) toolsSet() map[string]Path {
180 tools := make(map[string]Path)
Colin Cross5cb5b092019-02-02 21:25:18 -0800181 for _, c := range r.commands {
182 for _, tool := range c.tools {
Colin Cross69f59a32019-02-15 10:39:37 -0800183 tools[tool.String()] = tool
Colin Cross5cb5b092019-02-02 21:25:18 -0800184 }
185 }
186
187 return tools
188}
189
Colin Cross758290d2019-02-01 16:42:32 -0800190// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method.
Colin Cross69f59a32019-02-15 10:39:37 -0800191func (r *RuleBuilder) Tools() Paths {
Colin Cross5cb5b092019-02-02 21:25:18 -0800192 toolsSet := r.toolsSet()
193
Colin Cross69f59a32019-02-15 10:39:37 -0800194 var toolsList Paths
195 for _, tool := range toolsSet {
Colin Cross5cb5b092019-02-02 21:25:18 -0800196 toolsList = append(toolsList, tool)
Colin Crossfeec25b2019-01-30 17:32:39 -0800197 }
Colin Cross69f59a32019-02-15 10:39:37 -0800198
199 sort.Slice(toolsList, func(i, j int) bool {
200 return toolsList[i].String() < toolsList[j].String()
201 })
202
Colin Cross5cb5b092019-02-02 21:25:18 -0800203 return toolsList
Colin Crossfeec25b2019-01-30 17:32:39 -0800204}
205
Colin Cross758290d2019-02-01 16:42:32 -0800206// Commands returns a slice containing a the built command line for each call to RuleBuilder.Command.
Colin Crossfeec25b2019-01-30 17:32:39 -0800207func (r *RuleBuilder) Commands() []string {
208 var commands []string
209 for _, c := range r.commands {
210 commands = append(commands, string(c.buf))
211 }
212 return commands
213}
214
Colin Cross758290d2019-02-01 16:42:32 -0800215// BuilderContext is a subset of ModuleContext and SingletonContext.
Colin Cross786cd6d2019-02-01 16:41:11 -0800216type BuilderContext interface {
217 PathContext
218 Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
219 Build(PackageContext, BuildParams)
220}
221
Colin Cross758290d2019-02-01 16:42:32 -0800222var _ BuilderContext = ModuleContext(nil)
223var _ BuilderContext = SingletonContext(nil)
224
225// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
226// Outputs.
Colin Cross786cd6d2019-02-01 16:41:11 -0800227func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string, desc string) {
Colin Cross0d2f40a2019-02-05 22:31:15 -0800228 if len(r.missingDeps) > 0 {
229 ctx.Build(pctx, BuildParams{
230 Rule: ErrorRule,
Colin Cross69f59a32019-02-15 10:39:37 -0800231 Outputs: r.Outputs(),
Colin Cross0d2f40a2019-02-05 22:31:15 -0800232 Description: desc,
233 Args: map[string]string{
234 "error": "missing dependencies: " + strings.Join(r.missingDeps, ", "),
235 },
236 })
237 return
238 }
239
Colin Crossfeec25b2019-01-30 17:32:39 -0800240 if len(r.Commands()) > 0 {
241 ctx.Build(pctx, BuildParams{
242 Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
Colin Cross0b9f31f2019-02-28 11:00:01 -0800243 Command: strings.Join(proptools.NinjaEscapeList(r.Commands()), " && "),
Colin Cross69f59a32019-02-15 10:39:37 -0800244 CommandDeps: r.Tools().Strings(),
Colin Crossbaa676f2019-02-25 14:56:01 -0800245 Restat: r.restat,
Colin Crossfeec25b2019-01-30 17:32:39 -0800246 }),
Colin Cross69f59a32019-02-15 10:39:37 -0800247 Implicits: r.Inputs(),
248 Outputs: r.Outputs(),
Colin Crossfeec25b2019-01-30 17:32:39 -0800249 Description: desc,
250 })
251 }
252}
253
Colin Cross758290d2019-02-01 16:42:32 -0800254// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
255// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the
256// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
257// space as a separator from the previous method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800258type RuleBuilderCommand struct {
259 buf []byte
Colin Cross69f59a32019-02-15 10:39:37 -0800260 inputs Paths
261 outputs WritablePaths
262 tools Paths
Colin Crossfeec25b2019-01-30 17:32:39 -0800263}
264
Colin Cross758290d2019-02-01 16:42:32 -0800265// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
266// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800267func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
268 if len(c.buf) > 0 {
269 c.buf = append(c.buf, ' ')
270 }
271 c.buf = append(c.buf, text...)
272 return c
273}
274
Colin Cross758290d2019-02-01 16:42:32 -0800275// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or
276// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800277func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
278 return c.Text(fmt.Sprintf(format, a...))
279}
280
Colin Cross758290d2019-02-01 16:42:32 -0800281// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the
282// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800283func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
284 return c.Text(flag)
285}
286
Colin Cross758290d2019-02-01 16:42:32 -0800287// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
288// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
289// outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800290func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
291 return c.Text(flag + arg)
292}
293
Colin Crossc7ed0042019-02-11 14:11:09 -0800294// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to
295// calling FlagWithArg for argument.
296func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand {
297 for _, arg := range args {
298 c.FlagWithArg(flag, arg)
299 }
300 return c
301}
302
Roland Levillain2da5d9a2019-02-27 16:56:41 +0000303// 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 -0800304// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or
305// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800306func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
307 return c.Text(flag + strings.Join(list, sep))
308}
309
Colin Cross758290d2019-02-01 16:42:32 -0800310// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by
311// RuleBuilder.Tools.
Colin Cross69f59a32019-02-15 10:39:37 -0800312func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800313 c.tools = append(c.tools, path)
Colin Cross69f59a32019-02-15 10:39:37 -0800314 return c.Text(path.String())
Colin Crossfeec25b2019-01-30 17:32:39 -0800315}
316
Colin Cross758290d2019-02-01 16:42:32 -0800317// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
318// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800319func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800320 c.inputs = append(c.inputs, path)
Colin Cross69f59a32019-02-15 10:39:37 -0800321 return c.Text(path.String())
Colin Crossfeec25b2019-01-30 17:32:39 -0800322}
323
Colin Cross758290d2019-02-01 16:42:32 -0800324// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
325// dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800326func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800327 for _, path := range paths {
328 c.Input(path)
329 }
330 return c
331}
332
333// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
334// command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800335func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800336 c.inputs = append(c.inputs, path)
337 return c
338}
339
Colin Cross758290d2019-02-01 16:42:32 -0800340// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
341// command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800342func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800343 c.inputs = append(c.inputs, paths...)
344 return c
345}
346
Colin Cross758290d2019-02-01 16:42:32 -0800347// Output adds the specified output path to the command line. The path will also be added to the outputs returned by
348// RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800349func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800350 c.outputs = append(c.outputs, path)
Colin Cross69f59a32019-02-15 10:39:37 -0800351 return c.Text(path.String())
Colin Crossfeec25b2019-01-30 17:32:39 -0800352}
353
Colin Cross758290d2019-02-01 16:42:32 -0800354// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
355// the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800356func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800357 for _, path := range paths {
358 c.Output(path)
359 }
360 return c
361}
362
363// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
364// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800365func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800366 c.outputs = append(c.outputs, path)
367 return c
368}
369
Colin Cross758290d2019-02-01 16:42:32 -0800370// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
371// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800372func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800373 c.outputs = append(c.outputs, paths...)
374 return c
375}
376
377// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
378// will also be added to the dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800379func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800380 c.inputs = append(c.inputs, path)
Colin Cross69f59a32019-02-15 10:39:37 -0800381 return c.Text(flag + path.String())
Colin Crossfeec25b2019-01-30 17:32:39 -0800382}
383
Colin Cross758290d2019-02-01 16:42:32 -0800384// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
385// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
386// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800387func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800388 c.inputs = append(c.inputs, paths...)
Colin Cross69f59a32019-02-15 10:39:37 -0800389 return c.FlagWithList(flag, paths.Strings(), sep)
Colin Crossfeec25b2019-01-30 17:32:39 -0800390}
391
Colin Cross758290d2019-02-01 16:42:32 -0800392// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
393// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for
394// each input path.
Colin Cross69f59a32019-02-15 10:39:37 -0800395func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800396 for _, path := range paths {
397 c.FlagWithInput(flag, path)
398 }
399 return c
400}
401
402// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path
403// will also be added to the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800404func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800405 c.outputs = append(c.outputs, path)
Colin Cross69f59a32019-02-15 10:39:37 -0800406 return c.Text(flag + path.String())
Colin Crossfeec25b2019-01-30 17:32:39 -0800407}
408
Colin Cross758290d2019-02-01 16:42:32 -0800409// String returns the command line.
410func (c *RuleBuilderCommand) String() string {
411 return string(c.buf)
412}