blob: d7f1750694d47f1c7082a7ea328ba77ed0dbeda5 [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`.
Bob Badour103eb0f2022-01-10 13:50:57 -080055 edges TargetEdgeList
Bob Badoura99ac622021-10-25 16:21:00 -070056
Bob Badour103eb0f2022-01-10 13:50:57 -080057 // targets identifies, indexes, and describes the entire set of target node files.
Bob Badoura99ac622021-10-25 16:21:00 -070058 /// (guarded by mu)
59 targets map[string]*TargetNode
60
Bob Badour103eb0f2022-01-10 13:50:57 -080061 // 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
Bob Badoura99ac622021-10-25 16:21:00 -070064
Bob Badour103eb0f2022-01-10 13:50:57 -080065 // 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
Bob Badoura99ac622021-10-25 16:21:00 -070068
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
Bob Badoura99ac622021-10-25 16:21:00 -070077// Edges returns the list of edges in the graph. (unordered)
78func (lg *LicenseGraph) Edges() TargetEdgeList {
79 edges := make(TargetEdgeList, 0, len(lg.edges))
Bob Badour103eb0f2022-01-10 13:50:57 -080080 edges = append(edges, lg.edges...)
Bob Badoura99ac622021-10-25 16:21:00 -070081 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))
Bob Badour103eb0f2022-01-10 13:50:57 -080087 for _, target := range lg.targets {
88 targets = append(targets, target)
Bob Badoura99ac622021-10-25 16:21:00 -070089 }
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{},
Bob Badoura99ac622021-10-25 16:21:00 -070099 targets: make(map[string]*TargetNode),
100 }
101}
102
Bob Badoura99ac622021-10-25 16:21:00 -0700103// 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 {
Bob Badour103eb0f2022-01-10 13:50:57 -0800111 // target and dependency identify the nodes connected by the edge.
112 target, dependency *TargetNode
Bob Badoura99ac622021-10-25 16:21:00 -0700113
Bob Badour103eb0f2022-01-10 13:50:57 -0800114 // annotations identifies the set of compliance-relevant annotations describing the edge.
115 annotations TargetEdgeAnnotations
Bob Badoura99ac622021-10-25 16:21:00 -0700116}
117
118// Target identifies the target that depends on the dependency.
119//
120// Target needs Dependency to build.
Bob Badour103eb0f2022-01-10 13:50:57 -0800121func (e *TargetEdge) Target() *TargetNode {
122 return e.target
Bob Badoura99ac622021-10-25 16:21:00 -0700123}
124
125// Dependency identifies the target depended on by the target.
126//
127// Dependency builds without Target, but Target needs Dependency to build.
Bob Badour103eb0f2022-01-10 13:50:57 -0800128func (e *TargetEdge) Dependency() *TargetNode {
129 return e.dependency
Bob Badoura99ac622021-10-25 16:21:00 -0700130}
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.
Bob Badour103eb0f2022-01-10 13:50:57 -0800138func (e *TargetEdge) Annotations() TargetEdgeAnnotations {
139 return e.annotations
140}
141
Ibrahim Kanouchebedf1a82022-10-22 01:28:05 +0000142// IsRuntimeDependency returns true for edges representing shared libraries
143// linked dynamically at runtime.
144func (e *TargetEdge) IsRuntimeDependency() bool {
145 return edgeIsDynamicLink(e)
146}
147
148// IsDerivation returns true for edges where the target is a derivative
149// work of dependency.
150func (e *TargetEdge) IsDerivation() bool {
151 return edgeIsDerivation(e)
152}
153
154// IsBuildTool returns true for edges where the target is built
155// by dependency.
156func (e *TargetEdge) IsBuildTool() bool {
157 return !edgeIsDerivation(e) && !edgeIsDynamicLink(e)
158}
159
Bob Badour103eb0f2022-01-10 13:50:57 -0800160// String returns a human-readable string representation of the edge.
161func (e *TargetEdge) String() string {
162 return fmt.Sprintf("%s -[%s]> %s", e.target.name, strings.Join(e.annotations.AsList(), ", "), e.dependency.name)
Bob Badoura99ac622021-10-25 16:21:00 -0700163}
164
165// TargetEdgeList orders lists of edges by target then dependency then annotations.
Bob Badour103eb0f2022-01-10 13:50:57 -0800166type TargetEdgeList []*TargetEdge
Bob Badoura99ac622021-10-25 16:21:00 -0700167
168// Len returns the count of the elmements in the list.
Colin Cross35f79c32022-01-27 15:18:52 -0800169func (l TargetEdgeList) Len() int { return len(l) }
Bob Badoura99ac622021-10-25 16:21:00 -0700170
171// Swap rearranges 2 elements so that each occupies the other's former position.
172func (l TargetEdgeList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
173
174// Less returns true when the `i`th element is lexicographically less than the `j`th.
175func (l TargetEdgeList) Less(i, j int) bool {
Bob Badour103eb0f2022-01-10 13:50:57 -0800176 namei := l[i].target.name
177 namej := l[j].target.name
178 if namei == namej {
179 namei = l[i].dependency.name
180 namej = l[j].dependency.name
Bob Badoura99ac622021-10-25 16:21:00 -0700181 }
Bob Badour103eb0f2022-01-10 13:50:57 -0800182 if namei == namej {
183 return l[i].annotations.Compare(l[j].annotations) < 0
184 }
185 return namei < namej
186}
187
188// TargetEdgePathSegment describes a single arc in a TargetPath associating the
189// edge with a context `ctx` defined by whatever process is creating the path.
190type TargetEdgePathSegment struct {
191 edge *TargetEdge
Colin Cross35f79c32022-01-27 15:18:52 -0800192 ctx interface{}
Bob Badour103eb0f2022-01-10 13:50:57 -0800193}
194
195// Target identifies the target that depends on the dependency.
196//
197// Target needs Dependency to build.
198func (s TargetEdgePathSegment) Target() *TargetNode {
199 return s.edge.target
200}
201
202// Dependency identifies the target depended on by the target.
203//
204// Dependency builds without Target, but Target needs Dependency to build.
205func (s TargetEdgePathSegment) Dependency() *TargetNode {
206 return s.edge.dependency
207}
208
Ibrahim Kanouchebedf1a82022-10-22 01:28:05 +0000209// Edge describes the target edge.
210func (s TargetEdgePathSegment) Edge() *TargetEdge {
211 return s.edge
212}
213
Bob Badour103eb0f2022-01-10 13:50:57 -0800214// Annotations describes the type of edge by the set of annotations attached to
215// it.
216//
217// Only annotations prescribed by policy have any meaning for licensing, and
218// the meaning for licensing is likewise prescribed by policy. Other annotations
219// are preserved and ignored by policy.
220func (s TargetEdgePathSegment) Annotations() TargetEdgeAnnotations {
221 return s.edge.annotations
222}
223
224// Context returns the context associated with the path segment. The type and
225// value of the context defined by the process creating the path.
226func (s TargetEdgePathSegment) Context() interface{} {
227 return s.ctx
228}
229
230// String returns a human-readable string representation of the edge.
231func (s TargetEdgePathSegment) String() string {
232 return fmt.Sprintf("%s -[%s]> %s", s.edge.target.name, strings.Join(s.edge.annotations.AsList(), ", "), s.edge.dependency.name)
Bob Badoura99ac622021-10-25 16:21:00 -0700233}
234
235// TargetEdgePath describes a sequence of edges starting at a root and ending
236// at some final dependency.
Bob Badour103eb0f2022-01-10 13:50:57 -0800237type TargetEdgePath []TargetEdgePathSegment
Bob Badoura99ac622021-10-25 16:21:00 -0700238
239// NewTargetEdgePath creates a new, empty path with capacity `cap`.
240func NewTargetEdgePath(cap int) *TargetEdgePath {
241 p := make(TargetEdgePath, 0, cap)
242 return &p
243}
244
245// Push appends a new edge to the list verifying that the target of the new
246// edge is the dependency of the prior.
Bob Badour103eb0f2022-01-10 13:50:57 -0800247func (p *TargetEdgePath) Push(edge *TargetEdge, ctx interface{}) {
Bob Badoura99ac622021-10-25 16:21:00 -0700248 if len(*p) == 0 {
Bob Badour103eb0f2022-01-10 13:50:57 -0800249 *p = append(*p, TargetEdgePathSegment{edge, ctx})
Bob Badoura99ac622021-10-25 16:21:00 -0700250 return
251 }
Bob Badour103eb0f2022-01-10 13:50:57 -0800252 if (*p)[len(*p)-1].edge.dependency != edge.target {
253 panic(fmt.Errorf("disjoint path %s does not end at %s", p.String(), edge.target.name))
Bob Badoura99ac622021-10-25 16:21:00 -0700254 }
Bob Badour103eb0f2022-01-10 13:50:57 -0800255 *p = append(*p, TargetEdgePathSegment{edge, ctx})
Bob Badoura99ac622021-10-25 16:21:00 -0700256}
257
258// Pop shortens the path by 1 edge.
259func (p *TargetEdgePath) Pop() {
260 if len(*p) == 0 {
261 panic(fmt.Errorf("attempt to remove edge from empty path"))
262 }
263 *p = (*p)[:len(*p)-1]
264}
265
266// Clear makes the path length 0.
267func (p *TargetEdgePath) Clear() {
268 *p = (*p)[:0]
269}
270
Bob Badoure6fdd142021-12-09 22:10:43 -0800271// Copy makes a new path with the same value.
272func (p *TargetEdgePath) Copy() *TargetEdgePath {
273 result := make(TargetEdgePath, 0, len(*p))
274 for _, e := range *p {
275 result = append(result, e)
276 }
277 return &result
278}
279
Bob Badoura99ac622021-10-25 16:21:00 -0700280// String returns a string representation of the path: [n1 -> n2 -> ... -> nn].
281func (p *TargetEdgePath) String() string {
282 if p == nil {
283 return "nil"
284 }
285 if len(*p) == 0 {
286 return "[]"
287 }
288 var sb strings.Builder
289 fmt.Fprintf(&sb, "[")
Bob Badour103eb0f2022-01-10 13:50:57 -0800290 for _, s := range *p {
291 fmt.Fprintf(&sb, "%s -> ", s.edge.target.name)
Bob Badoura99ac622021-10-25 16:21:00 -0700292 }
Bob Badour103eb0f2022-01-10 13:50:57 -0800293 lastSegment := (*p)[len(*p)-1]
294 fmt.Fprintf(&sb, "%s]", lastSegment.edge.dependency.name)
Bob Badoura99ac622021-10-25 16:21:00 -0700295 return sb.String()
296}
297
298// TargetNode describes a module or target identified by the name of a specific
299// metadata file. (immutable)
300//
301// Each metadata file corresponds to a Soong module or to a Make target.
302//
303// A target node can appear as the target or as the dependency in edges.
304// Most target nodes appear as both target in one edge and as dependency in
305// other edges.
306type TargetNode targetNode
307
308// Name returns the string that identifies the target node.
309// i.e. path to license metadata file
310func (tn *TargetNode) Name() string {
311 return tn.name
312}
313
Bob Badour103eb0f2022-01-10 13:50:57 -0800314// Dependencies returns the list of edges to dependencies of `tn`.
315func (tn *TargetNode) Dependencies() TargetEdgeList {
316 edges := make(TargetEdgeList, 0, len(tn.edges))
317 edges = append(edges, tn.edges...)
318 return edges
319}
320
Bob Badoura99ac622021-10-25 16:21:00 -0700321// PackageName returns the string that identifes the package for the target.
322func (tn *TargetNode) PackageName() string {
323 return tn.proto.GetPackageName()
324}
325
Ibrahim Kanouchebedf1a82022-10-22 01:28:05 +0000326// ModuleName returns the module name of the target.
327func (tn *TargetNode) ModuleName() string {
328 return tn.proto.GetModuleName()
329}
330
Bob Badoura99ac622021-10-25 16:21:00 -0700331// Projects returns the projects defining the target node. (unordered)
332//
333// In an ideal world, only 1 project defines a target, but the interaction
334// between Soong and Make for a variety of architectures and for host versus
335// product means a module is sometimes defined more than once.
336func (tn *TargetNode) Projects() []string {
337 return append([]string{}, tn.proto.Projects...)
338}
339
Bob Badoura99ac622021-10-25 16:21:00 -0700340// LicenseConditions returns a copy of the set of license conditions
341// originating at the target. The values that appear and how each is resolved
342// is a matter of policy. (unordered)
343//
344// e.g. notice or proprietary
Bob Badour103eb0f2022-01-10 13:50:57 -0800345func (tn *TargetNode) LicenseConditions() LicenseConditionSet {
346 return tn.licenseConditions
Bob Badoura99ac622021-10-25 16:21:00 -0700347}
348
349// LicenseTexts returns the paths to the files containing the license texts for
350// the target. (unordered)
351func (tn *TargetNode) LicenseTexts() []string {
352 return append([]string{}, tn.proto.LicenseTexts...)
353}
354
355// IsContainer returns true if the target represents a container that merely
356// aggregates other targets.
357func (tn *TargetNode) IsContainer() bool {
358 return tn.proto.GetIsContainer()
359}
360
361// Built returns the list of files built by the module or target. (unordered)
362func (tn *TargetNode) Built() []string {
363 return append([]string{}, tn.proto.Built...)
364}
365
366// Installed returns the list of files installed by the module or target.
367// (unordered)
368func (tn *TargetNode) Installed() []string {
369 return append([]string{}, tn.proto.Installed...)
370}
371
Bob Badoure6fdd142021-12-09 22:10:43 -0800372// TargetFiles returns the list of files built or installed by the module or
373// target. (unordered)
374func (tn *TargetNode) TargetFiles() []string {
375 return append(tn.proto.Built, tn.proto.Installed...)
376}
377
Bob Badoura99ac622021-10-25 16:21:00 -0700378// InstallMap returns the list of path name transformations to make to move
379// files from their original location in the file system to their destination
380// inside a container. (unordered)
381func (tn *TargetNode) InstallMap() []InstallMap {
382 result := make([]InstallMap, 0, len(tn.proto.InstallMap))
383 for _, im := range tn.proto.InstallMap {
384 result = append(result, InstallMap{im.GetFromPath(), im.GetContainerPath()})
385 }
386 return result
387}
388
389// Sources returns the list of file names depended on by the target, which may
390// be a proper subset of those made available by dependency modules.
391// (unordered)
392func (tn *TargetNode) Sources() []string {
393 return append([]string{}, tn.proto.Sources...)
394}
395
396// InstallMap describes the mapping from an input filesystem file to file in a
397// container.
398type InstallMap struct {
399 // FromPath is the input path on the filesystem.
400 FromPath string
401
402 // ContainerPath is the path to the same file inside the container or
403 // installed location.
404 ContainerPath string
405}
406
407// TargetEdgeAnnotations describes an immutable set of annotations attached to
408// an edge from a target to a dependency.
409//
410// Annotations typically distinguish between static linkage versus dynamic
411// versus tools that are used at build time but are not linked in any way.
412type TargetEdgeAnnotations struct {
Bob Badour5446a6f2022-01-10 18:44:59 -0800413 annotations map[string]struct{}
Bob Badoura99ac622021-10-25 16:21:00 -0700414}
415
416// newEdgeAnnotations creates a new instance of TargetEdgeAnnotations.
417func newEdgeAnnotations() TargetEdgeAnnotations {
Bob Badour5446a6f2022-01-10 18:44:59 -0800418 return TargetEdgeAnnotations{make(map[string]struct{})}
Bob Badoura99ac622021-10-25 16:21:00 -0700419}
420
421// HasAnnotation returns true if an annotation `ann` is in the set.
422func (ea TargetEdgeAnnotations) HasAnnotation(ann string) bool {
423 _, ok := ea.annotations[ann]
424 return ok
425}
426
427// Compare orders TargetAnnotations returning:
428// -1 when ea < other,
429// +1 when ea > other, and
430// 0 when ea == other.
431func (ea TargetEdgeAnnotations) Compare(other TargetEdgeAnnotations) int {
432 a1 := ea.AsList()
433 a2 := other.AsList()
434 sort.Strings(a1)
435 sort.Strings(a2)
436 for k := 0; k < len(a1) && k < len(a2); k++ {
437 if a1[k] < a2[k] {
438 return -1
439 }
440 if a1[k] > a2[k] {
441 return 1
442 }
443 }
444 if len(a1) < len(a2) {
445 return -1
446 }
447 if len(a1) > len(a2) {
448 return 1
449 }
450 return 0
451}
452
453// AsList returns the list of annotation names attached to the edge.
454// (unordered)
455func (ea TargetEdgeAnnotations) AsList() []string {
456 l := make([]string, 0, len(ea.annotations))
457 for ann := range ea.annotations {
458 l = append(l, ann)
459 }
460 return l
461}
462
463// TargetNodeSet describes a set of distinct nodes in a license graph.
464type TargetNodeSet struct {
Bob Badour5446a6f2022-01-10 18:44:59 -0800465 nodes map[*TargetNode]struct{}
Bob Badoura99ac622021-10-25 16:21:00 -0700466}
467
468// Contains returns true when `target` is an element of the set.
469func (ts *TargetNodeSet) Contains(target *TargetNode) bool {
470 _, isPresent := ts.nodes[target]
471 return isPresent
472}
473
474// AsList returns the list of target nodes in the set. (unordered)
475func (ts *TargetNodeSet) AsList() TargetNodeList {
476 result := make(TargetNodeList, 0, len(ts.nodes))
477 for tn := range ts.nodes {
478 result = append(result, tn)
479 }
480 return result
481}
482
483// Names returns the array of target node namess in the set. (unordered)
484func (ts *TargetNodeSet) Names() []string {
485 result := make([]string, 0, len(ts.nodes))
486 for tn := range ts.nodes {
487 result = append(result, tn.name)
488 }
489 return result
490}
491
Bob Badour103eb0f2022-01-10 13:50:57 -0800492// String returns a human-readable string representation of the set.
493func (ts *TargetNodeSet) String() string {
494 return fmt.Sprintf("{%s}", strings.Join(ts.Names(), ", "))
495}
496
Bob Badoura99ac622021-10-25 16:21:00 -0700497// TargetNodeList orders a list of targets by name.
498type TargetNodeList []*TargetNode
499
500// Len returns the count of elements in the list.
Colin Cross35f79c32022-01-27 15:18:52 -0800501func (l TargetNodeList) Len() int { return len(l) }
Bob Badoura99ac622021-10-25 16:21:00 -0700502
503// Swap rearranges 2 elements so that each occupies the other's former position.
504func (l TargetNodeList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
505
506// Less returns true when the `i`th element is lexicographicallt less than the `j`th.
507func (l TargetNodeList) Less(i, j int) bool {
508 return l[i].name < l[j].name
509}
510
511// String returns a string representation of the list.
512func (l TargetNodeList) String() string {
513 var sb strings.Builder
514 fmt.Fprintf(&sb, "[")
515 sep := ""
516 for _, tn := range l {
517 fmt.Fprintf(&sb, "%s%s", sep, tn.name)
518 sep = " "
519 }
520 fmt.Fprintf(&sb, "]")
521 return sb.String()
522}
523
524// Names returns an array the names of the nodes in the same order as the nodes in the list.
525func (l TargetNodeList) Names() []string {
526 result := make([]string, 0, len(l))
527 for _, tn := range l {
528 result = append(result, tn.name)
529 }
530 return result
531}