blob: 61846feef75190a6d2a196a516b0d7aad2104f84 [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
Colin Cross47113642017-10-17 13:49:28 -070070 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.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070073}
74
Jamie Gennisd4e10182014-06-12 20:06:50 -070075// 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.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070079type BuildParams struct {
Dan Willemsen5c43e072016-10-25 21:26:12 -070080 Comment string // The comment that will appear above the definition.
Colin Crossaa873e12016-11-22 13:39:58 -080081 Depfile string // The dependency file name.
82 Deps Deps // The format of the dependency file.
Colin Cross7aa318f2017-05-08 17:50:22 -070083 Description string // The description that Ninja will print for the build.
Dan Willemsen5c43e072016-10-25 21:26:12 -070084 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 Args map[string]string // The variable/value pairs to set.
91 Optional bool // Skip outputting a default statement
Jamie Gennis1bc967e2014-05-27 16:34:41 -070092}
93
94// A poolDef describes a pool definition. It does not include the name of the
95// pool.
96type poolDef struct {
97 Comment string
98 Depth int
99}
100
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700101func parsePoolParams(scope scope, params *PoolParams) (*poolDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700102 error) {
103
104 def := &poolDef{
105 Comment: params.Comment,
106 Depth: params.Depth,
107 }
108
109 return def, nil
110}
111
112func (p *poolDef) WriteTo(nw *ninjaWriter, name string) error {
113 if p.Comment != "" {
114 err := nw.Comment(p.Comment)
115 if err != nil {
116 return err
117 }
118 }
119
120 err := nw.Pool(name)
121 if err != nil {
122 return err
123 }
124
125 return nw.ScopedAssign("depth", strconv.Itoa(p.Depth))
126}
127
128// A ruleDef describes a rule definition. It does not include the name of the
129// rule.
130type ruleDef struct {
Colin Cross47113642017-10-17 13:49:28 -0700131 CommandDeps []*ninjaString
132 CommandOrderOnly []*ninjaString
133 Comment string
134 Pool Pool
135 Variables map[string]*ninjaString
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700136}
137
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700138func parseRuleParams(scope scope, params *RuleParams) (*ruleDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700139 error) {
140
141 r := &ruleDef{
142 Comment: params.Comment,
143 Pool: params.Pool,
144 Variables: make(map[string]*ninjaString),
145 }
146
147 if params.Command == "" {
148 return nil, fmt.Errorf("encountered rule params with no command " +
149 "specified")
150 }
151
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700152 if r.Pool != nil && !scope.IsPoolVisible(r.Pool) {
153 return nil, fmt.Errorf("Pool %s is not visible in this scope", r.Pool)
154 }
155
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700156 value, err := parseNinjaString(scope, params.Command)
157 if err != nil {
158 return nil, fmt.Errorf("error parsing Command param: %s", err)
159 }
160 r.Variables["command"] = value
161
162 if params.Depfile != "" {
163 value, err = parseNinjaString(scope, params.Depfile)
164 if err != nil {
165 return nil, fmt.Errorf("error parsing Depfile param: %s", err)
166 }
167 r.Variables["depfile"] = value
168 }
169
170 if params.Deps != DepsNone {
171 r.Variables["deps"] = simpleNinjaString(params.Deps.String())
172 }
173
174 if params.Description != "" {
175 value, err = parseNinjaString(scope, params.Description)
176 if err != nil {
177 return nil, fmt.Errorf("error parsing Description param: %s", err)
178 }
179 r.Variables["description"] = value
180 }
181
182 if params.Generator {
183 r.Variables["generator"] = simpleNinjaString("true")
184 }
185
186 if params.Restat {
187 r.Variables["restat"] = simpleNinjaString("true")
188 }
189
190 if params.Rspfile != "" {
191 value, err = parseNinjaString(scope, params.Rspfile)
192 if err != nil {
193 return nil, fmt.Errorf("error parsing Rspfile param: %s", err)
194 }
195 r.Variables["rspfile"] = value
196 }
197
198 if params.RspfileContent != "" {
199 value, err = parseNinjaString(scope, params.RspfileContent)
200 if err != nil {
201 return nil, fmt.Errorf("error parsing RspfileContent param: %s",
202 err)
203 }
204 r.Variables["rspfile_content"] = value
205 }
206
Dan Willemsenfce63d32015-11-17 14:21:45 -0800207 r.CommandDeps, err = parseNinjaStrings(scope, params.CommandDeps)
208 if err != nil {
209 return nil, fmt.Errorf("error parsing CommandDeps param: %s", err)
210 }
211
Colin Cross47113642017-10-17 13:49:28 -0700212 r.CommandOrderOnly, err = parseNinjaStrings(scope, params.CommandOrderOnly)
213 if err != nil {
214 return nil, fmt.Errorf("error parsing CommandDeps param: %s", err)
215 }
216
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700217 return r, nil
218}
219
220func (r *ruleDef) WriteTo(nw *ninjaWriter, name string,
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800221 pkgNames map[*packageContext]string) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700222
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", r.Pool.fullName(pkgNames))
237 if err != nil {
238 return err
239 }
240 }
241
Colin Crossaa873e12016-11-22 13:39:58 -0800242 err = writeVariables(nw, r.Variables, pkgNames)
243 if err != nil {
244 return err
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700245 }
246
247 return nil
248}
249
250// A buildDef describes a build target definition.
251type buildDef struct {
Dan Willemsen5c43e072016-10-25 21:26:12 -0700252 Comment string
253 Rule Rule
254 RuleDef *ruleDef
255 Outputs []*ninjaString
256 ImplicitOutputs []*ninjaString
257 Inputs []*ninjaString
258 Implicits []*ninjaString
259 OrderOnly []*ninjaString
260 Args map[Variable]*ninjaString
Colin Crossaa873e12016-11-22 13:39:58 -0800261 Variables map[string]*ninjaString
Dan Willemsen5c43e072016-10-25 21:26:12 -0700262 Optional bool
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700263}
264
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700265func parseBuildParams(scope scope, params *BuildParams) (*buildDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700266 error) {
267
Doug Evansfcc67392015-11-08 12:21:58 -0800268 comment := params.Comment
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700269 rule := params.Rule
270
271 b := &buildDef{
Doug Evansfcc67392015-11-08 12:21:58 -0800272 Comment: comment,
Colin Crossa028bf42016-08-10 13:01:51 -0700273 Rule: rule,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700274 }
275
Colin Cross7aa318f2017-05-08 17:50:22 -0700276 setVariable := func(name string, value *ninjaString) {
277 if b.Variables == nil {
278 b.Variables = make(map[string]*ninjaString)
279 }
280 b.Variables[name] = value
281 }
282
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700283 if !scope.IsRuleVisible(rule) {
284 return nil, fmt.Errorf("Rule %s is not visible in this scope", rule)
285 }
286
287 if len(params.Outputs) == 0 {
288 return nil, errors.New("Outputs param has no elements")
289 }
290
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700291 var err error
292 b.Outputs, err = parseNinjaStrings(scope, params.Outputs)
293 if err != nil {
294 return nil, fmt.Errorf("error parsing Outputs param: %s", err)
295 }
296
Dan Willemsen5c43e072016-10-25 21:26:12 -0700297 b.ImplicitOutputs, err = parseNinjaStrings(scope, params.ImplicitOutputs)
298 if err != nil {
299 return nil, fmt.Errorf("error parsing ImplicitOutputs param: %s", err)
300 }
301
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700302 b.Inputs, err = parseNinjaStrings(scope, params.Inputs)
303 if err != nil {
304 return nil, fmt.Errorf("error parsing Inputs param: %s", err)
305 }
306
307 b.Implicits, err = parseNinjaStrings(scope, params.Implicits)
308 if err != nil {
309 return nil, fmt.Errorf("error parsing Implicits param: %s", err)
310 }
311
312 b.OrderOnly, err = parseNinjaStrings(scope, params.OrderOnly)
313 if err != nil {
314 return nil, fmt.Errorf("error parsing OrderOnly param: %s", err)
315 }
316
Colin Cross46f8d182014-11-06 17:21:33 -0800317 b.Optional = params.Optional
318
Colin Crossaa873e12016-11-22 13:39:58 -0800319 if params.Depfile != "" {
320 value, err := parseNinjaString(scope, params.Depfile)
321 if err != nil {
322 return nil, fmt.Errorf("error parsing Depfile param: %s", err)
323 }
Colin Cross7aa318f2017-05-08 17:50:22 -0700324 setVariable("depfile", value)
Colin Crossaa873e12016-11-22 13:39:58 -0800325 }
326
327 if params.Deps != DepsNone {
Colin Cross7aa318f2017-05-08 17:50:22 -0700328 setVariable("deps", simpleNinjaString(params.Deps.String()))
329 }
330
331 if params.Description != "" {
332 value, err := parseNinjaString(scope, params.Description)
333 if err != nil {
334 return nil, fmt.Errorf("error parsing Description param: %s", err)
335 }
336 setVariable("description", value)
Colin Crossaa873e12016-11-22 13:39:58 -0800337 }
338
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700339 argNameScope := rule.scope()
340
341 if len(params.Args) > 0 {
342 b.Args = make(map[Variable]*ninjaString)
343 for name, value := range params.Args {
344 if !rule.isArg(name) {
345 return nil, fmt.Errorf("unknown argument %q", name)
346 }
347
348 argVar, err := argNameScope.LookupVariable(name)
349 if err != nil {
350 // This shouldn't happen.
351 return nil, fmt.Errorf("argument lookup error: %s", err)
352 }
353
354 ninjaValue, err := parseNinjaString(scope, value)
355 if err != nil {
356 return nil, fmt.Errorf("error parsing variable %q: %s", name,
357 err)
358 }
359
360 b.Args[argVar] = ninjaValue
361 }
362 }
363
364 return b, nil
365}
366
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800367func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*packageContext]string) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700368 var (
Doug Evansfcc67392015-11-08 12:21:58 -0800369 comment = b.Comment
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700370 rule = b.Rule.fullName(pkgNames)
371 outputs = valueList(b.Outputs, pkgNames, outputEscaper)
Dan Willemsen5c43e072016-10-25 21:26:12 -0700372 implicitOuts = valueList(b.ImplicitOutputs, pkgNames, outputEscaper)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700373 explicitDeps = valueList(b.Inputs, pkgNames, inputEscaper)
374 implicitDeps = valueList(b.Implicits, pkgNames, inputEscaper)
375 orderOnlyDeps = valueList(b.OrderOnly, pkgNames, inputEscaper)
376 )
Dan Willemsenfce63d32015-11-17 14:21:45 -0800377
378 if b.RuleDef != nil {
379 implicitDeps = append(valueList(b.RuleDef.CommandDeps, pkgNames, inputEscaper), implicitDeps...)
Colin Cross47113642017-10-17 13:49:28 -0700380 orderOnlyDeps = append(valueList(b.RuleDef.CommandOrderOnly, pkgNames, inputEscaper), orderOnlyDeps...)
Dan Willemsenfce63d32015-11-17 14:21:45 -0800381 }
382
Dan Willemsen5c43e072016-10-25 21:26:12 -0700383 err := nw.Build(comment, rule, outputs, implicitOuts, explicitDeps, implicitDeps, orderOnlyDeps)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700384 if err != nil {
385 return err
386 }
387
Colin Crossadd65dd2014-10-22 11:04:14 -0700388 args := make(map[string]string)
389
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700390 for argVar, value := range b.Args {
Colin Crossadd65dd2014-10-22 11:04:14 -0700391 args[argVar.fullName(pkgNames)] = value.Value(pkgNames)
392 }
393
Colin Crossaa873e12016-11-22 13:39:58 -0800394 err = writeVariables(nw, b.Variables, pkgNames)
395 if err != nil {
396 return err
397 }
398
Colin Crossadd65dd2014-10-22 11:04:14 -0700399 var keys []string
400 for k := range args {
401 keys = append(keys, k)
402 }
403 sort.Strings(keys)
404
405 for _, name := range keys {
406 err = nw.ScopedAssign(name, args[name])
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700407 if err != nil {
408 return err
409 }
410 }
411
Colin Crossaa847f22014-11-06 14:26:38 -0800412 if !b.Optional {
Colin Crossde7afaa2019-01-23 13:23:00 -0800413 err = nw.Default(outputs...)
414 if err != nil {
415 return err
416 }
Colin Crossaa847f22014-11-06 14:26:38 -0800417 }
418
Doug Evans9c1cbb42015-11-09 11:37:39 -0800419 return nw.BlankLine()
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700420}
421
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800422func valueList(list []*ninjaString, pkgNames map[*packageContext]string,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700423 escaper *strings.Replacer) []string {
424
425 result := make([]string, len(list))
426 for i, ninjaStr := range list {
427 result[i] = ninjaStr.ValueWithEscaper(pkgNames, escaper)
428 }
429 return result
430}
Colin Crossaa873e12016-11-22 13:39:58 -0800431
432func writeVariables(nw *ninjaWriter, variables map[string]*ninjaString,
433 pkgNames map[*packageContext]string) error {
434 var keys []string
435 for k := range variables {
436 keys = append(keys, k)
437 }
438 sort.Strings(keys)
439
440 for _, name := range keys {
441 err := nw.ScopedAssign(name, variables[name].Value(pkgNames))
442 if err != nil {
443 return err
444 }
445 }
446 return nil
447}