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