1// Copyright 2024 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 "maps" 20 "slices" 21 22 "github.com/google/blueprint/pool" 23) 24 25// TransitionMutator implements a top-down mechanism where a module tells its 26// direct dependencies what variation they should be built in but the dependency 27// has the final say. 28// 29// When implementing a transition mutator, one needs to implement four methods: 30// - Split() that tells what variations a module has by itself 31// - OutgoingTransition() where a module tells what it wants from its 32// dependency 33// - IncomingTransition() where a module has the final say about its own 34// variation 35// - Mutate() that changes the state of a module depending on its variation 36// 37// That the effective variation of module B when depended on by module A is the 38// composition the outgoing transition of module A and the incoming transition 39// of module B. 40// 41// The outgoing transition should not take the properties of the dependency into 42// account, only those of the module that depends on it. For this reason, the 43// dependency is not even passed into it as an argument. Likewise, the incoming 44// transition should not take the properties of the depending module into 45// account and is thus not informed about it. This makes for a nice 46// decomposition of the decision logic. 47// 48// A given transition mutator only affects its own variation; other variations 49// stay unchanged along the dependency edges. 50// 51// Soong makes sure that all modules are created in the desired variations and 52// that dependency edges are set up correctly. This ensures that "missing 53// variation" errors do not happen and allows for more flexible changes in the 54// value of the variation among dependency edges (as opposed to bottom-up 55// mutators where if module A in variation X depends on module B and module B 56// has that variation X, A must depend on variation X of B) 57// 58// The limited power of the context objects passed to individual mutators 59// methods also makes it more difficult to shoot oneself in the foot. Complete 60// safety is not guaranteed because no one prevents individual transition 61// mutators from mutating modules in illegal ways and for e.g. Split() or 62// Mutate() to run their own visitations of the transitive dependency of the 63// module and both of these are bad ideas, but it's better than no guardrails at 64// all. 65// 66// This model is pretty close to Bazel's configuration transitions. The mapping 67// between concepts in Soong and Bazel is as follows: 68// - Module == configured target 69// - Variant == configuration 70// - Variation name == configuration flag 71// - Variation == configuration flag value 72// - Outgoing transition == attribute transition 73// - Incoming transition == rule transition 74// 75// The Split() method does not have a Bazel equivalent and Bazel split 76// transitions do not have a Soong equivalent. 77// 78// Mutate() does not make sense in Bazel due to the different models of the 79// two systems: when creating new variations, Soong clones the old module and 80// thus some way is needed to change it state whereas Bazel creates each 81// configuration of a given configured target anew. 82type TransitionMutator interface { 83 // Split returns the set of variations that should be created for a module no matter 84 // who depends on it. Used when Make depends on a particular variation or when 85 // the module knows its variations just based on information given to it in 86 // the Blueprint file. This method should not mutate the module it is called 87 // on. 88 Split(ctx BaseModuleContext) []TransitionInfo 89 90 // OutgoingTransition is called on a module to determine which variation it wants 91 // from its direct dependencies. The dependency itself can override this decision. 92 // This method should not mutate the module itself. 93 OutgoingTransition(ctx OutgoingTransitionContext, sourceTransitionInfo TransitionInfo) TransitionInfo 94 95 // IncomingTransition is called on a module to determine which variation it should 96 // be in based on the variation modules that depend on it want. This gives the module 97 // a final say about its own variations. This method should not mutate the module 98 // itself. 99 IncomingTransition(ctx IncomingTransitionContext, incomingTransitionInfo TransitionInfo) TransitionInfo 100 101 // Mutate is called after a module was split into multiple variations on each 102 // variation. It should not split the module any further but adding new dependencies 103 // is fine. Unlike all the other methods on TransitionMutator, this method is 104 // allowed to mutate the module. 105 Mutate(ctx BottomUpMutatorContext, transitionInfo TransitionInfo) 106 107 // TransitionInfoFromVariation is called when adding dependencies with an explicit variation after the 108 // TransitionMutator has already run. It takes a variation name and returns a TransitionInfo for that 109 // variation. It may not be possible for some TransitionMutators to generate an appropriate TransitionInfo 110 // if the variation does not contain all the information from the TransitionInfo, in which case the 111 // TransitionMutator can panic in TransitionInfoFromVariation, and adding dependencies with explicit variations 112 // for this TransitionMutator is not supported. 113 TransitionInfoFromVariation(string) TransitionInfo 114} 115 116type IncomingTransitionContext interface { 117 // Module returns the target of the dependency edge for which the transition 118 // is being computed 119 Module() Module 120 121 // ModuleName returns the name of the module. This is generally the value that was returned by Module.Name() when 122 // the module was created, but may have been modified by calls to BottomUpMutatorContext.Rename. 123 ModuleName() string 124 125 // DepTag() Returns the dependency tag through which this dependency is 126 // reached 127 DepTag() DependencyTag 128 129 // Config returns the config object that was passed to 130 // Context.PrepareBuildActions. 131 Config() interface{} 132 133 // Provider returns the value for a provider for the target of the dependency edge for which the 134 // transition is being computed. If the value is not set it returns nil and false. It panics if 135 // called before the appropriate mutator or GenerateBuildActions pass for the provider. The value 136 // returned may be a deep copy of the value originally passed to SetProvider. 137 // 138 // This method shouldn't be used directly, prefer the type-safe android.ModuleProvider instead. 139 Provider(provider AnyProviderKey) (any, bool) 140 141 // IsAddingDependency returns true if the transition is being called while adding a dependency 142 // after the transition mutator has already run, or false if it is being called when the transition 143 // mutator is running. This should be used sparingly, all uses will have to be removed in order 144 // to support creating variants on demand. 145 IsAddingDependency() bool 146 147 // ModuleErrorf reports an error at the line number of the module type in the module definition. 148 ModuleErrorf(fmt string, args ...interface{}) 149 150 // PropertyErrorf reports an error at the line number of a property in the module definition. 151 PropertyErrorf(property, fmt string, args ...interface{}) 152} 153 154type OutgoingTransitionContext interface { 155 // Module returns the source of the dependency edge for which the transition 156 // is being computed 157 Module() Module 158 159 // ModuleName returns the name of the module. This is generally the value that was returned by Module.Name() when 160 // the module was created, but may have been modified by calls to BottomUpMutatorContext.Rename. 161 ModuleName() string 162 163 // DepTag() Returns the dependency tag through which this dependency is 164 // reached 165 DepTag() DependencyTag 166 167 // Config returns the config object that was passed to 168 // Context.PrepareBuildActions. 169 Config() interface{} 170 171 // Provider returns the value for a provider for the source of the dependency edge for which the 172 // transition is being computed. If the value is not set it returns nil and false. It panics if 173 // called before the appropriate mutator or GenerateBuildActions pass for the provider. The value 174 // returned may be a deep copy of the value originally passed to SetProvider. 175 // 176 // This method shouldn't be used directly, prefer the type-safe android.ModuleProvider instead. 177 Provider(provider AnyProviderKey) (any, bool) 178 179 // ModuleErrorf reports an error at the line number of the module type in the module definition. 180 ModuleErrorf(fmt string, args ...interface{}) 181 182 // PropertyErrorf reports an error at the line number of a property in the module definition. 183 PropertyErrorf(property, fmt string, args ...interface{}) 184} 185 186type TransitionInfo interface { 187 // Variation returns a string that will be used as the variation name for modules that use this TransitionInfo 188 // as their configuration. It must return a unique value for each valid TransitionInfo in order to avoid 189 // conflicts, and all identical TransitionInfos must return the same value. 190 Variation() string 191} 192 193type transitionMutatorImpl struct { 194 name string 195 mutator TransitionMutator 196 index int 197 inputVariants map[*moduleGroup][]*moduleInfo 198 neverFar bool 199} 200 201// Adds each argument in items to l if it's not already there. 202func addToStringListIfNotPresent(l []string, items ...string) []string { 203 for _, i := range items { 204 if !slices.Contains(l, i) { 205 l = append(l, i) 206 } 207 } 208 209 return l 210} 211 212func (t *transitionMutatorImpl) addRequiredVariation(m *moduleInfo, variation string, transitionInfo TransitionInfo) { 213 m.incomingTransitionInfosLock.Lock() 214 defer m.incomingTransitionInfosLock.Unlock() 215 216 // This is only a consistency check. Leaking the variations of a transition 217 // mutator to another one could well lead to issues that are difficult to 218 // track down. 219 if m.currentTransitionMutator != "" && m.currentTransitionMutator != t.name { 220 panic(fmt.Errorf("transition mutator is %s in mutator %s", m.currentTransitionMutator, t.name)) 221 } 222 223 m.currentTransitionMutator = t.name 224 if existing, exists := m.incomingTransitionInfos[variation]; exists { 225 if existing != transitionInfo { 226 panic(fmt.Errorf("TransitionInfo %#v and %#v are different but have same variation %q", 227 existing, transitionInfo, variation)) 228 } 229 } else { 230 if m.incomingTransitionInfos == nil { 231 m.incomingTransitionInfos = make(map[string]TransitionInfo) 232 } 233 m.incomingTransitionInfos[variation] = transitionInfo 234 } 235} 236 237func (t *transitionMutatorImpl) propagateMutator(mctx BaseModuleContext) { 238 module := mctx.(*mutatorContext).module 239 mutatorSplits := t.mutator.Split(mctx) 240 if mutatorSplits == nil || len(mutatorSplits) == 0 { 241 panic(fmt.Errorf("transition mutator %s returned no splits for module %s", t.name, mctx.ModuleName())) 242 } 243 244 // transitionVariations for given a module can be mutated by the module itself 245 // and modules that directly depend on it. Since this is a top-down mutator, 246 // all modules that directly depend on this module have already been processed 247 // so no locking is necessary. 248 // Sort the module transitions, but keep the mutatorSplits in the order returned 249 // by Split, as the order can be significant when inter-variant dependencies are 250 // used. 251 transitionVariations := slices.Sorted(maps.Keys(module.incomingTransitionInfos)) 252 transitionInfoMap := module.incomingTransitionInfos 253 module.incomingTransitionInfos = nil 254 255 splitsVariations := make([]string, 0, len(mutatorSplits)) 256 for _, splitTransitionInfo := range mutatorSplits { 257 splitVariation := splitTransitionInfo.Variation() 258 splitsVariations = append(splitsVariations, splitVariation) 259 if transitionInfoMap == nil { 260 transitionInfoMap = make(map[string]TransitionInfo, len(mutatorSplits)) 261 } 262 transitionInfoMap[splitVariation] = splitTransitionInfo 263 } 264 265 transitionVariations = addToStringListIfNotPresent(splitsVariations, transitionVariations...) 266 267 outgoingTransitionVariationCache := make([][]string, len(transitionVariations)) 268 transitionInfos := make([]TransitionInfo, 0, len(transitionVariations)) 269 for srcVariationIndex, srcVariation := range transitionVariations { 270 srcVariationTransitionCache := make([]string, len(module.directDeps)) 271 for depIndex, dep := range module.directDeps { 272 transitionInfo := t.transition(mctx)(mctx.moduleInfo(), transitionInfoMap[srcVariation], dep.module, dep.tag) 273 variation := transitionInfo.Variation() 274 srcVariationTransitionCache[depIndex] = variation 275 t.addRequiredVariation(dep.module, variation, transitionInfo) 276 } 277 outgoingTransitionVariationCache[srcVariationIndex] = srcVariationTransitionCache 278 transitionInfos = append(transitionInfos, transitionInfoMap[srcVariation]) 279 } 280 module.outgoingTransitionCache = outgoingTransitionVariationCache 281 module.splitTransitionVariations = transitionVariations 282 module.splitTransitionInfos = transitionInfos 283} 284 285var ( 286 outgoingTransitionContextPool = pool.New[outgoingTransitionContextImpl]() 287 incomingTransitionContextPool = pool.New[incomingTransitionContextImpl]() 288) 289 290type transitionContextImpl struct { 291 context *Context 292 source *moduleInfo 293 dep *moduleInfo 294 depTag DependencyTag 295 postMutator bool 296 config interface{} 297 errs []error 298} 299 300func (c *transitionContextImpl) DepTag() DependencyTag { 301 return c.depTag 302} 303 304func (c *transitionContextImpl) Config() interface{} { 305 return c.config 306} 307 308func (c *transitionContextImpl) IsAddingDependency() bool { 309 return c.postMutator 310} 311 312func (c *transitionContextImpl) error(err error) { 313 if err != nil { 314 c.errs = append(c.errs, err) 315 } 316} 317 318func (c *transitionContextImpl) ModuleErrorf(fmt string, args ...interface{}) { 319 c.error(c.context.moduleErrorf(c.dep, fmt, args...)) 320} 321 322func (c *transitionContextImpl) PropertyErrorf(property, fmt string, args ...interface{}) { 323 c.error(c.context.PropertyErrorf(c.dep.logicModule, property, fmt, args...)) 324} 325 326type outgoingTransitionContextImpl struct { 327 transitionContextImpl 328} 329 330func (c *outgoingTransitionContextImpl) Module() Module { 331 return c.source.logicModule 332} 333 334func (c *outgoingTransitionContextImpl) ModuleName() string { 335 return c.source.group.name 336} 337 338func (c *outgoingTransitionContextImpl) Provider(provider AnyProviderKey) (any, bool) { 339 return c.context.provider(c.source, provider.provider()) 340} 341 342type incomingTransitionContextImpl struct { 343 transitionContextImpl 344} 345 346func (c *incomingTransitionContextImpl) Module() Module { 347 return c.dep.logicModule 348} 349 350func (c *incomingTransitionContextImpl) ModuleName() string { 351 return c.dep.group.name 352} 353 354func (c *incomingTransitionContextImpl) Provider(provider AnyProviderKey) (any, bool) { 355 return c.context.provider(c.dep, provider.provider()) 356} 357 358func (t *transitionMutatorImpl) transition(mctx BaseModuleContext) Transition { 359 return func(source *moduleInfo, sourceTransitionInfo TransitionInfo, dep *moduleInfo, depTag DependencyTag) TransitionInfo { 360 tc := transitionContextImpl{ 361 context: mctx.base().context, 362 source: source, 363 dep: dep, 364 depTag: depTag, 365 config: mctx.Config(), 366 } 367 outCtx := outgoingTransitionContextPool.Get() 368 *outCtx = outgoingTransitionContextImpl{tc} 369 outgoingTransitionInfo := t.mutator.OutgoingTransition(outCtx, sourceTransitionInfo) 370 for _, err := range outCtx.errs { 371 mctx.error(err) 372 } 373 outgoingTransitionContextPool.Put(outCtx) 374 outCtx = nil 375 if mctx.Failed() { 376 return outgoingTransitionInfo 377 } 378 inCtx := incomingTransitionContextPool.Get() 379 *inCtx = incomingTransitionContextImpl{tc} 380 finalTransitionInfo := t.mutator.IncomingTransition(inCtx, outgoingTransitionInfo) 381 for _, err := range inCtx.errs { 382 mctx.error(err) 383 } 384 incomingTransitionContextPool.Put(inCtx) 385 inCtx = nil 386 return finalTransitionInfo 387 } 388} 389 390func (t *transitionMutatorImpl) bottomUpMutator(mctx BottomUpMutatorContext) { 391 mc := mctx.(*mutatorContext) 392 // Fetch and clean up transition mutator state. No locking needed since the 393 // only time interaction between multiple modules is required is during the 394 // computation of the variations required by a given module. 395 variations := mc.module.splitTransitionVariations 396 transitionInfos := mc.module.splitTransitionInfos 397 outgoingTransitionCache := mc.module.outgoingTransitionCache 398 mc.module.splitTransitionInfos = nil 399 mc.module.splitTransitionVariations = nil 400 mc.module.outgoingTransitionCache = nil 401 mc.module.currentTransitionMutator = "" 402 403 if len(variations) < 1 { 404 panic(fmt.Errorf("no variations found for module %s by mutator %s", 405 mctx.ModuleName(), t.name)) 406 } 407 408 if len(variations) == 1 && variations[0] == "" { 409 // Module is not split, just apply the transition 410 mc.context.convertDepsToVariation(mc.module, 0, 411 chooseDepByIndexes(mc.mutator.name, outgoingTransitionCache)) 412 mc.context.setModuleTransitionInfo(mc.module, t, transitionInfos[0]) 413 } else { 414 modules := mc.createVariationsWithTransition(variations, outgoingTransitionCache) 415 for i, module := range modules { 416 mc.context.setModuleTransitionInfo(module, t, transitionInfos[i]) 417 } 418 } 419} 420 421func (t *transitionMutatorImpl) mutateMutator(mctx BottomUpMutatorContext) { 422 module := mctx.(*mutatorContext).module 423 t.mutator.Mutate(mctx, module.transitionInfos[t.index]) 424} 425 426type TransitionMutatorHandle interface { 427 // NeverFar causes the variations created by this mutator to never be ignored when adding 428 // far variation dependencies. Normally, far variation dependencies ignore all the variants 429 // of the source module, and only use the variants explicitly requested by the 430 // AddFarVariationDependencies call. 431 NeverFar() TransitionMutatorHandle 432} 433 434type transitionMutatorHandle struct { 435 inner MutatorHandle 436 impl *transitionMutatorImpl 437} 438 439var _ TransitionMutatorHandle = (*transitionMutatorHandle)(nil) 440 441func (h *transitionMutatorHandle) NeverFar() TransitionMutatorHandle { 442 h.impl.neverFar = true 443 return h 444} 445 446func (c *Context) RegisterTransitionMutator(name string, mutator TransitionMutator) TransitionMutatorHandle { 447 impl := &transitionMutatorImpl{name: name, mutator: mutator} 448 449 c.registerTransitionPropagateMutator(name+"_propagate", impl.propagateMutator) 450 bottomUpHandle := c.RegisterBottomUpMutator(name, impl.bottomUpMutator).setTransitionMutator(impl) 451 c.RegisterBottomUpMutator(name+"_mutate", impl.mutateMutator) 452 453 impl.index = len(c.transitionMutators) 454 c.transitionMutators = append(c.transitionMutators, impl) 455 c.transitionMutatorNames = append(c.transitionMutatorNames, name) 456 457 return &transitionMutatorHandle{inner: bottomUpHandle, impl: impl} 458} 459 460// This function is called for every dependency edge to determine which 461// variation of the dependency is needed. Its inputs are the depending module, 462// its variation, the dependency and the dependency tag. 463type Transition func(source *moduleInfo, sourceTransitionInfo TransitionInfo, dep *moduleInfo, depTag DependencyTag) TransitionInfo 464