1// Copyright 2016 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 "path" 20 "reflect" 21 "runtime" 22 23 "github.com/google/blueprint" 24 "github.com/google/blueprint/proptools" 25) 26 27// This file implements hooks that external module types can use to inject logic into existing 28// module types. Each hook takes an interface as a parameter so that new methods can be added 29// to the interface without breaking existing module types. 30 31// Load hooks are run after the module's properties have been filled from the blueprint file, but 32// before the module has been split into architecture variants, and before defaults modules have 33// been applied. 34type LoadHookContext interface { 35 EarlyModuleContext 36 37 AppendProperties(...interface{}) 38 PrependProperties(...interface{}) 39 CreateModule(ModuleFactory, ...interface{}) Module 40 CreateModuleInDirectory(ModuleFactory, string, ...interface{}) Module 41 42 registerScopedModuleType(name string, factory blueprint.ModuleFactory) 43 moduleFactories() map[string]blueprint.ModuleFactory 44} 45 46// Add a hook that will be called once the module has been loaded, i.e. its 47// properties have been initialized from the Android.bp file. 48// 49// Consider using SetDefaultableHook to register a hook for any module that implements 50// DefaultableModule as the hook is called after any defaults have been applied to the 51// module which could reduce duplication and make it easier to use. 52func AddLoadHook(m blueprint.Module, hook func(LoadHookContext)) { 53 blueprint.AddLoadHook(m, func(ctx blueprint.LoadHookContext) { 54 actx := &loadHookContext{ 55 earlyModuleContext: m.(Module).base().earlyModuleContextFactory(ctx), 56 bp: ctx, 57 } 58 hook(actx) 59 }) 60} 61 62func AddLoadHookWithPriority(m blueprint.Module, hook func(LoadHookContext), priority int) { 63 blueprint.AddLoadHookWithPriority(m, func(ctx blueprint.LoadHookContext) { 64 actx := &loadHookContext{ 65 earlyModuleContext: m.(Module).base().earlyModuleContextFactory(ctx), 66 bp: ctx, 67 } 68 hook(actx) 69 }, priority) 70} 71 72type loadHookContext struct { 73 earlyModuleContext 74 bp blueprint.LoadHookContext 75 module Module 76} 77 78func (l *loadHookContext) moduleFactories() map[string]blueprint.ModuleFactory { 79 return l.bp.ModuleFactories() 80} 81 82func (l *loadHookContext) appendPrependHelper(props []interface{}, 83 extendFn func([]interface{}, interface{}, proptools.ExtendPropertyFilterFunc) error) { 84 for _, p := range props { 85 err := extendFn(l.Module().base().GetProperties(), p, nil) 86 if err != nil { 87 if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok { 88 l.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error()) 89 } else { 90 panic(err) 91 } 92 } 93 } 94} 95func (l *loadHookContext) AppendProperties(props ...interface{}) { 96 l.appendPrependHelper(props, proptools.AppendMatchingProperties) 97} 98 99func (l *loadHookContext) PrependProperties(props ...interface{}) { 100 l.appendPrependHelper(props, proptools.PrependMatchingProperties) 101} 102 103func (l *loadHookContext) createModule(factory blueprint.ModuleFactory, name string, props ...interface{}) blueprint.Module { 104 return l.bp.CreateModule(factory, name, props...) 105} 106 107func (l *loadHookContext) createModuleInDirectory(factory blueprint.ModuleFactory, name, moduleDir string, props ...interface{}) blueprint.Module { 108 return l.bp.CreateModuleInDirectory(factory, name, moduleDir, props...) 109} 110 111type specifyDirectory struct { 112 specified bool 113 directory string 114} 115 116func doesNotSpecifyDirectory() specifyDirectory { 117 return specifyDirectory{ 118 specified: false, 119 directory: "", 120 } 121} 122 123func specifiesDirectory(directory string) specifyDirectory { 124 return specifyDirectory{ 125 specified: true, 126 directory: directory, 127 } 128} 129 130type createModuleContext interface { 131 Module() Module 132 HasMutatorFinished(mutatorName string) bool 133 createModule(blueprint.ModuleFactory, string, ...interface{}) blueprint.Module 134 createModuleInDirectory(blueprint.ModuleFactory, string, string, ...interface{}) blueprint.Module 135} 136 137func createModule(ctx createModuleContext, factory ModuleFactory, ext string, specifyDirectory specifyDirectory, props ...interface{}) Module { 138 if ctx.HasMutatorFinished("defaults") { 139 // Creating modules late is oftentimes problematic, because they don't have earlier 140 // mutators run on them. Prevent making modules after the defaults mutator has run. 141 panic("Cannot create a module after the defaults mutator has finished") 142 } 143 144 inherited := []interface{}{&ctx.Module().base().commonProperties} 145 146 var typeName string 147 if typeNameLookup, ok := ModuleTypeByFactory()[reflect.ValueOf(factory)]; ok { 148 typeName = typeNameLookup 149 } else { 150 factoryPtr := reflect.ValueOf(factory).Pointer() 151 factoryFunc := runtime.FuncForPC(factoryPtr) 152 filePath, _ := factoryFunc.FileLine(factoryPtr) 153 typeName = fmt.Sprintf("%s_%s", path.Base(filePath), factoryFunc.Name()) 154 } 155 typeName = typeName + "_" + ext 156 157 var module Module 158 if specifyDirectory.specified { 159 module = ctx.createModuleInDirectory(ModuleFactoryAdaptor(factory), typeName, specifyDirectory.directory, append(inherited, props...)...).(Module) 160 } else { 161 module = ctx.createModule(ModuleFactoryAdaptor(factory), typeName, append(inherited, props...)...).(Module) 162 } 163 164 if ctx.Module().base().variableProperties != nil && module.base().variableProperties != nil { 165 src := ctx.Module().base().variableProperties 166 dst := []interface{}{ 167 module.base().variableProperties, 168 // Put an empty copy of the src properties into dst so that properties in src that are not in dst 169 // don't cause a "failed to find property to extend" error. 170 proptools.CloneEmptyProperties(reflect.ValueOf(src)).Interface(), 171 } 172 err := proptools.AppendMatchingProperties(dst, src, nil) 173 if err != nil { 174 panic(err) 175 } 176 } 177 178 return module 179} 180 181func (l *loadHookContext) CreateModule(factory ModuleFactory, props ...interface{}) Module { 182 return createModule(l, factory, "_loadHookModule", doesNotSpecifyDirectory(), props...) 183} 184 185func (l *loadHookContext) CreateModuleInDirectory(factory ModuleFactory, directory string, props ...interface{}) Module { 186 return createModule(l, factory, "_loadHookModule", specifiesDirectory(directory), props...) 187} 188 189func (l *loadHookContext) registerScopedModuleType(name string, factory blueprint.ModuleFactory) { 190 l.bp.RegisterScopedModuleType(name, factory) 191} 192 193type InstallHookContext interface { 194 ModuleContext 195 SrcPath() Path 196 Path() InstallPath 197 Symlink() bool 198} 199 200// Install hooks are run after a module creates a rule to install a file or symlink. 201// The installed path is available from InstallHookContext.Path(), and 202// InstallHookContext.Symlink() will be true if it was a symlink. 203func AddInstallHook(m blueprint.Module, hook func(InstallHookContext)) { 204 h := &m.(Module).base().hooks 205 h.install = append(h.install, hook) 206} 207 208type installHookContext struct { 209 ModuleContext 210 srcPath Path 211 path InstallPath 212 symlink bool 213} 214 215var _ InstallHookContext = &installHookContext{} 216 217func (x *installHookContext) SrcPath() Path { 218 return x.srcPath 219} 220 221func (x *installHookContext) Path() InstallPath { 222 return x.path 223} 224 225func (x *installHookContext) Symlink() bool { 226 return x.symlink 227} 228 229func (x *hooks) runInstallHooks(ctx ModuleContext, srcPath Path, path InstallPath, symlink bool) { 230 if len(x.install) > 0 { 231 mctx := &installHookContext{ 232 ModuleContext: ctx, 233 srcPath: srcPath, 234 path: path, 235 symlink: symlink, 236 } 237 for _, x := range x.install { 238 x(mctx) 239 if mctx.Failed() { 240 return 241 } 242 } 243 } 244} 245 246type hooks struct { 247 install []func(InstallHookContext) 248} 249