blob: 93335a94a47af2b3e14350f0b1b76a7707b7ab31 [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 Badourc8178452022-01-31 13:05:53 -080021var (
22 // AllResolutions is a TraceConditions function that resolves all
23 // unfiltered license conditions.
24 AllResolutions = TraceConditions(func(tn *TargetNode) LicenseConditionSet { return tn.licenseConditions })
25)
26
27// TraceConditions is a function that returns the conditions to trace for each
28// target node `tn`.
29type TraceConditions func(tn *TargetNode) LicenseConditionSet
30
Bob Badour9ee7d032021-10-25 16:51:48 -070031// ResolveBottomUpConditions performs a bottom-up walk of the LicenseGraph
32// propagating conditions up the graph as necessary according to the properties
33// of each edge and according to each license condition in question.
34//
Bob Badour9ee7d032021-10-25 16:51:48 -070035// e.g. if a "restricted" condition applies to a binary, it also applies to all
36// of the statically-linked libraries and the transitive closure of their static
37// dependencies; even if neither they nor the transitive closure of their
38// dependencies originate any "restricted" conditions. The bottom-up walk will
39// not resolve the library and its transitive closure, but the later top-down
40// walk will.
Bob Badour103eb0f2022-01-10 13:50:57 -080041func ResolveBottomUpConditions(lg *LicenseGraph) {
Bob Badourc8178452022-01-31 13:05:53 -080042 TraceBottomUpConditions(lg, AllResolutions)
43}
44
45// TraceBottomUpConditions performs a bottom-up walk of the LicenseGraph
46// propagating trace conditions from `conditionsFn` up the graph as necessary
47// according to the properties of each edge and according to each license
48// condition in question.
49func TraceBottomUpConditions(lg *LicenseGraph, conditionsFn TraceConditions) {
Bob Badour9ee7d032021-10-25 16:51:48 -070050
51 // short-cut if already walked and cached
52 lg.mu.Lock()
Bob Badour103eb0f2022-01-10 13:50:57 -080053 wg := lg.wgBU
54
55 if wg != nil {
56 lg.mu.Unlock()
57 wg.Wait()
58 return
59 }
60 wg = &sync.WaitGroup{}
61 wg.Add(1)
62 lg.wgBU = wg
Bob Badour9ee7d032021-10-25 16:51:48 -070063 lg.mu.Unlock()
64
Bob Badour103eb0f2022-01-10 13:50:57 -080065 // amap identifes targets previously walked. (guarded by mu)
66 amap := make(map[*TargetNode]struct{})
67
Bob Badour085a2c22022-09-21 19:36:59 -070068 // mu guards concurrent access to amap
Bob Badour103eb0f2022-01-10 13:50:57 -080069 var mu sync.Mutex
70
71 var walk func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet
72
73 walk = func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet {
74 priorWalkResults := func() (LicenseConditionSet, bool) {
75 mu.Lock()
76 defer mu.Unlock()
77
78 if _, alreadyWalked := amap[target]; alreadyWalked {
79 if treatAsAggregate {
80 return target.resolution, true
81 }
Bob Badour085a2c22022-09-21 19:36:59 -070082 if !target.pure {
Bob Badour103eb0f2022-01-10 13:50:57 -080083 return target.resolution, true
84 }
85 // previously walked in a pure aggregate context,
86 // needs to walk again in non-aggregate context
Bob Badour103eb0f2022-01-10 13:50:57 -080087 } else {
Bob Badourc8178452022-01-31 13:05:53 -080088 target.resolution |= conditionsFn(target)
Bob Badour103eb0f2022-01-10 13:50:57 -080089 amap[target] = struct{}{}
90 }
Bob Badour085a2c22022-09-21 19:36:59 -070091 target.pure = treatAsAggregate
Bob Badour103eb0f2022-01-10 13:50:57 -080092 return target.resolution, false
93 }
94 cs, alreadyWalked := priorWalkResults()
95 if alreadyWalked {
96 return cs
97 }
98
99 c := make(chan LicenseConditionSet, len(target.edges))
100 // add all the conditions from all the dependencies
101 for _, edge := range target.edges {
102 go func(edge *TargetEdge) {
103 // walk dependency to get its conditions
104 cs := walk(edge.dependency, treatAsAggregate && edge.dependency.IsContainer())
105
106 // turn those into the conditions that apply to the target
107 cs = depConditionsPropagatingToTarget(lg, edge, cs, treatAsAggregate)
108
109 c <- cs
110 }(edge)
111 }
112 for i := 0; i < len(target.edges); i++ {
113 cs |= <-c
114 }
115 mu.Lock()
116 target.resolution |= cs
117 mu.Unlock()
118
119 // return conditions up the tree
120 return cs
Bob Badour9ee7d032021-10-25 16:51:48 -0700121 }
122
Bob Badour103eb0f2022-01-10 13:50:57 -0800123 // walk each of the roots
124 for _, rname := range lg.rootFiles {
125 rnode := lg.targets[rname]
126 _ = walk(rnode, rnode.IsContainer())
Bob Badour9ee7d032021-10-25 16:51:48 -0700127 }
Bob Badour9ee7d032021-10-25 16:51:48 -0700128
Bob Badour103eb0f2022-01-10 13:50:57 -0800129 wg.Done()
Bob Badour9ee7d032021-10-25 16:51:48 -0700130}
131
132// ResolveTopDownCondtions performs a top-down walk of the LicenseGraph
Bob Badour103eb0f2022-01-10 13:50:57 -0800133// propagating conditions from target to dependency.
Bob Badour9ee7d032021-10-25 16:51:48 -0700134//
135// e.g. For current policy, none of the conditions propagate from target to
136// dependency except restricted. For restricted, the policy is to share the
137// source of any libraries linked to restricted code and to provide notice.
Bob Badour103eb0f2022-01-10 13:50:57 -0800138func ResolveTopDownConditions(lg *LicenseGraph) {
Bob Badourc8178452022-01-31 13:05:53 -0800139 TraceTopDownConditions(lg, AllResolutions)
140}
141
142// TraceTopDownCondtions performs a top-down walk of the LicenseGraph
143// propagating trace conditions returned by `conditionsFn` from target to
144// dependency.
145func TraceTopDownConditions(lg *LicenseGraph, conditionsFn TraceConditions) {
Bob Badour9ee7d032021-10-25 16:51:48 -0700146
147 // short-cut if already walked and cached
148 lg.mu.Lock()
Bob Badour103eb0f2022-01-10 13:50:57 -0800149 wg := lg.wgTD
150
151 if wg != nil {
152 lg.mu.Unlock()
153 wg.Wait()
154 return
155 }
156 wg = &sync.WaitGroup{}
157 wg.Add(1)
158 lg.wgTD = wg
Bob Badour9ee7d032021-10-25 16:51:48 -0700159 lg.mu.Unlock()
160
Bob Badour9ee7d032021-10-25 16:51:48 -0700161 // start with the conditions propagated up the graph
Bob Badourc8178452022-01-31 13:05:53 -0800162 TraceBottomUpConditions(lg, conditionsFn)
Bob Badour9ee7d032021-10-25 16:51:48 -0700163
Bob Badour103eb0f2022-01-10 13:50:57 -0800164 // amap contains the set of targets already walked. (guarded by mu)
165 amap := make(map[*TargetNode]struct{})
Bob Badour3a820dd2021-12-07 15:35:07 -0800166
Bob Badour085a2c22022-09-21 19:36:59 -0700167 // mu guards concurrent access to amap
Bob Badour103eb0f2022-01-10 13:50:57 -0800168 var mu sync.Mutex
Bob Badour9ee7d032021-10-25 16:51:48 -0700169
Bob Badour103eb0f2022-01-10 13:50:57 -0800170 var walk func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool)
171
172 walk = func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool) {
173 defer wg.Done()
174 mu.Lock()
Bob Badourc8178452022-01-31 13:05:53 -0800175 fnode.resolution |= conditionsFn(fnode)
Bob Badour103eb0f2022-01-10 13:50:57 -0800176 fnode.resolution |= cs
Bob Badour085a2c22022-09-21 19:36:59 -0700177 fnode.pure = treatAsAggregate
Bob Badour103eb0f2022-01-10 13:50:57 -0800178 amap[fnode] = struct{}{}
Bob Badour103eb0f2022-01-10 13:50:57 -0800179 cs = fnode.resolution
180 mu.Unlock()
Bob Badour9ee7d032021-10-25 16:51:48 -0700181 // for each dependency
Bob Badour103eb0f2022-01-10 13:50:57 -0800182 for _, edge := range fnode.edges {
183 func(edge *TargetEdge) {
184 // dcs holds the dpendency conditions inherited from the target
Bob Badourc8178452022-01-31 13:05:53 -0800185 dcs := targetConditionsPropagatingToDep(lg, edge, cs, treatAsAggregate, conditionsFn)
Bob Badour103eb0f2022-01-10 13:50:57 -0800186 dnode := edge.dependency
187 mu.Lock()
188 defer mu.Unlock()
189 depcs := dnode.resolution
190 _, alreadyWalked := amap[dnode]
191 if !dcs.IsEmpty() && alreadyWalked {
192 if dcs.Difference(depcs).IsEmpty() {
193 // no new conditions
Bob Badour3a820dd2021-12-07 15:35:07 -0800194
Bob Badour103eb0f2022-01-10 13:50:57 -0800195 // pure aggregates never need walking a 2nd time with same conditions
196 if treatAsAggregate {
197 return
198 }
199 // non-aggregates don't need walking as non-aggregate a 2nd time
Bob Badour085a2c22022-09-21 19:36:59 -0700200 if !dnode.pure {
Bob Badour103eb0f2022-01-10 13:50:57 -0800201 return
202 }
203 // previously walked as pure aggregate; need to re-walk as non-aggregate
Bob Badour3a820dd2021-12-07 15:35:07 -0800204 }
Bob Badour9ee7d032021-10-25 16:51:48 -0700205 }
Bob Badour103eb0f2022-01-10 13:50:57 -0800206 // add the conditions to the dependency
207 wg.Add(1)
208 go walk(dnode, dcs, treatAsAggregate && dnode.IsContainer())
209 }(edge)
Bob Badour9ee7d032021-10-25 16:51:48 -0700210 }
211 }
212
213 // walk each of the roots
Bob Badour103eb0f2022-01-10 13:50:57 -0800214 for _, rname := range lg.rootFiles {
215 rnode := lg.targets[rname]
216 wg.Add(1)
Bob Badour9ee7d032021-10-25 16:51:48 -0700217 // add the conditions to the root and its transitive closure
Bob Badour103eb0f2022-01-10 13:50:57 -0800218 go walk(rnode, NewLicenseConditionSet(), rnode.IsContainer())
Bob Badour3a820dd2021-12-07 15:35:07 -0800219 }
Bob Badour103eb0f2022-01-10 13:50:57 -0800220 wg.Done()
221 wg.Wait()
Bob Badourb2855152021-12-08 12:52:59 -0800222}