license metadata remove path on top-down walk

Performance optimization means not every path will be traversed.

Instead of updating parents via the path, perform a 2nd bottom-up walk
after the top-down walk to propagate the new resolutions to parents.

Note: the 2nd walk method will add resolutions to statically linked
libraries etc. at deeper levels, but those do not affect what gets
reported. In particular, note that test data for dumpresolutions
changes, but none of the test data for listshare, checkshare etc.
changes.

Test: m all systemlicense listshare checkshare dumpgraph dumpresolutions

Bug: 68860345
Bug: 151177513
Bug: 151953481
Change-Id: I76361c4e33bbadbbea38cbec260430e8f9407628
This commit is contained in:
Bob Badour
2021-12-08 12:52:59 -08:00
parent 3a820dd5a0
commit b285515ca1
6 changed files with 177 additions and 78 deletions

View File

@@ -41,59 +41,7 @@ func ResolveBottomUpConditions(lg *LicenseGraph) *ResolutionSet {
// must be indexed for fast lookup
lg.indexForward()
rs = newResolutionSet()
// cmap contains an entry for every target that was previously walked as a pure aggregate only.
cmap := make(map[string]bool)
var walk func(f string, treatAsAggregate bool) actionSet
walk = func(f string, treatAsAggregate bool) actionSet {
target := lg.targets[f]
result := make(actionSet)
result[target] = newLicenseConditionSet()
result[target].add(target, target.proto.LicenseConditions...)
if preresolved, ok := rs.resolutions[target]; ok {
if treatAsAggregate {
result.addSet(preresolved)
return result
}
if _, asAggregate := cmap[f]; !asAggregate {
result.addSet(preresolved)
return result
}
// previously walked in a pure aggregate context,
// needs to walk again in non-aggregate context
delete(cmap, f)
}
if treatAsAggregate {
cmap[f] = true
}
// add all the conditions from all the dependencies
for _, edge := range lg.index[f] {
// walk dependency to get its conditions
as := walk(edge.dependency, treatAsAggregate && lg.targets[edge.dependency].IsContainer())
// turn those into the conditions that apply to the target
as = depActionsApplicableToTarget(TargetEdge{lg, edge}, as, treatAsAggregate)
// add them to the result
result.addSet(as)
}
// record these conditions as applicable to the target
rs.addConditions(target, result)
rs.addSelf(target, result.byName(ImpliesRestricted))
// return this up the tree
return result
}
// walk each of the roots
for _, r := range lg.rootFiles {
_ = walk(r, lg.targets[r].IsContainer())
}
rs = resolveBottomUp(lg, make(map[*TargetNode]actionSet) /* empty map; no prior resolves */)
// if not yet cached, save the result
lg.mu.Lock()
@@ -137,31 +85,13 @@ func ResolveTopDownConditions(lg *LicenseGraph) *ResolutionSet {
// cmap contains the set of targets walked as pure aggregates. i.e. containers
cmap := make(map[*TargetNode]bool)
path := make([]*dependencyEdge, 0, 32)
var walk func(fnode *TargetNode, cs *LicenseConditionSet, treatAsAggregate bool)
walk = func(fnode *TargetNode, cs *LicenseConditionSet, treatAsAggregate bool) {
if !cs.IsEmpty() {
parentsAllAggregate := true
for _, e := range path {
target := lg.targets[e.target]
if _, ok := rmap[target]; !ok {
rmap[target] = make(actionSet)
}
rmap[target].add(fnode, cs)
if !target.IsContainer() {
parentsAllAggregate = false
break
}
}
if parentsAllAggregate {
if _, ok := rmap[fnode]; !ok {
rmap[fnode] = make(actionSet)
}
rmap[fnode].add(fnode, cs)
}
if _, ok := rmap[fnode]; !ok {
rmap[fnode] = make(actionSet)
}
rmap[fnode].add(fnode, cs)
if treatAsAggregate {
cmap[fnode] = true
}
@@ -197,10 +127,8 @@ func ResolveTopDownConditions(lg *LicenseGraph) *ResolutionSet {
delete(cmap, dnode)
}
}
path = append(path, edge)
// add the conditions to the dependency
walk(dnode, dcs, treatAsAggregate && lg.targets[edge.dependency].IsContainer())
path = path[:len(path)-1]
}
}
@@ -216,7 +144,6 @@ func ResolveTopDownConditions(lg *LicenseGraph) *ResolutionSet {
continue
}
path = path[:0]
// add the conditions to the root and its transitive closure
walk(rnode, newLicenseConditionSet(), lg.targets[r].IsContainer())
}
@@ -230,7 +157,8 @@ func ResolveTopDownConditions(lg *LicenseGraph) *ResolutionSet {
}
}
rs = &ResolutionSet{rmap}
// propagate any new conditions back up the graph
rs = resolveBottomUp(lg, rmap)
// if not yet cached, save the result
lg.mu.Lock()
@@ -244,3 +172,70 @@ func ResolveTopDownConditions(lg *LicenseGraph) *ResolutionSet {
return rs
}
// resolveBottomUp implements a bottom-up resolve propagating conditions both
// from the graph, and from a `priors` map of resolutions.
func resolveBottomUp(lg *LicenseGraph, priors map[*TargetNode]actionSet) *ResolutionSet {
rs := newResolutionSet()
// cmap contains an entry for every target that was previously walked as a pure aggregate only.
cmap := make(map[string]bool)
var walk func(f string, treatAsAggregate bool) actionSet
walk = func(f string, treatAsAggregate bool) actionSet {
target := lg.targets[f]
result := make(actionSet)
result[target] = newLicenseConditionSet()
result[target].add(target, target.proto.LicenseConditions...)
if pas, ok := priors[target]; ok {
result.addSet(pas)
}
if preresolved, ok := rs.resolutions[target]; ok {
if treatAsAggregate {
result.addSet(preresolved)
return result
}
if _, asAggregate := cmap[f]; !asAggregate {
result.addSet(preresolved)
return result
}
// previously walked in a pure aggregate context,
// needs to walk again in non-aggregate context
delete(cmap, f)
}
if treatAsAggregate {
cmap[f] = true
}
// add all the conditions from all the dependencies
for _, edge := range lg.index[f] {
// walk dependency to get its conditions
as := walk(edge.dependency, treatAsAggregate && lg.targets[edge.dependency].IsContainer())
// turn those into the conditions that apply to the target
as = depActionsApplicableToTarget(TargetEdge{lg, edge}, as, treatAsAggregate)
// add them to the result
result.addSet(as)
}
// record these conditions as applicable to the target
rs.addConditions(target, result)
if len(priors) == 0 {
// on the first bottom-up resolve, parents have their own sharing and notice needs
// on the later resolve, if priors is empty, there will be nothing new to add
rs.addSelf(target, result.byName(ImpliesRestricted))
}
// return this up the tree
return result
}
// walk each of the roots
for _, r := range lg.rootFiles {
_ = walk(r, lg.targets[r].IsContainer())
}
return rs
}