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 Workerimport ( 18*9e94795aSAndroid Build Coastguard Worker "fmt" 19*9e94795aSAndroid Build Coastguard Worker "strings" 20*9e94795aSAndroid Build Coastguard Worker) 21*9e94795aSAndroid Build Coastguard Worker 22*9e94795aSAndroid Build Coastguard Worker// ResolutionSet describes an immutable set of targets and the license 23*9e94795aSAndroid Build Coastguard Worker// conditions each target must satisfy or "resolve" in a specific context. 24*9e94795aSAndroid Build Coastguard Worker// 25*9e94795aSAndroid Build Coastguard Worker// Ultimately, the purpose of recording the license metadata and building a 26*9e94795aSAndroid Build Coastguard Worker// license graph is to identify, describe, and verify the necessary actions or 27*9e94795aSAndroid Build Coastguard Worker// operations for compliance policy. 28*9e94795aSAndroid Build Coastguard Worker// 29*9e94795aSAndroid Build Coastguard Worker// i.e. What is the source-sharing policy? Has it been met? Meet it. 30*9e94795aSAndroid Build Coastguard Worker// 31*9e94795aSAndroid Build Coastguard Worker// i.e. Are there incompatible policy requirements? Such as a source-sharing 32*9e94795aSAndroid Build Coastguard Worker// policy applied to code that policy also says may not be shared? If so, stop 33*9e94795aSAndroid Build Coastguard Worker// and remove the dependencies that create the situation. 34*9e94795aSAndroid Build Coastguard Worker// 35*9e94795aSAndroid Build Coastguard Worker// The ResolutionSet is the base unit for mapping license conditions to the 36*9e94795aSAndroid Build Coastguard Worker// targets triggering some necessary action per policy. Different ResolutionSet 37*9e94795aSAndroid Build Coastguard Worker// values may be calculated for different contexts. 38*9e94795aSAndroid Build Coastguard Worker// 39*9e94795aSAndroid Build Coastguard Worker// e.g. Suppose an unencumbered binary links in a notice .a library. 40*9e94795aSAndroid Build Coastguard Worker// 41*9e94795aSAndroid Build Coastguard Worker// An "unencumbered" condition would originate from the binary, and a "notice" 42*9e94795aSAndroid Build Coastguard Worker// condition would originate from the .a library. A ResolutionSet for the 43*9e94795aSAndroid Build Coastguard Worker// context of the Notice policy might attach both conditions to the binary to 44*9e94795aSAndroid Build Coastguard Worker// act on the origin of each condition. By attaching the notice condition to 45*9e94795aSAndroid Build Coastguard Worker// the binary, the ResolutionSet stipulates the policy that the release of the 46*9e94795aSAndroid Build Coastguard Worker// unencumbered binary must provide suitable notice for the .a library. 47*9e94795aSAndroid Build Coastguard Worker// 48*9e94795aSAndroid Build Coastguard Worker// The resulting ResolutionSet could be used for building a notice file, for 49*9e94795aSAndroid Build Coastguard Worker// validating that a suitable notice has been built into the distribution, or 50*9e94795aSAndroid Build Coastguard Worker// for reporting what notices need to be given. 51*9e94795aSAndroid Build Coastguard Worker// 52*9e94795aSAndroid Build Coastguard Worker// The action is defined by the context. In the above example, the action is 53*9e94795aSAndroid Build Coastguard Worker// providing notice for the module acted on. In another context, the action 54*9e94795aSAndroid Build Coastguard Worker// might be sharing the source-code or preserving the privacy of the module 55*9e94795aSAndroid Build Coastguard Worker// acted on. 56*9e94795aSAndroid Build Coastguard Workertype ResolutionSet map[*TargetNode]ActionSet 57*9e94795aSAndroid Build Coastguard Worker 58*9e94795aSAndroid Build Coastguard Worker// AttachesTo identifies the list of targets triggering action to resolve 59*9e94795aSAndroid Build Coastguard Worker// conditions. (unordered) 60*9e94795aSAndroid Build Coastguard Workerfunc (rs ResolutionSet) AttachesTo() TargetNodeList { 61*9e94795aSAndroid Build Coastguard Worker result := make(TargetNodeList, 0, len(rs)) 62*9e94795aSAndroid Build Coastguard Worker for attachesTo := range rs { 63*9e94795aSAndroid Build Coastguard Worker result = append(result, attachesTo) 64*9e94795aSAndroid Build Coastguard Worker } 65*9e94795aSAndroid Build Coastguard Worker return result 66*9e94795aSAndroid Build Coastguard Worker} 67*9e94795aSAndroid Build Coastguard Worker 68*9e94795aSAndroid Build Coastguard Worker// AttachesToTarget returns true if the set contains conditions that 69*9e94795aSAndroid Build Coastguard Worker// are `attachedTo`. 70*9e94795aSAndroid Build Coastguard Workerfunc (rs ResolutionSet) AttachesToTarget(target *TargetNode) bool { 71*9e94795aSAndroid Build Coastguard Worker _, isPresent := rs[target] 72*9e94795aSAndroid Build Coastguard Worker return isPresent 73*9e94795aSAndroid Build Coastguard Worker} 74*9e94795aSAndroid Build Coastguard Worker 75*9e94795aSAndroid Build Coastguard Worker// IsPureAggregate returns true if `target`, which must be in 76*9e94795aSAndroid Build Coastguard Worker// `AttachesTo()` resolves to a pure aggregate in the resolution. 77*9e94795aSAndroid Build Coastguard Workerfunc (rs ResolutionSet) IsPureAggregate(target *TargetNode) bool { 78*9e94795aSAndroid Build Coastguard Worker _, isPresent := rs[target] 79*9e94795aSAndroid Build Coastguard Worker if !isPresent { 80*9e94795aSAndroid Build Coastguard Worker panic(fmt.Errorf("ResolutionSet.IsPureAggregate(%s): not attached to %s", target.Name(), target.Name())) 81*9e94795aSAndroid Build Coastguard Worker } 82*9e94795aSAndroid Build Coastguard Worker return target.pure 83*9e94795aSAndroid Build Coastguard Worker} 84*9e94795aSAndroid Build Coastguard Worker 85*9e94795aSAndroid Build Coastguard Worker// Resolutions returns the list of resolutions that `attachedTo` 86*9e94795aSAndroid Build Coastguard Worker// target must resolve. Returns empty list if no conditions apply. 87*9e94795aSAndroid Build Coastguard Workerfunc (rs ResolutionSet) Resolutions(attachesTo *TargetNode) ResolutionList { 88*9e94795aSAndroid Build Coastguard Worker as, ok := rs[attachesTo] 89*9e94795aSAndroid Build Coastguard Worker if !ok { 90*9e94795aSAndroid Build Coastguard Worker return nil 91*9e94795aSAndroid Build Coastguard Worker } 92*9e94795aSAndroid Build Coastguard Worker result := make(ResolutionList, 0, len(as)) 93*9e94795aSAndroid Build Coastguard Worker for actsOn, cs := range as { 94*9e94795aSAndroid Build Coastguard Worker result = append(result, Resolution{attachesTo, actsOn, cs}) 95*9e94795aSAndroid Build Coastguard Worker } 96*9e94795aSAndroid Build Coastguard Worker return result 97*9e94795aSAndroid Build Coastguard Worker} 98*9e94795aSAndroid Build Coastguard Worker 99*9e94795aSAndroid Build Coastguard Worker// AllActions returns the set of actions required to resolve the set omitting 100*9e94795aSAndroid Build Coastguard Worker// the attachment. 101*9e94795aSAndroid Build Coastguard Workerfunc (rs ResolutionSet) AllActions() ActionSet { 102*9e94795aSAndroid Build Coastguard Worker result := make(ActionSet) 103*9e94795aSAndroid Build Coastguard Worker for _, as := range rs { 104*9e94795aSAndroid Build Coastguard Worker for actsOn, cs := range as { 105*9e94795aSAndroid Build Coastguard Worker if _, ok := result[actsOn]; ok { 106*9e94795aSAndroid Build Coastguard Worker result[actsOn] = cs.Union(result[actsOn]) 107*9e94795aSAndroid Build Coastguard Worker } else { 108*9e94795aSAndroid Build Coastguard Worker result[actsOn] = cs 109*9e94795aSAndroid Build Coastguard Worker } 110*9e94795aSAndroid Build Coastguard Worker } 111*9e94795aSAndroid Build Coastguard Worker } 112*9e94795aSAndroid Build Coastguard Worker return result 113*9e94795aSAndroid Build Coastguard Worker} 114*9e94795aSAndroid Build Coastguard Worker 115*9e94795aSAndroid Build Coastguard Worker// String returns a human-readable string representation of the set. 116*9e94795aSAndroid Build Coastguard Workerfunc (rs ResolutionSet) String() string { 117*9e94795aSAndroid Build Coastguard Worker var sb strings.Builder 118*9e94795aSAndroid Build Coastguard Worker fmt.Fprintf(&sb, "{") 119*9e94795aSAndroid Build Coastguard Worker sep := "" 120*9e94795aSAndroid Build Coastguard Worker for attachesTo, as := range rs { 121*9e94795aSAndroid Build Coastguard Worker fmt.Fprintf(&sb, "%s%s -> %s", sep, attachesTo.Name(), as.String()) 122*9e94795aSAndroid Build Coastguard Worker sep = ", " 123*9e94795aSAndroid Build Coastguard Worker } 124*9e94795aSAndroid Build Coastguard Worker fmt.Fprintf(&sb, "}") 125*9e94795aSAndroid Build Coastguard Worker return sb.String() 126*9e94795aSAndroid Build Coastguard Worker} 127*9e94795aSAndroid Build Coastguard Worker 128*9e94795aSAndroid Build Coastguard Worker// ActionSet identifies a set of targets to act on and the license conditions 129*9e94795aSAndroid Build Coastguard Worker// the action will resolve. 130*9e94795aSAndroid Build Coastguard Workertype ActionSet map[*TargetNode]LicenseConditionSet 131*9e94795aSAndroid Build Coastguard Worker 132*9e94795aSAndroid Build Coastguard Worker// String returns a human-readable string representation of the set. 133*9e94795aSAndroid Build Coastguard Workerfunc (as ActionSet) String() string { 134*9e94795aSAndroid Build Coastguard Worker var sb strings.Builder 135*9e94795aSAndroid Build Coastguard Worker fmt.Fprintf(&sb, "{") 136*9e94795aSAndroid Build Coastguard Worker sep := "" 137*9e94795aSAndroid Build Coastguard Worker for actsOn, cs := range as { 138*9e94795aSAndroid Build Coastguard Worker fmt.Fprintf(&sb, "%s%s%s", sep, actsOn.Name(), cs.String()) 139*9e94795aSAndroid Build Coastguard Worker sep = ", " 140*9e94795aSAndroid Build Coastguard Worker } 141*9e94795aSAndroid Build Coastguard Worker fmt.Fprintf(&sb, "}") 142*9e94795aSAndroid Build Coastguard Worker return sb.String() 143*9e94795aSAndroid Build Coastguard Worker} 144