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