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 ProviderKey) interface{} 56 57 // ModuleHasProvider returns true if the provider for the given module has been set. 58 ModuleHasProvider(m Module, provider ProviderKey) bool 59 60 // ModuleErrorf reports an error at the line number of the module type in the module definition. 61 ModuleErrorf(module Module, format string, args ...interface{}) 62 63 // Errorf reports an error at the specified position of the module definition file. 64 Errorf(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 // SetNinjaBuildDir 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 SetNinjaBuildDir(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 // VisitAllModules calls pred for each defined variant of each module in an unspecified order, and if pred returns 103 // true calls visit. 104 VisitAllModulesIf(pred func(Module) bool, visit func(Module)) 105 106 // VisitDirectDeps calls visit for each direct dependency of the Module. If there are 107 // multiple direct dependencies on the same module visit will be called multiple times on 108 // that module and OtherModuleDependencyTag will return a different tag for each. 109 // 110 // The Module passed to the visit function should not be retained outside of the visit 111 // function, it may be invalidated by future mutators. 112 VisitDirectDeps(module Module, visit func(Module)) 113 114 // VisitDirectDepsIf calls pred for each direct dependency of the Module, and if pred 115 // returns true calls visit. If there are multiple direct dependencies on the same module 116 // pred and visit will be called multiple times on that module and OtherModuleDependencyTag 117 // will return a different tag for each. 118 // 119 // The Module passed to the visit function should not be retained outside of the visit 120 // function, it may be invalidated by future mutators. 121 VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module)) 122 123 // VisitDepsDepthFirst calls visit for each transitive dependency, traversing the dependency tree in depth first 124 // order. visit will only be called once for any given module, even if there are multiple paths through the 125 // dependency tree to the module or multiple direct dependencies with different tags. 126 VisitDepsDepthFirst(module Module, visit func(Module)) 127 128 // VisitDepsDepthFirst calls pred for each transitive dependency, and if pred returns true calls visit, traversing 129 // the dependency tree in depth first order. visit will only be called once for any given module, even if there are 130 // multiple paths through the dependency tree to the module or multiple direct dependencies with different tags. 131 VisitDepsDepthFirstIf(module Module, pred func(Module) bool, 132 visit func(Module)) 133 134 // VisitAllModuleVariants calls visit for each variant of the given module. 135 VisitAllModuleVariants(module Module, visit func(Module)) 136 137 // PrimaryModule returns the first variant of the given module. This can be used to perform 138 // // singleton actions that are only done once for all variants of a module. 139 PrimaryModule(module Module) Module 140 141 // FinalModule returns the last variant of the given module. This can be used to perform 142 // singleton actions that are only done once for all variants of a module. 143 FinalModule(module Module) Module 144 145 // AddNinjaFileDeps adds dependencies on the specified files to the rule that creates the ninja manifest. The 146 // primary builder will be rerun whenever the specified files are modified. 147 AddNinjaFileDeps(deps ...string) 148 149 // GlobWithDeps returns a list of files and directories that match the 150 // specified pattern but do not match any of the patterns in excludes. 151 // Any directories will have a '/' suffix. It also adds efficient 152 // dependencies to rerun the primary builder whenever a file matching 153 // the pattern as added or removed, without rerunning if a file that 154 // does not match the pattern is added to a searched directory. 155 GlobWithDeps(pattern string, excludes []string) ([]string, error) 156 157 // Fs returns a pathtools.Filesystem that can be used to interact with files. Using the Filesystem interface allows 158 // the singleton to be used in build system tests that run against a mock filesystem. 159 Fs() pathtools.FileSystem 160} 161 162var _ SingletonContext = (*singletonContext)(nil) 163 164type singletonContext struct { 165 name string 166 context *Context 167 config interface{} 168 scope *localScope 169 globals *liveTracker 170 171 ninjaFileDeps []string 172 errs []error 173 174 actionDefs localBuildActions 175} 176 177func (s *singletonContext) Config() interface{} { 178 return s.config 179} 180 181func (s *singletonContext) Name() string { 182 return s.name 183} 184 185func (s *singletonContext) ModuleName(logicModule Module) string { 186 return s.context.ModuleName(logicModule) 187} 188 189func (s *singletonContext) ModuleDir(logicModule Module) string { 190 return s.context.ModuleDir(logicModule) 191} 192 193func (s *singletonContext) ModuleSubDir(logicModule Module) string { 194 return s.context.ModuleSubDir(logicModule) 195} 196 197func (s *singletonContext) ModuleType(logicModule Module) string { 198 return s.context.ModuleType(logicModule) 199} 200 201func (s *singletonContext) ModuleProvider(logicModule Module, provider ProviderKey) interface{} { 202 return s.context.ModuleProvider(logicModule, provider) 203} 204 205// ModuleHasProvider returns true if the provider for the given module has been set. 206func (s *singletonContext) ModuleHasProvider(logicModule Module, provider ProviderKey) bool { 207 return s.context.ModuleHasProvider(logicModule, provider) 208} 209 210func (s *singletonContext) BlueprintFile(logicModule Module) string { 211 return s.context.BlueprintFile(logicModule) 212} 213 214func (s *singletonContext) error(err error) { 215 if err != nil { 216 s.errs = append(s.errs, err) 217 } 218} 219 220func (s *singletonContext) ModuleErrorf(logicModule Module, format string, 221 args ...interface{}) { 222 223 s.error(s.context.ModuleErrorf(logicModule, format, args...)) 224} 225 226func (s *singletonContext) Errorf(format string, args ...interface{}) { 227 // TODO: Make this not result in the error being printed as "internal error" 228 s.error(fmt.Errorf(format, args...)) 229} 230 231func (s *singletonContext) Failed() bool { 232 return len(s.errs) > 0 233} 234 235func (s *singletonContext) Variable(pctx PackageContext, name, value string) { 236 s.scope.ReparentTo(pctx) 237 238 v, err := s.scope.AddLocalVariable(name, value) 239 if err != nil { 240 panic(err) 241 } 242 243 s.actionDefs.variables = append(s.actionDefs.variables, v) 244} 245 246func (s *singletonContext) Rule(pctx PackageContext, name string, 247 params RuleParams, argNames ...string) Rule { 248 249 s.scope.ReparentTo(pctx) 250 251 r, err := s.scope.AddLocalRule(name, ¶ms, argNames...) 252 if err != nil { 253 panic(err) 254 } 255 256 s.actionDefs.rules = append(s.actionDefs.rules, r) 257 258 return r 259} 260 261func (s *singletonContext) Build(pctx PackageContext, params BuildParams) { 262 s.scope.ReparentTo(pctx) 263 264 def, err := parseBuildParams(s.scope, ¶ms) 265 if err != nil { 266 panic(err) 267 } 268 269 s.actionDefs.buildDefs = append(s.actionDefs.buildDefs, def) 270} 271 272func (s *singletonContext) Eval(pctx PackageContext, str string) (string, error) { 273 s.scope.ReparentTo(pctx) 274 275 ninjaStr, err := parseNinjaString(s.scope, str) 276 if err != nil { 277 return "", err 278 } 279 280 err = s.globals.addNinjaStringDeps(ninjaStr) 281 if err != nil { 282 return "", err 283 } 284 285 return ninjaStr.Eval(s.globals.variables) 286} 287 288func (s *singletonContext) RequireNinjaVersion(major, minor, micro int) { 289 s.context.requireNinjaVersion(major, minor, micro) 290} 291 292func (s *singletonContext) SetNinjaBuildDir(pctx PackageContext, value string) { 293 s.scope.ReparentTo(pctx) 294 295 ninjaValue, err := parseNinjaString(s.scope, value) 296 if err != nil { 297 panic(err) 298 } 299 300 s.context.setNinjaBuildDir(ninjaValue) 301} 302 303func (s *singletonContext) AddSubninja(file string) { 304 s.context.subninjas = append(s.context.subninjas, file) 305} 306 307func (s *singletonContext) VisitAllModules(visit func(Module)) { 308 var visitingModule Module 309 defer func() { 310 if r := recover(); r != nil { 311 panic(newPanicErrorf(r, "VisitAllModules(%s) for module %s", 312 funcName(visit), s.context.moduleInfo[visitingModule])) 313 } 314 }() 315 316 s.context.VisitAllModules(func(m Module) { 317 visitingModule = m 318 visit(m) 319 }) 320} 321 322func (s *singletonContext) VisitAllModulesIf(pred func(Module) bool, 323 visit func(Module)) { 324 325 s.context.VisitAllModulesIf(pred, visit) 326} 327 328func (s *singletonContext) VisitDirectDeps(module Module, visit func(Module)) { 329 s.context.VisitDirectDeps(module, visit) 330} 331 332func (s *singletonContext) VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module)) { 333 s.context.VisitDirectDepsIf(module, pred, visit) 334} 335 336func (s *singletonContext) VisitDepsDepthFirst(module Module, 337 visit func(Module)) { 338 339 s.context.VisitDepsDepthFirst(module, visit) 340} 341 342func (s *singletonContext) VisitDepsDepthFirstIf(module Module, 343 pred func(Module) bool, visit func(Module)) { 344 345 s.context.VisitDepsDepthFirstIf(module, pred, visit) 346} 347 348func (s *singletonContext) PrimaryModule(module Module) Module { 349 return s.context.PrimaryModule(module) 350} 351 352func (s *singletonContext) FinalModule(module Module) Module { 353 return s.context.FinalModule(module) 354} 355 356func (s *singletonContext) VisitAllModuleVariants(module Module, visit func(Module)) { 357 s.context.VisitAllModuleVariants(module, visit) 358} 359 360func (s *singletonContext) AddNinjaFileDeps(deps ...string) { 361 s.ninjaFileDeps = append(s.ninjaFileDeps, deps...) 362} 363 364func (s *singletonContext) GlobWithDeps(pattern string, 365 excludes []string) ([]string, error) { 366 return s.context.glob(pattern, excludes) 367} 368 369func (s *singletonContext) Fs() pathtools.FileSystem { 370 return s.context.fs 371} 372