blob: 0951ba1a2c86bd89a43353a7ea9e48c20b889b9e [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 Badourc8178452022-01-31 13:05:53 -080017var (
18 // AllResolutions is a TraceConditions function that resolves all
19 // unfiltered license conditions.
20 AllResolutions = TraceConditions(func(tn *TargetNode) LicenseConditionSet { return tn.licenseConditions })
21)
22
23// TraceConditions is a function that returns the conditions to trace for each
24// target node `tn`.
25type TraceConditions func(tn *TargetNode) LicenseConditionSet
26
Bob Badour9ee7d032021-10-25 16:51:48 -070027// ResolveBottomUpConditions performs a bottom-up walk of the LicenseGraph
28// propagating conditions up the graph as necessary according to the properties
29// of each edge and according to each license condition in question.
30//
Bob Badour9ee7d032021-10-25 16:51:48 -070031// e.g. if a "restricted" condition applies to a binary, it also applies to all
32// of the statically-linked libraries and the transitive closure of their static
33// dependencies; even if neither they nor the transitive closure of their
34// dependencies originate any "restricted" conditions. The bottom-up walk will
35// not resolve the library and its transitive closure, but the later top-down
36// walk will.
Bob Badour103eb0f2022-01-10 13:50:57 -080037func ResolveBottomUpConditions(lg *LicenseGraph) {
Bob Badourc8178452022-01-31 13:05:53 -080038 TraceBottomUpConditions(lg, AllResolutions)
39}
40
41// TraceBottomUpConditions performs a bottom-up walk of the LicenseGraph
42// propagating trace conditions from `conditionsFn` up the graph as necessary
43// according to the properties of each edge and according to each license
44// condition in question.
45func TraceBottomUpConditions(lg *LicenseGraph, conditionsFn TraceConditions) {
Bob Badour9ee7d032021-10-25 16:51:48 -070046
47 // short-cut if already walked and cached
Bob Badour5c12c662022-10-29 22:45:12 -070048 lg.onceBottomUp.Do(func() {
49 // amap identifes targets previously walked. (guarded by mu)
50 amap := make(map[*TargetNode]struct{})
Bob Badour103eb0f2022-01-10 13:50:57 -080051
Bob Badour5c12c662022-10-29 22:45:12 -070052 var walk func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet
Bob Badour103eb0f2022-01-10 13:50:57 -080053
Bob Badour5c12c662022-10-29 22:45:12 -070054 walk = func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet {
55 priorWalkResults := func() (LicenseConditionSet, bool) {
Bob Badour5c12c662022-10-29 22:45:12 -070056 if _, alreadyWalked := amap[target]; alreadyWalked {
57 if treatAsAggregate {
58 return target.resolution, true
59 }
60 if !target.pure {
61 return target.resolution, true
62 }
63 // previously walked in a pure aggregate context,
64 // needs to walk again in non-aggregate context
65 } else {
66 target.resolution |= conditionsFn(target)
67 amap[target] = struct{}{}
Bob Badour103eb0f2022-01-10 13:50:57 -080068 }
Bob Badour5c12c662022-10-29 22:45:12 -070069 target.pure = treatAsAggregate
70 return target.resolution, false
Bob Badour103eb0f2022-01-10 13:50:57 -080071 }
Bob Badour5c12c662022-10-29 22:45:12 -070072 cs, alreadyWalked := priorWalkResults()
73 if alreadyWalked {
74 return cs
75 }
76
Bob Badour5c12c662022-10-29 22:45:12 -070077 // add all the conditions from all the dependencies
78 for _, edge := range target.edges {
Bob Badourf9d23a92022-12-18 14:19:47 -080079 // walk dependency to get its conditions
80 dcs := walk(edge.dependency, treatAsAggregate && edge.dependency.IsContainer())
Bob Badour5c12c662022-10-29 22:45:12 -070081
Bob Badourf9d23a92022-12-18 14:19:47 -080082 // turn those into the conditions that apply to the target
83 dcs = depConditionsPropagatingToTarget(lg, edge, dcs, treatAsAggregate)
84 cs |= dcs
Bob Badour5c12c662022-10-29 22:45:12 -070085 }
Bob Badour5c12c662022-10-29 22:45:12 -070086 target.resolution |= cs
Bob Badourf9d23a92022-12-18 14:19:47 -080087 cs = target.resolution
Bob Badour5c12c662022-10-29 22:45:12 -070088
89 // return conditions up the tree
Bob Badour103eb0f2022-01-10 13:50:57 -080090 return cs
91 }
92
Bob Badour5c12c662022-10-29 22:45:12 -070093 // walk each of the roots
94 for _, rname := range lg.rootFiles {
95 rnode := lg.targets[rname]
96 _ = walk(rnode, rnode.IsContainer())
Bob Badour103eb0f2022-01-10 13:50:57 -080097 }
Bob Badour5c12c662022-10-29 22:45:12 -070098 })
Bob Badour9ee7d032021-10-25 16:51:48 -070099}
100
101// ResolveTopDownCondtions performs a top-down walk of the LicenseGraph
Bob Badour103eb0f2022-01-10 13:50:57 -0800102// propagating conditions from target to dependency.
Bob Badour9ee7d032021-10-25 16:51:48 -0700103//
104// e.g. For current policy, none of the conditions propagate from target to
105// dependency except restricted. For restricted, the policy is to share the
106// source of any libraries linked to restricted code and to provide notice.
Bob Badour103eb0f2022-01-10 13:50:57 -0800107func ResolveTopDownConditions(lg *LicenseGraph) {
Bob Badourc8178452022-01-31 13:05:53 -0800108 TraceTopDownConditions(lg, AllResolutions)
109}
110
111// TraceTopDownCondtions performs a top-down walk of the LicenseGraph
112// propagating trace conditions returned by `conditionsFn` from target to
113// dependency.
114func TraceTopDownConditions(lg *LicenseGraph, conditionsFn TraceConditions) {
Bob Badour9ee7d032021-10-25 16:51:48 -0700115
116 // short-cut if already walked and cached
Bob Badour5c12c662022-10-29 22:45:12 -0700117 lg.onceTopDown.Do(func() {
Bob Badour5c12c662022-10-29 22:45:12 -0700118 // start with the conditions propagated up the graph
119 TraceBottomUpConditions(lg, conditionsFn)
120
121 // amap contains the set of targets already walked. (guarded by mu)
122 amap := make(map[*TargetNode]struct{})
123
Bob Badour5c12c662022-10-29 22:45:12 -0700124 var walk func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool)
125
126 walk = func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool) {
Bob Badour5c12c662022-10-29 22:45:12 -0700127 continueWalk := func() bool {
Bob Badourf9d23a92022-12-18 14:19:47 -0800128 if _, alreadyWalked := amap[fnode]; alreadyWalked {
Bob Badour5c12c662022-10-29 22:45:12 -0700129 if cs.IsEmpty() {
130 return false
131 }
Bob Badourf9d23a92022-12-18 14:19:47 -0800132 if cs.Difference(fnode.resolution).IsEmpty() {
Bob Badour5c12c662022-10-29 22:45:12 -0700133 // no new conditions
134
135 // pure aggregates never need walking a 2nd time with same conditions
136 if treatAsAggregate {
137 return false
138 }
139 // non-aggregates don't need walking as non-aggregate a 2nd time
140 if !fnode.pure {
141 return false
142 }
143 // previously walked as pure aggregate; need to re-walk as non-aggregate
144 }
Bob Badourf9d23a92022-12-18 14:19:47 -0800145 } else {
146 fnode.resolution |= conditionsFn(fnode)
Bob Badour5c12c662022-10-29 22:45:12 -0700147 }
Bob Badour5c12c662022-10-29 22:45:12 -0700148 fnode.resolution |= cs
149 fnode.pure = treatAsAggregate
150 amap[fnode] = struct{}{}
151 cs = fnode.resolution
152 return true
153 }()
154 if !continueWalk {
155 return
156 }
157 // for each dependency
158 for _, edge := range fnode.edges {
159 // dcs holds the dpendency conditions inherited from the target
160 dcs := targetConditionsPropagatingToDep(lg, edge, cs, treatAsAggregate, conditionsFn)
161 dnode := edge.dependency
162 // add the conditions to the dependency
Bob Badourf9d23a92022-12-18 14:19:47 -0800163 walk(dnode, dcs, treatAsAggregate && dnode.IsContainer())
Bob Badour5c12c662022-10-29 22:45:12 -0700164 }
165 }
166
167 // walk each of the roots
168 for _, rname := range lg.rootFiles {
169 rnode := lg.targets[rname]
Bob Badour5c12c662022-10-29 22:45:12 -0700170 // add the conditions to the root and its transitive closure
Bob Badourf9d23a92022-12-18 14:19:47 -0800171 walk(rnode, NewLicenseConditionSet(), rnode.IsContainer())
Bob Badour5c12c662022-10-29 22:45:12 -0700172 }
Bob Badour5c12c662022-10-29 22:45:12 -0700173 })
Bob Badourb2855152021-12-08 12:52:59 -0800174}