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