blob: 0d3f0ae71f4538ac4f61a12708f99cd5202230e8 [file] [log] [blame]
Colin Cross8e0c5112015-01-23 14:15:10 -08001// 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
Jamie Gennis1bc967e2014-05-27 16:34:41 -070015package blueprint
16
17import (
18 "errors"
19 "fmt"
Jamie Gennis28a63fb2014-06-05 19:50:44 -070020 "reflect"
Jamie Gennis1bc967e2014-05-27 16:34:41 -070021 "runtime"
22 "strings"
Colin Cross691a60d2015-01-07 18:08:56 -080023 "sync"
Jamie Gennis1bc967e2014-05-27 16:34:41 -070024)
25
Jamie Gennis2fb20952014-10-03 02:49:58 -070026// 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// }
56type PackageContext struct {
Jamie Gennis1bc967e2014-05-27 16:34:41 -070057 fullName string
58 shortName string
59 pkgPath string
Jamie Gennis48aed8c2014-06-13 18:25:54 -070060 scope *basicScope
Jamie Gennis1bc967e2014-05-27 16:34:41 -070061}
62
Jamie Gennis2fb20952014-10-03 02:49:58 -070063var 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.
69func 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}
Jamie Gennis1bc967e2014-05-27 16:34:41 -070096
Jamie Gennis1bc967e2014-05-27 16:34:41 -070097var Phony Rule = &builtinRule{
98 name_: "phony",
99}
100
Ken Oslund4b9a0512015-04-14 16:26:58 -0700101var Console Pool = &builtinPool{
102 name_: "console",
103}
104
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700105var errRuleIsBuiltin = errors.New("the rule is a built-in")
Ken Oslund4b9a0512015-04-14 16:26:58 -0700106var errPoolIsBuiltin = errors.New("the pool is a built-in")
Jamie Gennis28a63fb2014-06-05 19:50:44 -0700107var errVariableIsArg = errors.New("argument variables have no value")
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700108
Jamie Gennis2fb20952014-10-03 02:49:58 -0700109// checkCalledFromInit panics if a Go package's init function is not on the
110// call stack.
111func 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 }
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700122}
123
Jamie Gennis0ed63ef2014-06-30 18:07:17 -0700124// 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.
Jamie Gennis2fb20952014-10-03 02:49:58 -0700127func callerName(skip int) (pkgPath, funcName string, ok bool) {
Jamie Gennis0ed63ef2014-06-30 18:07:17 -0700128 var pc [1]uintptr
129 n := runtime.Callers(skip+1, pc[:])
130 if n != 1 {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700131 return "", "", false
Jamie Gennis0ed63ef2014-06-30 18:07:17 -0700132 }
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:]
Jamie Gennis2fb20952014-10-03 02:49:58 -0700151 ok = true
Jamie Gennis0ed63ef2014-06-30 18:07:17 -0700152 return
153}
154
Jamie Gennis2fb20952014-10-03 02:49:58 -0700155// 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.
162func pkgPathToName(pkgPath string) string {
163 return strings.Replace(pkgPath, "/", ".", -1)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700164}
165
Jamie Gennis2fb20952014-10-03 02:49:58 -0700166// 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:
Jamie Gennis28a63fb2014-06-05 19:50:44 -0700175//
176// import (
177// "blueprint"
178// "foo/bar"
179// )
180//
Jamie Gennis2fb20952014-10-03 02:49:58 -0700181// var pctx = NewPackagePath("blah")
182//
Jamie Gennis28a63fb2014-06-05 19:50:44 -0700183// func init() {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700184// pctx.Import("foo/bar")
Jamie Gennis28a63fb2014-06-05 19:50:44 -0700185// }
186//
187// ...
188//
189// func (m *MyModule) GenerateBuildActions(ctx blueprint.Module) {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700190// ctx.Build(pctx, blueprint.BuildParams{
Jamie Gennis28a63fb2014-06-05 19:50:44 -0700191// Rule: bar.SomeRule,
192// Outputs: []string{"${bar.SomeVariable}"},
193// })
194// }
Jamie Gennisd4e10182014-06-12 20:06:50 -0700195//
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.
Jamie Gennis2fb20952014-10-03 02:49:58 -0700201func (p *PackageContext) Import(pkgPath string) {
202 checkCalledFromInit()
203 importPkg, ok := packageContexts[pkgPath]
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700204 if !ok {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700205 panic(fmt.Errorf("package %q has no context", pkgPath))
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700206 }
207
Jamie Gennis2fb20952014-10-03 02:49:58 -0700208 err := p.scope.AddImport(importPkg.shortName, importPkg.scope)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700209 if err != nil {
210 panic(err)
211 }
212}
213
Jamie Gennisd4e10182014-06-12 20:06:50 -0700214// 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.
Jamie Gennis2fb20952014-10-03 02:49:58 -0700217func (p *PackageContext) ImportAs(as, pkgPath string) {
218 checkCalledFromInit()
219 importPkg, ok := packageContexts[pkgPath]
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700220 if !ok {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700221 panic(fmt.Errorf("package %q has no context", pkgPath))
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700222 }
223
224 err := validateNinjaName(as)
225 if err != nil {
226 panic(err)
227 }
228
Jamie Gennis2fb20952014-10-03 02:49:58 -0700229 err = p.scope.AddImport(as, importPkg.scope)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700230 if err != nil {
231 panic(err)
232 }
233}
234
235type staticVariable struct {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700236 pctx *PackageContext
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700237 name_ string
238 value_ string
239}
240
Jamie Gennisd4e10182014-06-12 20:06:50 -0700241// 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.
Jamie Gennis2fb20952014-10-03 02:49:58 -0700250func (p *PackageContext) StaticVariable(name, value string) Variable {
251 checkCalledFromInit()
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700252 err := validateNinjaName(name)
253 if err != nil {
254 panic(err)
255 }
256
Jamie Gennis2fb20952014-10-03 02:49:58 -0700257 v := &staticVariable{p, name, value}
258 err = p.scope.AddVariable(v)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700259 if err != nil {
260 panic(err)
261 }
262
263 return v
264}
265
Jamie Gennis2fb20952014-10-03 02:49:58 -0700266func (v *staticVariable) packageContext() *PackageContext {
267 return v.pctx
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700268}
269
270func (v *staticVariable) name() string {
271 return v.name_
272}
273
Jamie Gennis2fb20952014-10-03 02:49:58 -0700274func (v *staticVariable) fullName(pkgNames map[*PackageContext]string) string {
275 return packageNamespacePrefix(pkgNames[v.pctx]) + v.name_
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700276}
277
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700278func (v *staticVariable) value(interface{}) (*ninjaString, error) {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700279 ninjaStr, err := parseNinjaString(v.pctx.scope, v.value_)
Jamie Gennis71bd58a2014-06-13 18:19:16 -0700280 if err != nil {
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700281 err = fmt.Errorf("error parsing variable %s value: %s", v, err)
Jamie Gennis71bd58a2014-06-13 18:19:16 -0700282 panic(err)
283 }
284 return ninjaStr, nil
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700285}
286
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700287func (v *staticVariable) String() string {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700288 return v.pctx.pkgPath + "." + v.name_
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700289}
290
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700291type variableFunc struct {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700292 pctx *PackageContext
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700293 name_ string
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700294 value_ func(interface{}) (string, error)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700295}
296
297// VariableFunc returns a Variable whose value is determined by a function that
Jamie Gennisd4e10182014-06-12 20:06:50 -0700298// 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.
Jamie Gennis2fb20952014-10-03 02:49:58 -0700308func (p *PackageContext) VariableFunc(name string,
309 f func(config interface{}) (string, error)) Variable {
310
311 checkCalledFromInit()
Jamie Gennisd4e10182014-06-12 20:06:50 -0700312
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700313 err := validateNinjaName(name)
314 if err != nil {
315 panic(err)
316 }
317
Jamie Gennis2fb20952014-10-03 02:49:58 -0700318 v := &variableFunc{p, name, f}
319 err = p.scope.AddVariable(v)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700320 if err != nil {
321 panic(err)
322 }
323
324 return v
325}
326
Jamie Gennis28a63fb2014-06-05 19:50:44 -0700327// VariableConfigMethod returns a Variable whose value is determined by calling
Jamie Gennisd4e10182014-06-12 20:06:50 -0700328// 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.
Jamie Gennis2fb20952014-10-03 02:49:58 -0700338func (p *PackageContext) VariableConfigMethod(name string,
339 method interface{}) Variable {
340
341 checkCalledFromInit()
342
Jamie Gennis28a63fb2014-06-05 19:50:44 -0700343 err := validateNinjaName(name)
344 if err != nil {
345 panic(err)
346 }
347
Jamie Gennis28a63fb2014-06-05 19:50:44 -0700348 methodValue := reflect.ValueOf(method)
349 validateVariableMethod(name, methodValue)
350
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700351 fun := func(config interface{}) (string, error) {
Jamie Gennis28a63fb2014-06-05 19:50:44 -0700352 result := methodValue.Call([]reflect.Value{reflect.ValueOf(config)})
353 resultStr := result[0].Interface().(string)
354 return resultStr, nil
355 }
356
Jamie Gennis2fb20952014-10-03 02:49:58 -0700357 v := &variableFunc{p, name, fun}
358 err = p.scope.AddVariable(v)
Jamie Gennis28a63fb2014-06-05 19:50:44 -0700359 if err != nil {
360 panic(err)
361 }
362
363 return v
364}
365
Jamie Gennis2fb20952014-10-03 02:49:58 -0700366func (v *variableFunc) packageContext() *PackageContext {
367 return v.pctx
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700368}
369
370func (v *variableFunc) name() string {
371 return v.name_
372}
373
Jamie Gennis2fb20952014-10-03 02:49:58 -0700374func (v *variableFunc) fullName(pkgNames map[*PackageContext]string) string {
375 return packageNamespacePrefix(pkgNames[v.pctx]) + v.name_
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700376}
377
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700378func (v *variableFunc) value(config interface{}) (*ninjaString, error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700379 value, err := v.value_(config)
380 if err != nil {
381 return nil, err
382 }
Jamie Gennis71bd58a2014-06-13 18:19:16 -0700383
Jamie Gennis2fb20952014-10-03 02:49:58 -0700384 ninjaStr, err := parseNinjaString(v.pctx.scope, value)
Jamie Gennis71bd58a2014-06-13 18:19:16 -0700385 if err != nil {
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700386 err = fmt.Errorf("error parsing variable %s value: %s", v, err)
Jamie Gennis71bd58a2014-06-13 18:19:16 -0700387 panic(err)
388 }
389
390 return ninjaStr, nil
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700391}
392
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700393func (v *variableFunc) String() string {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700394 return v.pctx.pkgPath + "." + v.name_
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700395}
396
Jamie Gennis28a63fb2014-06-05 19:50:44 -0700397func 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
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700417// 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.
422type argVariable struct {
423 name_ string
424}
425
Jamie Gennis2fb20952014-10-03 02:49:58 -0700426func (v *argVariable) packageContext() *PackageContext {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700427 panic("this should not be called")
428}
429
430func (v *argVariable) name() string {
431 return v.name_
432}
433
Jamie Gennis2fb20952014-10-03 02:49:58 -0700434func (v *argVariable) fullName(pkgNames map[*PackageContext]string) string {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700435 return v.name_
436}
437
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700438func (v *argVariable) value(config interface{}) (*ninjaString, error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700439 return nil, errVariableIsArg
440}
441
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700442func (v *argVariable) String() string {
443 return "<arg>:" + v.name_
444}
445
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700446type staticPool struct {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700447 pctx *PackageContext
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700448 name_ string
449 params PoolParams
450}
451
Jamie Gennisd4e10182014-06-12 20:06:50 -0700452// 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.
Jamie Gennis2fb20952014-10-03 02:49:58 -0700461func (p *PackageContext) StaticPool(name string, params PoolParams) Pool {
462 checkCalledFromInit()
463
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700464 err := validateNinjaName(name)
465 if err != nil {
466 panic(err)
467 }
468
Jamie Gennis2fb20952014-10-03 02:49:58 -0700469 pool := &staticPool{p, name, params}
470 err = p.scope.AddPool(pool)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700471 if err != nil {
472 panic(err)
473 }
474
Jamie Gennis2fb20952014-10-03 02:49:58 -0700475 return pool
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700476}
477
Jamie Gennis2fb20952014-10-03 02:49:58 -0700478func (p *staticPool) packageContext() *PackageContext {
479 return p.pctx
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700480}
481
482func (p *staticPool) name() string {
483 return p.name_
484}
485
Jamie Gennis2fb20952014-10-03 02:49:58 -0700486func (p *staticPool) fullName(pkgNames map[*PackageContext]string) string {
487 return packageNamespacePrefix(pkgNames[p.pctx]) + p.name_
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700488}
489
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700490func (p *staticPool) def(config interface{}) (*poolDef, error) {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700491 def, err := parsePoolParams(p.pctx.scope, &p.params)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700492 if err != nil {
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700493 panic(fmt.Errorf("error parsing PoolParams for %s: %s", p, err))
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700494 }
495 return def, nil
496}
497
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700498func (p *staticPool) String() string {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700499 return p.pctx.pkgPath + "." + p.name_
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700500}
501
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700502type poolFunc struct {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700503 pctx *PackageContext
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700504 name_ string
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700505 paramsFunc func(interface{}) (PoolParams, error)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700506}
507
Jamie Gennisd4e10182014-06-12 20:06:50 -0700508// 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.
Jamie Gennis2fb20952014-10-03 02:49:58 -0700518func (p *PackageContext) PoolFunc(name string, f func(interface{}) (PoolParams,
519 error)) Pool {
520
521 checkCalledFromInit()
522
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700523 err := validateNinjaName(name)
524 if err != nil {
525 panic(err)
526 }
527
Jamie Gennis2fb20952014-10-03 02:49:58 -0700528 pool := &poolFunc{p, name, f}
529 err = p.scope.AddPool(pool)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700530 if err != nil {
531 panic(err)
532 }
533
Jamie Gennis2fb20952014-10-03 02:49:58 -0700534 return pool
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700535}
536
Jamie Gennis2fb20952014-10-03 02:49:58 -0700537func (p *poolFunc) packageContext() *PackageContext {
538 return p.pctx
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700539}
540
541func (p *poolFunc) name() string {
542 return p.name_
543}
544
Jamie Gennis2fb20952014-10-03 02:49:58 -0700545func (p *poolFunc) fullName(pkgNames map[*PackageContext]string) string {
546 return packageNamespacePrefix(pkgNames[p.pctx]) + p.name_
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700547}
548
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700549func (p *poolFunc) def(config interface{}) (*poolDef, error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700550 params, err := p.paramsFunc(config)
551 if err != nil {
552 return nil, err
553 }
Jamie Gennis2fb20952014-10-03 02:49:58 -0700554 def, err := parsePoolParams(p.pctx.scope, &params)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700555 if err != nil {
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700556 panic(fmt.Errorf("error parsing PoolParams for %s: %s", p, err))
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700557 }
558 return def, nil
559}
560
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700561func (p *poolFunc) String() string {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700562 return p.pctx.pkgPath + "." + p.name_
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700563}
564
Ken Oslund4b9a0512015-04-14 16:26:58 -0700565type builtinPool struct {
Colin Cross63d5d4d2015-04-20 16:41:55 -0700566 name_ string
Ken Oslund4b9a0512015-04-14 16:26:58 -0700567}
568
569func (p *builtinPool) packageContext() *PackageContext {
570 return nil
571}
572
573func (p *builtinPool) name() string {
574 return p.name_
575}
576
577func (p *builtinPool) fullName(pkgNames map[*PackageContext]string) string {
578 return p.name_
579}
580
581func (p *builtinPool) def(config interface{}) (*poolDef, error) {
582 return nil, errPoolIsBuiltin
583}
584
585func (p *builtinPool) String() string {
586 return "<builtin>:" + p.name_
587}
588
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700589type staticRule struct {
Colin Cross691a60d2015-01-07 18:08:56 -0800590 pctx *PackageContext
591 name_ string
592 params RuleParams
593 argNames map[string]bool
594 scope_ *basicScope
595 sync.Mutex // protects scope_ during lazy creation
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700596}
597
Jamie Gennisd4e10182014-06-12 20:06:50 -0700598// 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.
Jamie Gennis2fb20952014-10-03 02:49:58 -0700616func (p *PackageContext) StaticRule(name string, params RuleParams,
617 argNames ...string) Rule {
618
619 checkCalledFromInit()
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700620
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
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700636 ruleScope := (*basicScope)(nil) // This will get created lazily
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700637
Colin Cross691a60d2015-01-07 18:08:56 -0800638 r := &staticRule{
639 pctx: p,
640 name_: name,
641 params: params,
642 argNames: argNamesSet,
643 scope_: ruleScope,
644 }
Jamie Gennis2fb20952014-10-03 02:49:58 -0700645 err = p.scope.AddRule(r)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700646 if err != nil {
647 panic(err)
648 }
649
650 return r
651}
652
Jamie Gennis2fb20952014-10-03 02:49:58 -0700653func (r *staticRule) packageContext() *PackageContext {
654 return r.pctx
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700655}
656
657func (r *staticRule) name() string {
658 return r.name_
659}
660
Jamie Gennis2fb20952014-10-03 02:49:58 -0700661func (r *staticRule) fullName(pkgNames map[*PackageContext]string) string {
662 return packageNamespacePrefix(pkgNames[r.pctx]) + r.name_
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700663}
664
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700665func (r *staticRule) def(interface{}) (*ruleDef, error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700666 def, err := parseRuleParams(r.scope(), &r.params)
667 if err != nil {
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700668 panic(fmt.Errorf("error parsing RuleParams for %s: %s", r, err))
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700669 }
670 return def, nil
671}
672
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700673func (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.
Colin Cross691a60d2015-01-07 18:08:56 -0800677 r.Lock()
678 defer r.Unlock()
679
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700680 if r.scope_ == nil {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700681 r.scope_ = makeRuleScope(r.pctx.scope, r.argNames)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700682 }
683 return r.scope_
684}
685
686func (r *staticRule) isArg(argName string) bool {
687 return r.argNames[argName]
688}
689
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700690func (r *staticRule) String() string {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700691 return r.pctx.pkgPath + "." + r.name_
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700692}
693
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700694type ruleFunc struct {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700695 pctx *PackageContext
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700696 name_ string
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700697 paramsFunc func(interface{}) (RuleParams, error)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700698 argNames map[string]bool
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700699 scope_ *basicScope
Colin Cross691a60d2015-01-07 18:08:56 -0800700 sync.Mutex // protects scope_ during lazy creation
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700701}
702
Jamie Gennisd4e10182014-06-12 20:06:50 -0700703// 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.
Jamie Gennis2fb20952014-10-03 02:49:58 -0700722func (p *PackageContext) RuleFunc(name string, f func(interface{}) (RuleParams,
723 error), argNames ...string) Rule {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700724
Jamie Gennis2fb20952014-10-03 02:49:58 -0700725 checkCalledFromInit()
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700726
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
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700742 ruleScope := (*basicScope)(nil) // This will get created lazily
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700743
Colin Cross691a60d2015-01-07 18:08:56 -0800744 rule := &ruleFunc{
745 pctx: p,
746 name_: name,
747 paramsFunc: f,
748 argNames: argNamesSet,
749 scope_: ruleScope,
750 }
Jamie Gennis2fb20952014-10-03 02:49:58 -0700751 err = p.scope.AddRule(rule)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700752 if err != nil {
753 panic(err)
754 }
755
Jamie Gennis2fb20952014-10-03 02:49:58 -0700756 return rule
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700757}
758
Jamie Gennis2fb20952014-10-03 02:49:58 -0700759func (r *ruleFunc) packageContext() *PackageContext {
760 return r.pctx
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700761}
762
763func (r *ruleFunc) name() string {
764 return r.name_
765}
766
Jamie Gennis2fb20952014-10-03 02:49:58 -0700767func (r *ruleFunc) fullName(pkgNames map[*PackageContext]string) string {
768 return packageNamespacePrefix(pkgNames[r.pctx]) + r.name_
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700769}
770
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700771func (r *ruleFunc) def(config interface{}) (*ruleDef, error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700772 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 {
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700778 panic(fmt.Errorf("error parsing RuleParams for %s: %s", r, err))
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700779 }
780 return def, nil
781}
782
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700783func (r *ruleFunc) scope() *basicScope {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700784 // 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.
Colin Cross691a60d2015-01-07 18:08:56 -0800787 r.Lock()
788 defer r.Unlock()
789
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700790 if r.scope_ == nil {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700791 r.scope_ = makeRuleScope(r.pctx.scope, r.argNames)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700792 }
793 return r.scope_
794}
795
796func (r *ruleFunc) isArg(argName string) bool {
797 return r.argNames[argName]
798}
799
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700800func (r *ruleFunc) String() string {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700801 return r.pctx.pkgPath + "." + r.name_
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700802}
803
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700804type builtinRule struct {
Colin Cross691a60d2015-01-07 18:08:56 -0800805 name_ string
806 scope_ *basicScope
807 sync.Mutex // protects scope_ during lazy creation
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700808}
809
Jamie Gennis2fb20952014-10-03 02:49:58 -0700810func (r *builtinRule) packageContext() *PackageContext {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700811 return nil
812}
813
814func (r *builtinRule) name() string {
815 return r.name_
816}
817
Jamie Gennis2fb20952014-10-03 02:49:58 -0700818func (r *builtinRule) fullName(pkgNames map[*PackageContext]string) string {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700819 return r.name_
820}
821
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700822func (r *builtinRule) def(config interface{}) (*ruleDef, error) {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700823 return nil, errRuleIsBuiltin
824}
825
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700826func (r *builtinRule) scope() *basicScope {
Colin Cross691a60d2015-01-07 18:08:56 -0800827 r.Lock()
828 defer r.Unlock()
829
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700830 if r.scope_ == nil {
831 r.scope_ = makeRuleScope(nil, nil)
832 }
833 return r.scope_
834}
835
836func (r *builtinRule) isArg(argName string) bool {
837 return false
838}
839
Jamie Gennis48aed8c2014-06-13 18:25:54 -0700840func (r *builtinRule) String() string {
841 return "<builtin>:" + r.name_
842}