Add dependency tags to apex available errors

Having dependency tags in the dependency path that shows why a specific
module is considered part of an apex makes it easier to understand why
that is the case and detect possible issues.

Bug: 152762638
Bug: 153306490
Test: m nothing
Merged-In: Iba2a8a5a6abe03dadee456e760aa4373cd00c07b
Change-Id: Iba2a8a5a6abe03dadee456e760aa4373cd00c07b
This commit is contained in:
Paul Duffin
2020-03-31 11:31:36 +01:00
parent 03e7d0ca9c
commit f020796cad
3 changed files with 45 additions and 2 deletions

View File

@@ -128,6 +128,13 @@ type BaseModuleContext interface {
// and returns a top-down dependency path from a start module to current child module. // and returns a top-down dependency path from a start module to current child module.
GetWalkPath() []Module GetWalkPath() []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
AddMissingDependencies(missingDeps []string) AddMissingDependencies(missingDeps []string)
Target() Target Target() Target
@@ -1386,6 +1393,7 @@ type baseModuleContext struct {
debug bool debug bool
walkPath []Module walkPath []Module
tagPath []blueprint.DependencyTag
strictVisitDeps bool // If true, enforce that all dependencies are enabled strictVisitDeps bool // If true, enforce that all dependencies are enabled
} }
@@ -1675,6 +1683,7 @@ func (b *baseModuleContext) WalkDepsBlueprint(visit func(blueprint.Module, bluep
func (b *baseModuleContext) WalkDeps(visit func(Module, Module) bool) { func (b *baseModuleContext) WalkDeps(visit func(Module, Module) bool) {
b.walkPath = []Module{b.Module()} b.walkPath = []Module{b.Module()}
b.tagPath = []blueprint.DependencyTag{}
b.bp.WalkDeps(func(child, parent blueprint.Module) bool { b.bp.WalkDeps(func(child, parent blueprint.Module) bool {
childAndroidModule, _ := child.(Module) childAndroidModule, _ := child.(Module)
parentAndroidModule, _ := parent.(Module) parentAndroidModule, _ := parent.(Module)
@@ -1682,8 +1691,10 @@ func (b *baseModuleContext) WalkDeps(visit func(Module, Module) bool) {
// record walkPath before visit // record walkPath before visit
for b.walkPath[len(b.walkPath)-1] != parentAndroidModule { for b.walkPath[len(b.walkPath)-1] != parentAndroidModule {
b.walkPath = b.walkPath[0 : len(b.walkPath)-1] 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.walkPath = append(b.walkPath, childAndroidModule)
b.tagPath = append(b.tagPath, b.OtherModuleDependencyTag(childAndroidModule))
return visit(childAndroidModule, parentAndroidModule) return visit(childAndroidModule, parentAndroidModule)
} else { } else {
return false return false
@@ -1695,6 +1706,10 @@ func (b *baseModuleContext) GetWalkPath() []Module {
return b.walkPath return b.walkPath
} }
func (b *baseModuleContext) GetTagPath() []blueprint.DependencyTag {
return b.tagPath
}
func (m *moduleContext) VisitAllModuleVariants(visit func(Module)) { func (m *moduleContext) VisitAllModuleVariants(visit func(Module)) {
m.bp.VisitAllModuleVariants(func(module blueprint.Module) { m.bp.VisitAllModuleVariants(func(module blueprint.Module) {
visit(module.(Module)) visit(module.(Module))

View File

@@ -18,6 +18,7 @@ import (
"fmt" "fmt"
"path" "path"
"path/filepath" "path/filepath"
"regexp"
"sort" "sort"
"strings" "strings"
"sync" "sync"
@@ -1807,6 +1808,24 @@ func (a *apexBundle) minSdkVersion(ctx android.BaseModuleContext) int {
return intVer return intVer
} }
// A regexp for removing boilerplate from BaseDependencyTag from the string representation of
// a dependency tag.
var tagCleaner = regexp.MustCompile(`\QBaseDependencyTag:blueprint.BaseDependencyTag{}\E(, )?`)
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("%#v", tag)
// Remove the boilerplate from BaseDependencyTag as it adds no value.
tagString = tagCleaner.ReplaceAllString(tagString, "")
return tagString
}
// Ensures that the dependencies are marked as available for this APEX // Ensures that the dependencies are marked as available for this APEX
func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) { func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) {
// Let's be practical. Availability for test, host, and the VNDK apex isn't important // Let's be practical. Availability for test, host, and the VNDK apex isn't important
@@ -1834,8 +1853,11 @@ func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) {
return true return true
} }
message := "" message := ""
for _, m := range ctx.GetWalkPath()[1:] { tagPath := ctx.GetTagPath()
message = fmt.Sprintf("%s\n -> %s", message, m.String()) // Skip the first module as that will be added at the start of the error message by ctx.ModuleErrorf().
walkPath := ctx.GetWalkPath()[1:]
for i, m := range walkPath {
message = fmt.Sprintf("%s\n via tag %s\n -> %s", message, PrettyPrintTag(tagPath[i]), m.String())
} }
ctx.ModuleErrorf("%q requires %q that is not available for the APEX. Dependency path:%s", fromName, toName, message) ctx.ModuleErrorf("%q requires %q that is not available for the APEX. Dependency path:%s", fromName, toName, message)
// Visit this module's dependencies to check and report any issues with their availability. // Visit this module's dependencies to check and report any issues with their availability.

View File

@@ -3516,11 +3516,17 @@ func TestApexAvailable_DirectDep(t *testing.T) {
func TestApexAvailable_IndirectDep(t *testing.T) { func TestApexAvailable_IndirectDep(t *testing.T) {
// libbbaz is an indirect dep // libbbaz is an indirect dep
testApexError(t, `requires "libbaz" that is not available for the APEX. Dependency path: testApexError(t, `requires "libbaz" that is not available for the APEX. Dependency path:
.*via tag apex\.dependencyTag.*"sharedLib".*
.*-> libfoo.*link:shared.* .*-> libfoo.*link:shared.*
.*via tag cc\.DependencyTag.*"reuse objects".*
.*-> libfoo.*link:static.* .*-> libfoo.*link:static.*
.*via tag cc\.DependencyTag.*"shared from static".*
.*-> libbar.*link:shared.* .*-> libbar.*link:shared.*
.*via tag cc\.DependencyTag.*"reuse objects".*
.*-> libbar.*link:static.* .*-> libbar.*link:static.*
.*via tag cc\.DependencyTag.*"shared from static".*
.*-> libbaz.*link:shared.* .*-> libbaz.*link:shared.*
.*via tag cc\.DependencyTag.*"reuse objects".*
.*-> libbaz.*link:static.*`, ` .*-> libbaz.*link:static.*`, `
apex { apex {
name: "myapex", name: "myapex",