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 20 "github.com/google/blueprint/pathtools" 21) 22 23type Singleton interface { 24 GenerateBuildActions(SingletonContext) 25} 26 27type SingletonContext interface { 28 // Config returns the config object that was passed to Context.PrepareBuildActions. 29 Config() interface{} 30 31 // Name returns the name of the current singleton passed to Context.RegisterSingletonType 32 Name() string 33 34 // ModuleName returns the name of the given Module. See BaseModuleContext.ModuleName for more information. 35 ModuleName(module Module) string 36 37 // ModuleDir returns the directory of the given Module. See BaseModuleContext.ModuleDir for more information. 38 ModuleDir(module Module) string 39 40 // ModuleSubDir returns the unique subdirectory name of the given Module. See ModuleContext.ModuleSubDir for 41 // more information. 42 ModuleSubDir(module Module) string 43 44 // ModuleType returns the type of the given Module. See BaseModuleContext.ModuleType for more information. 45 ModuleType(module Module) string 46 47 // BlueprintFile returns the path of the Blueprint file that defined the given module. 48 BlueprintFile(module Module) string 49 50 // ModuleProvider returns the value, if any, for the provider for a module. If the value for the 51 // provider was not set it returns the zero value of the type of the provider, which means the 52 // return value can always be type-asserted to the type of the provider. The return value should 53 // always be considered read-only. It panics if called before the appropriate mutator or 54 // GenerateBuildActions pass for the provider on the module. 55 ModuleProvider(module Module, provider AnyProviderKey) (any, bool) 56 57 // ModuleErrorf reports an error at the line number of the module type in the module definition. 58 ModuleErrorf(module Module, format string, args ...interface{}) 59 60 // Errorf reports an error at the specified position of the module definition file. 61 Errorf(format string, args ...interface{}) 62 63 // OtherModulePropertyErrorf reports an error on the line number of the given property of the given module 64 OtherModulePropertyErrorf(module Module, property string, format string, args ...interface{}) 65 66 // Failed returns true if any errors have been reported. In most cases the singleton can continue with generating 67 // build rules after an error, allowing it to report additional errors in a single run, but in cases where the error 68 // has prevented the singleton from creating necessary data it can return early when Failed returns true. 69 Failed() bool 70 71 // Variable creates a new ninja variable scoped to the singleton. It can be referenced by calls to Rule and Build 72 // in the same singleton. 73 Variable(pctx PackageContext, name, value string) 74 75 // Rule creates a new ninja rule scoped to the singleton. It can be referenced by calls to Build in the same 76 // singleton. 77 Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) Rule 78 79 // Build creates a new ninja build statement. 80 Build(pctx PackageContext, params BuildParams) 81 82 // RequireNinjaVersion sets the generated ninja manifest to require at least the specified version of ninja. 83 RequireNinjaVersion(major, minor, micro int) 84 85 // SetOutDir sets the value of the top-level "builddir" Ninja variable 86 // that controls where Ninja stores its build log files. This value can be 87 // set at most one time for a single build, later calls are ignored. 88 SetOutDir(pctx PackageContext, value string) 89 90 // AddSubninja adds a ninja file to include with subninja. This should likely 91 // only ever be used inside bootstrap to handle glob rules. 92 AddSubninja(file string) 93 94 // Eval takes a string with embedded ninja variables, and returns a string 95 // with all of the variables recursively expanded. Any variables references 96 // are expanded in the scope of the PackageContext. 97 Eval(pctx PackageContext, ninjaStr string) (string, error) 98 99 // VisitAllModules calls visit for each defined variant of each module in an unspecified order. 100 VisitAllModules(visit func(Module)) 101 102 // VisitAllModuleProxies calls visit for each defined variant of each module in an unspecified order. 103 VisitAllModuleProxies(visit func(proxy ModuleProxy)) 104 105 // VisitAllModules calls pred for each defined variant of each module in an unspecified order, and if pred returns 106 // true calls visit. 107 VisitAllModulesIf(pred func(Module) bool, visit func(Module)) 108 109 // VisitDirectDeps calls visit for each direct dependency of the Module. If there are 110 // multiple direct dependencies on the same module visit will be called multiple times on 111 // that module and OtherModuleDependencyTag will return a different tag for each. 112 // 113 // The Module passed to the visit function should not be retained outside of the visit 114 // function, it may be invalidated by future mutators. 115 VisitDirectDeps(module Module, visit func(Module)) 116 117 // VisitDirectDepsIf calls pred for each direct dependency of the Module, and if pred 118 // returns true calls visit. If there are multiple direct dependencies on the same module 119 // pred and visit will be called multiple times on that module and OtherModuleDependencyTag 120 // will return a different tag for each. 121 // 122 // The Module passed to the visit function should not be retained outside of the visit 123 // function, it may be invalidated by future mutators. 124 VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module)) 125 126 // VisitDepsDepthFirst calls visit for each transitive dependency, traversing the dependency tree in depth first 127 // order. visit will only be called once for any given module, even if there are multiple paths through the 128 // dependency tree to the module or multiple direct dependencies with different tags. 129 VisitDepsDepthFirst(module Module, visit func(Module)) 130 131 // VisitDepsDepthFirst calls pred for each transitive dependency, and if pred returns true calls visit, traversing 132 // the dependency tree in depth first order. visit will only be called once for any given module, even if there are 133 // multiple paths through the dependency tree to the module or multiple direct dependencies with different tags. 134 VisitDepsDepthFirstIf(module Module, pred func(Module) bool, 135 visit func(Module)) 136 137 // VisitAllModuleVariants calls visit for each variant of the given module. 138 VisitAllModuleVariants(module Module, visit func(Module)) 139 140 // VisitAllModuleVariantProxies calls visit for each variant of the given module. 141 VisitAllModuleVariantProxies(module Module, visit func(proxy ModuleProxy)) 142 143 // PrimaryModule returns the first variant of the given module. This can be used to perform 144 // singleton actions that are only done once for all variants of a module. 145 PrimaryModule(module Module) Module 146 147 // PrimaryModuleProxy returns the proxy of the first variant of the given module. 148 // This can be used to perform singleton actions that are only done once for 149 // all variants of a module. 150 PrimaryModuleProxy(module ModuleProxy) ModuleProxy 151 152 // IsFinalModule returns if the given module is the last variant. This can be used to perform 153 // singleton actions that are only done once for all variants of a module. 154 IsFinalModule(module Module) bool 155 156 // AddNinjaFileDeps adds dependencies on the specified files to the rule that creates the ninja manifest. The 157 // primary builder will be rerun whenever the specified files are modified. 158 AddNinjaFileDeps(deps ...string) 159 160 // GlobWithDeps returns a list of files and directories that match the 161 // specified pattern but do not match any of the patterns in excludes. 162 // Any directories will have a '/' suffix. It also adds efficient 163 // dependencies to rerun the primary builder whenever a file matching 164 // the pattern as added or removed, without rerunning if a file that 165 // does not match the pattern is added to a searched directory. 166 GlobWithDeps(pattern string, excludes []string) ([]string, error) 167 168 // Fs returns a pathtools.Filesystem that can be used to interact with files. Using the Filesystem interface allows 169 // the singleton to be used in build system tests that run against a mock filesystem. 170 Fs() pathtools.FileSystem 171 172 // ModuleVariantsFromName returns the list of module variants named `name` in the same namespace as `referer`. 173 // Allows generating build actions for `referer` based on the metadata for `name` deferred until the singleton context. 174 ModuleVariantsFromName(referer ModuleProxy, name string) []ModuleProxy 175 176 // HasMutatorFinished returns true if the given mutator has finished running. 177 // It will panic if given an invalid mutator name. 178 HasMutatorFinished(mutatorName string) bool 179} 180 181var _ SingletonContext = (*singletonContext)(nil) 182 183type singletonContext struct { 184 name string 185 context *Context 186 config interface{} 187 scope *localScope 188 globals *liveTracker 189 190 ninjaFileDeps []string 191 errs []error 192 193 actionDefs localBuildActions 194} 195 196func (s *singletonContext) Config() interface{} { 197 return s.config 198} 199 200func (s *singletonContext) Name() string { 201 return s.name 202} 203 204func (s *singletonContext) ModuleName(logicModule Module) string { 205 return s.context.ModuleName(getWrappedModule(logicModule)) 206} 207 208func (s *singletonContext) ModuleDir(logicModule Module) string { 209 return s.context.ModuleDir(getWrappedModule(logicModule)) 210} 211 212func (s *singletonContext) ModuleSubDir(logicModule Module) string { 213 return s.context.ModuleSubDir(getWrappedModule(logicModule)) 214} 215 216func (s *singletonContext) ModuleType(logicModule Module) string { 217 return s.context.ModuleType(getWrappedModule(logicModule)) 218} 219 220func (s *singletonContext) ModuleProvider(logicModule Module, provider AnyProviderKey) (any, bool) { 221 return s.context.ModuleProvider(getWrappedModule(logicModule), provider) 222} 223 224func (s *singletonContext) BlueprintFile(logicModule Module) string { 225 return s.context.BlueprintFile(getWrappedModule(logicModule)) 226} 227 228func (s *singletonContext) error(err error) { 229 if err != nil { 230 s.errs = append(s.errs, err) 231 } 232} 233 234func (s *singletonContext) ModuleErrorf(logicModule Module, format string, 235 args ...interface{}) { 236 237 s.error(s.context.ModuleErrorf(logicModule, format, args...)) 238} 239 240func (s *singletonContext) Errorf(format string, args ...interface{}) { 241 // TODO: Make this not result in the error being printed as "internal error" 242 s.error(fmt.Errorf(format, args...)) 243} 244 245func (s *singletonContext) OtherModulePropertyErrorf(logicModule Module, property string, format string, 246 args ...interface{}) { 247 248 s.error(s.context.PropertyErrorf(logicModule, property, format, args...)) 249} 250 251func (s *singletonContext) Failed() bool { 252 return len(s.errs) > 0 253} 254 255func (s *singletonContext) Variable(pctx PackageContext, name, value string) { 256 s.scope.ReparentTo(pctx) 257 258 v, err := s.scope.AddLocalVariable(name, value) 259 if err != nil { 260 panic(err) 261 } 262 263 s.actionDefs.variables = append(s.actionDefs.variables, v) 264} 265 266func (s *singletonContext) Rule(pctx PackageContext, name string, 267 params RuleParams, argNames ...string) Rule { 268 269 s.scope.ReparentTo(pctx) 270 271 r, err := s.scope.AddLocalRule(name, ¶ms, argNames...) 272 if err != nil { 273 panic(err) 274 } 275 276 s.actionDefs.rules = append(s.actionDefs.rules, r) 277 278 return r 279} 280 281func (s *singletonContext) Build(pctx PackageContext, params BuildParams) { 282 s.scope.ReparentTo(pctx) 283 284 def, err := parseBuildParams(s.scope, ¶ms, map[string]string{ 285 "module_name": s.name, 286 "module_type": "singleton", 287 }) 288 if err != nil { 289 panic(err) 290 } 291 292 s.actionDefs.buildDefs = append(s.actionDefs.buildDefs, def) 293} 294 295func (s *singletonContext) Eval(pctx PackageContext, str string) (string, error) { 296 s.scope.ReparentTo(pctx) 297 298 ninjaStr, err := parseNinjaString(s.scope, str) 299 if err != nil { 300 return "", err 301 } 302 303 err = s.globals.addNinjaStringDeps(ninjaStr) 304 if err != nil { 305 return "", err 306 } 307 308 return s.globals.Eval(ninjaStr) 309} 310 311func (s *singletonContext) RequireNinjaVersion(major, minor, micro int) { 312 s.context.requireNinjaVersion(major, minor, micro) 313} 314 315func (s *singletonContext) SetOutDir(pctx PackageContext, value string) { 316 s.scope.ReparentTo(pctx) 317 318 ninjaValue, err := parseNinjaString(s.scope, value) 319 if err != nil { 320 panic(err) 321 } 322 323 s.context.setOutDir(ninjaValue) 324} 325 326func (s *singletonContext) AddSubninja(file string) { 327 s.context.subninjas = append(s.context.subninjas, file) 328} 329 330func (s *singletonContext) VisitAllModules(visit func(Module)) { 331 var visitingModule Module 332 defer func() { 333 if r := recover(); r != nil { 334 panic(newPanicErrorf(r, "VisitAllModules(%s) for module %s", 335 funcName(visit), s.context.moduleInfo[visitingModule])) 336 } 337 }() 338 339 s.context.VisitAllModules(func(m Module) { 340 visitingModule = m 341 visit(m) 342 }) 343} 344 345func (s *singletonContext) VisitAllModuleProxies(visit func(proxy ModuleProxy)) { 346 s.VisitAllModules(visitProxyAdaptor(visit)) 347} 348 349func (s *singletonContext) VisitAllModulesIf(pred func(Module) bool, 350 visit func(Module)) { 351 352 s.context.VisitAllModulesIf(pred, visit) 353} 354 355func (s *singletonContext) VisitDirectDeps(module Module, visit func(Module)) { 356 s.context.VisitDirectDeps(module, visit) 357} 358 359func (s *singletonContext) VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module)) { 360 s.context.VisitDirectDepsIf(module, pred, visit) 361} 362 363func (s *singletonContext) VisitDepsDepthFirst(module Module, 364 visit func(Module)) { 365 366 s.context.VisitDepsDepthFirst(module, visit) 367} 368 369func (s *singletonContext) VisitDepsDepthFirstIf(module Module, 370 pred func(Module) bool, visit func(Module)) { 371 372 s.context.VisitDepsDepthFirstIf(module, pred, visit) 373} 374 375func (s *singletonContext) PrimaryModule(module Module) Module { 376 return s.context.PrimaryModule(module) 377} 378 379func (s *singletonContext) PrimaryModuleProxy(module ModuleProxy) ModuleProxy { 380 return ModuleProxy{s.context.PrimaryModule(module.module)} 381} 382 383func (s *singletonContext) IsFinalModule(module Module) bool { 384 return s.context.IsFinalModule(getWrappedModule(module)) 385} 386 387func (s *singletonContext) VisitAllModuleVariants(module Module, visit func(Module)) { 388 s.context.VisitAllModuleVariants(module, visit) 389} 390 391func (s *singletonContext) VisitAllModuleVariantProxies(module Module, visit func(proxy ModuleProxy)) { 392 s.context.VisitAllModuleVariants(getWrappedModule(module), visitProxyAdaptor(visit)) 393} 394 395func (s *singletonContext) AddNinjaFileDeps(deps ...string) { 396 s.ninjaFileDeps = append(s.ninjaFileDeps, deps...) 397} 398 399func (s *singletonContext) GlobWithDeps(pattern string, 400 excludes []string) ([]string, error) { 401 return s.context.glob(pattern, excludes) 402} 403 404func (s *singletonContext) Fs() pathtools.FileSystem { 405 return s.context.fs 406} 407 408func (s *singletonContext) ModuleVariantsFromName(referer ModuleProxy, name string) []ModuleProxy { 409 c := s.context 410 411 refererInfo := c.moduleInfo[referer.module] 412 if refererInfo == nil { 413 s.ModuleErrorf(referer, "could not find module %q", referer.Name()) 414 return nil 415 } 416 417 moduleGroup, exists := c.nameInterface.ModuleFromName(name, refererInfo.namespace()) 418 if !exists { 419 return nil 420 } 421 result := make([]ModuleProxy, 0, len(moduleGroup.modules)) 422 for _, moduleInfo := range moduleGroup.modules { 423 if moduleInfo.logicModule != nil { 424 result = append(result, ModuleProxy{moduleInfo.logicModule}) 425 } 426 } 427 return result 428} 429 430func (s *singletonContext) HasMutatorFinished(mutatorName string) bool { 431 return s.context.HasMutatorFinished(mutatorName) 432} 433 434func visitProxyAdaptor(visit func(proxy ModuleProxy)) func(module Module) { 435 return func(module Module) { 436 visit(ModuleProxy{ 437 module: module, 438 }) 439 } 440} 441