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