| package blueprint |
| |
| import ( |
| "errors" |
| "fmt" |
| "reflect" |
| "regexp" |
| "runtime" |
| "strings" |
| ) |
| |
| type pkg struct { |
| fullName string |
| shortName string |
| pkgPath string |
| scope *basicScope |
| } |
| |
| var pkgs = map[string]*pkg{} |
| |
| var pkgRegexp = regexp.MustCompile(`(.*)\.init(ยท[0-9]+)?`) |
| |
| var Phony Rule = &builtinRule{ |
| name_: "phony", |
| } |
| |
| var errRuleIsBuiltin = errors.New("the rule is a built-in") |
| var errVariableIsArg = errors.New("argument variables have no value") |
| |
| // We make a Ninja-friendly name out of a Go package name by replaceing all the |
| // '/' characters with '.'. We assume the results are unique, though this is |
| // not 100% guaranteed for Go package names that already contain '.' characters. |
| // Disallowing package names with '.' isn't reasonable since many package names |
| // contain the name of the hosting site (e.g. "code.google.com"). In practice |
| // this probably isn't really a problem. |
| func pkgPathToName(pkgPath string) string { |
| return strings.Replace(pkgPath, "/", ".", -1) |
| } |
| |
| // callerPackage returns the pkg of the function that called the caller of |
| // callerPackage. The caller of callerPackage must have been called from an |
| // init function of the package or callerPackage will panic. |
| // |
| // Looking for the package's init function on the call stack and using that to |
| // determine its package name is unfortunately dependent upon Go runtime |
| // implementation details. However, it allows us to ensure that it's easy to |
| // determine where a definition in a .ninja file came from. |
| func callerPackage() *pkg { |
| var pc [1]uintptr |
| n := runtime.Callers(3, pc[:]) |
| if n != 1 { |
| panic("unable to get caller pc") |
| } |
| |
| f := runtime.FuncForPC(pc[0]) |
| callerName := f.Name() |
| |
| submatches := pkgRegexp.FindSubmatch([]byte(callerName)) |
| if submatches == nil { |
| println(callerName) |
| panic("not called from an init func") |
| } |
| |
| pkgPath := string(submatches[1]) |
| |
| pkgName := pkgPathToName(pkgPath) |
| err := validateNinjaName(pkgName) |
| if err != nil { |
| panic(err) |
| } |
| |
| i := strings.LastIndex(pkgPath, "/") |
| shortName := pkgPath[i+1:] |
| |
| p, ok := pkgs[pkgPath] |
| if !ok { |
| p = &pkg{ |
| fullName: pkgName, |
| shortName: shortName, |
| pkgPath: pkgPath, |
| scope: newScope(nil), |
| } |
| pkgs[pkgPath] = p |
| } |
| |
| return p |
| } |
| |
| // Import enables access to the exported Ninja pools, rules, and variables that |
| // are defined at the package scope of another Go package. Go's visibility |
| // rules apply to these references - capitalized names indicate that something |
| // is exported. It may only be called from a Go package's init() function. The |
| // Go package path passed to Import must have already been imported into the Go |
| // package using a Go import statement. The imported variables may then be |
| // accessed from Ninja strings as "${pkg.Variable}", while the imported rules |
| // can simply be accessed as exported Go variables from the package. For |
| // example: |
| // |
| // import ( |
| // "blueprint" |
| // "foo/bar" |
| // ) |
| // |
| // func init() { |
| // blueprint.Import("foo/bar") |
| // } |
| // |
| // ... |
| // |
| // func (m *MyModule) GenerateBuildActions(ctx blueprint.Module) { |
| // ctx.Build(blueprint.BuildParams{ |
| // Rule: bar.SomeRule, |
| // Outputs: []string{"${bar.SomeVariable}"}, |
| // }) |
| // } |
| // |
| // Note that the local name used to refer to the package in Ninja variable names |
| // is derived from pkgPath by extracting the last path component. This differs |
| // from Go's import declaration, which derives the local name from the package |
| // clause in the imported package. By convention these names are made to match, |
| // but this is not required. |
| func Import(pkgPath string) { |
| callerPkg := callerPackage() |
| |
| importPkg, ok := pkgs[pkgPath] |
| if !ok { |
| panic(fmt.Errorf("package %q has no Blueprints definitions", pkgPath)) |
| } |
| |
| err := callerPkg.scope.AddImport(importPkg.shortName, importPkg.scope) |
| if err != nil { |
| panic(err) |
| } |
| } |
| |
| // ImportAs provides the same functionality as Import, but it allows the local |
| // name that will be used to refer to the package to be specified explicitly. |
| // It may only be called from a Go package's init() function. |
| func ImportAs(as, pkgPath string) { |
| callerPkg := callerPackage() |
| |
| importPkg, ok := pkgs[pkgPath] |
| if !ok { |
| panic(fmt.Errorf("package %q has no Blueprints definitions", pkgPath)) |
| } |
| |
| err := validateNinjaName(as) |
| if err != nil { |
| panic(err) |
| } |
| |
| err = callerPkg.scope.AddImport(as, importPkg.scope) |
| if err != nil { |
| panic(err) |
| } |
| } |
| |
| type staticVariable struct { |
| pkg_ *pkg |
| name_ string |
| value_ string |
| } |
| |
| // StaticVariable returns a Variable whose value does not depend on any |
| // configuration information. It may only be called during a Go package's |
| // initialization - either from the init() function or as part of a package- |
| // scoped variable's initialization. |
| // |
| // This function is usually used to initialize a package-scoped Go variable that |
| // represents a Ninja variable that will be output. The name argument should |
| // exactly match the Go variable name, and the value string may reference other |
| // Ninja variables that are visible within the calling Go package. |
| func StaticVariable(name, value string) Variable { |
| err := validateNinjaName(name) |
| if err != nil { |
| panic(err) |
| } |
| |
| pkg := callerPackage() |
| |
| v := &staticVariable{pkg, name, value} |
| err = pkg.scope.AddVariable(v) |
| if err != nil { |
| panic(err) |
| } |
| |
| return v |
| } |
| |
| func (v *staticVariable) pkg() *pkg { |
| return v.pkg_ |
| } |
| |
| func (v *staticVariable) name() string { |
| return v.name_ |
| } |
| |
| func (v *staticVariable) fullName(pkgNames map[*pkg]string) string { |
| return packageNamespacePrefix(pkgNames[v.pkg_]) + v.name_ |
| } |
| |
| func (v *staticVariable) value(interface{}) (*ninjaString, error) { |
| ninjaStr, err := parseNinjaString(v.pkg_.scope, v.value_) |
| if err != nil { |
| err = fmt.Errorf("error parsing variable %s value: %s", v, err) |
| panic(err) |
| } |
| return ninjaStr, nil |
| } |
| |
| func (v *staticVariable) String() string { |
| return v.pkg_.pkgPath + "." + v.name_ |
| } |
| |
| type variableFunc struct { |
| pkg_ *pkg |
| name_ string |
| value_ func(interface{}) (string, error) |
| } |
| |
| // VariableFunc returns a Variable whose value is determined by a function that |
| // takes a config object as input and returns either the variable value or an |
| // error. It may only be called during a Go package's initialization - either |
| // from the init() function or as part of a package-scoped variable's |
| // initialization. |
| // |
| // This function is usually used to initialize a package-scoped Go variable that |
| // represents a Ninja variable that will be output. The name argument should |
| // exactly match the Go variable name, and the value string returned by f may |
| // reference other Ninja variables that are visible within the calling Go |
| // package. |
| func VariableFunc(name string, f func(config interface{}) (string, |
| error)) Variable { |
| |
| err := validateNinjaName(name) |
| if err != nil { |
| panic(err) |
| } |
| |
| pkg := callerPackage() |
| |
| v := &variableFunc{pkg, name, f} |
| err = pkg.scope.AddVariable(v) |
| if err != nil { |
| panic(err) |
| } |
| |
| return v |
| } |
| |
| // VariableConfigMethod returns a Variable whose value is determined by calling |
| // a method on the config object. The method must take no arguments and return |
| // a single string that will be the variable's value. It may only be called |
| // during a Go package's initialization - either from the init() function or as |
| // part of a package-scoped variable's initialization. |
| // |
| // This function is usually used to initialize a package-scoped Go variable that |
| // represents a Ninja variable that will be output. The name argument should |
| // exactly match the Go variable name, and the value string returned by method |
| // may reference other Ninja variables that are visible within the calling Go |
| // package. |
| func VariableConfigMethod(name string, method interface{}) Variable { |
| err := validateNinjaName(name) |
| if err != nil { |
| panic(err) |
| } |
| |
| pkg := callerPackage() |
| |
| methodValue := reflect.ValueOf(method) |
| validateVariableMethod(name, methodValue) |
| |
| fun := func(config interface{}) (string, error) { |
| result := methodValue.Call([]reflect.Value{reflect.ValueOf(config)}) |
| resultStr := result[0].Interface().(string) |
| return resultStr, nil |
| } |
| |
| v := &variableFunc{pkg, name, fun} |
| err = pkg.scope.AddVariable(v) |
| if err != nil { |
| panic(err) |
| } |
| |
| return v |
| } |
| |
| func (v *variableFunc) pkg() *pkg { |
| return v.pkg_ |
| } |
| |
| func (v *variableFunc) name() string { |
| return v.name_ |
| } |
| |
| func (v *variableFunc) fullName(pkgNames map[*pkg]string) string { |
| return packageNamespacePrefix(pkgNames[v.pkg_]) + v.name_ |
| } |
| |
| func (v *variableFunc) value(config interface{}) (*ninjaString, error) { |
| value, err := v.value_(config) |
| if err != nil { |
| return nil, err |
| } |
| |
| ninjaStr, err := parseNinjaString(v.pkg_.scope, value) |
| if err != nil { |
| err = fmt.Errorf("error parsing variable %s value: %s", v, err) |
| panic(err) |
| } |
| |
| return ninjaStr, nil |
| } |
| |
| func (v *variableFunc) String() string { |
| return v.pkg_.pkgPath + "." + v.name_ |
| } |
| |
| func validateVariableMethod(name string, methodValue reflect.Value) { |
| methodType := methodValue.Type() |
| if methodType.Kind() != reflect.Func { |
| panic(fmt.Errorf("method given for variable %s is not a function", |
| name)) |
| } |
| if n := methodType.NumIn(); n != 1 { |
| panic(fmt.Errorf("method for variable %s has %d inputs (should be 1)", |
| name, n)) |
| } |
| if n := methodType.NumOut(); n != 1 { |
| panic(fmt.Errorf("method for variable %s has %d outputs (should be 1)", |
| name, n)) |
| } |
| if kind := methodType.Out(0).Kind(); kind != reflect.String { |
| panic(fmt.Errorf("method for variable %s does not return a string", |
| name)) |
| } |
| } |
| |
| // An argVariable is a Variable that exists only when it is set by a build |
| // statement to pass a value to the rule being invoked. It has no value, so it |
| // can never be used to create a Ninja assignment statement. It is inserted |
| // into the rule's scope, which is used for name lookups within the rule and |
| // when assigning argument values as part of a build statement. |
| type argVariable struct { |
| name_ string |
| } |
| |
| func (v *argVariable) pkg() *pkg { |
| panic("this should not be called") |
| } |
| |
| func (v *argVariable) name() string { |
| return v.name_ |
| } |
| |
| func (v *argVariable) fullName(pkgNames map[*pkg]string) string { |
| return v.name_ |
| } |
| |
| func (v *argVariable) value(config interface{}) (*ninjaString, error) { |
| return nil, errVariableIsArg |
| } |
| |
| func (v *argVariable) String() string { |
| return "<arg>:" + v.name_ |
| } |
| |
| type staticPool struct { |
| pkg_ *pkg |
| name_ string |
| params PoolParams |
| } |
| |
| // StaticPool returns a Pool whose value does not depend on any configuration |
| // information. It may only be called during a Go package's initialization - |
| // either from the init() function or as part of a package-scoped Go variable's |
| // initialization. |
| // |
| // This function is usually used to initialize a package-scoped Go variable that |
| // represents a Ninja pool that will be output. The name argument should |
| // exactly match the Go variable name, and the params fields may reference other |
| // Ninja variables that are visible within the calling Go package. |
| func StaticPool(name string, params PoolParams) Pool { |
| err := validateNinjaName(name) |
| if err != nil { |
| panic(err) |
| } |
| |
| pkg := callerPackage() |
| |
| p := &staticPool{pkg, name, params} |
| err = pkg.scope.AddPool(p) |
| if err != nil { |
| panic(err) |
| } |
| |
| return p |
| } |
| |
| func (p *staticPool) pkg() *pkg { |
| return p.pkg_ |
| } |
| |
| func (p *staticPool) name() string { |
| return p.name_ |
| } |
| |
| func (p *staticPool) fullName(pkgNames map[*pkg]string) string { |
| return packageNamespacePrefix(pkgNames[p.pkg_]) + p.name_ |
| } |
| |
| func (p *staticPool) def(config interface{}) (*poolDef, error) { |
| def, err := parsePoolParams(p.pkg_.scope, &p.params) |
| if err != nil { |
| panic(fmt.Errorf("error parsing PoolParams for %s: %s", p, err)) |
| } |
| return def, nil |
| } |
| |
| func (p *staticPool) String() string { |
| return p.pkg_.pkgPath + "." + p.name_ |
| } |
| |
| type poolFunc struct { |
| pkg_ *pkg |
| name_ string |
| paramsFunc func(interface{}) (PoolParams, error) |
| } |
| |
| // PoolFunc returns a Pool whose value is determined by a function that takes a |
| // config object as input and returns either the pool parameters or an error. It |
| // may only be called during a Go package's initialization - either from the |
| // init() function or as part of a package-scoped variable's initialization. |
| // |
| // This function is usually used to initialize a package-scoped Go variable that |
| // represents a Ninja pool that will be output. The name argument should |
| // exactly match the Go variable name, and the string fields of the PoolParams |
| // returned by f may reference other Ninja variables that are visible within the |
| // calling Go package. |
| func PoolFunc(name string, f func(interface{}) (PoolParams, error)) Pool { |
| err := validateNinjaName(name) |
| if err != nil { |
| panic(err) |
| } |
| |
| pkg := callerPackage() |
| |
| p := &poolFunc{pkg, name, f} |
| err = pkg.scope.AddPool(p) |
| if err != nil { |
| panic(err) |
| } |
| |
| return p |
| } |
| |
| func (p *poolFunc) pkg() *pkg { |
| return p.pkg_ |
| } |
| |
| func (p *poolFunc) name() string { |
| return p.name_ |
| } |
| |
| func (p *poolFunc) fullName(pkgNames map[*pkg]string) string { |
| return packageNamespacePrefix(pkgNames[p.pkg_]) + p.name_ |
| } |
| |
| func (p *poolFunc) def(config interface{}) (*poolDef, error) { |
| params, err := p.paramsFunc(config) |
| if err != nil { |
| return nil, err |
| } |
| def, err := parsePoolParams(p.pkg_.scope, ¶ms) |
| if err != nil { |
| panic(fmt.Errorf("error parsing PoolParams for %s: %s", p, err)) |
| } |
| return def, nil |
| } |
| |
| func (p *poolFunc) String() string { |
| return p.pkg_.pkgPath + "." + p.name_ |
| } |
| |
| type staticRule struct { |
| pkg_ *pkg |
| name_ string |
| params RuleParams |
| argNames map[string]bool |
| scope_ *basicScope |
| } |
| |
| // StaticRule returns a Rule whose value does not depend on any configuration |
| // information. It may only be called during a Go package's initialization - |
| // either from the init() function or as part of a package-scoped Go variable's |
| // initialization. |
| // |
| // This function is usually used to initialize a package-scoped Go variable that |
| // represents a Ninja rule that will be output. The name argument should |
| // exactly match the Go variable name, and the params fields may reference other |
| // Ninja variables that are visible within the calling Go package. |
| // |
| // The argNames arguments list Ninja variables that may be overridden by Ninja |
| // build statements that invoke the rule. These arguments may be referenced in |
| // any of the string fields of params. Arguments can shadow package-scoped |
| // variables defined within the caller's Go package, but they may not shadow |
| // those defined in another package. Shadowing a package-scoped variable |
| // results in the package-scoped variable's value being used for build |
| // statements that do not override the argument. For argument names that do not |
| // shadow package-scoped variables the default value is an empty string. |
| func StaticRule(name string, params RuleParams, argNames ...string) Rule { |
| pkg := callerPackage() |
| |
| err := validateNinjaName(name) |
| if err != nil { |
| panic(err) |
| } |
| |
| err = validateArgNames(argNames) |
| if err != nil { |
| panic(fmt.Errorf("invalid argument name: %s", err)) |
| } |
| |
| argNamesSet := make(map[string]bool) |
| for _, argName := range argNames { |
| argNamesSet[argName] = true |
| } |
| |
| ruleScope := (*basicScope)(nil) // This will get created lazily |
| |
| r := &staticRule{pkg, name, params, argNamesSet, ruleScope} |
| err = pkg.scope.AddRule(r) |
| if err != nil { |
| panic(err) |
| } |
| |
| return r |
| } |
| |
| func (r *staticRule) pkg() *pkg { |
| return r.pkg_ |
| } |
| |
| func (r *staticRule) name() string { |
| return r.name_ |
| } |
| |
| func (r *staticRule) fullName(pkgNames map[*pkg]string) string { |
| return packageNamespacePrefix(pkgNames[r.pkg_]) + r.name_ |
| } |
| |
| func (r *staticRule) def(interface{}) (*ruleDef, error) { |
| def, err := parseRuleParams(r.scope(), &r.params) |
| if err != nil { |
| panic(fmt.Errorf("error parsing RuleParams for %s: %s", r, err)) |
| } |
| return def, nil |
| } |
| |
| func (r *staticRule) scope() *basicScope { |
| // We lazily create the scope so that all the package-scoped variables get |
| // declared before the args are created. Otherwise we could incorrectly |
| // shadow a package-scoped variable with an arg variable. |
| if r.scope_ == nil { |
| r.scope_ = makeRuleScope(r.pkg_.scope, r.argNames) |
| } |
| return r.scope_ |
| } |
| |
| func (r *staticRule) isArg(argName string) bool { |
| return r.argNames[argName] |
| } |
| |
| func (r *staticRule) String() string { |
| return r.pkg_.pkgPath + "." + r.name_ |
| } |
| |
| type ruleFunc struct { |
| pkg_ *pkg |
| name_ string |
| paramsFunc func(interface{}) (RuleParams, error) |
| argNames map[string]bool |
| scope_ *basicScope |
| } |
| |
| // RuleFunc returns a Rule whose value is determined by a function that takes a |
| // config object as input and returns either the rule parameters or an error. It |
| // may only be called during a Go package's initialization - either from the |
| // init() function or as part of a package-scoped variable's initialization. |
| // |
| // This function is usually used to initialize a package-scoped Go variable that |
| // represents a Ninja rule that will be output. The name argument should |
| // exactly match the Go variable name, and the string fields of the RuleParams |
| // returned by f may reference other Ninja variables that are visible within the |
| // calling Go package. |
| // |
| // The argNames arguments list Ninja variables that may be overridden by Ninja |
| // build statements that invoke the rule. These arguments may be referenced in |
| // any of the string fields of the RuleParams returned by f. Arguments can |
| // shadow package-scoped variables defined within the caller's Go package, but |
| // they may not shadow those defined in another package. Shadowing a package- |
| // scoped variable results in the package-scoped variable's value being used for |
| // build statements that do not override the argument. For argument names that |
| // do not shadow package-scoped variables the default value is an empty string. |
| func RuleFunc(name string, f func(interface{}) (RuleParams, error), |
| argNames ...string) Rule { |
| |
| pkg := callerPackage() |
| |
| err := validateNinjaName(name) |
| if err != nil { |
| panic(err) |
| } |
| |
| err = validateArgNames(argNames) |
| if err != nil { |
| panic(fmt.Errorf("invalid argument name: %s", err)) |
| } |
| |
| argNamesSet := make(map[string]bool) |
| for _, argName := range argNames { |
| argNamesSet[argName] = true |
| } |
| |
| ruleScope := (*basicScope)(nil) // This will get created lazily |
| |
| r := &ruleFunc{pkg, name, f, argNamesSet, ruleScope} |
| err = pkg.scope.AddRule(r) |
| if err != nil { |
| panic(err) |
| } |
| |
| return r |
| } |
| |
| func (r *ruleFunc) pkg() *pkg { |
| return r.pkg_ |
| } |
| |
| func (r *ruleFunc) name() string { |
| return r.name_ |
| } |
| |
| func (r *ruleFunc) fullName(pkgNames map[*pkg]string) string { |
| return packageNamespacePrefix(pkgNames[r.pkg_]) + r.name_ |
| } |
| |
| func (r *ruleFunc) def(config interface{}) (*ruleDef, error) { |
| params, err := r.paramsFunc(config) |
| if err != nil { |
| return nil, err |
| } |
| def, err := parseRuleParams(r.scope(), ¶ms) |
| if err != nil { |
| panic(fmt.Errorf("error parsing RuleParams for %s: %s", r, err)) |
| } |
| return def, nil |
| } |
| |
| func (r *ruleFunc) scope() *basicScope { |
| // We lazily create the scope so that all the global variables get declared |
| // before the args are created. Otherwise we could incorrectly shadow a |
| // global variable with an arg variable. |
| if r.scope_ == nil { |
| r.scope_ = makeRuleScope(r.pkg_.scope, r.argNames) |
| } |
| return r.scope_ |
| } |
| |
| func (r *ruleFunc) isArg(argName string) bool { |
| return r.argNames[argName] |
| } |
| |
| func (r *ruleFunc) String() string { |
| return r.pkg_.pkgPath + "." + r.name_ |
| } |
| |
| type builtinRule struct { |
| name_ string |
| scope_ *basicScope |
| } |
| |
| func (r *builtinRule) pkg() *pkg { |
| return nil |
| } |
| |
| func (r *builtinRule) name() string { |
| return r.name_ |
| } |
| |
| func (r *builtinRule) fullName(pkgNames map[*pkg]string) string { |
| return r.name_ |
| } |
| |
| func (r *builtinRule) def(config interface{}) (*ruleDef, error) { |
| return nil, errRuleIsBuiltin |
| } |
| |
| func (r *builtinRule) scope() *basicScope { |
| if r.scope_ == nil { |
| r.scope_ = makeRuleScope(nil, nil) |
| } |
| return r.scope_ |
| } |
| |
| func (r *builtinRule) isArg(argName string) bool { |
| return false |
| } |
| |
| func (r *builtinRule) String() string { |
| return "<builtin>:" + r.name_ |
| } |
| |
| // A ModuleType represents a type of module that can be defined in a Blueprints |
| // file. In order for it to be used when interpreting Blueprints files, a |
| // ModuleType must first be registered with a Context object via the |
| // Context.RegisterModuleType method. |
| type ModuleType interface { |
| pkg() *pkg |
| name() string |
| new() (m Module, properties []interface{}) |
| } |
| |
| type moduleTypeFunc struct { |
| pkg_ *pkg |
| name_ string |
| new_ func() (Module, []interface{}) |
| } |
| |
| // MakeModuleType returns a new ModuleType object that will instantiate new |
| // Module objects with the given new function. MakeModuleType may only be |
| // called during a Go package's initialization - either from the init() function |
| // or as part of a package-scoped variable's initialization. |
| // |
| // This function is usually used to initialize a package-scoped Go ModuleType |
| // variable that can then be passed to Context.RegisterModuleType. The name |
| // argument should exactly match the Go variable name. Note that this name is |
| // different than the one passed to Context.RegisterModuleType. This name is |
| // used to identify the Go object in error messages, making it easier to |
| // identify problematic build logic code. The name passed to |
| // Context.RegisterModuleType is the name that appear in Blueprints files to |
| // instantiate modules of this type. |
| // |
| // The new function passed to MakeModuleType returns two values. The first is |
| // the newly created Module object. The second is a slice of pointers to that |
| // Module object's properties structs. Each properties struct is examined when |
| // parsing a module definition of this type in a Blueprints file. Exported |
| // fields of the properties structs are automatically set to the property values |
| // specified in the Blueprints file. The properties struct field names |
| // determine the name of the Blueprints file properties that are used - the |
| // Blueprints property name matches that of the properties struct field name |
| // with the first letter converted to lower-case. |
| // |
| // The fields of the properties struct must either []string, a string, or bool. |
| // The Context will panic if a Module gets instantiated with a properties struct |
| // containing a field that is not one these supported types. |
| // |
| // Any properties that appear in the Blueprints files that are not built-in |
| // module properties (such as "name" and "deps") and do not have a corresponding |
| // field in the returned module properties struct result in an error during the |
| // Context's parse phase. |
| // |
| // As an example, the follow code: |
| // |
| // var MyModuleType = blueprint.MakeModuleType("MyModuleType", newMyModule) |
| // |
| // type myModule struct { |
| // properties struct { |
| // Foo string |
| // Bar []string |
| // } |
| // } |
| // |
| // func newMyModule() (blueprint.Module, []interface{}) { |
| // module := new(myModule) |
| // properties := &module.properties |
| // return module, []interface{}{properties} |
| // } |
| // |
| // func main() { |
| // ctx := blueprint.NewContext() |
| // ctx.RegisterModuleType("my_module", MyModuleType) |
| // // ... |
| // } |
| // |
| // would support parsing a module defined in a Blueprints file as follows: |
| // |
| // my_module { |
| // name: "myName", |
| // foo: "my foo string", |
| // bar: ["my", "bar", "strings"], |
| // } |
| // |
| func MakeModuleType(name string, |
| new func() (m Module, propertyStructs []interface{})) ModuleType { |
| |
| pkg := callerPackage() |
| return &moduleTypeFunc{pkg, name, new} |
| } |
| |
| func (m *moduleTypeFunc) pkg() *pkg { |
| return m.pkg_ |
| } |
| |
| func (m *moduleTypeFunc) name() string { |
| return m.pkg_.pkgPath + "." + m.name_ |
| } |
| |
| func (m *moduleTypeFunc) new() (Module, []interface{}) { |
| return m.new_() |
| } |