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