summaryrefslogtreecommitdiffstats
path: root/meta/classes/create-spdx-image-3.0.bbclass
diff options
context:
space:
mode:
authorJoshua Watt <JPEWhacker@gmail.com>2024-07-12 09:58:13 -0600
committerRichard Purdie <richard.purdie@linuxfoundation.org>2024-07-16 14:55:53 +0100
commit8426e027e8b157fc39ff3e4e5d3eae7239081a2a (patch)
tree3e21369dfdbb93d879c8f753fff544953502616e /meta/classes/create-spdx-image-3.0.bbclass
parent28c9ac0d4f320b9fba261757b0a1c34b5e3b593b (diff)
downloadpoky-8426e027e8b157fc39ff3e4e5d3eae7239081a2a.tar.gz
classes/create-spdx-3.0: Add classes
Adds a class to generate SPDX 3.0 output and an image class that is used when generating images (From OE-Core rev: b63f6f50458fc6898e4deda5d6739e7bf3639c15) Signed-off-by: Joshua Watt <JPEWhacker@gmail.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/classes/create-spdx-image-3.0.bbclass')
-rw-r--r--meta/classes/create-spdx-image-3.0.bbclass415
1 files changed, 415 insertions, 0 deletions
diff --git a/meta/classes/create-spdx-image-3.0.bbclass b/meta/classes/create-spdx-image-3.0.bbclass
new file mode 100644
index 0000000000..bda11d54d4
--- /dev/null
+++ b/meta/classes/create-spdx-image-3.0.bbclass
@@ -0,0 +1,415 @@
1#
2# Copyright OpenEmbedded Contributors
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6# SPDX image tasks
7
8SPDX_ROOTFS_PACKAGES = "${SPDXDIR}/rootfs-packages.json"
9SPDXIMAGEDEPLOYDIR = "${SPDXDIR}/image-deploy"
10SPDXROOTFSDEPLOY = "${SPDXDIR}/rootfs-deploy"
11
12def collect_build_package_inputs(d, objset, build, packages):
13 providers = collect_package_providers(d)
14
15 build_deps = set()
16
17 for name in sorted(packages.keys()):
18 if name not in providers:
19 bb.fatal("Unable to find SPDX provider for '%s'" % name)
20
21 pkg_name, pkg_hashfn = providers[name]
22
23 # Copy all of the package SPDX files into the Sbom elements
24 pkg_spdx, _ = oe.sbom30.find_root_obj_in_jsonld(
25 d,
26 "packages",
27 pkg_name,
28 oe.spdx30.software_Package,
29 software_primaryPurpose=oe.spdx30.software_SoftwarePurpose.install,
30 )
31 build_deps.add(pkg_spdx._id)
32
33 if build_deps:
34 objset.new_scoped_relationship(
35 [build],
36 oe.spdx30.RelationshipType.hasInputs,
37 oe.spdx30.LifecycleScopeType.build,
38 sorted(list(build_deps)),
39 )
40
41
42python spdx_collect_rootfs_packages() {
43 import json
44 from pathlib import Path
45 from oe.rootfs import image_list_installed_packages
46
47 root_packages_file = Path(d.getVar("SPDX_ROOTFS_PACKAGES"))
48
49 packages = image_list_installed_packages(d)
50 if not packages:
51 packages = {}
52
53 root_packages_file.parent.mkdir(parents=True, exist_ok=True)
54 with root_packages_file.open("w") as f:
55 json.dump(packages, f)
56}
57ROOTFS_POSTUNINSTALL_COMMAND =+ "spdx_collect_rootfs_packages"
58
59python do_create_rootfs_spdx() {
60 import json
61 from pathlib import Path
62 import oe.spdx30
63 import oe.sbom30
64 from datetime import datetime
65
66 deploy_dir_spdx = Path(d.getVar("DEPLOY_DIR_SPDX"))
67 deploydir = Path(d.getVar("SPDXROOTFSDEPLOY"))
68 root_packages_file = Path(d.getVar("SPDX_ROOTFS_PACKAGES"))
69 image_basename = d.getVar("IMAGE_BASENAME")
70 machine = d.getVar("MACHINE")
71
72 with root_packages_file.open("r") as f:
73 packages = json.load(f)
74
75 objset = oe.sbom30.ObjectSet.new_objset(d, "%s-%s" % (image_basename, machine))
76
77 rootfs = objset.add_root(oe.spdx30.software_Package(
78 _id=objset.new_spdxid("rootfs", image_basename),
79 creationInfo=objset.doc.creationInfo,
80 name=image_basename,
81 software_primaryPurpose=oe.spdx30.software_SoftwarePurpose.archive,
82 ))
83 set_timestamp_now(d, rootfs, "builtTime")
84
85 rootfs_build = objset.add_root(objset.new_task_build("rootfs", "rootfs"))
86 set_timestamp_now(d, rootfs_build, "build_buildEndTime")
87
88 objset.new_scoped_relationship(
89 [rootfs_build],
90 oe.spdx30.RelationshipType.hasOutputs,
91 oe.spdx30.LifecycleScopeType.build,
92 [rootfs],
93 )
94
95 collect_build_package_inputs(d, objset, rootfs_build, packages)
96
97 oe.sbom30.write_recipe_jsonld_doc(d, objset, "rootfs", deploydir)
98}
99addtask do_create_rootfs_spdx after do_rootfs before do_image
100SSTATETASKS += "do_create_rootfs_spdx"
101do_create_rootfs_spdx[sstate-inputdirs] = "${SPDXROOTFSDEPLOY}"
102do_create_rootfs_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}"
103do_create_rootfs_spdx[recrdeptask] += "do_create_spdx do_create_package_spdx"
104do_create_rootfs_spdx[cleandirs] += "${SPDXROOTFSDEPLOY}"
105
106python do_create_rootfs_spdx_setscene() {
107 sstate_setscene(d)
108}
109addtask do_create_rootfs_spdx_setscene
110
111python do_create_image_spdx() {
112 import oe.spdx30
113 import oe.sbom30
114 import json
115 from pathlib import Path
116
117 image_deploy_dir = Path(d.getVar('IMGDEPLOYDIR'))
118 manifest_path = Path(d.getVar("IMAGE_OUTPUT_MANIFEST"))
119 spdx_work_dir = Path(d.getVar('SPDXIMAGEWORK'))
120
121 image_basename = d.getVar('IMAGE_BASENAME')
122 machine = d.getVar("MACHINE")
123
124 objset = oe.sbom30.ObjectSet.new_objset(d, "%s-%s" % (image_basename, machine))
125
126 with manifest_path.open("r") as f:
127 manifest = json.load(f)
128
129 builds = []
130 for task in manifest:
131 imagetype = task["imagetype"]
132 taskname = task["taskname"]
133
134 image_build = objset.add_root(objset.new_task_build(taskname, "image/%s" % imagetype))
135 set_timestamp_now(d, image_build, "build_buildEndTime")
136 builds.append(image_build)
137
138 artifacts = []
139
140 for image in task["images"]:
141 image_filename = image["filename"]
142 image_path = image_deploy_dir / image_filename
143 a = objset.add_root(oe.spdx30.software_File(
144 _id=objset.new_spdxid("image", image_filename),
145 creationInfo=objset.doc.creationInfo,
146 name=image_filename,
147 verifiedUsing=[
148 oe.spdx30.Hash(
149 algorithm=oe.spdx30.HashAlgorithm.sha256,
150 hashValue=bb.utils.sha256_file(image_path),
151 )
152 ]
153 ))
154 set_purposes(d, a, "SPDX_IMAGE_PURPOSE:%s" % imagetype, "SPDX_IMAGE_PURPOSE")
155 set_timestamp_now(d, a, "builtTime")
156
157 artifacts.append(a)
158
159 if artifacts:
160 objset.new_scoped_relationship(
161 [image_build],
162 oe.spdx30.RelationshipType.hasOutputs,
163 oe.spdx30.LifecycleScopeType.build,
164 artifacts,
165 )
166
167 if builds:
168 rootfs_image, _ = oe.sbom30.find_root_obj_in_jsonld(
169 d,
170 "rootfs",
171 "%s-%s" % (image_basename, machine),
172 oe.spdx30.software_Package,
173 # TODO: Should use a purpose to filter here?
174 )
175 objset.new_scoped_relationship(
176 builds,
177 oe.spdx30.RelationshipType.hasInputs,
178 oe.spdx30.LifecycleScopeType.build,
179 [rootfs_image._id],
180 )
181
182 objset.add_aliases()
183 objset.link()
184 oe.sbom30.write_recipe_jsonld_doc(d, objset, "image", spdx_work_dir)
185}
186addtask do_create_image_spdx after do_image_complete do_create_rootfs_spdx before do_build
187SSTATETASKS += "do_create_image_spdx"
188SSTATE_SKIP_CREATION:task-combine-image-type-spdx = "1"
189do_create_image_spdx[sstate-inputdirs] = "${SPDXIMAGEWORK}"
190do_create_image_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}"
191do_create_image_spdx[cleandirs] = "${SPDXIMAGEWORK}"
192do_create_image_spdx[dirs] = "${SPDXIMAGEWORK}"
193
194python do_create_image_spdx_setscene() {
195 sstate_setscene(d)
196}
197addtask do_create_image_spdx_setscene
198
199
200python do_create_image_sbom_spdx() {
201 import os
202 from pathlib import Path
203 import oe.spdx30
204 import oe.sbom30
205
206 image_name = d.getVar("IMAGE_NAME")
207 image_basename = d.getVar("IMAGE_BASENAME")
208 image_link_name = d.getVar("IMAGE_LINK_NAME")
209 imgdeploydir = Path(d.getVar("SPDXIMAGEDEPLOYDIR"))
210 machine = d.getVar("MACHINE")
211
212 spdx_path = imgdeploydir / (image_name + ".spdx.json")
213
214 root_elements = []
215
216 # TODO: Do we need to add the rootfs or are the image files sufficient?
217 rootfs_image, _ = oe.sbom30.find_root_obj_in_jsonld(
218 d,
219 "rootfs",
220 "%s-%s" % (image_basename, machine),
221 oe.spdx30.software_Package,
222 # TODO: Should use a purpose here?
223 )
224 root_elements.append(rootfs_image._id)
225
226 image_objset, _ = oe.sbom30.find_jsonld(d, "image", "%s-%s" % (image_basename, machine), required=True)
227 for o in image_objset.foreach_root(oe.spdx30.software_File):
228 root_elements.append(o._id)
229
230 objset, sbom = oe.sbom30.create_sbom(d, image_name, root_elements)
231
232 oe.sbom30.write_jsonld_doc(d, objset, spdx_path)
233
234 def make_image_link(target_path, suffix):
235 if image_link_name:
236 link = imgdeploydir / (image_link_name + suffix)
237 if link != target_path:
238 link.symlink_to(os.path.relpath(target_path, link.parent))
239
240 make_image_link(spdx_path, ".spdx.json")
241}
242addtask do_create_image_sbom_spdx after do_create_rootfs_spdx do_create_image_spdx before do_build
243SSTATETASKS += "do_create_image_sbom_spdx"
244SSTATE_SKIP_CREATION:task-create-image-sbom = "1"
245do_create_image_sbom_spdx[sstate-inputdirs] = "${SPDXIMAGEDEPLOYDIR}"
246do_create_image_sbom_spdx[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}"
247do_create_image_sbom_spdx[stamp-extra-info] = "${MACHINE_ARCH}"
248do_create_image_sbom_spdx[cleandirs] = "${SPDXIMAGEDEPLOYDIR}"
249do_create_image_sbom_spdx[recrdeptask] += "do_create_spdx do_create_package_spdx"
250
251python do_create_image_sbom_spdx_setscene() {
252 sstate_setscene(d)
253}
254addtask do_create_image_sbom_spdx_setscene
255
256do_populate_sdk[recrdeptask] += "do_create_spdx do_create_package_spdx"
257do_populate_sdk[cleandirs] += "${SPDXSDKWORK}"
258do_populate_sdk[postfuncs] += "sdk_create_sbom"
259POPULATE_SDK_POST_HOST_COMMAND:append:task-populate-sdk = " sdk_host_create_spdx"
260POPULATE_SDK_POST_TARGET_COMMAND:append:task-populate-sdk = " sdk_target_create_spdx"
261
262do_populate_sdk_ext[recrdeptask] += "do_create_spdx do_create_package_spdx"
263do_populate_sdk_ext[cleandirs] += "${SPDXSDKEXTWORK}"
264do_populate_sdk_ext[postfuncs] += "sdk_ext_create_sbom"
265POPULATE_SDK_POST_HOST_COMMAND:append:task-populate-sdk-ext = " sdk_ext_host_create_spdx"
266POPULATE_SDK_POST_TARGET_COMMAND:append:task-populate-sdk-ext = " sdk_ext_target_create_spdx"
267
268python sdk_host_create_spdx() {
269 from pathlib import Path
270 spdx_work_dir = Path(d.getVar('SPDXSDKWORK'))
271
272 sdk_create_spdx(d, "host", spdx_work_dir, d.getVar("TOOLCHAIN_OUTPUTNAME"))
273}
274
275python sdk_target_create_spdx() {
276 from pathlib import Path
277 spdx_work_dir = Path(d.getVar('SPDXSDKWORK'))
278
279 sdk_create_spdx(d, "target", spdx_work_dir, d.getVar("TOOLCHAIN_OUTPUTNAME"))
280}
281
282python sdk_ext_host_create_spdx() {
283 from pathlib import Path
284 spdx_work_dir = Path(d.getVar('SPDXSDKEXTWORK'))
285
286 # TODO: This doesn't seem to work
287 sdk_create_spdx(d, "host", spdx_work_dir, d.getVar("TOOLCHAINEXT_OUTPUTNAME"))
288}
289
290python sdk_ext_target_create_spdx() {
291 from pathlib import Path
292 spdx_work_dir = Path(d.getVar('SPDXSDKEXTWORK'))
293
294 # TODO: This doesn't seem to work
295 sdk_create_spdx(d, "target", spdx_work_dir, d.getVar("TOOLCHAINEXT_OUTPUTNAME"))
296}
297
298def sdk_create_spdx(d, sdk_type, spdx_work_dir, toolchain_outputname):
299 from pathlib import Path
300 from oe.sdk import sdk_list_installed_packages
301 import oe.spdx30
302 import oe.sbom30
303 from datetime import datetime
304
305 sdk_name = toolchain_outputname + "-" + sdk_type
306 sdk_packages = sdk_list_installed_packages(d, sdk_type == "target")
307
308 objset = oe.sbom30.ObjectSet.new_objset(d, sdk_name)
309
310 sdk_rootfs = objset.add_root(oe.spdx30.software_Package(
311 _id=objset.new_spdxid("sdk-rootfs", sdk_name),
312 creationInfo=objset.doc.creationInfo,
313 name=sdk_name,
314 software_primaryPurpose=oe.spdx30.software_SoftwarePurpose.archive,
315 ))
316 set_timestamp_now(d, sdk_rootfs, "builtTime")
317
318 sdk_build = objset.add_root(objset.new_task_build("sdk-rootfs", "sdk-rootfs"))
319 set_timestamp_now(d, sdk_build, "build_buildEndTime")
320
321 objset.new_scoped_relationship(
322 [sdk_build],
323 oe.spdx30.RelationshipType.hasOutputs,
324 oe.spdx30.LifecycleScopeType.build,
325 [sdk_rootfs],
326 )
327
328 collect_build_package_inputs(d, objset, sdk_build, sdk_packages)
329
330 objset.add_aliases()
331 oe.sbom30.write_jsonld_doc(d, objset, spdx_work_dir / "sdk-rootfs.spdx.json")
332
333python sdk_create_sbom() {
334 from pathlib import Path
335 sdk_deploydir = Path(d.getVar("SDKDEPLOYDIR"))
336 spdx_work_dir = Path(d.getVar('SPDXSDKWORK'))
337
338 create_sdk_sbom(d, sdk_deploydir, spdx_work_dir, d.getVar("TOOLCHAIN_OUTPUTNAME"))
339}
340
341python sdk_ext_create_sbom() {
342 from pathlib import Path
343 sdk_deploydir = Path(d.getVar("SDKEXTDEPLOYDIR"))
344 spdx_work_dir = Path(d.getVar('SPDXSDKEXTWORK'))
345
346 create_sdk_sbom(d, sdk_deploydir, spdx_work_dir, d.getVar("TOOLCHAINEXT_OUTPUTNAME"))
347}
348
349def create_sdk_sbom(d, sdk_deploydir, spdx_work_dir, toolchain_outputname):
350 import oe.spdx30
351 import oe.sbom30
352 from pathlib import Path
353 from datetime import datetime
354
355 # Load the document written earlier
356 rootfs_objset = oe.sbom30.load_jsonld(d, spdx_work_dir / "sdk-rootfs.spdx.json", required=True)
357
358 # Create a new build for the SDK installer
359 sdk_build = rootfs_objset.new_task_build("sdk-populate", "sdk-populate")
360 set_timestamp_now(d, sdk_build, "build_buildEndTime")
361
362 rootfs = rootfs_objset.find_root(oe.spdx30.software_Package)
363 if rootfs is None:
364 bb.fatal("Unable to find rootfs artifact")
365
366 rootfs_objset.new_scoped_relationship(
367 [sdk_build],
368 oe.spdx30.RelationshipType.hasInputs,
369 oe.spdx30.LifecycleScopeType.build,
370 [rootfs]
371 )
372
373 files = set()
374 root_files = []
375
376 # NOTE: os.walk() doesn't return symlinks
377 for dirpath, dirnames, filenames in os.walk(sdk_deploydir):
378 for fn in filenames:
379 fpath = Path(dirpath) / fn
380 if not fpath.is_file() or fpath.is_symlink():
381 continue
382
383 relpath = str(fpath.relative_to(sdk_deploydir))
384
385 f = rootfs_objset.new_file(
386 rootfs_objset.new_spdxid("sdk-installer", relpath),
387 relpath,
388 fpath,
389 )
390 set_timestamp_now(d, f, "builtTime")
391
392 if fn.endswith(".manifest"):
393 f.software_primaryPurpose = oe.spdx30.software_SoftwarePurpose.manifest
394 elif fn.endswith(".testdata.json"):
395 f.software_primaryPurpose = oe.spdx30.software_SoftwarePurpose.configuration
396 else:
397 set_purposes(d, f, "SPDX_SDK_PURPOSE")
398 root_files.append(f)
399
400 files.add(f)
401
402 if files:
403 rootfs_objset.new_scoped_relationship(
404 [sdk_build],
405 oe.spdx30.RelationshipType.hasOutputs,
406 oe.spdx30.LifecycleScopeType.build,
407 files,
408 )
409 else:
410 bb.warn(f"No SDK output files found in {sdk_deploydir}")
411
412 objset, sbom = oe.sbom30.create_sbom(d, toolchain_outputname, sorted(list(files)), [rootfs_objset])
413
414 oe.sbom30.write_jsonld_doc(d, objset, sdk_deploydir / (toolchain_outputname + ".spdx.json"))
415