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 // OtherModuleDependencyVariantExists returns true if a module with the 280 // specified name and variant exists. The variant must match the given 281 // variations. It must also match all the non-local variations of the current 282 // module. In other words, it checks for the module AddVariationDependencies 283 // would add a dependency on with the same arguments. 284 OtherModuleDependencyVariantExists(variations []Variation, name string) bool 285 286 // OtherModuleReverseDependencyVariantExists returns true if a module with the 287 // specified name exists with the same variations as the current module. In 288 // other words, it checks for the module AddReverseDependency would add a 289 // dependency on with the same argument. 290 OtherModuleReverseDependencyVariantExists(name string) bool 291} 292 293type DynamicDependerModuleContext BottomUpMutatorContext 294 295type ModuleContext interface { 296 BaseModuleContext 297 298 // ModuleSubDir returns a unique name for the current variant of a module that can be used as part of the path 299 // to ensure that each variant of a module gets its own intermediates directory to write to. 300 ModuleSubDir() string 301 302 // Variable creates a new ninja variable scoped to the module. It can be referenced by calls to Rule and Build 303 // in the same module. 304 Variable(pctx PackageContext, name, value string) 305 306 // Rule creates a new ninja rule scoped to the module. It can be referenced by calls to Build in the same module. 307 Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) Rule 308 309 // Build creates a new ninja build statement. 310 Build(pctx PackageContext, params BuildParams) 311 312 // PrimaryModule returns the first variant of the current module. Variants of a module are always visited in 313 // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from the 314 // Module returned by PrimaryModule without data races. This can be used to perform singleton actions that are 315 // only done once for all variants of a module. 316 PrimaryModule() Module 317 318 // FinalModule returns the last variant of the current module. Variants of a module are always visited in 319 // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from all 320 // variants using VisitAllModuleVariants if the current module == FinalModule(). This can be used to perform 321 // singleton actions that are only done once for all variants of a module. 322 FinalModule() Module 323 324 // VisitAllModuleVariants calls visit for each variant of the current module. Variants of a module are always 325 // visited in order by mutators and GenerateBuildActions, so the data created by the current mutator can be read 326 // from all variants if the current module == FinalModule(). Otherwise, care must be taken to not access any 327 // data modified by the current mutator. 328 VisitAllModuleVariants(visit func(Module)) 329 330 // GetMissingDependencies returns the list of dependencies that were passed to AddDependencies or related methods, 331 // but do not exist. It can be used with Context.SetAllowMissingDependencies to allow the primary builder to 332 // handle missing dependencies on its own instead of having Blueprint treat them as an error. 333 GetMissingDependencies() []string 334} 335 336var _ BaseModuleContext = (*baseModuleContext)(nil) 337 338type baseModuleContext struct { 339 context *Context 340 config interface{} 341 module *moduleInfo 342 errs []error 343 visitingParent *moduleInfo 344 visitingDep depInfo 345 ninjaFileDeps []string 346} 347 348func (d *baseModuleContext) moduleInfo() *moduleInfo { 349 return d.module 350} 351 352func (d *baseModuleContext) Module() Module { 353 return d.module.logicModule 354} 355 356func (d *baseModuleContext) ModuleName() string { 357 return d.module.Name() 358} 359 360func (d *baseModuleContext) ModuleType() string { 361 return d.module.typeName 362} 363 364func (d *baseModuleContext) ContainsProperty(name string) bool { 365 _, ok := d.module.propertyPos[name] 366 return ok 367} 368 369func (d *baseModuleContext) ModuleDir() string { 370 return filepath.Dir(d.module.relBlueprintsFile) 371} 372 373func (d *baseModuleContext) BlueprintsFile() string { 374 return d.module.relBlueprintsFile 375} 376 377func (d *baseModuleContext) Config() interface{} { 378 return d.config 379} 380 381func (d *baseModuleContext) error(err error) { 382 if err != nil { 383 d.errs = append(d.errs, err) 384 } 385} 386 387func (d *baseModuleContext) Errorf(pos scanner.Position, 388 format string, args ...interface{}) { 389 390 d.error(&BlueprintError{ 391 Err: fmt.Errorf(format, args...), 392 Pos: pos, 393 }) 394} 395 396func (d *baseModuleContext) ModuleErrorf(format string, 397 args ...interface{}) { 398 399 d.error(&ModuleError{ 400 BlueprintError: BlueprintError{ 401 Err: fmt.Errorf(format, args...), 402 Pos: d.module.pos, 403 }, 404 module: d.module, 405 }) 406} 407 408func (d *baseModuleContext) PropertyErrorf(property, format string, 409 args ...interface{}) { 410 411 pos := d.module.propertyPos[property] 412 413 if !pos.IsValid() { 414 pos = d.module.pos 415 } 416 417 d.error(&PropertyError{ 418 ModuleError: ModuleError{ 419 BlueprintError: BlueprintError{ 420 Err: fmt.Errorf(format, args...), 421 Pos: pos, 422 }, 423 module: d.module, 424 }, 425 property: property, 426 }) 427} 428 429func (d *baseModuleContext) Failed() bool { 430 return len(d.errs) > 0 431} 432 433func (d *baseModuleContext) GlobWithDeps(pattern string, 434 excludes []string) ([]string, error) { 435 return d.context.glob(pattern, excludes) 436} 437 438func (d *baseModuleContext) Fs() pathtools.FileSystem { 439 return d.context.fs 440} 441 442func (d *baseModuleContext) Namespace() Namespace { 443 return d.context.nameInterface.GetNamespace(newNamespaceContext(d.module)) 444} 445 446var _ ModuleContext = (*moduleContext)(nil) 447 448type moduleContext struct { 449 baseModuleContext 450 scope *localScope 451 actionDefs localBuildActions 452 handledMissingDeps bool 453} 454 455func (m *baseModuleContext) OtherModuleName(logicModule Module) string { 456 module := m.context.moduleInfo[logicModule] 457 return module.Name() 458} 459 460func (m *baseModuleContext) OtherModuleDir(logicModule Module) string { 461 module := m.context.moduleInfo[logicModule] 462 return filepath.Dir(module.relBlueprintsFile) 463} 464 465func (m *baseModuleContext) OtherModuleSubDir(logicModule Module) string { 466 module := m.context.moduleInfo[logicModule] 467 return module.variantName 468} 469 470func (m *baseModuleContext) OtherModuleType(logicModule Module) string { 471 module := m.context.moduleInfo[logicModule] 472 return module.typeName 473} 474 475func (m *baseModuleContext) OtherModuleErrorf(logicModule Module, format string, 476 args ...interface{}) { 477 478 module := m.context.moduleInfo[logicModule] 479 m.errs = append(m.errs, &ModuleError{ 480 BlueprintError: BlueprintError{ 481 Err: fmt.Errorf(format, args...), 482 Pos: module.pos, 483 }, 484 module: module, 485 }) 486} 487 488func (m *baseModuleContext) OtherModuleDependencyTag(logicModule Module) DependencyTag { 489 // fast path for calling OtherModuleDependencyTag from inside VisitDirectDeps 490 if logicModule == m.visitingDep.module.logicModule { 491 return m.visitingDep.tag 492 } 493 494 for _, dep := range m.visitingParent.directDeps { 495 if dep.module.logicModule == logicModule { 496 return dep.tag 497 } 498 } 499 500 return nil 501} 502 503func (m *baseModuleContext) OtherModuleExists(name string) bool { 504 _, exists := m.context.nameInterface.ModuleFromName(name, m.module.namespace()) 505 return exists 506} 507 508func (m *baseModuleContext) OtherModuleDependencyVariantExists(variations []Variation, name string) bool { 509 possibleDeps := m.context.moduleGroupFromName(name, m.module.namespace()) 510 if possibleDeps == nil { 511 return false 512 } 513 found, _ := m.context.findVariant(m.module, possibleDeps, variations, false, false) 514 return found != nil 515} 516 517func (m *baseModuleContext) OtherModuleReverseDependencyVariantExists(name string) bool { 518 possibleDeps := m.context.moduleGroupFromName(name, m.module.namespace()) 519 if possibleDeps == nil { 520 return false 521 } 522 found, _ := m.context.findVariant(m.module, possibleDeps, nil, false, true) 523 return found != nil 524} 525 526func (m *baseModuleContext) GetDirectDep(name string) (Module, DependencyTag) { 527 for _, dep := range m.module.directDeps { 528 if dep.module.Name() == name { 529 return dep.module.logicModule, dep.tag 530 } 531 } 532 533 return nil, nil 534} 535 536func (m *baseModuleContext) GetDirectDepWithTag(name string, tag DependencyTag) Module { 537 var deps []depInfo 538 for _, dep := range m.module.directDeps { 539 if dep.module.Name() == name { 540 if dep.tag == tag { 541 return dep.module.logicModule 542 } 543 deps = append(deps, dep) 544 } 545 } 546 547 if len(deps) != 0 { 548 panic(fmt.Errorf("Unable to find dependency %q with requested tag %#v. Found: %#v", deps[0].module, tag, deps)) 549 } 550 551 return nil 552} 553 554func (m *baseModuleContext) VisitDirectDeps(visit func(Module)) { 555 defer func() { 556 if r := recover(); r != nil { 557 panic(newPanicErrorf(r, "VisitDirectDeps(%s, %s) for dependency %s", 558 m.module, funcName(visit), m.visitingDep.module)) 559 } 560 }() 561 562 m.visitingParent = m.module 563 564 for _, dep := range m.module.directDeps { 565 m.visitingDep = dep 566 visit(dep.module.logicModule) 567 } 568 569 m.visitingParent = nil 570 m.visitingDep = depInfo{} 571} 572 573func (m *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) { 574 defer func() { 575 if r := recover(); r != nil { 576 panic(newPanicErrorf(r, "VisitDirectDepsIf(%s, %s, %s) for dependency %s", 577 m.module, funcName(pred), funcName(visit), m.visitingDep.module)) 578 } 579 }() 580 581 m.visitingParent = m.module 582 583 for _, dep := range m.module.directDeps { 584 m.visitingDep = dep 585 if pred(dep.module.logicModule) { 586 visit(dep.module.logicModule) 587 } 588 } 589 590 m.visitingParent = nil 591 m.visitingDep = depInfo{} 592} 593 594func (m *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) { 595 defer func() { 596 if r := recover(); r != nil { 597 panic(newPanicErrorf(r, "VisitDepsDepthFirst(%s, %s) for dependency %s", 598 m.module, funcName(visit), m.visitingDep.module)) 599 } 600 }() 601 602 m.context.walkDeps(m.module, false, nil, func(dep depInfo, parent *moduleInfo) { 603 m.visitingParent = parent 604 m.visitingDep = dep 605 visit(dep.module.logicModule) 606 }) 607 608 m.visitingParent = nil 609 m.visitingDep = depInfo{} 610} 611 612func (m *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool, 613 visit func(Module)) { 614 615 defer func() { 616 if r := recover(); r != nil { 617 panic(newPanicErrorf(r, "VisitDepsDepthFirstIf(%s, %s, %s) for dependency %s", 618 m.module, funcName(pred), funcName(visit), m.visitingDep.module)) 619 } 620 }() 621 622 m.context.walkDeps(m.module, false, nil, func(dep depInfo, parent *moduleInfo) { 623 if pred(dep.module.logicModule) { 624 m.visitingParent = parent 625 m.visitingDep = dep 626 visit(dep.module.logicModule) 627 } 628 }) 629 630 m.visitingParent = nil 631 m.visitingDep = depInfo{} 632} 633 634func (m *baseModuleContext) WalkDeps(visit func(child, parent Module) bool) { 635 m.context.walkDeps(m.module, true, func(dep depInfo, parent *moduleInfo) bool { 636 m.visitingParent = parent 637 m.visitingDep = dep 638 return visit(dep.module.logicModule, parent.logicModule) 639 }, nil) 640 641 m.visitingParent = nil 642 m.visitingDep = depInfo{} 643} 644 645func (m *baseModuleContext) AddNinjaFileDeps(deps ...string) { 646 m.ninjaFileDeps = append(m.ninjaFileDeps, deps...) 647} 648 649func (m *baseModuleContext) ModuleFactories() map[string]ModuleFactory { 650 ret := make(map[string]ModuleFactory) 651 for k, v := range m.context.moduleFactories { 652 ret[k] = v 653 } 654 return ret 655} 656 657func (m *moduleContext) ModuleSubDir() string { 658 return m.module.variantName 659} 660 661func (m *moduleContext) Variable(pctx PackageContext, name, value string) { 662 m.scope.ReparentTo(pctx) 663 664 v, err := m.scope.AddLocalVariable(name, value) 665 if err != nil { 666 panic(err) 667 } 668 669 m.actionDefs.variables = append(m.actionDefs.variables, v) 670} 671 672func (m *moduleContext) Rule(pctx PackageContext, name string, 673 params RuleParams, argNames ...string) Rule { 674 675 m.scope.ReparentTo(pctx) 676 677 r, err := m.scope.AddLocalRule(name, ¶ms, argNames...) 678 if err != nil { 679 panic(err) 680 } 681 682 m.actionDefs.rules = append(m.actionDefs.rules, r) 683 684 return r 685} 686 687func (m *moduleContext) Build(pctx PackageContext, params BuildParams) { 688 m.scope.ReparentTo(pctx) 689 690 def, err := parseBuildParams(m.scope, ¶ms) 691 if err != nil { 692 panic(err) 693 } 694 695 m.actionDefs.buildDefs = append(m.actionDefs.buildDefs, def) 696} 697 698func (m *moduleContext) PrimaryModule() Module { 699 return m.module.group.modules[0].logicModule 700} 701 702func (m *moduleContext) FinalModule() Module { 703 return m.module.group.modules[len(m.module.group.modules)-1].logicModule 704} 705 706func (m *moduleContext) VisitAllModuleVariants(visit func(Module)) { 707 m.context.visitAllModuleVariants(m.module, visit) 708} 709 710func (m *moduleContext) GetMissingDependencies() []string { 711 m.handledMissingDeps = true 712 return m.module.missingDeps 713} 714 715// 716// MutatorContext 717// 718 719type mutatorContext struct { 720 baseModuleContext 721 name string 722 reverseDeps []reverseDep 723 rename []rename 724 replace []replace 725 newVariations []*moduleInfo // new variants of existing modules 726 newModules []*moduleInfo // brand new modules 727 defaultVariation *string 728} 729 730type BaseMutatorContext interface { 731 BaseModuleContext 732 733 // Rename all variants of a module. The new name is not visible to calls to ModuleName, 734 // AddDependency or OtherModuleName until after this mutator pass is complete. 735 Rename(name string) 736 737 // MutatorName returns the name that this mutator was registered with. 738 MutatorName() string 739} 740 741type EarlyMutatorContext interface { 742 BaseMutatorContext 743 744 // CreateVariations splits a module into mulitple variants, one for each name in the variationNames 745 // parameter. It returns a list of new modules in the same order as the variationNames 746 // list. 747 // 748 // If any of the dependencies of the module being operated on were already split 749 // by calling CreateVariations with the same name, the dependency will automatically 750 // be updated to point the matching variant. 751 // 752 // If a module is split, and then a module depending on the first module is not split 753 // when the Mutator is later called on it, the dependency of the depending module will 754 // automatically be updated to point to the first variant. 755 CreateVariations(...string) []Module 756 757 // CreateLocationVariations splits a module into mulitple variants, one for each name in the variantNames 758 // parameter. It returns a list of new modules in the same order as the variantNames 759 // list. 760 // 761 // Local variations do not affect automatic dependency resolution - dependencies added 762 // to the split module via deps or DynamicDependerModule must exactly match a variant 763 // that contains all the non-local variations. 764 CreateLocalVariations(...string) []Module 765} 766 767type TopDownMutatorContext interface { 768 BaseMutatorContext 769 770 // CreateModule creates a new module by calling the factory method for the specified moduleType, and applies 771 // the specified property structs to it as if the properties were set in a blueprint file. 772 CreateModule(ModuleFactory, ...interface{}) Module 773} 774 775type BottomUpMutatorContext interface { 776 BaseMutatorContext 777 778 // AddDependency adds a dependency to the given module. 779 // Does not affect the ordering of the current mutator pass, but will be ordered 780 // correctly for all future mutator passes. 781 AddDependency(module Module, tag DependencyTag, name ...string) 782 783 // AddReverseDependency adds a dependency from the destination to the given module. 784 // Does not affect the ordering of the current mutator pass, but will be ordered 785 // correctly for all future mutator passes. All reverse dependencies for a destination module are 786 // collected until the end of the mutator pass, sorted by name, and then appended to the destination 787 // module's dependency list. 788 AddReverseDependency(module Module, tag DependencyTag, name string) 789 790 // CreateVariations splits a module into mulitple variants, one for each name in the variationNames 791 // parameter. It returns a list of new modules in the same order as the variationNames 792 // list. 793 // 794 // If any of the dependencies of the module being operated on were already split 795 // by calling CreateVariations with the same name, the dependency will automatically 796 // be updated to point the matching variant. 797 // 798 // If a module is split, and then a module depending on the first module is not split 799 // when the Mutator is later called on it, the dependency of the depending module will 800 // automatically be updated to point to the first variant. 801 CreateVariations(...string) []Module 802 803 // CreateLocationVariations splits a module into mulitple variants, one for each name in the variantNames 804 // parameter. It returns a list of new modules in the same order as the variantNames 805 // list. 806 // 807 // Local variations do not affect automatic dependency resolution - dependencies added 808 // to the split module via deps or DynamicDependerModule must exactly match a variant 809 // that contains all the non-local variations. 810 CreateLocalVariations(...string) []Module 811 812 // SetDependencyVariation sets all dangling dependencies on the current module to point to the variation 813 // with given name. This function ignores the default variation set by SetDefaultDependencyVariation. 814 SetDependencyVariation(string) 815 816 // SetDefaultDependencyVariation sets the default variation when a dangling reference is detected 817 // during the subsequent calls on Create*Variations* functions. To reset, set it to nil. 818 SetDefaultDependencyVariation(*string) 819 820 // AddVariationDependencies adds deps as dependencies of the current module, but uses the variations 821 // argument to select which variant of the dependency to use. A variant of the dependency must 822 // exist that matches the all of the non-local variations of the current module, plus the variations 823 // argument. 824 AddVariationDependencies([]Variation, DependencyTag, ...string) 825 826 // AddFarVariationDependencies adds deps as dependencies of the current module, but uses the 827 // variations argument to select which variant of the dependency to use. A variant of the 828 // dependency must exist that matches the variations argument, but may also have other variations. 829 // For any unspecified variation the first variant will be used. 830 // 831 // Unlike AddVariationDependencies, the variations of the current module are ignored - the 832 // dependency only needs to match the supplied variations. 833 AddFarVariationDependencies([]Variation, DependencyTag, ...string) 834 835 // AddInterVariantDependency adds a dependency between two variants of the same module. Variants are always 836 // ordered in the same orderas they were listed in CreateVariations, and AddInterVariantDependency does not change 837 // that ordering, but it associates a DependencyTag with the dependency and makes it visible to VisitDirectDeps, 838 // WalkDeps, etc. 839 AddInterVariantDependency(tag DependencyTag, from, to Module) 840 841 // ReplaceDependencies replaces all dependencies on the identical variant of the module with the 842 // specified name with the current variant of this module. Replacements don't take effect until 843 // after the mutator pass is finished. 844 ReplaceDependencies(string) 845 846 // ReplaceDependencies replaces all dependencies on the identical variant of the module with the 847 // specified name with the current variant of this module as long as the supplied predicate returns 848 // true. 849 // 850 // Replacements don't take effect until after the mutator pass is finished. 851 ReplaceDependenciesIf(string, ReplaceDependencyPredicate) 852 853 // AliasVariation takes a variationName that was passed to CreateVariations for this module, and creates an 854 // alias from the current variant to the new variant. The alias will be valid until the next time a mutator 855 // calls CreateVariations or CreateLocalVariations on this module without also calling AliasVariation. The 856 // alias can be used to add dependencies on the newly created variant using the variant map from before 857 // CreateVariations was run. 858 AliasVariation(variationName string) 859} 860 861// A Mutator function is called for each Module, and can use 862// MutatorContext.CreateVariations to split a Module into multiple Modules, 863// modifying properties on the new modules to differentiate them. It is called 864// after parsing all Blueprint files, but before generating any build rules, 865// and is always called on dependencies before being called on the depending module. 866// 867// The Mutator function should only modify members of properties structs, and not 868// members of the module struct itself, to ensure the modified values are copied 869// if a second Mutator chooses to split the module a second time. 870type TopDownMutator func(mctx TopDownMutatorContext) 871type BottomUpMutator func(mctx BottomUpMutatorContext) 872type EarlyMutator func(mctx EarlyMutatorContext) 873 874// DependencyTag is an interface to an arbitrary object that embeds BaseDependencyTag. It can be 875// used to transfer information on a dependency between the mutator that called AddDependency 876// and the GenerateBuildActions method. Variants created by CreateVariations have a copy of the 877// interface (pointing to the same concrete object) from their original module. 878type DependencyTag interface { 879 dependencyTag(DependencyTag) 880} 881 882type BaseDependencyTag struct { 883} 884 885func (BaseDependencyTag) dependencyTag(DependencyTag) { 886} 887 888var _ DependencyTag = BaseDependencyTag{} 889 890func (mctx *mutatorContext) MutatorName() string { 891 return mctx.name 892} 893 894func (mctx *mutatorContext) CreateVariations(variationNames ...string) []Module { 895 return mctx.createVariations(variationNames, false) 896} 897 898func (mctx *mutatorContext) CreateLocalVariations(variationNames ...string) []Module { 899 return mctx.createVariations(variationNames, true) 900} 901 902func (mctx *mutatorContext) createVariations(variationNames []string, local bool) []Module { 903 ret := []Module{} 904 modules, errs := mctx.context.createVariations(mctx.module, mctx.name, mctx.defaultVariation, variationNames) 905 if len(errs) > 0 { 906 mctx.errs = append(mctx.errs, errs...) 907 } 908 909 for i, module := range modules { 910 ret = append(ret, module.logicModule) 911 if !local { 912 if module.dependencyVariant == nil { 913 module.dependencyVariant = make(variationMap) 914 } 915 module.dependencyVariant[mctx.name] = variationNames[i] 916 } 917 } 918 919 if mctx.newVariations != nil { 920 panic("module already has variations from this mutator") 921 } 922 mctx.newVariations = modules 923 924 if len(ret) != len(variationNames) { 925 panic("oops!") 926 } 927 928 return ret 929} 930 931func (mctx *mutatorContext) AliasVariation(variationName string) { 932 if mctx.module.aliasTarget != nil { 933 panic(fmt.Errorf("AliasVariation already called")) 934 } 935 936 for _, variant := range mctx.newVariations { 937 if variant.variant[mctx.name] == variationName { 938 mctx.module.aliasTarget = variant 939 return 940 } 941 } 942 943 var foundVariations []string 944 for _, variant := range mctx.newVariations { 945 foundVariations = append(foundVariations, variant.variant[mctx.name]) 946 } 947 panic(fmt.Errorf("no %q variation in module variations %q", variationName, foundVariations)) 948} 949 950func (mctx *mutatorContext) SetDependencyVariation(variationName string) { 951 mctx.context.convertDepsToVariation(mctx.module, mctx.name, variationName, nil) 952} 953 954func (mctx *mutatorContext) SetDefaultDependencyVariation(variationName *string) { 955 mctx.defaultVariation = variationName 956} 957 958func (mctx *mutatorContext) Module() Module { 959 return mctx.module.logicModule 960} 961 962func (mctx *mutatorContext) AddDependency(module Module, tag DependencyTag, deps ...string) { 963 for _, dep := range deps { 964 modInfo := mctx.context.moduleInfo[module] 965 errs := mctx.context.addDependency(modInfo, tag, dep) 966 if len(errs) > 0 { 967 mctx.errs = append(mctx.errs, errs...) 968 } 969 } 970} 971 972func (mctx *mutatorContext) AddReverseDependency(module Module, tag DependencyTag, destName string) { 973 if _, ok := tag.(BaseDependencyTag); ok { 974 panic("BaseDependencyTag is not allowed to be used directly!") 975 } 976 977 destModule, errs := mctx.context.findReverseDependency(mctx.context.moduleInfo[module], destName) 978 if len(errs) > 0 { 979 mctx.errs = append(mctx.errs, errs...) 980 return 981 } 982 983 mctx.reverseDeps = append(mctx.reverseDeps, reverseDep{ 984 destModule, 985 depInfo{mctx.context.moduleInfo[module], tag}, 986 }) 987} 988 989func (mctx *mutatorContext) AddVariationDependencies(variations []Variation, tag DependencyTag, 990 deps ...string) { 991 992 for _, dep := range deps { 993 errs := mctx.context.addVariationDependency(mctx.module, variations, tag, dep, false) 994 if len(errs) > 0 { 995 mctx.errs = append(mctx.errs, errs...) 996 } 997 } 998} 999 1000func (mctx *mutatorContext) AddFarVariationDependencies(variations []Variation, tag DependencyTag, 1001 deps ...string) { 1002 1003 for _, dep := range deps { 1004 errs := mctx.context.addVariationDependency(mctx.module, variations, tag, dep, true) 1005 if len(errs) > 0 { 1006 mctx.errs = append(mctx.errs, errs...) 1007 } 1008 } 1009} 1010 1011func (mctx *mutatorContext) AddInterVariantDependency(tag DependencyTag, from, to Module) { 1012 mctx.context.addInterVariantDependency(mctx.module, tag, from, to) 1013} 1014 1015func (mctx *mutatorContext) ReplaceDependencies(name string) { 1016 mctx.ReplaceDependenciesIf(name, nil) 1017} 1018 1019type ReplaceDependencyPredicate func(from Module, tag DependencyTag, to Module) bool 1020 1021func (mctx *mutatorContext) ReplaceDependenciesIf(name string, predicate ReplaceDependencyPredicate) { 1022 target := mctx.context.moduleMatchingVariant(mctx.module, name) 1023 1024 if target == nil { 1025 panic(fmt.Errorf("ReplaceDependencies could not find identical variant %q for module %q", 1026 mctx.module.variantName, name)) 1027 } 1028 1029 mctx.replace = append(mctx.replace, replace{target, mctx.module, predicate}) 1030} 1031 1032func (mctx *mutatorContext) Rename(name string) { 1033 mctx.rename = append(mctx.rename, rename{mctx.module.group, name}) 1034} 1035 1036func (mctx *mutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) Module { 1037 module := newModule(factory) 1038 1039 module.relBlueprintsFile = mctx.module.relBlueprintsFile 1040 module.pos = mctx.module.pos 1041 module.propertyPos = mctx.module.propertyPos 1042 module.createdBy = mctx.module 1043 1044 for _, p := range props { 1045 err := proptools.AppendMatchingProperties(module.properties, p, nil) 1046 if err != nil { 1047 panic(err) 1048 } 1049 } 1050 1051 mctx.newModules = append(mctx.newModules, module) 1052 1053 return module.logicModule 1054} 1055 1056// SimpleName is an embeddable object to implement the ModuleContext.Name method using a property 1057// called "name". Modules that embed it must also add SimpleName.Properties to their property 1058// structure list. 1059type SimpleName struct { 1060 Properties struct { 1061 Name string 1062 } 1063} 1064 1065func (s *SimpleName) Name() string { 1066 return s.Properties.Name 1067} 1068 1069// Load Hooks 1070 1071type LoadHookContext interface { 1072 EarlyModuleContext 1073 1074 // CreateModule creates a new module by calling the factory method for the specified moduleType, and applies 1075 // the specified property structs to it as if the properties were set in a blueprint file. 1076 CreateModule(ModuleFactory, ...interface{}) Module 1077 1078 // RegisterScopedModuleType creates a new module type that is scoped to the current Blueprints 1079 // file. 1080 RegisterScopedModuleType(name string, factory ModuleFactory) 1081} 1082 1083func (l *loadHookContext) CreateModule(factory ModuleFactory, props ...interface{}) Module { 1084 module := newModule(factory) 1085 1086 module.relBlueprintsFile = l.module.relBlueprintsFile 1087 module.pos = l.module.pos 1088 module.propertyPos = l.module.propertyPos 1089 module.createdBy = l.module 1090 1091 for _, p := range props { 1092 err := proptools.AppendMatchingProperties(module.properties, p, nil) 1093 if err != nil { 1094 panic(err) 1095 } 1096 } 1097 1098 l.newModules = append(l.newModules, module) 1099 1100 return module.logicModule 1101} 1102 1103func (l *loadHookContext) RegisterScopedModuleType(name string, factory ModuleFactory) { 1104 if _, exists := l.context.moduleFactories[name]; exists { 1105 panic(fmt.Errorf("A global module type named %q already exists", name)) 1106 } 1107 1108 if _, exists := (*l.scopedModuleFactories)[name]; exists { 1109 panic(fmt.Errorf("A module type named %q already exists in this scope", name)) 1110 } 1111 1112 if *l.scopedModuleFactories == nil { 1113 (*l.scopedModuleFactories) = make(map[string]ModuleFactory) 1114 } 1115 1116 (*l.scopedModuleFactories)[name] = factory 1117} 1118 1119type loadHookContext struct { 1120 baseModuleContext 1121 newModules []*moduleInfo 1122 scopedModuleFactories *map[string]ModuleFactory 1123} 1124 1125type LoadHook func(ctx LoadHookContext) 1126 1127// Load hooks need to be added by module factories, which don't have any parameter to get to the 1128// Context, and only produce a Module interface with no base implementation, so the load hooks 1129// must be stored in a global map. The key is a pointer allocated by the module factory, so there 1130// is no chance of collisions even if tests are running in parallel with multiple contexts. The 1131// contents should be short-lived, they are added during a module factory and removed immediately 1132// after the module factory returns. 1133var pendingHooks sync.Map 1134 1135func AddLoadHook(module Module, hook LoadHook) { 1136 // Only one goroutine can be processing a given module, so no additional locking is required 1137 // for the slice stored in the sync.Map. 1138 v, exists := pendingHooks.Load(module) 1139 if !exists { 1140 v, _ = pendingHooks.LoadOrStore(module, new([]LoadHook)) 1141 } 1142 hooks := v.(*[]LoadHook) 1143 *hooks = append(*hooks, hook) 1144} 1145 1146func runAndRemoveLoadHooks(ctx *Context, config interface{}, module *moduleInfo, 1147 scopedModuleFactories *map[string]ModuleFactory) (newModules []*moduleInfo, errs []error) { 1148 1149 if v, exists := pendingHooks.Load(module.logicModule); exists { 1150 hooks := v.(*[]LoadHook) 1151 mctx := &loadHookContext{ 1152 baseModuleContext: baseModuleContext{ 1153 context: ctx, 1154 config: config, 1155 module: module, 1156 }, 1157 scopedModuleFactories: scopedModuleFactories, 1158 } 1159 1160 for _, hook := range *hooks { 1161 hook(mctx) 1162 newModules = append(newModules, mctx.newModules...) 1163 errs = append(errs, mctx.errs...) 1164 } 1165 pendingHooks.Delete(module.logicModule) 1166 1167 return newModules, errs 1168 } 1169 1170 return nil, nil 1171} 1172 1173// Check the syntax of a generated blueprint file. 1174// 1175// This is intended to perform a quick sanity check for generated blueprint 1176// code to ensure that it is syntactically correct, where syntactically correct 1177// means: 1178// * No variable definitions. 1179// * Valid module types. 1180// * Valid property names. 1181// * Valid values for the property type. 1182// 1183// It does not perform any semantic checking of properties, existence of referenced 1184// files, or dependencies. 1185// 1186// At a low level it: 1187// * Parses the contents. 1188// * Invokes relevant factory to create Module instances. 1189// * Unpacks the properties into the Module. 1190// * Does not invoke load hooks or any mutators. 1191// 1192// The filename is only used for reporting errors. 1193func CheckBlueprintSyntax(moduleFactories map[string]ModuleFactory, filename string, contents string) []error { 1194 scope := parser.NewScope(nil) 1195 file, errs := parser.Parse(filename, strings.NewReader(contents), scope) 1196 if len(errs) != 0 { 1197 return errs 1198 } 1199 1200 for _, def := range file.Defs { 1201 switch def := def.(type) { 1202 case *parser.Module: 1203 _, moduleErrs := processModuleDef(def, filename, moduleFactories, nil, false) 1204 errs = append(errs, moduleErrs...) 1205 1206 default: 1207 panic(fmt.Errorf("unknown definition type: %T", def)) 1208 } 1209 } 1210 1211 return errs 1212} 1213