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