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 "fmt" 19 "path/filepath" 20 "strings" 21 "sync" 22 "text/scanner" 23 24 "github.com/google/blueprint/parser" 25 "github.com/google/blueprint/pathtools" 26 "github.com/google/blueprint/proptools" 27) 28 29// A Module handles generating all of the Ninja build actions needed to build a 30// single module based on properties defined in a Blueprints file. Module 31// objects are initially created during the parse phase of a Context using one 32// of the registered module types (and the associated ModuleFactory function). 33// The Module's properties struct is automatically filled in with the property 34// values specified in the Blueprints file (see Context.RegisterModuleType for more 35// information on this). 36// 37// A Module can be split into multiple Modules by a Mutator. All existing 38// properties set on the module will be duplicated to the new Module, and then 39// modified as necessary by the Mutator. 40// 41// The Module implementation can access the build configuration as well as any 42// modules on which on which it depends (as defined by the "deps" property 43// specified in the Blueprints file, dynamically added by implementing the 44// (deprecated) DynamicDependerModule interface, or dynamically added by a 45// BottomUpMutator) using the ModuleContext passed to GenerateBuildActions. 46// This ModuleContext is also used to create Ninja build actions and to report 47// errors to the user. 48// 49// In addition to implementing the GenerateBuildActions method, a Module should 50// implement methods that provide dependant modules and singletons information 51// they need to generate their build actions. These methods will only be called 52// after GenerateBuildActions is called because the Context calls 53// GenerateBuildActions in dependency-order (and singletons are invoked after 54// all the Modules). The set of methods a Module supports will determine how 55// dependant Modules interact with it. 56// 57// For example, consider a Module that is responsible for generating a library 58// that other modules can link against. The library Module might implement the 59// following interface: 60// 61// type LibraryProducer interface { 62// LibraryFileName() string 63// } 64// 65// func IsLibraryProducer(module blueprint.Module) { 66// _, ok := module.(LibraryProducer) 67// return ok 68// } 69// 70// A binary-producing Module that depends on the library Module could then do: 71// 72// func (m *myBinaryModule) GenerateBuildActions(ctx blueprint.ModuleContext) { 73// ... 74// var libraryFiles []string 75// ctx.VisitDepsDepthFirstIf(IsLibraryProducer, 76// func(module blueprint.Module) { 77// libProducer := module.(LibraryProducer) 78// libraryFiles = append(libraryFiles, libProducer.LibraryFileName()) 79// }) 80// ... 81// } 82// 83// to build the list of library file names that should be included in its link 84// command. 85// 86// GenerateBuildActions may be called from multiple threads. It is guaranteed to 87// be called after it has finished being called on all dependencies and on all 88// variants of that appear earlier in the ModuleContext.VisitAllModuleVariants list. 89// Any accesses to global variables or to Module objects that are not dependencies 90// or variants of the current Module must be synchronized by the implementation of 91// GenerateBuildActions. 92type Module interface { 93 // Name returns a string used to uniquely identify each module. The return 94 // value must be unique across all modules. It is only called once, during 95 // initial blueprint parsing. To change the name later a mutator must call 96 // MutatorContext.Rename 97 // 98 // In most cases, Name should return the contents of a "name:" property from 99 // the blueprint file. An embeddable SimpleName object can be used for this 100 // case. 101 Name() string 102 103 // GenerateBuildActions is called by the Context that created the Module 104 // during its generate phase. This call should generate all Ninja build 105 // actions (rules, pools, and build statements) needed to build the module. 106 GenerateBuildActions(ModuleContext) 107} 108 109// A DynamicDependerModule is a Module that may add dependencies that do not 110// appear in its "deps" property. Any Module that implements this interface 111// will have its DynamicDependencies method called by the Context that created 112// it during generate phase. 113// 114// Deprecated, use a BottomUpMutator instead 115type DynamicDependerModule interface { 116 Module 117 118 // DynamicDependencies is called by the Context that created the 119 // DynamicDependerModule during its generate phase. This call should return 120 // the list of module names that the DynamicDependerModule depends on 121 // dynamically. Module names that already appear in the "deps" property may 122 // but do not need to be included in the returned list. 123 DynamicDependencies(DynamicDependerModuleContext) []string 124} 125 126type EarlyModuleContext interface { 127 // Module returns the current module as a Module. It should rarely be necessary, as the module already has a 128 // reference to itself. 129 Module() Module 130 131 // ModuleName returns the name of the module. This is generally the value that was returned by Module.Name() when 132 // the module was created, but may have been modified by calls to BaseMutatorContext.Rename. 133 ModuleName() string 134 135 // ModuleDir returns the path to the directory that contains the defintion of the module. 136 ModuleDir() string 137 138 // ModuleType returns the name of the module type that was used to create the module, as specified in 139 // RegisterModuleType. 140 ModuleType() string 141 142 // BlueprintFile returns the name of the blueprint file that contains the definition of this 143 // module. 144 BlueprintsFile() string 145 146 // Config returns the config object that was passed to Context.PrepareBuildActions. 147 Config() interface{} 148 149 // ContainsProperty returns true if the specified property name was set in the module definition. 150 ContainsProperty(name string) bool 151 152 // Errorf reports an error at the specified position of the module definition file. 153 Errorf(pos scanner.Position, fmt string, args ...interface{}) 154 155 // ModuleErrorf reports an error at the line number of the module type in the module definition. 156 ModuleErrorf(fmt string, args ...interface{}) 157 158 // PropertyErrorf reports an error at the line number of a property in the module definition. 159 PropertyErrorf(property, fmt string, args ...interface{}) 160 161 // Failed returns true if any errors have been reported. In most cases the module can continue with generating 162 // build rules after an error, allowing it to report additional errors in a single run, but in cases where the error 163 // has prevented the module from creating necessary data it can return early when Failed returns true. 164 Failed() bool 165 166 // GlobWithDeps returns a list of files and directories that match the 167 // specified pattern but do not match any of the patterns in excludes. 168 // Any directories will have a '/' suffix. It also adds efficient 169 // dependencies to rerun the primary builder whenever a file matching 170 // the pattern as added or removed, without rerunning if a file that 171 // does not match the pattern is added to a searched directory. 172 GlobWithDeps(pattern string, excludes []string) ([]string, error) 173 174 // Fs returns a pathtools.Filesystem that can be used to interact with files. Using the Filesystem interface allows 175 // the module to be used in build system tests that run against a mock filesystem. 176 Fs() pathtools.FileSystem 177 178 // AddNinjaFileDeps adds dependencies on the specified files to the rule that creates the ninja manifest. The 179 // primary builder will be rerun whenever the specified files are modified. 180 AddNinjaFileDeps(deps ...string) 181 182 moduleInfo() *moduleInfo 183 error(err error) 184 185 // Namespace returns the Namespace object provided by the NameInterface set by Context.SetNameInterface, or the 186 // default SimpleNameInterface if Context.SetNameInterface was not called. 187 Namespace() Namespace 188 189 // ModuleFactories returns a map of all of the global ModuleFactories by name. 190 ModuleFactories() map[string]ModuleFactory 191} 192 193type BaseModuleContext interface { 194 EarlyModuleContext 195 196 // GetDirectDepWithTag returns the Module the direct dependency with the specified name, or nil if 197 // none exists. It panics if the dependency does not have the specified tag. 198 GetDirectDepWithTag(name string, tag DependencyTag) Module 199 200 // GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified 201 // name, or nil if none exists. If there are multiple dependencies on the same module it returns 202 // the first DependencyTag. 203 GetDirectDep(name string) (Module, DependencyTag) 204 205 // VisitDirectDeps calls visit for each direct dependency. If there are multiple direct dependencies on the same 206 // module visit will be called multiple times on that module and OtherModuleDependencyTag will return a different 207 // tag for each. 208 // 209 // The Module passed to the visit function should not be retained outside of the visit function, it may be 210 // invalidated by future mutators. 211 VisitDirectDeps(visit func(Module)) 212 213 // VisitDirectDepsIf calls pred for each direct dependency, and if pred returns true calls visit. If there are 214 // multiple direct dependencies on the same module pred and visit will be called multiple times on that module and 215 // OtherModuleDependencyTag will return a different tag for each. 216 // 217 // The Module passed to the visit function should not be retained outside of the visit function, it may be 218 // invalidated by future mutators. 219 VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) 220 221 // VisitDepsDepthFirst calls visit for each transitive dependency, traversing the dependency tree in depth first 222 // order. visit will only be called once for any given module, even if there are multiple paths through the 223 // dependency tree to the module or multiple direct dependencies with different tags. OtherModuleDependencyTag will 224 // return the tag for the first path found to the module. 225 // 226 // The Module passed to the visit function should not be retained outside of the visit function, it may be 227 // invalidated by future mutators. 228 VisitDepsDepthFirst(visit func(Module)) 229 230 // VisitDepsDepthFirst calls pred for each transitive dependency, and if pred returns true calls visit, traversing 231 // the dependency tree in depth first order. visit will only be called once for any given module, even if there are 232 // multiple paths through the dependency tree to the module or multiple direct dependencies with different tags. 233 // OtherModuleDependencyTag will return the tag for the first path found to the module. The return value of pred 234 // does not affect which branches of the tree are traversed. 235 // 236 // The Module passed to the visit function should not be retained outside of the visit function, it may be 237 // invalidated by future mutators. 238 VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) 239 240 // WalkDeps calls visit for each transitive dependency, traversing the dependency tree in top down order. visit may 241 // be called multiple times for the same (child, parent) pair if there are multiple direct dependencies between the 242 // child and parent with different tags. OtherModuleDependencyTag will return the tag for the currently visited 243 // (child, parent) pair. If visit returns false WalkDeps will not continue recursing down to child. 244 // 245 // The Modules passed to the visit function should not be retained outside of the visit function, they may be 246 // invalidated by future mutators. 247 WalkDeps(visit func(Module, Module) bool) 248 249 // OtherModuleName returns the name of another Module. See BaseModuleContext.ModuleName for more information. 250 // It is intended for use inside the visit functions of Visit* and WalkDeps. 251 OtherModuleName(m Module) string 252 253 // OtherModuleDir returns the directory of another Module. See BaseModuleContext.ModuleDir for more information. 254 // It is intended for use inside the visit functions of Visit* and WalkDeps. 255 OtherModuleDir(m Module) string 256 257 // OtherModuleSubDir returns the unique subdirectory name of another Module. See ModuleContext.ModuleSubDir for 258 // more information. 259 // It is intended for use inside the visit functions of Visit* and WalkDeps. 260 OtherModuleSubDir(m Module) string 261 262 // OtherModuleType returns the type of another Module. See BaseModuleContext.ModuleType for more information. 263 // It is intended for use inside the visit functions of Visit* and WalkDeps. 264 OtherModuleType(m Module) string 265 266 // OtherModuleErrorf reports an error on another Module. See BaseModuleContext.ModuleErrorf for more information. 267 // It is intended for use inside the visit functions of Visit* and WalkDeps. 268 OtherModuleErrorf(m Module, fmt string, args ...interface{}) 269 270 // OtherModuleDependencyTag returns the dependency tag used to depend on a module, or nil if there is no dependency 271 // on the module. When called inside a Visit* method with current module being visited, and there are multiple 272 // dependencies on the module being visited, it returns the dependency tag used for the current dependency. 273 OtherModuleDependencyTag(m Module) DependencyTag 274 275 // OtherModuleExists returns true if a module with the specified name exists, as determined by the NameInterface 276 // passed to Context.SetNameInterface, or SimpleNameInterface if it was not called. 277 OtherModuleExists(name string) bool 278} 279 280type DynamicDependerModuleContext BottomUpMutatorContext 281 282type ModuleContext interface { 283 BaseModuleContext 284 285 // ModuleSubDir returns a unique name for the current variant of a module that can be used as part of the path 286 // to ensure that each variant of a module gets its own intermediates directory to write to. 287 ModuleSubDir() string 288 289 // Variable creates a new ninja variable scoped to the module. It can be referenced by calls to Rule and Build 290 // in the same module. 291 Variable(pctx PackageContext, name, value string) 292 293 // Rule creates a new ninja rule scoped to the module. It can be referenced by calls to Build in the same module. 294 Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) Rule 295 296 // Build creates a new ninja build statement. 297 Build(pctx PackageContext, params BuildParams) 298 299 // PrimaryModule returns the first variant of the current module. Variants of a module are always visited in 300 // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from the 301 // Module returned by PrimaryModule without data races. This can be used to perform singleton actions that are 302 // only done once for all variants of a module. 303 PrimaryModule() Module 304 305 // FinalModule returns the last variant of the current module. Variants of a module are always visited in 306 // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from all 307 // variants using VisitAllModuleVariants if the current module == FinalModule(). This can be used to perform 308 // singleton actions that are only done once for all variants of a module. 309 FinalModule() Module 310 311 // VisitAllModuleVariants calls visit for each variant of the current module. Variants of a module are always 312 // visited in order by mutators and GenerateBuildActions, so the data created by the current mutator can be read 313 // from all variants if the current module == FinalModule(). Otherwise, care must be taken to not access any 314 // data modified by the current mutator. 315 VisitAllModuleVariants(visit func(Module)) 316 317 // GetMissingDependencies returns the list of dependencies that were passed to AddDependencies or related methods, 318 // but do not exist. It can be used with Context.SetAllowMissingDependencies to allow the primary builder to 319 // handle missing dependencies on its own instead of having Blueprint treat them as an error. 320 GetMissingDependencies() []string 321} 322 323var _ BaseModuleContext = (*baseModuleContext)(nil) 324 325type baseModuleContext struct { 326 context *Context 327 config interface{} 328 module *moduleInfo 329 errs []error 330 visitingParent *moduleInfo 331 visitingDep depInfo 332 ninjaFileDeps []string 333} 334 335func (d *baseModuleContext) moduleInfo() *moduleInfo { 336 return d.module 337} 338 339func (d *baseModuleContext) Module() Module { 340 return d.module.logicModule 341} 342 343func (d *baseModuleContext) ModuleName() string { 344 return d.module.Name() 345} 346 347func (d *baseModuleContext) ModuleType() string { 348 return d.module.typeName 349} 350 351func (d *baseModuleContext) ContainsProperty(name string) bool { 352 _, ok := d.module.propertyPos[name] 353 return ok 354} 355 356func (d *baseModuleContext) ModuleDir() string { 357 return filepath.Dir(d.module.relBlueprintsFile) 358} 359 360func (d *baseModuleContext) BlueprintsFile() string { 361 return d.module.relBlueprintsFile 362} 363 364func (d *baseModuleContext) Config() interface{} { 365 return d.config 366} 367 368func (d *baseModuleContext) error(err error) { 369 if err != nil { 370 d.errs = append(d.errs, err) 371 } 372} 373 374func (d *baseModuleContext) Errorf(pos scanner.Position, 375 format string, args ...interface{}) { 376 377 d.error(&BlueprintError{ 378 Err: fmt.Errorf(format, args...), 379 Pos: pos, 380 }) 381} 382 383func (d *baseModuleContext) ModuleErrorf(format string, 384 args ...interface{}) { 385 386 d.error(&ModuleError{ 387 BlueprintError: BlueprintError{ 388 Err: fmt.Errorf(format, args...), 389 Pos: d.module.pos, 390 }, 391 module: d.module, 392 }) 393} 394 395func (d *baseModuleContext) PropertyErrorf(property, format string, 396 args ...interface{}) { 397 398 pos := d.module.propertyPos[property] 399 400 if !pos.IsValid() { 401 pos = d.module.pos 402 } 403 404 d.error(&PropertyError{ 405 ModuleError: ModuleError{ 406 BlueprintError: BlueprintError{ 407 Err: fmt.Errorf(format, args...), 408 Pos: pos, 409 }, 410 module: d.module, 411 }, 412 property: property, 413 }) 414} 415 416func (d *baseModuleContext) Failed() bool { 417 return len(d.errs) > 0 418} 419 420func (d *baseModuleContext) GlobWithDeps(pattern string, 421 excludes []string) ([]string, error) { 422 return d.context.glob(pattern, excludes) 423} 424 425func (d *baseModuleContext) Fs() pathtools.FileSystem { 426 return d.context.fs 427} 428 429func (d *baseModuleContext) Namespace() Namespace { 430 return d.context.nameInterface.GetNamespace(newNamespaceContext(d.module)) 431} 432 433var _ ModuleContext = (*moduleContext)(nil) 434 435type moduleContext struct { 436 baseModuleContext 437 scope *localScope 438 actionDefs localBuildActions 439 handledMissingDeps bool 440} 441 442func (m *baseModuleContext) OtherModuleName(logicModule Module) string { 443 module := m.context.moduleInfo[logicModule] 444 return module.Name() 445} 446 447func (m *baseModuleContext) OtherModuleDir(logicModule Module) string { 448 module := m.context.moduleInfo[logicModule] 449 return filepath.Dir(module.relBlueprintsFile) 450} 451 452func (m *baseModuleContext) OtherModuleSubDir(logicModule Module) string { 453 module := m.context.moduleInfo[logicModule] 454 return module.variantName 455} 456 457func (m *baseModuleContext) OtherModuleType(logicModule Module) string { 458 module := m.context.moduleInfo[logicModule] 459 return module.typeName 460} 461 462func (m *baseModuleContext) OtherModuleErrorf(logicModule Module, format string, 463 args ...interface{}) { 464 465 module := m.context.moduleInfo[logicModule] 466 m.errs = append(m.errs, &ModuleError{ 467 BlueprintError: BlueprintError{ 468 Err: fmt.Errorf(format, args...), 469 Pos: module.pos, 470 }, 471 module: module, 472 }) 473} 474 475func (m *baseModuleContext) OtherModuleDependencyTag(logicModule Module) DependencyTag { 476 // fast path for calling OtherModuleDependencyTag from inside VisitDirectDeps 477 if logicModule == m.visitingDep.module.logicModule { 478 return m.visitingDep.tag 479 } 480 481 for _, dep := range m.visitingParent.directDeps { 482 if dep.module.logicModule == logicModule { 483 return dep.tag 484 } 485 } 486 487 return nil 488} 489 490func (m *baseModuleContext) OtherModuleExists(name string) bool { 491 _, exists := m.context.nameInterface.ModuleFromName(name, m.module.namespace()) 492 return exists 493} 494 495func (m *baseModuleContext) GetDirectDep(name string) (Module, DependencyTag) { 496 for _, dep := range m.module.directDeps { 497 if dep.module.Name() == name { 498 return dep.module.logicModule, dep.tag 499 } 500 } 501 502 return nil, nil 503} 504 505func (m *baseModuleContext) GetDirectDepWithTag(name string, tag DependencyTag) Module { 506 var deps []depInfo 507 for _, dep := range m.module.directDeps { 508 if dep.module.Name() == name { 509 if dep.tag == tag { 510 return dep.module.logicModule 511 } 512 deps = append(deps, dep) 513 } 514 } 515 516 if len(deps) != 0 { 517 panic(fmt.Errorf("Unable to find dependency %q with requested tag %#v. Found: %#v", deps[0].module, tag, deps)) 518 } 519 520 return nil 521} 522 523func (m *baseModuleContext) VisitDirectDeps(visit func(Module)) { 524 defer func() { 525 if r := recover(); r != nil { 526 panic(newPanicErrorf(r, "VisitDirectDeps(%s, %s) for dependency %s", 527 m.module, funcName(visit), m.visitingDep.module)) 528 } 529 }() 530 531 m.visitingParent = m.module 532 533 for _, dep := range m.module.directDeps { 534 m.visitingDep = dep 535 visit(dep.module.logicModule) 536 } 537 538 m.visitingParent = nil 539 m.visitingDep = depInfo{} 540} 541 542func (m *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) { 543 defer func() { 544 if r := recover(); r != nil { 545 panic(newPanicErrorf(r, "VisitDirectDepsIf(%s, %s, %s) for dependency %s", 546 m.module, funcName(pred), funcName(visit), m.visitingDep.module)) 547 } 548 }() 549 550 m.visitingParent = m.module 551 552 for _, dep := range m.module.directDeps { 553 m.visitingDep = dep 554 if pred(dep.module.logicModule) { 555 visit(dep.module.logicModule) 556 } 557 } 558 559 m.visitingParent = nil 560 m.visitingDep = depInfo{} 561} 562 563func (m *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) { 564 defer func() { 565 if r := recover(); r != nil { 566 panic(newPanicErrorf(r, "VisitDepsDepthFirst(%s, %s) for dependency %s", 567 m.module, funcName(visit), m.visitingDep.module)) 568 } 569 }() 570 571 m.context.walkDeps(m.module, false, nil, func(dep depInfo, parent *moduleInfo) { 572 m.visitingParent = parent 573 m.visitingDep = dep 574 visit(dep.module.logicModule) 575 }) 576 577 m.visitingParent = nil 578 m.visitingDep = depInfo{} 579} 580 581func (m *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool, 582 visit func(Module)) { 583 584 defer func() { 585 if r := recover(); r != nil { 586 panic(newPanicErrorf(r, "VisitDepsDepthFirstIf(%s, %s, %s) for dependency %s", 587 m.module, funcName(pred), funcName(visit), m.visitingDep.module)) 588 } 589 }() 590 591 m.context.walkDeps(m.module, false, nil, func(dep depInfo, parent *moduleInfo) { 592 if pred(dep.module.logicModule) { 593 m.visitingParent = parent 594 m.visitingDep = dep 595 visit(dep.module.logicModule) 596 } 597 }) 598 599 m.visitingParent = nil 600 m.visitingDep = depInfo{} 601} 602 603func (m *baseModuleContext) WalkDeps(visit func(child, parent Module) bool) { 604 m.context.walkDeps(m.module, true, func(dep depInfo, parent *moduleInfo) bool { 605 m.visitingParent = parent 606 m.visitingDep = dep 607 return visit(dep.module.logicModule, parent.logicModule) 608 }, nil) 609 610 m.visitingParent = nil 611 m.visitingDep = depInfo{} 612} 613 614func (m *baseModuleContext) AddNinjaFileDeps(deps ...string) { 615 m.ninjaFileDeps = append(m.ninjaFileDeps, deps...) 616} 617 618func (m *baseModuleContext) ModuleFactories() map[string]ModuleFactory { 619 ret := make(map[string]ModuleFactory) 620 for k, v := range m.context.moduleFactories { 621 ret[k] = v 622 } 623 return ret 624} 625 626func (m *moduleContext) ModuleSubDir() string { 627 return m.module.variantName 628} 629 630func (m *moduleContext) Variable(pctx PackageContext, name, value string) { 631 m.scope.ReparentTo(pctx) 632 633 v, err := m.scope.AddLocalVariable(name, value) 634 if err != nil { 635 panic(err) 636 } 637 638 m.actionDefs.variables = append(m.actionDefs.variables, v) 639} 640 641func (m *moduleContext) Rule(pctx PackageContext, name string, 642 params RuleParams, argNames ...string) Rule { 643 644 m.scope.ReparentTo(pctx) 645 646 r, err := m.scope.AddLocalRule(name, ¶ms, argNames...) 647 if err != nil { 648 panic(err) 649 } 650 651 m.actionDefs.rules = append(m.actionDefs.rules, r) 652 653 return r 654} 655 656func (m *moduleContext) Build(pctx PackageContext, params BuildParams) { 657 m.scope.ReparentTo(pctx) 658 659 def, err := parseBuildParams(m.scope, ¶ms) 660 if err != nil { 661 panic(err) 662 } 663 664 m.actionDefs.buildDefs = append(m.actionDefs.buildDefs, def) 665} 666 667func (m *moduleContext) PrimaryModule() Module { 668 return m.module.group.modules[0].logicModule 669} 670 671func (m *moduleContext) FinalModule() Module { 672 return m.module.group.modules[len(m.module.group.modules)-1].logicModule 673} 674 675func (m *moduleContext) VisitAllModuleVariants(visit func(Module)) { 676 m.context.visitAllModuleVariants(m.module, visit) 677} 678 679func (m *moduleContext) GetMissingDependencies() []string { 680 m.handledMissingDeps = true 681 return m.module.missingDeps 682} 683 684// 685// MutatorContext 686// 687 688type mutatorContext struct { 689 baseModuleContext 690 name string 691 reverseDeps []reverseDep 692 rename []rename 693 replace []replace 694 newVariations []*moduleInfo // new variants of existing modules 695 newModules []*moduleInfo // brand new modules 696 defaultVariation *string 697} 698 699type BaseMutatorContext interface { 700 BaseModuleContext 701 702 // Rename all variants of a module. The new name is not visible to calls to ModuleName, 703 // AddDependency or OtherModuleName until after this mutator pass is complete. 704 Rename(name string) 705 706 // MutatorName returns the name that this mutator was registered with. 707 MutatorName() string 708} 709 710type EarlyMutatorContext interface { 711 BaseMutatorContext 712 713 // CreateVariations splits a module into mulitple variants, one for each name in the variationNames 714 // parameter. It returns a list of new modules in the same order as the variationNames 715 // list. 716 // 717 // If any of the dependencies of the module being operated on were already split 718 // by calling CreateVariations with the same name, the dependency will automatically 719 // be updated to point the matching variant. 720 // 721 // If a module is split, and then a module depending on the first module is not split 722 // when the Mutator is later called on it, the dependency of the depending module will 723 // automatically be updated to point to the first variant. 724 CreateVariations(...string) []Module 725 726 // CreateLocationVariations splits a module into mulitple variants, one for each name in the variantNames 727 // parameter. It returns a list of new modules in the same order as the variantNames 728 // list. 729 // 730 // Local variations do not affect automatic dependency resolution - dependencies added 731 // to the split module via deps or DynamicDependerModule must exactly match a variant 732 // that contains all the non-local variations. 733 CreateLocalVariations(...string) []Module 734} 735 736type TopDownMutatorContext interface { 737 BaseMutatorContext 738 739 // CreateModule creates a new module by calling the factory method for the specified moduleType, and applies 740 // the specified property structs to it as if the properties were set in a blueprint file. 741 CreateModule(ModuleFactory, ...interface{}) Module 742} 743 744type BottomUpMutatorContext interface { 745 BaseMutatorContext 746 747 // AddDependency adds a dependency to the given module. 748 // Does not affect the ordering of the current mutator pass, but will be ordered 749 // correctly for all future mutator passes. 750 AddDependency(module Module, tag DependencyTag, name ...string) 751 752 // AddReverseDependency adds a dependency from the destination to the given module. 753 // Does not affect the ordering of the current mutator pass, but will be ordered 754 // correctly for all future mutator passes. All reverse dependencies for a destination module are 755 // collected until the end of the mutator pass, sorted by name, and then appended to the destination 756 // module's dependency list. 757 AddReverseDependency(module Module, tag DependencyTag, name string) 758 759 // CreateVariations splits a module into mulitple variants, one for each name in the variationNames 760 // parameter. It returns a list of new modules in the same order as the variationNames 761 // list. 762 // 763 // If any of the dependencies of the module being operated on were already split 764 // by calling CreateVariations with the same name, the dependency will automatically 765 // be updated to point the matching variant. 766 // 767 // If a module is split, and then a module depending on the first module is not split 768 // when the Mutator is later called on it, the dependency of the depending module will 769 // automatically be updated to point to the first variant. 770 CreateVariations(...string) []Module 771 772 // CreateLocationVariations splits a module into mulitple variants, one for each name in the variantNames 773 // parameter. It returns a list of new modules in the same order as the variantNames 774 // list. 775 // 776 // Local variations do not affect automatic dependency resolution - dependencies added 777 // to the split module via deps or DynamicDependerModule must exactly match a variant 778 // that contains all the non-local variations. 779 CreateLocalVariations(...string) []Module 780 781 // SetDependencyVariation sets all dangling dependencies on the current module to point to the variation 782 // with given name. This function ignores the default variation set by SetDefaultDependencyVariation. 783 SetDependencyVariation(string) 784 785 // SetDefaultDependencyVariation sets the default variation when a dangling reference is detected 786 // during the subsequent calls on Create*Variations* functions. To reset, set it to nil. 787 SetDefaultDependencyVariation(*string) 788 789 // AddVariationDependencies adds deps as dependencies of the current module, but uses the variations 790 // argument to select which variant of the dependency to use. A variant of the dependency must 791 // exist that matches the all of the non-local variations of the current module, plus the variations 792 // argument. 793 AddVariationDependencies([]Variation, DependencyTag, ...string) 794 795 // AddFarVariationDependencies adds deps as dependencies of the current module, but uses the 796 // variations argument to select which variant of the dependency to use. A variant of the 797 // dependency must exist that matches the variations argument, but may also have other variations. 798 // For any unspecified variation the first variant will be used. 799 // 800 // Unlike AddVariationDependencies, the variations of the current module are ignored - the 801 // dependency only needs to match the supplied variations. 802 AddFarVariationDependencies([]Variation, DependencyTag, ...string) 803 804 // AddInterVariantDependency adds a dependency between two variants of the same module. Variants are always 805 // ordered in the same orderas they were listed in CreateVariations, and AddInterVariantDependency does not change 806 // that ordering, but it associates a DependencyTag with the dependency and makes it visible to VisitDirectDeps, 807 // WalkDeps, etc. 808 AddInterVariantDependency(tag DependencyTag, from, to Module) 809 810 // ReplaceDependencies replaces all dependencies on the identical variant of the module with the 811 // specified name with the current variant of this module. Replacements don't take effect until 812 // after the mutator pass is finished. 813 ReplaceDependencies(string) 814 815 // AliasVariation takes a variationName that was passed to CreateVariations for this module, and creates an 816 // alias from the current variant to the new variant. The alias will be valid until the next time a mutator 817 // calls CreateVariations or CreateLocalVariations on this module without also calling AliasVariation. The 818 // alias can be used to add dependencies on the newly created variant using the variant map from before 819 // CreateVariations was run. 820 AliasVariation(variationName string) 821} 822 823// A Mutator function is called for each Module, and can use 824// MutatorContext.CreateVariations to split a Module into multiple Modules, 825// modifying properties on the new modules to differentiate them. It is called 826// after parsing all Blueprint files, but before generating any build rules, 827// and is always called on dependencies before being called on the depending module. 828// 829// The Mutator function should only modify members of properties structs, and not 830// members of the module struct itself, to ensure the modified values are copied 831// if a second Mutator chooses to split the module a second time. 832type TopDownMutator func(mctx TopDownMutatorContext) 833type BottomUpMutator func(mctx BottomUpMutatorContext) 834type EarlyMutator func(mctx EarlyMutatorContext) 835 836// DependencyTag is an interface to an arbitrary object that embeds BaseDependencyTag. It can be 837// used to transfer information on a dependency between the mutator that called AddDependency 838// and the GenerateBuildActions method. Variants created by CreateVariations have a copy of the 839// interface (pointing to the same concrete object) from their original module. 840type DependencyTag interface { 841 dependencyTag(DependencyTag) 842} 843 844type BaseDependencyTag struct { 845} 846 847func (BaseDependencyTag) dependencyTag(DependencyTag) { 848} 849 850var _ DependencyTag = BaseDependencyTag{} 851 852func (mctx *mutatorContext) MutatorName() string { 853 return mctx.name 854} 855 856func (mctx *mutatorContext) CreateVariations(variationNames ...string) []Module { 857 return mctx.createVariations(variationNames, false) 858} 859 860func (mctx *mutatorContext) CreateLocalVariations(variationNames ...string) []Module { 861 return mctx.createVariations(variationNames, true) 862} 863 864func (mctx *mutatorContext) createVariations(variationNames []string, local bool) []Module { 865 ret := []Module{} 866 modules, errs := mctx.context.createVariations(mctx.module, mctx.name, mctx.defaultVariation, variationNames) 867 if len(errs) > 0 { 868 mctx.errs = append(mctx.errs, errs...) 869 } 870 871 for i, module := range modules { 872 ret = append(ret, module.logicModule) 873 if !local { 874 if module.dependencyVariant == nil { 875 module.dependencyVariant = make(variationMap) 876 } 877 module.dependencyVariant[mctx.name] = variationNames[i] 878 } 879 } 880 881 if mctx.newVariations != nil { 882 panic("module already has variations from this mutator") 883 } 884 mctx.newVariations = modules 885 886 if len(ret) != len(variationNames) { 887 panic("oops!") 888 } 889 890 return ret 891} 892 893func (mctx *mutatorContext) AliasVariation(variationName string) { 894 if mctx.module.aliasTarget != nil { 895 panic(fmt.Errorf("AliasVariation already called")) 896 } 897 898 for _, variant := range mctx.newVariations { 899 if variant.variant[mctx.name] == variationName { 900 mctx.module.aliasTarget = variant 901 return 902 } 903 } 904 905 var foundVariations []string 906 for _, variant := range mctx.newVariations { 907 foundVariations = append(foundVariations, variant.variant[mctx.name]) 908 } 909 panic(fmt.Errorf("no %q variation in module variations %q", variationName, foundVariations)) 910} 911 912func (mctx *mutatorContext) SetDependencyVariation(variationName string) { 913 mctx.context.convertDepsToVariation(mctx.module, mctx.name, variationName, nil) 914} 915 916func (mctx *mutatorContext) SetDefaultDependencyVariation(variationName *string) { 917 mctx.defaultVariation = variationName 918} 919 920func (mctx *mutatorContext) Module() Module { 921 return mctx.module.logicModule 922} 923 924func (mctx *mutatorContext) AddDependency(module Module, tag DependencyTag, deps ...string) { 925 for _, dep := range deps { 926 modInfo := mctx.context.moduleInfo[module] 927 errs := mctx.context.addDependency(modInfo, tag, dep) 928 if len(errs) > 0 { 929 mctx.errs = append(mctx.errs, errs...) 930 } 931 } 932} 933 934func (mctx *mutatorContext) AddReverseDependency(module Module, tag DependencyTag, destName string) { 935 if _, ok := tag.(BaseDependencyTag); ok { 936 panic("BaseDependencyTag is not allowed to be used directly!") 937 } 938 939 destModule, errs := mctx.context.findReverseDependency(mctx.context.moduleInfo[module], destName) 940 if len(errs) > 0 { 941 mctx.errs = append(mctx.errs, errs...) 942 return 943 } 944 945 mctx.reverseDeps = append(mctx.reverseDeps, reverseDep{ 946 destModule, 947 depInfo{mctx.context.moduleInfo[module], tag}, 948 }) 949} 950 951func (mctx *mutatorContext) AddVariationDependencies(variations []Variation, tag DependencyTag, 952 deps ...string) { 953 954 for _, dep := range deps { 955 errs := mctx.context.addVariationDependency(mctx.module, variations, tag, dep, false) 956 if len(errs) > 0 { 957 mctx.errs = append(mctx.errs, errs...) 958 } 959 } 960} 961 962func (mctx *mutatorContext) AddFarVariationDependencies(variations []Variation, tag DependencyTag, 963 deps ...string) { 964 965 for _, dep := range deps { 966 errs := mctx.context.addVariationDependency(mctx.module, variations, tag, dep, true) 967 if len(errs) > 0 { 968 mctx.errs = append(mctx.errs, errs...) 969 } 970 } 971} 972 973func (mctx *mutatorContext) AddInterVariantDependency(tag DependencyTag, from, to Module) { 974 mctx.context.addInterVariantDependency(mctx.module, tag, from, to) 975} 976 977func (mctx *mutatorContext) ReplaceDependencies(name string) { 978 target := mctx.context.moduleMatchingVariant(mctx.module, name) 979 980 if target == nil { 981 panic(fmt.Errorf("ReplaceDependencies could not find identical variant %q for module %q", 982 mctx.module.variantName, name)) 983 } 984 985 mctx.replace = append(mctx.replace, replace{target, mctx.module}) 986} 987 988func (mctx *mutatorContext) Rename(name string) { 989 mctx.rename = append(mctx.rename, rename{mctx.module.group, name}) 990} 991 992func (mctx *mutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) Module { 993 module := newModule(factory) 994 995 module.relBlueprintsFile = mctx.module.relBlueprintsFile 996 module.pos = mctx.module.pos 997 module.propertyPos = mctx.module.propertyPos 998 module.createdBy = mctx.module 999 1000 for _, p := range props { 1001 err := proptools.AppendMatchingProperties(module.properties, p, nil) 1002 if err != nil { 1003 panic(err) 1004 } 1005 } 1006 1007 mctx.newModules = append(mctx.newModules, module) 1008 1009 return module.logicModule 1010} 1011 1012// SimpleName is an embeddable object to implement the ModuleContext.Name method using a property 1013// called "name". Modules that embed it must also add SimpleName.Properties to their property 1014// structure list. 1015type SimpleName struct { 1016 Properties struct { 1017 Name string 1018 } 1019} 1020 1021func (s *SimpleName) Name() string { 1022 return s.Properties.Name 1023} 1024 1025// Load Hooks 1026 1027type LoadHookContext interface { 1028 EarlyModuleContext 1029 1030 // CreateModule creates a new module by calling the factory method for the specified moduleType, and applies 1031 // the specified property structs to it as if the properties were set in a blueprint file. 1032 CreateModule(ModuleFactory, ...interface{}) Module 1033 1034 // RegisterScopedModuleType creates a new module type that is scoped to the current Blueprints 1035 // file. 1036 RegisterScopedModuleType(name string, factory ModuleFactory) 1037} 1038 1039func (l *loadHookContext) CreateModule(factory ModuleFactory, props ...interface{}) Module { 1040 module := newModule(factory) 1041 1042 module.relBlueprintsFile = l.module.relBlueprintsFile 1043 module.pos = l.module.pos 1044 module.propertyPos = l.module.propertyPos 1045 module.createdBy = l.module 1046 1047 for _, p := range props { 1048 err := proptools.AppendMatchingProperties(module.properties, p, nil) 1049 if err != nil { 1050 panic(err) 1051 } 1052 } 1053 1054 l.newModules = append(l.newModules, module) 1055 1056 return module.logicModule 1057} 1058 1059func (l *loadHookContext) RegisterScopedModuleType(name string, factory ModuleFactory) { 1060 if _, exists := l.context.moduleFactories[name]; exists { 1061 panic(fmt.Errorf("A global module type named %q already exists", name)) 1062 } 1063 1064 if _, exists := (*l.scopedModuleFactories)[name]; exists { 1065 panic(fmt.Errorf("A module type named %q already exists in this scope", name)) 1066 } 1067 1068 if *l.scopedModuleFactories == nil { 1069 (*l.scopedModuleFactories) = make(map[string]ModuleFactory) 1070 } 1071 1072 (*l.scopedModuleFactories)[name] = factory 1073} 1074 1075type loadHookContext struct { 1076 baseModuleContext 1077 newModules []*moduleInfo 1078 scopedModuleFactories *map[string]ModuleFactory 1079} 1080 1081type LoadHook func(ctx LoadHookContext) 1082 1083// Load hooks need to be added by module factories, which don't have any parameter to get to the 1084// Context, and only produce a Module interface with no base implementation, so the load hooks 1085// must be stored in a global map. The key is a pointer allocated by the module factory, so there 1086// is no chance of collisions even if tests are running in parallel with multiple contexts. The 1087// contents should be short-lived, they are added during a module factory and removed immediately 1088// after the module factory returns. 1089var pendingHooks sync.Map 1090 1091func AddLoadHook(module Module, hook LoadHook) { 1092 // Only one goroutine can be processing a given module, so no additional locking is required 1093 // for the slice stored in the sync.Map. 1094 v, exists := pendingHooks.Load(module) 1095 if !exists { 1096 v, _ = pendingHooks.LoadOrStore(module, new([]LoadHook)) 1097 } 1098 hooks := v.(*[]LoadHook) 1099 *hooks = append(*hooks, hook) 1100} 1101 1102func runAndRemoveLoadHooks(ctx *Context, config interface{}, module *moduleInfo, 1103 scopedModuleFactories *map[string]ModuleFactory) (newModules []*moduleInfo, errs []error) { 1104 1105 if v, exists := pendingHooks.Load(module.logicModule); exists { 1106 hooks := v.(*[]LoadHook) 1107 mctx := &loadHookContext{ 1108 baseModuleContext: baseModuleContext{ 1109 context: ctx, 1110 config: config, 1111 module: module, 1112 }, 1113 scopedModuleFactories: scopedModuleFactories, 1114 } 1115 1116 for _, hook := range *hooks { 1117 hook(mctx) 1118 newModules = append(newModules, mctx.newModules...) 1119 errs = append(errs, mctx.errs...) 1120 } 1121 pendingHooks.Delete(module.logicModule) 1122 1123 return newModules, errs 1124 } 1125 1126 return nil, nil 1127} 1128 1129// Check the syntax of a generated blueprint file. 1130// 1131// This is intended to perform a quick sanity check for generated blueprint 1132// code to ensure that it is syntactically correct, where syntactically correct 1133// means: 1134// * No variable definitions. 1135// * Valid module types. 1136// * Valid property names. 1137// * Valid values for the property type. 1138// 1139// It does not perform any semantic checking of properties, existence of referenced 1140// files, or dependencies. 1141// 1142// At a low level it: 1143// * Parses the contents. 1144// * Invokes relevant factory to create Module instances. 1145// * Unpacks the properties into the Module. 1146// * Does not invoke load hooks or any mutators. 1147// 1148// The filename is only used for reporting errors. 1149func CheckBlueprintSyntax(moduleFactories map[string]ModuleFactory, filename string, contents string) []error { 1150 scope := parser.NewScope(nil) 1151 file, errs := parser.Parse(filename, strings.NewReader(contents), scope) 1152 if len(errs) != 0 { 1153 return errs 1154 } 1155 1156 for _, def := range file.Defs { 1157 switch def := def.(type) { 1158 case *parser.Module: 1159 _, moduleErrs := processModuleDef(def, filename, moduleFactories, nil, false) 1160 errs = append(errs, moduleErrs...) 1161 1162 default: 1163 panic(fmt.Errorf("unknown definition type: %T", def)) 1164 } 1165 } 1166 1167 return errs 1168} 1169