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