diff options
Diffstat (limited to 'meta')
-rw-r--r-- | meta/classes/create-spdx-2.2.bbclass | 61 | ||||
-rw-r--r-- | meta/lib/oe/sbom.py | 22 |
2 files changed, 57 insertions, 26 deletions
diff --git a/meta/classes/create-spdx-2.2.bbclass b/meta/classes/create-spdx-2.2.bbclass index e0f62a43a2..6ec0c1465e 100644 --- a/meta/classes/create-spdx-2.2.bbclass +++ b/meta/classes/create-spdx-2.2.bbclass | |||
@@ -4,7 +4,7 @@ | |||
4 | # SPDX-License-Identifier: GPL-2.0-only | 4 | # SPDX-License-Identifier: GPL-2.0-only |
5 | # | 5 | # |
6 | 6 | ||
7 | DEPLOY_DIR_SPDX ??= "${DEPLOY_DIR}/spdx/${MACHINE}" | 7 | DEPLOY_DIR_SPDX ??= "${DEPLOY_DIR}/spdx" |
8 | 8 | ||
9 | # The product name that the CVE database uses. Defaults to BPN, but may need to | 9 | # The product name that the CVE database uses. Defaults to BPN, but may need to |
10 | # be overriden per recipe (for example tiff.bb sets CVE_PRODUCT=libtiff). | 10 | # be overriden per recipe (for example tiff.bb sets CVE_PRODUCT=libtiff). |
@@ -337,6 +337,20 @@ def add_package_sources_from_debug(d, package_doc, spdx_package, package, packag | |||
337 | 337 | ||
338 | package_doc.add_relationship(pkg_file, "GENERATED_FROM", ref_id, comment=debugsrc) | 338 | package_doc.add_relationship(pkg_file, "GENERATED_FROM", ref_id, comment=debugsrc) |
339 | 339 | ||
340 | def collect_deps(d): | ||
341 | current_task = "do_" + d.getVar("BB_CURRENTTASK") | ||
342 | |||
343 | taskdepdata = d.getVar("BB_TASKDEPDATA", False) | ||
344 | deps = sorted(set( | ||
345 | (dep[0], dep[7]) for dep in taskdepdata.values() if | ||
346 | dep[1] == current_task and dep[0] != d.getVar("PN") | ||
347 | )) | ||
348 | |||
349 | return deps | ||
350 | |||
351 | collect_deps[vardepsexclude] += "BB_TASKDEPDATA" | ||
352 | collect_deps[vardeps] += "DEPENDS" | ||
353 | |||
340 | def collect_dep_recipes(d, doc, spdx_recipe): | 354 | def collect_dep_recipes(d, doc, spdx_recipe): |
341 | from pathlib import Path | 355 | from pathlib import Path |
342 | import oe.sbom | 356 | import oe.sbom |
@@ -345,13 +359,9 @@ def collect_dep_recipes(d, doc, spdx_recipe): | |||
345 | deploy_dir_spdx = Path(d.getVar("DEPLOY_DIR_SPDX")) | 359 | deploy_dir_spdx = Path(d.getVar("DEPLOY_DIR_SPDX")) |
346 | 360 | ||
347 | dep_recipes = [] | 361 | dep_recipes = [] |
348 | taskdepdata = d.getVar("BB_TASKDEPDATA", False) | 362 | |
349 | deps = sorted(set( | 363 | for dep_pn, dep_hashfn in collect_deps(d): |
350 | dep[0] for dep in taskdepdata.values() if | 364 | dep_recipe_path = oe.sbom.doc_path_by_hashfn(deploy_dir_spdx, "recipe-" + dep_pn, dep_hashfn) |
351 | dep[1] == "do_create_spdx" and dep[0] != d.getVar("PN") | ||
352 | )) | ||
353 | for dep_pn in deps: | ||
354 | dep_recipe_path = deploy_dir_spdx / "recipes" / ("recipe-%s.spdx.json" % dep_pn) | ||
355 | 365 | ||
356 | spdx_dep_doc, spdx_dep_sha1 = oe.sbom.read_doc(dep_recipe_path) | 366 | spdx_dep_doc, spdx_dep_sha1 = oe.sbom.read_doc(dep_recipe_path) |
357 | 367 | ||
@@ -380,8 +390,6 @@ def collect_dep_recipes(d, doc, spdx_recipe): | |||
380 | 390 | ||
381 | return dep_recipes | 391 | return dep_recipes |
382 | 392 | ||
383 | collect_dep_recipes[vardepsexclude] += "BB_TASKDEPDATA" | ||
384 | collect_dep_recipes[vardeps] += "DEPENDS" | ||
385 | 393 | ||
386 | def collect_dep_sources(d, dep_recipes): | 394 | def collect_dep_sources(d, dep_recipes): |
387 | import oe.sbom | 395 | import oe.sbom |
@@ -571,7 +579,7 @@ python do_create_spdx() { | |||
571 | 579 | ||
572 | dep_recipes = collect_dep_recipes(d, doc, recipe) | 580 | dep_recipes = collect_dep_recipes(d, doc, recipe) |
573 | 581 | ||
574 | doc_sha1 = oe.sbom.write_doc(d, doc, "recipes", indent=get_json_indent(d)) | 582 | doc_sha1 = oe.sbom.write_doc(d, doc, d.getVar("SSTATE_PKGARCH"), "recipes", indent=get_json_indent(d)) |
575 | dep_recipes.append(oe.sbom.DepRecipe(doc, doc_sha1, recipe)) | 583 | dep_recipes.append(oe.sbom.DepRecipe(doc, doc_sha1, recipe)) |
576 | 584 | ||
577 | recipe_ref = oe.spdx.SPDXExternalDocumentRef() | 585 | recipe_ref = oe.spdx.SPDXExternalDocumentRef() |
@@ -636,7 +644,7 @@ python do_create_spdx() { | |||
636 | 644 | ||
637 | add_package_sources_from_debug(d, package_doc, spdx_package, package, package_files, sources) | 645 | add_package_sources_from_debug(d, package_doc, spdx_package, package, package_files, sources) |
638 | 646 | ||
639 | oe.sbom.write_doc(d, package_doc, "packages", indent=get_json_indent(d)) | 647 | oe.sbom.write_doc(d, package_doc, d.getVar("SSTATE_PKGARCH"), "packages", indent=get_json_indent(d)) |
640 | } | 648 | } |
641 | # NOTE: depending on do_unpack is a hack that is necessary to get it's dependencies for archive the source | 649 | # NOTE: depending on do_unpack is a hack that is necessary to get it's dependencies for archive the source |
642 | addtask do_create_spdx after do_package do_packagedata do_unpack before do_populate_sdk do_build do_rm_work | 650 | addtask do_create_spdx after do_package do_packagedata do_unpack before do_populate_sdk do_build do_rm_work |
@@ -667,11 +675,11 @@ def collect_package_providers(d): | |||
667 | 675 | ||
668 | taskdepdata = d.getVar("BB_TASKDEPDATA", False) | 676 | taskdepdata = d.getVar("BB_TASKDEPDATA", False) |
669 | deps = sorted(set( | 677 | deps = sorted(set( |
670 | dep[0] for dep in taskdepdata.values() if dep[0] != d.getVar("PN") | 678 | (dep[0], dep[7]) for dep in taskdepdata.values() if dep[0] != d.getVar("PN") |
671 | )) | 679 | )) |
672 | deps.append(d.getVar("PN")) | 680 | deps.append((d.getVar("PN"), d.getVar("BB_HASHFILENAME"))) |
673 | 681 | ||
674 | for dep_pn in deps: | 682 | for dep_pn, dep_hashfn in deps: |
675 | recipe_data = oe.packagedata.read_pkgdata(dep_pn, d) | 683 | recipe_data = oe.packagedata.read_pkgdata(dep_pn, d) |
676 | 684 | ||
677 | for pkg in recipe_data.get("PACKAGES", "").split(): | 685 | for pkg in recipe_data.get("PACKAGES", "").split(): |
@@ -681,7 +689,7 @@ def collect_package_providers(d): | |||
681 | rprovides.add(pkg) | 689 | rprovides.add(pkg) |
682 | 690 | ||
683 | for r in rprovides: | 691 | for r in rprovides: |
684 | providers[r] = pkg | 692 | providers[r] = (pkg, dep_hashfn) |
685 | 693 | ||
686 | return providers | 694 | return providers |
687 | 695 | ||
@@ -717,7 +725,7 @@ python do_create_runtime_spdx() { | |||
717 | if not oe.packagedata.packaged(package, localdata): | 725 | if not oe.packagedata.packaged(package, localdata): |
718 | continue | 726 | continue |
719 | 727 | ||
720 | pkg_spdx_path = deploy_dir_spdx / "packages" / (pkg_name + ".spdx.json") | 728 | pkg_spdx_path = oe.sbom.doc_path(deploy_dir_spdx, pkg_name, d.getVar("SSTATE_PKGARCH"), "packages") |
721 | 729 | ||
722 | package_doc, package_doc_sha1 = oe.sbom.read_doc(pkg_spdx_path) | 730 | package_doc, package_doc_sha1 = oe.sbom.read_doc(pkg_spdx_path) |
723 | 731 | ||
@@ -761,7 +769,7 @@ python do_create_runtime_spdx() { | |||
761 | if dep not in providers: | 769 | if dep not in providers: |
762 | continue | 770 | continue |
763 | 771 | ||
764 | dep = providers[dep] | 772 | (dep, dep_hashfn) = providers[dep] |
765 | 773 | ||
766 | if not oe.packagedata.packaged(dep, localdata): | 774 | if not oe.packagedata.packaged(dep, localdata): |
767 | continue | 775 | continue |
@@ -772,7 +780,7 @@ python do_create_runtime_spdx() { | |||
772 | if dep in dep_package_cache: | 780 | if dep in dep_package_cache: |
773 | (dep_spdx_package, dep_package_ref) = dep_package_cache[dep] | 781 | (dep_spdx_package, dep_package_ref) = dep_package_cache[dep] |
774 | else: | 782 | else: |
775 | dep_path = deploy_dir_spdx / "packages" / ("%s.spdx.json" % dep_pkg) | 783 | dep_path = oe.sbom.doc_path_by_hashfn(deploy_dir_spdx, dep_pkg, dep_hashfn) |
776 | 784 | ||
777 | spdx_dep_doc, spdx_dep_sha1 = oe.sbom.read_doc(dep_path) | 785 | spdx_dep_doc, spdx_dep_sha1 = oe.sbom.read_doc(dep_path) |
778 | 786 | ||
@@ -800,7 +808,7 @@ python do_create_runtime_spdx() { | |||
800 | ) | 808 | ) |
801 | seen_deps.add(dep) | 809 | seen_deps.add(dep) |
802 | 810 | ||
803 | oe.sbom.write_doc(d, runtime_doc, "runtime", spdx_deploy, indent=get_json_indent(d)) | 811 | oe.sbom.write_doc(d, runtime_doc, d.getVar("SSTATE_PKGARCH"), "runtime", spdx_deploy, indent=get_json_indent(d)) |
804 | } | 812 | } |
805 | 813 | ||
806 | addtask do_create_runtime_spdx after do_create_spdx before do_build do_rm_work | 814 | addtask do_create_runtime_spdx after do_create_spdx before do_build do_rm_work |
@@ -933,6 +941,8 @@ def combine_spdx(d, rootfs_name, rootfs_deploydir, rootfs_spdxid, packages, spdx | |||
933 | import tarfile | 941 | import tarfile |
934 | import bb.compress.zstd | 942 | import bb.compress.zstd |
935 | 943 | ||
944 | providers = collect_package_providers(d) | ||
945 | |||
936 | creation_time = datetime.now(tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") | 946 | creation_time = datetime.now(tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") |
937 | deploy_dir_spdx = Path(d.getVar("DEPLOY_DIR_SPDX")) | 947 | deploy_dir_spdx = Path(d.getVar("DEPLOY_DIR_SPDX")) |
938 | source_date_epoch = d.getVar("SOURCE_DATE_EPOCH") | 948 | source_date_epoch = d.getVar("SOURCE_DATE_EPOCH") |
@@ -956,7 +966,12 @@ def combine_spdx(d, rootfs_name, rootfs_deploydir, rootfs_spdxid, packages, spdx | |||
956 | doc.packages.append(image) | 966 | doc.packages.append(image) |
957 | 967 | ||
958 | for name in sorted(packages.keys()): | 968 | for name in sorted(packages.keys()): |
959 | pkg_spdx_path = deploy_dir_spdx / "packages" / (name + ".spdx.json") | 969 | if name not in providers: |
970 | bb.fatal("Unable to find provider for '%s'" % name) | ||
971 | |||
972 | pkg_name, pkg_hashfn = providers[name] | ||
973 | |||
974 | pkg_spdx_path = oe.sbom.doc_path_by_hashfn(deploy_dir_spdx, pkg_name, pkg_hashfn) | ||
960 | pkg_doc, pkg_doc_sha1 = oe.sbom.read_doc(pkg_spdx_path) | 975 | pkg_doc, pkg_doc_sha1 = oe.sbom.read_doc(pkg_spdx_path) |
961 | 976 | ||
962 | for p in pkg_doc.packages: | 977 | for p in pkg_doc.packages: |
@@ -973,7 +988,7 @@ def combine_spdx(d, rootfs_name, rootfs_deploydir, rootfs_spdxid, packages, spdx | |||
973 | else: | 988 | else: |
974 | bb.fatal("Unable to find package with name '%s' in SPDX file %s" % (name, pkg_spdx_path)) | 989 | bb.fatal("Unable to find package with name '%s' in SPDX file %s" % (name, pkg_spdx_path)) |
975 | 990 | ||
976 | runtime_spdx_path = deploy_dir_spdx / "runtime" / ("runtime-" + name + ".spdx.json") | 991 | runtime_spdx_path = oe.sbom.doc_path_by_hashfn(deploy_dir_spdx, "runtime-" + name, pkg_hashfn) |
977 | runtime_doc, runtime_doc_sha1 = oe.sbom.read_doc(runtime_spdx_path) | 992 | runtime_doc, runtime_doc_sha1 = oe.sbom.read_doc(runtime_spdx_path) |
978 | 993 | ||
979 | runtime_ref = oe.spdx.SPDXExternalDocumentRef() | 994 | runtime_ref = oe.spdx.SPDXExternalDocumentRef() |
@@ -1045,7 +1060,7 @@ def combine_spdx(d, rootfs_name, rootfs_deploydir, rootfs_spdxid, packages, spdx | |||
1045 | }) | 1060 | }) |
1046 | 1061 | ||
1047 | for ref in doc.externalDocumentRefs: | 1062 | for ref in doc.externalDocumentRefs: |
1048 | ref_path = deploy_dir_spdx / "by-namespace" / ref.spdxDocument.replace("/", "_") | 1063 | ref_path = oe.sbom.doc_path_by_namespace(deploy_dir_spdx, ref.spdxDocument) |
1049 | collect_spdx_document(ref_path) | 1064 | collect_spdx_document(ref_path) |
1050 | 1065 | ||
1051 | collect_spdx_document(image_spdx_path) | 1066 | collect_spdx_document(image_spdx_path) |
diff --git a/meta/lib/oe/sbom.py b/meta/lib/oe/sbom.py index 22ed5070ea..1130fa668b 100644 --- a/meta/lib/oe/sbom.py +++ b/meta/lib/oe/sbom.py | |||
@@ -38,18 +38,34 @@ def get_sdk_spdxid(sdk): | |||
38 | return "SPDXRef-SDK-%s" % sdk | 38 | return "SPDXRef-SDK-%s" % sdk |
39 | 39 | ||
40 | 40 | ||
41 | def write_doc(d, spdx_doc, subdir, spdx_deploy=None, indent=None): | 41 | def doc_path_by_namespace(spdx_deploy, doc_namespace): |
42 | return spdx_deploy / "by-namespace" / doc_namespace.replace("/", "_") | ||
43 | |||
44 | |||
45 | def doc_path_by_hashfn(spdx_deploy, doc_name, hashfn): | ||
46 | return spdx_deploy / "by-hash" / hashfn.split()[1] / (doc_name + ".spdx.json") | ||
47 | |||
48 | |||
49 | def doc_path(spdx_deploy, doc_name, arch, subdir): | ||
50 | return spdx_deploy / arch/ subdir / (doc_name + ".spdx.json") | ||
51 | |||
52 | |||
53 | def write_doc(d, spdx_doc, arch, subdir, spdx_deploy=None, indent=None): | ||
42 | from pathlib import Path | 54 | from pathlib import Path |
43 | 55 | ||
44 | if spdx_deploy is None: | 56 | if spdx_deploy is None: |
45 | spdx_deploy = Path(d.getVar("SPDXDEPLOY")) | 57 | spdx_deploy = Path(d.getVar("SPDXDEPLOY")) |
46 | 58 | ||
47 | dest = spdx_deploy / subdir / (spdx_doc.name + ".spdx.json") | 59 | dest = doc_path(spdx_deploy, spdx_doc.name, arch, subdir) |
48 | dest.parent.mkdir(exist_ok=True, parents=True) | 60 | dest.parent.mkdir(exist_ok=True, parents=True) |
49 | with dest.open("wb") as f: | 61 | with dest.open("wb") as f: |
50 | doc_sha1 = spdx_doc.to_json(f, sort_keys=True, indent=indent) | 62 | doc_sha1 = spdx_doc.to_json(f, sort_keys=True, indent=indent) |
51 | 63 | ||
52 | l = spdx_deploy / "by-namespace" / spdx_doc.documentNamespace.replace("/", "_") | 64 | l = doc_path_by_namespace(spdx_deploy, spdx_doc.documentNamespace) |
65 | l.parent.mkdir(exist_ok=True, parents=True) | ||
66 | l.symlink_to(os.path.relpath(dest, l.parent)) | ||
67 | |||
68 | l = doc_path_by_hashfn(spdx_deploy, spdx_doc.name, d.getVar("BB_HASHFILENAME")) | ||
53 | l.parent.mkdir(exist_ok=True, parents=True) | 69 | l.parent.mkdir(exist_ok=True, parents=True) |
54 | l.symlink_to(os.path.relpath(dest, l.parent)) | 70 | l.symlink_to(os.path.relpath(dest, l.parent)) |
55 | 71 | ||