blob: 5edd7b65be1a9598d9d1efe910b274b1b0a90b76 [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{
243 Command: strings.Join(proptools.NinjaEscape(r.Commands()), " && "),
Colin Cross69f59a32019-02-15 10:39:37 -0800244 CommandDeps: r.Tools().Strings(),
Colin Crossfeec25b2019-01-30 17:32:39 -0800245 }),
Colin Cross69f59a32019-02-15 10:39:37 -0800246 Implicits: r.Inputs(),
247 Outputs: r.Outputs(),
Colin Crossfeec25b2019-01-30 17:32:39 -0800248 Description: desc,
249 })
250 }
251}
252
Colin Cross758290d2019-02-01 16:42:32 -0800253// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
254// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the
255// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
256// space as a separator from the previous method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800257type RuleBuilderCommand struct {
258 buf []byte
Colin Cross69f59a32019-02-15 10:39:37 -0800259 inputs Paths
260 outputs WritablePaths
261 tools Paths
Colin Crossfeec25b2019-01-30 17:32:39 -0800262}
263
Colin Cross758290d2019-02-01 16:42:32 -0800264// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
265// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800266func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
267 if len(c.buf) > 0 {
268 c.buf = append(c.buf, ' ')
269 }
270 c.buf = append(c.buf, text...)
271 return c
272}
273
Colin Cross758290d2019-02-01 16:42:32 -0800274// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or
275// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800276func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
277 return c.Text(fmt.Sprintf(format, a...))
278}
279
Colin Cross758290d2019-02-01 16:42:32 -0800280// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the
281// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800282func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
283 return c.Text(flag)
284}
285
Colin Cross758290d2019-02-01 16:42:32 -0800286// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
287// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
288// outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800289func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
290 return c.Text(flag + arg)
291}
292
Colin Crossc7ed0042019-02-11 14:11:09 -0800293// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to
294// calling FlagWithArg for argument.
295func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand {
296 for _, arg := range args {
297 c.FlagWithArg(flag, arg)
298 }
299 return c
300}
301
Colin Cross758290d2019-02-01 16:42:32 -0800302// FlagWithArg adds the specified flag and list of arguments to the command line, with the arguments joined by sep
303// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or
304// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800305func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
306 return c.Text(flag + strings.Join(list, sep))
307}
308
Colin Cross758290d2019-02-01 16:42:32 -0800309// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by
310// RuleBuilder.Tools.
Colin Cross69f59a32019-02-15 10:39:37 -0800311func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800312 c.tools = append(c.tools, path)
Colin Cross69f59a32019-02-15 10:39:37 -0800313 return c.Text(path.String())
Colin Crossfeec25b2019-01-30 17:32:39 -0800314}
315
Colin Cross758290d2019-02-01 16:42:32 -0800316// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
317// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800318func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800319 c.inputs = append(c.inputs, path)
Colin Cross69f59a32019-02-15 10:39:37 -0800320 return c.Text(path.String())
Colin Crossfeec25b2019-01-30 17:32:39 -0800321}
322
Colin Cross758290d2019-02-01 16:42:32 -0800323// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
324// dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800325func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800326 for _, path := range paths {
327 c.Input(path)
328 }
329 return c
330}
331
332// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
333// command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800334func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800335 c.inputs = append(c.inputs, path)
336 return c
337}
338
Colin Cross758290d2019-02-01 16:42:32 -0800339// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
340// command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800341func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800342 c.inputs = append(c.inputs, paths...)
343 return c
344}
345
Colin Cross758290d2019-02-01 16:42:32 -0800346// Output adds the specified output path to the command line. The path will also be added to the outputs returned by
347// RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800348func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800349 c.outputs = append(c.outputs, path)
Colin Cross69f59a32019-02-15 10:39:37 -0800350 return c.Text(path.String())
Colin Crossfeec25b2019-01-30 17:32:39 -0800351}
352
Colin Cross758290d2019-02-01 16:42:32 -0800353// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
354// the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800355func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800356 for _, path := range paths {
357 c.Output(path)
358 }
359 return c
360}
361
362// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
363// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800364func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800365 c.outputs = append(c.outputs, path)
366 return c
367}
368
Colin Cross758290d2019-02-01 16:42:32 -0800369// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
370// the command line.
Colin Cross69f59a32019-02-15 10:39:37 -0800371func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800372 c.outputs = append(c.outputs, paths...)
373 return c
374}
375
376// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
377// will also be added to the dependencies returned by RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800378func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800379 c.inputs = append(c.inputs, path)
Colin Cross69f59a32019-02-15 10:39:37 -0800380 return c.Text(flag + path.String())
Colin Crossfeec25b2019-01-30 17:32:39 -0800381}
382
Colin Cross758290d2019-02-01 16:42:32 -0800383// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
384// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
385// RuleBuilder.Inputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800386func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800387 c.inputs = append(c.inputs, paths...)
Colin Cross69f59a32019-02-15 10:39:37 -0800388 return c.FlagWithList(flag, paths.Strings(), sep)
Colin Crossfeec25b2019-01-30 17:32:39 -0800389}
390
Colin Cross758290d2019-02-01 16:42:32 -0800391// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
392// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for
393// each input path.
Colin Cross69f59a32019-02-15 10:39:37 -0800394func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
Colin Cross758290d2019-02-01 16:42:32 -0800395 for _, path := range paths {
396 c.FlagWithInput(flag, path)
397 }
398 return c
399}
400
401// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path
402// will also be added to the outputs returned by RuleBuilder.Outputs.
Colin Cross69f59a32019-02-15 10:39:37 -0800403func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
Colin Crossfeec25b2019-01-30 17:32:39 -0800404 c.outputs = append(c.outputs, path)
Colin Cross69f59a32019-02-15 10:39:37 -0800405 return c.Text(flag + path.String())
Colin Crossfeec25b2019-01-30 17:32:39 -0800406}
407
Colin Cross758290d2019-02-01 16:42:32 -0800408// String returns the command line.
409func (c *RuleBuilderCommand) String() string {
410 return string(c.buf)
411}