1// Copyright 2017 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 android 16 17import ( 18 "slices" 19 "sync" 20 21 "github.com/google/blueprint" 22) 23 24// SingletonContext 25type SingletonContext interface { 26 blueprintSingletonContext() blueprint.SingletonContext 27 28 Config() Config 29 DeviceConfig() DeviceConfig 30 31 ModuleName(module blueprint.Module) string 32 ModuleDir(module blueprint.Module) string 33 ModuleSubDir(module blueprint.Module) string 34 ModuleType(module blueprint.Module) string 35 BlueprintFile(module blueprint.Module) string 36 37 // ModuleVariantsFromName returns the list of module variants named `name` in the same namespace as `referer` enforcing visibility rules. 38 // Allows generating build actions for `referer` based on the metadata for `name` deferred until the singleton context. 39 ModuleVariantsFromName(referer ModuleProxy, name string) []ModuleProxy 40 41 otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) 42 43 ModuleErrorf(module blueprint.Module, format string, args ...interface{}) 44 Errorf(format string, args ...interface{}) 45 Failed() bool 46 47 Variable(pctx PackageContext, name, value string) 48 Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule 49 Build(pctx PackageContext, params BuildParams) 50 51 // Phony creates a Make-style phony rule, a rule with no commands that can depend on other 52 // phony rules or real files. Phony can be called on the same name multiple times to add 53 // additional dependencies. 54 Phony(name string, deps ...Path) 55 56 RequireNinjaVersion(major, minor, micro int) 57 58 // SetOutDir sets the value of the top-level "builddir" Ninja variable 59 // that controls where Ninja stores its build log files. This value can be 60 // set at most one time for a single build, later calls are ignored. 61 SetOutDir(pctx PackageContext, value string) 62 63 // Eval takes a string with embedded ninja variables, and returns a string 64 // with all of the variables recursively expanded. Any variables references 65 // are expanded in the scope of the PackageContext. 66 Eval(pctx PackageContext, ninjaStr string) (string, error) 67 68 VisitAllModulesBlueprint(visit func(blueprint.Module)) 69 VisitAllModules(visit func(Module)) 70 VisitAllModuleProxies(visit func(proxy ModuleProxy)) 71 VisitAllModulesIf(pred func(Module) bool, visit func(Module)) 72 73 VisitDirectDeps(module Module, visit func(Module)) 74 VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module)) 75 76 // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module 77 VisitDepsDepthFirst(module Module, visit func(Module)) 78 // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module 79 VisitDepsDepthFirstIf(module Module, pred func(Module) bool, 80 visit func(Module)) 81 82 VisitAllModuleVariants(module Module, visit func(Module)) 83 84 VisitAllModuleVariantProxies(module Module, visit func(proxy ModuleProxy)) 85 86 PrimaryModule(module Module) Module 87 88 PrimaryModuleProxy(module ModuleProxy) ModuleProxy 89 90 IsFinalModule(module Module) bool 91 92 AddNinjaFileDeps(deps ...string) 93 94 // GlobWithDeps returns a list of files that match the specified pattern but do not match any 95 // of the patterns in excludes. It also adds efficient dependencies to rerun the primary 96 // builder whenever a file matching the pattern as added or removed, without rerunning if a 97 // file that does not match the pattern is added to a searched directory. 98 GlobWithDeps(pattern string, excludes []string) ([]string, error) 99 100 // OtherModulePropertyErrorf reports an error on the line number of the given property of the given module 101 OtherModulePropertyErrorf(module Module, property string, format string, args ...interface{}) 102 103 // HasMutatorFinished returns true if the given mutator has finished running. 104 // It will panic if given an invalid mutator name. 105 HasMutatorFinished(mutatorName string) bool 106 107 // DistForGoals creates a rule to copy one or more Paths to the artifacts 108 // directory on the build server when any of the specified goals are built. 109 DistForGoal(goal string, paths ...Path) 110 111 // DistForGoalWithFilename creates a rule to copy a Path to the artifacts 112 // directory on the build server with the given filename when the specified 113 // goal is built. 114 DistForGoalWithFilename(goal string, path Path, filename string) 115 116 // DistForGoals creates a rule to copy one or more Paths to the artifacts 117 // directory on the build server when any of the specified goals are built. 118 DistForGoals(goals []string, paths ...Path) 119 120 // DistForGoalsWithFilename creates a rule to copy a Path to the artifacts 121 // directory on the build server with the given filename when any of the 122 // specified goals are built. 123 DistForGoalsWithFilename(goals []string, path Path, filename string) 124} 125 126type singletonAdaptor struct { 127 Singleton 128 129 buildParams []BuildParams 130 ruleParams map[blueprint.Rule]blueprint.RuleParams 131} 132 133var _ testBuildProvider = (*singletonAdaptor)(nil) 134 135func (s *singletonAdaptor) GenerateBuildActions(ctx blueprint.SingletonContext) { 136 sctx := &singletonContextAdaptor{SingletonContext: ctx} 137 if sctx.Config().captureBuild { 138 sctx.ruleParams = make(map[blueprint.Rule]blueprint.RuleParams) 139 } 140 141 s.Singleton.GenerateBuildActions(sctx) 142 143 s.buildParams = sctx.buildParams 144 s.ruleParams = sctx.ruleParams 145 146 if len(sctx.dists) > 0 { 147 dists := getSingletonDists(sctx.Config()) 148 dists.lock.Lock() 149 defer dists.lock.Unlock() 150 dists.dists = append(dists.dists, sctx.dists...) 151 } 152} 153 154func (s *singletonAdaptor) BuildParamsForTests() []BuildParams { 155 return s.buildParams 156} 157 158func (s *singletonAdaptor) RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams { 159 return s.ruleParams 160} 161 162var singletonDistsKey = NewOnceKey("singletonDistsKey") 163 164type singletonDistsAndLock struct { 165 dists []dist 166 lock sync.Mutex 167} 168 169func getSingletonDists(config Config) *singletonDistsAndLock { 170 return config.Once(singletonDistsKey, func() interface{} { 171 return &singletonDistsAndLock{} 172 }).(*singletonDistsAndLock) 173} 174 175type Singleton interface { 176 GenerateBuildActions(SingletonContext) 177} 178 179type singletonContextAdaptor struct { 180 blueprint.SingletonContext 181 182 buildParams []BuildParams 183 ruleParams map[blueprint.Rule]blueprint.RuleParams 184 dists []dist 185} 186 187func (s *singletonContextAdaptor) blueprintSingletonContext() blueprint.SingletonContext { 188 return s.SingletonContext 189} 190 191func (s *singletonContextAdaptor) Config() Config { 192 return s.SingletonContext.Config().(Config) 193} 194 195func (s *singletonContextAdaptor) DeviceConfig() DeviceConfig { 196 return DeviceConfig{s.Config().deviceConfig} 197} 198 199func (s *singletonContextAdaptor) Variable(pctx PackageContext, name, value string) { 200 s.SingletonContext.Variable(pctx.PackageContext, name, value) 201} 202 203func (s *singletonContextAdaptor) Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule { 204 if s.Config().UseRemoteBuild() { 205 if params.Pool == nil { 206 // When USE_GOMA=true or USE_RBE=true are set and the rule is not supported by goma/RBE, restrict 207 // jobs to the local parallelism value 208 params.Pool = localPool 209 } else if params.Pool == remotePool { 210 // remotePool is a fake pool used to identify rule that are supported for remoting. If the rule's 211 // pool is the remotePool, replace with nil so that ninja runs it at NINJA_REMOTE_NUM_JOBS 212 // parallelism. 213 params.Pool = nil 214 } 215 } 216 rule := s.SingletonContext.Rule(pctx.PackageContext, name, params, argNames...) 217 if s.Config().captureBuild { 218 s.ruleParams[rule] = params 219 } 220 return rule 221} 222 223func (s *singletonContextAdaptor) Build(pctx PackageContext, params BuildParams) { 224 if s.Config().captureBuild { 225 s.buildParams = append(s.buildParams, params) 226 } 227 bparams := convertBuildParams(params) 228 s.SingletonContext.Build(pctx.PackageContext, bparams) 229} 230 231func (s *singletonContextAdaptor) Phony(name string, deps ...Path) { 232 addSingletonPhony(s.Config(), name, deps...) 233} 234 235func (s *singletonContextAdaptor) SetOutDir(pctx PackageContext, value string) { 236 s.SingletonContext.SetOutDir(pctx.PackageContext, value) 237} 238 239func (s *singletonContextAdaptor) Eval(pctx PackageContext, ninjaStr string) (string, error) { 240 return s.SingletonContext.Eval(pctx.PackageContext, ninjaStr) 241} 242 243// visitAdaptor wraps a visit function that takes an android.Module parameter into 244// a function that takes a blueprint.Module parameter and only calls the visit function if the 245// blueprint.Module is an android.Module. 246func visitAdaptor(visit func(Module)) func(blueprint.Module) { 247 return func(module blueprint.Module) { 248 if aModule, ok := module.(Module); ok { 249 visit(aModule) 250 } 251 } 252} 253 254// visitProxyAdaptor wraps a visit function that takes an android.ModuleProxy parameter into 255// a function that takes a blueprint.ModuleProxy parameter. 256func visitProxyAdaptor(visit func(proxy ModuleProxy)) func(proxy blueprint.ModuleProxy) { 257 return func(module blueprint.ModuleProxy) { 258 visit(ModuleProxy{ 259 module: module, 260 }) 261 } 262} 263 264// predAdaptor wraps a pred function that takes an android.Module parameter 265// into a function that takes an blueprint.Module parameter and only calls the visit function if the 266// blueprint.Module is an android.Module, otherwise returns false. 267func predAdaptor(pred func(Module) bool) func(blueprint.Module) bool { 268 return func(module blueprint.Module) bool { 269 if aModule, ok := module.(Module); ok { 270 return pred(aModule) 271 } else { 272 return false 273 } 274 } 275} 276 277func (s *singletonContextAdaptor) ModuleName(module blueprint.Module) string { 278 return s.SingletonContext.ModuleName(getWrappedModule(module)) 279} 280 281func (s *singletonContextAdaptor) ModuleDir(module blueprint.Module) string { 282 return s.SingletonContext.ModuleDir(getWrappedModule(module)) 283} 284 285func (s *singletonContextAdaptor) ModuleSubDir(module blueprint.Module) string { 286 return s.SingletonContext.ModuleSubDir(getWrappedModule(module)) 287} 288 289func (s *singletonContextAdaptor) ModuleType(module blueprint.Module) string { 290 return s.SingletonContext.ModuleType(getWrappedModule(module)) 291} 292 293func (s *singletonContextAdaptor) BlueprintFile(module blueprint.Module) string { 294 return s.SingletonContext.BlueprintFile(getWrappedModule(module)) 295} 296 297func (s *singletonContextAdaptor) VisitAllModulesBlueprint(visit func(blueprint.Module)) { 298 s.SingletonContext.VisitAllModules(visit) 299} 300 301func (s *singletonContextAdaptor) VisitAllModules(visit func(Module)) { 302 s.SingletonContext.VisitAllModules(visitAdaptor(visit)) 303} 304 305func (s *singletonContextAdaptor) VisitAllModuleProxies(visit func(proxy ModuleProxy)) { 306 s.SingletonContext.VisitAllModuleProxies(visitProxyAdaptor(visit)) 307} 308 309func (s *singletonContextAdaptor) VisitAllModulesIf(pred func(Module) bool, visit func(Module)) { 310 s.SingletonContext.VisitAllModulesIf(predAdaptor(pred), visitAdaptor(visit)) 311} 312 313func (s *singletonContextAdaptor) VisitDirectDeps(module Module, visit func(Module)) { 314 s.SingletonContext.VisitDirectDeps(module, visitAdaptor(visit)) 315} 316 317func (s *singletonContextAdaptor) VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module)) { 318 s.SingletonContext.VisitDirectDepsIf(module, predAdaptor(pred), visitAdaptor(visit)) 319} 320 321func (s *singletonContextAdaptor) VisitDepsDepthFirst(module Module, visit func(Module)) { 322 s.SingletonContext.VisitDepsDepthFirst(module, visitAdaptor(visit)) 323} 324 325func (s *singletonContextAdaptor) VisitDepsDepthFirstIf(module Module, pred func(Module) bool, visit func(Module)) { 326 s.SingletonContext.VisitDepsDepthFirstIf(module, predAdaptor(pred), visitAdaptor(visit)) 327} 328 329func (s *singletonContextAdaptor) VisitAllModuleVariants(module Module, visit func(Module)) { 330 s.SingletonContext.VisitAllModuleVariants(module, visitAdaptor(visit)) 331} 332 333func (s *singletonContextAdaptor) VisitAllModuleVariantProxies(module Module, visit func(proxy ModuleProxy)) { 334 s.SingletonContext.VisitAllModuleVariantProxies(getWrappedModule(module), visitProxyAdaptor(visit)) 335} 336 337func (s *singletonContextAdaptor) PrimaryModule(module Module) Module { 338 return s.SingletonContext.PrimaryModule(module).(Module) 339} 340 341func (s *singletonContextAdaptor) PrimaryModuleProxy(module ModuleProxy) ModuleProxy { 342 return ModuleProxy{s.SingletonContext.PrimaryModuleProxy(module.module)} 343} 344 345func (s *singletonContextAdaptor) IsFinalModule(module Module) bool { 346 return s.SingletonContext.IsFinalModule(getWrappedModule(module)) 347} 348 349func (s *singletonContextAdaptor) ModuleVariantsFromName(referer ModuleProxy, name string) []ModuleProxy { 350 // get module reference for visibility enforcement 351 qualified := createVisibilityModuleProxyReference(s, s.ModuleName(referer), s.ModuleDir(referer), referer) 352 353 modules := s.SingletonContext.ModuleVariantsFromName(referer.module, name) 354 result := make([]ModuleProxy, 0, len(modules)) 355 for _, module := range modules { 356 // enforce visibility 357 depName := s.ModuleName(module) 358 depDir := s.ModuleDir(module) 359 depQualified := qualifiedModuleName{depDir, depName} 360 // Targets are always visible to other targets in their own package. 361 if depQualified.pkg != qualified.name.pkg { 362 rule := effectiveVisibilityRules(s.Config(), depQualified) 363 if !rule.matches(qualified) { 364 s.ModuleErrorf(referer, "module %q references %q which is not visible to this module\nYou may need to add %q to its visibility", 365 referer.Name(), depQualified, "//"+s.ModuleDir(referer)) 366 continue 367 } 368 } 369 result = append(result, ModuleProxy{module}) 370 } 371 return result 372} 373 374func (s *singletonContextAdaptor) otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) { 375 return s.SingletonContext.ModuleProvider(module, provider) 376} 377 378func (s *singletonContextAdaptor) OtherModulePropertyErrorf(module Module, property string, format string, args ...interface{}) { 379 s.blueprintSingletonContext().OtherModulePropertyErrorf(module, property, format, args...) 380} 381 382func (s *singletonContextAdaptor) HasMutatorFinished(mutatorName string) bool { 383 return s.blueprintSingletonContext().HasMutatorFinished(mutatorName) 384} 385func (s *singletonContextAdaptor) DistForGoal(goal string, paths ...Path) { 386 s.DistForGoals([]string{goal}, paths...) 387} 388 389func (s *singletonContextAdaptor) DistForGoalWithFilename(goal string, path Path, filename string) { 390 s.DistForGoalsWithFilename([]string{goal}, path, filename) 391} 392 393func (s *singletonContextAdaptor) DistForGoals(goals []string, paths ...Path) { 394 var copies distCopies 395 for _, path := range paths { 396 copies = append(copies, distCopy{ 397 from: path, 398 dest: path.Base(), 399 }) 400 } 401 s.dists = append(s.dists, dist{ 402 goals: slices.Clone(goals), 403 paths: copies, 404 }) 405} 406 407func (s *singletonContextAdaptor) DistForGoalsWithFilename(goals []string, path Path, filename string) { 408 s.dists = append(s.dists, dist{ 409 goals: slices.Clone(goals), 410 paths: distCopies{{from: path, dest: filename}}, 411 }) 412} 413