blob: d005839bd07d26772a65b05fb5f09f65a20f5d17 [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"
19 "path/filepath"
20 "sort"
21 "strings"
22
23 "github.com/google/blueprint"
24 "github.com/google/blueprint/proptools"
25)
26
Colin Cross758290d2019-02-01 16:42:32 -080027// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
28// graph.
Colin Crossfeec25b2019-01-30 17:32:39 -080029type RuleBuilder struct {
Colin Cross5cb5b092019-02-02 21:25:18 -080030 commands []*RuleBuilderCommand
31 installs []RuleBuilderInstall
32 temporariesSet map[string]bool
33 restat bool
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{
39 temporariesSet: make(map[string]bool),
40 }
Colin Cross758290d2019-02-01 16:42:32 -080041}
42
43// RuleBuilderInstall is a tuple of install from and to locations.
44type RuleBuilderInstall struct {
45 From, To string
46}
47
48// 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 -080049func (r *RuleBuilder) Restat() *RuleBuilder {
50 r.restat = true
51 return r
52}
53
Colin Cross758290d2019-02-01 16:42:32 -080054// Install associates an output of the rule with an install location, which can be retrieved later using
55// RuleBuilder.Installs.
Colin Crossfeec25b2019-01-30 17:32:39 -080056func (r *RuleBuilder) Install(from, to string) {
57 r.installs = append(r.installs, RuleBuilderInstall{from, to})
58}
59
Colin Cross758290d2019-02-01 16:42:32 -080060// Command returns a new RuleBuilderCommand for the rule. The commands will be ordered in the rule by when they were
61// created by this method. That can be mutated through their methods in any order, as long as the mutations do not
62// race with any call to Build.
Colin Crossfeec25b2019-01-30 17:32:39 -080063func (r *RuleBuilder) Command() *RuleBuilderCommand {
64 command := &RuleBuilderCommand{}
65 r.commands = append(r.commands, command)
66 return command
67}
68
Colin Cross5cb5b092019-02-02 21:25:18 -080069// Temporary marks an output of a command as an intermediate file that will be used as an input to another command
70// in the same rule, and should not be listed in Outputs.
71func (r *RuleBuilder) Temporary(path string) {
72 r.temporariesSet[path] = true
73}
74
75// DeleteTemporaryFiles adds a command to the rule that deletes any outputs that have been marked using Temporary
76// when the rule runs. DeleteTemporaryFiles should be called after all calls to Temporary.
77func (r *RuleBuilder) DeleteTemporaryFiles() {
78 var temporariesList []string
79
80 for intermediate := range r.temporariesSet {
81 temporariesList = append(temporariesList, intermediate)
82 }
83 sort.Strings(temporariesList)
84
85 r.Command().Text("rm").Flag("-f").Outputs(temporariesList)
86}
87
Colin Cross758290d2019-02-01 16:42:32 -080088// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take input paths, such
89// as RuleBuilderCommand.Input, RuleBuilderComand.Implicit, or RuleBuilderCommand.FlagWithInput. Inputs to a command
90// that are also outputs of another command in the same RuleBuilder are filtered out.
Colin Crossfeec25b2019-01-30 17:32:39 -080091func (r *RuleBuilder) Inputs() []string {
92 outputs := r.outputSet()
93
94 inputs := make(map[string]bool)
95 for _, c := range r.commands {
96 for _, input := range c.inputs {
97 if !outputs[input] {
98 inputs[input] = true
99 }
100 }
101 }
102
103 var inputList []string
104 for input := range inputs {
105 inputList = append(inputList, input)
106 }
107 sort.Strings(inputList)
108
109 return inputList
110}
111
112func (r *RuleBuilder) outputSet() map[string]bool {
113 outputs := make(map[string]bool)
114 for _, c := range r.commands {
115 for _, output := range c.outputs {
116 outputs[output] = true
117 }
118 }
119 return outputs
120}
121
Colin Cross758290d2019-02-01 16:42:32 -0800122// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take output paths, such
123// as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or RuleBuilderCommand.FlagWithInput.
Colin Crossfeec25b2019-01-30 17:32:39 -0800124func (r *RuleBuilder) Outputs() []string {
125 outputs := r.outputSet()
126
127 var outputList []string
128 for output := range outputs {
Colin Cross5cb5b092019-02-02 21:25:18 -0800129 if !r.temporariesSet[output] {
130 outputList = append(outputList, output)
131 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800132 }
133 sort.Strings(outputList)
134 return outputList
135}
136
Colin Cross758290d2019-02-01 16:42:32 -0800137// Installs returns the list of tuples passed to Install.
Colin Crossfeec25b2019-01-30 17:32:39 -0800138func (r *RuleBuilder) Installs() []RuleBuilderInstall {
139 return append([]RuleBuilderInstall(nil), r.installs...)
140}
141
Colin Cross5cb5b092019-02-02 21:25:18 -0800142func (r *RuleBuilder) toolsSet() map[string]bool {
143 tools := make(map[string]bool)
144 for _, c := range r.commands {
145 for _, tool := range c.tools {
146 tools[tool] = true
147 }
148 }
149
150 return tools
151}
152
Colin Cross758290d2019-02-01 16:42:32 -0800153// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800154func (r *RuleBuilder) Tools() []string {
Colin Cross5cb5b092019-02-02 21:25:18 -0800155 toolsSet := r.toolsSet()
156
157 var toolsList []string
158 for tool := range toolsSet {
159 toolsList = append(toolsList, tool)
Colin Crossfeec25b2019-01-30 17:32:39 -0800160 }
Colin Cross5cb5b092019-02-02 21:25:18 -0800161 sort.Strings(toolsList)
162 return toolsList
Colin Crossfeec25b2019-01-30 17:32:39 -0800163}
164
Colin Cross758290d2019-02-01 16:42:32 -0800165// Commands returns a slice containing a the built command line for each call to RuleBuilder.Command.
Colin Crossfeec25b2019-01-30 17:32:39 -0800166func (r *RuleBuilder) Commands() []string {
167 var commands []string
168 for _, c := range r.commands {
169 commands = append(commands, string(c.buf))
170 }
171 return commands
172}
173
Colin Cross758290d2019-02-01 16:42:32 -0800174// BuilderContext is a subset of ModuleContext and SingletonContext.
Colin Cross786cd6d2019-02-01 16:41:11 -0800175type BuilderContext interface {
176 PathContext
177 Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
178 Build(PackageContext, BuildParams)
179}
180
Colin Cross758290d2019-02-01 16:42:32 -0800181var _ BuilderContext = ModuleContext(nil)
182var _ BuilderContext = SingletonContext(nil)
183
184// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
185// Outputs.
Colin Cross786cd6d2019-02-01 16:41:11 -0800186func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string, desc string) {
187 // TODO: convert RuleBuilder arguments and storage to Paths
188 mctx, _ := ctx.(ModuleContext)
Colin Crossfeec25b2019-01-30 17:32:39 -0800189 var inputs Paths
190 for _, input := range r.Inputs() {
Colin Cross786cd6d2019-02-01 16:41:11 -0800191 // Module output paths
192 if mctx != nil {
193 rel, isRel := MaybeRel(ctx, PathForModuleOut(mctx).String(), input)
194 if isRel {
195 inputs = append(inputs, PathForModuleOut(mctx, rel))
196 continue
197 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800198 }
Colin Cross786cd6d2019-02-01 16:41:11 -0800199
200 // Other output paths
201 rel, isRel := MaybeRel(ctx, PathForOutput(ctx).String(), input)
202 if isRel {
203 inputs = append(inputs, PathForOutput(ctx, rel))
204 continue
205 }
206
207 // TODO: remove this once boot image is moved to where PathForOutput can find it.
208 inputs = append(inputs, &unknownRulePath{input})
Colin Crossfeec25b2019-01-30 17:32:39 -0800209 }
210
211 var outputs WritablePaths
212 for _, output := range r.Outputs() {
Colin Cross786cd6d2019-02-01 16:41:11 -0800213 if mctx != nil {
214 rel := Rel(ctx, PathForModuleOut(mctx).String(), output)
215 outputs = append(outputs, PathForModuleOut(mctx, rel))
216 } else {
217 rel := Rel(ctx, PathForOutput(ctx).String(), output)
218 outputs = append(outputs, PathForOutput(ctx, rel))
219 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800220 }
221
222 if len(r.Commands()) > 0 {
223 ctx.Build(pctx, BuildParams{
224 Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
225 Command: strings.Join(proptools.NinjaEscape(r.Commands()), " && "),
226 CommandDeps: r.Tools(),
227 }),
228 Implicits: inputs,
229 Outputs: outputs,
230 Description: desc,
231 })
232 }
233}
234
Colin Cross758290d2019-02-01 16:42:32 -0800235// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
236// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the
237// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
238// space as a separator from the previous method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800239type RuleBuilderCommand struct {
240 buf []byte
241 inputs []string
242 outputs []string
243 tools []string
244}
245
Colin Cross758290d2019-02-01 16:42:32 -0800246// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
247// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800248func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
249 if len(c.buf) > 0 {
250 c.buf = append(c.buf, ' ')
251 }
252 c.buf = append(c.buf, text...)
253 return c
254}
255
Colin Cross758290d2019-02-01 16:42:32 -0800256// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or
257// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800258func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
259 return c.Text(fmt.Sprintf(format, a...))
260}
261
Colin Cross758290d2019-02-01 16:42:32 -0800262// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the
263// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800264func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
265 return c.Text(flag)
266}
267
Colin Cross758290d2019-02-01 16:42:32 -0800268// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
269// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
270// outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800271func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
272 return c.Text(flag + arg)
273}
274
Colin Cross758290d2019-02-01 16:42:32 -0800275// FlagWithArg adds the specified flag and list of arguments to the command line, with the arguments joined by sep
276// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or
277// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800278func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
279 return c.Text(flag + strings.Join(list, sep))
280}
281
Colin Cross758290d2019-02-01 16:42:32 -0800282// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by
283// RuleBuilder.Tools.
Colin Crossfeec25b2019-01-30 17:32:39 -0800284func (c *RuleBuilderCommand) Tool(path string) *RuleBuilderCommand {
285 c.tools = append(c.tools, path)
286 return c.Text(path)
287}
288
Colin Cross758290d2019-02-01 16:42:32 -0800289// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
290// RuleBuilder.Inputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800291func (c *RuleBuilderCommand) Input(path string) *RuleBuilderCommand {
292 c.inputs = append(c.inputs, path)
293 return c.Text(path)
294}
295
Colin Cross758290d2019-02-01 16:42:32 -0800296// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
297// dependencies returned by RuleBuilder.Inputs.
298func (c *RuleBuilderCommand) Inputs(paths []string) *RuleBuilderCommand {
299 for _, path := range paths {
300 c.Input(path)
301 }
302 return c
303}
304
305// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
306// command line.
Colin Crossfeec25b2019-01-30 17:32:39 -0800307func (c *RuleBuilderCommand) Implicit(path string) *RuleBuilderCommand {
308 c.inputs = append(c.inputs, path)
309 return c
310}
311
Colin Cross758290d2019-02-01 16:42:32 -0800312// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
313// command line.
Colin Crossfeec25b2019-01-30 17:32:39 -0800314func (c *RuleBuilderCommand) Implicits(paths []string) *RuleBuilderCommand {
315 c.inputs = append(c.inputs, paths...)
316 return c
317}
318
Colin Cross758290d2019-02-01 16:42:32 -0800319// Output adds the specified output path to the command line. The path will also be added to the outputs returned by
320// RuleBuilder.Outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800321func (c *RuleBuilderCommand) Output(path string) *RuleBuilderCommand {
322 c.outputs = append(c.outputs, path)
323 return c.Text(path)
324}
325
Colin Cross758290d2019-02-01 16:42:32 -0800326// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
327// the outputs returned by RuleBuilder.Outputs.
328func (c *RuleBuilderCommand) Outputs(paths []string) *RuleBuilderCommand {
329 for _, path := range paths {
330 c.Output(path)
331 }
332 return c
333}
334
335// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
336// the command line.
Colin Crossfeec25b2019-01-30 17:32:39 -0800337func (c *RuleBuilderCommand) ImplicitOutput(path string) *RuleBuilderCommand {
338 c.outputs = append(c.outputs, path)
339 return c
340}
341
Colin Cross758290d2019-02-01 16:42:32 -0800342// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
343// the command line.
344func (c *RuleBuilderCommand) ImplicitOutputs(paths []string) *RuleBuilderCommand {
345 c.outputs = append(c.outputs, paths...)
346 return c
347}
348
349// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
350// will also be added to the dependencies returned by RuleBuilder.Inputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800351func (c *RuleBuilderCommand) FlagWithInput(flag, path string) *RuleBuilderCommand {
352 c.inputs = append(c.inputs, path)
353 return c.Text(flag + path)
354}
355
Colin Cross758290d2019-02-01 16:42:32 -0800356// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
357// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
358// RuleBuilder.Inputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800359func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths []string, sep string) *RuleBuilderCommand {
360 c.inputs = append(c.inputs, paths...)
361 return c.FlagWithList(flag, paths, sep)
362}
363
Colin Cross758290d2019-02-01 16:42:32 -0800364// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
365// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for
366// each input path.
367func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths []string) *RuleBuilderCommand {
368 for _, path := range paths {
369 c.FlagWithInput(flag, path)
370 }
371 return c
372}
373
374// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path
375// will also be added to the outputs returned by RuleBuilder.Outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800376func (c *RuleBuilderCommand) FlagWithOutput(flag, path string) *RuleBuilderCommand {
377 c.outputs = append(c.outputs, path)
378 return c.Text(flag + path)
379}
380
Colin Cross758290d2019-02-01 16:42:32 -0800381// String returns the command line.
382func (c *RuleBuilderCommand) String() string {
383 return string(c.buf)
384}
385
Colin Crossfeec25b2019-01-30 17:32:39 -0800386type unknownRulePath struct {
387 path string
388}
389
390var _ Path = (*unknownRulePath)(nil)
391
392func (p *unknownRulePath) String() string { return p.path }
393func (p *unknownRulePath) Ext() string { return filepath.Ext(p.path) }
394func (p *unknownRulePath) Base() string { return filepath.Base(p.path) }
395func (p *unknownRulePath) Rel() string { return p.path }