blob: b9690e5d6670348df1ec7199a2c5725c9b5d282c [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"
6 "strconv"
7 "strings"
8)
9
Jamie Gennisd4e10182014-06-12 20:06:50 -070010// A Deps value indicates the dependency file format that Ninja should expect to
11// be output by a compiler.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070012type Deps int
13
14const (
15 DepsNone Deps = iota
16 DepsGCC
17 DepsMSVC
18)
19
20func (d Deps) String() string {
21 switch d {
22 case DepsNone:
23 return "none"
24 case DepsGCC:
25 return "gcc"
26 case DepsMSVC:
27 return "msvc"
28 default:
29 panic(fmt.Sprintf("unknown deps value: %d", d))
30 }
31}
32
Jamie Gennisd4e10182014-06-12 20:06:50 -070033// A PoolParams object contains the set of parameters that make up a Ninja pool
34// definition.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070035type PoolParams struct {
Jamie Gennisd4e10182014-06-12 20:06:50 -070036 Comment string // The comment that will appear above the definition.
37 Depth int // The Ninja pool depth.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070038}
39
Jamie Gennisd4e10182014-06-12 20:06:50 -070040// A RuleParams object contains the set of parameters that make up a Ninja rule
41// definition. Each field except for Comment corresponds with a Ninja variable
42// of the same name.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070043type RuleParams struct {
Jamie Gennisd4e10182014-06-12 20:06:50 -070044 Comment string // The comment that will appear above the definition.
45 Command string // The command that Ninja will run for the rule.
46 Depfile string // The dependency file name.
47 Deps Deps // The format of the dependency file.
48 Description string // The description that Ninja will print for the rule.
49 Generator bool // Whether the rule generates the Ninja manifest file.
50 Pool Pool // The Ninja pool to which the rule belongs.
51 Restat bool // Whether Ninja should re-stat the rule's outputs.
52 Rspfile string // The response file.
53 RspfileContent string // The response file content.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070054}
55
Jamie Gennisd4e10182014-06-12 20:06:50 -070056// A BuildParams object contains the set of parameters that make up a Ninja
57// build statement. Each field except for Args corresponds with a part of the
58// Ninja build statement. The Args field contains variable names and values
59// that are set within the build statement's scope in the Ninja file.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070060type BuildParams struct {
Jamie Gennisd4e10182014-06-12 20:06:50 -070061 Rule Rule // The rule to invoke.
62 Outputs []string // The list of output targets.
63 Inputs []string // The list of explicit input dependencies.
64 Implicits []string // The list of implicit dependencies.
65 OrderOnly []string // The list of order-only dependencies.
66 Args map[string]string // The variable/value pairs to set.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070067}
68
69// A poolDef describes a pool definition. It does not include the name of the
70// pool.
71type poolDef struct {
72 Comment string
73 Depth int
74}
75
Jamie Gennis48aed8c2014-06-13 18:25:54 -070076func parsePoolParams(scope scope, params *PoolParams) (*poolDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -070077 error) {
78
79 def := &poolDef{
80 Comment: params.Comment,
81 Depth: params.Depth,
82 }
83
84 return def, nil
85}
86
87func (p *poolDef) WriteTo(nw *ninjaWriter, name string) error {
88 if p.Comment != "" {
89 err := nw.Comment(p.Comment)
90 if err != nil {
91 return err
92 }
93 }
94
95 err := nw.Pool(name)
96 if err != nil {
97 return err
98 }
99
100 return nw.ScopedAssign("depth", strconv.Itoa(p.Depth))
101}
102
103// A ruleDef describes a rule definition. It does not include the name of the
104// rule.
105type ruleDef struct {
106 Comment string
107 Pool Pool
108 Variables map[string]*ninjaString
109}
110
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700111func parseRuleParams(scope scope, params *RuleParams) (*ruleDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700112 error) {
113
114 r := &ruleDef{
115 Comment: params.Comment,
116 Pool: params.Pool,
117 Variables: make(map[string]*ninjaString),
118 }
119
120 if params.Command == "" {
121 return nil, fmt.Errorf("encountered rule params with no command " +
122 "specified")
123 }
124
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700125 if r.Pool != nil && !scope.IsPoolVisible(r.Pool) {
126 return nil, fmt.Errorf("Pool %s is not visible in this scope", r.Pool)
127 }
128
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700129 value, err := parseNinjaString(scope, params.Command)
130 if err != nil {
131 return nil, fmt.Errorf("error parsing Command param: %s", err)
132 }
133 r.Variables["command"] = value
134
135 if params.Depfile != "" {
136 value, err = parseNinjaString(scope, params.Depfile)
137 if err != nil {
138 return nil, fmt.Errorf("error parsing Depfile param: %s", err)
139 }
140 r.Variables["depfile"] = value
141 }
142
143 if params.Deps != DepsNone {
144 r.Variables["deps"] = simpleNinjaString(params.Deps.String())
145 }
146
147 if params.Description != "" {
148 value, err = parseNinjaString(scope, params.Description)
149 if err != nil {
150 return nil, fmt.Errorf("error parsing Description param: %s", err)
151 }
152 r.Variables["description"] = value
153 }
154
155 if params.Generator {
156 r.Variables["generator"] = simpleNinjaString("true")
157 }
158
159 if params.Restat {
160 r.Variables["restat"] = simpleNinjaString("true")
161 }
162
163 if params.Rspfile != "" {
164 value, err = parseNinjaString(scope, params.Rspfile)
165 if err != nil {
166 return nil, fmt.Errorf("error parsing Rspfile param: %s", err)
167 }
168 r.Variables["rspfile"] = value
169 }
170
171 if params.RspfileContent != "" {
172 value, err = parseNinjaString(scope, params.RspfileContent)
173 if err != nil {
174 return nil, fmt.Errorf("error parsing RspfileContent param: %s",
175 err)
176 }
177 r.Variables["rspfile_content"] = value
178 }
179
180 return r, nil
181}
182
183func (r *ruleDef) WriteTo(nw *ninjaWriter, name string,
184 pkgNames map[*pkg]string) error {
185
186 if r.Comment != "" {
187 err := nw.Comment(r.Comment)
188 if err != nil {
189 return err
190 }
191 }
192
193 err := nw.Rule(name)
194 if err != nil {
195 return err
196 }
197
198 if r.Pool != nil {
199 err = nw.ScopedAssign("pool", r.Pool.fullName(pkgNames))
200 if err != nil {
201 return err
202 }
203 }
204
205 for name, value := range r.Variables {
206 err = nw.ScopedAssign(name, value.Value(pkgNames))
207 if err != nil {
208 return err
209 }
210 }
211
212 return nil
213}
214
215// A buildDef describes a build target definition.
216type buildDef struct {
217 Rule Rule
218 Outputs []*ninjaString
219 Inputs []*ninjaString
220 Implicits []*ninjaString
221 OrderOnly []*ninjaString
222 Args map[Variable]*ninjaString
223}
224
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700225func parseBuildParams(scope scope, params *BuildParams) (*buildDef,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700226 error) {
227
228 rule := params.Rule
229
230 b := &buildDef{
231 Rule: rule,
232 }
233
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700234 if !scope.IsRuleVisible(rule) {
235 return nil, fmt.Errorf("Rule %s is not visible in this scope", rule)
236 }
237
238 if len(params.Outputs) == 0 {
239 return nil, errors.New("Outputs param has no elements")
240 }
241
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700242 var err error
243 b.Outputs, err = parseNinjaStrings(scope, params.Outputs)
244 if err != nil {
245 return nil, fmt.Errorf("error parsing Outputs param: %s", err)
246 }
247
248 b.Inputs, err = parseNinjaStrings(scope, params.Inputs)
249 if err != nil {
250 return nil, fmt.Errorf("error parsing Inputs param: %s", err)
251 }
252
253 b.Implicits, err = parseNinjaStrings(scope, params.Implicits)
254 if err != nil {
255 return nil, fmt.Errorf("error parsing Implicits param: %s", err)
256 }
257
258 b.OrderOnly, err = parseNinjaStrings(scope, params.OrderOnly)
259 if err != nil {
260 return nil, fmt.Errorf("error parsing OrderOnly param: %s", err)
261 }
262
263 argNameScope := rule.scope()
264
265 if len(params.Args) > 0 {
266 b.Args = make(map[Variable]*ninjaString)
267 for name, value := range params.Args {
268 if !rule.isArg(name) {
269 return nil, fmt.Errorf("unknown argument %q", name)
270 }
271
272 argVar, err := argNameScope.LookupVariable(name)
273 if err != nil {
274 // This shouldn't happen.
275 return nil, fmt.Errorf("argument lookup error: %s", err)
276 }
277
278 ninjaValue, err := parseNinjaString(scope, value)
279 if err != nil {
280 return nil, fmt.Errorf("error parsing variable %q: %s", name,
281 err)
282 }
283
284 b.Args[argVar] = ninjaValue
285 }
286 }
287
288 return b, nil
289}
290
291func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*pkg]string) error {
292 var (
293 rule = b.Rule.fullName(pkgNames)
294 outputs = valueList(b.Outputs, pkgNames, outputEscaper)
295 explicitDeps = valueList(b.Inputs, pkgNames, inputEscaper)
296 implicitDeps = valueList(b.Implicits, pkgNames, inputEscaper)
297 orderOnlyDeps = valueList(b.OrderOnly, pkgNames, inputEscaper)
298 )
299 err := nw.Build(rule, outputs, explicitDeps, implicitDeps, orderOnlyDeps)
300 if err != nil {
301 return err
302 }
303
304 for argVar, value := range b.Args {
305 name := argVar.fullName(pkgNames)
306 err = nw.ScopedAssign(name, value.Value(pkgNames))
307 if err != nil {
308 return err
309 }
310 }
311
312 return nil
313}
314
315func valueList(list []*ninjaString, pkgNames map[*pkg]string,
316 escaper *strings.Replacer) []string {
317
318 result := make([]string, len(list))
319 for i, ninjaStr := range list {
320 result[i] = ninjaStr.ValueWithEscaper(pkgNames, escaper)
321 }
322 return result
323}