| // Copyright 2021 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package compliance |
| |
| import ( |
| "fmt" |
| "strings" |
| ) |
| |
| // ResolutionSet describes an immutable set of targets and the license |
| // conditions each target must satisfy or "resolve" in a specific context. |
| // |
| // Ultimately, the purpose of recording the license metadata and building a |
| // license graph is to identify, describe, and verify the necessary actions or |
| // operations for compliance policy. |
| // |
| // i.e. What is the source-sharing policy? Has it been met? Meet it. |
| // |
| // i.e. Are there incompatible policy requirements? Such as a source-sharing |
| // policy applied to code that policy also says may not be shared? If so, stop |
| // and remove the dependencies that create the situation. |
| // |
| // The ResolutionSet is the base unit for mapping license conditions to the |
| // targets triggering some necessary action per policy. Different ResolutionSet |
| // values may be calculated for different contexts. |
| // |
| // e.g. Suppose an unencumbered binary links in a notice .a library. |
| // |
| // An "unencumbered" condition would originate from the binary, and a "notice" |
| // condition would originate from the .a library. A ResolutionSet for the |
| // context of the Notice policy might attach both conditions to the binary to |
| // act on the origin of each condition. By attaching the notice condition to |
| // the binary, the ResolutionSet stipulates the policy that the release of the |
| // unencumbered binary must provide suitable notice for the .a library. |
| // |
| // The resulting ResolutionSet could be used for building a notice file, for |
| // validating that a suitable notice has been built into the distribution, or |
| // for reporting what notices need to be given. |
| // |
| // The action is defined by the context. In the above example, the action is |
| // providing notice for the module acted on. In another context, the action |
| // might be sharing the source-code or preserving the privacy of the module |
| // acted on. |
| type ResolutionSet map[*TargetNode]ActionSet |
| |
| // AttachesTo identifies the list of targets triggering action to resolve |
| // conditions. (unordered) |
| func (rs ResolutionSet) AttachesTo() TargetNodeList { |
| result := make(TargetNodeList, 0, len(rs)) |
| for attachesTo := range rs { |
| result = append(result, attachesTo) |
| } |
| return result |
| } |
| |
| // AttachesToTarget returns true if the set contains conditions that |
| // are `attachedTo`. |
| func (rs ResolutionSet) AttachesToTarget(target *TargetNode) bool { |
| _, isPresent := rs[target] |
| return isPresent |
| } |
| |
| // IsPureAggregate returns true if `target`, which must be in |
| // `AttachesTo()` resolves to a pure aggregate in the resolution. |
| func (rs ResolutionSet) IsPureAggregate(target *TargetNode) bool { |
| _, isPresent := rs[target] |
| if !isPresent { |
| panic(fmt.Errorf("ResolutionSet.IsPureAggregate(%s): not attached to %s", target.Name(), target.Name())) |
| } |
| return target.pure |
| } |
| |
| // Resolutions returns the list of resolutions that `attachedTo` |
| // target must resolve. Returns empty list if no conditions apply. |
| func (rs ResolutionSet) Resolutions(attachesTo *TargetNode) ResolutionList { |
| as, ok := rs[attachesTo] |
| if !ok { |
| return nil |
| } |
| result := make(ResolutionList, 0, len(as)) |
| for actsOn, cs := range as { |
| result = append(result, Resolution{attachesTo, actsOn, cs}) |
| } |
| return result |
| } |
| |
| // AllActions returns the set of actions required to resolve the set omitting |
| // the attachment. |
| func (rs ResolutionSet) AllActions() ActionSet { |
| result := make(ActionSet) |
| for _, as := range rs { |
| for actsOn, cs := range as { |
| if _, ok := result[actsOn]; ok { |
| result[actsOn] = cs.Union(result[actsOn]) |
| } else { |
| result[actsOn] = cs |
| } |
| } |
| } |
| return result |
| } |
| |
| // String returns a human-readable string representation of the set. |
| func (rs ResolutionSet) String() string { |
| var sb strings.Builder |
| fmt.Fprintf(&sb, "{") |
| sep := "" |
| for attachesTo, as := range rs { |
| fmt.Fprintf(&sb, "%s%s -> %s", sep, attachesTo.Name(), as.String()) |
| sep = ", " |
| } |
| fmt.Fprintf(&sb, "}") |
| return sb.String() |
| } |
| |
| // ActionSet identifies a set of targets to act on and the license conditions |
| // the action will resolve. |
| type ActionSet map[*TargetNode]LicenseConditionSet |
| |
| // String returns a human-readable string representation of the set. |
| func (as ActionSet) String() string { |
| var sb strings.Builder |
| fmt.Fprintf(&sb, "{") |
| sep := "" |
| for actsOn, cs := range as { |
| fmt.Fprintf(&sb, "%s%s%s", sep, actsOn.Name(), cs.String()) |
| sep = ", " |
| } |
| fmt.Fprintf(&sb, "}") |
| return sb.String() |
| } |