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