blob: 3f3329dc1b655e59d85cc3c8c415049926241266 [file] [log] [blame]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001package blueprint
2
3import (
4 "fmt"
5 "strconv"
6 "strings"
7)
8
9type Deps int
10
11const (
12 DepsNone Deps = iota
13 DepsGCC
14 DepsMSVC
15)
16
17func (d Deps) String() string {
18 switch d {
19 case DepsNone:
20 return "none"
21 case DepsGCC:
22 return "gcc"
23 case DepsMSVC:
24 return "msvc"
25 default:
26 panic(fmt.Sprintf("unknown deps value: %d", d))
27 }
28}
29
30type PoolParams struct {
31 Comment string
32 Depth int
33}
34
35type RuleParams struct {
36 Comment string
37 Command string
38 Depfile string
39 Deps Deps
40 Description string
41 Generator bool
42 Pool Pool
43 Restat bool
44 Rspfile string
45 RspfileContent string
46}
47
48type BuildParams struct {
49 Rule Rule
50 Outputs []string
51 Inputs []string
52 Implicits []string
53 OrderOnly []string
54 Args map[string]string
55}
56
57// A poolDef describes a pool definition. It does not include the name of the
58// pool.
59type poolDef struct {
60 Comment string
61 Depth int
62}
63
64func parsePoolParams(scope variableLookup, params *PoolParams) (*poolDef,
65 error) {
66
67 def := &poolDef{
68 Comment: params.Comment,
69 Depth: params.Depth,
70 }
71
72 return def, nil
73}
74
75func (p *poolDef) WriteTo(nw *ninjaWriter, name string) error {
76 if p.Comment != "" {
77 err := nw.Comment(p.Comment)
78 if err != nil {
79 return err
80 }
81 }
82
83 err := nw.Pool(name)
84 if err != nil {
85 return err
86 }
87
88 return nw.ScopedAssign("depth", strconv.Itoa(p.Depth))
89}
90
91// A ruleDef describes a rule definition. It does not include the name of the
92// rule.
93type ruleDef struct {
94 Comment string
95 Pool Pool
96 Variables map[string]*ninjaString
97}
98
99func parseRuleParams(scope variableLookup, params *RuleParams) (*ruleDef,
100 error) {
101
102 r := &ruleDef{
103 Comment: params.Comment,
104 Pool: params.Pool,
105 Variables: make(map[string]*ninjaString),
106 }
107
108 if params.Command == "" {
109 return nil, fmt.Errorf("encountered rule params with no command " +
110 "specified")
111 }
112
113 value, err := parseNinjaString(scope, params.Command)
114 if err != nil {
115 return nil, fmt.Errorf("error parsing Command param: %s", err)
116 }
117 r.Variables["command"] = value
118
119 if params.Depfile != "" {
120 value, err = parseNinjaString(scope, params.Depfile)
121 if err != nil {
122 return nil, fmt.Errorf("error parsing Depfile param: %s", err)
123 }
124 r.Variables["depfile"] = value
125 }
126
127 if params.Deps != DepsNone {
128 r.Variables["deps"] = simpleNinjaString(params.Deps.String())
129 }
130
131 if params.Description != "" {
132 value, err = parseNinjaString(scope, params.Description)
133 if err != nil {
134 return nil, fmt.Errorf("error parsing Description param: %s", err)
135 }
136 r.Variables["description"] = value
137 }
138
139 if params.Generator {
140 r.Variables["generator"] = simpleNinjaString("true")
141 }
142
143 if params.Restat {
144 r.Variables["restat"] = simpleNinjaString("true")
145 }
146
147 if params.Rspfile != "" {
148 value, err = parseNinjaString(scope, params.Rspfile)
149 if err != nil {
150 return nil, fmt.Errorf("error parsing Rspfile param: %s", err)
151 }
152 r.Variables["rspfile"] = value
153 }
154
155 if params.RspfileContent != "" {
156 value, err = parseNinjaString(scope, params.RspfileContent)
157 if err != nil {
158 return nil, fmt.Errorf("error parsing RspfileContent param: %s",
159 err)
160 }
161 r.Variables["rspfile_content"] = value
162 }
163
164 return r, nil
165}
166
167func (r *ruleDef) WriteTo(nw *ninjaWriter, name string,
168 pkgNames map[*pkg]string) error {
169
170 if r.Comment != "" {
171 err := nw.Comment(r.Comment)
172 if err != nil {
173 return err
174 }
175 }
176
177 err := nw.Rule(name)
178 if err != nil {
179 return err
180 }
181
182 if r.Pool != nil {
183 err = nw.ScopedAssign("pool", r.Pool.fullName(pkgNames))
184 if err != nil {
185 return err
186 }
187 }
188
189 for name, value := range r.Variables {
190 err = nw.ScopedAssign(name, value.Value(pkgNames))
191 if err != nil {
192 return err
193 }
194 }
195
196 return nil
197}
198
199// A buildDef describes a build target definition.
200type buildDef struct {
201 Rule Rule
202 Outputs []*ninjaString
203 Inputs []*ninjaString
204 Implicits []*ninjaString
205 OrderOnly []*ninjaString
206 Args map[Variable]*ninjaString
207}
208
209func parseBuildParams(scope variableLookup, params *BuildParams) (*buildDef,
210 error) {
211
212 rule := params.Rule
213
214 b := &buildDef{
215 Rule: rule,
216 }
217
218 var err error
219 b.Outputs, err = parseNinjaStrings(scope, params.Outputs)
220 if err != nil {
221 return nil, fmt.Errorf("error parsing Outputs param: %s", err)
222 }
223
224 b.Inputs, err = parseNinjaStrings(scope, params.Inputs)
225 if err != nil {
226 return nil, fmt.Errorf("error parsing Inputs param: %s", err)
227 }
228
229 b.Implicits, err = parseNinjaStrings(scope, params.Implicits)
230 if err != nil {
231 return nil, fmt.Errorf("error parsing Implicits param: %s", err)
232 }
233
234 b.OrderOnly, err = parseNinjaStrings(scope, params.OrderOnly)
235 if err != nil {
236 return nil, fmt.Errorf("error parsing OrderOnly param: %s", err)
237 }
238
239 argNameScope := rule.scope()
240
241 if len(params.Args) > 0 {
242 b.Args = make(map[Variable]*ninjaString)
243 for name, value := range params.Args {
244 if !rule.isArg(name) {
245 return nil, fmt.Errorf("unknown argument %q", name)
246 }
247
248 argVar, err := argNameScope.LookupVariable(name)
249 if err != nil {
250 // This shouldn't happen.
251 return nil, fmt.Errorf("argument lookup error: %s", err)
252 }
253
254 ninjaValue, err := parseNinjaString(scope, value)
255 if err != nil {
256 return nil, fmt.Errorf("error parsing variable %q: %s", name,
257 err)
258 }
259
260 b.Args[argVar] = ninjaValue
261 }
262 }
263
264 return b, nil
265}
266
267func (b *buildDef) WriteTo(nw *ninjaWriter, pkgNames map[*pkg]string) error {
268 var (
269 rule = b.Rule.fullName(pkgNames)
270 outputs = valueList(b.Outputs, pkgNames, outputEscaper)
271 explicitDeps = valueList(b.Inputs, pkgNames, inputEscaper)
272 implicitDeps = valueList(b.Implicits, pkgNames, inputEscaper)
273 orderOnlyDeps = valueList(b.OrderOnly, pkgNames, inputEscaper)
274 )
275 err := nw.Build(rule, outputs, explicitDeps, implicitDeps, orderOnlyDeps)
276 if err != nil {
277 return err
278 }
279
280 for argVar, value := range b.Args {
281 name := argVar.fullName(pkgNames)
282 err = nw.ScopedAssign(name, value.Value(pkgNames))
283 if err != nil {
284 return err
285 }
286 }
287
288 return nil
289}
290
291func valueList(list []*ninjaString, pkgNames map[*pkg]string,
292 escaper *strings.Replacer) []string {
293
294 result := make([]string, len(list))
295 for i, ninjaStr := range list {
296 result[i] = ninjaStr.ValueWithEscaper(pkgNames, escaper)
297 }
298 return result
299}