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