blob: f26e7e83a7b4dd0db27e025d30457772fd62be9e [file] [log] [blame]
Bob Badoura99ac622021-10-25 16:21:00 -07001// 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 "sort"
20 "strings"
21 "sync"
22)
23
24// LicenseGraph describes the immutable license metadata for a set of root
25// targets and the transitive closure of their dependencies.
26//
27// Alternatively, a graph is a set of edges. In this case directed, annotated
28// edges from targets to dependencies.
29//
30// A LicenseGraph provides the frame of reference for all of the other types
31// defined here. It is possible to have multiple graphs, and to have targets,
32// edges, and resolutions from multiple graphs. But it is an error to try to
33// mix items from different graphs in the same operation.
34// May panic if attempted.
35//
36// The compliance package assumes specific private implementations of each of
37// these interfaces. May panic if attempts are made to combine different
38// implementations of some interfaces with expected implementations of other
39// interfaces here.
40type LicenseGraph struct {
41 // rootFiles identifies the original set of files to read. (immutable)
42 //
43 // Defines the starting "top" for top-down walks.
44 //
45 // Alternatively, an instance of licenseGraphImp conceptually defines a scope within
46 // the universe of build graphs as a sub-graph rooted at rootFiles where all edges
47 // and targets for the instance are defined relative to and within that scope. For
48 // most analyses, the correct scope is to root the graph at all of the distributed
49 // artifacts.
50 rootFiles []string
51
52 // edges lists the directed edges in the graph from target to dependency. (guarded by mu)
53 //
54 // Alternatively, the graph is the set of `edges`.
55 edges []*dependencyEdge
56
57 // targets identifies, indexes by name, and describes the entire set of target node files.
58 /// (guarded by mu)
59 targets map[string]*TargetNode
60
61 // index facilitates looking up edges from targets. (creation guarded by my)
62 //
63 // This is a forward index from target to dependencies. i.e. "top-down"
64 index map[string][]*dependencyEdge
65
66 // rsBU caches the results of a full bottom-up resolve. (creation guarded by mu)
67 //
68 // A bottom-up resolve is a prerequisite for all of the top-down resolves so caching
69 // the result is a performance win.
70 rsBU *ResolutionSet
71
72 // rsTD caches the results of a full top-down resolve. (creation guarded by mu)
73 //
74 // A top-down resolve is a prerequisite for final resolutions.
75 // e.g. a shipped node inheriting a `restricted` condition from a parent through a
76 // dynamic dependency implies a notice dependency on the parent; even though, the
77 // distribution does not happen as a result of the dynamic dependency itself.
78 rsTD *ResolutionSet
79
80 // shippedNodes caches the results of a full walk of nodes identifying targets
81 // distributed either directly or as derivative works. (creation guarded by mu)
82 shippedNodes *TargetNodeSet
83
84 // mu guards against concurrent update.
85 mu sync.Mutex
86}
87
88// TargetNode returns the target node identified by `name`.
89func (lg *LicenseGraph) TargetNode(name string) *TargetNode {
90 if _, ok := lg.targets[name]; !ok {
91 panic(fmt.Errorf("target node %q missing from graph", name))
92 }
93 return lg.targets[name]
94}
95
96// HasTargetNode returns true if a target node identified by `name` appears in
97// the graph.
98func (lg *LicenseGraph) HasTargetNode(name string) bool {
99 _, isPresent := lg.targets[name]
100 return isPresent
101}
102
103// Edges returns the list of edges in the graph. (unordered)
104func (lg *LicenseGraph) Edges() TargetEdgeList {
105 edges := make(TargetEdgeList, 0, len(lg.edges))
106 for _, e := range lg.edges {
107 edges = append(edges, TargetEdge{lg, e})
108 }
109 return edges
110}
111
112// Targets returns the list of target nodes in the graph. (unordered)
113func (lg *LicenseGraph) Targets() TargetNodeList {
114 targets := make(TargetNodeList, 0, len(lg.targets))
115 for target := range lg.targets {
116 targets = append(targets, lg.targets[target])
117 }
118 return targets
119}
120
121// compliance-only LicenseGraph methods
122
123// newLicenseGraph constructs a new, empty instance of LicenseGraph.
124func newLicenseGraph() *LicenseGraph {
125 return &LicenseGraph{
126 rootFiles: []string{},
127 edges: make([]*dependencyEdge, 0, 1000),
128 targets: make(map[string]*TargetNode),
129 }
130}
131
132// indexForward guarantees the `index` map is populated to look up edges by
133// `target`.
134func (lg *LicenseGraph) indexForward() {
135 lg.mu.Lock()
136 defer func() {
137 lg.mu.Unlock()
138 }()
139
140 if lg.index != nil {
141 return
142 }
143
144 lg.index = make(map[string][]*dependencyEdge)
145 for _, e := range lg.edges {
146 if _, ok := lg.index[e.target]; ok {
147 lg.index[e.target] = append(lg.index[e.target], e)
148 } else {
149 lg.index[e.target] = []*dependencyEdge{e}
150 }
151 }
152}
153
154// TargetEdge describes a directed, annotated edge from a target to a
155// dependency. (immutable)
156//
157// A LicenseGraph, above, is a set of TargetEdges.
158//
159// i.e. `Target` depends on `Dependency` in the manner described by
160// `Annotations`.
161type TargetEdge struct {
162 // lg identifies the scope, i.e. license graph, in which the edge appears.
163 lg *LicenseGraph
164
165 // e identifies describes the target, dependency, and annotations of the edge.
166 e *dependencyEdge
167}
168
169// Target identifies the target that depends on the dependency.
170//
171// Target needs Dependency to build.
172func (e TargetEdge) Target() *TargetNode {
173 return e.lg.targets[e.e.target]
174}
175
176// Dependency identifies the target depended on by the target.
177//
178// Dependency builds without Target, but Target needs Dependency to build.
179func (e TargetEdge) Dependency() *TargetNode {
180 return e.lg.targets[e.e.dependency]
181}
182
183// Annotations describes the type of edge by the set of annotations attached to
184// it.
185//
186// Only annotations prescribed by policy have any meaning for licensing, and
187// the meaning for licensing is likewise prescribed by policy. Other annotations
188// are preserved and ignored by policy.
189func (e TargetEdge) Annotations() TargetEdgeAnnotations {
190 return e.e.annotations
191}
192
193// TargetEdgeList orders lists of edges by target then dependency then annotations.
194type TargetEdgeList []TargetEdge
195
196// Len returns the count of the elmements in the list.
197func (l TargetEdgeList) Len() int { return len(l) }
198
199// Swap rearranges 2 elements so that each occupies the other's former position.
200func (l TargetEdgeList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
201
202// Less returns true when the `i`th element is lexicographically less than the `j`th.
203func (l TargetEdgeList) Less(i, j int) bool {
204 if l[i].e.target == l[j].e.target {
205 if l[i].e.dependency == l[j].e.dependency {
206 return l[i].e.annotations.Compare(l[j].e.annotations) < 0
207 }
208 return l[i].e.dependency < l[j].e.dependency
209 }
210 return l[i].e.target < l[j].e.target
211}
212
213// TargetEdgePath describes a sequence of edges starting at a root and ending
214// at some final dependency.
215type TargetEdgePath []TargetEdge
216
217// NewTargetEdgePath creates a new, empty path with capacity `cap`.
218func NewTargetEdgePath(cap int) *TargetEdgePath {
219 p := make(TargetEdgePath, 0, cap)
220 return &p
221}
222
223// Push appends a new edge to the list verifying that the target of the new
224// edge is the dependency of the prior.
225func (p *TargetEdgePath) Push(edge TargetEdge) {
226 if len(*p) == 0 {
227 *p = append(*p, edge)
228 return
229 }
230 if (*p)[len(*p)-1].e.dependency != edge.e.target {
231 panic(fmt.Errorf("disjoint path %s does not end at %s", p.String(), edge.e.target))
232 }
233 *p = append(*p, edge)
234}
235
236// Pop shortens the path by 1 edge.
237func (p *TargetEdgePath) Pop() {
238 if len(*p) == 0 {
239 panic(fmt.Errorf("attempt to remove edge from empty path"))
240 }
241 *p = (*p)[:len(*p)-1]
242}
243
244// Clear makes the path length 0.
245func (p *TargetEdgePath) Clear() {
246 *p = (*p)[:0]
247}
248
249// String returns a string representation of the path: [n1 -> n2 -> ... -> nn].
250func (p *TargetEdgePath) String() string {
251 if p == nil {
252 return "nil"
253 }
254 if len(*p) == 0 {
255 return "[]"
256 }
257 var sb strings.Builder
258 fmt.Fprintf(&sb, "[")
259 for _, e := range *p {
260 fmt.Fprintf(&sb, "%s -> ", e.e.target)
261 }
262 fmt.Fprintf(&sb, "%s]", (*p)[len(*p)-1].e.dependency)
263 return sb.String()
264}
265
266// TargetNode describes a module or target identified by the name of a specific
267// metadata file. (immutable)
268//
269// Each metadata file corresponds to a Soong module or to a Make target.
270//
271// A target node can appear as the target or as the dependency in edges.
272// Most target nodes appear as both target in one edge and as dependency in
273// other edges.
274type TargetNode targetNode
275
276// Name returns the string that identifies the target node.
277// i.e. path to license metadata file
278func (tn *TargetNode) Name() string {
279 return tn.name
280}
281
282// PackageName returns the string that identifes the package for the target.
283func (tn *TargetNode) PackageName() string {
284 return tn.proto.GetPackageName()
285}
286
287// ModuleTypes returns the list of module types implementing the target.
288// (unordered)
289//
290// In an ideal world, only 1 module type would implement each target, but the
291// interactions between Soong and Make for host versus product and for a
292// variety of architectures sometimes causes multiple module types per target
293// (often a regular build target and a prebuilt.)
294func (tn *TargetNode) ModuleTypes() []string {
295 return append([]string{}, tn.proto.ModuleTypes...)
296}
297
298// ModuleClasses returns the list of module classes implementing the target.
299// (unordered)
300func (tn *TargetNode) ModuleClasses() []string {
301 return append([]string{}, tn.proto.ModuleClasses...)
302}
303
304// Projects returns the projects defining the target node. (unordered)
305//
306// In an ideal world, only 1 project defines a target, but the interaction
307// between Soong and Make for a variety of architectures and for host versus
308// product means a module is sometimes defined more than once.
309func (tn *TargetNode) Projects() []string {
310 return append([]string{}, tn.proto.Projects...)
311}
312
313// LicenseKinds returns the list of license kind names for the module or
314// target. (unordered)
315//
316// e.g. SPDX-license-identifier-MIT or legacy_proprietary
317func (tn *TargetNode) LicenseKinds() []string {
318 return append([]string{}, tn.proto.LicenseKinds...)
319}
320
321// LicenseConditions returns a copy of the set of license conditions
322// originating at the target. The values that appear and how each is resolved
323// is a matter of policy. (unordered)
324//
325// e.g. notice or proprietary
326func (tn *TargetNode) LicenseConditions() *LicenseConditionSet {
327 result := newLicenseConditionSet()
328 result.add(tn, tn.proto.LicenseConditions...)
329 return result
330}
331
332// LicenseTexts returns the paths to the files containing the license texts for
333// the target. (unordered)
334func (tn *TargetNode) LicenseTexts() []string {
335 return append([]string{}, tn.proto.LicenseTexts...)
336}
337
338// IsContainer returns true if the target represents a container that merely
339// aggregates other targets.
340func (tn *TargetNode) IsContainer() bool {
341 return tn.proto.GetIsContainer()
342}
343
344// Built returns the list of files built by the module or target. (unordered)
345func (tn *TargetNode) Built() []string {
346 return append([]string{}, tn.proto.Built...)
347}
348
349// Installed returns the list of files installed by the module or target.
350// (unordered)
351func (tn *TargetNode) Installed() []string {
352 return append([]string{}, tn.proto.Installed...)
353}
354
355// InstallMap returns the list of path name transformations to make to move
356// files from their original location in the file system to their destination
357// inside a container. (unordered)
358func (tn *TargetNode) InstallMap() []InstallMap {
359 result := make([]InstallMap, 0, len(tn.proto.InstallMap))
360 for _, im := range tn.proto.InstallMap {
361 result = append(result, InstallMap{im.GetFromPath(), im.GetContainerPath()})
362 }
363 return result
364}
365
366// Sources returns the list of file names depended on by the target, which may
367// be a proper subset of those made available by dependency modules.
368// (unordered)
369func (tn *TargetNode) Sources() []string {
370 return append([]string{}, tn.proto.Sources...)
371}
372
373// InstallMap describes the mapping from an input filesystem file to file in a
374// container.
375type InstallMap struct {
376 // FromPath is the input path on the filesystem.
377 FromPath string
378
379 // ContainerPath is the path to the same file inside the container or
380 // installed location.
381 ContainerPath string
382}
383
384// TargetEdgeAnnotations describes an immutable set of annotations attached to
385// an edge from a target to a dependency.
386//
387// Annotations typically distinguish between static linkage versus dynamic
388// versus tools that are used at build time but are not linked in any way.
389type TargetEdgeAnnotations struct {
Bob Badour5446a6f2022-01-10 18:44:59 -0800390 annotations map[string]struct{}
Bob Badoura99ac622021-10-25 16:21:00 -0700391}
392
393// newEdgeAnnotations creates a new instance of TargetEdgeAnnotations.
394func newEdgeAnnotations() TargetEdgeAnnotations {
Bob Badour5446a6f2022-01-10 18:44:59 -0800395 return TargetEdgeAnnotations{make(map[string]struct{})}
Bob Badoura99ac622021-10-25 16:21:00 -0700396}
397
398// HasAnnotation returns true if an annotation `ann` is in the set.
399func (ea TargetEdgeAnnotations) HasAnnotation(ann string) bool {
400 _, ok := ea.annotations[ann]
401 return ok
402}
403
404// Compare orders TargetAnnotations returning:
405// -1 when ea < other,
406// +1 when ea > other, and
407// 0 when ea == other.
408func (ea TargetEdgeAnnotations) Compare(other TargetEdgeAnnotations) int {
409 a1 := ea.AsList()
410 a2 := other.AsList()
411 sort.Strings(a1)
412 sort.Strings(a2)
413 for k := 0; k < len(a1) && k < len(a2); k++ {
414 if a1[k] < a2[k] {
415 return -1
416 }
417 if a1[k] > a2[k] {
418 return 1
419 }
420 }
421 if len(a1) < len(a2) {
422 return -1
423 }
424 if len(a1) > len(a2) {
425 return 1
426 }
427 return 0
428}
429
430// AsList returns the list of annotation names attached to the edge.
431// (unordered)
432func (ea TargetEdgeAnnotations) AsList() []string {
433 l := make([]string, 0, len(ea.annotations))
434 for ann := range ea.annotations {
435 l = append(l, ann)
436 }
437 return l
438}
439
440// TargetNodeSet describes a set of distinct nodes in a license graph.
441type TargetNodeSet struct {
Bob Badour5446a6f2022-01-10 18:44:59 -0800442 nodes map[*TargetNode]struct{}
Bob Badoura99ac622021-10-25 16:21:00 -0700443}
444
445// Contains returns true when `target` is an element of the set.
446func (ts *TargetNodeSet) Contains(target *TargetNode) bool {
447 _, isPresent := ts.nodes[target]
448 return isPresent
449}
450
451// AsList returns the list of target nodes in the set. (unordered)
452func (ts *TargetNodeSet) AsList() TargetNodeList {
453 result := make(TargetNodeList, 0, len(ts.nodes))
454 for tn := range ts.nodes {
455 result = append(result, tn)
456 }
457 return result
458}
459
460// Names returns the array of target node namess in the set. (unordered)
461func (ts *TargetNodeSet) Names() []string {
462 result := make([]string, 0, len(ts.nodes))
463 for tn := range ts.nodes {
464 result = append(result, tn.name)
465 }
466 return result
467}
468
469// TargetNodeList orders a list of targets by name.
470type TargetNodeList []*TargetNode
471
472// Len returns the count of elements in the list.
473func (l TargetNodeList) Len() int { return len(l) }
474
475// Swap rearranges 2 elements so that each occupies the other's former position.
476func (l TargetNodeList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
477
478// Less returns true when the `i`th element is lexicographicallt less than the `j`th.
479func (l TargetNodeList) Less(i, j int) bool {
480 return l[i].name < l[j].name
481}
482
483// String returns a string representation of the list.
484func (l TargetNodeList) String() string {
485 var sb strings.Builder
486 fmt.Fprintf(&sb, "[")
487 sep := ""
488 for _, tn := range l {
489 fmt.Fprintf(&sb, "%s%s", sep, tn.name)
490 sep = " "
491 }
492 fmt.Fprintf(&sb, "]")
493 return sb.String()
494}
495
496// Names returns an array the names of the nodes in the same order as the nodes in the list.
497func (l TargetNodeList) Names() []string {
498 result := make([]string, 0, len(l))
499 for _, tn := range l {
500 result = append(result, tn.name)
501 }
502 return result
503}