Merge remote-tracking branch 'aosp/upstream' master
* aosp/upstream:
Allow multiple dependencies on the same module
Test: m checkbuild
Change-Id: I75947f1e8be7921a9a6dc9c9ae80aa279f906b75
diff --git a/context.go b/context.go
index f96306b..11ee6f0 100644
--- a/context.go
+++ b/context.go
@@ -1401,12 +1401,6 @@
}
if m := c.findMatchingVariant(module, possibleDeps); m != nil {
- for _, dep := range module.directDeps {
- if m == dep.module {
- // TODO(ccross): what if adding a dependency with a different tag?
- return nil
- }
- }
module.directDeps = append(module.directDeps, depInfo{m, tag})
atomic.AddUint32(&c.depsModified, 1)
return nil
@@ -2386,7 +2380,7 @@
return nil
}
-func (c *Context) walkDeps(topModule *moduleInfo,
+func (c *Context) walkDeps(topModule *moduleInfo, allowDuplicates bool,
visitDown func(depInfo, *moduleInfo) bool, visitUp func(depInfo, *moduleInfo)) {
visited := make(map[*moduleInfo]bool)
@@ -2402,7 +2396,7 @@
var walk func(module *moduleInfo)
walk = func(module *moduleInfo) {
for _, dep := range module.directDeps {
- if !visited[dep.module] {
+ if allowDuplicates || !visited[dep.module] {
visited[dep.module] = true
visiting = dep.module
recurse := true
@@ -2884,7 +2878,7 @@
}
}()
- c.walkDeps(topModule, nil, func(dep depInfo, parent *moduleInfo) {
+ c.walkDeps(topModule, false, nil, func(dep depInfo, parent *moduleInfo) {
visiting = dep.module
visit(dep.module.logicModule)
})
@@ -2902,7 +2896,7 @@
}
}()
- c.walkDeps(topModule, nil, func(dep depInfo, parent *moduleInfo) {
+ c.walkDeps(topModule, false, nil, func(dep depInfo, parent *moduleInfo) {
if pred(dep.module.logicModule) {
visiting = dep.module
visit(dep.module.logicModule)
diff --git a/context_test.go b/context_test.go
index 635f73e..c08b0b7 100644
--- a/context_test.go
+++ b/context_test.go
@@ -189,7 +189,7 @@
var outputDown string
var outputUp string
topModule := ctx.modulesFromName("A", nil)[0]
- ctx.walkDeps(topModule,
+ ctx.walkDeps(topModule, false,
func(dep depInfo, parent *moduleInfo) bool {
if dep.module.logicModule.(Walker).Walk() {
outputDown += ctx.ModuleName(dep.module.logicModule)
@@ -203,10 +203,99 @@
}
})
if outputDown != "CFG" {
- t.Fatalf("unexpected walkDeps behaviour: %s\ndown should be: CFG", outputDown)
+ t.Errorf("unexpected walkDeps behaviour: %s\ndown should be: CFG", outputDown)
}
if outputUp != "GFC" {
- t.Fatalf("unexpected walkDeps behaviour: %s\nup should be: GFC", outputUp)
+ t.Errorf("unexpected walkDeps behaviour: %s\nup should be: GFC", outputUp)
+ }
+}
+
+// |---B===D - represents a non-walkable edge
+// A = represents a walkable edge
+// |===C===E===\
+// | | A should not be visited because it's the root node.
+// |===F===G B, D should not be walked.
+// \===/ G should be visited multiple times
+func TestWalkDepsDuplicates(t *testing.T) {
+ ctx := NewContext()
+ ctx.MockFileSystem(map[string][]byte{
+ "Blueprints": []byte(`
+ foo_module {
+ name: "A",
+ deps: ["B", "C"],
+ }
+
+ bar_module {
+ name: "B",
+ deps: ["D"],
+ }
+
+ foo_module {
+ name: "C",
+ deps: ["E", "F"],
+ }
+
+ foo_module {
+ name: "D",
+ }
+
+ foo_module {
+ name: "E",
+ deps: ["G"],
+ }
+
+ foo_module {
+ name: "F",
+ deps: ["G", "G"],
+ }
+
+ foo_module {
+ name: "G",
+ }
+ `),
+ })
+
+ ctx.RegisterModuleType("foo_module", newFooModule)
+ ctx.RegisterModuleType("bar_module", newBarModule)
+ _, errs := ctx.ParseBlueprintsFiles("Blueprints")
+ if len(errs) > 0 {
+ t.Errorf("unexpected parse errors:")
+ for _, err := range errs {
+ t.Errorf(" %s", err)
+ }
+ t.FailNow()
+ }
+
+ _, errs = ctx.ResolveDependencies(nil)
+ if len(errs) > 0 {
+ t.Errorf("unexpected dep errors:")
+ for _, err := range errs {
+ t.Errorf(" %s", err)
+ }
+ t.FailNow()
+ }
+
+ var outputDown string
+ var outputUp string
+ topModule := ctx.modulesFromName("A", nil)[0]
+ ctx.walkDeps(topModule, true,
+ func(dep depInfo, parent *moduleInfo) bool {
+ if dep.module.logicModule.(Walker).Walk() {
+ outputDown += ctx.ModuleName(dep.module.logicModule)
+ return true
+ }
+ return false
+ },
+ func(dep depInfo, parent *moduleInfo) {
+ if dep.module.logicModule.(Walker).Walk() {
+ outputUp += ctx.ModuleName(dep.module.logicModule)
+ }
+ })
+ if outputDown != "CEGFGG" {
+ t.Errorf("unexpected walkDeps behaviour: %s\ndown should be: CEGFGG", outputDown)
+ }
+ if outputUp != "GEGGFC" {
+ t.Errorf("unexpected walkDeps behaviour: %s\nup should be: GEGGFC", outputUp)
}
}
diff --git a/module_ctx.go b/module_ctx.go
index fe6c12b..200fd9a 100644
--- a/module_ctx.go
+++ b/module_ctx.go
@@ -304,6 +304,9 @@
})
}
+// 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.
func (m *baseModuleContext) OtherModuleDependencyTag(logicModule Module) DependencyTag {
// fast path for calling OtherModuleDependencyTag from inside VisitDirectDeps
if logicModule == m.visitingDep.module.logicModule {
@@ -319,8 +322,9 @@
return nil
}
-// GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified
-// name, or nil if none exists.
+// 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 (m *baseModuleContext) GetDirectDep(name string) (Module, DependencyTag) {
for _, dep := range m.module.directDeps {
if dep.module.Name() == name {
@@ -347,6 +351,8 @@
return nil
}
+// 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.
func (m *baseModuleContext) VisitDirectDeps(visit func(Module)) {
defer func() {
if r := recover(); r != nil {
@@ -366,6 +372,9 @@
m.visitingDep = depInfo{}
}
+// 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.
func (m *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) {
defer func() {
if r := recover(); r != nil {
@@ -387,6 +396,10 @@
m.visitingDep = depInfo{}
}
+// VisitDepsDepthFirst calls visit for each transitive dependency, traversing the dependency tree in depth first order.
+// visit will only be called once for any given module, even if there are multiple paths through the dependency tree
+// to the module or multiple direct dependencies with different tags. OtherModuleDependencyTag will return the tag for
+// the first path found to the module.
func (m *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) {
defer func() {
if r := recover(); r != nil {
@@ -395,7 +408,7 @@
}
}()
- m.context.walkDeps(m.module, nil, func(dep depInfo, parent *moduleInfo) {
+ m.context.walkDeps(m.module, false, nil, func(dep depInfo, parent *moduleInfo) {
m.visitingParent = parent
m.visitingDep = dep
visit(dep.module.logicModule)
@@ -405,6 +418,11 @@
m.visitingDep = depInfo{}
}
+// VisitDepsDepthFirst calls pred for each transitive dependency, and if pred returns true calls visit, traversing the
+// dependency tree in depth first order. visit will only be called once for any given module, even if there are
+// multiple paths through the dependency tree to the module or multiple direct dependencies with different tags.
+// OtherModuleDependencyTag will return the tag for the first path found to the module. The return value of pred does
+// not affect which branches of the tree are traversed.
func (m *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool,
visit func(Module)) {
@@ -415,7 +433,7 @@
}
}()
- m.context.walkDeps(m.module, nil, func(dep depInfo, parent *moduleInfo) {
+ m.context.walkDeps(m.module, false, nil, func(dep depInfo, parent *moduleInfo) {
if pred(dep.module.logicModule) {
m.visitingParent = parent
m.visitingDep = dep
@@ -427,8 +445,12 @@
m.visitingDep = depInfo{}
}
+// WalkDeps calls visit for each transitive dependency, traversing the dependency tree in top down order. visit may be
+// called multiple times for the same module if there are multiple paths through the dependency tree to the module or
+// multiple direct dependencies with different tags. OtherModuleDependencyTag will return the tag for the currently
+// visited path to the module. If visit returns false WalkDeps will not continue recursing down the current path.
func (m *baseModuleContext) WalkDeps(visit func(Module, Module) bool) {
- m.context.walkDeps(m.module, func(dep depInfo, parent *moduleInfo) bool {
+ m.context.walkDeps(m.module, true, func(dep depInfo, parent *moduleInfo) bool {
m.visitingParent = parent
m.visitingDep = dep
return visit(dep.module.logicModule, parent.logicModule)
diff --git a/visit_test.go b/visit_test.go
index 3aa0f1b..873e72c 100644
--- a/visit_test.go
+++ b/visit_test.go
@@ -83,6 +83,9 @@
// D
// |
// E
+// / \
+// \ /
+// F
func setupVisitTest(t *testing.T) *Context {
ctx := NewContext()
ctx.RegisterModuleType("visit_module", newVisitModule)
@@ -113,6 +116,11 @@
visit_module {
name: "E",
+ visit: ["F", "F"],
+ }
+
+ visit_module {
+ name: "F",
}
`),
})
@@ -142,10 +150,16 @@
ctx := setupVisitTest(t)
topModule := ctx.modulesFromName("A", nil)[0].logicModule.(*visitModule)
- assertString(t, topModule.properties.VisitDepsDepthFirst, "EDCB")
- assertString(t, topModule.properties.VisitDepsDepthFirstIf, "EDC")
+ assertString(t, topModule.properties.VisitDepsDepthFirst, "FEDCB")
+ assertString(t, topModule.properties.VisitDepsDepthFirstIf, "FEDC")
assertString(t, topModule.properties.VisitDirectDeps, "B")
assertString(t, topModule.properties.VisitDirectDepsIf, "")
+
+ eModule := ctx.modulesFromName("E", nil)[0].logicModule.(*visitModule)
+ assertString(t, eModule.properties.VisitDepsDepthFirst, "F")
+ assertString(t, eModule.properties.VisitDepsDepthFirstIf, "F")
+ assertString(t, eModule.properties.VisitDirectDeps, "FF")
+ assertString(t, eModule.properties.VisitDirectDepsIf, "FF")
}
func assertString(t *testing.T, got, expected string) {