• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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