blob: 3f39eb77e1902f49db0efd4eea46831543381c15 [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"
Colin Cross92054a42021-01-21 16:49:25 -080031 memoizeFullName(pkgNames map[*packageContext]string) // precompute fullName if desired
Colin Cross2ce594e2020-01-29 12:58:03 -080032 value(config interface{}) (ninjaString, error)
Jamie Gennis48aed8c2014-06-13 18:25:54 -070033 String() string
Jamie Gennis1bc967e2014-05-27 16:34:41 -070034}
35
36// A Pool represents a Ninja pool that will be written to the output .ninja
37// file.
38type Pool interface {
Dan Willemsenaeffbf72015-11-25 15:29:32 -080039 packageContext() *packageContext
Jamie Gennis2fb20952014-10-03 02:49:58 -070040 name() string // "foo"
Dan Willemsenaeffbf72015-11-25 15:29:32 -080041 fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo"
Colin Cross92054a42021-01-21 16:49:25 -080042 memoizeFullName(pkgNames map[*packageContext]string) // precompute fullName if desired
Jamie Gennis6eb4d242014-06-11 18:31:16 -070043 def(config interface{}) (*poolDef, error)
Jamie Gennis48aed8c2014-06-13 18:25:54 -070044 String() string
Jamie Gennis1bc967e2014-05-27 16:34:41 -070045}
46
47// A Rule represents a Ninja build rule that will be written to the output
48// .ninja file.
49type Rule interface {
Dan Willemsenaeffbf72015-11-25 15:29:32 -080050 packageContext() *packageContext
Jamie Gennis2fb20952014-10-03 02:49:58 -070051 name() string // "foo"
Dan Willemsenaeffbf72015-11-25 15:29:32 -080052 fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo"
Colin Cross92054a42021-01-21 16:49:25 -080053 memoizeFullName(pkgNames map[*packageContext]string) // precompute fullName if desired
Jamie Gennis6eb4d242014-06-11 18:31:16 -070054 def(config interface{}) (*ruleDef, error)
Jamie Gennis48aed8c2014-06-13 18:25:54 -070055 scope() *basicScope
Jamie Gennis1bc967e2014-05-27 16:34:41 -070056 isArg(argName string) bool
Jamie Gennis48aed8c2014-06-13 18:25:54 -070057 String() string
Jamie Gennis1bc967e2014-05-27 16:34:41 -070058}
59
Jamie Gennis48aed8c2014-06-13 18:25:54 -070060type basicScope struct {
61 parent *basicScope
Jamie Gennis1bc967e2014-05-27 16:34:41 -070062 variables map[string]Variable
63 pools map[string]Pool
64 rules map[string]Rule
Jamie Gennis48aed8c2014-06-13 18:25:54 -070065 imports map[string]*basicScope
Jamie Gennis1bc967e2014-05-27 16:34:41 -070066}
67
Jamie Gennis48aed8c2014-06-13 18:25:54 -070068func newScope(parent *basicScope) *basicScope {
69 return &basicScope{
Jamie Gennis1bc967e2014-05-27 16:34:41 -070070 parent: parent,
71 variables: make(map[string]Variable),
72 pools: make(map[string]Pool),
73 rules: make(map[string]Rule),
Jamie Gennis48aed8c2014-06-13 18:25:54 -070074 imports: make(map[string]*basicScope),
Jamie Gennis1bc967e2014-05-27 16:34:41 -070075 }
76}
77
Jamie Gennis48aed8c2014-06-13 18:25:54 -070078func makeRuleScope(parent *basicScope, argNames map[string]bool) *basicScope {
Jamie Gennis1bc967e2014-05-27 16:34:41 -070079 scope := newScope(parent)
80 for argName := range argNames {
81 _, err := scope.LookupVariable(argName)
82 if err != nil {
83 arg := &argVariable{argName}
84 err = scope.AddVariable(arg)
85 if err != nil {
86 // This should not happen. We should have already checked that
87 // the name is valid and that the scope doesn't have a variable
88 // with this name.
89 panic(err)
90 }
91 }
92 }
93
94 // We treat built-in variables like arguments for the purpose of this scope.
95 for _, builtin := range builtinRuleArgs {
96 arg := &argVariable{builtin}
97 err := scope.AddVariable(arg)
98 if err != nil {
99 panic(err)
100 }
101 }
102
103 return scope
104}
105
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700106func (s *basicScope) LookupVariable(name string) (Variable, error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700107 dotIndex := strings.IndexRune(name, '.')
108 if dotIndex >= 0 {
109 // The variable name looks like "pkg.var"
110 if dotIndex+1 == len(name) {
111 return nil, fmt.Errorf("variable name %q ends with a '.'", name)
112 }
113 if strings.ContainsRune(name[dotIndex+1:], '.') {
114 return nil, fmt.Errorf("variable name %q contains multiple '.' "+
115 "characters", name)
116 }
117
118 pkgName := name[:dotIndex]
119 varName := name[dotIndex+1:]
120
121 first, _ := utf8.DecodeRuneInString(varName)
122 if !unicode.IsUpper(first) {
123 return nil, fmt.Errorf("cannot refer to unexported name %q", name)
124 }
125
126 importedScope, err := s.lookupImportedScope(pkgName)
127 if err != nil {
128 return nil, err
129 }
130
131 v, ok := importedScope.variables[varName]
132 if !ok {
133 return nil, fmt.Errorf("package %q does not contain variable %q",
134 pkgName, varName)
135 }
136
137 return v, nil
138 } else {
139 // The variable name has no package part; just "var"
140 for ; s != nil; s = s.parent {
141 v, ok := s.variables[name]
142 if ok {
143 return v, nil
144 }
145 }
146 return nil, fmt.Errorf("undefined variable %q", name)
147 }
148}
149
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700150func (s *basicScope) IsRuleVisible(rule Rule) bool {
151 _, isBuiltin := rule.(*builtinRule)
152 if isBuiltin {
153 return true
154 }
155
156 name := rule.name()
157
158 for s != nil {
159 if s.rules[name] == rule {
160 return true
161 }
162
163 for _, import_ := range s.imports {
164 if import_.rules[name] == rule {
165 return true
166 }
167 }
168
169 s = s.parent
170 }
171
172 return false
173}
174
175func (s *basicScope) IsPoolVisible(pool Pool) bool {
Ken Oslund4b9a0512015-04-14 16:26:58 -0700176 _, isBuiltin := pool.(*builtinPool)
177 if isBuiltin {
178 return true
179 }
180
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700181 name := pool.name()
182
183 for s != nil {
184 if s.pools[name] == pool {
185 return true
186 }
187
188 for _, import_ := range s.imports {
189 if import_.pools[name] == pool {
190 return true
191 }
192 }
193
194 s = s.parent
195 }
196
197 return false
198}
199
200func (s *basicScope) lookupImportedScope(pkgName string) (*basicScope, error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700201 for ; s != nil; s = s.parent {
202 importedScope, ok := s.imports[pkgName]
203 if ok {
204 return importedScope, nil
205 }
206 }
207 return nil, fmt.Errorf("unknown imported package %q (missing call to "+
208 "blueprint.Import()?)", pkgName)
209}
210
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700211func (s *basicScope) AddImport(name string, importedScope *basicScope) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700212 _, present := s.imports[name]
213 if present {
214 return fmt.Errorf("import %q is already defined in this scope", name)
215 }
216 s.imports[name] = importedScope
217 return nil
218}
219
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700220func (s *basicScope) AddVariable(v Variable) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700221 name := v.name()
222 _, present := s.variables[name]
223 if present {
224 return fmt.Errorf("variable %q is already defined in this scope", name)
225 }
226 s.variables[name] = v
227 return nil
228}
229
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700230func (s *basicScope) AddPool(p Pool) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700231 name := p.name()
232 _, present := s.pools[name]
233 if present {
234 return fmt.Errorf("pool %q is already defined in this scope", name)
235 }
236 s.pools[name] = p
237 return nil
238}
239
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700240func (s *basicScope) AddRule(r Rule) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700241 name := r.name()
242 _, present := s.rules[name]
243 if present {
244 return fmt.Errorf("rule %q is already defined in this scope", name)
245 }
246 s.rules[name] = r
247 return nil
248}
249
250type localScope struct {
251 namePrefix string
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700252 scope *basicScope
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700253}
254
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700255func newLocalScope(parent *basicScope, namePrefix string) *localScope {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700256 return &localScope{
257 namePrefix: namePrefix,
258 scope: newScope(parent),
259 }
260}
261
Jamie Gennis2fb20952014-10-03 02:49:58 -0700262// ReparentTo sets the localScope's parent scope to the scope of the given
263// package context. This allows a ModuleContext and SingletonContext to call
264// a function defined in a different Go package and have that function retain
265// access to all of the package-scoped variables of its own package.
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800266func (s *localScope) ReparentTo(pctx PackageContext) {
267 s.scope.parent = pctx.getScope()
Jamie Gennis0ed63ef2014-06-30 18:07:17 -0700268}
269
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700270func (s *localScope) LookupVariable(name string) (Variable, error) {
271 return s.scope.LookupVariable(name)
272}
273
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700274func (s *localScope) IsRuleVisible(rule Rule) bool {
275 return s.scope.IsRuleVisible(rule)
276}
277
278func (s *localScope) IsPoolVisible(pool Pool) bool {
279 return s.scope.IsPoolVisible(pool)
280}
281
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700282func (s *localScope) AddLocalVariable(name, value string) (*localVariable,
283 error) {
284
285 err := validateNinjaName(name)
286 if err != nil {
287 return nil, err
288 }
289
290 if strings.ContainsRune(name, '.') {
291 return nil, fmt.Errorf("local variable name %q contains '.'", name)
292 }
293
294 ninjaValue, err := parseNinjaString(s.scope, value)
295 if err != nil {
296 return nil, err
297 }
298
299 v := &localVariable{
Colin Cross92054a42021-01-21 16:49:25 -0800300 fullName_: s.namePrefix + name,
301 name_: name,
302 value_: ninjaValue,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700303 }
304
305 err = s.scope.AddVariable(v)
306 if err != nil {
307 return nil, err
308 }
309
310 return v, nil
311}
312
313func (s *localScope) AddLocalRule(name string, params *RuleParams,
314 argNames ...string) (*localRule, error) {
315
316 err := validateNinjaName(name)
317 if err != nil {
318 return nil, err
319 }
320
321 err = validateArgNames(argNames)
322 if err != nil {
323 return nil, fmt.Errorf("invalid argument name: %s", err)
324 }
325
326 argNamesSet := make(map[string]bool)
327 for _, argName := range argNames {
328 argNamesSet[argName] = true
329 }
330
331 ruleScope := makeRuleScope(s.scope, argNamesSet)
332
333 def, err := parseRuleParams(ruleScope, params)
334 if err != nil {
335 return nil, err
336 }
337
338 r := &localRule{
Colin Cross92054a42021-01-21 16:49:25 -0800339 fullName_: s.namePrefix + name,
340 name_: name,
341 def_: def,
342 argNames: argNamesSet,
343 scope_: ruleScope,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700344 }
345
346 err = s.scope.AddRule(r)
347 if err != nil {
348 return nil, err
349 }
350
351 return r, nil
352}
353
354type localVariable struct {
Colin Cross92054a42021-01-21 16:49:25 -0800355 fullName_ string
356 name_ string
357 value_ ninjaString
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700358}
359
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800360func (l *localVariable) packageContext() *packageContext {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700361 return nil
362}
363
364func (l *localVariable) name() string {
365 return l.name_
366}
367
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800368func (l *localVariable) fullName(pkgNames map[*packageContext]string) string {
Colin Cross92054a42021-01-21 16:49:25 -0800369 return l.fullName_
370}
371
372func (l *localVariable) memoizeFullName(pkgNames map[*packageContext]string) {
373 // Nothing to do, full name is known at initialization.
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700374}
375
Colin Cross2ce594e2020-01-29 12:58:03 -0800376func (l *localVariable) value(interface{}) (ninjaString, error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700377 return l.value_, nil
378}
379
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700380func (l *localVariable) String() string {
Colin Cross92054a42021-01-21 16:49:25 -0800381 return "<local var>:" + l.fullName_
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700382}
383
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700384type localRule struct {
Colin Cross92054a42021-01-21 16:49:25 -0800385 fullName_ string
386 name_ string
387 def_ *ruleDef
388 argNames map[string]bool
389 scope_ *basicScope
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700390}
391
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800392func (l *localRule) packageContext() *packageContext {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700393 return nil
394}
395
396func (l *localRule) name() string {
397 return l.name_
398}
399
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800400func (l *localRule) fullName(pkgNames map[*packageContext]string) string {
Colin Cross92054a42021-01-21 16:49:25 -0800401 return l.fullName_
402}
403
404func (l *localRule) memoizeFullName(pkgNames map[*packageContext]string) {
405 // Nothing to do, full name is known at initialization.
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700406}
407
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700408func (l *localRule) def(interface{}) (*ruleDef, error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700409 return l.def_, nil
410}
411
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700412func (r *localRule) scope() *basicScope {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700413 return r.scope_
414}
415
416func (r *localRule) isArg(argName string) bool {
417 return r.argNames[argName]
418}
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700419
420func (r *localRule) String() string {
Colin Cross92054a42021-01-21 16:49:25 -0800421 return "<local rule>:" + r.fullName_
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700422}