github.com/colincross/blueprint@v0.0.0-20150626231830-9c067caf2eb5/package_ctx.go (about)

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