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