• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2014 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 blueprint
16
17import (
18	"errors"
19	"fmt"
20	"sort"
21	"strconv"
22	"strings"
23
24	"github.com/google/blueprint/uniquelist"
25)
26
27// A Deps value indicates the dependency file format that Ninja should expect to
28// be output by a compiler.
29type Deps int
30
31const (
32	DepsNone Deps = iota
33	DepsGCC
34	DepsMSVC
35)
36
37func (d Deps) String() string {
38	switch d {
39	case DepsNone:
40		return "none"
41	case DepsGCC:
42		return "gcc"
43	case DepsMSVC:
44		return "msvc"
45	default:
46		panic(fmt.Sprintf("unknown deps value: %d", d))
47	}
48}
49
50// A PoolParams object contains the set of parameters that make up a Ninja pool
51// definition.
52type PoolParams struct {
53	Comment string // The comment that will appear above the definition.
54	Depth   int    // The Ninja pool depth.
55}
56
57// A RuleParams object contains the set of parameters that make up a Ninja rule
58// definition.
59type RuleParams struct {
60	// These fields correspond to a Ninja variable of the same name.
61	Command        string // The command that Ninja will run for the rule.
62	Depfile        string // The dependency file name.
63	Deps           Deps   // The format of the dependency file.
64	Description    string // The description that Ninja will print for the rule.
65	Generator      bool   // Whether the rule generates the Ninja manifest file.
66	Pool           Pool   // The Ninja pool to which the rule belongs.
67	Restat         bool   // Whether Ninja should re-stat the rule's outputs.
68	Rspfile        string // The response file.
69	RspfileContent string // The response file content.
70
71	// These fields are used internally in Blueprint
72	CommandDeps      []string // Command-specific implicit dependencies to prepend to builds
73	CommandOrderOnly []string // Command-specific order-only dependencies to prepend to builds
74	Comment          string   // The comment that will appear above the definition.
75}
76
77// A BuildParams object contains the set of parameters that make up a Ninja
78// build statement.  Each field except for Args corresponds with a part of the
79// Ninja build statement.  The Args field contains variable names and values
80// that are set within the build statement's scope in the Ninja file.
81type BuildParams struct {
82	Comment         string            // The comment that will appear above the definition.
83	Depfile         string            // The dependency file name.
84	Deps            Deps              // The format of the dependency file.
85	Description     string            // The description that Ninja will print for the build.
86	Rule            Rule              // The rule to invoke.
87	Outputs         []string          // The list of explicit output targets.
88	ImplicitOutputs []string          // The list of implicit output targets.
89	Inputs          []string          // The list of explicit input dependencies.
90	Implicits       []string          // The list of implicit input dependencies.
91	OrderOnly       []string          // The list of order-only dependencies.
92	Validations     []string          // The list of validations to run when this rule runs.
93	Args            map[string]string // The variable/value pairs to set.
94	Default         bool              // Output a ninja default statement
95	PhonyOutput     bool              // This is a phony_output
96}
97
98// A poolDef describes a pool definition.  It does not include the name of the
99// pool.
100type poolDef struct {
101	Comment string
102	Depth   int
103}
104
105func parsePoolParams(scope scope, params *PoolParams) (*poolDef,
106	error) {
107
108	def := &poolDef{
109		Comment: params.Comment,
110		Depth:   params.Depth,
111	}
112
113	return def, nil
114}
115
116func (p *poolDef) WriteTo(nw *ninjaWriter, name string) error {
117	if p.Comment != "" {
118		err := nw.Comment(p.Comment)
119		if err != nil {
120			return err
121		}
122	}
123
124	err := nw.Pool(name)
125	if err != nil {
126		return err
127	}
128
129	return nw.ScopedAssign("depth", strconv.Itoa(p.Depth))
130}
131
132// A ruleDef describes a rule definition.  It does not include the name of the
133// rule.
134type ruleDef struct {
135	CommandDeps      []*ninjaString
136	CommandOrderOnly []*ninjaString
137	Comment          string
138	Pool             Pool
139	Variables        map[string]*ninjaString
140}
141
142func parseRuleParams(scope scope, params *RuleParams) (*ruleDef,
143	error) {
144
145	r := &ruleDef{
146		Comment:   params.Comment,
147		Pool:      params.Pool,
148		Variables: make(map[string]*ninjaString),
149	}
150
151	if params.Command == "" {
152		return nil, fmt.Errorf("encountered rule params with no command " +
153			"specified")
154	}
155
156	if r.Pool != nil && !scope.IsPoolVisible(r.Pool) {
157		return nil, fmt.Errorf("Pool %s is not visible in this scope", r.Pool)
158	}
159
160	value, err := parseNinjaString(scope, params.Command)
161	if err != nil {
162		return nil, fmt.Errorf("error parsing Command param: %s", err)
163	}
164	r.Variables["command"] = value
165
166	if params.Depfile != "" {
167		value, err = parseNinjaString(scope, params.Depfile)
168		if err != nil {
169			return nil, fmt.Errorf("error parsing Depfile param: %s", err)
170		}
171		r.Variables["depfile"] = value
172	}
173
174	if params.Deps != DepsNone {
175		r.Variables["deps"] = simpleNinjaString(params.Deps.String())
176	}
177
178	if params.Description != "" {
179		value, err = parseNinjaString(scope, params.Description)
180		if err != nil {
181			return nil, fmt.Errorf("error parsing Description param: %s", err)
182		}
183		r.Variables["description"] = value
184	}
185
186	if params.Generator {
187		r.Variables["generator"] = simpleNinjaString("true")
188	}
189
190	if params.Restat {
191		r.Variables["restat"] = simpleNinjaString("true")
192	}
193
194	if params.Rspfile != "" {
195		value, err = parseNinjaString(scope, params.Rspfile)
196		if err != nil {
197			return nil, fmt.Errorf("error parsing Rspfile param: %s", err)
198		}
199		r.Variables["rspfile"] = value
200	}
201
202	if params.RspfileContent != "" {
203		value, err = parseNinjaString(scope, params.RspfileContent)
204		if err != nil {
205			return nil, fmt.Errorf("error parsing RspfileContent param: %s",
206				err)
207		}
208		r.Variables["rspfile_content"] = value
209	}
210
211	r.CommandDeps, err = parseNinjaStrings(scope, params.CommandDeps)
212	if err != nil {
213		return nil, fmt.Errorf("error parsing CommandDeps param: %s", err)
214	}
215
216	r.CommandOrderOnly, err = parseNinjaStrings(scope, params.CommandOrderOnly)
217	if err != nil {
218		return nil, fmt.Errorf("error parsing CommandDeps param: %s", err)
219	}
220
221	return r, nil
222}
223
224func (r *ruleDef) WriteTo(nw *ninjaWriter, name string, nameTracker *nameTracker) error {
225
226	if r.Comment != "" {
227		err := nw.Comment(r.Comment)
228		if err != nil {
229			return err
230		}
231	}
232
233	err := nw.Rule(name)
234	if err != nil {
235		return err
236	}
237
238	if r.Pool != nil {
239		err = nw.ScopedAssign("pool", nameTracker.Pool(r.Pool))
240		if err != nil {
241			return err
242		}
243	}
244
245	err = writeVariables(nw, r.Variables, nameTracker)
246	if err != nil {
247		return err
248	}
249
250	return nil
251}
252
253// A buildDef describes a build target definition.
254type buildDef struct {
255	Comment               string
256	Rule                  Rule
257	RuleDef               *ruleDef
258	Outputs               []*ninjaString
259	OutputStrings         []string
260	ImplicitOutputs       []*ninjaString
261	ImplicitOutputStrings []string
262	Inputs                []*ninjaString
263	InputStrings          []string
264	Implicits             []*ninjaString
265	ImplicitStrings       []string
266	OrderOnly             []*ninjaString
267	OrderOnlyStrings      uniquelist.UniqueList[string]
268	Validations           []*ninjaString
269	ValidationStrings     []string
270	Args                  map[Variable]*ninjaString
271	Variables             map[string]*ninjaString
272	Default               bool
273}
274
275func formatTags(tags map[string]string, rule Rule) string {
276	// Maps in golang do not have a guaranteed iteration order, nor is there an
277	// ordered map type in the stdlib, but we need to deterministically generate
278	// the ninja file.
279	keys := make([]string, 0, len(tags))
280	for k := range tags {
281		keys = append(keys, k)
282	}
283	sort.Strings(keys)
284	pairs := make([]string, 0, len(keys))
285	for _, k := range keys {
286		pairs = append(pairs, k+"="+tags[k])
287	}
288	pairs = append(pairs, "rule_name="+rule.name())
289	return strings.Join(pairs, ";")
290}
291
292func parseBuildParams(scope scope, params *BuildParams,
293	tags map[string]string) (*buildDef, error) {
294
295	comment := params.Comment
296	rule := params.Rule
297
298	b := &buildDef{
299		Comment: comment,
300		Rule:    rule,
301	}
302
303	setVariable := func(name string, value *ninjaString) {
304		if b.Variables == nil {
305			b.Variables = make(map[string]*ninjaString)
306		}
307		b.Variables[name] = value
308	}
309
310	if !scope.IsRuleVisible(rule) {
311		return nil, fmt.Errorf("Rule %s is not visible in this scope", rule)
312	}
313
314	if len(params.Outputs) == 0 {
315		return nil, errors.New("Outputs param has no elements")
316	}
317
318	var err error
319	b.Outputs, b.OutputStrings, err = parseNinjaOrSimpleStrings(scope, params.Outputs)
320	if err != nil {
321		return nil, fmt.Errorf("error parsing Outputs param: %s", err)
322	}
323
324	b.ImplicitOutputs, b.ImplicitOutputStrings, err = parseNinjaOrSimpleStrings(scope, params.ImplicitOutputs)
325	if err != nil {
326		return nil, fmt.Errorf("error parsing ImplicitOutputs param: %s", err)
327	}
328
329	b.Inputs, b.InputStrings, err = parseNinjaOrSimpleStrings(scope, params.Inputs)
330	if err != nil {
331		return nil, fmt.Errorf("error parsing Inputs param: %s", err)
332	}
333
334	b.Implicits, b.ImplicitStrings, err = parseNinjaOrSimpleStrings(scope, params.Implicits)
335	if err != nil {
336		return nil, fmt.Errorf("error parsing Implicits param: %s", err)
337	}
338
339	var orderOnlyStrings []string
340	b.OrderOnly, orderOnlyStrings, err = parseNinjaOrSimpleStrings(scope, params.OrderOnly)
341	if err != nil {
342		return nil, fmt.Errorf("error parsing OrderOnly param: %s", err)
343	}
344
345	b.OrderOnlyStrings = uniquelist.Make(orderOnlyStrings)
346
347	b.Validations, b.ValidationStrings, err = parseNinjaOrSimpleStrings(scope, params.Validations)
348	if err != nil {
349		return nil, fmt.Errorf("error parsing Validations param: %s", err)
350	}
351
352	b.Default = params.Default
353
354	if params.Depfile != "" {
355		value, err := parseNinjaString(scope, params.Depfile)
356		if err != nil {
357			return nil, fmt.Errorf("error parsing Depfile param: %s", err)
358		}
359		setVariable("depfile", value)
360	}
361
362	if params.Deps != DepsNone {
363		setVariable("deps", simpleNinjaString(params.Deps.String()))
364	}
365
366	if params.Description != "" {
367		value, err := parseNinjaString(scope, params.Description)
368		if err != nil {
369			return nil, fmt.Errorf("error parsing Description param: %s", err)
370		}
371		setVariable("description", value)
372	}
373
374	if len(tags) > 0 {
375		setVariable("tags", simpleNinjaString(formatTags(tags, rule)))
376	}
377
378	argNameScope := rule.scope()
379
380	if len(params.Args) > 0 {
381		b.Args = make(map[Variable]*ninjaString)
382		for name, value := range params.Args {
383			if !rule.isArg(name) {
384				return nil, fmt.Errorf("unknown argument %q", name)
385			}
386
387			argVar, err := argNameScope.LookupVariable(name)
388			if err != nil {
389				// This shouldn't happen.
390				return nil, fmt.Errorf("argument lookup error: %s", err)
391			}
392
393			ninjaValue, err := parseNinjaString(scope, value)
394			if err != nil {
395				return nil, fmt.Errorf("error parsing variable %q: %s", name,
396					err)
397			}
398
399			b.Args[argVar] = ninjaValue
400		}
401	}
402
403	if params.PhonyOutput {
404		setVariable("phony_output", simpleNinjaString("true"))
405	}
406
407	return b, nil
408}
409
410func (b *buildDef) WriteTo(nw *ninjaWriter, nameTracker *nameTracker) error {
411	var (
412		comment             = b.Comment
413		rule                = nameTracker.Rule(b.Rule)
414		outputs             = b.Outputs
415		implicitOuts        = b.ImplicitOutputs
416		explicitDeps        = b.Inputs
417		implicitDeps        = b.Implicits
418		orderOnlyDeps       = b.OrderOnly
419		validations         = b.Validations
420		outputStrings       = b.OutputStrings
421		implicitOutStrings  = b.ImplicitOutputStrings
422		explicitDepStrings  = b.InputStrings
423		implicitDepStrings  = b.ImplicitStrings
424		orderOnlyDepStrings = b.OrderOnlyStrings
425		validationStrings   = b.ValidationStrings
426	)
427
428	if b.RuleDef != nil {
429		implicitDeps = append(b.RuleDef.CommandDeps, implicitDeps...)
430		orderOnlyDeps = append(b.RuleDef.CommandOrderOnly, orderOnlyDeps...)
431	}
432
433	err := nw.Build(comment, rule, outputs, implicitOuts, explicitDeps, implicitDeps, orderOnlyDeps, validations,
434		outputStrings, implicitOutStrings, explicitDepStrings,
435		implicitDepStrings, orderOnlyDepStrings.ToSlice(), validationStrings,
436		nameTracker)
437	if err != nil {
438		return err
439	}
440
441	err = writeVariables(nw, b.Variables, nameTracker)
442	if err != nil {
443		return err
444	}
445
446	type nameValuePair struct {
447		name, value string
448	}
449
450	args := make([]nameValuePair, 0, len(b.Args))
451
452	for argVar, value := range b.Args {
453		fullName := nameTracker.Variable(argVar)
454		args = append(args, nameValuePair{fullName, value.Value(nameTracker)})
455	}
456	sort.Slice(args, func(i, j int) bool { return args[i].name < args[j].name })
457
458	for _, pair := range args {
459		err = nw.ScopedAssign(pair.name, pair.value)
460		if err != nil {
461			return err
462		}
463	}
464
465	if b.Default {
466		err = nw.Default(nameTracker, outputs, outputStrings)
467		if err != nil {
468			return err
469		}
470	}
471
472	return nw.BlankLine()
473}
474
475func writeVariables(nw *ninjaWriter, variables map[string]*ninjaString, nameTracker *nameTracker) error {
476	var keys []string
477	for k := range variables {
478		keys = append(keys, k)
479	}
480	sort.Strings(keys)
481
482	for _, name := range keys {
483		err := nw.ScopedAssign(name, variables[name].Value(nameTracker))
484		if err != nil {
485			return err
486		}
487	}
488	return nil
489}
490