xref: /aosp_15_r20/build/make/tools/compliance/policy_resolve.go (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
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