1# Copyright (c) 2021 The Linux Foundation
2#
3# SPDX-License-Identifier: Apache-2.0
4
5from dataclasses import dataclass, field
6from enum import Enum
7from typing import Optional
8
9
10# DocumentConfig contains settings used to configure how the SPDX Document
11# should be built.
12@dataclass(eq=True)
13class DocumentConfig:
14    # name of document
15    name: str = ""
16
17    # namespace for this document
18    namespace: str = ""
19
20    # standardized DocumentRef- (including that prefix) that the other
21    # docs will use to refer to this one
22    docRefID: str = ""
23
24
25# Document contains the data assembled by the SBOM builder, to be used to
26# create the actual SPDX Document.
27class Document:
28    # initialize with a DocumentConfig
29    def __init__(self, cfg):
30        super().__init__()
31
32        # configuration - DocumentConfig
33        self.cfg = cfg
34
35        # dict of SPDX ID => Package
36        self.pkgs = {}
37
38        # relationships "owned" by this Document, _not_ those "owned" by its
39        # Packages or Files; will likely be just DESCRIBES
40        self.relationships = []
41
42        # dict of filename (ignoring its directory) => number of times it has
43        # been seen while adding files to this Document; used to calculate
44        # useful SPDX IDs
45        self.timesSeen = {}
46
47        # dict of absolute path on disk => File
48        self.fileLinks = {}
49
50        # set of other Documents that our elements' Relationships refer to
51        self.externalDocuments = set()
52
53        # set of LicenseRef- custom licenses to be declared
54        # may or may not include "LicenseRef-" license prefix
55        self.customLicenseIDs = set()
56
57        # this Document's SHA1 hash, filled in _after_ the Document has been
58        # written to disk, so that others can refer to it
59        self.myDocSHA1 = ""
60
61
62# PackageConfig contains settings used to configure how an SPDX Package should
63# be built.
64@dataclass(eq=True)
65class PackageConfig:
66    # package name
67    name: str = ""
68
69    # SPDX ID, including "SPDXRef-"
70    spdxID: str = ""
71
72    # primary package purpose (ex. "LIBRARY", "APPLICATION", etc.)
73    primaryPurpose: str = ""
74
75    # package URL
76    url: str = ""
77
78    # package version
79    version: str = ""
80
81    # package revision
82    revision: str = ""
83
84    # package external references
85    externalReferences: list = field(default_factory=list)
86
87    # the Package's declared license
88    declaredLicense: str = "NOASSERTION"
89
90    # the Package's copyright text
91    copyrightText: str = "NOASSERTION"
92
93    # absolute path of the "root" directory on disk, to be used as the
94    # base directory from which this Package's Files will calculate their
95    # relative paths
96    # may want to note this in a Package comment field
97    relativeBaseDir: str = ""
98
99
100# Package contains the data assembled by the SBOM builder, to be used to
101# create the actual SPDX Package.
102class Package:
103    # initialize with:
104    # 1) PackageConfig
105    # 2) the Document that owns this Package
106    def __init__(self, cfg, doc):
107        super().__init__()
108
109        # configuration - PackageConfig
110        self.cfg = cfg
111
112        # Document that owns this Package
113        self.doc = doc
114
115        # verification code, calculated per section 7.9 of SPDX spec v2.3
116        self.verificationCode = ""
117
118        # concluded license for this Package, if
119        # cfg.shouldConcludePackageLicense == True; NOASSERTION otherwise
120        self.concludedLicense = "NOASSERTION"
121
122        # list of licenses found in this Package's Files
123        self.licenseInfoFromFiles = []
124
125        # Files in this Package
126        # dict of SPDX ID => File
127        self.files = {}
128
129        # Relationships "owned" by this Package (e.g., this Package is left
130        # side)
131        self.rlns = []
132
133        # If this Package was a target, which File was its main build product?
134        self.targetBuildFile = None
135
136
137# RelationshipDataElementType defines whether a RelationshipData element
138# (e.g., the "owner" or the "other" element) is a File, a target Package,
139# a Package's ID (as other only, and only where owner type is DOCUMENT),
140# or the SPDX document itself (as owner only).
141class RelationshipDataElementType(Enum):
142    UNKNOWN = 0
143    FILENAME = 1
144    TARGETNAME = 2
145    PACKAGEID = 3
146    DOCUMENT = 4
147
148
149# RelationshipData contains the pre-analysis data about a relationship between
150# Files and/or Packages/targets. It is eventually parsed into a corresponding
151# Relationship after we have organized the SPDX Package and File data.
152@dataclass(eq=True)
153class RelationshipData:
154    # for the "owner" element (e.g., the left side of the Relationship),
155    # is it a filename or a target name (e.g., a Package in the build doc)
156    ownerType: RelationshipDataElementType = RelationshipDataElementType.UNKNOWN
157
158    # owner file absolute path (if ownerType is FILENAME)
159    ownerFileAbspath: str = ""
160
161    # owner target name (if ownerType is TARGETNAME)
162    ownerTargetName: str = ""
163
164    # owner SPDX Document (if ownerType is DOCUMENT)
165    ownerDocument: Optional['Document'] = None
166
167    # for the "other" element (e.g., the right side of the Relationship),
168    # is it a filename or a target name (e.g., a Package in the build doc)
169    otherType: RelationshipDataElementType = RelationshipDataElementType.UNKNOWN
170
171    # other file absolute path (if otherType is FILENAME)
172    otherFileAbspath: str = ""
173
174    # other target name (if otherType is TARGETNAME)
175    otherTargetName: str = ""
176
177    # other package ID (if ownerType is DOCUMENT and otherType is PACKAGEID)
178    otherPackageID: str = ""
179
180    # text string with Relationship type
181    # from table 68 in section 11.1 of SPDX spec v2.3
182    rlnType: str = ""
183
184
185# Relationship contains the post-analysis, processed data about a relationship
186# in a form suitable for creating the actual SPDX Relationship in a particular
187# Document's context.
188@dataclass(eq=True)
189class Relationship:
190    # SPDX ID for left side of relationship
191    # including "SPDXRef-" as well as "DocumentRef-" if needed
192    refA: str = ""
193
194    # SPDX ID for right side of relationship
195    # including "SPDXRef-" as well as "DocumentRef-" if needed
196    refB: str = ""
197
198    # text string with Relationship type
199    # from table 68 in section 11.1 of SPDX spec v2.3
200    rlnType: str = ""
201
202
203# File contains the data needed to create a File element in the context of a
204# particular SPDX Document and Package.
205class File:
206    # initialize with:
207    # 1) Document containing this File
208    # 2) Package containing this File
209    def __init__(self, doc, pkg):
210        super().__init__()
211
212        # absolute path to this file on disk
213        self.abspath = ""
214
215        # relative path for this file, measured from the owning Package's
216        # cfg.relativeBaseDir
217        self.relpath = ""
218
219        # SPDX ID for this file, including "SPDXRef-"
220        self.spdxID = ""
221
222        # SHA1 hash
223        self.sha1 = ""
224
225        # SHA256 hash, if pkg.cfg.doSHA256 == True; empty string otherwise
226        self.sha256 = ""
227
228        # MD5 hash, if pkg.cfg.doMD5 == True; empty string otherwise
229        self.md5 = ""
230
231        # concluded license, if pkg.cfg.shouldConcludeFileLicenses == True;
232        # "NOASSERTION" otherwise
233        self.concludedLicense = "NOASSERTION"
234
235        # license info in file
236        self.licenseInfoInFile = []
237
238        # copyright text
239        self.copyrightText = "NOASSERTION"
240
241        # Relationships "owned" by this File (e.g., this File is left side)
242        self.rlns = []
243
244        # Package that owns this File
245        self.pkg = pkg
246
247        # Document that owns this File
248        self.doc = doc
249