blob: fac1d05b691557f27307a082148874ed6281914c [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 Badour5c12c662022-10-29 22:45:12 -070061 // onceBottomUp makes sure the bottom-up resolve walk only happens one time.
62 onceBottomUp sync.Once
Bob Badoura99ac622021-10-25 16:21:00 -070063
Bob Badour5c12c662022-10-29 22:45:12 -070064 // onceTopDown makes sure the top-down resolve walk only happens one time.
65 onceTopDown sync.Once
Bob Badoura99ac622021-10-25 16:21:00 -070066
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
Bob Badoura99ac622021-10-25 16:21:00 -070075// Edges returns the list of edges in the graph. (unordered)
76func (lg *LicenseGraph) Edges() TargetEdgeList {
77 edges := make(TargetEdgeList, 0, len(lg.edges))
Bob Badour103eb0f2022-01-10 13:50:57 -080078 edges = append(edges, lg.edges...)
Bob Badoura99ac622021-10-25 16:21:00 -070079 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))
Bob Badour103eb0f2022-01-10 13:50:57 -080085 for _, target := range lg.targets {
86 targets = append(targets, target)
Bob Badoura99ac622021-10-25 16:21:00 -070087 }
88 return targets
89}
90
91// compliance-only LicenseGraph methods
92
93// newLicenseGraph constructs a new, empty instance of LicenseGraph.
94func newLicenseGraph() *LicenseGraph {
95 return &LicenseGraph{
96 rootFiles: []string{},
Bob Badoura99ac622021-10-25 16:21:00 -070097 targets: make(map[string]*TargetNode),
98 }
99}
100
Bob Badoura99ac622021-10-25 16:21:00 -0700101// TargetEdge describes a directed, annotated edge from a target to a
102// dependency. (immutable)
103//
104// A LicenseGraph, above, is a set of TargetEdges.
105//
106// i.e. `Target` depends on `Dependency` in the manner described by
107// `Annotations`.
108type TargetEdge struct {
Bob Badour103eb0f2022-01-10 13:50:57 -0800109 // target and dependency identify the nodes connected by the edge.
110 target, dependency *TargetNode
Bob Badoura99ac622021-10-25 16:21:00 -0700111
Bob Badour103eb0f2022-01-10 13:50:57 -0800112 // annotations identifies the set of compliance-relevant annotations describing the edge.
113 annotations TargetEdgeAnnotations
Bob Badoura99ac622021-10-25 16:21:00 -0700114}
115
116// Target identifies the target that depends on the dependency.
117//
118// Target needs Dependency to build.
Bob Badour103eb0f2022-01-10 13:50:57 -0800119func (e *TargetEdge) Target() *TargetNode {
120 return e.target
Bob Badoura99ac622021-10-25 16:21:00 -0700121}
122
123// Dependency identifies the target depended on by the target.
124//
125// Dependency builds without Target, but Target needs Dependency to build.
Bob Badour103eb0f2022-01-10 13:50:57 -0800126func (e *TargetEdge) Dependency() *TargetNode {
127 return e.dependency
Bob Badoura99ac622021-10-25 16:21:00 -0700128}
129
130// Annotations describes the type of edge by the set of annotations attached to
131// it.
132//
133// Only annotations prescribed by policy have any meaning for licensing, and
134// the meaning for licensing is likewise prescribed by policy. Other annotations
135// are preserved and ignored by policy.
Bob Badour103eb0f2022-01-10 13:50:57 -0800136func (e *TargetEdge) Annotations() TargetEdgeAnnotations {
137 return e.annotations
138}
139
Ibrahim Kanouchebedf1a82022-10-22 01:28:05 +0000140// IsRuntimeDependency returns true for edges representing shared libraries
141// linked dynamically at runtime.
142func (e *TargetEdge) IsRuntimeDependency() bool {
143 return edgeIsDynamicLink(e)
144}
145
146// IsDerivation returns true for edges where the target is a derivative
147// work of dependency.
148func (e *TargetEdge) IsDerivation() bool {
149 return edgeIsDerivation(e)
150}
151
152// IsBuildTool returns true for edges where the target is built
153// by dependency.
154func (e *TargetEdge) IsBuildTool() bool {
155 return !edgeIsDerivation(e) && !edgeIsDynamicLink(e)
156}
157
Bob Badour103eb0f2022-01-10 13:50:57 -0800158// String returns a human-readable string representation of the edge.
159func (e *TargetEdge) String() string {
160 return fmt.Sprintf("%s -[%s]> %s", e.target.name, strings.Join(e.annotations.AsList(), ", "), e.dependency.name)
Bob Badoura99ac622021-10-25 16:21:00 -0700161}
162
163// TargetEdgeList orders lists of edges by target then dependency then annotations.
Bob Badour103eb0f2022-01-10 13:50:57 -0800164type TargetEdgeList []*TargetEdge
Bob Badoura99ac622021-10-25 16:21:00 -0700165
166// Len returns the count of the elmements in the list.
Colin Cross35f79c32022-01-27 15:18:52 -0800167func (l TargetEdgeList) Len() int { return len(l) }
Bob Badoura99ac622021-10-25 16:21:00 -0700168
169// Swap rearranges 2 elements so that each occupies the other's former position.
170func (l TargetEdgeList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
171
172// Less returns true when the `i`th element is lexicographically less than the `j`th.
173func (l TargetEdgeList) Less(i, j int) bool {
Bob Badour103eb0f2022-01-10 13:50:57 -0800174 namei := l[i].target.name
175 namej := l[j].target.name
176 if namei == namej {
177 namei = l[i].dependency.name
178 namej = l[j].dependency.name
Bob Badoura99ac622021-10-25 16:21:00 -0700179 }
Bob Badour103eb0f2022-01-10 13:50:57 -0800180 if namei == namej {
181 return l[i].annotations.Compare(l[j].annotations) < 0
182 }
183 return namei < namej
184}
185
186// TargetEdgePathSegment describes a single arc in a TargetPath associating the
187// edge with a context `ctx` defined by whatever process is creating the path.
188type TargetEdgePathSegment struct {
189 edge *TargetEdge
Colin Cross35f79c32022-01-27 15:18:52 -0800190 ctx interface{}
Bob Badour103eb0f2022-01-10 13:50:57 -0800191}
192
193// Target identifies the target that depends on the dependency.
194//
195// Target needs Dependency to build.
196func (s TargetEdgePathSegment) Target() *TargetNode {
197 return s.edge.target
198}
199
200// Dependency identifies the target depended on by the target.
201//
202// Dependency builds without Target, but Target needs Dependency to build.
203func (s TargetEdgePathSegment) Dependency() *TargetNode {
204 return s.edge.dependency
205}
206
Ibrahim Kanouchebedf1a82022-10-22 01:28:05 +0000207// Edge describes the target edge.
208func (s TargetEdgePathSegment) Edge() *TargetEdge {
209 return s.edge
210}
211
Bob Badour103eb0f2022-01-10 13:50:57 -0800212// Annotations describes the type of edge by the set of annotations attached to
213// it.
214//
215// Only annotations prescribed by policy have any meaning for licensing, and
216// the meaning for licensing is likewise prescribed by policy. Other annotations
217// are preserved and ignored by policy.
218func (s TargetEdgePathSegment) Annotations() TargetEdgeAnnotations {
219 return s.edge.annotations
220}
221
222// Context returns the context associated with the path segment. The type and
223// value of the context defined by the process creating the path.
224func (s TargetEdgePathSegment) Context() interface{} {
225 return s.ctx
226}
227
228// String returns a human-readable string representation of the edge.
229func (s TargetEdgePathSegment) String() string {
230 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 -0700231}
232
233// TargetEdgePath describes a sequence of edges starting at a root and ending
234// at some final dependency.
Bob Badour103eb0f2022-01-10 13:50:57 -0800235type TargetEdgePath []TargetEdgePathSegment
Bob Badoura99ac622021-10-25 16:21:00 -0700236
237// NewTargetEdgePath creates a new, empty path with capacity `cap`.
238func NewTargetEdgePath(cap int) *TargetEdgePath {
239 p := make(TargetEdgePath, 0, cap)
240 return &p
241}
242
243// Push appends a new edge to the list verifying that the target of the new
244// edge is the dependency of the prior.
Bob Badour103eb0f2022-01-10 13:50:57 -0800245func (p *TargetEdgePath) Push(edge *TargetEdge, ctx interface{}) {
Bob Badoura99ac622021-10-25 16:21:00 -0700246 if len(*p) == 0 {
Bob Badour103eb0f2022-01-10 13:50:57 -0800247 *p = append(*p, TargetEdgePathSegment{edge, ctx})
Bob Badoura99ac622021-10-25 16:21:00 -0700248 return
249 }
Bob Badour103eb0f2022-01-10 13:50:57 -0800250 if (*p)[len(*p)-1].edge.dependency != edge.target {
251 panic(fmt.Errorf("disjoint path %s does not end at %s", p.String(), edge.target.name))
Bob Badoura99ac622021-10-25 16:21:00 -0700252 }
Bob Badour103eb0f2022-01-10 13:50:57 -0800253 *p = append(*p, TargetEdgePathSegment{edge, ctx})
Bob Badoura99ac622021-10-25 16:21:00 -0700254}
255
256// Pop shortens the path by 1 edge.
257func (p *TargetEdgePath) Pop() {
258 if len(*p) == 0 {
259 panic(fmt.Errorf("attempt to remove edge from empty path"))
260 }
261 *p = (*p)[:len(*p)-1]
262}
263
264// Clear makes the path length 0.
265func (p *TargetEdgePath) Clear() {
266 *p = (*p)[:0]
267}
268
Bob Badoure6fdd142021-12-09 22:10:43 -0800269// Copy makes a new path with the same value.
270func (p *TargetEdgePath) Copy() *TargetEdgePath {
271 result := make(TargetEdgePath, 0, len(*p))
272 for _, e := range *p {
273 result = append(result, e)
274 }
275 return &result
276}
277
Bob Badoura99ac622021-10-25 16:21:00 -0700278// String returns a string representation of the path: [n1 -> n2 -> ... -> nn].
279func (p *TargetEdgePath) String() string {
280 if p == nil {
281 return "nil"
282 }
283 if len(*p) == 0 {
284 return "[]"
285 }
286 var sb strings.Builder
287 fmt.Fprintf(&sb, "[")
Bob Badour103eb0f2022-01-10 13:50:57 -0800288 for _, s := range *p {
289 fmt.Fprintf(&sb, "%s -> ", s.edge.target.name)
Bob Badoura99ac622021-10-25 16:21:00 -0700290 }
Bob Badour103eb0f2022-01-10 13:50:57 -0800291 lastSegment := (*p)[len(*p)-1]
292 fmt.Fprintf(&sb, "%s]", lastSegment.edge.dependency.name)
Bob Badoura99ac622021-10-25 16:21:00 -0700293 return sb.String()
294}
295
296// TargetNode describes a module or target identified by the name of a specific
297// metadata file. (immutable)
298//
299// Each metadata file corresponds to a Soong module or to a Make target.
300//
301// A target node can appear as the target or as the dependency in edges.
302// Most target nodes appear as both target in one edge and as dependency in
303// other edges.
304type TargetNode targetNode
305
306// Name returns the string that identifies the target node.
307// i.e. path to license metadata file
308func (tn *TargetNode) Name() string {
309 return tn.name
310}
311
Bob Badour103eb0f2022-01-10 13:50:57 -0800312// Dependencies returns the list of edges to dependencies of `tn`.
313func (tn *TargetNode) Dependencies() TargetEdgeList {
314 edges := make(TargetEdgeList, 0, len(tn.edges))
315 edges = append(edges, tn.edges...)
316 return edges
317}
318
Bob Badoura99ac622021-10-25 16:21:00 -0700319// PackageName returns the string that identifes the package for the target.
320func (tn *TargetNode) PackageName() string {
321 return tn.proto.GetPackageName()
322}
323
Ibrahim Kanouchebedf1a82022-10-22 01:28:05 +0000324// ModuleName returns the module name of the target.
325func (tn *TargetNode) ModuleName() string {
326 return tn.proto.GetModuleName()
327}
328
Bob Badoura99ac622021-10-25 16:21:00 -0700329// Projects returns the projects defining the target node. (unordered)
330//
331// In an ideal world, only 1 project defines a target, but the interaction
332// between Soong and Make for a variety of architectures and for host versus
333// product means a module is sometimes defined more than once.
334func (tn *TargetNode) Projects() []string {
335 return append([]string{}, tn.proto.Projects...)
336}
337
Bob Badoura99ac622021-10-25 16:21:00 -0700338// LicenseConditions returns a copy of the set of license conditions
339// originating at the target. The values that appear and how each is resolved
340// is a matter of policy. (unordered)
341//
342// e.g. notice or proprietary
Bob Badour103eb0f2022-01-10 13:50:57 -0800343func (tn *TargetNode) LicenseConditions() LicenseConditionSet {
344 return tn.licenseConditions
Bob Badoura99ac622021-10-25 16:21:00 -0700345}
346
347// LicenseTexts returns the paths to the files containing the license texts for
348// the target. (unordered)
349func (tn *TargetNode) LicenseTexts() []string {
350 return append([]string{}, tn.proto.LicenseTexts...)
351}
352
353// IsContainer returns true if the target represents a container that merely
354// aggregates other targets.
355func (tn *TargetNode) IsContainer() bool {
356 return tn.proto.GetIsContainer()
357}
358
359// Built returns the list of files built by the module or target. (unordered)
360func (tn *TargetNode) Built() []string {
361 return append([]string{}, tn.proto.Built...)
362}
363
364// Installed returns the list of files installed by the module or target.
365// (unordered)
366func (tn *TargetNode) Installed() []string {
367 return append([]string{}, tn.proto.Installed...)
368}
369
Bob Badoure6fdd142021-12-09 22:10:43 -0800370// TargetFiles returns the list of files built or installed by the module or
371// target. (unordered)
372func (tn *TargetNode) TargetFiles() []string {
373 return append(tn.proto.Built, tn.proto.Installed...)
374}
375
Bob Badoura99ac622021-10-25 16:21:00 -0700376// InstallMap returns the list of path name transformations to make to move
377// files from their original location in the file system to their destination
378// inside a container. (unordered)
379func (tn *TargetNode) InstallMap() []InstallMap {
380 result := make([]InstallMap, 0, len(tn.proto.InstallMap))
381 for _, im := range tn.proto.InstallMap {
382 result = append(result, InstallMap{im.GetFromPath(), im.GetContainerPath()})
383 }
384 return result
385}
386
387// Sources returns the list of file names depended on by the target, which may
388// be a proper subset of those made available by dependency modules.
389// (unordered)
390func (tn *TargetNode) Sources() []string {
391 return append([]string{}, tn.proto.Sources...)
392}
393
394// InstallMap describes the mapping from an input filesystem file to file in a
395// container.
396type InstallMap struct {
397 // FromPath is the input path on the filesystem.
398 FromPath string
399
400 // ContainerPath is the path to the same file inside the container or
401 // installed location.
402 ContainerPath string
403}
404
405// TargetEdgeAnnotations describes an immutable set of annotations attached to
406// an edge from a target to a dependency.
407//
408// Annotations typically distinguish between static linkage versus dynamic
409// versus tools that are used at build time but are not linked in any way.
410type TargetEdgeAnnotations struct {
Bob Badour5446a6f2022-01-10 18:44:59 -0800411 annotations map[string]struct{}
Bob Badoura99ac622021-10-25 16:21:00 -0700412}
413
414// newEdgeAnnotations creates a new instance of TargetEdgeAnnotations.
415func newEdgeAnnotations() TargetEdgeAnnotations {
Bob Badour5446a6f2022-01-10 18:44:59 -0800416 return TargetEdgeAnnotations{make(map[string]struct{})}
Bob Badoura99ac622021-10-25 16:21:00 -0700417}
418
419// HasAnnotation returns true if an annotation `ann` is in the set.
420func (ea TargetEdgeAnnotations) HasAnnotation(ann string) bool {
421 _, ok := ea.annotations[ann]
422 return ok
423}
424
425// Compare orders TargetAnnotations returning:
426// -1 when ea < other,
427// +1 when ea > other, and
428// 0 when ea == other.
429func (ea TargetEdgeAnnotations) Compare(other TargetEdgeAnnotations) int {
430 a1 := ea.AsList()
431 a2 := other.AsList()
432 sort.Strings(a1)
433 sort.Strings(a2)
434 for k := 0; k < len(a1) && k < len(a2); k++ {
435 if a1[k] < a2[k] {
436 return -1
437 }
438 if a1[k] > a2[k] {
439 return 1
440 }
441 }
442 if len(a1) < len(a2) {
443 return -1
444 }
445 if len(a1) > len(a2) {
446 return 1
447 }
448 return 0
449}
450
451// AsList returns the list of annotation names attached to the edge.
452// (unordered)
453func (ea TargetEdgeAnnotations) AsList() []string {
454 l := make([]string, 0, len(ea.annotations))
455 for ann := range ea.annotations {
456 l = append(l, ann)
457 }
458 return l
459}
460
461// TargetNodeSet describes a set of distinct nodes in a license graph.
Bob Badour3fe369c2022-12-18 14:15:02 -0800462type TargetNodeSet map[*TargetNode]struct{}
Bob Badoura99ac622021-10-25 16:21:00 -0700463
464// Contains returns true when `target` is an element of the set.
Bob Badour3fe369c2022-12-18 14:15:02 -0800465func (ts TargetNodeSet) Contains(target *TargetNode) bool {
466 _, isPresent := ts[target]
Bob Badoura99ac622021-10-25 16:21:00 -0700467 return isPresent
468}
469
Bob Badoura99ac622021-10-25 16:21:00 -0700470// Names returns the array of target node namess in the set. (unordered)
Bob Badour3fe369c2022-12-18 14:15:02 -0800471func (ts TargetNodeSet) Names() []string {
472 result := make([]string, 0, len(ts))
473 for tn := range ts {
Bob Badoura99ac622021-10-25 16:21:00 -0700474 result = append(result, tn.name)
475 }
476 return result
477}
478
Bob Badour103eb0f2022-01-10 13:50:57 -0800479// String returns a human-readable string representation of the set.
Bob Badour3fe369c2022-12-18 14:15:02 -0800480func (ts TargetNodeSet) String() string {
Bob Badour103eb0f2022-01-10 13:50:57 -0800481 return fmt.Sprintf("{%s}", strings.Join(ts.Names(), ", "))
482}
483
Bob Badoura99ac622021-10-25 16:21:00 -0700484// TargetNodeList orders a list of targets by name.
485type TargetNodeList []*TargetNode
486
487// Len returns the count of elements in the list.
Colin Cross35f79c32022-01-27 15:18:52 -0800488func (l TargetNodeList) Len() int { return len(l) }
Bob Badoura99ac622021-10-25 16:21:00 -0700489
490// Swap rearranges 2 elements so that each occupies the other's former position.
491func (l TargetNodeList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
492
493// Less returns true when the `i`th element is lexicographicallt less than the `j`th.
494func (l TargetNodeList) Less(i, j int) bool {
495 return l[i].name < l[j].name
496}
497
498// String returns a string representation of the list.
499func (l TargetNodeList) String() string {
500 var sb strings.Builder
501 fmt.Fprintf(&sb, "[")
502 sep := ""
503 for _, tn := range l {
504 fmt.Fprintf(&sb, "%s%s", sep, tn.name)
505 sep = " "
506 }
507 fmt.Fprintf(&sb, "]")
508 return sb.String()
509}
510
511// Names returns an array the names of the nodes in the same order as the nodes in the list.
512func (l TargetNodeList) Names() []string {
513 result := make([]string, 0, len(l))
514 for _, tn := range l {
515 result = append(result, tn.name)
516 }
517 return result
518}