• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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	"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 TargetEdgeList
56
57	// targets identifies, indexes, and describes the entire set of target node files.
58	/// (guarded by mu)
59	targets map[string]*TargetNode
60
61	// wgBU becomes non-nil when the bottom-up resolve begins and reaches 0
62	// (i.e. Wait() proceeds) when the bottom-up resolve completes. (guarded by mu)
63	wgBU *sync.WaitGroup
64
65	// wgTD becomes non-nil when the top-down resolve begins and reaches 0 (i.e. Wait()
66	// proceeds) when the top-down resolve completes. (guarded by mu)
67	wgTD *sync.WaitGroup
68
69	// shippedNodes caches the results of a full walk of nodes identifying targets
70	// distributed either directly or as derivative works. (creation guarded by mu)
71	shippedNodes *TargetNodeSet
72
73	// mu guards against concurrent update.
74	mu sync.Mutex
75}
76
77// Edges returns the list of edges in the graph. (unordered)
78func (lg *LicenseGraph) Edges() TargetEdgeList {
79	edges := make(TargetEdgeList, 0, len(lg.edges))
80	edges = append(edges, lg.edges...)
81	return edges
82}
83
84// Targets returns the list of target nodes in the graph. (unordered)
85func (lg *LicenseGraph) Targets() TargetNodeList {
86	targets := make(TargetNodeList, 0, len(lg.targets))
87	for _, target := range lg.targets {
88		targets = append(targets, target)
89	}
90	return targets
91}
92
93// compliance-only LicenseGraph methods
94
95// newLicenseGraph constructs a new, empty instance of LicenseGraph.
96func newLicenseGraph() *LicenseGraph {
97	return &LicenseGraph{
98		rootFiles: []string{},
99		targets:   make(map[string]*TargetNode),
100	}
101}
102
103// TargetEdge describes a directed, annotated edge from a target to a
104// dependency. (immutable)
105//
106// A LicenseGraph, above, is a set of TargetEdges.
107//
108// i.e. `Target` depends on `Dependency` in the manner described by
109// `Annotations`.
110type TargetEdge struct {
111	// target and dependency identify the nodes connected by the edge.
112	target, dependency *TargetNode
113
114	// annotations identifies the set of compliance-relevant annotations describing the edge.
115	annotations TargetEdgeAnnotations
116}
117
118// Target identifies the target that depends on the dependency.
119//
120// Target needs Dependency to build.
121func (e *TargetEdge) Target() *TargetNode {
122	return e.target
123}
124
125// Dependency identifies the target depended on by the target.
126//
127// Dependency builds without Target, but Target needs Dependency to build.
128func (e *TargetEdge) Dependency() *TargetNode {
129	return e.dependency
130}
131
132// Annotations describes the type of edge by the set of annotations attached to
133// it.
134//
135// Only annotations prescribed by policy have any meaning for licensing, and
136// the meaning for licensing is likewise prescribed by policy. Other annotations
137// are preserved and ignored by policy.
138func (e *TargetEdge) Annotations() TargetEdgeAnnotations {
139	return e.annotations
140}
141
142// String returns a human-readable string representation of the edge.
143func (e *TargetEdge) String() string {
144	return fmt.Sprintf("%s -[%s]> %s", e.target.name, strings.Join(e.annotations.AsList(), ", "), e.dependency.name)
145}
146
147// TargetEdgeList orders lists of edges by target then dependency then annotations.
148type TargetEdgeList []*TargetEdge
149
150// Len returns the count of the elmements in the list.
151func (l TargetEdgeList) Len() int { return len(l) }
152
153// Swap rearranges 2 elements so that each occupies the other's former position.
154func (l TargetEdgeList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
155
156// Less returns true when the `i`th element is lexicographically less than the `j`th.
157func (l TargetEdgeList) Less(i, j int) bool {
158	namei := l[i].target.name
159	namej := l[j].target.name
160	if namei == namej {
161		namei = l[i].dependency.name
162		namej = l[j].dependency.name
163	}
164	if namei == namej {
165		return l[i].annotations.Compare(l[j].annotations) < 0
166	}
167	return namei < namej
168}
169
170// TargetEdgePathSegment describes a single arc in a TargetPath associating the
171// edge with a context `ctx` defined by whatever process is creating the path.
172type TargetEdgePathSegment struct {
173	edge *TargetEdge
174	ctx  interface{}
175}
176
177// Target identifies the target that depends on the dependency.
178//
179// Target needs Dependency to build.
180func (s TargetEdgePathSegment) Target() *TargetNode {
181	return s.edge.target
182}
183
184// Dependency identifies the target depended on by the target.
185//
186// Dependency builds without Target, but Target needs Dependency to build.
187func (s TargetEdgePathSegment) Dependency() *TargetNode {
188	return s.edge.dependency
189}
190
191// Annotations describes the type of edge by the set of annotations attached to
192// it.
193//
194// Only annotations prescribed by policy have any meaning for licensing, and
195// the meaning for licensing is likewise prescribed by policy. Other annotations
196// are preserved and ignored by policy.
197func (s TargetEdgePathSegment) Annotations() TargetEdgeAnnotations {
198	return s.edge.annotations
199}
200
201// Context returns the context associated with the path segment. The type and
202// value of the context defined by the process creating the path.
203func (s TargetEdgePathSegment) Context() interface{} {
204	return s.ctx
205}
206
207// String returns a human-readable string representation of the edge.
208func (s TargetEdgePathSegment) String() string {
209	return fmt.Sprintf("%s -[%s]> %s", s.edge.target.name, strings.Join(s.edge.annotations.AsList(), ", "), s.edge.dependency.name)
210}
211
212// TargetEdgePath describes a sequence of edges starting at a root and ending
213// at some final dependency.
214type TargetEdgePath []TargetEdgePathSegment
215
216// NewTargetEdgePath creates a new, empty path with capacity `cap`.
217func NewTargetEdgePath(cap int) *TargetEdgePath {
218	p := make(TargetEdgePath, 0, cap)
219	return &p
220}
221
222// Push appends a new edge to the list verifying that the target of the new
223// edge is the dependency of the prior.
224func (p *TargetEdgePath) Push(edge *TargetEdge, ctx interface{}) {
225	if len(*p) == 0 {
226		*p = append(*p, TargetEdgePathSegment{edge, ctx})
227		return
228	}
229	if (*p)[len(*p)-1].edge.dependency != edge.target {
230		panic(fmt.Errorf("disjoint path %s does not end at %s", p.String(), edge.target.name))
231	}
232	*p = append(*p, TargetEdgePathSegment{edge, ctx})
233}
234
235// Pop shortens the path by 1 edge.
236func (p *TargetEdgePath) Pop() {
237	if len(*p) == 0 {
238		panic(fmt.Errorf("attempt to remove edge from empty path"))
239	}
240	*p = (*p)[:len(*p)-1]
241}
242
243// Clear makes the path length 0.
244func (p *TargetEdgePath) Clear() {
245	*p = (*p)[:0]
246}
247
248// Copy makes a new path with the same value.
249func (p *TargetEdgePath) Copy() *TargetEdgePath {
250	result := make(TargetEdgePath, 0, len(*p))
251	for _, e := range *p {
252		result = append(result, e)
253	}
254	return &result
255}
256
257// String returns a string representation of the path: [n1 -> n2 -> ... -> nn].
258func (p *TargetEdgePath) String() string {
259	if p == nil {
260		return "nil"
261	}
262	if len(*p) == 0 {
263		return "[]"
264	}
265	var sb strings.Builder
266	fmt.Fprintf(&sb, "[")
267	for _, s := range *p {
268		fmt.Fprintf(&sb, "%s -> ", s.edge.target.name)
269	}
270	lastSegment := (*p)[len(*p)-1]
271	fmt.Fprintf(&sb, "%s]", lastSegment.edge.dependency.name)
272	return sb.String()
273}
274
275// TargetNode describes a module or target identified by the name of a specific
276// metadata file. (immutable)
277//
278// Each metadata file corresponds to a Soong module or to a Make target.
279//
280// A target node can appear as the target or as the dependency in edges.
281// Most target nodes appear as both target in one edge and as dependency in
282// other edges.
283type TargetNode targetNode
284
285// Name returns the string that identifies the target node.
286// i.e. path to license metadata file
287func (tn *TargetNode) Name() string {
288	return tn.name
289}
290
291// Dependencies returns the list of edges to dependencies of `tn`.
292func (tn *TargetNode) Dependencies() TargetEdgeList {
293	edges := make(TargetEdgeList, 0, len(tn.edges))
294	edges = append(edges, tn.edges...)
295	return edges
296}
297
298// PackageName returns the string that identifes the package for the target.
299func (tn *TargetNode) PackageName() string {
300	return tn.proto.GetPackageName()
301}
302
303// ModuleTypes returns the list of module types implementing the target.
304// (unordered)
305//
306// In an ideal world, only 1 module type would implement each target, but the
307// interactions between Soong and Make for host versus product and for a
308// variety of architectures sometimes causes multiple module types per target
309// (often a regular build target and a prebuilt.)
310func (tn *TargetNode) ModuleTypes() []string {
311	return append([]string{}, tn.proto.ModuleTypes...)
312}
313
314// ModuleClasses returns the list of module classes implementing the target.
315// (unordered)
316func (tn *TargetNode) ModuleClasses() []string {
317	return append([]string{}, tn.proto.ModuleClasses...)
318}
319
320// Projects returns the projects defining the target node. (unordered)
321//
322// In an ideal world, only 1 project defines a target, but the interaction
323// between Soong and Make for a variety of architectures and for host versus
324// product means a module is sometimes defined more than once.
325func (tn *TargetNode) Projects() []string {
326	return append([]string{}, tn.proto.Projects...)
327}
328
329// LicenseKinds returns the list of license kind names for the module or
330// target. (unordered)
331//
332// e.g. SPDX-license-identifier-MIT or legacy_proprietary
333func (tn *TargetNode) LicenseKinds() []string {
334	return append([]string{}, tn.proto.LicenseKinds...)
335}
336
337// LicenseConditions returns a copy of the set of license conditions
338// originating at the target. The values that appear and how each is resolved
339// is a matter of policy. (unordered)
340//
341// e.g. notice or proprietary
342func (tn *TargetNode) LicenseConditions() LicenseConditionSet {
343	return tn.licenseConditions
344}
345
346// LicenseTexts returns the paths to the files containing the license texts for
347// the target. (unordered)
348func (tn *TargetNode) LicenseTexts() []string {
349	return append([]string{}, tn.proto.LicenseTexts...)
350}
351
352// IsContainer returns true if the target represents a container that merely
353// aggregates other targets.
354func (tn *TargetNode) IsContainer() bool {
355	return tn.proto.GetIsContainer()
356}
357
358// Built returns the list of files built by the module or target. (unordered)
359func (tn *TargetNode) Built() []string {
360	return append([]string{}, tn.proto.Built...)
361}
362
363// Installed returns the list of files installed by the module or target.
364// (unordered)
365func (tn *TargetNode) Installed() []string {
366	return append([]string{}, tn.proto.Installed...)
367}
368
369// TargetFiles returns the list of files built or installed by the module or
370// target. (unordered)
371func (tn *TargetNode) TargetFiles() []string {
372	return append(tn.proto.Built, tn.proto.Installed...)
373}
374
375// InstallMap returns the list of path name transformations to make to move
376// files from their original location in the file system to their destination
377// inside a container. (unordered)
378func (tn *TargetNode) InstallMap() []InstallMap {
379	result := make([]InstallMap, 0, len(tn.proto.InstallMap))
380	for _, im := range tn.proto.InstallMap {
381		result = append(result, InstallMap{im.GetFromPath(), im.GetContainerPath()})
382	}
383	return result
384}
385
386// Sources returns the list of file names depended on by the target, which may
387// be a proper subset of those made available by dependency modules.
388// (unordered)
389func (tn *TargetNode) Sources() []string {
390	return append([]string{}, tn.proto.Sources...)
391}
392
393// InstallMap describes the mapping from an input filesystem file to file in a
394// container.
395type InstallMap struct {
396	// FromPath is the input path on the filesystem.
397	FromPath string
398
399	// ContainerPath is the path to the same file inside the container or
400	// installed location.
401	ContainerPath string
402}
403
404// TargetEdgeAnnotations describes an immutable set of annotations attached to
405// an edge from a target to a dependency.
406//
407// Annotations typically distinguish between static linkage versus dynamic
408// versus tools that are used at build time but are not linked in any way.
409type TargetEdgeAnnotations struct {
410	annotations map[string]struct{}
411}
412
413// newEdgeAnnotations creates a new instance of TargetEdgeAnnotations.
414func newEdgeAnnotations() TargetEdgeAnnotations {
415	return TargetEdgeAnnotations{make(map[string]struct{})}
416}
417
418// HasAnnotation returns true if an annotation `ann` is in the set.
419func (ea TargetEdgeAnnotations) HasAnnotation(ann string) bool {
420	_, ok := ea.annotations[ann]
421	return ok
422}
423
424// Compare orders TargetAnnotations returning:
425// -1 when ea < other,
426// +1 when ea > other, and
427// 0 when ea == other.
428func (ea TargetEdgeAnnotations) Compare(other TargetEdgeAnnotations) int {
429	a1 := ea.AsList()
430	a2 := other.AsList()
431	sort.Strings(a1)
432	sort.Strings(a2)
433	for k := 0; k < len(a1) && k < len(a2); k++ {
434		if a1[k] < a2[k] {
435			return -1
436		}
437		if a1[k] > a2[k] {
438			return 1
439		}
440	}
441	if len(a1) < len(a2) {
442		return -1
443	}
444	if len(a1) > len(a2) {
445		return 1
446	}
447	return 0
448}
449
450// AsList returns the list of annotation names attached to the edge.
451// (unordered)
452func (ea TargetEdgeAnnotations) AsList() []string {
453	l := make([]string, 0, len(ea.annotations))
454	for ann := range ea.annotations {
455		l = append(l, ann)
456	}
457	return l
458}
459
460// TargetNodeSet describes a set of distinct nodes in a license graph.
461type TargetNodeSet struct {
462	nodes map[*TargetNode]struct{}
463}
464
465// Contains returns true when `target` is an element of the set.
466func (ts *TargetNodeSet) Contains(target *TargetNode) bool {
467	_, isPresent := ts.nodes[target]
468	return isPresent
469}
470
471// AsList returns the list of target nodes in the set. (unordered)
472func (ts *TargetNodeSet) AsList() TargetNodeList {
473	result := make(TargetNodeList, 0, len(ts.nodes))
474	for tn := range ts.nodes {
475		result = append(result, tn)
476	}
477	return result
478}
479
480// Names returns the array of target node namess in the set. (unordered)
481func (ts *TargetNodeSet) Names() []string {
482	result := make([]string, 0, len(ts.nodes))
483	for tn := range ts.nodes {
484		result = append(result, tn.name)
485	}
486	return result
487}
488
489// String returns a human-readable string representation of the set.
490func (ts *TargetNodeSet) String() string {
491	return fmt.Sprintf("{%s}", strings.Join(ts.Names(), ", "))
492}
493
494// TargetNodeList orders a list of targets by name.
495type TargetNodeList []*TargetNode
496
497// Len returns the count of elements in the list.
498func (l TargetNodeList) Len() int { return len(l) }
499
500// Swap rearranges 2 elements so that each occupies the other's former position.
501func (l TargetNodeList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
502
503// Less returns true when the `i`th element is lexicographicallt less than the `j`th.
504func (l TargetNodeList) Less(i, j int) bool {
505	return l[i].name < l[j].name
506}
507
508// String returns a string representation of the list.
509func (l TargetNodeList) String() string {
510	var sb strings.Builder
511	fmt.Fprintf(&sb, "[")
512	sep := ""
513	for _, tn := range l {
514		fmt.Fprintf(&sb, "%s%s", sep, tn.name)
515		sep = " "
516	}
517	fmt.Fprintf(&sb, "]")
518	return sb.String()
519}
520
521// Names returns an array the names of the nodes in the same order as the nodes in the list.
522func (l TargetNodeList) Names() []string {
523	result := make([]string, 0, len(l))
524	for _, tn := range l {
525		result = append(result, tn.name)
526	}
527	return result
528}
529