diff options
| author | Joshua Watt <JPEWhacker@gmail.com> | 2024-07-12 09:58:13 -0600 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2024-07-16 14:55:53 +0100 |
| commit | 8426e027e8b157fc39ff3e4e5d3eae7239081a2a (patch) | |
| tree | 3e21369dfdbb93d879c8f753fff544953502616e /meta/classes/create-spdx-image-3.0.bbclass | |
| parent | 28c9ac0d4f320b9fba261757b0a1c34b5e3b593b (diff) | |
| download | poky-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.bbclass | 415 |
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 | |||
| 8 | SPDX_ROOTFS_PACKAGES = "${SPDXDIR}/rootfs-packages.json" | ||
| 9 | SPDXIMAGEDEPLOYDIR = "${SPDXDIR}/image-deploy" | ||
| 10 | SPDXROOTFSDEPLOY = "${SPDXDIR}/rootfs-deploy" | ||
| 11 | |||
| 12 | def 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 | |||
| 42 | python 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 | } | ||
| 57 | ROOTFS_POSTUNINSTALL_COMMAND =+ "spdx_collect_rootfs_packages" | ||
| 58 | |||
| 59 | python 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 | } | ||
| 99 | addtask do_create_rootfs_spdx after do_rootfs before do_image | ||
| 100 | SSTATETASKS += "do_create_rootfs_spdx" | ||
| 101 | do_create_rootfs_spdx[sstate-inputdirs] = "${SPDXROOTFSDEPLOY}" | ||
| 102 | do_create_rootfs_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}" | ||
| 103 | do_create_rootfs_spdx[recrdeptask] += "do_create_spdx do_create_package_spdx" | ||
| 104 | do_create_rootfs_spdx[cleandirs] += "${SPDXROOTFSDEPLOY}" | ||
| 105 | |||
| 106 | python do_create_rootfs_spdx_setscene() { | ||
| 107 | sstate_setscene(d) | ||
| 108 | } | ||
| 109 | addtask do_create_rootfs_spdx_setscene | ||
| 110 | |||
| 111 | python 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 | } | ||
| 186 | addtask do_create_image_spdx after do_image_complete do_create_rootfs_spdx before do_build | ||
| 187 | SSTATETASKS += "do_create_image_spdx" | ||
| 188 | SSTATE_SKIP_CREATION:task-combine-image-type-spdx = "1" | ||
| 189 | do_create_image_spdx[sstate-inputdirs] = "${SPDXIMAGEWORK}" | ||
| 190 | do_create_image_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}" | ||
| 191 | do_create_image_spdx[cleandirs] = "${SPDXIMAGEWORK}" | ||
| 192 | do_create_image_spdx[dirs] = "${SPDXIMAGEWORK}" | ||
| 193 | |||
| 194 | python do_create_image_spdx_setscene() { | ||
| 195 | sstate_setscene(d) | ||
| 196 | } | ||
| 197 | addtask do_create_image_spdx_setscene | ||
| 198 | |||
| 199 | |||
| 200 | python 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 | } | ||
| 242 | addtask do_create_image_sbom_spdx after do_create_rootfs_spdx do_create_image_spdx before do_build | ||
| 243 | SSTATETASKS += "do_create_image_sbom_spdx" | ||
| 244 | SSTATE_SKIP_CREATION:task-create-image-sbom = "1" | ||
| 245 | do_create_image_sbom_spdx[sstate-inputdirs] = "${SPDXIMAGEDEPLOYDIR}" | ||
| 246 | do_create_image_sbom_spdx[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}" | ||
| 247 | do_create_image_sbom_spdx[stamp-extra-info] = "${MACHINE_ARCH}" | ||
| 248 | do_create_image_sbom_spdx[cleandirs] = "${SPDXIMAGEDEPLOYDIR}" | ||
| 249 | do_create_image_sbom_spdx[recrdeptask] += "do_create_spdx do_create_package_spdx" | ||
| 250 | |||
| 251 | python do_create_image_sbom_spdx_setscene() { | ||
| 252 | sstate_setscene(d) | ||
| 253 | } | ||
| 254 | addtask do_create_image_sbom_spdx_setscene | ||
| 255 | |||
| 256 | do_populate_sdk[recrdeptask] += "do_create_spdx do_create_package_spdx" | ||
| 257 | do_populate_sdk[cleandirs] += "${SPDXSDKWORK}" | ||
| 258 | do_populate_sdk[postfuncs] += "sdk_create_sbom" | ||
| 259 | POPULATE_SDK_POST_HOST_COMMAND:append:task-populate-sdk = " sdk_host_create_spdx" | ||
| 260 | POPULATE_SDK_POST_TARGET_COMMAND:append:task-populate-sdk = " sdk_target_create_spdx" | ||
| 261 | |||
| 262 | do_populate_sdk_ext[recrdeptask] += "do_create_spdx do_create_package_spdx" | ||
| 263 | do_populate_sdk_ext[cleandirs] += "${SPDXSDKEXTWORK}" | ||
| 264 | do_populate_sdk_ext[postfuncs] += "sdk_ext_create_sbom" | ||
| 265 | POPULATE_SDK_POST_HOST_COMMAND:append:task-populate-sdk-ext = " sdk_ext_host_create_spdx" | ||
| 266 | POPULATE_SDK_POST_TARGET_COMMAND:append:task-populate-sdk-ext = " sdk_ext_target_create_spdx" | ||
| 267 | |||
| 268 | python 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 | |||
| 275 | python 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 | |||
| 282 | python 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 | |||
| 290 | python 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 | |||
| 298 | def 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 | |||
| 333 | python 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 | |||
| 341 | python 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 | |||
| 349 | def 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 | |||
