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 memoizeFullName(pkgNames map[*packageContext]string) // precompute fullName if desired 32 value(config interface{}) (ninjaString, error) 33 String() string 34} 35 36// A Pool represents a Ninja pool that will be written to the output .ninja 37// file. 38type Pool interface { 39 packageContext() *packageContext 40 name() string // "foo" 41 fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo" 42 memoizeFullName(pkgNames map[*packageContext]string) // precompute fullName if desired 43 def(config interface{}) (*poolDef, error) 44 String() string 45} 46 47// A Rule represents a Ninja build rule that will be written to the output 48// .ninja file. 49type Rule interface { 50 packageContext() *packageContext 51 name() string // "foo" 52 fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo" 53 memoizeFullName(pkgNames map[*packageContext]string) // precompute fullName if desired 54 def(config interface{}) (*ruleDef, error) 55 scope() *basicScope 56 isArg(argName string) bool 57 String() string 58} 59 60type basicScope struct { 61 parent *basicScope 62 variables map[string]Variable 63 pools map[string]Pool 64 rules map[string]Rule 65 imports map[string]*basicScope 66} 67 68func newScope(parent *basicScope) *basicScope { 69 return &basicScope{ 70 parent: parent, 71 variables: make(map[string]Variable), 72 pools: make(map[string]Pool), 73 rules: make(map[string]Rule), 74 imports: make(map[string]*basicScope), 75 } 76} 77 78func makeRuleScope(parent *basicScope, argNames map[string]bool) *basicScope { 79 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 106func (s *basicScope) LookupVariable(name string) (Variable, error) { 107 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 150func (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 { 176 _, isBuiltin := pool.(*builtinPool) 177 if isBuiltin { 178 return true 179 } 180 181 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) { 201 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 211func (s *basicScope) AddImport(name string, importedScope *basicScope) error { 212 _, 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 220func (s *basicScope) AddVariable(v Variable) error { 221 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 230func (s *basicScope) AddPool(p Pool) error { 231 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 240func (s *basicScope) AddRule(r Rule) error { 241 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 252 scope *basicScope 253} 254 255func newLocalScope(parent *basicScope, namePrefix string) *localScope { 256 return &localScope{ 257 namePrefix: namePrefix, 258 scope: newScope(parent), 259 } 260} 261 262// 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. 266func (s *localScope) ReparentTo(pctx PackageContext) { 267 s.scope.parent = pctx.getScope() 268} 269 270func (s *localScope) LookupVariable(name string) (Variable, error) { 271 return s.scope.LookupVariable(name) 272} 273 274func (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 282func (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{ 300 fullName_: s.namePrefix + name, 301 name_: name, 302 value_: ninjaValue, 303 } 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{ 339 fullName_: s.namePrefix + name, 340 name_: name, 341 def_: def, 342 argNames: argNamesSet, 343 scope_: ruleScope, 344 } 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 { 355 fullName_ string 356 name_ string 357 value_ ninjaString 358} 359 360func (l *localVariable) packageContext() *packageContext { 361 return nil 362} 363 364func (l *localVariable) name() string { 365 return l.name_ 366} 367 368func (l *localVariable) fullName(pkgNames map[*packageContext]string) string { 369 return l.fullName_ 370} 371 372func (l *localVariable) memoizeFullName(pkgNames map[*packageContext]string) { 373 // Nothing to do, full name is known at initialization. 374} 375 376func (l *localVariable) value(interface{}) (ninjaString, error) { 377 return l.value_, nil 378} 379 380func (l *localVariable) String() string { 381 return "<local var>:" + l.fullName_ 382} 383 384type localRule struct { 385 fullName_ string 386 name_ string 387 def_ *ruleDef 388 argNames map[string]bool 389 scope_ *basicScope 390} 391 392func (l *localRule) packageContext() *packageContext { 393 return nil 394} 395 396func (l *localRule) name() string { 397 return l.name_ 398} 399 400func (l *localRule) fullName(pkgNames map[*packageContext]string) string { 401 return l.fullName_ 402} 403 404func (l *localRule) memoizeFullName(pkgNames map[*packageContext]string) { 405 // Nothing to do, full name is known at initialization. 406} 407 408func (l *localRule) def(interface{}) (*ruleDef, error) { 409 return l.def_, nil 410} 411 412func (r *localRule) scope() *basicScope { 413 return r.scope_ 414} 415 416func (r *localRule) isArg(argName string) bool { 417 return r.argNames[argName] 418} 419 420func (r *localRule) String() string { 421 return "<local rule>:" + r.fullName_ 422} 423