Home | History | Annotate | Download | only in blueprint
      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 
     15 package blueprint
     16 
     17 import (
     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.
     27 type 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.
     37 type 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.
     47 type 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 
     57 type 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 
     65 func 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 
     75 func 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 
    103 func (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 
    147 func (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 
    172 func (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 
    197 func (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 
    208 func (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 
    217 func (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 
    227 func (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 
    237 func (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 
    247 type localScope struct {
    248 	namePrefix string
    249 	scope      *basicScope
    250 }
    251 
    252 func 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.
    263 func (s *localScope) ReparentTo(pctx PackageContext) {
    264 	s.scope.parent = pctx.getScope()
    265 }
    266 
    267 func (s *localScope) LookupVariable(name string) (Variable, error) {
    268 	return s.scope.LookupVariable(name)
    269 }
    270 
    271 func (s *localScope) IsRuleVisible(rule Rule) bool {
    272 	return s.scope.IsRuleVisible(rule)
    273 }
    274 
    275 func (s *localScope) IsPoolVisible(pool Pool) bool {
    276 	return s.scope.IsPoolVisible(pool)
    277 }
    278 
    279 func (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 
    310 func (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 
    351 type localVariable struct {
    352 	namePrefix string
    353 	name_      string
    354 	value_     *ninjaString
    355 }
    356 
    357 func (l *localVariable) packageContext() *packageContext {
    358 	return nil
    359 }
    360 
    361 func (l *localVariable) name() string {
    362 	return l.name_
    363 }
    364 
    365 func (l *localVariable) fullName(pkgNames map[*packageContext]string) string {
    366 	return l.namePrefix + l.name_
    367 }
    368 
    369 func (l *localVariable) value(interface{}) (*ninjaString, error) {
    370 	return l.value_, nil
    371 }
    372 
    373 func (l *localVariable) String() string {
    374 	return "<local var>:" + l.namePrefix + l.name_
    375 }
    376 
    377 type localRule struct {
    378 	namePrefix string
    379 	name_      string
    380 	def_       *ruleDef
    381 	argNames   map[string]bool
    382 	scope_     *basicScope
    383 }
    384 
    385 func (l *localRule) packageContext() *packageContext {
    386 	return nil
    387 }
    388 
    389 func (l *localRule) name() string {
    390 	return l.name_
    391 }
    392 
    393 func (l *localRule) fullName(pkgNames map[*packageContext]string) string {
    394 	return l.namePrefix + l.name_
    395 }
    396 
    397 func (l *localRule) def(interface{}) (*ruleDef, error) {
    398 	return l.def_, nil
    399 }
    400 
    401 func (r *localRule) scope() *basicScope {
    402 	return r.scope_
    403 }
    404 
    405 func (r *localRule) isArg(argName string) bool {
    406 	return r.argNames[argName]
    407 }
    408 
    409 func (r *localRule) String() string {
    410 	return "<local rule>:" + r.namePrefix + r.name_
    411 }
    412