2021-10-26 01:21:00 +02:00
|
|
|
// 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
|
2022-01-10 22:50:57 +01:00
|
|
|
// 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
|
2021-10-26 01:21:00 +02:00
|
|
|
// 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.
|
|
|
|
//
|
2022-01-10 22:50:57 +01:00
|
|
|
// 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
|
2021-10-26 01:21:00 +02:00
|
|
|
|
|
|
|
// AttachesTo identifies the list of targets triggering action to resolve
|
|
|
|
// conditions. (unordered)
|
2022-01-10 22:50:57 +01:00
|
|
|
func (rs ResolutionSet) AttachesTo() TargetNodeList {
|
|
|
|
result := make(TargetNodeList, 0, len(rs))
|
|
|
|
for attachesTo := range rs {
|
|
|
|
result = append(result, attachesTo)
|
2021-10-26 01:21:00 +02:00
|
|
|
}
|
2022-01-10 22:50:57 +01:00
|
|
|
return result
|
2021-10-26 01:21:00 +02:00
|
|
|
}
|
|
|
|
|
2022-01-10 22:50:57 +01:00
|
|
|
// AttachesToTarget returns true if the set contains conditions that
|
|
|
|
// are `attachedTo`.
|
|
|
|
func (rs ResolutionSet) AttachesToTarget(target *TargetNode) bool {
|
|
|
|
_, isPresent := rs[target]
|
|
|
|
return isPresent
|
2021-10-26 01:21:00 +02:00
|
|
|
}
|
|
|
|
|
2022-09-22 04:36:59 +02:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2021-10-26 01:21:00 +02:00
|
|
|
// Resolutions returns the list of resolutions that `attachedTo`
|
|
|
|
// target must resolve. Returns empty list if no conditions apply.
|
2022-01-10 22:50:57 +01:00
|
|
|
func (rs ResolutionSet) Resolutions(attachesTo *TargetNode) ResolutionList {
|
|
|
|
as, ok := rs[attachesTo]
|
2021-10-26 01:21:00 +02:00
|
|
|
if !ok {
|
2022-01-10 22:50:57 +01:00
|
|
|
return nil
|
2021-10-26 01:21:00 +02:00
|
|
|
}
|
|
|
|
result := make(ResolutionList, 0, len(as))
|
|
|
|
for actsOn, cs := range as {
|
2022-01-10 22:50:57 +01:00
|
|
|
result = append(result, Resolution{attachesTo, actsOn, cs})
|
2021-10-26 01:21:00 +02:00
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2022-01-31 22:05:53 +01:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2022-01-10 22:50:57 +01:00
|
|
|
// 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 = ", "
|
2021-10-26 01:21:00 +02:00
|
|
|
}
|
2022-01-10 22:50:57 +01:00
|
|
|
fmt.Fprintf(&sb, "}")
|
|
|
|
return sb.String()
|
2021-10-26 01:21:00 +02:00
|
|
|
}
|
|
|
|
|
2022-01-10 22:50:57 +01:00
|
|
|
// ActionSet identifies a set of targets to act on and the license conditions
|
|
|
|
// the action will resolve.
|
|
|
|
type ActionSet map[*TargetNode]LicenseConditionSet
|
2021-10-26 01:21:00 +02:00
|
|
|
|
2022-01-10 22:50:57 +01:00
|
|
|
// 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 = ", "
|
2021-10-26 01:21:00 +02:00
|
|
|
}
|
2022-01-10 22:50:57 +01:00
|
|
|
fmt.Fprintf(&sb, "}")
|
|
|
|
return sb.String()
|
2021-10-26 01:21:00 +02:00
|
|
|
}
|