1*9e94795aSAndroid Build Coastguard Worker// Copyright 2021 Google LLC 2*9e94795aSAndroid Build Coastguard Worker// 3*9e94795aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*9e94795aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*9e94795aSAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*9e94795aSAndroid Build Coastguard Worker// 7*9e94795aSAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*9e94795aSAndroid Build Coastguard Worker// 9*9e94795aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*9e94795aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*9e94795aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*9e94795aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*9e94795aSAndroid Build Coastguard Worker// limitations under the License. 14*9e94795aSAndroid Build Coastguard Worker 15*9e94795aSAndroid Build Coastguard Workerpackage compliance 16*9e94795aSAndroid Build Coastguard Worker 17*9e94795aSAndroid Build Coastguard Worker// EdgeContextProvider is an interface for injecting edge-specific context 18*9e94795aSAndroid Build Coastguard Worker// into walk paths. 19*9e94795aSAndroid Build Coastguard Workertype EdgeContextProvider interface { 20*9e94795aSAndroid Build Coastguard Worker // Context returns the context for `edge` when added to `path`. 21*9e94795aSAndroid Build Coastguard Worker Context(lg *LicenseGraph, path TargetEdgePath, edge *TargetEdge) interface{} 22*9e94795aSAndroid Build Coastguard Worker} 23*9e94795aSAndroid Build Coastguard Worker 24*9e94795aSAndroid Build Coastguard Worker// NoEdgeContext implements EdgeContextProvider for walks that use no context. 25*9e94795aSAndroid Build Coastguard Workertype NoEdgeContext struct{} 26*9e94795aSAndroid Build Coastguard Worker 27*9e94795aSAndroid Build Coastguard Worker// Context returns nil. 28*9e94795aSAndroid Build Coastguard Workerfunc (ctx NoEdgeContext) Context(lg *LicenseGraph, path TargetEdgePath, edge *TargetEdge) interface{} { 29*9e94795aSAndroid Build Coastguard Worker return nil 30*9e94795aSAndroid Build Coastguard Worker} 31*9e94795aSAndroid Build Coastguard Worker 32*9e94795aSAndroid Build Coastguard Worker// ApplicableConditionsContext provides the subset of conditions in `universe` 33*9e94795aSAndroid Build Coastguard Worker// that apply to each edge in a path. 34*9e94795aSAndroid Build Coastguard Workertype ApplicableConditionsContext struct { 35*9e94795aSAndroid Build Coastguard Worker universe LicenseConditionSet 36*9e94795aSAndroid Build Coastguard Worker} 37*9e94795aSAndroid Build Coastguard Worker 38*9e94795aSAndroid Build Coastguard Worker// Context returns the LicenseConditionSet applicable to the edge. 39*9e94795aSAndroid Build Coastguard Workerfunc (ctx ApplicableConditionsContext) Context(lg *LicenseGraph, path TargetEdgePath, edge *TargetEdge) interface{} { 40*9e94795aSAndroid Build Coastguard Worker universe := ctx.universe 41*9e94795aSAndroid Build Coastguard Worker if len(path) > 0 { 42*9e94795aSAndroid Build Coastguard Worker universe = path[len(path)-1].ctx.(LicenseConditionSet) 43*9e94795aSAndroid Build Coastguard Worker } 44*9e94795aSAndroid Build Coastguard Worker return conditionsAttachingAcrossEdge(lg, edge, universe) 45*9e94795aSAndroid Build Coastguard Worker} 46*9e94795aSAndroid Build Coastguard Worker 47*9e94795aSAndroid Build Coastguard Worker// VisitNode is called for each root and for each walked dependency node by 48*9e94795aSAndroid Build Coastguard Worker// WalkTopDown and WalkTopDownBreadthFirst. When VisitNode returns true, WalkTopDown will proceed to walk 49*9e94795aSAndroid Build Coastguard Worker// down the dependences of the node 50*9e94795aSAndroid Build Coastguard Workertype VisitNode func(lg *LicenseGraph, target *TargetNode, path TargetEdgePath) bool 51*9e94795aSAndroid Build Coastguard Worker 52*9e94795aSAndroid Build Coastguard Worker// WalkTopDown does a top-down walk of `lg` calling `visit` and descending 53*9e94795aSAndroid Build Coastguard Worker// into depenencies when `visit` returns true. 54*9e94795aSAndroid Build Coastguard Workerfunc WalkTopDown(ctx EdgeContextProvider, lg *LicenseGraph, visit VisitNode) { 55*9e94795aSAndroid Build Coastguard Worker path := NewTargetEdgePath(32) 56*9e94795aSAndroid Build Coastguard Worker 57*9e94795aSAndroid Build Coastguard Worker var walk func(fnode *TargetNode) 58*9e94795aSAndroid Build Coastguard Worker walk = func(fnode *TargetNode) { 59*9e94795aSAndroid Build Coastguard Worker visitChildren := visit(lg, fnode, *path) 60*9e94795aSAndroid Build Coastguard Worker if !visitChildren { 61*9e94795aSAndroid Build Coastguard Worker return 62*9e94795aSAndroid Build Coastguard Worker } 63*9e94795aSAndroid Build Coastguard Worker for _, edge := range fnode.edges { 64*9e94795aSAndroid Build Coastguard Worker var edgeContext interface{} 65*9e94795aSAndroid Build Coastguard Worker if ctx == nil { 66*9e94795aSAndroid Build Coastguard Worker edgeContext = nil 67*9e94795aSAndroid Build Coastguard Worker } else { 68*9e94795aSAndroid Build Coastguard Worker edgeContext = ctx.Context(lg, *path, edge) 69*9e94795aSAndroid Build Coastguard Worker } 70*9e94795aSAndroid Build Coastguard Worker path.Push(edge, edgeContext) 71*9e94795aSAndroid Build Coastguard Worker walk(edge.dependency) 72*9e94795aSAndroid Build Coastguard Worker path.Pop() 73*9e94795aSAndroid Build Coastguard Worker } 74*9e94795aSAndroid Build Coastguard Worker } 75*9e94795aSAndroid Build Coastguard Worker 76*9e94795aSAndroid Build Coastguard Worker for _, r := range lg.rootFiles { 77*9e94795aSAndroid Build Coastguard Worker path.Clear() 78*9e94795aSAndroid Build Coastguard Worker walk(lg.targets[r]) 79*9e94795aSAndroid Build Coastguard Worker } 80*9e94795aSAndroid Build Coastguard Worker} 81*9e94795aSAndroid Build Coastguard Worker 82*9e94795aSAndroid Build Coastguard Worker// WalkTopDownBreadthFirst performs a Breadth-first top down walk of `lg` calling `visit` and descending 83*9e94795aSAndroid Build Coastguard Worker// into depenencies when `visit` returns true. 84*9e94795aSAndroid Build Coastguard Workerfunc WalkTopDownBreadthFirst(ctx EdgeContextProvider, lg *LicenseGraph, visit VisitNode) { 85*9e94795aSAndroid Build Coastguard Worker path := NewTargetEdgePath(32) 86*9e94795aSAndroid Build Coastguard Worker 87*9e94795aSAndroid Build Coastguard Worker var walk func(fnode *TargetNode) 88*9e94795aSAndroid Build Coastguard Worker walk = func(fnode *TargetNode) { 89*9e94795aSAndroid Build Coastguard Worker edgesToWalk := make(TargetEdgeList, 0, len(fnode.edges)) 90*9e94795aSAndroid Build Coastguard Worker for _, edge := range fnode.edges { 91*9e94795aSAndroid Build Coastguard Worker var edgeContext interface{} 92*9e94795aSAndroid Build Coastguard Worker if ctx == nil { 93*9e94795aSAndroid Build Coastguard Worker edgeContext = nil 94*9e94795aSAndroid Build Coastguard Worker } else { 95*9e94795aSAndroid Build Coastguard Worker edgeContext = ctx.Context(lg, *path, edge) 96*9e94795aSAndroid Build Coastguard Worker } 97*9e94795aSAndroid Build Coastguard Worker path.Push(edge, edgeContext) 98*9e94795aSAndroid Build Coastguard Worker if visit(lg, edge.dependency, *path){ 99*9e94795aSAndroid Build Coastguard Worker edgesToWalk = append(edgesToWalk, edge) 100*9e94795aSAndroid Build Coastguard Worker } 101*9e94795aSAndroid Build Coastguard Worker path.Pop() 102*9e94795aSAndroid Build Coastguard Worker } 103*9e94795aSAndroid Build Coastguard Worker 104*9e94795aSAndroid Build Coastguard Worker for _, edge := range(edgesToWalk) { 105*9e94795aSAndroid Build Coastguard Worker var edgeContext interface{} 106*9e94795aSAndroid Build Coastguard Worker if ctx == nil { 107*9e94795aSAndroid Build Coastguard Worker edgeContext = nil 108*9e94795aSAndroid Build Coastguard Worker } else { 109*9e94795aSAndroid Build Coastguard Worker edgeContext = ctx.Context(lg, *path, edge) 110*9e94795aSAndroid Build Coastguard Worker } 111*9e94795aSAndroid Build Coastguard Worker path.Push(edge, edgeContext) 112*9e94795aSAndroid Build Coastguard Worker walk(edge.dependency) 113*9e94795aSAndroid Build Coastguard Worker path.Pop() 114*9e94795aSAndroid Build Coastguard Worker } 115*9e94795aSAndroid Build Coastguard Worker } 116*9e94795aSAndroid Build Coastguard Worker 117*9e94795aSAndroid Build Coastguard Worker path.Clear() 118*9e94795aSAndroid Build Coastguard Worker rootsToWalk := make([]*TargetNode, 0, len(lg.rootFiles)) 119*9e94795aSAndroid Build Coastguard Worker for _, r := range lg.rootFiles { 120*9e94795aSAndroid Build Coastguard Worker if visit(lg, lg.targets[r], *path){ 121*9e94795aSAndroid Build Coastguard Worker rootsToWalk = append(rootsToWalk, lg.targets[r]) 122*9e94795aSAndroid Build Coastguard Worker } 123*9e94795aSAndroid Build Coastguard Worker } 124*9e94795aSAndroid Build Coastguard Worker 125*9e94795aSAndroid Build Coastguard Worker for _, rnode := range(rootsToWalk) { 126*9e94795aSAndroid Build Coastguard Worker walk(rnode) 127*9e94795aSAndroid Build Coastguard Worker } 128*9e94795aSAndroid Build Coastguard Worker} 129*9e94795aSAndroid Build Coastguard Worker 130*9e94795aSAndroid Build Coastguard Worker// resolutionKey identifies results from walking a specific target for a 131*9e94795aSAndroid Build Coastguard Worker// specific set of conditions. 132*9e94795aSAndroid Build Coastguard Workertype resolutionKey struct { 133*9e94795aSAndroid Build Coastguard Worker target *TargetNode 134*9e94795aSAndroid Build Coastguard Worker cs LicenseConditionSet 135*9e94795aSAndroid Build Coastguard Worker} 136*9e94795aSAndroid Build Coastguard Worker 137*9e94795aSAndroid Build Coastguard Worker// WalkResolutionsForCondition performs a top-down walk of the LicenseGraph 138*9e94795aSAndroid Build Coastguard Worker// resolving all distributed works for `conditions`. 139*9e94795aSAndroid Build Coastguard Workerfunc WalkResolutionsForCondition(lg *LicenseGraph, conditions LicenseConditionSet) ResolutionSet { 140*9e94795aSAndroid Build Coastguard Worker shipped := ShippedNodes(lg) 141*9e94795aSAndroid Build Coastguard Worker 142*9e94795aSAndroid Build Coastguard Worker // rmap maps 'attachesTo' targets to the `actsOn` targets and applicable conditions 143*9e94795aSAndroid Build Coastguard Worker rmap := make(map[resolutionKey]ActionSet) 144*9e94795aSAndroid Build Coastguard Worker 145*9e94795aSAndroid Build Coastguard Worker // cmap identifies previously walked target/condition pairs. 146*9e94795aSAndroid Build Coastguard Worker cmap := make(map[resolutionKey]struct{}) 147*9e94795aSAndroid Build Coastguard Worker 148*9e94795aSAndroid Build Coastguard Worker // result accumulates the resolutions to return. 149*9e94795aSAndroid Build Coastguard Worker result := make(ResolutionSet) 150*9e94795aSAndroid Build Coastguard Worker WalkTopDown(ApplicableConditionsContext{conditions}, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool { 151*9e94795aSAndroid Build Coastguard Worker universe := conditions 152*9e94795aSAndroid Build Coastguard Worker if len(path) > 0 { 153*9e94795aSAndroid Build Coastguard Worker universe = path[len(path)-1].ctx.(LicenseConditionSet) 154*9e94795aSAndroid Build Coastguard Worker } 155*9e94795aSAndroid Build Coastguard Worker 156*9e94795aSAndroid Build Coastguard Worker if universe.IsEmpty() { 157*9e94795aSAndroid Build Coastguard Worker return false 158*9e94795aSAndroid Build Coastguard Worker } 159*9e94795aSAndroid Build Coastguard Worker key := resolutionKey{tn, universe} 160*9e94795aSAndroid Build Coastguard Worker 161*9e94795aSAndroid Build Coastguard Worker if _, alreadyWalked := cmap[key]; alreadyWalked { 162*9e94795aSAndroid Build Coastguard Worker pure := true 163*9e94795aSAndroid Build Coastguard Worker for _, p := range path { 164*9e94795aSAndroid Build Coastguard Worker target := p.Target() 165*9e94795aSAndroid Build Coastguard Worker tkey := resolutionKey{target, universe} 166*9e94795aSAndroid Build Coastguard Worker if _, ok := rmap[tkey]; !ok { 167*9e94795aSAndroid Build Coastguard Worker rmap[tkey] = make(ActionSet) 168*9e94795aSAndroid Build Coastguard Worker } 169*9e94795aSAndroid Build Coastguard Worker // attach prior walk outcome to ancestor 170*9e94795aSAndroid Build Coastguard Worker for actsOn, cs := range rmap[key] { 171*9e94795aSAndroid Build Coastguard Worker rmap[tkey][actsOn] = cs 172*9e94795aSAndroid Build Coastguard Worker } 173*9e94795aSAndroid Build Coastguard Worker // if prior walk produced results, copy results 174*9e94795aSAndroid Build Coastguard Worker // to ancestor. 175*9e94795aSAndroid Build Coastguard Worker if _, ok := result[tn]; ok && pure { 176*9e94795aSAndroid Build Coastguard Worker if _, ok := result[target]; !ok { 177*9e94795aSAndroid Build Coastguard Worker result[target] = make(ActionSet) 178*9e94795aSAndroid Build Coastguard Worker } 179*9e94795aSAndroid Build Coastguard Worker for actsOn, cs := range result[tn] { 180*9e94795aSAndroid Build Coastguard Worker result[target][actsOn] = cs 181*9e94795aSAndroid Build Coastguard Worker } 182*9e94795aSAndroid Build Coastguard Worker pure = target.IsContainer() 183*9e94795aSAndroid Build Coastguard Worker } 184*9e94795aSAndroid Build Coastguard Worker } 185*9e94795aSAndroid Build Coastguard Worker // if all ancestors are pure aggregates, attach 186*9e94795aSAndroid Build Coastguard Worker // matching prior walk conditions to self. Prior walk 187*9e94795aSAndroid Build Coastguard Worker // will not have done so if any ancestor was not an 188*9e94795aSAndroid Build Coastguard Worker // aggregate. 189*9e94795aSAndroid Build Coastguard Worker if pure { 190*9e94795aSAndroid Build Coastguard Worker match := rmap[key][tn].Intersection(universe) 191*9e94795aSAndroid Build Coastguard Worker if !match.IsEmpty() { 192*9e94795aSAndroid Build Coastguard Worker if _, ok := result[tn]; !ok { 193*9e94795aSAndroid Build Coastguard Worker result[tn] = make(ActionSet) 194*9e94795aSAndroid Build Coastguard Worker } 195*9e94795aSAndroid Build Coastguard Worker result[tn][tn] = match 196*9e94795aSAndroid Build Coastguard Worker } 197*9e94795aSAndroid Build Coastguard Worker } 198*9e94795aSAndroid Build Coastguard Worker return false 199*9e94795aSAndroid Build Coastguard Worker } 200*9e94795aSAndroid Build Coastguard Worker // no need to walk node or dependencies if not shipped 201*9e94795aSAndroid Build Coastguard Worker if !shipped.Contains(tn) { 202*9e94795aSAndroid Build Coastguard Worker return false 203*9e94795aSAndroid Build Coastguard Worker } 204*9e94795aSAndroid Build Coastguard Worker if _, ok := rmap[key]; !ok { 205*9e94795aSAndroid Build Coastguard Worker rmap[key] = make(ActionSet) 206*9e94795aSAndroid Build Coastguard Worker } 207*9e94795aSAndroid Build Coastguard Worker // add self to walk outcome 208*9e94795aSAndroid Build Coastguard Worker rmap[key][tn] = tn.resolution 209*9e94795aSAndroid Build Coastguard Worker cmap[key] = struct{}{} 210*9e94795aSAndroid Build Coastguard Worker cs := tn.resolution 211*9e94795aSAndroid Build Coastguard Worker if !cs.IsEmpty() { 212*9e94795aSAndroid Build Coastguard Worker cs = cs.Intersection(universe) 213*9e94795aSAndroid Build Coastguard Worker pure := true 214*9e94795aSAndroid Build Coastguard Worker for _, p := range path { 215*9e94795aSAndroid Build Coastguard Worker target := p.Target() 216*9e94795aSAndroid Build Coastguard Worker tkey := resolutionKey{target, universe} 217*9e94795aSAndroid Build Coastguard Worker if _, ok := rmap[tkey]; !ok { 218*9e94795aSAndroid Build Coastguard Worker rmap[tkey] = make(ActionSet) 219*9e94795aSAndroid Build Coastguard Worker } 220*9e94795aSAndroid Build Coastguard Worker // copy current node's action into ancestor 221*9e94795aSAndroid Build Coastguard Worker rmap[tkey][tn] = tn.resolution 222*9e94795aSAndroid Build Coastguard Worker // conditionally put matching conditions into 223*9e94795aSAndroid Build Coastguard Worker // result 224*9e94795aSAndroid Build Coastguard Worker if pure && !cs.IsEmpty() { 225*9e94795aSAndroid Build Coastguard Worker if _, ok := result[target]; !ok { 226*9e94795aSAndroid Build Coastguard Worker result[target] = make(ActionSet) 227*9e94795aSAndroid Build Coastguard Worker } 228*9e94795aSAndroid Build Coastguard Worker result[target][tn] = cs 229*9e94795aSAndroid Build Coastguard Worker pure = target.IsContainer() 230*9e94795aSAndroid Build Coastguard Worker } 231*9e94795aSAndroid Build Coastguard Worker } 232*9e94795aSAndroid Build Coastguard Worker // if all ancestors are pure aggregates, attach 233*9e94795aSAndroid Build Coastguard Worker // matching conditions to self. 234*9e94795aSAndroid Build Coastguard Worker if pure && !cs.IsEmpty() { 235*9e94795aSAndroid Build Coastguard Worker if _, ok := result[tn]; !ok { 236*9e94795aSAndroid Build Coastguard Worker result[tn] = make(ActionSet) 237*9e94795aSAndroid Build Coastguard Worker } 238*9e94795aSAndroid Build Coastguard Worker result[tn][tn] = cs 239*9e94795aSAndroid Build Coastguard Worker } 240*9e94795aSAndroid Build Coastguard Worker } 241*9e94795aSAndroid Build Coastguard Worker return true 242*9e94795aSAndroid Build Coastguard Worker }) 243*9e94795aSAndroid Build Coastguard Worker 244*9e94795aSAndroid Build Coastguard Worker return result 245*9e94795aSAndroid Build Coastguard Worker} 246*9e94795aSAndroid Build Coastguard Worker 247*9e94795aSAndroid Build Coastguard Worker// WalkActionsForCondition performs a top-down walk of the LicenseGraph 248*9e94795aSAndroid Build Coastguard Worker// resolving all distributed works for `conditions`. 249*9e94795aSAndroid Build Coastguard Workerfunc WalkActionsForCondition(lg *LicenseGraph, conditions LicenseConditionSet) ActionSet { 250*9e94795aSAndroid Build Coastguard Worker // amap maps 'actsOn' targets to the applicable conditions 251*9e94795aSAndroid Build Coastguard Worker // 252*9e94795aSAndroid Build Coastguard Worker // amap is the resulting ActionSet 253*9e94795aSAndroid Build Coastguard Worker amap := make(ActionSet) 254*9e94795aSAndroid Build Coastguard Worker 255*9e94795aSAndroid Build Coastguard Worker for tn := range ShippedNodes(lg) { 256*9e94795aSAndroid Build Coastguard Worker if cs := conditions.Intersection(tn.resolution); !cs.IsEmpty() { 257*9e94795aSAndroid Build Coastguard Worker amap[tn] = cs 258*9e94795aSAndroid Build Coastguard Worker } 259*9e94795aSAndroid Build Coastguard Worker } 260*9e94795aSAndroid Build Coastguard Worker 261*9e94795aSAndroid Build Coastguard Worker return amap 262*9e94795aSAndroid Build Coastguard Worker} 263