blob: c2e598994e5e0b4f107c7c6f4b0b1bba86460acd [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 {
30 commands []*RuleBuilderCommand
31 installs []RuleBuilderInstall
32 restat bool
33}
34
Colin Cross758290d2019-02-01 16:42:32 -080035// NewRuleBuilder returns a newly created RuleBuilder.
36func NewRuleBuilder() *RuleBuilder {
37 return &RuleBuilder{}
38}
39
40// RuleBuilderInstall is a tuple of install from and to locations.
41type RuleBuilderInstall struct {
42 From, To string
43}
44
45// 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 -080046func (r *RuleBuilder) Restat() *RuleBuilder {
47 r.restat = true
48 return r
49}
50
Colin Cross758290d2019-02-01 16:42:32 -080051// Install associates an output of the rule with an install location, which can be retrieved later using
52// RuleBuilder.Installs.
Colin Crossfeec25b2019-01-30 17:32:39 -080053func (r *RuleBuilder) Install(from, to string) {
54 r.installs = append(r.installs, RuleBuilderInstall{from, to})
55}
56
Colin Cross758290d2019-02-01 16:42:32 -080057// Command returns a new RuleBuilderCommand for the rule. The commands will be ordered in the rule by when they were
58// created by this method. That can be mutated through their methods in any order, as long as the mutations do not
59// race with any call to Build.
Colin Crossfeec25b2019-01-30 17:32:39 -080060func (r *RuleBuilder) Command() *RuleBuilderCommand {
61 command := &RuleBuilderCommand{}
62 r.commands = append(r.commands, command)
63 return command
64}
65
Colin Cross758290d2019-02-01 16:42:32 -080066// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take input paths, such
67// as RuleBuilderCommand.Input, RuleBuilderComand.Implicit, or RuleBuilderCommand.FlagWithInput. Inputs to a command
68// that are also outputs of another command in the same RuleBuilder are filtered out.
Colin Crossfeec25b2019-01-30 17:32:39 -080069func (r *RuleBuilder) Inputs() []string {
70 outputs := r.outputSet()
71
72 inputs := make(map[string]bool)
73 for _, c := range r.commands {
74 for _, input := range c.inputs {
75 if !outputs[input] {
76 inputs[input] = true
77 }
78 }
79 }
80
81 var inputList []string
82 for input := range inputs {
83 inputList = append(inputList, input)
84 }
85 sort.Strings(inputList)
86
87 return inputList
88}
89
90func (r *RuleBuilder) outputSet() map[string]bool {
91 outputs := make(map[string]bool)
92 for _, c := range r.commands {
93 for _, output := range c.outputs {
94 outputs[output] = true
95 }
96 }
97 return outputs
98}
99
Colin Cross758290d2019-02-01 16:42:32 -0800100// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take output paths, such
101// as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or RuleBuilderCommand.FlagWithInput.
Colin Crossfeec25b2019-01-30 17:32:39 -0800102func (r *RuleBuilder) Outputs() []string {
103 outputs := r.outputSet()
104
105 var outputList []string
106 for output := range outputs {
107 outputList = append(outputList, output)
108 }
109 sort.Strings(outputList)
110 return outputList
111}
112
Colin Cross758290d2019-02-01 16:42:32 -0800113// Installs returns the list of tuples passed to Install.
Colin Crossfeec25b2019-01-30 17:32:39 -0800114func (r *RuleBuilder) Installs() []RuleBuilderInstall {
115 return append([]RuleBuilderInstall(nil), r.installs...)
116}
117
Colin Cross758290d2019-02-01 16:42:32 -0800118// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800119func (r *RuleBuilder) Tools() []string {
120 var tools []string
121 for _, c := range r.commands {
122 tools = append(tools, c.tools...)
123 }
124 return tools
125}
126
Colin Cross758290d2019-02-01 16:42:32 -0800127// Commands returns a slice containing a the built command line for each call to RuleBuilder.Command.
Colin Crossfeec25b2019-01-30 17:32:39 -0800128func (r *RuleBuilder) Commands() []string {
129 var commands []string
130 for _, c := range r.commands {
131 commands = append(commands, string(c.buf))
132 }
133 return commands
134}
135
Colin Cross758290d2019-02-01 16:42:32 -0800136// BuilderContext is a subset of ModuleContext and SingletonContext.
Colin Cross786cd6d2019-02-01 16:41:11 -0800137type BuilderContext interface {
138 PathContext
139 Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
140 Build(PackageContext, BuildParams)
141}
142
Colin Cross758290d2019-02-01 16:42:32 -0800143var _ BuilderContext = ModuleContext(nil)
144var _ BuilderContext = SingletonContext(nil)
145
146// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
147// Outputs.
Colin Cross786cd6d2019-02-01 16:41:11 -0800148func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string, desc string) {
149 // TODO: convert RuleBuilder arguments and storage to Paths
150 mctx, _ := ctx.(ModuleContext)
Colin Crossfeec25b2019-01-30 17:32:39 -0800151 var inputs Paths
152 for _, input := range r.Inputs() {
Colin Cross786cd6d2019-02-01 16:41:11 -0800153 // Module output paths
154 if mctx != nil {
155 rel, isRel := MaybeRel(ctx, PathForModuleOut(mctx).String(), input)
156 if isRel {
157 inputs = append(inputs, PathForModuleOut(mctx, rel))
158 continue
159 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800160 }
Colin Cross786cd6d2019-02-01 16:41:11 -0800161
162 // Other output paths
163 rel, isRel := MaybeRel(ctx, PathForOutput(ctx).String(), input)
164 if isRel {
165 inputs = append(inputs, PathForOutput(ctx, rel))
166 continue
167 }
168
169 // TODO: remove this once boot image is moved to where PathForOutput can find it.
170 inputs = append(inputs, &unknownRulePath{input})
Colin Crossfeec25b2019-01-30 17:32:39 -0800171 }
172
173 var outputs WritablePaths
174 for _, output := range r.Outputs() {
Colin Cross786cd6d2019-02-01 16:41:11 -0800175 if mctx != nil {
176 rel := Rel(ctx, PathForModuleOut(mctx).String(), output)
177 outputs = append(outputs, PathForModuleOut(mctx, rel))
178 } else {
179 rel := Rel(ctx, PathForOutput(ctx).String(), output)
180 outputs = append(outputs, PathForOutput(ctx, rel))
181 }
Colin Crossfeec25b2019-01-30 17:32:39 -0800182 }
183
184 if len(r.Commands()) > 0 {
185 ctx.Build(pctx, BuildParams{
186 Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
187 Command: strings.Join(proptools.NinjaEscape(r.Commands()), " && "),
188 CommandDeps: r.Tools(),
189 }),
190 Implicits: inputs,
191 Outputs: outputs,
192 Description: desc,
193 })
194 }
195}
196
Colin Cross758290d2019-02-01 16:42:32 -0800197// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
198// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the
199// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
200// space as a separator from the previous method.
Colin Crossfeec25b2019-01-30 17:32:39 -0800201type RuleBuilderCommand struct {
202 buf []byte
203 inputs []string
204 outputs []string
205 tools []string
206}
207
Colin Cross758290d2019-02-01 16:42:32 -0800208// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
209// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800210func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
211 if len(c.buf) > 0 {
212 c.buf = append(c.buf, ' ')
213 }
214 c.buf = append(c.buf, text...)
215 return c
216}
217
Colin Cross758290d2019-02-01 16:42:32 -0800218// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or
219// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800220func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
221 return c.Text(fmt.Sprintf(format, a...))
222}
223
Colin Cross758290d2019-02-01 16:42:32 -0800224// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the
225// rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800226func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
227 return c.Text(flag)
228}
229
Colin Cross758290d2019-02-01 16:42:32 -0800230// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
231// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
232// outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800233func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
234 return c.Text(flag + arg)
235}
236
Colin Cross758290d2019-02-01 16:42:32 -0800237// FlagWithArg adds the specified flag and list of arguments to the command line, with the arguments joined by sep
238// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or
239// the rule will not have them listed in its dependencies or outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800240func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
241 return c.Text(flag + strings.Join(list, sep))
242}
243
Colin Cross758290d2019-02-01 16:42:32 -0800244// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by
245// RuleBuilder.Tools.
Colin Crossfeec25b2019-01-30 17:32:39 -0800246func (c *RuleBuilderCommand) Tool(path string) *RuleBuilderCommand {
247 c.tools = append(c.tools, path)
248 return c.Text(path)
249}
250
Colin Cross758290d2019-02-01 16:42:32 -0800251// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
252// RuleBuilder.Inputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800253func (c *RuleBuilderCommand) Input(path string) *RuleBuilderCommand {
254 c.inputs = append(c.inputs, path)
255 return c.Text(path)
256}
257
Colin Cross758290d2019-02-01 16:42:32 -0800258// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
259// dependencies returned by RuleBuilder.Inputs.
260func (c *RuleBuilderCommand) Inputs(paths []string) *RuleBuilderCommand {
261 for _, path := range paths {
262 c.Input(path)
263 }
264 return c
265}
266
267// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
268// command line.
Colin Crossfeec25b2019-01-30 17:32:39 -0800269func (c *RuleBuilderCommand) Implicit(path string) *RuleBuilderCommand {
270 c.inputs = append(c.inputs, path)
271 return c
272}
273
Colin Cross758290d2019-02-01 16:42:32 -0800274// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
275// command line.
Colin Crossfeec25b2019-01-30 17:32:39 -0800276func (c *RuleBuilderCommand) Implicits(paths []string) *RuleBuilderCommand {
277 c.inputs = append(c.inputs, paths...)
278 return c
279}
280
Colin Cross758290d2019-02-01 16:42:32 -0800281// Output adds the specified output path to the command line. The path will also be added to the outputs returned by
282// RuleBuilder.Outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800283func (c *RuleBuilderCommand) Output(path string) *RuleBuilderCommand {
284 c.outputs = append(c.outputs, path)
285 return c.Text(path)
286}
287
Colin Cross758290d2019-02-01 16:42:32 -0800288// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
289// the outputs returned by RuleBuilder.Outputs.
290func (c *RuleBuilderCommand) Outputs(paths []string) *RuleBuilderCommand {
291 for _, path := range paths {
292 c.Output(path)
293 }
294 return c
295}
296
297// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
298// the command line.
Colin Crossfeec25b2019-01-30 17:32:39 -0800299func (c *RuleBuilderCommand) ImplicitOutput(path string) *RuleBuilderCommand {
300 c.outputs = append(c.outputs, path)
301 return c
302}
303
Colin Cross758290d2019-02-01 16:42:32 -0800304// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
305// the command line.
306func (c *RuleBuilderCommand) ImplicitOutputs(paths []string) *RuleBuilderCommand {
307 c.outputs = append(c.outputs, paths...)
308 return c
309}
310
311// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
312// will also be added to the dependencies returned by RuleBuilder.Inputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800313func (c *RuleBuilderCommand) FlagWithInput(flag, path string) *RuleBuilderCommand {
314 c.inputs = append(c.inputs, path)
315 return c.Text(flag + path)
316}
317
Colin Cross758290d2019-02-01 16:42:32 -0800318// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
319// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
320// RuleBuilder.Inputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800321func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths []string, sep string) *RuleBuilderCommand {
322 c.inputs = append(c.inputs, paths...)
323 return c.FlagWithList(flag, paths, sep)
324}
325
Colin Cross758290d2019-02-01 16:42:32 -0800326// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
327// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for
328// each input path.
329func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths []string) *RuleBuilderCommand {
330 for _, path := range paths {
331 c.FlagWithInput(flag, path)
332 }
333 return c
334}
335
336// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path
337// will also be added to the outputs returned by RuleBuilder.Outputs.
Colin Crossfeec25b2019-01-30 17:32:39 -0800338func (c *RuleBuilderCommand) FlagWithOutput(flag, path string) *RuleBuilderCommand {
339 c.outputs = append(c.outputs, path)
340 return c.Text(flag + path)
341}
342
Colin Cross758290d2019-02-01 16:42:32 -0800343// String returns the command line.
344func (c *RuleBuilderCommand) String() string {
345 return string(c.buf)
346}
347
Colin Crossfeec25b2019-01-30 17:32:39 -0800348type unknownRulePath struct {
349 path string
350}
351
352var _ Path = (*unknownRulePath)(nil)
353
354func (p *unknownRulePath) String() string { return p.path }
355func (p *unknownRulePath) Ext() string { return filepath.Ext(p.path) }
356func (p *unknownRulePath) Base() string { return filepath.Base(p.path) }
357func (p *unknownRulePath) Rel() string { return p.path }