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