blob: 84db0cf7d3ef29ccd91ff9298fa454e46a3d4bea [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 (
18 "fmt"
19 "strings"
20 "unicode"
21 "unicode/utf8"
22)
23
24// A Variable represents a global Ninja variable definition that will be written
25// to the output .ninja file. A variable may contain references to other global
26// Ninja variables, but circular variable references are not allowed.
27type Variable interface {
Dan Willemsenaeffbf72015-11-25 15:29:32 -080028 packageContext() *packageContext
Jamie Gennis2fb20952014-10-03 02:49:58 -070029 name() string // "foo"
Dan Willemsenaeffbf72015-11-25 15:29:32 -080030 fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo"
Jamie Gennis6eb4d242014-06-11 18:31:16 -070031 value(config interface{}) (*ninjaString, error)
Jamie Gennis48aed8c2014-06-13 18:25:54 -070032 String() string
Jamie Gennis1bc967e2014-05-27 16:34:41 -070033}
34
35// A Pool represents a Ninja pool that will be written to the output .ninja
36// file.
37type Pool interface {
Dan Willemsenaeffbf72015-11-25 15:29:32 -080038 packageContext() *packageContext
Jamie Gennis2fb20952014-10-03 02:49:58 -070039 name() string // "foo"
Dan Willemsenaeffbf72015-11-25 15:29:32 -080040 fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo"
Jamie Gennis6eb4d242014-06-11 18:31:16 -070041 def(config interface{}) (*poolDef, error)
Jamie Gennis48aed8c2014-06-13 18:25:54 -070042 String() string
Jamie Gennis1bc967e2014-05-27 16:34:41 -070043}
44
45// A Rule represents a Ninja build rule that will be written to the output
46// .ninja file.
47type Rule interface {
Dan Willemsenaeffbf72015-11-25 15:29:32 -080048 packageContext() *packageContext
Jamie Gennis2fb20952014-10-03 02:49:58 -070049 name() string // "foo"
Dan Willemsenaeffbf72015-11-25 15:29:32 -080050 fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo"
Jamie Gennis6eb4d242014-06-11 18:31:16 -070051 def(config interface{}) (*ruleDef, error)
Jamie Gennis48aed8c2014-06-13 18:25:54 -070052 scope() *basicScope
Jamie Gennis1bc967e2014-05-27 16:34:41 -070053 isArg(argName string) bool
Jamie Gennis48aed8c2014-06-13 18:25:54 -070054 String() string
Jamie Gennis1bc967e2014-05-27 16:34:41 -070055}
56
Jamie Gennis48aed8c2014-06-13 18:25:54 -070057type basicScope struct {
58 parent *basicScope
Jamie Gennis1bc967e2014-05-27 16:34:41 -070059 variables map[string]Variable
60 pools map[string]Pool
61 rules map[string]Rule
Jamie Gennis48aed8c2014-06-13 18:25:54 -070062 imports map[string]*basicScope
Jamie Gennis1bc967e2014-05-27 16:34:41 -070063}
64
Jamie Gennis48aed8c2014-06-13 18:25:54 -070065func newScope(parent *basicScope) *basicScope {
66 return &basicScope{
Jamie Gennis1bc967e2014-05-27 16:34:41 -070067 parent: parent,
68 variables: make(map[string]Variable),
69 pools: make(map[string]Pool),
70 rules: make(map[string]Rule),
Jamie Gennis48aed8c2014-06-13 18:25:54 -070071 imports: make(map[string]*basicScope),
Jamie Gennis1bc967e2014-05-27 16:34:41 -070072 }
73}
74
Jamie Gennis48aed8c2014-06-13 18:25:54 -070075func makeRuleScope(parent *basicScope, argNames map[string]bool) *basicScope {
Jamie Gennis1bc967e2014-05-27 16:34:41 -070076 scope := newScope(parent)
77 for argName := range argNames {
78 _, err := scope.LookupVariable(argName)
79 if err != nil {
80 arg := &argVariable{argName}
81 err = scope.AddVariable(arg)
82 if err != nil {
83 // This should not happen. We should have already checked that
84 // the name is valid and that the scope doesn't have a variable
85 // with this name.
86 panic(err)
87 }
88 }
89 }
90
91 // We treat built-in variables like arguments for the purpose of this scope.
92 for _, builtin := range builtinRuleArgs {
93 arg := &argVariable{builtin}
94 err := scope.AddVariable(arg)
95 if err != nil {
96 panic(err)
97 }
98 }
99
100 return scope
101}
102
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700103func (s *basicScope) LookupVariable(name string) (Variable, error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700104 dotIndex := strings.IndexRune(name, '.')
105 if dotIndex >= 0 {
106 // The variable name looks like "pkg.var"
107 if dotIndex+1 == len(name) {
108 return nil, fmt.Errorf("variable name %q ends with a '.'", name)
109 }
110 if strings.ContainsRune(name[dotIndex+1:], '.') {
111 return nil, fmt.Errorf("variable name %q contains multiple '.' "+
112 "characters", name)
113 }
114
115 pkgName := name[:dotIndex]
116 varName := name[dotIndex+1:]
117
118 first, _ := utf8.DecodeRuneInString(varName)
119 if !unicode.IsUpper(first) {
120 return nil, fmt.Errorf("cannot refer to unexported name %q", name)
121 }
122
123 importedScope, err := s.lookupImportedScope(pkgName)
124 if err != nil {
125 return nil, err
126 }
127
128 v, ok := importedScope.variables[varName]
129 if !ok {
130 return nil, fmt.Errorf("package %q does not contain variable %q",
131 pkgName, varName)
132 }
133
134 return v, nil
135 } else {
136 // The variable name has no package part; just "var"
137 for ; s != nil; s = s.parent {
138 v, ok := s.variables[name]
139 if ok {
140 return v, nil
141 }
142 }
143 return nil, fmt.Errorf("undefined variable %q", name)
144 }
145}
146
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700147func (s *basicScope) IsRuleVisible(rule Rule) bool {
148 _, isBuiltin := rule.(*builtinRule)
149 if isBuiltin {
150 return true
151 }
152
153 name := rule.name()
154
155 for s != nil {
156 if s.rules[name] == rule {
157 return true
158 }
159
160 for _, import_ := range s.imports {
161 if import_.rules[name] == rule {
162 return true
163 }
164 }
165
166 s = s.parent
167 }
168
169 return false
170}
171
172func (s *basicScope) IsPoolVisible(pool Pool) bool {
Ken Oslund4b9a0512015-04-14 16:26:58 -0700173 _, isBuiltin := pool.(*builtinPool)
174 if isBuiltin {
175 return true
176 }
177
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700178 name := pool.name()
179
180 for s != nil {
181 if s.pools[name] == pool {
182 return true
183 }
184
185 for _, import_ := range s.imports {
186 if import_.pools[name] == pool {
187 return true
188 }
189 }
190
191 s = s.parent
192 }
193
194 return false
195}
196
197func (s *basicScope) lookupImportedScope(pkgName string) (*basicScope, error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700198 for ; s != nil; s = s.parent {
199 importedScope, ok := s.imports[pkgName]
200 if ok {
201 return importedScope, nil
202 }
203 }
204 return nil, fmt.Errorf("unknown imported package %q (missing call to "+
205 "blueprint.Import()?)", pkgName)
206}
207
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700208func (s *basicScope) AddImport(name string, importedScope *basicScope) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700209 _, present := s.imports[name]
210 if present {
211 return fmt.Errorf("import %q is already defined in this scope", name)
212 }
213 s.imports[name] = importedScope
214 return nil
215}
216
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700217func (s *basicScope) AddVariable(v Variable) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700218 name := v.name()
219 _, present := s.variables[name]
220 if present {
221 return fmt.Errorf("variable %q is already defined in this scope", name)
222 }
223 s.variables[name] = v
224 return nil
225}
226
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700227func (s *basicScope) AddPool(p Pool) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700228 name := p.name()
229 _, present := s.pools[name]
230 if present {
231 return fmt.Errorf("pool %q is already defined in this scope", name)
232 }
233 s.pools[name] = p
234 return nil
235}
236
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700237func (s *basicScope) AddRule(r Rule) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700238 name := r.name()
239 _, present := s.rules[name]
240 if present {
241 return fmt.Errorf("rule %q is already defined in this scope", name)
242 }
243 s.rules[name] = r
244 return nil
245}
246
247type localScope struct {
248 namePrefix string
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700249 scope *basicScope
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700250}
251
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700252func newLocalScope(parent *basicScope, namePrefix string) *localScope {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700253 return &localScope{
254 namePrefix: namePrefix,
255 scope: newScope(parent),
256 }
257}
258
Jamie Gennis2fb20952014-10-03 02:49:58 -0700259// ReparentTo sets the localScope's parent scope to the scope of the given
260// package context. This allows a ModuleContext and SingletonContext to call
261// a function defined in a different Go package and have that function retain
262// access to all of the package-scoped variables of its own package.
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800263func (s *localScope) ReparentTo(pctx PackageContext) {
264 s.scope.parent = pctx.getScope()
Jamie Gennis0ed63ef2014-06-30 18:07:17 -0700265}
266
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700267func (s *localScope) LookupVariable(name string) (Variable, error) {
268 return s.scope.LookupVariable(name)
269}
270
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700271func (s *localScope) IsRuleVisible(rule Rule) bool {
272 return s.scope.IsRuleVisible(rule)
273}
274
275func (s *localScope) IsPoolVisible(pool Pool) bool {
276 return s.scope.IsPoolVisible(pool)
277}
278
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700279func (s *localScope) AddLocalVariable(name, value string) (*localVariable,
280 error) {
281
282 err := validateNinjaName(name)
283 if err != nil {
284 return nil, err
285 }
286
287 if strings.ContainsRune(name, '.') {
288 return nil, fmt.Errorf("local variable name %q contains '.'", name)
289 }
290
291 ninjaValue, err := parseNinjaString(s.scope, value)
292 if err != nil {
293 return nil, err
294 }
295
296 v := &localVariable{
297 namePrefix: s.namePrefix,
298 name_: name,
299 value_: ninjaValue,
300 }
301
302 err = s.scope.AddVariable(v)
303 if err != nil {
304 return nil, err
305 }
306
307 return v, nil
308}
309
310func (s *localScope) AddLocalRule(name string, params *RuleParams,
311 argNames ...string) (*localRule, error) {
312
313 err := validateNinjaName(name)
314 if err != nil {
315 return nil, err
316 }
317
318 err = validateArgNames(argNames)
319 if err != nil {
320 return nil, fmt.Errorf("invalid argument name: %s", err)
321 }
322
323 argNamesSet := make(map[string]bool)
324 for _, argName := range argNames {
325 argNamesSet[argName] = true
326 }
327
328 ruleScope := makeRuleScope(s.scope, argNamesSet)
329
330 def, err := parseRuleParams(ruleScope, params)
331 if err != nil {
332 return nil, err
333 }
334
335 r := &localRule{
336 namePrefix: s.namePrefix,
337 name_: name,
338 def_: def,
339 argNames: argNamesSet,
340 scope_: ruleScope,
341 }
342
343 err = s.scope.AddRule(r)
344 if err != nil {
345 return nil, err
346 }
347
348 return r, nil
349}
350
351type localVariable struct {
352 namePrefix string
353 name_ string
354 value_ *ninjaString
355}
356
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800357func (l *localVariable) packageContext() *packageContext {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700358 return nil
359}
360
361func (l *localVariable) name() string {
362 return l.name_
363}
364
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800365func (l *localVariable) fullName(pkgNames map[*packageContext]string) string {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700366 return l.namePrefix + l.name_
367}
368
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700369func (l *localVariable) value(interface{}) (*ninjaString, error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700370 return l.value_, nil
371}
372
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700373func (l *localVariable) String() string {
374 return "<local var>:" + l.namePrefix + l.name_
375}
376
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700377type localRule struct {
378 namePrefix string
379 name_ string
380 def_ *ruleDef
381 argNames map[string]bool
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700382 scope_ *basicScope
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700383}
384
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800385func (l *localRule) packageContext() *packageContext {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700386 return nil
387}
388
389func (l *localRule) name() string {
390 return l.name_
391}
392
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800393func (l *localRule) fullName(pkgNames map[*packageContext]string) string {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700394 return l.namePrefix + l.name_
395}
396
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700397func (l *localRule) def(interface{}) (*ruleDef, error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700398 return l.def_, nil
399}
400
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700401func (r *localRule) scope() *basicScope {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700402 return r.scope_
403}
404
405func (r *localRule) isArg(argName string) bool {
406 return r.argNames[argName]
407}
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700408
409func (r *localRule) String() string {
410 return "<local rule>:" + r.namePrefix + r.name_
411}