blob: 3233d4128190eb453193ce45b1e17b0cc54df8f0 [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 {
Doug Evansfcc67392015-11-08 12:21:58 -080079 Comment string // The comment that will appear above the definition.
Jamie Gennisd4e10182014-06-12 20:06:50 -070080 Rule Rule // The rule to invoke.
81 Outputs []string // The list of output targets.
82 Inputs []string // The list of explicit input dependencies.
83 Implicits []string // The list of implicit dependencies.
84 OrderOnly []string // The list of order-only dependencies.
85 Args map[string]string // The variable/value pairs to set.
Colin Cross46f8d182014-11-06 17:21:33 -080086 Optional bool // Skip outputting a default statement
Jamie Gennis1bc967e2014-05-27 16:34:41 -070087}
88
89// A poolDef describes a pool definition. It does not include the name of the
90// pool.
91type poolDef struct {
92 Comment string
93 Depth int
94}
95
Jamie Gennis48aed8c2014-06-13 18:25:54 -070096func parsePoolParams(scope scope, params *PoolParams) (*poolDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -070097 error) {
98
99 def := &poolDef{
100 Comment: params.Comment,
101 Depth: params.Depth,
102 }
103
104 return def, nil
105}
106
107func (p *poolDef) WriteTo(nw *ninjaWriter, name string) error {
108 if p.Comment != "" {
109 err := nw.Comment(p.Comment)
110 if err != nil {
111 return err
112 }
113 }
114
115 err := nw.Pool(name)
116 if err != nil {
117 return err
118 }
119
120 return nw.ScopedAssign("depth", strconv.Itoa(p.Depth))
121}
122
123// A ruleDef describes a rule definition. It does not include the name of the
124// rule.
125type ruleDef struct {
Dan Willemsenfce63d32015-11-17 14:21:45 -0800126 CommandDeps []*ninjaString
127 Comment string
128 Pool Pool
129 Variables map[string]*ninjaString
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700130}
131
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700132func parseRuleParams(scope scope, params *RuleParams) (*ruleDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700133 error) {
134
135 r := &ruleDef{
136 Comment: params.Comment,
137 Pool: params.Pool,
138 Variables: make(map[string]*ninjaString),
139 }
140
141 if params.Command == "" {
142 return nil, fmt.Errorf("encountered rule params with no command " +
143 "specified")
144 }
145
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700146 if r.Pool != nil && !scope.IsPoolVisible(r.Pool) {
147 return nil, fmt.Errorf("Pool %s is not visible in this scope", r.Pool)
148 }
149
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700150 value, err := parseNinjaString(scope, params.Command)
151 if err != nil {
152 return nil, fmt.Errorf("error parsing Command param: %s", err)
153 }
154 r.Variables["command"] = value
155
156 if params.Depfile != "" {
157 value, err = parseNinjaString(scope, params.Depfile)
158 if err != nil {
159 return nil, fmt.Errorf("error parsing Depfile param: %s", err)
160 }
161 r.Variables["depfile"] = value
162 }
163
164 if params.Deps != DepsNone {
165 r.Variables["deps"] = simpleNinjaString(params.Deps.String())
166 }
167
168 if params.Description != "" {
169 value, err = parseNinjaString(scope, params.Description)
170 if err != nil {
171 return nil, fmt.Errorf("error parsing Description param: %s", err)
172 }
173 r.Variables["description"] = value
174 }
175
176 if params.Generator {
177 r.Variables["generator"] = simpleNinjaString("true")
178 }
179
180 if params.Restat {
181 r.Variables["restat"] = simpleNinjaString("true")
182 }
183
184 if params.Rspfile != "" {
185 value, err = parseNinjaString(scope, params.Rspfile)
186 if err != nil {
187 return nil, fmt.Errorf("error parsing Rspfile param: %s", err)
188 }
189 r.Variables["rspfile"] = value
190 }
191
192 if params.RspfileContent != "" {
193 value, err = parseNinjaString(scope, params.RspfileContent)
194 if err != nil {
195 return nil, fmt.Errorf("error parsing RspfileContent param: %s",
196 err)
197 }
198 r.Variables["rspfile_content"] = value
199 }
200
Dan Willemsenfce63d32015-11-17 14:21:45 -0800201 r.CommandDeps, err = parseNinjaStrings(scope, params.CommandDeps)
202 if err != nil {
203 return nil, fmt.Errorf("error parsing CommandDeps param: %s", err)
204 }
205
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700206 return r, nil
207}
208
209func (r *ruleDef) WriteTo(nw *ninjaWriter, name string,
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800210 pkgNames map[*packageContext]string) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700211
212 if r.Comment != "" {
213 err := nw.Comment(r.Comment)
214 if err != nil {
215 return err
216 }
217 }
218
219 err := nw.Rule(name)
220 if err != nil {
221 return err
222 }
223
224 if r.Pool != nil {
225 err = nw.ScopedAssign("pool", r.Pool.fullName(pkgNames))
226 if err != nil {
227 return err
228 }
229 }
230
Colin Crossadd65dd2014-10-22 11:04:14 -0700231 var keys []string
232 for k := range r.Variables {
233 keys = append(keys, k)
234 }
235 sort.Strings(keys)
236
237 for _, name := range keys {
238 err = nw.ScopedAssign(name, r.Variables[name].Value(pkgNames))
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700239 if err != nil {
240 return err
241 }
242 }
243
244 return nil
245}
246
247// A buildDef describes a build target definition.
248type buildDef struct {
Doug Evansfcc67392015-11-08 12:21:58 -0800249 Comment string
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700250 Rule Rule
Dan Willemsenfce63d32015-11-17 14:21:45 -0800251 RuleDef *ruleDef
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700252 Outputs []*ninjaString
253 Inputs []*ninjaString
254 Implicits []*ninjaString
255 OrderOnly []*ninjaString
256 Args map[Variable]*ninjaString
Colin Crossaa847f22014-11-06 14:26:38 -0800257 Optional bool
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700258}
259
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700260func parseBuildParams(scope scope, params *BuildParams) (*buildDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700261 error) {
262
Doug Evansfcc67392015-11-08 12:21:58 -0800263 comment := params.Comment
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700264 rule := params.Rule
265
266 b := &buildDef{
Doug Evansfcc67392015-11-08 12:21:58 -0800267 Comment: comment,
Colin Crossa028bf42016-08-10 13:01:51 -0700268 Rule: rule,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700269 }
270
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700271 if !scope.IsRuleVisible(rule) {
272 return nil, fmt.Errorf("Rule %s is not visible in this scope", rule)
273 }
274
275 if len(params.Outputs) == 0 {
276 return nil, errors.New("Outputs param has no elements")
277 }
278
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700279 var err error
280 b.Outputs, err = parseNinjaStrings(scope, params.Outputs)
281 if err != nil {
282 return nil, fmt.Errorf("error parsing Outputs param: %s", err)
283 }
284
285 b.Inputs, err = parseNinjaStrings(scope, params.Inputs)
286 if err != nil {
287 return nil, fmt.Errorf("error parsing Inputs param: %s", err)
288 }
289
290 b.Implicits, err = parseNinjaStrings(scope, params.Implicits)
291 if err != nil {
292 return nil, fmt.Errorf("error parsing Implicits param: %s", err)
293 }
294
295 b.OrderOnly, err = parseNinjaStrings(scope, params.OrderOnly)
296 if err != nil {
297 return nil, fmt.Errorf("error parsing OrderOnly param: %s", err)
298 }
299
Colin Cross46f8d182014-11-06 17:21:33 -0800300 b.Optional = params.Optional
301
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700302 argNameScope := rule.scope()
303
304 if len(params.Args) > 0 {
305 b.Args = make(map[Variable]*ninjaString)
306 for name, value := range params.Args {
307 if !rule.isArg(name) {
308 return nil, fmt.Errorf("unknown argument %q", name)
309 }
310
311 argVar, err := argNameScope.LookupVariable(name)
312 if err != nil {
313 // This shouldn't happen.
314 return nil, fmt.Errorf("argument lookup error: %s", err)
315 }
316
317 ninjaValue, err := parseNinjaString(scope, value)
318 if err != nil {
319 return nil, fmt.Errorf("error parsing variable %q: %s", name,
320 err)
321 }
322
323 b.Args[argVar] = ninjaValue
324 }
325 }
326
327 return b, nil
328}
329
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800330func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*packageContext]string) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700331 var (
Doug Evansfcc67392015-11-08 12:21:58 -0800332 comment = b.Comment
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700333 rule = b.Rule.fullName(pkgNames)
334 outputs = valueList(b.Outputs, pkgNames, outputEscaper)
335 explicitDeps = valueList(b.Inputs, pkgNames, inputEscaper)
336 implicitDeps = valueList(b.Implicits, pkgNames, inputEscaper)
337 orderOnlyDeps = valueList(b.OrderOnly, pkgNames, inputEscaper)
338 )
Dan Willemsenfce63d32015-11-17 14:21:45 -0800339
340 if b.RuleDef != nil {
341 implicitDeps = append(valueList(b.RuleDef.CommandDeps, pkgNames, inputEscaper), implicitDeps...)
342 }
343
Doug Evansfcc67392015-11-08 12:21:58 -0800344 err := nw.Build(comment, rule, outputs, explicitDeps, implicitDeps, orderOnlyDeps)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700345 if err != nil {
346 return err
347 }
348
Colin Crossadd65dd2014-10-22 11:04:14 -0700349 args := make(map[string]string)
350
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700351 for argVar, value := range b.Args {
Colin Crossadd65dd2014-10-22 11:04:14 -0700352 args[argVar.fullName(pkgNames)] = value.Value(pkgNames)
353 }
354
355 var keys []string
356 for k := range args {
357 keys = append(keys, k)
358 }
359 sort.Strings(keys)
360
361 for _, name := range keys {
362 err = nw.ScopedAssign(name, args[name])
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700363 if err != nil {
364 return err
365 }
366 }
367
Colin Crossaa847f22014-11-06 14:26:38 -0800368 if !b.Optional {
369 nw.Default(outputs...)
370 }
371
Doug Evans9c1cbb42015-11-09 11:37:39 -0800372 return nw.BlankLine()
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700373}
374
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800375func valueList(list []*ninjaString, pkgNames map[*packageContext]string,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700376 escaper *strings.Replacer) []string {
377
378 result := make([]string, len(list))
379 for i, ninjaStr := range list {
380 result[i] = ninjaStr.ValueWithEscaper(pkgNames, escaper)
381 }
382 return result
383}