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(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{}
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	fullName_ string
257}
258
259// StaticVariable returns a Variable whose value does not depend on any
260// configuration information.  It may only be called during a Go package's
261// initialization - either from the init() function or as part of a package-
262// scoped variable's initialization.
263//
264// This function is usually used to initialize a package-scoped Go variable that
265// represents a Ninja variable that will be output.  The name argument should
266// exactly match the Go variable name, and the value string may reference other
267// Ninja variables that are visible within the calling Go package.
268func (p *packageContext) StaticVariable(name, value string) Variable {
269	checkCalledFromInit()
270	err := validateNinjaName(name)
271	if err != nil {
272		panic(err)
273	}
274
275	v := &staticVariable{
276		pctx:   p,
277		name_:  name,
278		value_: value,
279	}
280	err = p.scope.AddVariable(v)
281	if err != nil {
282		panic(err)
283	}
284
285	return v
286}
287
288func (v *staticVariable) packageContext() *packageContext {
289	return v.pctx
290}
291
292func (v *staticVariable) name() string {
293	return v.name_
294}
295
296func (v *staticVariable) fullName(pkgNames map[*packageContext]string) string {
297	if v.fullName_ != "" {
298		return v.fullName_
299	}
300	return packageNamespacePrefix(pkgNames[v.pctx]) + v.name_
301}
302
303func (v *staticVariable) memoizeFullName(pkgNames map[*packageContext]string) {
304	v.fullName_ = v.fullName(pkgNames)
305}
306
307func (v *staticVariable) value(interface{}) (ninjaString, error) {
308	ninjaStr, err := parseNinjaString(v.pctx.scope, v.value_)
309	if err != nil {
310		err = fmt.Errorf("error parsing variable %s value: %s", v, err)
311		panic(err)
312	}
313	return ninjaStr, nil
314}
315
316func (v *staticVariable) String() string {
317	return v.pctx.pkgPath + "." + v.name_
318}
319
320type variableFunc struct {
321	pctx      *packageContext
322	name_     string
323	value_    func(interface{}) (string, error)
324	fullName_ string
325}
326
327// VariableFunc returns a Variable whose value is determined by a function that
328// takes a config object as input and returns either the variable value or an
329// error.  It may only be called during a Go package's initialization - either
330// from the init() function or as part of a package-scoped variable's
331// 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 f may
336// reference other Ninja variables that are visible within the calling Go
337// package.
338func (p *packageContext) VariableFunc(name string,
339	f func(config interface{}) (string, error)) Variable {
340
341	checkCalledFromInit()
342
343	err := validateNinjaName(name)
344	if err != nil {
345		panic(err)
346	}
347
348	v := &variableFunc{
349		pctx:   p,
350		name_:  name,
351		value_: f,
352	}
353	err = p.scope.AddVariable(v)
354	if err != nil {
355		panic(err)
356	}
357
358	return v
359}
360
361// VariableConfigMethod returns a Variable whose value is determined by calling
362// a method on the config object.  The method must take no arguments and return
363// a single string that will be the variable's value.  It may only be called
364// during a Go package's initialization - either from the init() function or as
365// part of a package-scoped variable's initialization.
366//
367// This function is usually used to initialize a package-scoped Go variable that
368// represents a Ninja variable that will be output.  The name argument should
369// exactly match the Go variable name, and the value string returned by method
370// may reference other Ninja variables that are visible within the calling Go
371// package.
372func (p *packageContext) VariableConfigMethod(name string,
373	method interface{}) Variable {
374
375	checkCalledFromInit()
376
377	err := validateNinjaName(name)
378	if err != nil {
379		panic(err)
380	}
381
382	methodValue := reflect.ValueOf(method)
383	validateVariableMethod(name, methodValue)
384
385	fun := func(config interface{}) (string, error) {
386		result := methodValue.Call([]reflect.Value{reflect.ValueOf(config)})
387		resultStr := result[0].Interface().(string)
388		return resultStr, nil
389	}
390
391	v := &variableFunc{
392		pctx:   p,
393		name_:  name,
394		value_: fun,
395	}
396	err = p.scope.AddVariable(v)
397	if err != nil {
398		panic(err)
399	}
400
401	return v
402}
403
404func (v *variableFunc) packageContext() *packageContext {
405	return v.pctx
406}
407
408func (v *variableFunc) name() string {
409	return v.name_
410}
411
412func (v *variableFunc) fullName(pkgNames map[*packageContext]string) string {
413	if v.fullName_ != "" {
414		return v.fullName_
415	}
416	return packageNamespacePrefix(pkgNames[v.pctx]) + v.name_
417}
418
419func (v *variableFunc) memoizeFullName(pkgNames map[*packageContext]string) {
420	v.fullName_ = v.fullName(pkgNames)
421}
422
423func (v *variableFunc) value(config interface{}) (ninjaString, error) {
424	value, err := v.value_(config)
425	if err != nil {
426		return nil, err
427	}
428
429	ninjaStr, err := parseNinjaString(v.pctx.scope, value)
430	if err != nil {
431		err = fmt.Errorf("error parsing variable %s value: %s", v, err)
432		panic(err)
433	}
434
435	return ninjaStr, nil
436}
437
438func (v *variableFunc) String() string {
439	return v.pctx.pkgPath + "." + v.name_
440}
441
442func validateVariableMethod(name string, methodValue reflect.Value) {
443	methodType := methodValue.Type()
444	if methodType.Kind() != reflect.Func {
445		panic(fmt.Errorf("method given for variable %s is not a function",
446			name))
447	}
448	if n := methodType.NumIn(); n != 1 {
449		panic(fmt.Errorf("method for variable %s has %d inputs (should be 1)",
450			name, n))
451	}
452	if n := methodType.NumOut(); n != 1 {
453		panic(fmt.Errorf("method for variable %s has %d outputs (should be 1)",
454			name, n))
455	}
456	if kind := methodType.Out(0).Kind(); kind != reflect.String {
457		panic(fmt.Errorf("method for variable %s does not return a string",
458			name))
459	}
460}
461
462// An argVariable is a Variable that exists only when it is set by a build
463// statement to pass a value to the rule being invoked.  It has no value, so it
464// can never be used to create a Ninja assignment statement.  It is inserted
465// into the rule's scope, which is used for name lookups within the rule and
466// when assigning argument values as part of a build statement.
467type argVariable struct {
468	name_ string
469}
470
471func (v *argVariable) packageContext() *packageContext {
472	panic("this should not be called")
473}
474
475func (v *argVariable) name() string {
476	return v.name_
477}
478
479func (v *argVariable) fullName(pkgNames map[*packageContext]string) string {
480	return v.name_
481}
482
483func (v *argVariable) memoizeFullName(pkgNames map[*packageContext]string) {
484	// Nothing to do, full name is known at initialization.
485}
486
487func (v *argVariable) value(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	fullName_ string
500}
501
502// StaticPool returns a Pool whose value does not depend on any configuration
503// information.  It may only be called during a Go package's initialization -
504// either from the init() function or as part of a package-scoped Go variable's
505// initialization.
506//
507// This function is usually used to initialize a package-scoped Go variable that
508// represents a Ninja pool that will be output.  The name argument should
509// exactly match the Go variable name, and the params fields may reference other
510// Ninja variables that are visible within the calling Go package.
511func (p *packageContext) StaticPool(name string, params PoolParams) Pool {
512	checkCalledFromInit()
513
514	err := validateNinjaName(name)
515	if err != nil {
516		panic(err)
517	}
518
519	pool := &staticPool{
520		pctx:   p,
521		name_:  name,
522		params: params,
523	}
524	err = p.scope.AddPool(pool)
525	if err != nil {
526		panic(err)
527	}
528
529	return pool
530}
531
532func (p *staticPool) packageContext() *packageContext {
533	return p.pctx
534}
535
536func (p *staticPool) name() string {
537	return p.name_
538}
539
540func (p *staticPool) fullName(pkgNames map[*packageContext]string) string {
541	if p.fullName_ != "" {
542		return p.fullName_
543	}
544	return packageNamespacePrefix(pkgNames[p.pctx]) + p.name_
545}
546
547func (p *staticPool) memoizeFullName(pkgNames map[*packageContext]string) {
548	p.fullName_ = p.fullName(pkgNames)
549}
550
551func (p *staticPool) def(config interface{}) (*poolDef, error) {
552	def, err := parsePoolParams(p.pctx.scope, &p.params)
553	if err != nil {
554		panic(fmt.Errorf("error parsing PoolParams for %s: %s", p, err))
555	}
556	return def, nil
557}
558
559func (p *staticPool) String() string {
560	return p.pctx.pkgPath + "." + p.name_
561}
562
563type poolFunc struct {
564	pctx       *packageContext
565	name_      string
566	paramsFunc func(interface{}) (PoolParams, error)
567	fullName_  string
568}
569
570// PoolFunc returns a Pool whose value is determined by a function that takes a
571// config object as input and returns either the pool parameters or an error. It
572// may only be called during a Go package's initialization - either from the
573// init() function or as part of a package-scoped variable's initialization.
574//
575// This function is usually used to initialize a package-scoped Go variable that
576// represents a Ninja pool that will be output.  The name argument should
577// exactly match the Go variable name, and the string fields of the PoolParams
578// returned by f may reference other Ninja variables that are visible within the
579// calling Go package.
580func (p *packageContext) PoolFunc(name string, f func(interface{}) (PoolParams,
581	error)) Pool {
582
583	checkCalledFromInit()
584
585	err := validateNinjaName(name)
586	if err != nil {
587		panic(err)
588	}
589
590	pool := &poolFunc{
591		pctx:       p,
592		name_:      name,
593		paramsFunc: f,
594	}
595	err = p.scope.AddPool(pool)
596	if err != nil {
597		panic(err)
598	}
599
600	return pool
601}
602
603func (p *poolFunc) packageContext() *packageContext {
604	return p.pctx
605}
606
607func (p *poolFunc) name() string {
608	return p.name_
609}
610
611func (p *poolFunc) fullName(pkgNames map[*packageContext]string) string {
612	if p.fullName_ != "" {
613		return p.fullName_
614	}
615	return packageNamespacePrefix(pkgNames[p.pctx]) + p.name_
616}
617
618func (p *poolFunc) memoizeFullName(pkgNames map[*packageContext]string) {
619	p.fullName_ = p.fullName(pkgNames)
620}
621
622func (p *poolFunc) def(config interface{}) (*poolDef, error) {
623	params, err := p.paramsFunc(config)
624	if err != nil {
625		return nil, err
626	}
627	def, err := parsePoolParams(p.pctx.scope, &params)
628	if err != nil {
629		panic(fmt.Errorf("error parsing PoolParams for %s: %s", p, err))
630	}
631	return def, nil
632}
633
634func (p *poolFunc) String() string {
635	return p.pctx.pkgPath + "." + p.name_
636}
637
638type builtinPool struct {
639	name_ string
640}
641
642func (p *builtinPool) packageContext() *packageContext {
643	return nil
644}
645
646func (p *builtinPool) name() string {
647	return p.name_
648}
649
650func (p *builtinPool) fullName(pkgNames map[*packageContext]string) string {
651	return p.name_
652}
653
654func (p *builtinPool) memoizeFullName(pkgNames map[*packageContext]string) {
655	// Nothing to do, full name is known at initialization.
656}
657
658func (p *builtinPool) def(config interface{}) (*poolDef, error) {
659	return nil, errPoolIsBuiltin
660}
661
662// NewBuiltinPool returns a Pool object that refers to a pool name created outside of Blueprint
663func NewBuiltinPool(name string) Pool {
664	return &builtinPool{
665		name_: name,
666	}
667}
668
669func (p *builtinPool) String() string {
670	return "<builtin>:" + p.name_
671}
672
673type staticRule struct {
674	pctx       *packageContext
675	name_      string
676	params     RuleParams
677	argNames   map[string]bool
678	scope_     *basicScope
679	fullName_  string
680	sync.Mutex // protects scope_ during lazy creation
681}
682
683// StaticRule returns a Rule whose value does not depend on any configuration
684// information.  It may only be called during a Go package's initialization -
685// either from the init() function or as part of a package-scoped Go variable's
686// initialization.
687//
688// This function is usually used to initialize a package-scoped Go variable that
689// represents a Ninja rule that will be output.  The name argument should
690// exactly match the Go variable name, and the params fields may reference other
691// Ninja variables that are visible within the calling Go package.
692//
693// The argNames arguments list Ninja variables that may be overridden by Ninja
694// build statements that invoke the rule.  These arguments may be referenced in
695// any of the string fields of params.  Arguments can shadow package-scoped
696// variables defined within the caller's Go package, but they may not shadow
697// those defined in another package.  Shadowing a package-scoped variable
698// results in the package-scoped variable's value being used for build
699// statements that do not override the argument.  For argument names that do not
700// shadow package-scoped variables the default value is an empty string.
701func (p *packageContext) StaticRule(name string, params RuleParams,
702	argNames ...string) Rule {
703
704	checkCalledFromInit()
705
706	err := validateNinjaName(name)
707	if err != nil {
708		panic(err)
709	}
710
711	err = validateArgNames(argNames)
712	if err != nil {
713		panic(fmt.Errorf("invalid argument name: %s", err))
714	}
715
716	argNamesSet := make(map[string]bool)
717	for _, argName := range argNames {
718		argNamesSet[argName] = true
719	}
720
721	ruleScope := (*basicScope)(nil) // This will get created lazily
722
723	r := &staticRule{
724		pctx:     p,
725		name_:    name,
726		params:   params,
727		argNames: argNamesSet,
728		scope_:   ruleScope,
729	}
730	err = p.scope.AddRule(r)
731	if err != nil {
732		panic(err)
733	}
734
735	return r
736}
737
738func (r *staticRule) packageContext() *packageContext {
739	return r.pctx
740}
741
742func (r *staticRule) name() string {
743	return r.name_
744}
745
746func (r *staticRule) fullName(pkgNames map[*packageContext]string) string {
747	if r.fullName_ != "" {
748		return r.fullName_
749	}
750	return packageNamespacePrefix(pkgNames[r.pctx]) + r.name_
751}
752
753func (r *staticRule) memoizeFullName(pkgNames map[*packageContext]string) {
754	r.fullName_ = r.fullName(pkgNames)
755}
756
757func (r *staticRule) def(interface{}) (*ruleDef, error) {
758	def, err := parseRuleParams(r.scope(), &r.params)
759	if err != nil {
760		panic(fmt.Errorf("error parsing RuleParams for %s: %s", r, err))
761	}
762	return def, nil
763}
764
765func (r *staticRule) scope() *basicScope {
766	// We lazily create the scope so that all the package-scoped variables get
767	// declared before the args are created.  Otherwise we could incorrectly
768	// shadow a package-scoped variable with an arg variable.
769	r.Lock()
770	defer r.Unlock()
771
772	if r.scope_ == nil {
773		r.scope_ = makeRuleScope(r.pctx.scope, r.argNames)
774	}
775	return r.scope_
776}
777
778func (r *staticRule) isArg(argName string) bool {
779	return r.argNames[argName]
780}
781
782func (r *staticRule) String() string {
783	return r.pctx.pkgPath + "." + r.name_
784}
785
786type ruleFunc struct {
787	pctx       *packageContext
788	name_      string
789	paramsFunc func(interface{}) (RuleParams, error)
790	argNames   map[string]bool
791	scope_     *basicScope
792	fullName_  string
793	sync.Mutex // protects scope_ during lazy creation
794}
795
796// RuleFunc returns a Rule whose value is determined by a function that takes a
797// config object as input and returns either the rule parameters or an error. It
798// may only be called during a Go package's initialization - either from the
799// init() function or as part of a package-scoped variable's initialization.
800//
801// This function is usually used to initialize a package-scoped Go variable that
802// represents a Ninja rule that will be output.  The name argument should
803// exactly match the Go variable name, and the string fields of the RuleParams
804// returned by f may reference other Ninja variables that are visible within the
805// calling Go package.
806//
807// The argNames arguments list Ninja variables that may be overridden by Ninja
808// build statements that invoke the rule.  These arguments may be referenced in
809// any of the string fields of the RuleParams returned by f.  Arguments can
810// shadow package-scoped variables defined within the caller's Go package, but
811// they may not shadow those defined in another package.  Shadowing a package-
812// scoped variable results in the package-scoped variable's value being used for
813// build statements that do not override the argument.  For argument names that
814// do not shadow package-scoped variables the default value is an empty string.
815func (p *packageContext) RuleFunc(name string, f func(interface{}) (RuleParams,
816	error), argNames ...string) Rule {
817
818	checkCalledFromInit()
819
820	err := validateNinjaName(name)
821	if err != nil {
822		panic(err)
823	}
824
825	err = validateArgNames(argNames)
826	if err != nil {
827		panic(fmt.Errorf("invalid argument name: %s", err))
828	}
829
830	argNamesSet := make(map[string]bool)
831	for _, argName := range argNames {
832		argNamesSet[argName] = true
833	}
834
835	ruleScope := (*basicScope)(nil) // This will get created lazily
836
837	rule := &ruleFunc{
838		pctx:       p,
839		name_:      name,
840		paramsFunc: f,
841		argNames:   argNamesSet,
842		scope_:     ruleScope,
843	}
844	err = p.scope.AddRule(rule)
845	if err != nil {
846		panic(err)
847	}
848
849	return rule
850}
851
852func (r *ruleFunc) packageContext() *packageContext {
853	return r.pctx
854}
855
856func (r *ruleFunc) name() string {
857	return r.name_
858}
859
860func (r *ruleFunc) fullName(pkgNames map[*packageContext]string) string {
861	if r.fullName_ != "" {
862		return r.fullName_
863	}
864	return packageNamespacePrefix(pkgNames[r.pctx]) + r.name_
865}
866
867func (r *ruleFunc) memoizeFullName(pkgNames map[*packageContext]string) {
868	r.fullName_ = r.fullName(pkgNames)
869}
870
871func (r *ruleFunc) def(config interface{}) (*ruleDef, error) {
872	params, err := r.paramsFunc(config)
873	if err != nil {
874		return nil, err
875	}
876	def, err := parseRuleParams(r.scope(), &params)
877	if err != nil {
878		panic(fmt.Errorf("error parsing RuleParams for %s: %s", r, err))
879	}
880	return def, nil
881}
882
883func (r *ruleFunc) scope() *basicScope {
884	// We lazily create the scope so that all the global variables get declared
885	// before the args are created.  Otherwise we could incorrectly shadow a
886	// global variable with an arg variable.
887	r.Lock()
888	defer r.Unlock()
889
890	if r.scope_ == nil {
891		r.scope_ = makeRuleScope(r.pctx.scope, r.argNames)
892	}
893	return r.scope_
894}
895
896func (r *ruleFunc) isArg(argName string) bool {
897	return r.argNames[argName]
898}
899
900func (r *ruleFunc) String() string {
901	return r.pctx.pkgPath + "." + r.name_
902}
903
904type builtinRule struct {
905	name_      string
906	scope_     *basicScope
907	sync.Mutex // protects scope_ during lazy creation
908}
909
910func (r *builtinRule) packageContext() *packageContext {
911	return nil
912}
913
914func (r *builtinRule) name() string {
915	return r.name_
916}
917
918func (r *builtinRule) fullName(pkgNames map[*packageContext]string) string {
919	return r.name_
920}
921
922func (r *builtinRule) memoizeFullName(pkgNames map[*packageContext]string) {
923	// Nothing to do, full name is known at initialization.
924}
925
926func (r *builtinRule) def(config interface{}) (*ruleDef, error) {
927	return nil, errRuleIsBuiltin
928}
929
930func (r *builtinRule) scope() *basicScope {
931	r.Lock()
932	defer r.Unlock()
933
934	if r.scope_ == nil {
935		r.scope_ = makeRuleScope(nil, nil)
936	}
937	return r.scope_
938}
939
940func (r *builtinRule) isArg(argName string) bool {
941	return false
942}
943
944func (r *builtinRule) String() string {
945	return "<builtin>:" + r.name_
946}
947
948// NewBuiltinRule returns a Rule object that refers to a rule that was created outside of Blueprint
949func NewBuiltinRule(name string) Rule {
950	return &builtinRule{
951		name_: name,
952	}
953}
954
955func (p *packageContext) AddNinjaFileDeps(deps ...string) {
956	p.ninjaFileDeps = append(p.ninjaFileDeps, deps...)
957}
958