blob: 69233c2979447a23dafc0f2fff1f9cf8a5f5d053 [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.
Jingwen Chenfd8af0b2020-10-06 08:52:27 +080059 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.
Dan Willemsenfce63d32015-11-17 14:21:45 -080069
70 // These fields are used internally in Blueprint
Colin Cross47113642017-10-17 13:49:28 -070071 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.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070074}
75
Jamie Gennisd4e10182014-06-12 20:06:50 -070076// 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.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070080type BuildParams struct {
Dan Willemsen5c43e072016-10-25 21:26:12 -070081 Comment string // The comment that will appear above the definition.
Colin Crossaa873e12016-11-22 13:39:58 -080082 Depfile string // The dependency file name.
83 Deps Deps // The format of the dependency file.
Colin Cross7aa318f2017-05-08 17:50:22 -070084 Description string // The description that Ninja will print for the build.
Dan Willemsen5c43e072016-10-25 21:26:12 -070085 Rule Rule // The rule to invoke.
86 Outputs []string // The list of explicit output targets.
87 ImplicitOutputs []string // The list of implicit output targets.
Jingwen Chenfd8af0b2020-10-06 08:52:27 +080088 SymlinkOutputs []string // The list of Outputs or ImplicitOutputs that are symlinks.
Dan Willemsen5c43e072016-10-25 21:26:12 -070089 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.
Colin Cross9ece72b2020-07-09 14:24:56 -070092 Validations []string // The list of validations to run when this rule runs.
Dan Willemsen5c43e072016-10-25 21:26:12 -070093 Args map[string]string // The variable/value pairs to set.
94 Optional bool // Skip outputting a default statement
Jamie Gennis1bc967e2014-05-27 16:34:41 -070095}
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
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700104func parsePoolParams(scope scope, params *PoolParams) (*poolDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700105 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 {
Colin Cross2ce594e2020-01-29 12:58:03 -0800134 CommandDeps []ninjaString
135 CommandOrderOnly []ninjaString
Colin Cross47113642017-10-17 13:49:28 -0700136 Comment string
137 Pool Pool
Colin Cross2ce594e2020-01-29 12:58:03 -0800138 Variables map[string]ninjaString
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700139}
140
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700141func parseRuleParams(scope scope, params *RuleParams) (*ruleDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700142 error) {
143
144 r := &ruleDef{
145 Comment: params.Comment,
146 Pool: params.Pool,
Colin Cross2ce594e2020-01-29 12:58:03 -0800147 Variables: make(map[string]ninjaString),
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700148 }
149
150 if params.Command == "" {
151 return nil, fmt.Errorf("encountered rule params with no command " +
152 "specified")
153 }
154
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700155 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
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700159 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
Jingwen Chenfd8af0b2020-10-06 08:52:27 +0800210 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
Dan Willemsenfce63d32015-11-17 14:21:45 -0800219 r.CommandDeps, err = parseNinjaStrings(scope, params.CommandDeps)
220 if err != nil {
221 return nil, fmt.Errorf("error parsing CommandDeps param: %s", err)
222 }
223
Colin Cross47113642017-10-17 13:49:28 -0700224 r.CommandOrderOnly, err = parseNinjaStrings(scope, params.CommandOrderOnly)
225 if err != nil {
226 return nil, fmt.Errorf("error parsing CommandDeps param: %s", err)
227 }
228
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700229 return r, nil
230}
231
232func (r *ruleDef) WriteTo(nw *ninjaWriter, name string,
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800233 pkgNames map[*packageContext]string) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700234
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
Colin Crossaa873e12016-11-22 13:39:58 -0800254 err = writeVariables(nw, r.Variables, pkgNames)
255 if err != nil {
256 return err
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700257 }
258
259 return nil
260}
261
262// A buildDef describes a build target definition.
263type buildDef struct {
Dan Willemsen5c43e072016-10-25 21:26:12 -0700264 Comment string
265 Rule Rule
266 RuleDef *ruleDef
Colin Cross2ce594e2020-01-29 12:58:03 -0800267 Outputs []ninjaString
268 ImplicitOutputs []ninjaString
269 Inputs []ninjaString
270 Implicits []ninjaString
271 OrderOnly []ninjaString
Colin Cross9ece72b2020-07-09 14:24:56 -0700272 Validations []ninjaString
Colin Cross2ce594e2020-01-29 12:58:03 -0800273 Args map[Variable]ninjaString
274 Variables map[string]ninjaString
Dan Willemsen5c43e072016-10-25 21:26:12 -0700275 Optional bool
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700276}
277
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700278func parseBuildParams(scope scope, params *BuildParams) (*buildDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700279 error) {
280
Doug Evansfcc67392015-11-08 12:21:58 -0800281 comment := params.Comment
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700282 rule := params.Rule
283
284 b := &buildDef{
Doug Evansfcc67392015-11-08 12:21:58 -0800285 Comment: comment,
Colin Crossa028bf42016-08-10 13:01:51 -0700286 Rule: rule,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700287 }
288
Colin Cross2ce594e2020-01-29 12:58:03 -0800289 setVariable := func(name string, value ninjaString) {
Colin Cross7aa318f2017-05-08 17:50:22 -0700290 if b.Variables == nil {
Colin Cross2ce594e2020-01-29 12:58:03 -0800291 b.Variables = make(map[string]ninjaString)
Colin Cross7aa318f2017-05-08 17:50:22 -0700292 }
293 b.Variables[name] = value
294 }
295
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700296 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
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700304 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
Dan Willemsen5c43e072016-10-25 21:26:12 -0700310 b.ImplicitOutputs, err = parseNinjaStrings(scope, params.ImplicitOutputs)
311 if err != nil {
312 return nil, fmt.Errorf("error parsing ImplicitOutputs param: %s", err)
313 }
314
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700315 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
Colin Cross9ece72b2020-07-09 14:24:56 -0700330 b.Validations, err = parseNinjaStrings(scope, params.Validations)
331 if err != nil {
332 return nil, fmt.Errorf("error parsing Validations param: %s", err)
333 }
334
Colin Cross46f8d182014-11-06 17:21:33 -0800335 b.Optional = params.Optional
336
Colin Crossaa873e12016-11-22 13:39:58 -0800337 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 }
Colin Cross7aa318f2017-05-08 17:50:22 -0700342 setVariable("depfile", value)
Colin Crossaa873e12016-11-22 13:39:58 -0800343 }
344
345 if params.Deps != DepsNone {
Colin Cross7aa318f2017-05-08 17:50:22 -0700346 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)
Colin Crossaa873e12016-11-22 13:39:58 -0800355 }
356
Jingwen Chenfd8af0b2020-10-06 08:52:27 +0800357 if len(params.SymlinkOutputs) > 0 {
358 setVariable(
359 "symlink_outputs",
360 simpleNinjaString(strings.Join(params.SymlinkOutputs, " ")))
361 }
362
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700363 argNameScope := rule.scope()
364
365 if len(params.Args) > 0 {
Colin Cross2ce594e2020-01-29 12:58:03 -0800366 b.Args = make(map[Variable]ninjaString)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700367 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
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800391func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*packageContext]string) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700392 var (
Doug Evansfcc67392015-11-08 12:21:58 -0800393 comment = b.Comment
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700394 rule = b.Rule.fullName(pkgNames)
Colin Cross8a401482021-01-21 18:27:14 -0800395 outputs = b.Outputs
396 implicitOuts = b.ImplicitOutputs
397 explicitDeps = b.Inputs
398 implicitDeps = b.Implicits
399 orderOnlyDeps = b.OrderOnly
400 validations = b.Validations
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700401 )
Dan Willemsenfce63d32015-11-17 14:21:45 -0800402
403 if b.RuleDef != nil {
Colin Cross8a401482021-01-21 18:27:14 -0800404 implicitDeps = append(b.RuleDef.CommandDeps, implicitDeps...)
405 orderOnlyDeps = append(b.RuleDef.CommandOrderOnly, orderOnlyDeps...)
Dan Willemsenfce63d32015-11-17 14:21:45 -0800406 }
407
Colin Cross8a401482021-01-21 18:27:14 -0800408 err := nw.Build(comment, rule, outputs, implicitOuts, explicitDeps, implicitDeps, orderOnlyDeps, validations, pkgNames)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700409 if err != nil {
410 return err
411 }
412
Colin Crossaa873e12016-11-22 13:39:58 -0800413 err = writeVariables(nw, b.Variables, pkgNames)
414 if err != nil {
415 return err
416 }
417
Colin Cross00890dd2021-01-21 15:29:16 -0800418 type nameValuePair struct {
419 name, value string
Colin Crossadd65dd2014-10-22 11:04:14 -0700420 }
Colin Crossadd65dd2014-10-22 11:04:14 -0700421
Colin Cross00890dd2021-01-21 15:29:16 -0800422 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)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700432 if err != nil {
433 return err
434 }
435 }
436
Colin Crossaa847f22014-11-06 14:26:38 -0800437 if !b.Optional {
Colin Cross8a401482021-01-21 18:27:14 -0800438 err = nw.Default(pkgNames, outputs...)
Colin Crossde7afaa2019-01-23 13:23:00 -0800439 if err != nil {
440 return err
441 }
Colin Crossaa847f22014-11-06 14:26:38 -0800442 }
443
Doug Evans9c1cbb42015-11-09 11:37:39 -0800444 return nw.BlankLine()
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700445}
446
Colin Cross2ce594e2020-01-29 12:58:03 -0800447func writeVariables(nw *ninjaWriter, variables map[string]ninjaString,
Colin Crossaa873e12016-11-22 13:39:58 -0800448 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}