blob: 30dbbc3c43e23f7a732f51eb5b2dc241cab32ffe [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.
76type TransitionMutator interface {
77 // 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.
82 Split(ctx BaseModuleContext) []string
83
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.
87 OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string
88
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.
93 IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string
94
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.
99 Mutate(ctx BottomUpMutatorContext, variation string)
100}
101
Colin Crossc6f3f022025-01-31 11:16:26 -0800102// androidTransitionMutator is a copy of blueprint.TransitionMutator with the context argument types changed
103// from blueprint.BaseModuleContext to BaseModuleContext, etc.
104type androidTransitionMutator interface {
105 Split(ctx BaseModuleContext) []blueprint.TransitionInfo
106 OutgoingTransition(ctx OutgoingTransitionContext, sourceTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo
107 IncomingTransition(ctx IncomingTransitionContext, incomingTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo
108 Mutate(ctx BottomUpMutatorContext, transitionInfo blueprint.TransitionInfo)
109 TransitionInfoFromVariation(variation string) blueprint.TransitionInfo
110}
111
Colin Cross7e129d22024-12-03 14:26:00 -0800112type IncomingTransitionContext interface {
113 ArchModuleContext
114 ModuleProviderContext
115 ModuleErrorContext
116
117 // Module returns the target of the dependency edge for which the transition
118 // is being computed
119 Module() Module
120
Colin Cross4e041522025-01-31 11:43:13 -0800121 // 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() blueprint.DependencyTag
128
Colin Cross7e129d22024-12-03 14:26:00 -0800129 // Config returns the configuration for the build.
130 Config() Config
131
132 DeviceConfig() DeviceConfig
133
134 // IsAddingDependency returns true if the transition is being called while adding a dependency
135 // after the transition mutator has already run, or false if it is being called when the transition
136 // mutator is running. This should be used sparingly, all uses will have to be removed in order
137 // to support creating variants on demand.
138 IsAddingDependency() bool
139}
140
141type OutgoingTransitionContext interface {
142 ArchModuleContext
143 ModuleProviderContext
144
145 // Module returns the target of the dependency edge for which the transition
146 // is being computed
147 Module() Module
148
Colin Cross4e041522025-01-31 11:43:13 -0800149 // ModuleName returns the name of the module. This is generally the value that was returned by Module.Name() when
150 // the module was created, but may have been modified by calls to BottomUpMutatorContext.Rename.
151 ModuleName() string
152
Colin Cross7e129d22024-12-03 14:26:00 -0800153 // DepTag() Returns the dependency tag through which this dependency is
154 // reached
155 DepTag() blueprint.DependencyTag
156
157 // Config returns the configuration for the build.
158 Config() Config
159
160 DeviceConfig() DeviceConfig
161}
162
Colin Crossc6f3f022025-01-31 11:16:26 -0800163// androidTransitionMutatorAdapter wraps an androidTransitionMutator to convert it to a blueprint.TransitionInfo
164// by converting the blueprint.*Context objects into android.*Context objects.
165type androidTransitionMutatorAdapter struct {
Colin Cross7e129d22024-12-03 14:26:00 -0800166 finalPhase bool
Colin Crossc6f3f022025-01-31 11:16:26 -0800167 mutator androidTransitionMutator
Colin Cross7e129d22024-12-03 14:26:00 -0800168 name string
169}
170
Colin Crossc6f3f022025-01-31 11:16:26 -0800171func (a *androidTransitionMutatorAdapter) Split(ctx blueprint.BaseModuleContext) []blueprint.TransitionInfo {
Colin Cross7e129d22024-12-03 14:26:00 -0800172 if a.finalPhase {
173 panic("TransitionMutator not allowed in FinalDepsMutators")
174 }
Colin Crossc6f3f022025-01-31 11:16:26 -0800175 m := ctx.Module().(Module)
176 moduleContext := m.base().baseModuleContextFactory(ctx)
177 return a.mutator.Split(&moduleContext)
Colin Cross7e129d22024-12-03 14:26:00 -0800178}
179
Colin Crossc6f3f022025-01-31 11:16:26 -0800180func (a *androidTransitionMutatorAdapter) OutgoingTransition(bpctx blueprint.OutgoingTransitionContext,
181 sourceTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo {
182 m := bpctx.Module().(Module)
183 ctx := outgoingTransitionContextPool.Get().(*outgoingTransitionContextImpl)
184 defer outgoingTransitionContextPool.Put(ctx)
185 *ctx = outgoingTransitionContextImpl{
186 archModuleContext: m.base().archModuleContextFactory(bpctx),
187 bp: bpctx,
Colin Cross7e129d22024-12-03 14:26:00 -0800188 }
Colin Crossc6f3f022025-01-31 11:16:26 -0800189 return a.mutator.OutgoingTransition(ctx, sourceTransitionInfo)
Colin Cross7e129d22024-12-03 14:26:00 -0800190}
191
Colin Crossc6f3f022025-01-31 11:16:26 -0800192func (a *androidTransitionMutatorAdapter) IncomingTransition(bpctx blueprint.IncomingTransitionContext,
193 incomingTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo {
194 m := bpctx.Module().(Module)
195 ctx := incomingTransitionContextPool.Get().(*incomingTransitionContextImpl)
196 defer incomingTransitionContextPool.Put(ctx)
197 *ctx = incomingTransitionContextImpl{
198 archModuleContext: m.base().archModuleContextFactory(bpctx),
199 bp: bpctx,
Colin Cross7e129d22024-12-03 14:26:00 -0800200 }
Colin Crossc6f3f022025-01-31 11:16:26 -0800201 return a.mutator.IncomingTransition(ctx, incomingTransitionInfo)
Colin Cross7e129d22024-12-03 14:26:00 -0800202}
203
Colin Crossc6f3f022025-01-31 11:16:26 -0800204func (a *androidTransitionMutatorAdapter) Mutate(ctx blueprint.BottomUpMutatorContext, transitionInfo blueprint.TransitionInfo) {
205 am := ctx.Module().(Module)
206 variation := transitionInfo.Variation()
207 if variation != "" {
208 // TODO: this should really be checking whether the TransitionMutator affected this module, not
209 // the empty variant, but TransitionMutator has no concept of skipping a module.
210 base := am.base()
211 base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, a.name)
212 base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variation)
Colin Cross7e129d22024-12-03 14:26:00 -0800213 }
Colin Crossc6f3f022025-01-31 11:16:26 -0800214
215 mctx := bottomUpMutatorContextFactory(ctx, am, a.finalPhase)
216 defer bottomUpMutatorContextPool.Put(mctx)
217 a.mutator.Mutate(mctx, transitionInfo)
Colin Cross7e129d22024-12-03 14:26:00 -0800218}
219
Colin Crossc6f3f022025-01-31 11:16:26 -0800220func (a *androidTransitionMutatorAdapter) TransitionInfoFromVariation(variation string) blueprint.TransitionInfo {
221 return a.mutator.TransitionInfoFromVariation(variation)
222}
223
224// variationTransitionMutatorAdapter wraps a TransitionMutator to convert it to an androidTransitionMutator
225// by wrapping the string info object used by TransitionMutator with variationTransitionInfo to convert it into
226// blueprint.TransitionInfo.
227type variationTransitionMutatorAdapter struct {
228 m TransitionMutator
229}
230
231func (v variationTransitionMutatorAdapter) Split(ctx BaseModuleContext) []blueprint.TransitionInfo {
232 variations := v.m.Split(ctx)
233 transitionInfos := make([]blueprint.TransitionInfo, 0, len(variations))
234 for _, variation := range variations {
235 transitionInfos = append(transitionInfos, variationTransitionInfo{variation})
236 }
237 return transitionInfos
238}
239
240func (v variationTransitionMutatorAdapter) OutgoingTransition(ctx OutgoingTransitionContext,
241 sourceTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo {
242
243 sourceVariationTransitionInfo, _ := sourceTransitionInfo.(variationTransitionInfo)
244 outgoingVariation := v.m.OutgoingTransition(ctx, sourceVariationTransitionInfo.variation)
245 return variationTransitionInfo{outgoingVariation}
246}
247
248func (v variationTransitionMutatorAdapter) IncomingTransition(ctx IncomingTransitionContext,
249 incomingTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo {
250
251 incomingVariationTransitionInfo, _ := incomingTransitionInfo.(variationTransitionInfo)
252 variation := v.m.IncomingTransition(ctx, incomingVariationTransitionInfo.variation)
253 return variationTransitionInfo{variation}
254}
255
256func (v variationTransitionMutatorAdapter) Mutate(ctx BottomUpMutatorContext, transitionInfo blueprint.TransitionInfo) {
257 variationTransitionInfo, _ := transitionInfo.(variationTransitionInfo)
258 v.m.Mutate(ctx, variationTransitionInfo.variation)
259}
260
261func (v variationTransitionMutatorAdapter) TransitionInfoFromVariation(variation string) blueprint.TransitionInfo {
262 return variationTransitionInfo{variation}
263}
264
265// variationTransitionInfo is a blueprint.TransitionInfo that contains a single variation string.
266type variationTransitionInfo struct {
267 variation string
268}
269
270func (v variationTransitionInfo) Variation() string {
271 return v.variation
272}
273
274// incomingTransitionContextImpl wraps a blueprint.IncomingTransitionContext to convert it to an
275// IncomingTransitionContext.
Colin Cross7e129d22024-12-03 14:26:00 -0800276type incomingTransitionContextImpl struct {
277 archModuleContext
278 bp blueprint.IncomingTransitionContext
279}
280
281func (c *incomingTransitionContextImpl) Module() Module {
282 return c.bp.Module().(Module)
283}
284
Colin Cross4e041522025-01-31 11:43:13 -0800285func (c *incomingTransitionContextImpl) ModuleName() string {
286 return c.bp.ModuleName()
287}
288
289func (c *incomingTransitionContextImpl) DepTag() blueprint.DependencyTag {
290 return c.bp.DepTag()
291}
292
Colin Cross7e129d22024-12-03 14:26:00 -0800293func (c *incomingTransitionContextImpl) Config() Config {
294 return c.bp.Config().(Config)
295}
296
297func (c *incomingTransitionContextImpl) DeviceConfig() DeviceConfig {
298 return DeviceConfig{c.bp.Config().(Config).deviceConfig}
299}
300
301func (c *incomingTransitionContextImpl) IsAddingDependency() bool {
302 return c.bp.IsAddingDependency()
303}
304
305func (c *incomingTransitionContextImpl) provider(provider blueprint.AnyProviderKey) (any, bool) {
306 return c.bp.Provider(provider)
307}
308
309func (c *incomingTransitionContextImpl) ModuleErrorf(fmt string, args ...interface{}) {
310 c.bp.ModuleErrorf(fmt, args)
311}
312
313func (c *incomingTransitionContextImpl) PropertyErrorf(property, fmt string, args ...interface{}) {
314 c.bp.PropertyErrorf(property, fmt, args)
315}
316
Colin Crossc6f3f022025-01-31 11:16:26 -0800317// outgoingTransitionContextImpl wraps a blueprint.OutgoingTransitionContext to convert it to an
318// OutgoingTransitionContext.
Colin Cross7e129d22024-12-03 14:26:00 -0800319type outgoingTransitionContextImpl struct {
320 archModuleContext
321 bp blueprint.OutgoingTransitionContext
322}
323
324func (c *outgoingTransitionContextImpl) Module() Module {
325 return c.bp.Module().(Module)
326}
327
Colin Cross4e041522025-01-31 11:43:13 -0800328func (c *outgoingTransitionContextImpl) ModuleName() string {
329 return c.bp.ModuleName()
330}
331
Colin Cross7e129d22024-12-03 14:26:00 -0800332func (c *outgoingTransitionContextImpl) DepTag() blueprint.DependencyTag {
333 return c.bp.DepTag()
334}
335
336func (c *outgoingTransitionContextImpl) Config() Config {
337 return c.bp.Config().(Config)
338}
339
340func (c *outgoingTransitionContextImpl) DeviceConfig() DeviceConfig {
341 return DeviceConfig{c.bp.Config().(Config).deviceConfig}
342}
343
344func (c *outgoingTransitionContextImpl) provider(provider blueprint.AnyProviderKey) (any, bool) {
345 return c.bp.Provider(provider)
346}