summaryrefslogtreecommitdiffstats
path: root/meta/lib
diff options
context:
space:
mode:
Diffstat (limited to 'meta/lib')
-rw-r--r--meta/lib/oe/sbom.py63
-rw-r--r--meta/lib/oe/spdx.py263
2 files changed, 326 insertions, 0 deletions
diff --git a/meta/lib/oe/sbom.py b/meta/lib/oe/sbom.py
new file mode 100644
index 0000000000..d40e5b792f
--- /dev/null
+++ b/meta/lib/oe/sbom.py
@@ -0,0 +1,63 @@
1#
2# SPDX-License-Identifier: GPL-2.0-only
3#
4
5import collections
6
7DepRecipe = collections.namedtuple("DepRecipe", ("doc", "doc_sha1", "recipe"))
8DepSource = collections.namedtuple("DepSource", ("doc", "doc_sha1", "recipe", "file"))
9
10
11def get_recipe_spdxid(d):
12 return "SPDXRef-%s-%s" % ("Recipe", d.getVar("PN"))
13
14
15def get_package_spdxid(pkg):
16 return "SPDXRef-Package-%s" % pkg
17
18
19def get_source_file_spdxid(d, idx):
20 return "SPDXRef-SourceFile-%s-%d" % (d.getVar("PN"), idx)
21
22
23def get_packaged_file_spdxid(pkg, idx):
24 return "SPDXRef-PackagedFile-%s-%d" % (pkg, idx)
25
26
27def get_image_spdxid(img):
28 return "SPDXRef-Image-%s" % img
29
30
31def write_doc(d, spdx_doc, subdir):
32 from pathlib import Path
33
34 spdx_deploy = Path(d.getVar("SPDXDEPLOY"))
35
36 dest = spdx_deploy / subdir / (spdx_doc.name + ".spdx.json")
37 dest.parent.mkdir(exist_ok=True, parents=True)
38 with dest.open("wb") as f:
39 doc_sha1 = spdx_doc.to_json(f, sort_keys=True)
40
41 l = spdx_deploy / "by-namespace" / spdx_doc.documentNamespace.replace("/", "_")
42 l.parent.mkdir(exist_ok=True, parents=True)
43 l.symlink_to(os.path.relpath(dest, l.parent))
44
45 return doc_sha1
46
47
48def read_doc(filename):
49 import hashlib
50 import oe.spdx
51
52 with filename.open("rb") as f:
53 sha1 = hashlib.sha1()
54 while True:
55 chunk = f.read(4096)
56 if not chunk:
57 break
58 sha1.update(chunk)
59
60 f.seek(0)
61 doc = oe.spdx.SPDXDocument.from_json(f)
62
63 return (doc, sha1.hexdigest())
diff --git a/meta/lib/oe/spdx.py b/meta/lib/oe/spdx.py
new file mode 100644
index 0000000000..3f569c6862
--- /dev/null
+++ b/meta/lib/oe/spdx.py
@@ -0,0 +1,263 @@
1#
2# SPDX-License-Identifier: GPL-2.0-only
3#
4
5import hashlib
6import itertools
7import json
8
9SPDX_VERSION = "2.2"
10
11
12class _Property(object):
13 def __init__(self, *, default=None):
14 self.default = default
15
16 def setdefault(self, dest, name):
17 if self.default is not None:
18 dest.setdefault(name, self.default)
19
20
21class _String(_Property):
22 def __init__(self, **kwargs):
23 super().__init__(**kwargs)
24
25 def set_property(self, attrs, name):
26 def get_helper(obj):
27 return obj._spdx[name]
28
29 def set_helper(obj, value):
30 obj._spdx[name] = value
31
32 def del_helper(obj):
33 del obj._spdx[name]
34
35 attrs[name] = property(get_helper, set_helper, del_helper)
36
37 def init(self, source):
38 return source
39
40
41class _Object(_Property):
42 def __init__(self, cls, **kwargs):
43 super().__init__(**kwargs)
44 self.cls = cls
45
46 def set_property(self, attrs, name):
47 def get_helper(obj):
48 if not name in obj._spdx:
49 obj._spdx[name] = self.cls()
50 return obj._spdx[name]
51
52 def set_helper(obj, value):
53 obj._spdx[name] = value
54
55 def del_helper(obj):
56 del obj._spdx[name]
57
58 attrs[name] = property(get_helper, set_helper)
59
60 def init(self, source):
61 return self.cls(**source)
62
63
64class _ListProperty(_Property):
65 def __init__(self, prop, **kwargs):
66 super().__init__(**kwargs)
67 self.prop = prop
68
69 def set_property(self, attrs, name):
70 def get_helper(obj):
71 if not name in obj._spdx:
72 obj._spdx[name] = []
73 return obj._spdx[name]
74
75 def del_helper(obj):
76 del obj._spdx[name]
77
78 attrs[name] = property(get_helper, None, del_helper)
79
80 def init(self, source):
81 return [self.prop.init(o) for o in source]
82
83
84class _StringList(_ListProperty):
85 def __init__(self, **kwargs):
86 super().__init__(_String(), **kwargs)
87
88
89class _ObjectList(_ListProperty):
90 def __init__(self, cls, **kwargs):
91 super().__init__(_Object(cls), **kwargs)
92
93
94class MetaSPDXObject(type):
95 def __new__(mcls, name, bases, attrs):
96 attrs["_properties"] = {}
97
98 for key in attrs.keys():
99 if isinstance(attrs[key], _Property):
100 prop = attrs[key]
101 attrs["_properties"][key] = prop
102 prop.set_property(attrs, key)
103
104 return super().__new__(mcls, name, bases, attrs)
105
106
107class SPDXObject(metaclass=MetaSPDXObject):
108 def __init__(self, **d):
109 self._spdx = {}
110
111 for name, prop in self._properties.items():
112 prop.setdefault(self._spdx, name)
113 if name in d:
114 self._spdx[name] = prop.init(d[name])
115
116 def serializer(self):
117 return self._spdx
118
119 def __setattr__(self, name, value):
120 if name in self._properties or name == "_spdx":
121 super().__setattr__(name, value)
122 return
123 raise KeyError("%r is not a valid SPDX property" % name)
124
125
126class SPDXChecksum(SPDXObject):
127 algorithm = _String()
128 checksumValue = _String()
129
130
131class SPDXRelationship(SPDXObject):
132 spdxElementId = _String()
133 relatedSpdxElement = _String()
134 relationshipType = _String()
135 comment = _String()
136
137
138class SPDXExternalReference(SPDXObject):
139 referenceCategory = _String()
140 referenceType = _String()
141 referenceLocator = _String()
142
143
144class SPDXPackageVerificationCode(SPDXObject):
145 packageVerificationCodeValue = _String()
146 packageVerificationCodeExcludedFiles = _StringList()
147
148
149class SPDXPackage(SPDXObject):
150 name = _String()
151 SPDXID = _String()
152 versionInfo = _String()
153 downloadLocation = _String(default="NOASSERTION")
154 packageSupplier = _String(default="NOASSERTION")
155 homepage = _String()
156 licenseConcluded = _String(default="NOASSERTION")
157 licenseDeclared = _String(default="NOASSERTION")
158 summary = _String()
159 description = _String()
160 sourceInfo = _String()
161 copyrightText = _String(default="NOASSERTION")
162 licenseInfoFromFiles = _StringList(default=["NOASSERTION"])
163 externalRefs = _ObjectList(SPDXExternalReference)
164 packageVerificationCode = _Object(SPDXPackageVerificationCode)
165 hasFiles = _StringList()
166 packageFileName = _String()
167
168
169class SPDXFile(SPDXObject):
170 SPDXID = _String()
171 fileName = _String()
172 licenseConcluded = _String(default="NOASSERTION")
173 copyrightText = _String(default="NOASSERTION")
174 licenseInfoInFiles = _StringList(default=["NOASSERTION"])
175 checksums = _ObjectList(SPDXChecksum)
176 fileTypes = _StringList()
177
178
179class SPDXCreationInfo(SPDXObject):
180 created = _String()
181 licenseListVersion = _String()
182 comment = _String()
183 creators = _StringList()
184
185
186class SPDXExternalDocumentRef(SPDXObject):
187 externalDocumentId = _String()
188 spdxDocument = _String()
189 checksum = _Object(SPDXChecksum)
190
191
192class SPDXDocument(SPDXObject):
193 spdxVersion = _String(default="SPDX-" + SPDX_VERSION)
194 dataLicense = _String(default="CC0-1.0")
195 SPDXID = _String(default="SPDXRef-DOCUMENT")
196 name = _String()
197 documentNamespace = _String()
198 creationInfo = _Object(SPDXCreationInfo)
199 packages = _ObjectList(SPDXPackage)
200 files = _ObjectList(SPDXFile)
201 relationships = _ObjectList(SPDXRelationship)
202 externalDocumentRefs = _ObjectList(SPDXExternalDocumentRef)
203
204 def __init__(self, **d):
205 super().__init__(**d)
206
207 def to_json(self, f, *, sort_keys=False, indent=None, separators=None):
208 class Encoder(json.JSONEncoder):
209 def default(self, o):
210 if isinstance(o, SPDXObject):
211 return o.serializer()
212
213 return super().default(o)
214
215 sha1 = hashlib.sha1()
216 for chunk in Encoder(
217 sort_keys=sort_keys,
218 indent=indent,
219 separators=separators,
220 ).iterencode(self):
221 chunk = chunk.encode("utf-8")
222 f.write(chunk)
223 sha1.update(chunk)
224
225 return sha1.hexdigest()
226
227 @classmethod
228 def from_json(cls, f):
229 return cls(**json.load(f))
230
231 def add_relationship(self, _from, relationship, _to, *, comment=None):
232 if isinstance(_from, SPDXObject):
233 from_spdxid = _from.SPDXID
234 else:
235 from_spdxid = _from
236
237 if isinstance(_to, SPDXObject):
238 to_spdxid = _to.SPDXID
239 else:
240 to_spdxid = _to
241
242 r = SPDXRelationship(
243 spdxElementId=from_spdxid,
244 relatedSpdxElement=to_spdxid,
245 relationshipType=relationship,
246 )
247
248 if comment is not None:
249 r.comment = comment
250
251 self.relationships.append(r)
252
253 def find_by_spdxid(self, spdxid):
254 for o in itertools.chain(self.packages, self.files):
255 if o.SPDXID == spdxid:
256 return o
257 return None
258
259 def find_external_document_ref(self, namespace):
260 for r in self.externalDocumentRefs:
261 if r.spdxDocument == namespace:
262 return r
263 return None