blob: b87a38afe1f67cfb2c679e3c2ee50224f87d348e [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
56// definition. Each field except for Comment corresponds with a Ninja variable
57// of the same name.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070058type RuleParams struct {
Jamie Gennisd4e10182014-06-12 20:06:50 -070059 Comment string // The comment that will appear above the definition.
60 Command string // The command that Ninja will run for the rule.
61 Depfile string // The dependency file name.
62 Deps Deps // The format of the dependency file.
63 Description string // The description that Ninja will print for the rule.
64 Generator bool // Whether the rule generates the Ninja manifest file.
65 Pool Pool // The Ninja pool to which the rule belongs.
66 Restat bool // Whether Ninja should re-stat the rule's outputs.
67 Rspfile string // The response file.
68 RspfileContent string // The response file content.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070069}
70
Jamie Gennisd4e10182014-06-12 20:06:50 -070071// A BuildParams object contains the set of parameters that make up a Ninja
72// build statement. Each field except for Args corresponds with a part of the
73// Ninja build statement. The Args field contains variable names and values
74// that are set within the build statement's scope in the Ninja file.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070075type BuildParams struct {
Jamie Gennisd4e10182014-06-12 20:06:50 -070076 Rule Rule // The rule to invoke.
77 Outputs []string // The list of output targets.
78 Inputs []string // The list of explicit input dependencies.
79 Implicits []string // The list of implicit dependencies.
80 OrderOnly []string // The list of order-only dependencies.
81 Args map[string]string // The variable/value pairs to set.
Colin Cross46f8d182014-11-06 17:21:33 -080082 Optional bool // Skip outputting a default statement
Jamie Gennis1bc967e2014-05-27 16:34:41 -070083}
84
85// A poolDef describes a pool definition. It does not include the name of the
86// pool.
87type poolDef struct {
88 Comment string
89 Depth int
90}
91
Jamie Gennis48aed8c2014-06-13 18:25:54 -070092func parsePoolParams(scope scope, params *PoolParams) (*poolDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -070093 error) {
94
95 def := &poolDef{
96 Comment: params.Comment,
97 Depth: params.Depth,
98 }
99
100 return def, nil
101}
102
103func (p *poolDef) WriteTo(nw *ninjaWriter, name string) error {
104 if p.Comment != "" {
105 err := nw.Comment(p.Comment)
106 if err != nil {
107 return err
108 }
109 }
110
111 err := nw.Pool(name)
112 if err != nil {
113 return err
114 }
115
116 return nw.ScopedAssign("depth", strconv.Itoa(p.Depth))
117}
118
119// A ruleDef describes a rule definition. It does not include the name of the
120// rule.
121type ruleDef struct {
122 Comment string
123 Pool Pool
124 Variables map[string]*ninjaString
125}
126
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700127func parseRuleParams(scope scope, params *RuleParams) (*ruleDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700128 error) {
129
130 r := &ruleDef{
131 Comment: params.Comment,
132 Pool: params.Pool,
133 Variables: make(map[string]*ninjaString),
134 }
135
136 if params.Command == "" {
137 return nil, fmt.Errorf("encountered rule params with no command " +
138 "specified")
139 }
140
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700141 if r.Pool != nil && !scope.IsPoolVisible(r.Pool) {
142 return nil, fmt.Errorf("Pool %s is not visible in this scope", r.Pool)
143 }
144
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700145 value, err := parseNinjaString(scope, params.Command)
146 if err != nil {
147 return nil, fmt.Errorf("error parsing Command param: %s", err)
148 }
149 r.Variables["command"] = value
150
151 if params.Depfile != "" {
152 value, err = parseNinjaString(scope, params.Depfile)
153 if err != nil {
154 return nil, fmt.Errorf("error parsing Depfile param: %s", err)
155 }
156 r.Variables["depfile"] = value
157 }
158
159 if params.Deps != DepsNone {
160 r.Variables["deps"] = simpleNinjaString(params.Deps.String())
161 }
162
163 if params.Description != "" {
164 value, err = parseNinjaString(scope, params.Description)
165 if err != nil {
166 return nil, fmt.Errorf("error parsing Description param: %s", err)
167 }
168 r.Variables["description"] = value
169 }
170
171 if params.Generator {
172 r.Variables["generator"] = simpleNinjaString("true")
173 }
174
175 if params.Restat {
176 r.Variables["restat"] = simpleNinjaString("true")
177 }
178
179 if params.Rspfile != "" {
180 value, err = parseNinjaString(scope, params.Rspfile)
181 if err != nil {
182 return nil, fmt.Errorf("error parsing Rspfile param: %s", err)
183 }
184 r.Variables["rspfile"] = value
185 }
186
187 if params.RspfileContent != "" {
188 value, err = parseNinjaString(scope, params.RspfileContent)
189 if err != nil {
190 return nil, fmt.Errorf("error parsing RspfileContent param: %s",
191 err)
192 }
193 r.Variables["rspfile_content"] = value
194 }
195
196 return r, nil
197}
198
199func (r *ruleDef) WriteTo(nw *ninjaWriter, name string,
Jamie Gennis2fb20952014-10-03 02:49:58 -0700200 pkgNames map[*PackageContext]string) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700201
202 if r.Comment != "" {
203 err := nw.Comment(r.Comment)
204 if err != nil {
205 return err
206 }
207 }
208
209 err := nw.Rule(name)
210 if err != nil {
211 return err
212 }
213
214 if r.Pool != nil {
215 err = nw.ScopedAssign("pool", r.Pool.fullName(pkgNames))
216 if err != nil {
217 return err
218 }
219 }
220
Colin Crossadd65dd2014-10-22 11:04:14 -0700221 var keys []string
222 for k := range r.Variables {
223 keys = append(keys, k)
224 }
225 sort.Strings(keys)
226
227 for _, name := range keys {
228 err = nw.ScopedAssign(name, r.Variables[name].Value(pkgNames))
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700229 if err != nil {
230 return err
231 }
232 }
233
234 return nil
235}
236
237// A buildDef describes a build target definition.
238type buildDef struct {
239 Rule Rule
240 Outputs []*ninjaString
241 Inputs []*ninjaString
242 Implicits []*ninjaString
243 OrderOnly []*ninjaString
244 Args map[Variable]*ninjaString
Colin Crossaa847f22014-11-06 14:26:38 -0800245 Optional bool
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700246}
247
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700248func parseBuildParams(scope scope, params *BuildParams) (*buildDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700249 error) {
250
251 rule := params.Rule
252
253 b := &buildDef{
254 Rule: rule,
255 }
256
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700257 if !scope.IsRuleVisible(rule) {
258 return nil, fmt.Errorf("Rule %s is not visible in this scope", rule)
259 }
260
261 if len(params.Outputs) == 0 {
262 return nil, errors.New("Outputs param has no elements")
263 }
264
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700265 var err error
266 b.Outputs, err = parseNinjaStrings(scope, params.Outputs)
267 if err != nil {
268 return nil, fmt.Errorf("error parsing Outputs param: %s", err)
269 }
270
271 b.Inputs, err = parseNinjaStrings(scope, params.Inputs)
272 if err != nil {
273 return nil, fmt.Errorf("error parsing Inputs param: %s", err)
274 }
275
276 b.Implicits, err = parseNinjaStrings(scope, params.Implicits)
277 if err != nil {
278 return nil, fmt.Errorf("error parsing Implicits param: %s", err)
279 }
280
281 b.OrderOnly, err = parseNinjaStrings(scope, params.OrderOnly)
282 if err != nil {
283 return nil, fmt.Errorf("error parsing OrderOnly param: %s", err)
284 }
285
Colin Cross46f8d182014-11-06 17:21:33 -0800286 b.Optional = params.Optional
287
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700288 argNameScope := rule.scope()
289
290 if len(params.Args) > 0 {
291 b.Args = make(map[Variable]*ninjaString)
292 for name, value := range params.Args {
293 if !rule.isArg(name) {
294 return nil, fmt.Errorf("unknown argument %q", name)
295 }
296
297 argVar, err := argNameScope.LookupVariable(name)
298 if err != nil {
299 // This shouldn't happen.
300 return nil, fmt.Errorf("argument lookup error: %s", err)
301 }
302
303 ninjaValue, err := parseNinjaString(scope, value)
304 if err != nil {
305 return nil, fmt.Errorf("error parsing variable %q: %s", name,
306 err)
307 }
308
309 b.Args[argVar] = ninjaValue
310 }
311 }
312
313 return b, nil
314}
315
Jamie Gennis2fb20952014-10-03 02:49:58 -0700316func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*PackageContext]string) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700317 var (
318 rule = b.Rule.fullName(pkgNames)
319 outputs = valueList(b.Outputs, pkgNames, outputEscaper)
320 explicitDeps = valueList(b.Inputs, pkgNames, inputEscaper)
321 implicitDeps = valueList(b.Implicits, pkgNames, inputEscaper)
322 orderOnlyDeps = valueList(b.OrderOnly, pkgNames, inputEscaper)
323 )
324 err := nw.Build(rule, outputs, explicitDeps, implicitDeps, orderOnlyDeps)
325 if err != nil {
326 return err
327 }
328
Colin Crossadd65dd2014-10-22 11:04:14 -0700329 args := make(map[string]string)
330
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700331 for argVar, value := range b.Args {
Colin Crossadd65dd2014-10-22 11:04:14 -0700332 args[argVar.fullName(pkgNames)] = value.Value(pkgNames)
333 }
334
335 var keys []string
336 for k := range args {
337 keys = append(keys, k)
338 }
339 sort.Strings(keys)
340
341 for _, name := range keys {
342 err = nw.ScopedAssign(name, args[name])
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700343 if err != nil {
344 return err
345 }
346 }
347
Colin Crossaa847f22014-11-06 14:26:38 -0800348 if !b.Optional {
349 nw.Default(outputs...)
350 }
351
Doug Evans9c1cbb42015-11-09 11:37:39 -0800352 return nw.BlankLine()
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700353}
354
Jamie Gennis2fb20952014-10-03 02:49:58 -0700355func valueList(list []*ninjaString, pkgNames map[*PackageContext]string,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700356 escaper *strings.Replacer) []string {
357
358 result := make([]string, len(list))
359 for i, ninjaStr := range list {
360 result[i] = ninjaStr.ValueWithEscaper(pkgNames, escaper)
361 }
362 return result
363}