blob: fc8ed4ccee8959e4f37f77c1a09ee7da7adba85f [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
Bob Badour5c12c662022-10-29 22:45:12 -070052 lg.onceBottomUp.Do(func() {
53 // amap identifes targets previously walked. (guarded by mu)
54 amap := make(map[*TargetNode]struct{})
Bob Badour103eb0f2022-01-10 13:50:57 -080055
Bob Badour5c12c662022-10-29 22:45:12 -070056 // mu guards concurrent access to amap
57 var mu sync.Mutex
Bob Badour9ee7d032021-10-25 16:51:48 -070058
Bob Badour5c12c662022-10-29 22:45:12 -070059 var walk func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet
Bob Badour103eb0f2022-01-10 13:50:57 -080060
Bob Badour5c12c662022-10-29 22:45:12 -070061 walk = func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet {
62 priorWalkResults := func() (LicenseConditionSet, bool) {
63 mu.Lock()
64 defer mu.Unlock()
Bob Badour103eb0f2022-01-10 13:50:57 -080065
Bob Badour5c12c662022-10-29 22:45:12 -070066 if _, alreadyWalked := amap[target]; alreadyWalked {
67 if treatAsAggregate {
68 return target.resolution, true
69 }
70 if !target.pure {
71 return target.resolution, true
72 }
73 // previously walked in a pure aggregate context,
74 // needs to walk again in non-aggregate context
75 } else {
76 target.resolution |= conditionsFn(target)
77 amap[target] = struct{}{}
Bob Badour103eb0f2022-01-10 13:50:57 -080078 }
Bob Badour5c12c662022-10-29 22:45:12 -070079 target.pure = treatAsAggregate
80 return target.resolution, false
Bob Badour103eb0f2022-01-10 13:50:57 -080081 }
Bob Badour5c12c662022-10-29 22:45:12 -070082 cs, alreadyWalked := priorWalkResults()
83 if alreadyWalked {
84 return cs
85 }
86
87 c := make(chan LicenseConditionSet, len(target.edges))
88 // add all the conditions from all the dependencies
89 for _, edge := range target.edges {
90 go func(edge *TargetEdge) {
91 // walk dependency to get its conditions
92 cs := walk(edge.dependency, treatAsAggregate && edge.dependency.IsContainer())
93
94 // turn those into the conditions that apply to the target
95 cs = depConditionsPropagatingToTarget(lg, edge, cs, treatAsAggregate)
96
97 c <- cs
98 }(edge)
99 }
100 for i := 0; i < len(target.edges); i++ {
101 cs |= <-c
102 }
103 mu.Lock()
104 target.resolution |= cs
105 mu.Unlock()
106
107 // return conditions up the tree
Bob Badour103eb0f2022-01-10 13:50:57 -0800108 return cs
109 }
110
Bob Badour5c12c662022-10-29 22:45:12 -0700111 // walk each of the roots
112 for _, rname := range lg.rootFiles {
113 rnode := lg.targets[rname]
114 _ = walk(rnode, rnode.IsContainer())
Bob Badour103eb0f2022-01-10 13:50:57 -0800115 }
Bob Badour5c12c662022-10-29 22:45:12 -0700116 })
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 Badourc8178452022-01-31 13:05:53 -0800126 TraceTopDownConditions(lg, AllResolutions)
127}
128
129// TraceTopDownCondtions performs a top-down walk of the LicenseGraph
130// propagating trace conditions returned by `conditionsFn` from target to
131// dependency.
132func TraceTopDownConditions(lg *LicenseGraph, conditionsFn TraceConditions) {
Bob Badour9ee7d032021-10-25 16:51:48 -0700133
134 // short-cut if already walked and cached
Bob Badour5c12c662022-10-29 22:45:12 -0700135 lg.onceTopDown.Do(func() {
136 wg := &sync.WaitGroup{}
Bob Badour103eb0f2022-01-10 13:50:57 -0800137 wg.Add(1)
Bob Badour5c12c662022-10-29 22:45:12 -0700138
139 // start with the conditions propagated up the graph
140 TraceBottomUpConditions(lg, conditionsFn)
141
142 // amap contains the set of targets already walked. (guarded by mu)
143 amap := make(map[*TargetNode]struct{})
144
145 // mu guards concurrent access to amap
146 var mu sync.Mutex
147
148 var walk func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool)
149
150 walk = func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool) {
151 defer wg.Done()
152 continueWalk := func() bool {
153 mu.Lock()
154 defer mu.Unlock()
155
156 depcs := fnode.resolution
157 _, alreadyWalked := amap[fnode]
158 if alreadyWalked {
159 if cs.IsEmpty() {
160 return false
161 }
162 if cs.Difference(depcs).IsEmpty() {
163 // no new conditions
164
165 // pure aggregates never need walking a 2nd time with same conditions
166 if treatAsAggregate {
167 return false
168 }
169 // non-aggregates don't need walking as non-aggregate a 2nd time
170 if !fnode.pure {
171 return false
172 }
173 // previously walked as pure aggregate; need to re-walk as non-aggregate
174 }
175 }
176 fnode.resolution |= conditionsFn(fnode)
177 fnode.resolution |= cs
178 fnode.pure = treatAsAggregate
179 amap[fnode] = struct{}{}
180 cs = fnode.resolution
181 return true
182 }()
183 if !continueWalk {
184 return
185 }
186 // for each dependency
187 for _, edge := range fnode.edges {
188 // dcs holds the dpendency conditions inherited from the target
189 dcs := targetConditionsPropagatingToDep(lg, edge, cs, treatAsAggregate, conditionsFn)
190 dnode := edge.dependency
191 // add the conditions to the dependency
192 wg.Add(1)
193 go walk(dnode, dcs, treatAsAggregate && dnode.IsContainer())
194 }
195 }
196
197 // walk each of the roots
198 for _, rname := range lg.rootFiles {
199 rnode := lg.targets[rname]
200 wg.Add(1)
201 // add the conditions to the root and its transitive closure
202 go walk(rnode, NewLicenseConditionSet(), rnode.IsContainer())
203 }
204 wg.Done()
205 wg.Wait()
206 })
Bob Badourb2855152021-12-08 12:52:59 -0800207}