|
| 1 | +# SPDX-FileCopyrightText: 2025 Helio Chissini de Castro <heliocastro@gmail.com> |
| 2 | +# SPDX-License-Identifier: MIT |
| 3 | + |
| 4 | + |
| 5 | +from pydantic import BaseModel, ConfigDict, Field |
| 6 | + |
| 7 | +from ort.models.dependency_graph_edge import DependencyGraphEdge |
| 8 | +from ort.models.dependency_graph_node import DependencyGraphNode |
| 9 | +from ort.models.dependency_reference import DependencyReference |
| 10 | +from ort.models.identifier import Identifier |
| 11 | +from ort.models.root_dependency_index import RootDependencyIndex |
| 12 | + |
| 13 | + |
| 14 | +class DependencyGraph(BaseModel): |
| 15 | + """ |
| 16 | + Represents the graph of dependencies of a project. |
| 17 | +
|
| 18 | + This class holds information about a project's scopes and their dependencies in a format that minimizes the |
| 19 | + consumption of memory. In projects with many scopes there is often a high degree of duplication in the dependencies |
| 20 | + of the scopes. To avoid this, this class aims to share as many parts of the dependency graph as possible between |
| 21 | + the different scopes. Ideally, there is only a single dependency graph containing the dependencies used by all |
| 22 | + scopes. This is not always possible due to inconsistencies in dependency relations, like a package using different |
| 23 | + dependencies in different scopes. Then the dependency graph is split into multiple fragments, and each fragment has |
| 24 | + a consistent view on the dependencies it contains. |
| 25 | +
|
| 26 | + When constructing a dependency graph the dependencies are organized as a connected structure of DependencyReference |
| 27 | + objects in memory. Originally, the serialization format of a graph was based on this structure, but that turned out |
| 28 | + to be not ideal: During serialization, sub graphs referenced from multiple nodes (e.g. libraries with transitive |
| 29 | + dependencies referenced from multiple projects) get duplicated, which can cause a significant amount of redundancy. |
| 30 | + Therefore, the data representation has been changed again to a form, which can be serialized without introducing |
| 31 | + redundancy. It consists of the following elements: |
| 32 | +
|
| 33 | + - packages: A list with the coordinates of all the packages (free of duplication) that are referenced by the graph. |
| 34 | + This allows extracting the packages directly, but also has the advantage that the package coordinates do not have |
| 35 | + to be repeated over and over: All the references to packages are expressed by indices into this list. |
| 36 | + - nodes: An ordered list with the nodes of the dependency graph. A single node represents a package, and therefore |
| 37 | + has a reference into the list with package coordinates. It can, however, happen that packages occur multiple |
| 38 | + times in the graph if they are in different subtrees with different sets of transitive dependencies. Then there |
| 39 | + are multiple nodes for the packages affected, and a fragment_index is used to identify them uniquely. Nodes also |
| 40 | + store information about issues of a package and their linkage. |
| 41 | + - edges: Here the structure of the graph comes in. Each edge connects two nodes and represents a directed |
| 42 | + depends-on relationship. The nodes are referenced by numeric indices into the list of nodes. |
| 43 | + - scopes: This is a map that associates the scopes used by projects with their direct dependencies. A single |
| 44 | + dependency graph contains the dependencies of all the projects processed by a specific package manager. |
| 45 | + Therefore, the keys of this map are scope names qualified by the coordinates of a project; which makes them |
| 46 | + unique. The values are references to the nodes in the graph that correspond to the packages the scopes depend on |
| 47 | + directly. |
| 48 | +
|
| 49 | + To navigate this structure, start with a scope and gather the references to its direct dependency nodes. Then, by |
| 50 | + following the edges starting from these nodes, the set of transitive dependencies can be determined. The numeric |
| 51 | + indices can be resolved via the packages list. |
| 52 | + """ |
| 53 | + |
| 54 | + model_config = ConfigDict( |
| 55 | + extra="forbid", |
| 56 | + ) |
| 57 | + |
| 58 | + packages: list[Identifier] = Field( |
| 59 | + ..., |
| 60 | + description="A list with the identifiers of the packages that appear in the dependency graph. This list is " |
| 61 | + "used to resolve the numeric indices contained in the dependency_graph_node objects.", |
| 62 | + ) |
| 63 | + |
| 64 | + scope_roots: set[DependencyReference] = Field( |
| 65 | + ..., |
| 66 | + description="Stores the dependency graph as a list of root nodes for the direct dependencies referenced by " |
| 67 | + "scopes. Starting with these nodes, the whole graph can be traversed. The nodes are constructed " |
| 68 | + "from the direct dependencies declared by scopes that cannot be reached via other paths in the " |
| 69 | + "dependency graph. Note that this property exists for backwards compatibility only; it is replaced " |
| 70 | + "by the lists of nodes and edges.", |
| 71 | + ) |
| 72 | + |
| 73 | + scopes: dict[str, list[RootDependencyIndex]] = Field( |
| 74 | + ..., |
| 75 | + description="A mapping from scope names to the direct dependencies of the scopes. Based on this information, " |
| 76 | + "the set of scopes of a project can be constructed from the serialized form.", |
| 77 | + ) |
| 78 | + |
| 79 | + nodes: list[DependencyGraphNode] = Field( |
| 80 | + ..., |
| 81 | + description="A list with the nodes of this dependency graph. Nodes correspond to packages, but in contrast to " |
| 82 | + "the packages list, there can be multiple nodes for a single package. The order of nodes in this " |
| 83 | + "list is relevant; the edges of the graph reference their nodes by numeric indices.", |
| 84 | + ) |
| 85 | + |
| 86 | + edges: set[DependencyGraphEdge] = Field( |
| 87 | + ..., |
| 88 | + description="A set with the edges of this dependency graph. By traversing the edges, the dependencies of " |
| 89 | + "packages can be determined.", |
| 90 | + ) |
0 commit comments