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 	"errors"
     19 	"fmt"
     20 	"reflect"
     21 	"regexp"
     22 	"runtime"
     23 	"strings"
     24 	"sync"
     25 )
     26 
     27 // A PackageContext provides a way to create package-scoped Ninja pools,
     28 // rules, and variables.  A Go package should create a single unexported
     29 // package-scoped PackageContext variable that it uses to create all package-
     30 // scoped Ninja object definitions.  This PackageContext object should then be
     31 // passed to all calls to define module- or singleton-specific Ninja
     32 // definitions.  For example:
     33 //
     34 //     package blah
     35 //
     36 //     import (
     37 //         "blueprint"
     38 //     )
     39 //
     40 //     var (
     41 //         pctx = NewPackageContext("path/to/blah")
     42 //
     43 //         myPrivateVar = pctx.StaticVariable("myPrivateVar", "abcdef")
     44 //         MyExportedVar = pctx.StaticVariable("MyExportedVar", "$myPrivateVar 123456!")
     45 //
     46 //         SomeRule = pctx.StaticRule(...)
     47 //     )
     48 //
     49 //     // ...
     50 //
     51 //     func (m *MyModule) GenerateBuildActions(ctx blueprint.Module) {
     52 //         ctx.Build(pctx, blueprint.BuildParams{
     53 //             Rule:    SomeRule,
     54 //             Outputs: []string{"$myPrivateVar"},
     55 //         })
     56 //     }
     57 type PackageContext interface {
     58 	Import(pkgPath string)
     59 	ImportAs(as, pkgPath string)
     60 
     61 	StaticVariable(name, value string) Variable
     62 	VariableFunc(name string, f func(config interface{}) (string, error)) Variable
     63 	VariableConfigMethod(name string, method interface{}) Variable
     64 
     65 	StaticPool(name string, params PoolParams) Pool
     66 	PoolFunc(name string, f func(interface{}) (PoolParams, error)) Pool
     67 
     68 	StaticRule(name string, params RuleParams, argNames ...string) Rule
     69 	RuleFunc(name string, f func(interface{}) (RuleParams, error), argNames ...string) Rule
     70 
     71 	AddNinjaFileDeps(deps ...string)
     72 
     73 	getScope() *basicScope
     74 }
     75 
     76 type packageContext struct {
     77 	fullName      string
     78 	shortName     string
     79 	pkgPath       string
     80 	scope         *basicScope
     81 	ninjaFileDeps []string
     82 }
     83 
     84 var _ PackageContext = &packageContext{}
     85 
     86 func (p *packageContext) getScope() *basicScope {
     87 	return p.scope
     88 }
     89 
     90 var packageContexts = map[string]*packageContext{}
     91 
     92 // NewPackageContext creates a PackageContext object for a given package.  The
     93 // pkgPath argument should always be set to the full path used to import the
     94 // package.  This function may only be called from a Go package's init()
     95 // function or as part of a package-scoped variable initialization.
     96 func NewPackageContext(pkgPath string) PackageContext {
     97 	checkCalledFromInit()
     98 
     99 	if _, present := packageContexts[pkgPath]; present {
    100 		panic(fmt.Errorf("package %q already has a package context", pkgPath))
    101 	}
    102 
    103 	pkgName := pkgPathToName(pkgPath)
    104 	err := validateNinjaName(pkgName)
    105 	if err != nil {
    106 		panic(err)
    107 	}
    108 
    109 	i := strings.LastIndex(pkgPath, "/")
    110 	shortName := pkgPath[i+1:]
    111 
    112 	p := &packageContext{
    113 		fullName:  pkgName,
    114 		shortName: shortName,
    115 		pkgPath:   pkgPath,
    116 		scope:     newScope(nil),
    117 	}
    118 
    119 	packageContexts[pkgPath] = p
    120 
    121 	return p
    122 }
    123 
    124 var Phony Rule = NewBuiltinRule("phony")
    125 
    126 var Console Pool = NewBuiltinPool("console")
    127 
    128 var errRuleIsBuiltin = errors.New("the rule is a built-in")
    129 var errPoolIsBuiltin = errors.New("the pool is a built-in")
    130 var errVariableIsArg = errors.New("argument variables have no value")
    131 
    132 // checkCalledFromInit panics if a Go package's init function is not on the
    133 // call stack.
    134 func checkCalledFromInit() {
    135 	for skip := 3; ; skip++ {
    136 		_, funcName, ok := callerName(skip)
    137 		if !ok {
    138 			panic("not called from an init func")
    139 		}
    140 
    141 		if funcName == "init" || strings.HasPrefix(funcName, "init") ||
    142 			funcName == "init.ializers" || strings.HasPrefix(funcName, "init.") {
    143 			return
    144 		}
    145 	}
    146 }
    147 
    148 // A regex to find a package path within a function name. It finds the shortest string that is
    149 // followed by '.' and doesn't have any '/'s left.
    150 var pkgPathRe = regexp.MustCompile(`^(.*?)\.([^/]+)$`)
    151 
    152 // callerName returns the package path and function name of the calling
    153 // function.  The skip argument has the same meaning as the skip argument of
    154 // runtime.Callers.
    155 func callerName(skip int) (pkgPath, funcName string, ok bool) {
    156 	var pc [1]uintptr
    157 	n := runtime.Callers(skip+1, pc[:])
    158 	if n != 1 {
    159 		return "", "", false
    160 	}
    161 
    162 	f := runtime.FuncForPC(pc[0]).Name()
    163 	s := pkgPathRe.FindStringSubmatch(f)
    164 	if len(s) < 3 {
    165 		panic(fmt.Errorf("failed to extract package path and function name from %q", f))
    166 	}
    167 
    168 	return s[1], s[2], true
    169 }
    170 
    171 // pkgPathToName makes a Ninja-friendly name out of a Go package name by
    172 // replaceing all the '/' characters with '.'.  We assume the results are
    173 // unique, though this is not 100% guaranteed for Go package names that
    174 // already contain '.' characters. Disallowing package names with '.' isn't
    175 // reasonable since many package names contain the name of the hosting site
    176 // (e.g. "code.google.com").  In practice this probably isn't really a
    177 // problem.
    178 func pkgPathToName(pkgPath string) string {
    179 	return strings.Replace(pkgPath, "/", ".", -1)
    180 }
    181 
    182 // Import enables access to the exported Ninja pools, rules, and variables
    183 // that are defined at the package scope of another Go package.  Go's
    184 // visibility rules apply to these references - capitalized names indicate
    185 // that something is exported.  It may only be called from a Go package's
    186 // init() function.  The Go package path passed to Import must have already
    187 // been imported into the Go package using a Go import statement.  The
    188 // imported variables may then be accessed from Ninja strings as
    189 // "${pkg.Variable}", while the imported rules can simply be accessed as
    190 // exported Go variables from the package.  For example:
    191 //
    192 //     import (
    193 //         "blueprint"
    194 //         "foo/bar"
    195 //     )
    196 //
    197 //     var pctx = NewPackagePath("blah")
    198 //
    199 //     func init() {
    200 //         pctx.Import("foo/bar")
    201 //     }
    202 //
    203 //     ...
    204 //
    205 //     func (m *MyModule) GenerateBuildActions(ctx blueprint.Module) {
    206 //         ctx.Build(pctx, blueprint.BuildParams{
    207 //             Rule:    bar.SomeRule,
    208 //             Outputs: []string{"${bar.SomeVariable}"},
    209 //         })
    210 //     }
    211 //
    212 // Note that the local name used to refer to the package in Ninja variable names
    213 // is derived from pkgPath by extracting the last path component.  This differs
    214 // from Go's import declaration, which derives the local name from the package
    215 // clause in the imported package.  By convention these names are made to match,
    216 // but this is not required.
    217 func (p *packageContext) Import(pkgPath string) {
    218 	checkCalledFromInit()
    219 	importPkg, ok := packageContexts[pkgPath]
    220 	if !ok {
    221 		panic(fmt.Errorf("package %q has no context", pkgPath))
    222 	}
    223 
    224 	err := p.scope.AddImport(importPkg.shortName, importPkg.scope)
    225 	if err != nil {
    226 		panic(err)
    227 	}
    228 }
    229 
    230 // ImportAs provides the same functionality as Import, but it allows the local
    231 // name that will be used to refer to the package to be specified explicitly.
    232 // It may only be called from a Go package's init() function.
    233 func (p *packageContext) ImportAs(as, pkgPath string) {
    234 	checkCalledFromInit()
    235 	importPkg, ok := packageContexts[pkgPath]
    236 	if !ok {
    237 		panic(fmt.Errorf("package %q has no context", pkgPath))
    238 	}
    239 
    240 	err := validateNinjaName(as)
    241 	if err != nil {
    242 		panic(err)
    243 	}
    244 
    245 	err = p.scope.AddImport(as, importPkg.scope)
    246 	if err != nil {
    247 		panic(err)
    248 	}
    249 }
    250 
    251 type staticVariable struct {
    252 	pctx   *packageContext
    253 	name_  string
    254 	value_ string
    255 }
    256 
    257 // StaticVariable returns a Variable whose value does not depend on any
    258 // configuration information.  It may only be called during a Go package's
    259 // initialization - either from the init() function or as part of a package-
    260 // scoped variable's initialization.
    261 //
    262 // This function is usually used to initialize a package-scoped Go variable that
    263 // represents a Ninja variable that will be output.  The name argument should
    264 // exactly match the Go variable name, and the value string may reference other
    265 // Ninja variables that are visible within the calling Go package.
    266 func (p *packageContext) StaticVariable(name, value string) Variable {
    267 	checkCalledFromInit()
    268 	err := validateNinjaName(name)
    269 	if err != nil {
    270 		panic(err)
    271 	}
    272 
    273 	v := &staticVariable{p, name, value}
    274 	err = p.scope.AddVariable(v)
    275 	if err != nil {
    276 		panic(err)
    277 	}
    278 
    279 	return v
    280 }
    281 
    282 func (v *staticVariable) packageContext() *packageContext {
    283 	return v.pctx
    284 }
    285 
    286 func (v *staticVariable) name() string {
    287 	return v.name_
    288 }
    289 
    290 func (v *staticVariable) fullName(pkgNames map[*packageContext]string) string {
    291 	return packageNamespacePrefix(pkgNames[v.pctx]) + v.name_
    292 }
    293 
    294 func (v *staticVariable) value(interface{}) (*ninjaString, error) {
    295 	ninjaStr, err := parseNinjaString(v.pctx.scope, v.value_)
    296 	if err != nil {
    297 		err = fmt.Errorf("error parsing variable %s value: %s", v, err)
    298 		panic(err)
    299 	}
    300 	return ninjaStr, nil
    301 }
    302 
    303 func (v *staticVariable) String() string {
    304 	return v.pctx.pkgPath + "." + v.name_
    305 }
    306 
    307 type variableFunc struct {
    308 	pctx   *packageContext
    309 	name_  string
    310 	value_ func(interface{}) (string, error)
    311 }
    312 
    313 // VariableFunc returns a Variable whose value is determined by a function that
    314 // takes a config object as input and returns either the variable value or an
    315 // error.  It may only be called during a Go package's initialization - either
    316 // from the init() function or as part of a package-scoped variable's
    317 // initialization.
    318 //
    319 // This function is usually used to initialize a package-scoped Go variable that
    320 // represents a Ninja variable that will be output.  The name argument should
    321 // exactly match the Go variable name, and the value string returned by f may
    322 // reference other Ninja variables that are visible within the calling Go
    323 // package.
    324 func (p *packageContext) VariableFunc(name string,
    325 	f func(config interface{}) (string, error)) Variable {
    326 
    327 	checkCalledFromInit()
    328 
    329 	err := validateNinjaName(name)
    330 	if err != nil {
    331 		panic(err)
    332 	}
    333 
    334 	v := &variableFunc{p, name, f}
    335 	err = p.scope.AddVariable(v)
    336 	if err != nil {
    337 		panic(err)
    338 	}
    339 
    340 	return v
    341 }
    342 
    343 // VariableConfigMethod returns a Variable whose value is determined by calling
    344 // a method on the config object.  The method must take no arguments and return
    345 // a single string that will be the variable's value.  It may only be called
    346 // during a Go package's initialization - either from the init() function or as
    347 // part of a package-scoped variable's initialization.
    348 //
    349 // This function is usually used to initialize a package-scoped Go variable that
    350 // represents a Ninja variable that will be output.  The name argument should
    351 // exactly match the Go variable name, and the value string returned by method
    352 // may reference other Ninja variables that are visible within the calling Go
    353 // package.
    354 func (p *packageContext) VariableConfigMethod(name string,
    355 	method interface{}) Variable {
    356 
    357 	checkCalledFromInit()
    358 
    359 	err := validateNinjaName(name)
    360 	if err != nil {
    361 		panic(err)
    362 	}
    363 
    364 	methodValue := reflect.ValueOf(method)
    365 	validateVariableMethod(name, methodValue)
    366 
    367 	fun := func(config interface{}) (string, error) {
    368 		result := methodValue.Call([]reflect.Value{reflect.ValueOf(config)})
    369 		resultStr := result[0].Interface().(string)
    370 		return resultStr, nil
    371 	}
    372 
    373 	v := &variableFunc{p, name, fun}
    374 	err = p.scope.AddVariable(v)
    375 	if err != nil {
    376 		panic(err)
    377 	}
    378 
    379 	return v
    380 }
    381 
    382 func (v *variableFunc) packageContext() *packageContext {
    383 	return v.pctx
    384 }
    385 
    386 func (v *variableFunc) name() string {
    387 	return v.name_
    388 }
    389 
    390 func (v *variableFunc) fullName(pkgNames map[*packageContext]string) string {
    391 	return packageNamespacePrefix(pkgNames[v.pctx]) + v.name_
    392 }
    393 
    394 func (v *variableFunc) value(config interface{}) (*ninjaString, error) {
    395 	value, err := v.value_(config)
    396 	if err != nil {
    397 		return nil, err
    398 	}
    399 
    400 	ninjaStr, err := parseNinjaString(v.pctx.scope, value)
    401 	if err != nil {
    402 		err = fmt.Errorf("error parsing variable %s value: %s", v, err)
    403 		panic(err)
    404 	}
    405 
    406 	return ninjaStr, nil
    407 }
    408 
    409 func (v *variableFunc) String() string {
    410 	return v.pctx.pkgPath + "." + v.name_
    411 }
    412 
    413 func validateVariableMethod(name string, methodValue reflect.Value) {
    414 	methodType := methodValue.Type()
    415 	if methodType.Kind() != reflect.Func {
    416 		panic(fmt.Errorf("method given for variable %s is not a function",
    417 			name))
    418 	}
    419 	if n := methodType.NumIn(); n != 1 {
    420 		panic(fmt.Errorf("method for variable %s has %d inputs (should be 1)",
    421 			name, n))
    422 	}
    423 	if n := methodType.NumOut(); n != 1 {
    424 		panic(fmt.Errorf("method for variable %s has %d outputs (should be 1)",
    425 			name, n))
    426 	}
    427 	if kind := methodType.Out(0).Kind(); kind != reflect.String {
    428 		panic(fmt.Errorf("method for variable %s does not return a string",
    429 			name))
    430 	}
    431 }
    432 
    433 // An argVariable is a Variable that exists only when it is set by a build
    434 // statement to pass a value to the rule being invoked.  It has no value, so it
    435 // can never be used to create a Ninja assignment statement.  It is inserted
    436 // into the rule's scope, which is used for name lookups within the rule and
    437 // when assigning argument values as part of a build statement.
    438 type argVariable struct {
    439 	name_ string
    440 }
    441 
    442 func (v *argVariable) packageContext() *packageContext {
    443 	panic("this should not be called")
    444 }
    445 
    446 func (v *argVariable) name() string {
    447 	return v.name_
    448 }
    449 
    450 func (v *argVariable) fullName(pkgNames map[*packageContext]string) string {
    451 	return v.name_
    452 }
    453 
    454 func (v *argVariable) value(config interface{}) (*ninjaString, error) {
    455 	return nil, errVariableIsArg
    456 }
    457 
    458 func (v *argVariable) String() string {
    459 	return "<arg>:" + v.name_
    460 }
    461 
    462 type staticPool struct {
    463 	pctx   *packageContext
    464 	name_  string
    465 	params PoolParams
    466 }
    467 
    468 // StaticPool returns a Pool whose value does not depend on any configuration
    469 // information.  It may only be called during a Go package's initialization -
    470 // either from the init() function or as part of a package-scoped Go variable's
    471 // initialization.
    472 //
    473 // This function is usually used to initialize a package-scoped Go variable that
    474 // represents a Ninja pool that will be output.  The name argument should
    475 // exactly match the Go variable name, and the params fields may reference other
    476 // Ninja variables that are visible within the calling Go package.
    477 func (p *packageContext) StaticPool(name string, params PoolParams) Pool {
    478 	checkCalledFromInit()
    479 
    480 	err := validateNinjaName(name)
    481 	if err != nil {
    482 		panic(err)
    483 	}
    484 
    485 	pool := &staticPool{p, name, params}
    486 	err = p.scope.AddPool(pool)
    487 	if err != nil {
    488 		panic(err)
    489 	}
    490 
    491 	return pool
    492 }
    493 
    494 func (p *staticPool) packageContext() *packageContext {
    495 	return p.pctx
    496 }
    497 
    498 func (p *staticPool) name() string {
    499 	return p.name_
    500 }
    501 
    502 func (p *staticPool) fullName(pkgNames map[*packageContext]string) string {
    503 	return packageNamespacePrefix(pkgNames[p.pctx]) + p.name_
    504 }
    505 
    506 func (p *staticPool) def(config interface{}) (*poolDef, error) {
    507 	def, err := parsePoolParams(p.pctx.scope, &p.params)
    508 	if err != nil {
    509 		panic(fmt.Errorf("error parsing PoolParams for %s: %s", p, err))
    510 	}
    511 	return def, nil
    512 }
    513 
    514 func (p *staticPool) String() string {
    515 	return p.pctx.pkgPath + "." + p.name_
    516 }
    517 
    518 type poolFunc struct {
    519 	pctx       *packageContext
    520 	name_      string
    521 	paramsFunc func(interface{}) (PoolParams, error)
    522 }
    523 
    524 // PoolFunc returns a Pool whose value is determined by a function that takes a
    525 // config object as input and returns either the pool parameters or an error. It
    526 // may only be called during a Go package's initialization - either from the
    527 // init() function or as part of a package-scoped variable's initialization.
    528 //
    529 // This function is usually used to initialize a package-scoped Go variable that
    530 // represents a Ninja pool that will be output.  The name argument should
    531 // exactly match the Go variable name, and the string fields of the PoolParams
    532 // returned by f may reference other Ninja variables that are visible within the
    533 // calling Go package.
    534 func (p *packageContext) PoolFunc(name string, f func(interface{}) (PoolParams,
    535 	error)) Pool {
    536 
    537 	checkCalledFromInit()
    538 
    539 	err := validateNinjaName(name)
    540 	if err != nil {
    541 		panic(err)
    542 	}
    543 
    544 	pool := &poolFunc{p, name, f}
    545 	err = p.scope.AddPool(pool)
    546 	if err != nil {
    547 		panic(err)
    548 	}
    549 
    550 	return pool
    551 }
    552 
    553 func (p *poolFunc) packageContext() *packageContext {
    554 	return p.pctx
    555 }
    556 
    557 func (p *poolFunc) name() string {
    558 	return p.name_
    559 }
    560 
    561 func (p *poolFunc) fullName(pkgNames map[*packageContext]string) string {
    562 	return packageNamespacePrefix(pkgNames[p.pctx]) + p.name_
    563 }
    564 
    565 func (p *poolFunc) def(config interface{}) (*poolDef, error) {
    566 	params, err := p.paramsFunc(config)
    567 	if err != nil {
    568 		return nil, err
    569 	}
    570 	def, err := parsePoolParams(p.pctx.scope, &params)
    571 	if err != nil {
    572 		panic(fmt.Errorf("error parsing PoolParams for %s: %s", p, err))
    573 	}
    574 	return def, nil
    575 }
    576 
    577 func (p *poolFunc) String() string {
    578 	return p.pctx.pkgPath + "." + p.name_
    579 }
    580 
    581 type builtinPool struct {
    582 	name_ string
    583 }
    584 
    585 func (p *builtinPool) packageContext() *packageContext {
    586 	return nil
    587 }
    588 
    589 func (p *builtinPool) name() string {
    590 	return p.name_
    591 }
    592 
    593 func (p *builtinPool) fullName(pkgNames map[*packageContext]string) string {
    594 	return p.name_
    595 }
    596 
    597 func (p *builtinPool) def(config interface{}) (*poolDef, error) {
    598 	return nil, errPoolIsBuiltin
    599 }
    600 
    601 // NewBuiltinPool returns a Pool object that refers to a pool name created outside of Blueprint
    602 func NewBuiltinPool(name string) Pool {
    603 	return &builtinPool{
    604 		name_: name,
    605 	}
    606 }
    607 
    608 func (p *builtinPool) String() string {
    609 	return "<builtin>:" + p.name_
    610 }
    611 
    612 type staticRule struct {
    613 	pctx       *packageContext
    614 	name_      string
    615 	params     RuleParams
    616 	argNames   map[string]bool
    617 	scope_     *basicScope
    618 	sync.Mutex // protects scope_ during lazy creation
    619 }
    620 
    621 // StaticRule returns a Rule whose value does not depend on any configuration
    622 // information.  It may only be called during a Go package's initialization -
    623 // either from the init() function or as part of a package-scoped Go variable's
    624 // initialization.
    625 //
    626 // This function is usually used to initialize a package-scoped Go variable that
    627 // represents a Ninja rule that will be output.  The name argument should
    628 // exactly match the Go variable name, and the params fields may reference other
    629 // Ninja variables that are visible within the calling Go package.
    630 //
    631 // The argNames arguments list Ninja variables that may be overridden by Ninja
    632 // build statements that invoke the rule.  These arguments may be referenced in
    633 // any of the string fields of params.  Arguments can shadow package-scoped
    634 // variables defined within the caller's Go package, but they may not shadow
    635 // those defined in another package.  Shadowing a package-scoped variable
    636 // results in the package-scoped variable's value being used for build
    637 // statements that do not override the argument.  For argument names that do not
    638 // shadow package-scoped variables the default value is an empty string.
    639 func (p *packageContext) StaticRule(name string, params RuleParams,
    640 	argNames ...string) Rule {
    641 
    642 	checkCalledFromInit()
    643 
    644 	err := validateNinjaName(name)
    645 	if err != nil {
    646 		panic(err)
    647 	}
    648 
    649 	err = validateArgNames(argNames)
    650 	if err != nil {
    651 		panic(fmt.Errorf("invalid argument name: %s", err))
    652 	}
    653 
    654 	argNamesSet := make(map[string]bool)
    655 	for _, argName := range argNames {
    656 		argNamesSet[argName] = true
    657 	}
    658 
    659 	ruleScope := (*basicScope)(nil) // This will get created lazily
    660 
    661 	r := &staticRule{
    662 		pctx:     p,
    663 		name_:    name,
    664 		params:   params,
    665 		argNames: argNamesSet,
    666 		scope_:   ruleScope,
    667 	}
    668 	err = p.scope.AddRule(r)
    669 	if err != nil {
    670 		panic(err)
    671 	}
    672 
    673 	return r
    674 }
    675 
    676 func (r *staticRule) packageContext() *packageContext {
    677 	return r.pctx
    678 }
    679 
    680 func (r *staticRule) name() string {
    681 	return r.name_
    682 }
    683 
    684 func (r *staticRule) fullName(pkgNames map[*packageContext]string) string {
    685 	return packageNamespacePrefix(pkgNames[r.pctx]) + r.name_
    686 }
    687 
    688 func (r *staticRule) def(interface{}) (*ruleDef, error) {
    689 	def, err := parseRuleParams(r.scope(), &r.params)
    690 	if err != nil {
    691 		panic(fmt.Errorf("error parsing RuleParams for %s: %s", r, err))
    692 	}
    693 	return def, nil
    694 }
    695 
    696 func (r *staticRule) scope() *basicScope {
    697 	// We lazily create the scope so that all the package-scoped variables get
    698 	// declared before the args are created.  Otherwise we could incorrectly
    699 	// shadow a package-scoped variable with an arg variable.
    700 	r.Lock()
    701 	defer r.Unlock()
    702 
    703 	if r.scope_ == nil {
    704 		r.scope_ = makeRuleScope(r.pctx.scope, r.argNames)
    705 	}
    706 	return r.scope_
    707 }
    708 
    709 func (r *staticRule) isArg(argName string) bool {
    710 	return r.argNames[argName]
    711 }
    712 
    713 func (r *staticRule) String() string {
    714 	return r.pctx.pkgPath + "." + r.name_
    715 }
    716 
    717 type ruleFunc struct {
    718 	pctx       *packageContext
    719 	name_      string
    720 	paramsFunc func(interface{}) (RuleParams, error)
    721 	argNames   map[string]bool
    722 	scope_     *basicScope
    723 	sync.Mutex // protects scope_ during lazy creation
    724 }
    725 
    726 // RuleFunc returns a Rule whose value is determined by a function that takes a
    727 // config object as input and returns either the rule parameters or an error. It
    728 // may only be called during a Go package's initialization - either from the
    729 // init() function or as part of a package-scoped variable's initialization.
    730 //
    731 // This function is usually used to initialize a package-scoped Go variable that
    732 // represents a Ninja rule that will be output.  The name argument should
    733 // exactly match the Go variable name, and the string fields of the RuleParams
    734 // returned by f may reference other Ninja variables that are visible within the
    735 // calling Go package.
    736 //
    737 // The argNames arguments list Ninja variables that may be overridden by Ninja
    738 // build statements that invoke the rule.  These arguments may be referenced in
    739 // any of the string fields of the RuleParams returned by f.  Arguments can
    740 // shadow package-scoped variables defined within the caller's Go package, but
    741 // they may not shadow those defined in another package.  Shadowing a package-
    742 // scoped variable results in the package-scoped variable's value being used for
    743 // build statements that do not override the argument.  For argument names that
    744 // do not shadow package-scoped variables the default value is an empty string.
    745 func (p *packageContext) RuleFunc(name string, f func(interface{}) (RuleParams,
    746 	error), argNames ...string) Rule {
    747 
    748 	checkCalledFromInit()
    749 
    750 	err := validateNinjaName(name)
    751 	if err != nil {
    752 		panic(err)
    753 	}
    754 
    755 	err = validateArgNames(argNames)
    756 	if err != nil {
    757 		panic(fmt.Errorf("invalid argument name: %s", err))
    758 	}
    759 
    760 	argNamesSet := make(map[string]bool)
    761 	for _, argName := range argNames {
    762 		argNamesSet[argName] = true
    763 	}
    764 
    765 	ruleScope := (*basicScope)(nil) // This will get created lazily
    766 
    767 	rule := &ruleFunc{
    768 		pctx:       p,
    769 		name_:      name,
    770 		paramsFunc: f,
    771 		argNames:   argNamesSet,
    772 		scope_:     ruleScope,
    773 	}
    774 	err = p.scope.AddRule(rule)
    775 	if err != nil {
    776 		panic(err)
    777 	}
    778 
    779 	return rule
    780 }
    781 
    782 func (r *ruleFunc) packageContext() *packageContext {
    783 	return r.pctx
    784 }
    785 
    786 func (r *ruleFunc) name() string {
    787 	return r.name_
    788 }
    789 
    790 func (r *ruleFunc) fullName(pkgNames map[*packageContext]string) string {
    791 	return packageNamespacePrefix(pkgNames[r.pctx]) + r.name_
    792 }
    793 
    794 func (r *ruleFunc) def(config interface{}) (*ruleDef, error) {
    795 	params, err := r.paramsFunc(config)
    796 	if err != nil {
    797 		return nil, err
    798 	}
    799 	def, err := parseRuleParams(r.scope(), &params)
    800 	if err != nil {
    801 		panic(fmt.Errorf("error parsing RuleParams for %s: %s", r, err))
    802 	}
    803 	return def, nil
    804 }
    805 
    806 func (r *ruleFunc) scope() *basicScope {
    807 	// We lazily create the scope so that all the global variables get declared
    808 	// before the args are created.  Otherwise we could incorrectly shadow a
    809 	// global variable with an arg variable.
    810 	r.Lock()
    811 	defer r.Unlock()
    812 
    813 	if r.scope_ == nil {
    814 		r.scope_ = makeRuleScope(r.pctx.scope, r.argNames)
    815 	}
    816 	return r.scope_
    817 }
    818 
    819 func (r *ruleFunc) isArg(argName string) bool {
    820 	return r.argNames[argName]
    821 }
    822 
    823 func (r *ruleFunc) String() string {
    824 	return r.pctx.pkgPath + "." + r.name_
    825 }
    826 
    827 type builtinRule struct {
    828 	name_      string
    829 	scope_     *basicScope
    830 	sync.Mutex // protects scope_ during lazy creation
    831 }
    832 
    833 func (r *builtinRule) packageContext() *packageContext {
    834 	return nil
    835 }
    836 
    837 func (r *builtinRule) name() string {
    838 	return r.name_
    839 }
    840 
    841 func (r *builtinRule) fullName(pkgNames map[*packageContext]string) string {
    842 	return r.name_
    843 }
    844 
    845 func (r *builtinRule) def(config interface{}) (*ruleDef, error) {
    846 	return nil, errRuleIsBuiltin
    847 }
    848 
    849 func (r *builtinRule) scope() *basicScope {
    850 	r.Lock()
    851 	defer r.Unlock()
    852 
    853 	if r.scope_ == nil {
    854 		r.scope_ = makeRuleScope(nil, nil)
    855 	}
    856 	return r.scope_
    857 }
    858 
    859 func (r *builtinRule) isArg(argName string) bool {
    860 	return false
    861 }
    862 
    863 func (r *builtinRule) String() string {
    864 	return "<builtin>:" + r.name_
    865 }
    866 
    867 // NewBuiltinRule returns a Rule object that refers to a rule that was created outside of Blueprint
    868 func NewBuiltinRule(name string) Rule {
    869 	return &builtinRule{
    870 		name_: name,
    871 	}
    872 }
    873 
    874 func (p *packageContext) AddNinjaFileDeps(deps ...string) {
    875 	p.ninjaFileDeps = append(p.ninjaFileDeps, deps...)
    876 }
    877