| // Copyright 2015 Google Inc. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package android |
| |
| import ( |
| "fmt" |
| "github.com/google/blueprint" |
| "regexp" |
| "strings" |
| ) |
| |
| // BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns |
| // a Config instead of an interface{}, and some methods have been wrapped to use an android.Module |
| // instead of a blueprint.Module, plus some extra methods that return Android-specific information |
| // about the current module. |
| type BaseModuleContext interface { |
| ArchModuleContext |
| EarlyModuleContext |
| |
| blueprintBaseModuleContext() blueprint.BaseModuleContext |
| |
| // OtherModuleName returns the name of another Module. See BaseModuleContext.ModuleName for more information. |
| // It is intended for use inside the visit functions of Visit* and WalkDeps. |
| OtherModuleName(m blueprint.Module) string |
| |
| // OtherModuleDir returns the directory of another Module. See BaseModuleContext.ModuleDir for more information. |
| // It is intended for use inside the visit functions of Visit* and WalkDeps. |
| OtherModuleDir(m blueprint.Module) string |
| |
| // OtherModuleErrorf reports an error on another Module. See BaseModuleContext.ModuleErrorf for more information. |
| // It is intended for use inside the visit functions of Visit* and WalkDeps. |
| OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{}) |
| |
| // OtherModuleDependencyTag returns the dependency tag used to depend on a module, or nil if there is no dependency |
| // on the module. When called inside a Visit* method with current module being visited, and there are multiple |
| // dependencies on the module being visited, it returns the dependency tag used for the current dependency. |
| OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag |
| |
| // OtherModuleExists returns true if a module with the specified name exists, as determined by the NameInterface |
| // passed to Context.SetNameInterface, or SimpleNameInterface if it was not called. |
| OtherModuleExists(name string) bool |
| |
| // OtherModuleDependencyVariantExists returns true if a module with the |
| // specified name and variant exists. The variant must match the given |
| // variations. It must also match all the non-local variations of the current |
| // module. In other words, it checks for the module that AddVariationDependencies |
| // would add a dependency on with the same arguments. |
| OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool |
| |
| // OtherModuleFarDependencyVariantExists returns true if a module with the |
| // specified name and variant exists. The variant must match the given |
| // variations, but not the non-local variations of the current module. In |
| // other words, it checks for the module that AddFarVariationDependencies |
| // would add a dependency on with the same arguments. |
| OtherModuleFarDependencyVariantExists(variations []blueprint.Variation, name string) bool |
| |
| // OtherModuleReverseDependencyVariantExists returns true if a module with the |
| // specified name exists with the same variations as the current module. In |
| // other words, it checks for the module that AddReverseDependency would add a |
| // dependency on with the same argument. |
| OtherModuleReverseDependencyVariantExists(name string) bool |
| |
| // OtherModuleType returns the type of another Module. See BaseModuleContext.ModuleType for more information. |
| // It is intended for use inside the visit functions of Visit* and WalkDeps. |
| OtherModuleType(m blueprint.Module) string |
| |
| // otherModuleProvider returns the value for a provider for the given module. If the value is |
| // not set it returns nil and false. The value returned may be a deep copy of the value originally |
| // passed to SetProvider. |
| // |
| // This method shouldn't be used directly, prefer the type-safe android.OtherModuleProvider instead. |
| otherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) |
| |
| // Provider returns the value for a provider for the current module. If the value is |
| // not set it returns nil and false. It panics if called before the appropriate |
| // mutator or GenerateBuildActions pass for the provider. The value returned may be a deep |
| // copy of the value originally passed to SetProvider. |
| // |
| // This method shouldn't be used directly, prefer the type-safe android.ModuleProvider instead. |
| provider(provider blueprint.AnyProviderKey) (any, bool) |
| |
| // setProvider sets the value for a provider for the current module. It panics if not called |
| // during the appropriate mutator or GenerateBuildActions pass for the provider, if the value |
| // is not of the appropriate type, or if the value has already been set. The value should not |
| // be modified after being passed to SetProvider. |
| // |
| // This method shouldn't be used directly, prefer the type-safe android.SetProvider instead. |
| setProvider(provider blueprint.AnyProviderKey, value any) |
| |
| GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module |
| |
| // GetDirectDepWithTag returns the Module the direct dependency with the specified name, or nil if |
| // none exists. It panics if the dependency does not have the specified tag. It skips any |
| // dependencies that are not an android.Module. |
| GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module |
| |
| // GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified |
| // name, or nil if none exists. If there are multiple dependencies on the same module it returns |
| // the first DependencyTag. |
| GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) |
| |
| // VisitDirectDepsBlueprint calls visit for each direct dependency. If there are multiple |
| // direct dependencies on the same module visit will be called multiple times on that module |
| // and OtherModuleDependencyTag will return a different tag for each. |
| // |
| // The Module passed to the visit function should not be retained outside of the visit |
| // function, it may be invalidated by future mutators. |
| VisitDirectDepsBlueprint(visit func(blueprint.Module)) |
| |
| // VisitDirectDepsIgnoreBlueprint calls visit for each direct dependency. If there are multiple |
| // direct dependencies on the same module visit will be called multiple times on that module |
| // and OtherModuleDependencyTag will return a different tag for each. It silently ignores any |
| // dependencies that are not an android.Module. |
| // |
| // The Module passed to the visit function should not be retained outside of the visit |
| // function, it may be invalidated by future mutators. |
| VisitDirectDepsIgnoreBlueprint(visit func(Module)) |
| |
| // VisitDirectDeps calls visit for each direct dependency. If there are multiple |
| // direct dependencies on the same module visit will be called multiple times on that module |
| // and OtherModuleDependencyTag will return a different tag for each. It raises an error if any of the |
| // dependencies are not an android.Module. |
| // |
| // The Module passed to the visit function should not be retained outside of the visit |
| // function, it may be invalidated by future mutators. |
| VisitDirectDeps(visit func(Module)) |
| |
| VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) |
| |
| // VisitDirectDepsIf calls pred for each direct dependency, and if pred returns true calls visit. If there are |
| // multiple direct dependencies on the same module pred and visit will be called multiple times on that module and |
| // OtherModuleDependencyTag will return a different tag for each. It skips any |
| // dependencies that are not an android.Module. |
| // |
| // The Module passed to the visit function should not be retained outside of the visit function, it may be |
| // invalidated by future mutators. |
| VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) |
| // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module |
| VisitDepsDepthFirst(visit func(Module)) |
| // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module |
| VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) |
| |
| // WalkDeps calls visit for each transitive dependency, traversing the dependency tree in top down order. visit may |
| // be called multiple times for the same (child, parent) pair if there are multiple direct dependencies between the |
| // child and parent with different tags. OtherModuleDependencyTag will return the tag for the currently visited |
| // (child, parent) pair. If visit returns false WalkDeps will not continue recursing down to child. It skips |
| // any dependencies that are not an android.Module. |
| // |
| // The Modules passed to the visit function should not be retained outside of the visit function, they may be |
| // invalidated by future mutators. |
| WalkDeps(visit func(child, parent Module) bool) |
| |
| // WalkDepsBlueprint calls visit for each transitive dependency, traversing the dependency |
| // tree in top down order. visit may be called multiple times for the same (child, parent) |
| // pair if there are multiple direct dependencies between the child and parent with different |
| // tags. OtherModuleDependencyTag will return the tag for the currently visited |
| // (child, parent) pair. If visit returns false WalkDeps will not continue recursing down |
| // to child. |
| // |
| // The Modules passed to the visit function should not be retained outside of the visit function, they may be |
| // invalidated by future mutators. |
| WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) |
| |
| // GetWalkPath is supposed to be called in visit function passed in WalkDeps() |
| // and returns a top-down dependency path from a start module to current child module. |
| GetWalkPath() []Module |
| |
| // PrimaryModule returns the first variant of the current module. Variants of a module are always visited in |
| // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from the |
| // Module returned by PrimaryModule without data races. This can be used to perform singleton actions that are |
| // only done once for all variants of a module. |
| PrimaryModule() Module |
| |
| // FinalModule returns the last variant of the current module. Variants of a module are always visited in |
| // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from all |
| // variants using VisitAllModuleVariants if the current module == FinalModule(). This can be used to perform |
| // singleton actions that are only done once for all variants of a module. |
| FinalModule() Module |
| |
| // VisitAllModuleVariants calls visit for each variant of the current module. Variants of a module are always |
| // visited in order by mutators and GenerateBuildActions, so the data created by the current mutator can be read |
| // from all variants if the current module == FinalModule(). Otherwise, care must be taken to not access any |
| // data modified by the current mutator. |
| VisitAllModuleVariants(visit func(Module)) |
| |
| // GetTagPath is supposed to be called in visit function passed in WalkDeps() |
| // and returns a top-down dependency tags path from a start module to current child module. |
| // It has one less entry than GetWalkPath() as it contains the dependency tags that |
| // exist between each adjacent pair of modules in the GetWalkPath(). |
| // GetTagPath()[i] is the tag between GetWalkPath()[i] and GetWalkPath()[i+1] |
| GetTagPath() []blueprint.DependencyTag |
| |
| // GetPathString is supposed to be called in visit function passed in WalkDeps() |
| // and returns a multi-line string showing the modules and dependency tags |
| // among them along the top-down dependency path from a start module to current child module. |
| // skipFirst when set to true, the output doesn't include the start module, |
| // which is already printed when this function is used along with ModuleErrorf(). |
| GetPathString(skipFirst bool) string |
| |
| AddMissingDependencies(missingDeps []string) |
| |
| // getMissingDependencies returns the list of missing dependencies. |
| // Calling this function prevents adding new dependencies. |
| getMissingDependencies() []string |
| } |
| |
| type baseModuleContext struct { |
| bp blueprint.BaseModuleContext |
| earlyModuleContext |
| archModuleContext |
| |
| walkPath []Module |
| tagPath []blueprint.DependencyTag |
| |
| strictVisitDeps bool // If true, enforce that all dependencies are enabled |
| |
| } |
| |
| func (b *baseModuleContext) OtherModuleName(m blueprint.Module) string { |
| return b.bp.OtherModuleName(m) |
| } |
| func (b *baseModuleContext) OtherModuleDir(m blueprint.Module) string { return b.bp.OtherModuleDir(m) } |
| func (b *baseModuleContext) OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{}) { |
| b.bp.OtherModuleErrorf(m, fmt, args...) |
| } |
| func (b *baseModuleContext) OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag { |
| return b.bp.OtherModuleDependencyTag(m) |
| } |
| func (b *baseModuleContext) OtherModuleExists(name string) bool { return b.bp.OtherModuleExists(name) } |
| func (b *baseModuleContext) OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool { |
| return b.bp.OtherModuleDependencyVariantExists(variations, name) |
| } |
| func (b *baseModuleContext) OtherModuleFarDependencyVariantExists(variations []blueprint.Variation, name string) bool { |
| return b.bp.OtherModuleFarDependencyVariantExists(variations, name) |
| } |
| func (b *baseModuleContext) OtherModuleReverseDependencyVariantExists(name string) bool { |
| return b.bp.OtherModuleReverseDependencyVariantExists(name) |
| } |
| func (b *baseModuleContext) OtherModuleType(m blueprint.Module) string { |
| return b.bp.OtherModuleType(m) |
| } |
| |
| func (b *baseModuleContext) otherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) { |
| return b.bp.OtherModuleProvider(m, provider) |
| } |
| |
| func (b *baseModuleContext) provider(provider blueprint.AnyProviderKey) (any, bool) { |
| return b.bp.Provider(provider) |
| } |
| |
| func (b *baseModuleContext) setProvider(provider blueprint.AnyProviderKey, value any) { |
| b.bp.SetProvider(provider, value) |
| } |
| |
| func (b *baseModuleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module { |
| return b.bp.GetDirectDepWithTag(name, tag) |
| } |
| |
| func (b *baseModuleContext) blueprintBaseModuleContext() blueprint.BaseModuleContext { |
| return b.bp |
| } |
| |
| func (b *baseModuleContext) AddMissingDependencies(deps []string) { |
| if deps != nil { |
| missingDeps := &b.Module().base().commonProperties.MissingDeps |
| *missingDeps = append(*missingDeps, deps...) |
| *missingDeps = FirstUniqueStrings(*missingDeps) |
| } |
| } |
| |
| func (b *baseModuleContext) checkedMissingDeps() bool { |
| return b.Module().base().commonProperties.CheckedMissingDeps |
| } |
| |
| func (b *baseModuleContext) getMissingDependencies() []string { |
| checked := &b.Module().base().commonProperties.CheckedMissingDeps |
| *checked = true |
| var missingDeps []string |
| missingDeps = append(missingDeps, b.Module().base().commonProperties.MissingDeps...) |
| missingDeps = append(missingDeps, b.bp.EarlyGetMissingDependencies()...) |
| missingDeps = FirstUniqueStrings(missingDeps) |
| return missingDeps |
| } |
| |
| type AllowDisabledModuleDependency interface { |
| blueprint.DependencyTag |
| AllowDisabledModuleDependency(target Module) bool |
| } |
| |
| func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag blueprint.DependencyTag, strict bool, ignoreBlueprint bool) Module { |
| aModule, _ := module.(Module) |
| |
| if !strict { |
| return aModule |
| } |
| |
| if aModule == nil { |
| if !ignoreBlueprint { |
| b.ModuleErrorf("module %q (%#v) not an android module", b.OtherModuleName(module), tag) |
| } |
| return nil |
| } |
| |
| if !aModule.Enabled() { |
| if t, ok := tag.(AllowDisabledModuleDependency); !ok || !t.AllowDisabledModuleDependency(aModule) { |
| if b.Config().AllowMissingDependencies() { |
| b.AddMissingDependencies([]string{b.OtherModuleName(aModule)}) |
| } else { |
| b.ModuleErrorf("depends on disabled module %q", b.OtherModuleName(aModule)) |
| } |
| } |
| return nil |
| } |
| return aModule |
| } |
| |
| type dep struct { |
| mod blueprint.Module |
| tag blueprint.DependencyTag |
| } |
| |
| func (b *baseModuleContext) getDirectDepsInternal(name string, tag blueprint.DependencyTag) []dep { |
| var deps []dep |
| b.VisitDirectDepsBlueprint(func(module blueprint.Module) { |
| if aModule, _ := module.(Module); aModule != nil { |
| if aModule.base().BaseModuleName() == name { |
| returnedTag := b.bp.OtherModuleDependencyTag(aModule) |
| if tag == nil || returnedTag == tag { |
| deps = append(deps, dep{aModule, returnedTag}) |
| } |
| } |
| } else if b.bp.OtherModuleName(module) == name { |
| returnedTag := b.bp.OtherModuleDependencyTag(module) |
| if tag == nil || returnedTag == tag { |
| deps = append(deps, dep{module, returnedTag}) |
| } |
| } |
| }) |
| return deps |
| } |
| |
| func (b *baseModuleContext) getDirectDepInternal(name string, tag blueprint.DependencyTag) (blueprint.Module, blueprint.DependencyTag) { |
| deps := b.getDirectDepsInternal(name, tag) |
| if len(deps) == 1 { |
| return deps[0].mod, deps[0].tag |
| } else if len(deps) >= 2 { |
| panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q", |
| name, b.ModuleName())) |
| } else { |
| return nil, nil |
| } |
| } |
| |
| func (b *baseModuleContext) getDirectDepFirstTag(name string) (blueprint.Module, blueprint.DependencyTag) { |
| foundDeps := b.getDirectDepsInternal(name, nil) |
| deps := map[blueprint.Module]bool{} |
| for _, dep := range foundDeps { |
| deps[dep.mod] = true |
| } |
| if len(deps) == 1 { |
| return foundDeps[0].mod, foundDeps[0].tag |
| } else if len(deps) >= 2 { |
| // this could happen if two dependencies have the same name in different namespaces |
| // TODO(b/186554727): this should not occur if namespaces are handled within |
| // getDirectDepsInternal. |
| panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q", |
| name, b.ModuleName())) |
| } else { |
| return nil, nil |
| } |
| } |
| |
| func (b *baseModuleContext) GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module { |
| var deps []Module |
| b.VisitDirectDepsBlueprint(func(module blueprint.Module) { |
| if aModule, _ := module.(Module); aModule != nil { |
| if b.bp.OtherModuleDependencyTag(aModule) == tag { |
| deps = append(deps, aModule) |
| } |
| } |
| }) |
| return deps |
| } |
| |
| // GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified |
| // name, or nil if none exists. If there are multiple dependencies on the same module it returns the |
| // first DependencyTag. |
| func (b *baseModuleContext) GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) { |
| return b.getDirectDepFirstTag(name) |
| } |
| |
| func (b *baseModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module)) { |
| b.bp.VisitDirectDeps(visit) |
| } |
| |
| func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) { |
| b.visitDirectDeps(visit, false) |
| } |
| |
| func (b *baseModuleContext) VisitDirectDepsIgnoreBlueprint(visit func(Module)) { |
| b.visitDirectDeps(visit, true) |
| } |
| |
| func (b *baseModuleContext) visitDirectDeps(visit func(Module), ignoreBlueprint bool) { |
| b.bp.VisitDirectDeps(func(module blueprint.Module) { |
| if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, ignoreBlueprint); aModule != nil { |
| visit(aModule) |
| } |
| }) |
| } |
| |
| func (b *baseModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) { |
| b.bp.VisitDirectDeps(func(module blueprint.Module) { |
| if b.bp.OtherModuleDependencyTag(module) == tag { |
| if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil { |
| visit(aModule) |
| } |
| } |
| }) |
| } |
| |
| func (b *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) { |
| b.bp.VisitDirectDepsIf( |
| // pred |
| func(module blueprint.Module) bool { |
| if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil { |
| return pred(aModule) |
| } else { |
| return false |
| } |
| }, |
| // visit |
| func(module blueprint.Module) { |
| visit(module.(Module)) |
| }) |
| } |
| |
| func (b *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) { |
| b.bp.VisitDepsDepthFirst(func(module blueprint.Module) { |
| if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil { |
| visit(aModule) |
| } |
| }) |
| } |
| |
| func (b *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) { |
| b.bp.VisitDepsDepthFirstIf( |
| // pred |
| func(module blueprint.Module) bool { |
| if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil { |
| return pred(aModule) |
| } else { |
| return false |
| } |
| }, |
| // visit |
| func(module blueprint.Module) { |
| visit(module.(Module)) |
| }) |
| } |
| |
| func (b *baseModuleContext) WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) { |
| b.bp.WalkDeps(visit) |
| } |
| |
| func (b *baseModuleContext) WalkDeps(visit func(Module, Module) bool) { |
| b.walkPath = []Module{b.Module()} |
| b.tagPath = []blueprint.DependencyTag{} |
| b.bp.WalkDeps(func(child, parent blueprint.Module) bool { |
| childAndroidModule, _ := child.(Module) |
| parentAndroidModule, _ := parent.(Module) |
| if childAndroidModule != nil && parentAndroidModule != nil { |
| // record walkPath before visit |
| for b.walkPath[len(b.walkPath)-1] != parentAndroidModule { |
| b.walkPath = b.walkPath[0 : len(b.walkPath)-1] |
| b.tagPath = b.tagPath[0 : len(b.tagPath)-1] |
| } |
| b.walkPath = append(b.walkPath, childAndroidModule) |
| b.tagPath = append(b.tagPath, b.OtherModuleDependencyTag(childAndroidModule)) |
| return visit(childAndroidModule, parentAndroidModule) |
| } else { |
| return false |
| } |
| }) |
| } |
| |
| func (b *baseModuleContext) GetWalkPath() []Module { |
| return b.walkPath |
| } |
| |
| func (b *baseModuleContext) GetTagPath() []blueprint.DependencyTag { |
| return b.tagPath |
| } |
| |
| func (b *baseModuleContext) VisitAllModuleVariants(visit func(Module)) { |
| b.bp.VisitAllModuleVariants(func(module blueprint.Module) { |
| visit(module.(Module)) |
| }) |
| } |
| |
| func (b *baseModuleContext) PrimaryModule() Module { |
| return b.bp.PrimaryModule().(Module) |
| } |
| |
| func (b *baseModuleContext) FinalModule() Module { |
| return b.bp.FinalModule().(Module) |
| } |
| |
| // IsMetaDependencyTag returns true for cross-cutting metadata dependencies. |
| func IsMetaDependencyTag(tag blueprint.DependencyTag) bool { |
| if tag == licenseKindTag { |
| return true |
| } else if tag == licensesTag { |
| return true |
| } else if tag == acDepTag { |
| return true |
| } |
| return false |
| } |
| |
| // A regexp for removing boilerplate from BaseDependencyTag from the string representation of |
| // a dependency tag. |
| var tagCleaner = regexp.MustCompile(`\QBaseDependencyTag:{}\E(, )?`) |
| |
| // PrettyPrintTag returns string representation of the tag, but prefers |
| // custom String() method if available. |
| func PrettyPrintTag(tag blueprint.DependencyTag) string { |
| // Use tag's custom String() method if available. |
| if stringer, ok := tag.(fmt.Stringer); ok { |
| return stringer.String() |
| } |
| |
| // Otherwise, get a default string representation of the tag's struct. |
| tagString := fmt.Sprintf("%T: %+v", tag, tag) |
| |
| // Remove the boilerplate from BaseDependencyTag as it adds no value. |
| tagString = tagCleaner.ReplaceAllString(tagString, "") |
| return tagString |
| } |
| |
| func (b *baseModuleContext) GetPathString(skipFirst bool) string { |
| sb := strings.Builder{} |
| tagPath := b.GetTagPath() |
| walkPath := b.GetWalkPath() |
| if !skipFirst { |
| sb.WriteString(walkPath[0].String()) |
| } |
| for i, m := range walkPath[1:] { |
| sb.WriteString("\n") |
| sb.WriteString(fmt.Sprintf(" via tag %s\n", PrettyPrintTag(tagPath[i]))) |
| sb.WriteString(fmt.Sprintf(" -> %s", m.String())) |
| } |
| return sb.String() |
| } |