1// 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 15package 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 { 28 packageContext() *packageContext 29 name() string // "foo" 30 fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo" 31 value(config interface{}) (*ninjaString, error) 32 String() string 33} 34 35// A Pool represents a Ninja pool that will be written to the output .ninja 36// file. 37type Pool interface { 38 packageContext() *packageContext 39 name() string // "foo" 40 fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo" 41 def(config interface{}) (*poolDef, error) 42 String() string 43} 44 45// A Rule represents a Ninja build rule that will be written to the output 46// .ninja file. 47type Rule interface { 48 packageContext() *packageContext 49 name() string // "foo" 50 fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo" 51 def(config interface{}) (*ruleDef, error) 52 scope() *basicScope 53 isArg(argName string) bool 54 String() string 55} 56 57type basicScope struct { 58 parent *basicScope 59 variables map[string]Variable 60 pools map[string]Pool 61 rules map[string]Rule 62 imports map[string]*basicScope 63} 64 65func newScope(parent *basicScope) *basicScope { 66 return &basicScope{ 67 parent: parent, 68 variables: make(map[string]Variable), 69 pools: make(map[string]Pool), 70 rules: make(map[string]Rule), 71 imports: make(map[string]*basicScope), 72 } 73} 74 75func makeRuleScope(parent *basicScope, argNames map[string]bool) *basicScope { 76 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 103func (s *basicScope) LookupVariable(name string) (Variable, error) { 104 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 147func (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 { 173 _, isBuiltin := pool.(*builtinPool) 174 if isBuiltin { 175 return true 176 } 177 178 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) { 198 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 208func (s *basicScope) AddImport(name string, importedScope *basicScope) error { 209 _, 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 217func (s *basicScope) AddVariable(v Variable) error { 218 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 227func (s *basicScope) AddPool(p Pool) error { 228 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 237func (s *basicScope) AddRule(r Rule) error { 238 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 249 scope *basicScope 250} 251 252func newLocalScope(parent *basicScope, namePrefix string) *localScope { 253 return &localScope{ 254 namePrefix: namePrefix, 255 scope: newScope(parent), 256 } 257} 258 259// 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. 263func (s *localScope) ReparentTo(pctx PackageContext) { 264 s.scope.parent = pctx.getScope() 265} 266 267func (s *localScope) LookupVariable(name string) (Variable, error) { 268 return s.scope.LookupVariable(name) 269} 270 271func (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 279func (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 357func (l *localVariable) packageContext() *packageContext { 358 return nil 359} 360 361func (l *localVariable) name() string { 362 return l.name_ 363} 364 365func (l *localVariable) fullName(pkgNames map[*packageContext]string) string { 366 return l.namePrefix + l.name_ 367} 368 369func (l *localVariable) value(interface{}) (*ninjaString, error) { 370 return l.value_, nil 371} 372 373func (l *localVariable) String() string { 374 return "<local var>:" + l.namePrefix + l.name_ 375} 376 377type localRule struct { 378 namePrefix string 379 name_ string 380 def_ *ruleDef 381 argNames map[string]bool 382 scope_ *basicScope 383} 384 385func (l *localRule) packageContext() *packageContext { 386 return nil 387} 388 389func (l *localRule) name() string { 390 return l.name_ 391} 392 393func (l *localRule) fullName(pkgNames map[*packageContext]string) string { 394 return l.namePrefix + l.name_ 395} 396 397func (l *localRule) def(interface{}) (*ruleDef, error) { 398 return l.def_, nil 399} 400 401func (r *localRule) scope() *basicScope { 402 return r.scope_ 403} 404 405func (r *localRule) isArg(argName string) bool { 406 return r.argNames[argName] 407} 408 409func (r *localRule) String() string { 410 return "<local rule>:" + r.namePrefix + r.name_ 411} 412