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 Workervar ( 18*9e94795aSAndroid Build Coastguard Worker // AllResolutions is a TraceConditions function that resolves all 19*9e94795aSAndroid Build Coastguard Worker // unfiltered license conditions. 20*9e94795aSAndroid Build Coastguard Worker AllResolutions = TraceConditions(func(tn *TargetNode) LicenseConditionSet { return tn.licenseConditions }) 21*9e94795aSAndroid Build Coastguard Worker) 22*9e94795aSAndroid Build Coastguard Worker 23*9e94795aSAndroid Build Coastguard Worker// TraceConditions is a function that returns the conditions to trace for each 24*9e94795aSAndroid Build Coastguard Worker// target node `tn`. 25*9e94795aSAndroid Build Coastguard Workertype TraceConditions func(tn *TargetNode) LicenseConditionSet 26*9e94795aSAndroid Build Coastguard Worker 27*9e94795aSAndroid Build Coastguard Worker// ResolveBottomUpConditions performs a bottom-up walk of the LicenseGraph 28*9e94795aSAndroid Build Coastguard Worker// propagating conditions up the graph as necessary according to the properties 29*9e94795aSAndroid Build Coastguard Worker// of each edge and according to each license condition in question. 30*9e94795aSAndroid Build Coastguard Worker// 31*9e94795aSAndroid Build Coastguard Worker// e.g. if a "restricted" condition applies to a binary, it also applies to all 32*9e94795aSAndroid Build Coastguard Worker// of the statically-linked libraries and the transitive closure of their static 33*9e94795aSAndroid Build Coastguard Worker// dependencies; even if neither they nor the transitive closure of their 34*9e94795aSAndroid Build Coastguard Worker// dependencies originate any "restricted" conditions. The bottom-up walk will 35*9e94795aSAndroid Build Coastguard Worker// not resolve the library and its transitive closure, but the later top-down 36*9e94795aSAndroid Build Coastguard Worker// walk will. 37*9e94795aSAndroid Build Coastguard Workerfunc ResolveBottomUpConditions(lg *LicenseGraph) { 38*9e94795aSAndroid Build Coastguard Worker TraceBottomUpConditions(lg, AllResolutions) 39*9e94795aSAndroid Build Coastguard Worker} 40*9e94795aSAndroid Build Coastguard Worker 41*9e94795aSAndroid Build Coastguard Worker// TraceBottomUpConditions performs a bottom-up walk of the LicenseGraph 42*9e94795aSAndroid Build Coastguard Worker// propagating trace conditions from `conditionsFn` up the graph as necessary 43*9e94795aSAndroid Build Coastguard Worker// according to the properties of each edge and according to each license 44*9e94795aSAndroid Build Coastguard Worker// condition in question. 45*9e94795aSAndroid Build Coastguard Workerfunc TraceBottomUpConditions(lg *LicenseGraph, conditionsFn TraceConditions) { 46*9e94795aSAndroid Build Coastguard Worker 47*9e94795aSAndroid Build Coastguard Worker // short-cut if already walked and cached 48*9e94795aSAndroid Build Coastguard Worker lg.onceBottomUp.Do(func() { 49*9e94795aSAndroid Build Coastguard Worker // amap identifes targets previously walked. (guarded by mu) 50*9e94795aSAndroid Build Coastguard Worker amap := make(map[*TargetNode]struct{}) 51*9e94795aSAndroid Build Coastguard Worker 52*9e94795aSAndroid Build Coastguard Worker var walk func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet 53*9e94795aSAndroid Build Coastguard Worker 54*9e94795aSAndroid Build Coastguard Worker walk = func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet { 55*9e94795aSAndroid Build Coastguard Worker priorWalkResults := func() (LicenseConditionSet, bool) { 56*9e94795aSAndroid Build Coastguard Worker if _, alreadyWalked := amap[target]; alreadyWalked { 57*9e94795aSAndroid Build Coastguard Worker if treatAsAggregate { 58*9e94795aSAndroid Build Coastguard Worker return target.resolution, true 59*9e94795aSAndroid Build Coastguard Worker } 60*9e94795aSAndroid Build Coastguard Worker if !target.pure { 61*9e94795aSAndroid Build Coastguard Worker return target.resolution, true 62*9e94795aSAndroid Build Coastguard Worker } 63*9e94795aSAndroid Build Coastguard Worker // previously walked in a pure aggregate context, 64*9e94795aSAndroid Build Coastguard Worker // needs to walk again in non-aggregate context 65*9e94795aSAndroid Build Coastguard Worker } else { 66*9e94795aSAndroid Build Coastguard Worker target.resolution |= conditionsFn(target) 67*9e94795aSAndroid Build Coastguard Worker amap[target] = struct{}{} 68*9e94795aSAndroid Build Coastguard Worker } 69*9e94795aSAndroid Build Coastguard Worker target.pure = treatAsAggregate 70*9e94795aSAndroid Build Coastguard Worker return target.resolution, false 71*9e94795aSAndroid Build Coastguard Worker } 72*9e94795aSAndroid Build Coastguard Worker cs, alreadyWalked := priorWalkResults() 73*9e94795aSAndroid Build Coastguard Worker if alreadyWalked { 74*9e94795aSAndroid Build Coastguard Worker return cs 75*9e94795aSAndroid Build Coastguard Worker } 76*9e94795aSAndroid Build Coastguard Worker 77*9e94795aSAndroid Build Coastguard Worker // add all the conditions from all the dependencies 78*9e94795aSAndroid Build Coastguard Worker for _, edge := range target.edges { 79*9e94795aSAndroid Build Coastguard Worker // walk dependency to get its conditions 80*9e94795aSAndroid Build Coastguard Worker dcs := walk(edge.dependency, treatAsAggregate && edge.dependency.IsContainer()) 81*9e94795aSAndroid Build Coastguard Worker 82*9e94795aSAndroid Build Coastguard Worker // turn those into the conditions that apply to the target 83*9e94795aSAndroid Build Coastguard Worker dcs = depConditionsPropagatingToTarget(lg, edge, dcs, treatAsAggregate) 84*9e94795aSAndroid Build Coastguard Worker cs |= dcs 85*9e94795aSAndroid Build Coastguard Worker } 86*9e94795aSAndroid Build Coastguard Worker target.resolution |= cs 87*9e94795aSAndroid Build Coastguard Worker cs = target.resolution 88*9e94795aSAndroid Build Coastguard Worker 89*9e94795aSAndroid Build Coastguard Worker // return conditions up the tree 90*9e94795aSAndroid Build Coastguard Worker return cs 91*9e94795aSAndroid Build Coastguard Worker } 92*9e94795aSAndroid Build Coastguard Worker 93*9e94795aSAndroid Build Coastguard Worker // walk each of the roots 94*9e94795aSAndroid Build Coastguard Worker for _, rname := range lg.rootFiles { 95*9e94795aSAndroid Build Coastguard Worker rnode := lg.targets[rname] 96*9e94795aSAndroid Build Coastguard Worker _ = walk(rnode, rnode.IsContainer()) 97*9e94795aSAndroid Build Coastguard Worker } 98*9e94795aSAndroid Build Coastguard Worker }) 99*9e94795aSAndroid Build Coastguard Worker} 100*9e94795aSAndroid Build Coastguard Worker 101*9e94795aSAndroid Build Coastguard Worker// ResolveTopDownCondtions performs a top-down walk of the LicenseGraph 102*9e94795aSAndroid Build Coastguard Worker// propagating conditions from target to dependency. 103*9e94795aSAndroid Build Coastguard Worker// 104*9e94795aSAndroid Build Coastguard Worker// e.g. For current policy, none of the conditions propagate from target to 105*9e94795aSAndroid Build Coastguard Worker// dependency except restricted. For restricted, the policy is to share the 106*9e94795aSAndroid Build Coastguard Worker// source of any libraries linked to restricted code and to provide notice. 107*9e94795aSAndroid Build Coastguard Workerfunc ResolveTopDownConditions(lg *LicenseGraph) { 108*9e94795aSAndroid Build Coastguard Worker TraceTopDownConditions(lg, AllResolutions) 109*9e94795aSAndroid Build Coastguard Worker} 110*9e94795aSAndroid Build Coastguard Worker 111*9e94795aSAndroid Build Coastguard Worker// TraceTopDownCondtions performs a top-down walk of the LicenseGraph 112*9e94795aSAndroid Build Coastguard Worker// propagating trace conditions returned by `conditionsFn` from target to 113*9e94795aSAndroid Build Coastguard Worker// dependency. 114*9e94795aSAndroid Build Coastguard Workerfunc TraceTopDownConditions(lg *LicenseGraph, conditionsFn TraceConditions) { 115*9e94795aSAndroid Build Coastguard Worker 116*9e94795aSAndroid Build Coastguard Worker // short-cut if already walked and cached 117*9e94795aSAndroid Build Coastguard Worker lg.onceTopDown.Do(func() { 118*9e94795aSAndroid Build Coastguard Worker // start with the conditions propagated up the graph 119*9e94795aSAndroid Build Coastguard Worker TraceBottomUpConditions(lg, conditionsFn) 120*9e94795aSAndroid Build Coastguard Worker 121*9e94795aSAndroid Build Coastguard Worker // amap contains the set of targets already walked. (guarded by mu) 122*9e94795aSAndroid Build Coastguard Worker amap := make(map[*TargetNode]struct{}) 123*9e94795aSAndroid Build Coastguard Worker 124*9e94795aSAndroid Build Coastguard Worker var walk func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool) 125*9e94795aSAndroid Build Coastguard Worker 126*9e94795aSAndroid Build Coastguard Worker walk = func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool) { 127*9e94795aSAndroid Build Coastguard Worker continueWalk := func() bool { 128*9e94795aSAndroid Build Coastguard Worker if _, alreadyWalked := amap[fnode]; alreadyWalked { 129*9e94795aSAndroid Build Coastguard Worker if cs.IsEmpty() { 130*9e94795aSAndroid Build Coastguard Worker return false 131*9e94795aSAndroid Build Coastguard Worker } 132*9e94795aSAndroid Build Coastguard Worker if cs.Difference(fnode.resolution).IsEmpty() { 133*9e94795aSAndroid Build Coastguard Worker // no new conditions 134*9e94795aSAndroid Build Coastguard Worker 135*9e94795aSAndroid Build Coastguard Worker // pure aggregates never need walking a 2nd time with same conditions 136*9e94795aSAndroid Build Coastguard Worker if treatAsAggregate { 137*9e94795aSAndroid Build Coastguard Worker return false 138*9e94795aSAndroid Build Coastguard Worker } 139*9e94795aSAndroid Build Coastguard Worker // non-aggregates don't need walking as non-aggregate a 2nd time 140*9e94795aSAndroid Build Coastguard Worker if !fnode.pure { 141*9e94795aSAndroid Build Coastguard Worker return false 142*9e94795aSAndroid Build Coastguard Worker } 143*9e94795aSAndroid Build Coastguard Worker // previously walked as pure aggregate; need to re-walk as non-aggregate 144*9e94795aSAndroid Build Coastguard Worker } 145*9e94795aSAndroid Build Coastguard Worker } else { 146*9e94795aSAndroid Build Coastguard Worker fnode.resolution |= conditionsFn(fnode) 147*9e94795aSAndroid Build Coastguard Worker } 148*9e94795aSAndroid Build Coastguard Worker fnode.resolution |= cs 149*9e94795aSAndroid Build Coastguard Worker fnode.pure = treatAsAggregate 150*9e94795aSAndroid Build Coastguard Worker amap[fnode] = struct{}{} 151*9e94795aSAndroid Build Coastguard Worker cs = fnode.resolution 152*9e94795aSAndroid Build Coastguard Worker return true 153*9e94795aSAndroid Build Coastguard Worker }() 154*9e94795aSAndroid Build Coastguard Worker if !continueWalk { 155*9e94795aSAndroid Build Coastguard Worker return 156*9e94795aSAndroid Build Coastguard Worker } 157*9e94795aSAndroid Build Coastguard Worker // for each dependency 158*9e94795aSAndroid Build Coastguard Worker for _, edge := range fnode.edges { 159*9e94795aSAndroid Build Coastguard Worker // dcs holds the dpendency conditions inherited from the target 160*9e94795aSAndroid Build Coastguard Worker dcs := targetConditionsPropagatingToDep(lg, edge, cs, treatAsAggregate, conditionsFn) 161*9e94795aSAndroid Build Coastguard Worker dnode := edge.dependency 162*9e94795aSAndroid Build Coastguard Worker // add the conditions to the dependency 163*9e94795aSAndroid Build Coastguard Worker walk(dnode, dcs, treatAsAggregate && dnode.IsContainer()) 164*9e94795aSAndroid Build Coastguard Worker } 165*9e94795aSAndroid Build Coastguard Worker } 166*9e94795aSAndroid Build Coastguard Worker 167*9e94795aSAndroid Build Coastguard Worker // walk each of the roots 168*9e94795aSAndroid Build Coastguard Worker for _, rname := range lg.rootFiles { 169*9e94795aSAndroid Build Coastguard Worker rnode := lg.targets[rname] 170*9e94795aSAndroid Build Coastguard Worker // add the conditions to the root and its transitive closure 171*9e94795aSAndroid Build Coastguard Worker walk(rnode, NewLicenseConditionSet(), rnode.IsContainer()) 172*9e94795aSAndroid Build Coastguard Worker } 173*9e94795aSAndroid Build Coastguard Worker }) 174*9e94795aSAndroid Build Coastguard Worker} 175