• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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	"sort"
20	"strings"
21
22	"github.com/google/blueprint"
23	"github.com/google/blueprint/proptools"
24)
25
26// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
27// graph.
28type RuleBuilder struct {
29	commands       []*RuleBuilderCommand
30	installs       RuleBuilderInstalls
31	temporariesSet map[WritablePath]bool
32	restat         bool
33	missingDeps    []string
34}
35
36// NewRuleBuilder returns a newly created RuleBuilder.
37func NewRuleBuilder() *RuleBuilder {
38	return &RuleBuilder{
39		temporariesSet: make(map[WritablePath]bool),
40	}
41}
42
43// RuleBuilderInstall is a tuple of install from and to locations.
44type RuleBuilderInstall struct {
45	From Path
46	To   string
47}
48
49type 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		}
59		sb.WriteString(install.From.String())
60		sb.WriteRune(':')
61		sb.WriteString(install.To)
62	}
63	return sb.String()
64}
65
66// 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
75// Restat marks the rule as a restat rule, which will be passed to ModuleContext.Rule in BuildParams.Restat.
76func (r *RuleBuilder) Restat() *RuleBuilder {
77	r.restat = true
78	return r
79}
80
81// Install associates an output of the rule with an install location, which can be retrieved later using
82// RuleBuilder.Installs.
83func (r *RuleBuilder) Install(from Path, to string) {
84	r.installs = append(r.installs, RuleBuilderInstall{from, to})
85}
86
87// 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.
90func (r *RuleBuilder) Command() *RuleBuilderCommand {
91	command := &RuleBuilderCommand{}
92	r.commands = append(r.commands, command)
93	return command
94}
95
96// 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.
98func (r *RuleBuilder) Temporary(path WritablePath) {
99	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() {
105	var temporariesList WritablePaths
106
107	for intermediate := range r.temporariesSet {
108		temporariesList = append(temporariesList, intermediate)
109	}
110
111	sort.Slice(temporariesList, func(i, j int) bool {
112		return temporariesList[i].String() < temporariesList[j].String()
113	})
114
115	r.Command().Text("rm").Flag("-f").Outputs(temporariesList)
116}
117
118// 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.
121func (r *RuleBuilder) Inputs() Paths {
122	outputs := r.outputSet()
123
124	inputs := make(map[string]Path)
125	for _, c := range r.commands {
126		for _, input := range c.inputs {
127			if _, isOutput := outputs[input.String()]; !isOutput {
128				inputs[input.String()] = input
129			}
130		}
131	}
132
133	var inputList Paths
134	for _, input := range inputs {
135		inputList = append(inputList, input)
136	}
137
138	sort.Slice(inputList, func(i, j int) bool {
139		return inputList[i].String() < inputList[j].String()
140	})
141
142	return inputList
143}
144
145func (r *RuleBuilder) outputSet() map[string]WritablePath {
146	outputs := make(map[string]WritablePath)
147	for _, c := range r.commands {
148		for _, output := range c.outputs {
149			outputs[output.String()] = output
150		}
151	}
152	return outputs
153}
154
155// 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.
157func (r *RuleBuilder) Outputs() WritablePaths {
158	outputs := r.outputSet()
159
160	var outputList WritablePaths
161	for _, output := range outputs {
162		if !r.temporariesSet[output] {
163			outputList = append(outputList, output)
164		}
165	}
166
167	sort.Slice(outputList, func(i, j int) bool {
168		return outputList[i].String() < outputList[j].String()
169	})
170
171	return outputList
172}
173
174// DepFiles returns the list of paths that were passed to the RuleBuilderCommand methods that take depfile paths, such
175// as RuleBuilderCommand.DepFile or RuleBuilderCommand.FlagWithDepFile.
176func (r *RuleBuilder) DepFiles() WritablePaths {
177	var depFiles WritablePaths
178
179	for _, c := range r.commands {
180		for _, depFile := range c.depFiles {
181			depFiles = append(depFiles, depFile)
182		}
183	}
184
185	return depFiles
186}
187
188// Installs returns the list of tuples passed to Install.
189func (r *RuleBuilder) Installs() RuleBuilderInstalls {
190	return append(RuleBuilderInstalls(nil), r.installs...)
191}
192
193func (r *RuleBuilder) toolsSet() map[string]Path {
194	tools := make(map[string]Path)
195	for _, c := range r.commands {
196		for _, tool := range c.tools {
197			tools[tool.String()] = tool
198		}
199	}
200
201	return tools
202}
203
204// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method.
205func (r *RuleBuilder) Tools() Paths {
206	toolsSet := r.toolsSet()
207
208	var toolsList Paths
209	for _, tool := range toolsSet {
210		toolsList = append(toolsList, tool)
211	}
212
213	sort.Slice(toolsList, func(i, j int) bool {
214		return toolsList[i].String() < toolsList[j].String()
215	})
216
217	return toolsList
218}
219
220// Commands returns a slice containing a the built command line for each call to RuleBuilder.Command.
221func (r *RuleBuilder) Commands() []string {
222	var commands []string
223	for _, c := range r.commands {
224		commands = append(commands, string(c.buf))
225	}
226	return commands
227}
228
229// BuilderContext is a subset of ModuleContext and SingletonContext.
230type BuilderContext interface {
231	PathContext
232	Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
233	Build(PackageContext, BuildParams)
234}
235
236var _ BuilderContext = ModuleContext(nil)
237var _ BuilderContext = SingletonContext(nil)
238
239func (r *RuleBuilder) depFileMergerCmd(ctx PathContext, depFiles WritablePaths) *RuleBuilderCommand {
240	return (&RuleBuilderCommand{}).
241		Tool(ctx.Config().HostToolPath(ctx, "dep_fixer")).
242		Flags(depFiles.Strings())
243}
244
245// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
246// Outputs.
247func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string, desc string) {
248	name = ninjaNameEscape(name)
249
250	if len(r.missingDeps) > 0 {
251		ctx.Build(pctx, BuildParams{
252			Rule:        ErrorRule,
253			Outputs:     r.Outputs(),
254			Description: desc,
255			Args: map[string]string{
256				"error": "missing dependencies: " + strings.Join(r.missingDeps, ", "),
257			},
258		})
259		return
260	}
261
262	tools := r.Tools()
263	commands := r.Commands()
264
265	var depFile WritablePath
266	var depFormat blueprint.Deps
267	if depFiles := r.DepFiles(); len(depFiles) > 0 {
268		depFile = depFiles[0]
269		depFormat = blueprint.DepsGCC
270		if len(depFiles) > 1 {
271			// Add a command locally that merges all depfiles together into the first depfile.
272			cmd := r.depFileMergerCmd(ctx, depFiles)
273			commands = append(commands, string(cmd.buf))
274			tools = append(tools, cmd.tools...)
275		}
276	}
277
278	// Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
279	// ImplicitOutputs.  RuleBuilder never uses "$out", so the distinction between Outputs and ImplicitOutputs
280	// doesn't matter.
281	var output WritablePath
282	var implicitOutputs WritablePaths
283	if outputs := r.Outputs(); len(outputs) > 0 {
284		output = outputs[0]
285		implicitOutputs = outputs[1:]
286	}
287
288	if len(commands) > 0 {
289		ctx.Build(pctx, BuildParams{
290			Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
291				Command:     strings.Join(proptools.NinjaEscapeList(commands), " && "),
292				CommandDeps: tools.Strings(),
293				Restat:      r.restat,
294			}),
295			Implicits:       r.Inputs(),
296			Output:          output,
297			ImplicitOutputs: implicitOutputs,
298			Depfile:         depFile,
299			Deps:            depFormat,
300			Description:     desc,
301		})
302	}
303}
304
305// RuleBuilderCommand is a builder for a command in a command line.  It can be mutated by its methods to add to the
306// command and track dependencies.  The methods mutate the RuleBuilderCommand in place, as well as return the
307// RuleBuilderCommand, so they can be used chained or unchained.  All methods that add text implicitly add a single
308// space as a separator from the previous method.
309type RuleBuilderCommand struct {
310	buf      []byte
311	inputs   Paths
312	outputs  WritablePaths
313	depFiles WritablePaths
314	tools    Paths
315}
316
317// Text adds the specified raw text to the command line.  The text should not contain input or output paths or the
318// rule will not have them listed in its dependencies or outputs.
319func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
320	if len(c.buf) > 0 {
321		c.buf = append(c.buf, ' ')
322	}
323	c.buf = append(c.buf, text...)
324	return c
325}
326
327// Textf adds the specified formatted text to the command line.  The text should not contain input or output paths or
328// the rule will not have them listed in its dependencies or outputs.
329func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
330	return c.Text(fmt.Sprintf(format, a...))
331}
332
333// Flag adds the specified raw text to the command line.  The text should not contain input or output paths or the
334// rule will not have them listed in its dependencies or outputs.
335func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
336	return c.Text(flag)
337}
338
339// Flags adds the specified raw text to the command line.  The text should not contain input or output paths or the
340// rule will not have them listed in its dependencies or outputs.
341func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
342	for _, flag := range flags {
343		c.Text(flag)
344	}
345	return c
346}
347
348// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them.  The flag
349// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
350// outputs.
351func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
352	return c.Text(flag + arg)
353}
354
355// FlagForEachArg adds the specified flag joined with each argument to the command line.  The result is identical to
356// calling FlagWithArg for argument.
357func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand {
358	for _, arg := range args {
359		c.FlagWithArg(flag, arg)
360	}
361	return c
362}
363
364// FlagWithList adds the specified flag and list of arguments to the command line, with the arguments joined by sep
365// and no separator between the flag and arguments.  The flag and arguments should not contain input or output paths or
366// the rule will not have them listed in its dependencies or outputs.
367func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
368	return c.Text(flag + strings.Join(list, sep))
369}
370
371// Tool adds the specified tool path to the command line.  The path will be also added to the dependencies returned by
372// RuleBuilder.Tools.
373func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
374	c.tools = append(c.tools, path)
375	return c.Text(path.String())
376}
377
378// Input adds the specified input path to the command line.  The path will also be added to the dependencies returned by
379// RuleBuilder.Inputs.
380func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
381	c.inputs = append(c.inputs, path)
382	return c.Text(path.String())
383}
384
385// Inputs adds the specified input paths to the command line, separated by spaces.  The paths will also be added to the
386// dependencies returned by RuleBuilder.Inputs.
387func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
388	for _, path := range paths {
389		c.Input(path)
390	}
391	return c
392}
393
394// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
395// command line.
396func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
397	c.inputs = append(c.inputs, path)
398	return c
399}
400
401// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
402// command line.
403func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
404	c.inputs = append(c.inputs, paths...)
405	return c
406}
407
408// Output adds the specified output path to the command line.  The path will also be added to the outputs returned by
409// RuleBuilder.Outputs.
410func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
411	c.outputs = append(c.outputs, path)
412	return c.Text(path.String())
413}
414
415// Outputs adds the specified output paths to the command line, separated by spaces.  The paths will also be added to
416// the outputs returned by RuleBuilder.Outputs.
417func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
418	for _, path := range paths {
419		c.Output(path)
420	}
421	return c
422}
423
424// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
425// line, and causes RuleBuilder.Build file to set the depfile flag for ninja.  If multiple depfiles are added to
426// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
427func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
428	c.depFiles = append(c.depFiles, path)
429	return c.Text(path.String())
430}
431
432// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
433// the command line.
434func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
435	c.outputs = append(c.outputs, path)
436	return c
437}
438
439// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
440// the command line.
441func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
442	c.outputs = append(c.outputs, paths...)
443	return c
444}
445
446// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
447// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja.  If multiple depfiles
448// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
449// depfiles together.
450func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand {
451	c.depFiles = append(c.depFiles, path)
452	return c
453}
454
455// FlagWithInput adds the specified flag and input path to the command line, with no separator between them.  The path
456// will also be added to the dependencies returned by RuleBuilder.Inputs.
457func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
458	c.inputs = append(c.inputs, path)
459	return c.Text(flag + path.String())
460}
461
462// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
463// and no separator between the flag and inputs.  The input paths will also be added to the dependencies returned by
464// RuleBuilder.Inputs.
465func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
466	c.inputs = append(c.inputs, paths...)
467	return c.FlagWithList(flag, paths.Strings(), sep)
468}
469
470// FlagForEachInput adds the specified flag joined with each input path to the command line.  The input paths will also
471// be added to the dependencies returned by RuleBuilder.Inputs.  The result is identical to calling FlagWithInput for
472// each input path.
473func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
474	for _, path := range paths {
475		c.FlagWithInput(flag, path)
476	}
477	return c
478}
479
480// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them.  The path
481// will also be added to the outputs returned by RuleBuilder.Outputs.
482func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
483	c.outputs = append(c.outputs, path)
484	return c.Text(flag + path.String())
485}
486
487// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them.  The path
488// will also be added to the outputs returned by RuleBuilder.Outputs.
489func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
490	c.depFiles = append(c.depFiles, path)
491	return c.Text(flag + path.String())
492}
493
494// String returns the command line.
495func (c *RuleBuilderCommand) String() string {
496	return string(c.buf)
497}
498
499func ninjaNameEscape(s string) string {
500	b := []byte(s)
501	escaped := false
502	for i, c := range b {
503		valid := (c >= 'a' && c <= 'z') ||
504			(c >= 'A' && c <= 'Z') ||
505			(c >= '0' && c <= '9') ||
506			(c == '_') ||
507			(c == '-') ||
508			(c == '.')
509		if !valid {
510			b[i] = '_'
511			escaped = true
512		}
513	}
514	if escaped {
515		s = string(b)
516	}
517	return s
518}
519