blob: 336894af0aec50a975f8ac2d4b301423c8a68062 [file] [log] [blame]
Bob Badour9ee7d032021-10-25 16:51:48 -07001// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package compliance
16
Bob Badour103eb0f2022-01-10 13:50:57 -080017import (
18 "sync"
19)
20
Bob Badour9ee7d032021-10-25 16:51:48 -070021// ResolveBottomUpConditions performs a bottom-up walk of the LicenseGraph
22// propagating conditions up the graph as necessary according to the properties
23// of each edge and according to each license condition in question.
24//
Bob Badour9ee7d032021-10-25 16:51:48 -070025// e.g. if a "restricted" condition applies to a binary, it also applies to all
26// of the statically-linked libraries and the transitive closure of their static
27// dependencies; even if neither they nor the transitive closure of their
28// dependencies originate any "restricted" conditions. The bottom-up walk will
29// not resolve the library and its transitive closure, but the later top-down
30// walk will.
Bob Badour103eb0f2022-01-10 13:50:57 -080031func ResolveBottomUpConditions(lg *LicenseGraph) {
Bob Badour9ee7d032021-10-25 16:51:48 -070032
33 // short-cut if already walked and cached
34 lg.mu.Lock()
Bob Badour103eb0f2022-01-10 13:50:57 -080035 wg := lg.wgBU
36
37 if wg != nil {
38 lg.mu.Unlock()
39 wg.Wait()
40 return
41 }
42 wg = &sync.WaitGroup{}
43 wg.Add(1)
44 lg.wgBU = wg
Bob Badour9ee7d032021-10-25 16:51:48 -070045 lg.mu.Unlock()
46
Bob Badour103eb0f2022-01-10 13:50:57 -080047 // amap identifes targets previously walked. (guarded by mu)
48 amap := make(map[*TargetNode]struct{})
49
50 // cmap identifies targets previously walked as pure aggregates. i.e. as containers
51 // (guarded by mu)
52 cmap := make(map[*TargetNode]struct{})
53 var mu sync.Mutex
54
55 var walk func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet
56
57 walk = func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet {
58 priorWalkResults := func() (LicenseConditionSet, bool) {
59 mu.Lock()
60 defer mu.Unlock()
61
62 if _, alreadyWalked := amap[target]; alreadyWalked {
63 if treatAsAggregate {
64 return target.resolution, true
65 }
66 if _, asAggregate := cmap[target]; !asAggregate {
67 return target.resolution, true
68 }
69 // previously walked in a pure aggregate context,
70 // needs to walk again in non-aggregate context
71 delete(cmap, target)
72 } else {
73 target.resolution |= target.licenseConditions
74 amap[target] = struct{}{}
75 }
76 if treatAsAggregate {
77 cmap[target] = struct{}{}
78 }
79 return target.resolution, false
80 }
81 cs, alreadyWalked := priorWalkResults()
82 if alreadyWalked {
83 return cs
84 }
85
86 c := make(chan LicenseConditionSet, len(target.edges))
87 // add all the conditions from all the dependencies
88 for _, edge := range target.edges {
89 go func(edge *TargetEdge) {
90 // walk dependency to get its conditions
91 cs := walk(edge.dependency, treatAsAggregate && edge.dependency.IsContainer())
92
93 // turn those into the conditions that apply to the target
94 cs = depConditionsPropagatingToTarget(lg, edge, cs, treatAsAggregate)
95
96 c <- cs
97 }(edge)
98 }
99 for i := 0; i < len(target.edges); i++ {
100 cs |= <-c
101 }
102 mu.Lock()
103 target.resolution |= cs
104 mu.Unlock()
105
106 // return conditions up the tree
107 return cs
Bob Badour9ee7d032021-10-25 16:51:48 -0700108 }
109
Bob Badour103eb0f2022-01-10 13:50:57 -0800110 // walk each of the roots
111 for _, rname := range lg.rootFiles {
112 rnode := lg.targets[rname]
113 _ = walk(rnode, rnode.IsContainer())
Bob Badour9ee7d032021-10-25 16:51:48 -0700114 }
Bob Badour9ee7d032021-10-25 16:51:48 -0700115
Bob Badour103eb0f2022-01-10 13:50:57 -0800116 wg.Done()
Bob Badour9ee7d032021-10-25 16:51:48 -0700117}
118
119// ResolveTopDownCondtions performs a top-down walk of the LicenseGraph
Bob Badour103eb0f2022-01-10 13:50:57 -0800120// propagating conditions from target to dependency.
Bob Badour9ee7d032021-10-25 16:51:48 -0700121//
122// e.g. For current policy, none of the conditions propagate from target to
123// dependency except restricted. For restricted, the policy is to share the
124// source of any libraries linked to restricted code and to provide notice.
Bob Badour103eb0f2022-01-10 13:50:57 -0800125func ResolveTopDownConditions(lg *LicenseGraph) {
Bob Badour9ee7d032021-10-25 16:51:48 -0700126
127 // short-cut if already walked and cached
128 lg.mu.Lock()
Bob Badour103eb0f2022-01-10 13:50:57 -0800129 wg := lg.wgTD
130
131 if wg != nil {
132 lg.mu.Unlock()
133 wg.Wait()
134 return
135 }
136 wg = &sync.WaitGroup{}
137 wg.Add(1)
138 lg.wgTD = wg
Bob Badour9ee7d032021-10-25 16:51:48 -0700139 lg.mu.Unlock()
140
Bob Badour9ee7d032021-10-25 16:51:48 -0700141 // start with the conditions propagated up the graph
Bob Badour103eb0f2022-01-10 13:50:57 -0800142 ResolveBottomUpConditions(lg)
Bob Badour9ee7d032021-10-25 16:51:48 -0700143
Bob Badour103eb0f2022-01-10 13:50:57 -0800144 // amap contains the set of targets already walked. (guarded by mu)
145 amap := make(map[*TargetNode]struct{})
Bob Badour3a820dd2021-12-07 15:35:07 -0800146
147 // cmap contains the set of targets walked as pure aggregates. i.e. containers
Bob Badour103eb0f2022-01-10 13:50:57 -0800148 // (guarded by mu)
Bob Badour5446a6f2022-01-10 18:44:59 -0800149 cmap := make(map[*TargetNode]struct{})
Bob Badour9ee7d032021-10-25 16:51:48 -0700150
Bob Badour103eb0f2022-01-10 13:50:57 -0800151 // mu guards concurrent access to cmap
152 var mu sync.Mutex
Bob Badour9ee7d032021-10-25 16:51:48 -0700153
Bob Badour103eb0f2022-01-10 13:50:57 -0800154 var walk func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool)
155
156 walk = func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool) {
157 defer wg.Done()
158 mu.Lock()
159 fnode.resolution |= fnode.licenseConditions
160 fnode.resolution |= cs
161 amap[fnode] = struct{}{}
Bob Badour3a820dd2021-12-07 15:35:07 -0800162 if treatAsAggregate {
Bob Badour5446a6f2022-01-10 18:44:59 -0800163 cmap[fnode] = struct{}{}
Bob Badour3a820dd2021-12-07 15:35:07 -0800164 }
Bob Badour103eb0f2022-01-10 13:50:57 -0800165 cs = fnode.resolution
166 mu.Unlock()
Bob Badour9ee7d032021-10-25 16:51:48 -0700167 // for each dependency
Bob Badour103eb0f2022-01-10 13:50:57 -0800168 for _, edge := range fnode.edges {
169 func(edge *TargetEdge) {
170 // dcs holds the dpendency conditions inherited from the target
171 dcs := targetConditionsPropagatingToDep(lg, edge, cs, treatAsAggregate)
172 dnode := edge.dependency
173 mu.Lock()
174 defer mu.Unlock()
175 depcs := dnode.resolution
176 _, alreadyWalked := amap[dnode]
177 if !dcs.IsEmpty() && alreadyWalked {
178 if dcs.Difference(depcs).IsEmpty() {
179 // no new conditions
Bob Badour3a820dd2021-12-07 15:35:07 -0800180
Bob Badour103eb0f2022-01-10 13:50:57 -0800181 // pure aggregates never need walking a 2nd time with same conditions
182 if treatAsAggregate {
183 return
184 }
185 // non-aggregates don't need walking as non-aggregate a 2nd time
186 if _, asAggregate := cmap[dnode]; !asAggregate {
187 return
188 }
189 // previously walked as pure aggregate; need to re-walk as non-aggregate
190 delete(cmap, dnode)
Bob Badour3a820dd2021-12-07 15:35:07 -0800191 }
Bob Badour9ee7d032021-10-25 16:51:48 -0700192 }
Bob Badour103eb0f2022-01-10 13:50:57 -0800193 // add the conditions to the dependency
194 wg.Add(1)
195 go walk(dnode, dcs, treatAsAggregate && dnode.IsContainer())
196 }(edge)
Bob Badour9ee7d032021-10-25 16:51:48 -0700197 }
198 }
199
200 // walk each of the roots
Bob Badour103eb0f2022-01-10 13:50:57 -0800201 for _, rname := range lg.rootFiles {
202 rnode := lg.targets[rname]
203 wg.Add(1)
Bob Badour9ee7d032021-10-25 16:51:48 -0700204 // add the conditions to the root and its transitive closure
Bob Badour103eb0f2022-01-10 13:50:57 -0800205 go walk(rnode, NewLicenseConditionSet(), rnode.IsContainer())
Bob Badour3a820dd2021-12-07 15:35:07 -0800206 }
Bob Badour103eb0f2022-01-10 13:50:57 -0800207 wg.Done()
208 wg.Wait()
Bob Badourb2855152021-12-08 12:52:59 -0800209}