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