blob: cda2559957ef992c199d3fc1800b5a96b9025681 [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.
80 Rule Rule // The rule to invoke.
81 Outputs []string // The list of explicit output targets.
82 ImplicitOutputs []string // The list of implicit output targets.
83 Inputs []string // The list of explicit input dependencies.
84 Implicits []string // The list of implicit input dependencies.
85 OrderOnly []string // The list of order-only dependencies.
86 Args map[string]string // The variable/value pairs to set.
87 Optional bool // Skip outputting a default statement
Jamie Gennis1bc967e2014-05-27 16:34:41 -070088}
89
90// A poolDef describes a pool definition. It does not include the name of the
91// pool.
92type poolDef struct {
93 Comment string
94 Depth int
95}
96
Jamie Gennis48aed8c2014-06-13 18:25:54 -070097func parsePoolParams(scope scope, params *PoolParams) (*poolDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -070098 error) {
99
100 def := &poolDef{
101 Comment: params.Comment,
102 Depth: params.Depth,
103 }
104
105 return def, nil
106}
107
108func (p *poolDef) WriteTo(nw *ninjaWriter, name string) error {
109 if p.Comment != "" {
110 err := nw.Comment(p.Comment)
111 if err != nil {
112 return err
113 }
114 }
115
116 err := nw.Pool(name)
117 if err != nil {
118 return err
119 }
120
121 return nw.ScopedAssign("depth", strconv.Itoa(p.Depth))
122}
123
124// A ruleDef describes a rule definition. It does not include the name of the
125// rule.
126type ruleDef struct {
Dan Willemsenfce63d32015-11-17 14:21:45 -0800127 CommandDeps []*ninjaString
128 Comment string
129 Pool Pool
130 Variables map[string]*ninjaString
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700131}
132
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700133func parseRuleParams(scope scope, params *RuleParams) (*ruleDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700134 error) {
135
136 r := &ruleDef{
137 Comment: params.Comment,
138 Pool: params.Pool,
139 Variables: make(map[string]*ninjaString),
140 }
141
142 if params.Command == "" {
143 return nil, fmt.Errorf("encountered rule params with no command " +
144 "specified")
145 }
146
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700147 if r.Pool != nil && !scope.IsPoolVisible(r.Pool) {
148 return nil, fmt.Errorf("Pool %s is not visible in this scope", r.Pool)
149 }
150
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700151 value, err := parseNinjaString(scope, params.Command)
152 if err != nil {
153 return nil, fmt.Errorf("error parsing Command param: %s", err)
154 }
155 r.Variables["command"] = value
156
157 if params.Depfile != "" {
158 value, err = parseNinjaString(scope, params.Depfile)
159 if err != nil {
160 return nil, fmt.Errorf("error parsing Depfile param: %s", err)
161 }
162 r.Variables["depfile"] = value
163 }
164
165 if params.Deps != DepsNone {
166 r.Variables["deps"] = simpleNinjaString(params.Deps.String())
167 }
168
169 if params.Description != "" {
170 value, err = parseNinjaString(scope, params.Description)
171 if err != nil {
172 return nil, fmt.Errorf("error parsing Description param: %s", err)
173 }
174 r.Variables["description"] = value
175 }
176
177 if params.Generator {
178 r.Variables["generator"] = simpleNinjaString("true")
179 }
180
181 if params.Restat {
182 r.Variables["restat"] = simpleNinjaString("true")
183 }
184
185 if params.Rspfile != "" {
186 value, err = parseNinjaString(scope, params.Rspfile)
187 if err != nil {
188 return nil, fmt.Errorf("error parsing Rspfile param: %s", err)
189 }
190 r.Variables["rspfile"] = value
191 }
192
193 if params.RspfileContent != "" {
194 value, err = parseNinjaString(scope, params.RspfileContent)
195 if err != nil {
196 return nil, fmt.Errorf("error parsing RspfileContent param: %s",
197 err)
198 }
199 r.Variables["rspfile_content"] = value
200 }
201
Dan Willemsenfce63d32015-11-17 14:21:45 -0800202 r.CommandDeps, err = parseNinjaStrings(scope, params.CommandDeps)
203 if err != nil {
204 return nil, fmt.Errorf("error parsing CommandDeps param: %s", err)
205 }
206
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700207 return r, nil
208}
209
210func (r *ruleDef) WriteTo(nw *ninjaWriter, name string,
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800211 pkgNames map[*packageContext]string) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700212
213 if r.Comment != "" {
214 err := nw.Comment(r.Comment)
215 if err != nil {
216 return err
217 }
218 }
219
220 err := nw.Rule(name)
221 if err != nil {
222 return err
223 }
224
225 if r.Pool != nil {
226 err = nw.ScopedAssign("pool", r.Pool.fullName(pkgNames))
227 if err != nil {
228 return err
229 }
230 }
231
Colin Crossadd65dd2014-10-22 11:04:14 -0700232 var keys []string
233 for k := range r.Variables {
234 keys = append(keys, k)
235 }
236 sort.Strings(keys)
237
238 for _, name := range keys {
239 err = nw.ScopedAssign(name, r.Variables[name].Value(pkgNames))
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700240 if err != nil {
241 return err
242 }
243 }
244
245 return nil
246}
247
248// A buildDef describes a build target definition.
249type buildDef struct {
Dan Willemsen5c43e072016-10-25 21:26:12 -0700250 Comment string
251 Rule Rule
252 RuleDef *ruleDef
253 Outputs []*ninjaString
254 ImplicitOutputs []*ninjaString
255 Inputs []*ninjaString
256 Implicits []*ninjaString
257 OrderOnly []*ninjaString
258 Args map[Variable]*ninjaString
259 Optional bool
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700260}
261
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700262func parseBuildParams(scope scope, params *BuildParams) (*buildDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700263 error) {
264
Doug Evansfcc67392015-11-08 12:21:58 -0800265 comment := params.Comment
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700266 rule := params.Rule
267
268 b := &buildDef{
Doug Evansfcc67392015-11-08 12:21:58 -0800269 Comment: comment,
Colin Crossa028bf42016-08-10 13:01:51 -0700270 Rule: rule,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700271 }
272
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700273 if !scope.IsRuleVisible(rule) {
274 return nil, fmt.Errorf("Rule %s is not visible in this scope", rule)
275 }
276
277 if len(params.Outputs) == 0 {
278 return nil, errors.New("Outputs param has no elements")
279 }
280
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700281 var err error
282 b.Outputs, err = parseNinjaStrings(scope, params.Outputs)
283 if err != nil {
284 return nil, fmt.Errorf("error parsing Outputs param: %s", err)
285 }
286
Dan Willemsen5c43e072016-10-25 21:26:12 -0700287 b.ImplicitOutputs, err = parseNinjaStrings(scope, params.ImplicitOutputs)
288 if err != nil {
289 return nil, fmt.Errorf("error parsing ImplicitOutputs param: %s", err)
290 }
291
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700292 b.Inputs, err = parseNinjaStrings(scope, params.Inputs)
293 if err != nil {
294 return nil, fmt.Errorf("error parsing Inputs param: %s", err)
295 }
296
297 b.Implicits, err = parseNinjaStrings(scope, params.Implicits)
298 if err != nil {
299 return nil, fmt.Errorf("error parsing Implicits param: %s", err)
300 }
301
302 b.OrderOnly, err = parseNinjaStrings(scope, params.OrderOnly)
303 if err != nil {
304 return nil, fmt.Errorf("error parsing OrderOnly param: %s", err)
305 }
306
Colin Cross46f8d182014-11-06 17:21:33 -0800307 b.Optional = params.Optional
308
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700309 argNameScope := rule.scope()
310
311 if len(params.Args) > 0 {
312 b.Args = make(map[Variable]*ninjaString)
313 for name, value := range params.Args {
314 if !rule.isArg(name) {
315 return nil, fmt.Errorf("unknown argument %q", name)
316 }
317
318 argVar, err := argNameScope.LookupVariable(name)
319 if err != nil {
320 // This shouldn't happen.
321 return nil, fmt.Errorf("argument lookup error: %s", err)
322 }
323
324 ninjaValue, err := parseNinjaString(scope, value)
325 if err != nil {
326 return nil, fmt.Errorf("error parsing variable %q: %s", name,
327 err)
328 }
329
330 b.Args[argVar] = ninjaValue
331 }
332 }
333
334 return b, nil
335}
336
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800337func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*packageContext]string) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700338 var (
Doug Evansfcc67392015-11-08 12:21:58 -0800339 comment = b.Comment
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700340 rule = b.Rule.fullName(pkgNames)
341 outputs = valueList(b.Outputs, pkgNames, outputEscaper)
Dan Willemsen5c43e072016-10-25 21:26:12 -0700342 implicitOuts = valueList(b.ImplicitOutputs, pkgNames, outputEscaper)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700343 explicitDeps = valueList(b.Inputs, pkgNames, inputEscaper)
344 implicitDeps = valueList(b.Implicits, pkgNames, inputEscaper)
345 orderOnlyDeps = valueList(b.OrderOnly, pkgNames, inputEscaper)
346 )
Dan Willemsenfce63d32015-11-17 14:21:45 -0800347
348 if b.RuleDef != nil {
349 implicitDeps = append(valueList(b.RuleDef.CommandDeps, pkgNames, inputEscaper), implicitDeps...)
350 }
351
Dan Willemsen5c43e072016-10-25 21:26:12 -0700352 err := nw.Build(comment, rule, outputs, implicitOuts, explicitDeps, implicitDeps, orderOnlyDeps)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700353 if err != nil {
354 return err
355 }
356
Colin Crossadd65dd2014-10-22 11:04:14 -0700357 args := make(map[string]string)
358
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700359 for argVar, value := range b.Args {
Colin Crossadd65dd2014-10-22 11:04:14 -0700360 args[argVar.fullName(pkgNames)] = value.Value(pkgNames)
361 }
362
363 var keys []string
364 for k := range args {
365 keys = append(keys, k)
366 }
367 sort.Strings(keys)
368
369 for _, name := range keys {
370 err = nw.ScopedAssign(name, args[name])
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700371 if err != nil {
372 return err
373 }
374 }
375
Colin Crossaa847f22014-11-06 14:26:38 -0800376 if !b.Optional {
377 nw.Default(outputs...)
378 }
379
Doug Evans9c1cbb42015-11-09 11:37:39 -0800380 return nw.BlankLine()
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700381}
382
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800383func valueList(list []*ninjaString, pkgNames map[*packageContext]string,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700384 escaper *strings.Replacer) []string {
385
386 result := make([]string, len(list))
387 for i, ninjaStr := range list {
388 result[i] = ninjaStr.ValueWithEscaper(pkgNames, escaper)
389 }
390 return result
391}