1// Copyright 2020 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 "fmt" 19 "sync" 20 21 "github.com/google/blueprint" 22) 23 24// A SingletonModule is halfway between a Singleton and a Module. It has access to visiting 25// other modules via its GenerateSingletonBuildActions method, but must be defined in an Android.bp 26// file and can also be depended on like a module. It must be used zero or one times in an 27// Android.bp file, and it can only have a single variant. 28// 29// The SingletonModule's GenerateAndroidBuildActions method will be called before any normal or 30// singleton module that depends on it, but its GenerateSingletonBuildActions method will be called 31// after all modules, in registration order with other singletons and singleton modules. 32// GenerateAndroidBuildActions and GenerateSingletonBuildActions will not be called if the 33// SingletonModule was not instantiated in an Android.bp file. 34// 35// Since the SingletonModule rules likely depend on the modules visited during 36// GenerateSingletonBuildActions, the GenerateAndroidBuildActions is unlikely to produce any 37// rules directly. Instead, it will probably set some providers to paths that will later have rules 38// generated to produce them in GenerateSingletonBuildActions. 39// 40// The expected use case for a SingletonModule is a module that produces files that depend on all 41// modules in the tree and will be used by other modules. For example it could produce a text 42// file that lists all modules that meet a certain criteria, and that text file could be an input 43// to another module. Care must be taken that the ninja rules produced by the SingletonModule 44// don't produce a cycle by referencing output files of rules of modules that depend on the 45// SingletonModule. 46// 47// A SingletonModule must embed a SingletonModuleBase struct, and its factory method must be 48// registered with RegisterSingletonModuleType from an init() function. 49// 50// A SingletonModule can also implement SingletonMakeVarsProvider to export values to Make. 51type SingletonModule interface { 52 Module 53 GenerateSingletonBuildActions(SingletonContext) 54 singletonModuleBase() *SingletonModuleBase 55} 56 57// SingletonModuleBase must be embedded into implementers of the SingletonModule interface. 58type SingletonModuleBase struct { 59 ModuleBase 60 61 lock sync.Mutex 62 bp string 63 variant string 64} 65 66// GenerateBuildActions wraps the ModuleBase GenerateBuildActions method, verifying it was only 67// called once to prevent multiple variants of a SingletonModule. 68func (smb *SingletonModuleBase) GenerateBuildActions(ctx blueprint.ModuleContext) { 69 smb.lock.Lock() 70 if smb.variant != "" { 71 ctx.ModuleErrorf("GenerateAndroidBuildActions already called for variant %q, SingletonModules can only have one variant", smb.variant) 72 } 73 smb.variant = ctx.ModuleSubDir() 74 smb.lock.Unlock() 75 76 smb.ModuleBase.GenerateBuildActions(ctx) 77} 78 79// InitAndroidSingletonModule must be called from the SingletonModule's factory function to 80// initialize SingletonModuleBase. 81func InitAndroidSingletonModule(sm SingletonModule) { 82 InitAndroidModule(sm) 83} 84 85// singletonModuleBase retrieves the embedded SingletonModuleBase from a SingletonModule. 86func (smb *SingletonModuleBase) singletonModuleBase() *SingletonModuleBase { return smb } 87 88// SingletonModuleFactory is a factory method that returns a SingletonModule. 89type SingletonModuleFactory func() SingletonModule 90 91// SingletonModuleFactoryAdaptor converts a SingletonModuleFactory into a SingletonFactory and a 92// ModuleFactory. 93func SingletonModuleFactoryAdaptor(name string, factory SingletonModuleFactory) (SingletonFactory, ModuleFactory) { 94 // The sm variable acts as a static holder of the only SingletonModule instance. Calls to the 95 // returned SingletonFactory and ModuleFactory lambdas will always return the same sm value. 96 // The SingletonFactory is only expected to be called once, but the ModuleFactory may be 97 // called multiple times if the module is replaced with a clone of itself at the end of 98 // blueprint.ResolveDependencies. 99 var sm SingletonModule 100 s := func() Singleton { 101 sm = factory() 102 return &singletonModuleSingletonAdaptor{sm} 103 } 104 m := func() Module { 105 if sm == nil { 106 panic(fmt.Errorf("Singleton %q for SingletonModule was not instantiated", name)) 107 } 108 109 // Check for multiple uses of a SingletonModule in a LoadHook. Checking directly in the 110 // factory would incorrectly flag when the factory was called again when the module is 111 // replaced with a clone of itself at the end of blueprint.ResolveDependencies. 112 AddLoadHook(sm, func(ctx LoadHookContext) { 113 smb := sm.singletonModuleBase() 114 smb.lock.Lock() 115 defer smb.lock.Unlock() 116 if smb.bp != "" { 117 ctx.ModuleErrorf("Duplicate SingletonModule %q, previously used in %s", name, smb.bp) 118 } 119 smb.bp = ctx.BlueprintsFile() 120 }) 121 return sm 122 } 123 return s, m 124} 125 126// singletonModuleSingletonAdaptor makes a SingletonModule into a Singleton by translating the 127// GenerateSingletonBuildActions method to Singleton.GenerateBuildActions. 128type singletonModuleSingletonAdaptor struct { 129 sm SingletonModule 130} 131 132// GenerateBuildActions calls the SingletonModule's GenerateSingletonBuildActions method, but only 133// if the module was defined in an Android.bp file. 134func (smsa *singletonModuleSingletonAdaptor) GenerateBuildActions(ctx SingletonContext) { 135 if smsa.sm.singletonModuleBase().bp != "" { 136 smsa.sm.GenerateSingletonBuildActions(ctx) 137 } 138} 139 140func (smsa *singletonModuleSingletonAdaptor) MakeVars(ctx MakeVarsContext) { 141 if smsa.sm.singletonModuleBase().bp != "" { 142 if makeVars, ok := smsa.sm.(SingletonMakeVarsProvider); ok { 143 makeVars.MakeVars(ctx) 144 } 145 } 146} 147