blob: e1aa891d179bf607746e072603675ff644e65a4b [file] [log] [blame]
Colin Cross7e129d22024-12-03 14:26:00 -08001// 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 android
16
17import "github.com/google/blueprint"
18
19// TransitionMutator implements a top-down mechanism where a module tells its
20// direct dependencies what variation they should be built in but the dependency
21// has the final say.
22//
23// When implementing a transition mutator, one needs to implement four methods:
24// - Split() that tells what variations a module has by itself
25// - OutgoingTransition() where a module tells what it wants from its
26// dependency
27// - IncomingTransition() where a module has the final say about its own
28// variation
29// - Mutate() that changes the state of a module depending on its variation
30//
31// That the effective variation of module B when depended on by module A is the
32// composition the outgoing transition of module A and the incoming transition
33// of module B.
34//
35// the outgoing transition should not take the properties of the dependency into
36// account, only those of the module that depends on it. For this reason, the
37// dependency is not even passed into it as an argument. Likewise, the incoming
38// transition should not take the properties of the depending module into
39// account and is thus not informed about it. This makes for a nice
40// decomposition of the decision logic.
41//
42// A given transition mutator only affects its own variation; other variations
43// stay unchanged along the dependency edges.
44//
45// Soong makes sure that all modules are created in the desired variations and
46// that dependency edges are set up correctly. This ensures that "missing
47// variation" errors do not happen and allows for more flexible changes in the
48// value of the variation among dependency edges (as oppposed to bottom-up
49// mutators where if module A in variation X depends on module B and module B
50// has that variation X, A must depend on variation X of B)
51//
52// The limited power of the context objects passed to individual mutators
53// methods also makes it more difficult to shoot oneself in the foot. Complete
54// safety is not guaranteed because no one prevents individual transition
55// mutators from mutating modules in illegal ways and for e.g. Split() or
56// Mutate() to run their own visitations of the transitive dependency of the
57// module and both of these are bad ideas, but it's better than no guardrails at
58// all.
59//
60// This model is pretty close to Bazel's configuration transitions. The mapping
61// between concepts in Soong and Bazel is as follows:
62// - Module == configured target
63// - Variant == configuration
64// - Variation name == configuration flag
65// - Variation == configuration flag value
66// - Outgoing transition == attribute transition
67// - Incoming transition == rule transition
68//
69// The Split() method does not have a Bazel equivalent and Bazel split
70// transitions do not have a Soong equivalent.
71//
72// Mutate() does not make sense in Bazel due to the different models of the
73// two systems: when creating new variations, Soong clones the old module and
74// thus some way is needed to change it state whereas Bazel creates each
75// configuration of a given configured target anew.
Colin Cross90d7df92025-01-31 11:29:33 -080076type TransitionMutator[T blueprint.TransitionInfo] interface {
Colin Cross7e129d22024-12-03 14:26:00 -080077 // Split returns the set of variations that should be created for a module no
78 // matter who depends on it. Used when Make depends on a particular variation
79 // or when the module knows its variations just based on information given to
80 // it in the Blueprint file. This method should not mutate the module it is
81 // called on.
Colin Cross90d7df92025-01-31 11:29:33 -080082 Split(ctx BaseModuleContext) []T
Colin Cross7e129d22024-12-03 14:26:00 -080083
84 // OutgoingTransition is called on a module to determine which variation it wants
85 // from its direct dependencies. The dependency itself can override this decision.
86 // This method should not mutate the module itself.
Colin Cross90d7df92025-01-31 11:29:33 -080087 OutgoingTransition(ctx OutgoingTransitionContext, sourceTransitionInfo T) T
Colin Cross7e129d22024-12-03 14:26:00 -080088
89 // IncomingTransition is called on a module to determine which variation it should
90 // be in based on the variation modules that depend on it want. This gives the module
91 // a final say about its own variations. This method should not mutate the module
92 // itself.
Colin Cross90d7df92025-01-31 11:29:33 -080093 IncomingTransition(ctx IncomingTransitionContext, incomingTransitionInfo T) T
Colin Cross7e129d22024-12-03 14:26:00 -080094
95 // Mutate is called after a module was split into multiple variations on each variation.
96 // It should not split the module any further but adding new dependencies is
97 // fine. Unlike all the other methods on TransitionMutator, this method is
98 // allowed to mutate the module.
Colin Cross90d7df92025-01-31 11:29:33 -080099 Mutate(ctx BottomUpMutatorContext, transitionInfo T)
100
101 // TransitionInfoFromVariation is called when adding dependencies with an explicit variation after the
102 // TransitionMutator has already run. It takes a variation name and returns a TransitionInfo for that
103 // variation. It may not be possible for some TransitionMutators to generate an appropriate TransitionInfo
104 // if the variation does not contain all the information from the TransitionInfo, in which case the
105 // TransitionMutator can panic in TransitionInfoFromVariation, and adding dependencies with explicit variations
106 // for this TransitionMutator is not supported.
107 TransitionInfoFromVariation(variation string) T
Colin Cross7e129d22024-12-03 14:26:00 -0800108}
109
Colin Crossc6f3f022025-01-31 11:16:26 -0800110// androidTransitionMutator is a copy of blueprint.TransitionMutator with the context argument types changed
111// from blueprint.BaseModuleContext to BaseModuleContext, etc.
112type androidTransitionMutator interface {
113 Split(ctx BaseModuleContext) []blueprint.TransitionInfo
114 OutgoingTransition(ctx OutgoingTransitionContext, sourceTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo
115 IncomingTransition(ctx IncomingTransitionContext, incomingTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo
116 Mutate(ctx BottomUpMutatorContext, transitionInfo blueprint.TransitionInfo)
117 TransitionInfoFromVariation(variation string) blueprint.TransitionInfo
118}
119
Colin Cross90d7df92025-01-31 11:29:33 -0800120// VariationTransitionMutator is a simpler version of androidTransitionMutator that passes variation strings instead
121// of a blueprint.TransitionInfo object.
122type VariationTransitionMutator interface {
123 Split(ctx BaseModuleContext) []string
124 OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string
125 IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string
126 Mutate(ctx BottomUpMutatorContext, variation string)
127}
128
Colin Cross7e129d22024-12-03 14:26:00 -0800129type IncomingTransitionContext interface {
130 ArchModuleContext
131 ModuleProviderContext
132 ModuleErrorContext
133
134 // Module returns the target of the dependency edge for which the transition
135 // is being computed
136 Module() Module
137
Colin Cross4e041522025-01-31 11:43:13 -0800138 // ModuleName returns the name of the module. This is generally the value that was returned by Module.Name() when
139 // the module was created, but may have been modified by calls to BottomUpMutatorContext.Rename.
140 ModuleName() string
141
142 // DepTag() Returns the dependency tag through which this dependency is
143 // reached
144 DepTag() blueprint.DependencyTag
145
Colin Cross7e129d22024-12-03 14:26:00 -0800146 // Config returns the configuration for the build.
147 Config() Config
148
149 DeviceConfig() DeviceConfig
150
151 // IsAddingDependency returns true if the transition is being called while adding a dependency
152 // after the transition mutator has already run, or false if it is being called when the transition
153 // mutator is running. This should be used sparingly, all uses will have to be removed in order
154 // to support creating variants on demand.
155 IsAddingDependency() bool
156}
157
158type OutgoingTransitionContext interface {
159 ArchModuleContext
160 ModuleProviderContext
161
162 // Module returns the target of the dependency edge for which the transition
163 // is being computed
164 Module() Module
165
Colin Cross4e041522025-01-31 11:43:13 -0800166 // ModuleName returns the name of the module. This is generally the value that was returned by Module.Name() when
167 // the module was created, but may have been modified by calls to BottomUpMutatorContext.Rename.
168 ModuleName() string
169
Colin Cross7e129d22024-12-03 14:26:00 -0800170 // DepTag() Returns the dependency tag through which this dependency is
171 // reached
172 DepTag() blueprint.DependencyTag
173
174 // Config returns the configuration for the build.
175 Config() Config
176
177 DeviceConfig() DeviceConfig
178}
179
Colin Crossc6f3f022025-01-31 11:16:26 -0800180// androidTransitionMutatorAdapter wraps an androidTransitionMutator to convert it to a blueprint.TransitionInfo
181// by converting the blueprint.*Context objects into android.*Context objects.
182type androidTransitionMutatorAdapter struct {
Colin Cross7e129d22024-12-03 14:26:00 -0800183 finalPhase bool
Colin Crossc6f3f022025-01-31 11:16:26 -0800184 mutator androidTransitionMutator
Colin Cross7e129d22024-12-03 14:26:00 -0800185 name string
186}
187
Colin Crossc6f3f022025-01-31 11:16:26 -0800188func (a *androidTransitionMutatorAdapter) Split(ctx blueprint.BaseModuleContext) []blueprint.TransitionInfo {
Colin Cross7e129d22024-12-03 14:26:00 -0800189 if a.finalPhase {
190 panic("TransitionMutator not allowed in FinalDepsMutators")
191 }
Colin Crossc6f3f022025-01-31 11:16:26 -0800192 m := ctx.Module().(Module)
193 moduleContext := m.base().baseModuleContextFactory(ctx)
194 return a.mutator.Split(&moduleContext)
Colin Cross7e129d22024-12-03 14:26:00 -0800195}
196
Colin Crossc6f3f022025-01-31 11:16:26 -0800197func (a *androidTransitionMutatorAdapter) OutgoingTransition(bpctx blueprint.OutgoingTransitionContext,
198 sourceTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo {
199 m := bpctx.Module().(Module)
200 ctx := outgoingTransitionContextPool.Get().(*outgoingTransitionContextImpl)
201 defer outgoingTransitionContextPool.Put(ctx)
202 *ctx = outgoingTransitionContextImpl{
203 archModuleContext: m.base().archModuleContextFactory(bpctx),
204 bp: bpctx,
Colin Cross7e129d22024-12-03 14:26:00 -0800205 }
Colin Crossc6f3f022025-01-31 11:16:26 -0800206 return a.mutator.OutgoingTransition(ctx, sourceTransitionInfo)
Colin Cross7e129d22024-12-03 14:26:00 -0800207}
208
Colin Crossc6f3f022025-01-31 11:16:26 -0800209func (a *androidTransitionMutatorAdapter) IncomingTransition(bpctx blueprint.IncomingTransitionContext,
210 incomingTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo {
211 m := bpctx.Module().(Module)
212 ctx := incomingTransitionContextPool.Get().(*incomingTransitionContextImpl)
213 defer incomingTransitionContextPool.Put(ctx)
214 *ctx = incomingTransitionContextImpl{
215 archModuleContext: m.base().archModuleContextFactory(bpctx),
216 bp: bpctx,
Colin Cross7e129d22024-12-03 14:26:00 -0800217 }
Colin Crossc6f3f022025-01-31 11:16:26 -0800218 return a.mutator.IncomingTransition(ctx, incomingTransitionInfo)
Colin Cross7e129d22024-12-03 14:26:00 -0800219}
220
Colin Crossc6f3f022025-01-31 11:16:26 -0800221func (a *androidTransitionMutatorAdapter) Mutate(ctx blueprint.BottomUpMutatorContext, transitionInfo blueprint.TransitionInfo) {
222 am := ctx.Module().(Module)
223 variation := transitionInfo.Variation()
224 if variation != "" {
225 // TODO: this should really be checking whether the TransitionMutator affected this module, not
226 // the empty variant, but TransitionMutator has no concept of skipping a module.
227 base := am.base()
228 base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, a.name)
229 base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variation)
Colin Cross7e129d22024-12-03 14:26:00 -0800230 }
Colin Crossc6f3f022025-01-31 11:16:26 -0800231
232 mctx := bottomUpMutatorContextFactory(ctx, am, a.finalPhase)
233 defer bottomUpMutatorContextPool.Put(mctx)
234 a.mutator.Mutate(mctx, transitionInfo)
Colin Cross7e129d22024-12-03 14:26:00 -0800235}
236
Colin Crossc6f3f022025-01-31 11:16:26 -0800237func (a *androidTransitionMutatorAdapter) TransitionInfoFromVariation(variation string) blueprint.TransitionInfo {
238 return a.mutator.TransitionInfoFromVariation(variation)
239}
240
Colin Cross90d7df92025-01-31 11:29:33 -0800241// variationTransitionMutatorAdapter wraps a VariationTransitionMutator to convert it to an androidTransitionMutator
242// by wrapping the string info object used by VariationTransitionMutator with variationTransitionInfo to convert it into
Colin Crossc6f3f022025-01-31 11:16:26 -0800243// blueprint.TransitionInfo.
244type variationTransitionMutatorAdapter struct {
Colin Cross90d7df92025-01-31 11:29:33 -0800245 m VariationTransitionMutator
Colin Crossc6f3f022025-01-31 11:16:26 -0800246}
247
248func (v variationTransitionMutatorAdapter) Split(ctx BaseModuleContext) []blueprint.TransitionInfo {
249 variations := v.m.Split(ctx)
250 transitionInfos := make([]blueprint.TransitionInfo, 0, len(variations))
251 for _, variation := range variations {
252 transitionInfos = append(transitionInfos, variationTransitionInfo{variation})
253 }
254 return transitionInfos
255}
256
257func (v variationTransitionMutatorAdapter) OutgoingTransition(ctx OutgoingTransitionContext,
258 sourceTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo {
259
260 sourceVariationTransitionInfo, _ := sourceTransitionInfo.(variationTransitionInfo)
261 outgoingVariation := v.m.OutgoingTransition(ctx, sourceVariationTransitionInfo.variation)
262 return variationTransitionInfo{outgoingVariation}
263}
264
265func (v variationTransitionMutatorAdapter) IncomingTransition(ctx IncomingTransitionContext,
266 incomingTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo {
267
268 incomingVariationTransitionInfo, _ := incomingTransitionInfo.(variationTransitionInfo)
269 variation := v.m.IncomingTransition(ctx, incomingVariationTransitionInfo.variation)
270 return variationTransitionInfo{variation}
271}
272
273func (v variationTransitionMutatorAdapter) Mutate(ctx BottomUpMutatorContext, transitionInfo blueprint.TransitionInfo) {
274 variationTransitionInfo, _ := transitionInfo.(variationTransitionInfo)
275 v.m.Mutate(ctx, variationTransitionInfo.variation)
276}
277
278func (v variationTransitionMutatorAdapter) TransitionInfoFromVariation(variation string) blueprint.TransitionInfo {
279 return variationTransitionInfo{variation}
280}
281
282// variationTransitionInfo is a blueprint.TransitionInfo that contains a single variation string.
283type variationTransitionInfo struct {
284 variation string
285}
286
287func (v variationTransitionInfo) Variation() string {
288 return v.variation
289}
290
Colin Cross90d7df92025-01-31 11:29:33 -0800291// genericTransitionMutatorAdapter wraps a TransitionMutator to convert it to an androidTransitionMutator
292type genericTransitionMutatorAdapter[T blueprint.TransitionInfo] struct {
293 m TransitionMutator[T]
294}
295
296// NewGenericTransitionMutatorAdapter is used to convert a generic TransitionMutator[T] into an androidTransitionMutator
297// that can be passed to RegisterMutatorsContext.InfoBasedTransition.
298func NewGenericTransitionMutatorAdapter[T blueprint.TransitionInfo](m TransitionMutator[T]) androidTransitionMutator {
299 return &genericTransitionMutatorAdapter[T]{m}
300}
301
302func (g *genericTransitionMutatorAdapter[T]) convertTransitionInfoToT(transitionInfo blueprint.TransitionInfo) T {
303 if transitionInfo == nil {
304 var zero T
305 return zero
306 }
307 return transitionInfo.(T)
308}
309
310func (g *genericTransitionMutatorAdapter[T]) Split(ctx BaseModuleContext) []blueprint.TransitionInfo {
311 transitionInfos := g.m.Split(ctx)
312 bpTransitionInfos := make([]blueprint.TransitionInfo, 0, len(transitionInfos))
313 for _, transitionInfo := range transitionInfos {
314 bpTransitionInfos = append(bpTransitionInfos, transitionInfo)
315 }
316 return bpTransitionInfos
317}
318
319func (g *genericTransitionMutatorAdapter[T]) OutgoingTransition(ctx OutgoingTransitionContext, sourceTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo {
320 sourceTransitionInfoT := g.convertTransitionInfoToT(sourceTransitionInfo)
321 return g.m.OutgoingTransition(ctx, sourceTransitionInfoT)
322}
323
324func (g *genericTransitionMutatorAdapter[T]) IncomingTransition(ctx IncomingTransitionContext, incomingTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo {
325 incomingTransitionInfoT := g.convertTransitionInfoToT(incomingTransitionInfo)
326 return g.m.IncomingTransition(ctx, incomingTransitionInfoT)
327}
328
329func (g *genericTransitionMutatorAdapter[T]) Mutate(ctx BottomUpMutatorContext, transitionInfo blueprint.TransitionInfo) {
330 transitionInfoT := g.convertTransitionInfoToT(transitionInfo)
331 g.m.Mutate(ctx, transitionInfoT)
332}
333
334func (g *genericTransitionMutatorAdapter[T]) TransitionInfoFromVariation(variation string) blueprint.TransitionInfo {
335 return g.m.TransitionInfoFromVariation(variation)
336}
337
Colin Crossc6f3f022025-01-31 11:16:26 -0800338// incomingTransitionContextImpl wraps a blueprint.IncomingTransitionContext to convert it to an
339// IncomingTransitionContext.
Colin Cross7e129d22024-12-03 14:26:00 -0800340type incomingTransitionContextImpl struct {
341 archModuleContext
342 bp blueprint.IncomingTransitionContext
343}
344
345func (c *incomingTransitionContextImpl) Module() Module {
346 return c.bp.Module().(Module)
347}
348
Colin Cross4e041522025-01-31 11:43:13 -0800349func (c *incomingTransitionContextImpl) ModuleName() string {
350 return c.bp.ModuleName()
351}
352
353func (c *incomingTransitionContextImpl) DepTag() blueprint.DependencyTag {
354 return c.bp.DepTag()
355}
356
Colin Cross7e129d22024-12-03 14:26:00 -0800357func (c *incomingTransitionContextImpl) Config() Config {
358 return c.bp.Config().(Config)
359}
360
361func (c *incomingTransitionContextImpl) DeviceConfig() DeviceConfig {
362 return DeviceConfig{c.bp.Config().(Config).deviceConfig}
363}
364
365func (c *incomingTransitionContextImpl) IsAddingDependency() bool {
366 return c.bp.IsAddingDependency()
367}
368
369func (c *incomingTransitionContextImpl) provider(provider blueprint.AnyProviderKey) (any, bool) {
370 return c.bp.Provider(provider)
371}
372
373func (c *incomingTransitionContextImpl) ModuleErrorf(fmt string, args ...interface{}) {
374 c.bp.ModuleErrorf(fmt, args)
375}
376
377func (c *incomingTransitionContextImpl) PropertyErrorf(property, fmt string, args ...interface{}) {
378 c.bp.PropertyErrorf(property, fmt, args)
379}
380
Colin Crossc6f3f022025-01-31 11:16:26 -0800381// outgoingTransitionContextImpl wraps a blueprint.OutgoingTransitionContext to convert it to an
382// OutgoingTransitionContext.
Colin Cross7e129d22024-12-03 14:26:00 -0800383type outgoingTransitionContextImpl struct {
384 archModuleContext
385 bp blueprint.OutgoingTransitionContext
386}
387
388func (c *outgoingTransitionContextImpl) Module() Module {
389 return c.bp.Module().(Module)
390}
391
Colin Cross4e041522025-01-31 11:43:13 -0800392func (c *outgoingTransitionContextImpl) ModuleName() string {
393 return c.bp.ModuleName()
394}
395
Colin Cross7e129d22024-12-03 14:26:00 -0800396func (c *outgoingTransitionContextImpl) DepTag() blueprint.DependencyTag {
397 return c.bp.DepTag()
398}
399
400func (c *outgoingTransitionContextImpl) Config() Config {
401 return c.bp.Config().(Config)
402}
403
404func (c *outgoingTransitionContextImpl) DeviceConfig() DeviceConfig {
405 return DeviceConfig{c.bp.Config().(Config).deviceConfig}
406}
407
408func (c *outgoingTransitionContextImpl) provider(provider blueprint.AnyProviderKey) (any, bool) {
409 return c.bp.Provider(provider)
410}