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