blob: 8737ed22938b63393d9ecd7e9fd91ac8d0b74fe5 [file] [log] [blame]
Colin Cross8e0c5112015-01-23 14:15:10 -08001// 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
Jamie Gennis1bc967e2014-05-27 16:34:41 -070015package blueprint
16
17import (
Jamie Gennis48aed8c2014-06-13 18:25:54 -070018 "errors"
Jamie Gennis1bc967e2014-05-27 16:34:41 -070019 "fmt"
Colin Crossadd65dd2014-10-22 11:04:14 -070020 "sort"
Jamie Gennis1bc967e2014-05-27 16:34:41 -070021 "strconv"
22 "strings"
23)
24
Jamie Gennisd4e10182014-06-12 20:06:50 -070025// A Deps value indicates the dependency file format that Ninja should expect to
26// be output by a compiler.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070027type 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
Jamie Gennisd4e10182014-06-12 20:06:50 -070048// A PoolParams object contains the set of parameters that make up a Ninja pool
49// definition.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070050type PoolParams struct {
Jamie Gennisd4e10182014-06-12 20:06:50 -070051 Comment string // The comment that will appear above the definition.
52 Depth int // The Ninja pool depth.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070053}
54
Jamie Gennisd4e10182014-06-12 20:06:50 -070055// A RuleParams object contains the set of parameters that make up a Ninja rule
Dan Willemsenfce63d32015-11-17 14:21:45 -080056// definition.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070057type RuleParams struct {
Dan Willemsenfce63d32015-11-17 14:21:45 -080058 // These fields correspond to a Ninja variable of the same name.
Jamie Gennisd4e10182014-06-12 20:06:50 -070059 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.
Dan Willemsenfce63d32015-11-17 14:21:45 -080068
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.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070072}
73
Jamie Gennisd4e10182014-06-12 20:06:50 -070074// 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.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070078type BuildParams struct {
Dan Willemsen5c43e072016-10-25 21:26:12 -070079 Comment string // The comment that will appear above the definition.
Colin Crossaa873e12016-11-22 13:39:58 -080080 Depfile string // The dependency file name.
81 Deps Deps // The format of the dependency file.
Dan Willemsen5c43e072016-10-25 21:26:12 -070082 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
Jamie Gennis1bc967e2014-05-27 16:34:41 -070090}
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
Jamie Gennis48aed8c2014-06-13 18:25:54 -070099func parsePoolParams(scope scope, params *PoolParams) (*poolDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700100 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 {
Dan Willemsenfce63d32015-11-17 14:21:45 -0800129 CommandDeps []*ninjaString
130 Comment string
131 Pool Pool
132 Variables map[string]*ninjaString
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700133}
134
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700135func parseRuleParams(scope scope, params *RuleParams) (*ruleDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700136 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
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700149 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
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700153 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
Dan Willemsenfce63d32015-11-17 14:21:45 -0800204 r.CommandDeps, err = parseNinjaStrings(scope, params.CommandDeps)
205 if err != nil {
206 return nil, fmt.Errorf("error parsing CommandDeps param: %s", err)
207 }
208
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700209 return r, nil
210}
211
212func (r *ruleDef) WriteTo(nw *ninjaWriter, name string,
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800213 pkgNames map[*packageContext]string) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700214
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
Colin Crossaa873e12016-11-22 13:39:58 -0800234 err = writeVariables(nw, r.Variables, pkgNames)
235 if err != nil {
236 return err
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700237 }
238
239 return nil
240}
241
242// A buildDef describes a build target definition.
243type buildDef struct {
Dan Willemsen5c43e072016-10-25 21:26:12 -0700244 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
Colin Crossaa873e12016-11-22 13:39:58 -0800253 Variables map[string]*ninjaString
Dan Willemsen5c43e072016-10-25 21:26:12 -0700254 Optional bool
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700255}
256
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700257func parseBuildParams(scope scope, params *BuildParams) (*buildDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700258 error) {
259
Doug Evansfcc67392015-11-08 12:21:58 -0800260 comment := params.Comment
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700261 rule := params.Rule
262
263 b := &buildDef{
Doug Evansfcc67392015-11-08 12:21:58 -0800264 Comment: comment,
Colin Crossa028bf42016-08-10 13:01:51 -0700265 Rule: rule,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700266 }
267
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700268 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
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700276 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
Dan Willemsen5c43e072016-10-25 21:26:12 -0700282 b.ImplicitOutputs, err = parseNinjaStrings(scope, params.ImplicitOutputs)
283 if err != nil {
284 return nil, fmt.Errorf("error parsing ImplicitOutputs param: %s", err)
285 }
286
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700287 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
Colin Cross46f8d182014-11-06 17:21:33 -0800302 b.Optional = params.Optional
303
Colin Crossaa873e12016-11-22 13:39:58 -0800304 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
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700322 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
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800350func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*packageContext]string) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700351 var (
Doug Evansfcc67392015-11-08 12:21:58 -0800352 comment = b.Comment
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700353 rule = b.Rule.fullName(pkgNames)
354 outputs = valueList(b.Outputs, pkgNames, outputEscaper)
Dan Willemsen5c43e072016-10-25 21:26:12 -0700355 implicitOuts = valueList(b.ImplicitOutputs, pkgNames, outputEscaper)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700356 explicitDeps = valueList(b.Inputs, pkgNames, inputEscaper)
357 implicitDeps = valueList(b.Implicits, pkgNames, inputEscaper)
358 orderOnlyDeps = valueList(b.OrderOnly, pkgNames, inputEscaper)
359 )
Dan Willemsenfce63d32015-11-17 14:21:45 -0800360
361 if b.RuleDef != nil {
362 implicitDeps = append(valueList(b.RuleDef.CommandDeps, pkgNames, inputEscaper), implicitDeps...)
363 }
364
Dan Willemsen5c43e072016-10-25 21:26:12 -0700365 err := nw.Build(comment, rule, outputs, implicitOuts, explicitDeps, implicitDeps, orderOnlyDeps)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700366 if err != nil {
367 return err
368 }
369
Colin Crossadd65dd2014-10-22 11:04:14 -0700370 args := make(map[string]string)
371
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700372 for argVar, value := range b.Args {
Colin Crossadd65dd2014-10-22 11:04:14 -0700373 args[argVar.fullName(pkgNames)] = value.Value(pkgNames)
374 }
375
Colin Crossaa873e12016-11-22 13:39:58 -0800376 err = writeVariables(nw, b.Variables, pkgNames)
377 if err != nil {
378 return err
379 }
380
Colin Crossadd65dd2014-10-22 11:04:14 -0700381 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])
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700389 if err != nil {
390 return err
391 }
392 }
393
Colin Crossaa847f22014-11-06 14:26:38 -0800394 if !b.Optional {
395 nw.Default(outputs...)
396 }
397
Doug Evans9c1cbb42015-11-09 11:37:39 -0800398 return nw.BlankLine()
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700399}
400
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800401func valueList(list []*ninjaString, pkgNames map[*packageContext]string,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700402 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}
Colin Crossaa873e12016-11-22 13:39:58 -0800410
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}