blob: 2bd8b1449aec0e51f30d95fd98804b5426a7b5ab [file] [log] [blame]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001package blueprint
2
3import (
Jamie Gennis48aed8c2014-06-13 18:25:54 -07004 "errors"
Jamie Gennis1bc967e2014-05-27 16:34:41 -07005 "fmt"
Colin Crossadd65dd2014-10-22 11:04:14 -07006 "sort"
Jamie Gennis1bc967e2014-05-27 16:34:41 -07007 "strconv"
8 "strings"
9)
10
Jamie Gennisd4e10182014-06-12 20:06:50 -070011// A Deps value indicates the dependency file format that Ninja should expect to
12// be output by a compiler.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070013type Deps int
14
15const (
16 DepsNone Deps = iota
17 DepsGCC
18 DepsMSVC
19)
20
21func (d Deps) String() string {
22 switch d {
23 case DepsNone:
24 return "none"
25 case DepsGCC:
26 return "gcc"
27 case DepsMSVC:
28 return "msvc"
29 default:
30 panic(fmt.Sprintf("unknown deps value: %d", d))
31 }
32}
33
Jamie Gennisd4e10182014-06-12 20:06:50 -070034// A PoolParams object contains the set of parameters that make up a Ninja pool
35// definition.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070036type PoolParams struct {
Jamie Gennisd4e10182014-06-12 20:06:50 -070037 Comment string // The comment that will appear above the definition.
38 Depth int // The Ninja pool depth.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070039}
40
Jamie Gennisd4e10182014-06-12 20:06:50 -070041// A RuleParams object contains the set of parameters that make up a Ninja rule
42// definition. Each field except for Comment corresponds with a Ninja variable
43// of the same name.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070044type RuleParams struct {
Jamie Gennisd4e10182014-06-12 20:06:50 -070045 Comment string // The comment that will appear above the definition.
46 Command string // The command that Ninja will run for the rule.
47 Depfile string // The dependency file name.
48 Deps Deps // The format of the dependency file.
49 Description string // The description that Ninja will print for the rule.
50 Generator bool // Whether the rule generates the Ninja manifest file.
51 Pool Pool // The Ninja pool to which the rule belongs.
52 Restat bool // Whether Ninja should re-stat the rule's outputs.
53 Rspfile string // The response file.
54 RspfileContent string // The response file content.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070055}
56
Jamie Gennisd4e10182014-06-12 20:06:50 -070057// A BuildParams object contains the set of parameters that make up a Ninja
58// build statement. Each field except for Args corresponds with a part of the
59// Ninja build statement. The Args field contains variable names and values
60// that are set within the build statement's scope in the Ninja file.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070061type BuildParams struct {
Jamie Gennisd4e10182014-06-12 20:06:50 -070062 Rule Rule // The rule to invoke.
63 Outputs []string // The list of output targets.
64 Inputs []string // The list of explicit input dependencies.
65 Implicits []string // The list of implicit dependencies.
66 OrderOnly []string // The list of order-only dependencies.
67 Args map[string]string // The variable/value pairs to set.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070068}
69
70// A poolDef describes a pool definition. It does not include the name of the
71// pool.
72type poolDef struct {
73 Comment string
74 Depth int
75}
76
Jamie Gennis48aed8c2014-06-13 18:25:54 -070077func parsePoolParams(scope scope, params *PoolParams) (*poolDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -070078 error) {
79
80 def := &poolDef{
81 Comment: params.Comment,
82 Depth: params.Depth,
83 }
84
85 return def, nil
86}
87
88func (p *poolDef) WriteTo(nw *ninjaWriter, name string) error {
89 if p.Comment != "" {
90 err := nw.Comment(p.Comment)
91 if err != nil {
92 return err
93 }
94 }
95
96 err := nw.Pool(name)
97 if err != nil {
98 return err
99 }
100
101 return nw.ScopedAssign("depth", strconv.Itoa(p.Depth))
102}
103
104// A ruleDef describes a rule definition. It does not include the name of the
105// rule.
106type ruleDef struct {
107 Comment string
108 Pool Pool
109 Variables map[string]*ninjaString
110}
111
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700112func parseRuleParams(scope scope, params *RuleParams) (*ruleDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700113 error) {
114
115 r := &ruleDef{
116 Comment: params.Comment,
117 Pool: params.Pool,
118 Variables: make(map[string]*ninjaString),
119 }
120
121 if params.Command == "" {
122 return nil, fmt.Errorf("encountered rule params with no command " +
123 "specified")
124 }
125
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700126 if r.Pool != nil && !scope.IsPoolVisible(r.Pool) {
127 return nil, fmt.Errorf("Pool %s is not visible in this scope", r.Pool)
128 }
129
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700130 value, err := parseNinjaString(scope, params.Command)
131 if err != nil {
132 return nil, fmt.Errorf("error parsing Command param: %s", err)
133 }
134 r.Variables["command"] = value
135
136 if params.Depfile != "" {
137 value, err = parseNinjaString(scope, params.Depfile)
138 if err != nil {
139 return nil, fmt.Errorf("error parsing Depfile param: %s", err)
140 }
141 r.Variables["depfile"] = value
142 }
143
144 if params.Deps != DepsNone {
145 r.Variables["deps"] = simpleNinjaString(params.Deps.String())
146 }
147
148 if params.Description != "" {
149 value, err = parseNinjaString(scope, params.Description)
150 if err != nil {
151 return nil, fmt.Errorf("error parsing Description param: %s", err)
152 }
153 r.Variables["description"] = value
154 }
155
156 if params.Generator {
157 r.Variables["generator"] = simpleNinjaString("true")
158 }
159
160 if params.Restat {
161 r.Variables["restat"] = simpleNinjaString("true")
162 }
163
164 if params.Rspfile != "" {
165 value, err = parseNinjaString(scope, params.Rspfile)
166 if err != nil {
167 return nil, fmt.Errorf("error parsing Rspfile param: %s", err)
168 }
169 r.Variables["rspfile"] = value
170 }
171
172 if params.RspfileContent != "" {
173 value, err = parseNinjaString(scope, params.RspfileContent)
174 if err != nil {
175 return nil, fmt.Errorf("error parsing RspfileContent param: %s",
176 err)
177 }
178 r.Variables["rspfile_content"] = value
179 }
180
181 return r, nil
182}
183
184func (r *ruleDef) WriteTo(nw *ninjaWriter, name string,
Jamie Gennis2fb20952014-10-03 02:49:58 -0700185 pkgNames map[*PackageContext]string) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700186
187 if r.Comment != "" {
188 err := nw.Comment(r.Comment)
189 if err != nil {
190 return err
191 }
192 }
193
194 err := nw.Rule(name)
195 if err != nil {
196 return err
197 }
198
199 if r.Pool != nil {
200 err = nw.ScopedAssign("pool", r.Pool.fullName(pkgNames))
201 if err != nil {
202 return err
203 }
204 }
205
Colin Crossadd65dd2014-10-22 11:04:14 -0700206 var keys []string
207 for k := range r.Variables {
208 keys = append(keys, k)
209 }
210 sort.Strings(keys)
211
212 for _, name := range keys {
213 err = nw.ScopedAssign(name, r.Variables[name].Value(pkgNames))
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700214 if err != nil {
215 return err
216 }
217 }
218
219 return nil
220}
221
222// A buildDef describes a build target definition.
223type buildDef struct {
224 Rule Rule
225 Outputs []*ninjaString
226 Inputs []*ninjaString
227 Implicits []*ninjaString
228 OrderOnly []*ninjaString
229 Args map[Variable]*ninjaString
Colin Crossaa847f22014-11-06 14:26:38 -0800230 Optional bool
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700231}
232
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700233func parseBuildParams(scope scope, params *BuildParams) (*buildDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700234 error) {
235
236 rule := params.Rule
237
238 b := &buildDef{
239 Rule: rule,
240 }
241
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700242 if !scope.IsRuleVisible(rule) {
243 return nil, fmt.Errorf("Rule %s is not visible in this scope", rule)
244 }
245
246 if len(params.Outputs) == 0 {
247 return nil, errors.New("Outputs param has no elements")
248 }
249
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700250 var err error
251 b.Outputs, err = parseNinjaStrings(scope, params.Outputs)
252 if err != nil {
253 return nil, fmt.Errorf("error parsing Outputs param: %s", err)
254 }
255
256 b.Inputs, err = parseNinjaStrings(scope, params.Inputs)
257 if err != nil {
258 return nil, fmt.Errorf("error parsing Inputs param: %s", err)
259 }
260
261 b.Implicits, err = parseNinjaStrings(scope, params.Implicits)
262 if err != nil {
263 return nil, fmt.Errorf("error parsing Implicits param: %s", err)
264 }
265
266 b.OrderOnly, err = parseNinjaStrings(scope, params.OrderOnly)
267 if err != nil {
268 return nil, fmt.Errorf("error parsing OrderOnly param: %s", err)
269 }
270
271 argNameScope := rule.scope()
272
273 if len(params.Args) > 0 {
274 b.Args = make(map[Variable]*ninjaString)
275 for name, value := range params.Args {
276 if !rule.isArg(name) {
277 return nil, fmt.Errorf("unknown argument %q", name)
278 }
279
280 argVar, err := argNameScope.LookupVariable(name)
281 if err != nil {
282 // This shouldn't happen.
283 return nil, fmt.Errorf("argument lookup error: %s", err)
284 }
285
286 ninjaValue, err := parseNinjaString(scope, value)
287 if err != nil {
288 return nil, fmt.Errorf("error parsing variable %q: %s", name,
289 err)
290 }
291
292 b.Args[argVar] = ninjaValue
293 }
294 }
295
296 return b, nil
297}
298
Jamie Gennis2fb20952014-10-03 02:49:58 -0700299func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*PackageContext]string) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700300 var (
301 rule = b.Rule.fullName(pkgNames)
302 outputs = valueList(b.Outputs, pkgNames, outputEscaper)
303 explicitDeps = valueList(b.Inputs, pkgNames, inputEscaper)
304 implicitDeps = valueList(b.Implicits, pkgNames, inputEscaper)
305 orderOnlyDeps = valueList(b.OrderOnly, pkgNames, inputEscaper)
306 )
307 err := nw.Build(rule, outputs, explicitDeps, implicitDeps, orderOnlyDeps)
308 if err != nil {
309 return err
310 }
311
Colin Crossadd65dd2014-10-22 11:04:14 -0700312 args := make(map[string]string)
313
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700314 for argVar, value := range b.Args {
Colin Crossadd65dd2014-10-22 11:04:14 -0700315 args[argVar.fullName(pkgNames)] = value.Value(pkgNames)
316 }
317
318 var keys []string
319 for k := range args {
320 keys = append(keys, k)
321 }
322 sort.Strings(keys)
323
324 for _, name := range keys {
325 err = nw.ScopedAssign(name, args[name])
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700326 if err != nil {
327 return err
328 }
329 }
330
Colin Crossaa847f22014-11-06 14:26:38 -0800331 if !b.Optional {
332 nw.Default(outputs...)
333 }
334
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700335 return nil
336}
337
Jamie Gennis2fb20952014-10-03 02:49:58 -0700338func valueList(list []*ninjaString, pkgNames map[*PackageContext]string,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700339 escaper *strings.Replacer) []string {
340
341 result := make([]string, len(list))
342 for i, ninjaStr := range list {
343 result[i] = ninjaStr.ValueWithEscaper(pkgNames, escaper)
344 }
345 return result
346}