• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 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 (
18	"fmt"
19	"regexp"
20	"strings"
21
22	"github.com/google/blueprint"
23	"github.com/google/blueprint/proptools"
24)
25
26// BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns
27// a Config instead of an interface{}, and some methods have been wrapped to use an android.Module
28// instead of a blueprint.Module, plus some extra methods that return Android-specific information
29// about the current module.
30type BaseModuleContext interface {
31	ArchModuleContext
32	EarlyModuleContext
33
34	blueprintBaseModuleContext() blueprint.BaseModuleContext
35
36	// OtherModuleName returns the name of another Module.  See BaseModuleContext.ModuleName for more information.
37	// It is intended for use inside the visit functions of Visit* and WalkDeps.
38	OtherModuleName(m blueprint.Module) string
39
40	// OtherModuleDir returns the directory of another Module.  See BaseModuleContext.ModuleDir for more information.
41	// It is intended for use inside the visit functions of Visit* and WalkDeps.
42	OtherModuleDir(m blueprint.Module) string
43
44	// OtherModuleErrorf reports an error on another Module.  See BaseModuleContext.ModuleErrorf for more information.
45	// It is intended for use inside the visit functions of Visit* and WalkDeps.
46	OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{})
47
48	// OtherModuleDependencyTag returns the dependency tag used to depend on a module, or nil if there is no dependency
49	// on the module.  When called inside a Visit* method with current module being visited, and there are multiple
50	// dependencies on the module being visited, it returns the dependency tag used for the current dependency.
51	OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
52
53	// OtherModuleExists returns true if a module with the specified name exists, as determined by the NameInterface
54	// passed to Context.SetNameInterface, or SimpleNameInterface if it was not called.
55	OtherModuleExists(name string) bool
56
57	// OtherModuleDependencyVariantExists returns true if a module with the
58	// specified name and variant exists. The variant must match the given
59	// variations. It must also match all the non-local variations of the current
60	// module. In other words, it checks for the module that AddVariationDependencies
61	// would add a dependency on with the same arguments.
62	OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool
63
64	// OtherModuleFarDependencyVariantExists returns true if a module with the
65	// specified name and variant exists. The variant must match the given
66	// variations, but not the non-local variations of the current module. In
67	// other words, it checks for the module that AddFarVariationDependencies
68	// would add a dependency on with the same arguments.
69	OtherModuleFarDependencyVariantExists(variations []blueprint.Variation, name string) bool
70
71	// OtherModuleReverseDependencyVariantExists returns true if a module with the
72	// specified name exists with the same variations as the current module. In
73	// other words, it checks for the module that AddReverseDependency would add a
74	// dependency on with the same argument.
75	OtherModuleReverseDependencyVariantExists(name string) bool
76
77	// OtherModuleType returns the type of another Module.  See BaseModuleContext.ModuleType for more information.
78	// It is intended for use inside the visit functions of Visit* and WalkDeps.
79	OtherModuleType(m blueprint.Module) string
80
81	// otherModuleProvider returns the value for a provider for the given module.  If the value is
82	// not set it returns nil and false.  The value returned may be a deep copy of the value originally
83	// passed to SetProvider.
84	//
85	// This method shouldn't be used directly, prefer the type-safe android.OtherModuleProvider instead.
86	otherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
87
88	// Provider returns the value for a provider for the current module.  If the value is
89	// not set it returns nil and false.  It panics if called before the appropriate
90	// mutator or GenerateBuildActions pass for the provider.  The value returned may be a deep
91	// copy of the value originally passed to SetProvider.
92	//
93	// This method shouldn't be used directly, prefer the type-safe android.ModuleProvider instead.
94	provider(provider blueprint.AnyProviderKey) (any, bool)
95
96	// setProvider sets the value for a provider for the current module.  It panics if not called
97	// during the appropriate mutator or GenerateBuildActions pass for the provider, if the value
98	// is not of the appropriate type, or if the value has already been set.  The value should not
99	// be modified after being passed to SetProvider.
100	//
101	// This method shouldn't be used directly, prefer the type-safe android.SetProvider instead.
102	setProvider(provider blueprint.AnyProviderKey, value any)
103
104	GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module
105
106	// GetDirectDepWithTag returns the Module the direct dependency with the specified name, or nil if
107	// none exists.  It panics if the dependency does not have the specified tag.  It skips any
108	// dependencies that are not an android.Module.
109	GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module
110
111	// GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified
112	// name, or nil if none exists.  If there are multiple dependencies on the same module it returns
113	// the first DependencyTag.
114	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
115
116	// VisitDirectDepsBlueprint calls visit for each direct dependency.  If there are multiple
117	// direct dependencies on the same module visit will be called multiple times on that module
118	// and OtherModuleDependencyTag will return a different tag for each.
119	//
120	// The Module passed to the visit function should not be retained outside of the visit
121	// function, it may be invalidated by future mutators.
122	VisitDirectDepsBlueprint(visit func(blueprint.Module))
123
124	// VisitDirectDepsIgnoreBlueprint calls visit for each direct dependency.  If there are multiple
125	// direct dependencies on the same module visit will be called multiple times on that module
126	// and OtherModuleDependencyTag will return a different tag for each.  It silently ignores any
127	// dependencies that are not an android.Module.
128	//
129	// The Module passed to the visit function should not be retained outside of the visit
130	// function, it may be invalidated by future mutators.
131	VisitDirectDepsIgnoreBlueprint(visit func(Module))
132
133	// VisitDirectDeps calls visit for each direct dependency.  If there are multiple
134	// direct dependencies on the same module visit will be called multiple times on that module
135	// and OtherModuleDependencyTag will return a different tag for each.  It raises an error if any of the
136	// dependencies are not an android.Module.
137	//
138	// The Module passed to the visit function should not be retained outside of the visit
139	// function, it may be invalidated by future mutators.
140	VisitDirectDeps(visit func(Module))
141
142	VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module))
143
144	// VisitDirectDepsIf calls pred for each direct dependency, and if pred returns true calls visit.  If there are
145	// multiple direct dependencies on the same module pred and visit will be called multiple times on that module and
146	// OtherModuleDependencyTag will return a different tag for each.  It skips any
147	// dependencies that are not an android.Module.
148	//
149	// The Module passed to the visit function should not be retained outside of the visit function, it may be
150	// invalidated by future mutators.
151	VisitDirectDepsIf(pred func(Module) bool, visit func(Module))
152	// Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
153	VisitDepsDepthFirst(visit func(Module))
154	// Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
155	VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module))
156
157	// WalkDeps calls visit for each transitive dependency, traversing the dependency tree in top down order.  visit may
158	// be called multiple times for the same (child, parent) pair if there are multiple direct dependencies between the
159	// child and parent with different tags.  OtherModuleDependencyTag will return the tag for the currently visited
160	// (child, parent) pair.  If visit returns false WalkDeps will not continue recursing down to child.  It skips
161	// any dependencies that are not an android.Module.
162	//
163	// The Modules passed to the visit function should not be retained outside of the visit function, they may be
164	// invalidated by future mutators.
165	WalkDeps(visit func(child, parent Module) bool)
166
167	// WalkDepsBlueprint calls visit for each transitive dependency, traversing the dependency
168	// tree in top down order.  visit may be called multiple times for the same (child, parent)
169	// pair if there are multiple direct dependencies between the child and parent with different
170	// tags.  OtherModuleDependencyTag will return the tag for the currently visited
171	// (child, parent) pair.  If visit returns false WalkDeps will not continue recursing down
172	// to child.
173	//
174	// The Modules passed to the visit function should not be retained outside of the visit function, they may be
175	// invalidated by future mutators.
176	WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool)
177
178	// GetWalkPath is supposed to be called in visit function passed in WalkDeps()
179	// and returns a top-down dependency path from a start module to current child module.
180	GetWalkPath() []Module
181
182	// PrimaryModule returns the first variant of the current module.  Variants of a module are always visited in
183	// order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from the
184	// Module returned by PrimaryModule without data races.  This can be used to perform singleton actions that are
185	// only done once for all variants of a module.
186	PrimaryModule() Module
187
188	// FinalModule returns the last variant of the current module.  Variants of a module are always visited in
189	// order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from all
190	// variants using VisitAllModuleVariants if the current module == FinalModule().  This can be used to perform
191	// singleton actions that are only done once for all variants of a module.
192	FinalModule() Module
193
194	// VisitAllModuleVariants calls visit for each variant of the current module.  Variants of a module are always
195	// visited in order by mutators and GenerateBuildActions, so the data created by the current mutator can be read
196	// from all variants if the current module == FinalModule().  Otherwise, care must be taken to not access any
197	// data modified by the current mutator.
198	VisitAllModuleVariants(visit func(Module))
199
200	// GetTagPath is supposed to be called in visit function passed in WalkDeps()
201	// and returns a top-down dependency tags path from a start module to current child module.
202	// It has one less entry than GetWalkPath() as it contains the dependency tags that
203	// exist between each adjacent pair of modules in the GetWalkPath().
204	// GetTagPath()[i] is the tag between GetWalkPath()[i] and GetWalkPath()[i+1]
205	GetTagPath() []blueprint.DependencyTag
206
207	// GetPathString is supposed to be called in visit function passed in WalkDeps()
208	// and returns a multi-line string showing the modules and dependency tags
209	// among them along the top-down dependency path from a start module to current child module.
210	// skipFirst when set to true, the output doesn't include the start module,
211	// which is already printed when this function is used along with ModuleErrorf().
212	GetPathString(skipFirst bool) string
213
214	AddMissingDependencies(missingDeps []string)
215
216	// getMissingDependencies returns the list of missing dependencies.
217	// Calling this function prevents adding new dependencies.
218	getMissingDependencies() []string
219
220	// EvaluateConfiguration makes ModuleContext a valid proptools.ConfigurableEvaluator, so this context
221	// can be used to evaluate the final value of Configurable properties.
222	EvaluateConfiguration(condition proptools.ConfigurableCondition, property string) proptools.ConfigurableValue
223}
224
225type baseModuleContext struct {
226	bp blueprint.BaseModuleContext
227	earlyModuleContext
228	archModuleContext
229
230	walkPath []Module
231	tagPath  []blueprint.DependencyTag
232
233	strictVisitDeps bool // If true, enforce that all dependencies are enabled
234
235}
236
237func (b *baseModuleContext) OtherModuleName(m blueprint.Module) string {
238	return b.bp.OtherModuleName(m)
239}
240func (b *baseModuleContext) OtherModuleDir(m blueprint.Module) string { return b.bp.OtherModuleDir(m) }
241func (b *baseModuleContext) OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{}) {
242	b.bp.OtherModuleErrorf(m, fmt, args...)
243}
244func (b *baseModuleContext) OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag {
245	return b.bp.OtherModuleDependencyTag(m)
246}
247func (b *baseModuleContext) OtherModuleExists(name string) bool { return b.bp.OtherModuleExists(name) }
248func (b *baseModuleContext) OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool {
249	return b.bp.OtherModuleDependencyVariantExists(variations, name)
250}
251func (b *baseModuleContext) OtherModuleFarDependencyVariantExists(variations []blueprint.Variation, name string) bool {
252	return b.bp.OtherModuleFarDependencyVariantExists(variations, name)
253}
254func (b *baseModuleContext) OtherModuleReverseDependencyVariantExists(name string) bool {
255	return b.bp.OtherModuleReverseDependencyVariantExists(name)
256}
257func (b *baseModuleContext) OtherModuleType(m blueprint.Module) string {
258	return b.bp.OtherModuleType(m)
259}
260
261func (b *baseModuleContext) otherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
262	return b.bp.OtherModuleProvider(m, provider)
263}
264
265func (b *baseModuleContext) provider(provider blueprint.AnyProviderKey) (any, bool) {
266	return b.bp.Provider(provider)
267}
268
269func (b *baseModuleContext) setProvider(provider blueprint.AnyProviderKey, value any) {
270	b.bp.SetProvider(provider, value)
271}
272
273func (b *baseModuleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module {
274	return b.bp.GetDirectDepWithTag(name, tag)
275}
276
277func (b *baseModuleContext) blueprintBaseModuleContext() blueprint.BaseModuleContext {
278	return b.bp
279}
280
281func (b *baseModuleContext) AddMissingDependencies(deps []string) {
282	if deps != nil {
283		missingDeps := &b.Module().base().commonProperties.MissingDeps
284		*missingDeps = append(*missingDeps, deps...)
285		*missingDeps = FirstUniqueStrings(*missingDeps)
286	}
287}
288
289func (b *baseModuleContext) checkedMissingDeps() bool {
290	return b.Module().base().commonProperties.CheckedMissingDeps
291}
292
293func (b *baseModuleContext) getMissingDependencies() []string {
294	checked := &b.Module().base().commonProperties.CheckedMissingDeps
295	*checked = true
296	var missingDeps []string
297	missingDeps = append(missingDeps, b.Module().base().commonProperties.MissingDeps...)
298	missingDeps = append(missingDeps, b.bp.EarlyGetMissingDependencies()...)
299	missingDeps = FirstUniqueStrings(missingDeps)
300	return missingDeps
301}
302
303type AllowDisabledModuleDependency interface {
304	blueprint.DependencyTag
305	AllowDisabledModuleDependency(target Module) bool
306}
307
308type AlwaysAllowDisabledModuleDependencyTag struct{}
309
310func (t AlwaysAllowDisabledModuleDependencyTag) AllowDisabledModuleDependency(Module) bool {
311	return true
312}
313
314func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag blueprint.DependencyTag, strict bool, ignoreBlueprint bool) Module {
315	aModule, _ := module.(Module)
316
317	if !strict {
318		return aModule
319	}
320
321	if aModule == nil {
322		if !ignoreBlueprint {
323			b.ModuleErrorf("module %q (%#v) not an android module", b.OtherModuleName(module), tag)
324		}
325		return nil
326	}
327
328	if !aModule.Enabled(b) {
329		if t, ok := tag.(AllowDisabledModuleDependency); !ok || !t.AllowDisabledModuleDependency(aModule) {
330			if b.Config().AllowMissingDependencies() {
331				b.AddMissingDependencies([]string{b.OtherModuleName(aModule)})
332			} else {
333				b.ModuleErrorf("depends on disabled module %q", b.OtherModuleName(aModule))
334			}
335		}
336		return nil
337	}
338	return aModule
339}
340
341type dep struct {
342	mod blueprint.Module
343	tag blueprint.DependencyTag
344}
345
346func (b *baseModuleContext) getDirectDepsInternal(name string, tag blueprint.DependencyTag) []dep {
347	var deps []dep
348	b.VisitDirectDepsBlueprint(func(module blueprint.Module) {
349		if aModule, _ := module.(Module); aModule != nil {
350			if aModule.base().BaseModuleName() == name {
351				returnedTag := b.bp.OtherModuleDependencyTag(aModule)
352				if tag == nil || returnedTag == tag {
353					deps = append(deps, dep{aModule, returnedTag})
354				}
355			}
356		} else if b.bp.OtherModuleName(module) == name {
357			returnedTag := b.bp.OtherModuleDependencyTag(module)
358			if tag == nil || returnedTag == tag {
359				deps = append(deps, dep{module, returnedTag})
360			}
361		}
362	})
363	return deps
364}
365
366func (b *baseModuleContext) getDirectDepInternal(name string, tag blueprint.DependencyTag) (blueprint.Module, blueprint.DependencyTag) {
367	deps := b.getDirectDepsInternal(name, tag)
368	if len(deps) == 1 {
369		return deps[0].mod, deps[0].tag
370	} else if len(deps) >= 2 {
371		panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q",
372			name, b.ModuleName()))
373	} else {
374		return nil, nil
375	}
376}
377
378func (b *baseModuleContext) getDirectDepFirstTag(name string) (blueprint.Module, blueprint.DependencyTag) {
379	foundDeps := b.getDirectDepsInternal(name, nil)
380	deps := map[blueprint.Module]bool{}
381	for _, dep := range foundDeps {
382		deps[dep.mod] = true
383	}
384	if len(deps) == 1 {
385		return foundDeps[0].mod, foundDeps[0].tag
386	} else if len(deps) >= 2 {
387		// this could happen if two dependencies have the same name in different namespaces
388		// TODO(b/186554727): this should not occur if namespaces are handled within
389		// getDirectDepsInternal.
390		panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q",
391			name, b.ModuleName()))
392	} else {
393		return nil, nil
394	}
395}
396
397func (b *baseModuleContext) GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module {
398	var deps []Module
399	b.VisitDirectDepsBlueprint(func(module blueprint.Module) {
400		if aModule, _ := module.(Module); aModule != nil {
401			if b.bp.OtherModuleDependencyTag(aModule) == tag {
402				deps = append(deps, aModule)
403			}
404		}
405	})
406	return deps
407}
408
409// GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified
410// name, or nil if none exists. If there are multiple dependencies on the same module it returns the
411// first DependencyTag.
412func (b *baseModuleContext) GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) {
413	return b.getDirectDepFirstTag(name)
414}
415
416func (b *baseModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module)) {
417	b.bp.VisitDirectDeps(visit)
418}
419
420func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) {
421	b.visitDirectDeps(visit, false)
422}
423
424func (b *baseModuleContext) VisitDirectDepsIgnoreBlueprint(visit func(Module)) {
425	b.visitDirectDeps(visit, true)
426}
427
428func (b *baseModuleContext) visitDirectDeps(visit func(Module), ignoreBlueprint bool) {
429	b.bp.VisitDirectDeps(func(module blueprint.Module) {
430		if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, ignoreBlueprint); aModule != nil {
431			visit(aModule)
432		}
433	})
434}
435
436func (b *baseModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) {
437	b.bp.VisitDirectDeps(func(module blueprint.Module) {
438		if b.bp.OtherModuleDependencyTag(module) == tag {
439			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil {
440				visit(aModule)
441			}
442		}
443	})
444}
445
446func (b *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) {
447	b.bp.VisitDirectDepsIf(
448		// pred
449		func(module blueprint.Module) bool {
450			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil {
451				return pred(aModule)
452			} else {
453				return false
454			}
455		},
456		// visit
457		func(module blueprint.Module) {
458			visit(module.(Module))
459		})
460}
461
462func (b *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) {
463	b.bp.VisitDepsDepthFirst(func(module blueprint.Module) {
464		if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil {
465			visit(aModule)
466		}
467	})
468}
469
470func (b *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) {
471	b.bp.VisitDepsDepthFirstIf(
472		// pred
473		func(module blueprint.Module) bool {
474			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil {
475				return pred(aModule)
476			} else {
477				return false
478			}
479		},
480		// visit
481		func(module blueprint.Module) {
482			visit(module.(Module))
483		})
484}
485
486func (b *baseModuleContext) WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) {
487	b.bp.WalkDeps(visit)
488}
489
490func (b *baseModuleContext) WalkDeps(visit func(Module, Module) bool) {
491	b.walkPath = []Module{b.Module()}
492	b.tagPath = []blueprint.DependencyTag{}
493	b.bp.WalkDeps(func(child, parent blueprint.Module) bool {
494		childAndroidModule, _ := child.(Module)
495		parentAndroidModule, _ := parent.(Module)
496		if childAndroidModule != nil && parentAndroidModule != nil {
497			// record walkPath before visit
498			for b.walkPath[len(b.walkPath)-1] != parentAndroidModule {
499				b.walkPath = b.walkPath[0 : len(b.walkPath)-1]
500				b.tagPath = b.tagPath[0 : len(b.tagPath)-1]
501			}
502			b.walkPath = append(b.walkPath, childAndroidModule)
503			b.tagPath = append(b.tagPath, b.OtherModuleDependencyTag(childAndroidModule))
504			return visit(childAndroidModule, parentAndroidModule)
505		} else {
506			return false
507		}
508	})
509}
510
511func (b *baseModuleContext) GetWalkPath() []Module {
512	return b.walkPath
513}
514
515func (b *baseModuleContext) GetTagPath() []blueprint.DependencyTag {
516	return b.tagPath
517}
518
519func (b *baseModuleContext) VisitAllModuleVariants(visit func(Module)) {
520	b.bp.VisitAllModuleVariants(func(module blueprint.Module) {
521		visit(module.(Module))
522	})
523}
524
525func (b *baseModuleContext) PrimaryModule() Module {
526	return b.bp.PrimaryModule().(Module)
527}
528
529func (b *baseModuleContext) FinalModule() Module {
530	return b.bp.FinalModule().(Module)
531}
532
533// IsMetaDependencyTag returns true for cross-cutting metadata dependencies.
534func IsMetaDependencyTag(tag blueprint.DependencyTag) bool {
535	if tag == licenseKindTag {
536		return true
537	} else if tag == licensesTag {
538		return true
539	} else if tag == AcDepTag {
540		return true
541	}
542	return false
543}
544
545// A regexp for removing boilerplate from BaseDependencyTag from the string representation of
546// a dependency tag.
547var tagCleaner = regexp.MustCompile(`\QBaseDependencyTag:{}\E(, )?`)
548
549// PrettyPrintTag returns string representation of the tag, but prefers
550// custom String() method if available.
551func PrettyPrintTag(tag blueprint.DependencyTag) string {
552	// Use tag's custom String() method if available.
553	if stringer, ok := tag.(fmt.Stringer); ok {
554		return stringer.String()
555	}
556
557	// Otherwise, get a default string representation of the tag's struct.
558	tagString := fmt.Sprintf("%T: %+v", tag, tag)
559
560	// Remove the boilerplate from BaseDependencyTag as it adds no value.
561	tagString = tagCleaner.ReplaceAllString(tagString, "")
562	return tagString
563}
564
565func (b *baseModuleContext) GetPathString(skipFirst bool) string {
566	sb := strings.Builder{}
567	tagPath := b.GetTagPath()
568	walkPath := b.GetWalkPath()
569	if !skipFirst {
570		sb.WriteString(walkPath[0].String())
571	}
572	for i, m := range walkPath[1:] {
573		sb.WriteString("\n")
574		sb.WriteString(fmt.Sprintf("           via tag %s\n", PrettyPrintTag(tagPath[i])))
575		sb.WriteString(fmt.Sprintf("    -> %s", m.String()))
576	}
577	return sb.String()
578}
579
580func (m *baseModuleContext) EvaluateConfiguration(condition proptools.ConfigurableCondition, property string) proptools.ConfigurableValue {
581	return m.Module().ConfigurableEvaluator(m).EvaluateConfiguration(condition, property)
582}
583