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