Signed-off-by: Joshua Watt <jpewhac...@gmail.com>
---
 meta/classes/create-spdx.bbclass | 179 ++++++++++++++++++++++++++++++-
 meta/lib/oe/sbom.py              |   5 +-
 2 files changed, 181 insertions(+), 3 deletions(-)

diff --git a/meta/classes/create-spdx.bbclass b/meta/classes/create-spdx.bbclass
index 14caae8a50..28a2e64f52 100644
--- a/meta/classes/create-spdx.bbclass
+++ b/meta/classes/create-spdx.bbclass
@@ -13,6 +13,8 @@ SPDXDIR ??= "${WORKDIR}/spdx"
 SPDXDEPLOY = "${SPDXDIR}/deploy"
 SPDXWORK = "${SPDXDIR}/work"
 
+SPDXRUNTIMEDEPLOY = "${SPDXDIR}/runtime-deploy"
+
 SPDX_INCLUDE_SOURCES ??= "0"
 SPDX_INCLUDE_PACKAGED ??= "0"
 SPDX_ARCHIVE_SOURCES ??= "0"
@@ -486,6 +488,163 @@ do_create_spdx[cleandirs] = "${SPDXDEPLOY} ${SPDXWORK}"
 do_create_spdx[depends] += "${PATCHDEPENDENCY}"
 do_create_spdx[deptask] = "do_create_spdx"
 
+def collect_package_providers(d):
+    from pathlib import Path
+    import oe.sbom
+    import oe.spdx
+    import json
+
+    deploy_dir_spdx = Path(d.getVar("DEPLOY_DIR_SPDX"))
+
+    providers = {}
+
+    taskdepdata = d.getVar("BB_TASKDEPDATA", False)
+    deps = sorted(set(
+        dep[0] for dep in taskdepdata.values() if
+            dep[1] == "do_create_spdx" and dep[0] != d.getVar("PN")
+    ))
+    deps.append(d.getVar("PN"))
+
+    for dep_pn in deps:
+        recipe_data = oe.packagedata.read_pkgdata(dep_pn, d)
+
+        for pkg in recipe_data.get("PACKAGES", "").split():
+
+            pkg_data = oe.packagedata.read_subpkgdata_dict(pkg, d)
+            rprovides = set(n for n, _ in 
bb.utils.explode_dep_versions2(pkg_data.get("RPROVIDES", "")).items())
+            rprovides.add(pkg)
+
+            for r in rprovides:
+                providers[r] = pkg
+
+    return providers
+
+collect_package_providers[vardepsexclude] += "BB_TASKDEPDATA"
+
+python do_create_runtime_spdx() {
+    from datetime import datetime, timezone
+    import oe.sbom
+    import oe.spdx
+    import oe.packagedata
+    from pathlib import Path
+
+    deploy_dir_spdx = Path(d.getVar("DEPLOY_DIR_SPDX"))
+    spdx_deploy = Path(d.getVar("SPDXRUNTIMEDEPLOY"))
+
+    creation_time = 
datetime.now(tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
+
+    providers = collect_package_providers(d)
+
+    bb.build.exec_func("read_subpackage_metadata", d)
+
+    dep_package_cache = {}
+
+    pkgdest = Path(d.getVar("PKGDEST"))
+    for package in d.getVar("PACKAGES").split():
+        localdata = bb.data.createCopy(d)
+        pkg_name = d.getVar("PKG:%s" % package) or package
+        localdata.setVar("PKG", pkg_name)
+        localdata.setVar('OVERRIDES', d.getVar("OVERRIDES", False) + ":" + 
package)
+
+        if not oe.packagedata.packaged(package, localdata):
+            continue
+
+        pkg_spdx_path = deploy_dir_spdx / "packages" / (pkg_name + 
".spdx.json")
+
+        package_doc, package_doc_sha1 = oe.sbom.read_doc(pkg_spdx_path)
+
+        for p in package_doc.packages:
+            if p.name == pkg_name:
+                spdx_package = p
+                break
+        else:
+            bb.fatal("Package '%s' not found in %s" % (pkg_name, 
pkg_spdx_path))
+
+        runtime_doc = oe.spdx.SPDXDocument()
+        runtime_doc.name = "runtime-" + pkg_name
+        runtime_doc.documentNamespace = get_doc_namespace(localdata, 
runtime_doc)
+        runtime_doc.creationInfo.created = creation_time
+        runtime_doc.creationInfo.comment = "This document was created by 
analyzing package runtime dependencies."
+        runtime_doc.creationInfo.creators.append("Tool: OpenEmbedded Core 
create-spdx.bbclass")
+        runtime_doc.creationInfo.creators.append("Organization: OpenEmbedded 
()")
+        runtime_doc.creationInfo.creators.append("Person: N/A ()")
+
+        package_ref = oe.spdx.SPDXExternalDocumentRef()
+        package_ref.externalDocumentId = "DocumentRef-package"
+        package_ref.spdxDocument = package_doc.documentNamespace
+        package_ref.checksum.algorithm = "SHA1"
+        package_ref.checksum.checksumValue = package_doc_sha1
+
+        runtime_doc.externalDocumentRefs.append(package_ref)
+
+        runtime_doc.add_relationship(
+            runtime_doc.SPDXID,
+            "AMENDS",
+            "%s:%s" % (package_ref.externalDocumentId, package_doc.SPDXID)
+        )
+
+        deps = bb.utils.explode_dep_versions2(localdata.getVar("RDEPENDS") or 
"")
+        seen_deps = set()
+        for dep, _ in deps.items():
+            if dep in seen_deps:
+                continue
+
+            dep = providers[dep]
+
+            if not oe.packagedata.packaged(dep, localdata):
+                continue
+
+            dep_pkg_data = oe.packagedata.read_subpkgdata_dict(dep, d)
+            dep_pkg = dep_pkg_data["PKG"]
+
+            if dep in dep_package_cache:
+                (dep_spdx_package, dep_package_ref) = dep_package_cache[dep]
+            else:
+                dep_path = deploy_dir_spdx / "packages" / ("%s.spdx.json" % 
dep_pkg)
+
+                spdx_dep_doc, spdx_dep_sha1 = oe.sbom.read_doc(dep_path)
+
+                for pkg in spdx_dep_doc.packages:
+                    if pkg.name == dep_pkg:
+                        dep_spdx_package = pkg
+                        break
+                else:
+                    bb.fatal("Package '%s' not found in %s" % (dep_pkg, 
dep_path))
+
+                dep_package_ref = oe.spdx.SPDXExternalDocumentRef()
+                dep_package_ref.externalDocumentId = 
"DocumentRef-runtime-dependency-" + spdx_dep_doc.name
+                dep_package_ref.spdxDocument = spdx_dep_doc.documentNamespace
+                dep_package_ref.checksum.algorithm = "SHA1"
+                dep_package_ref.checksum.checksumValue = spdx_dep_sha1
+
+                dep_package_cache[dep] = (dep_spdx_package, dep_package_ref)
+
+            runtime_doc.externalDocumentRefs.append(dep_package_ref)
+
+            runtime_doc.add_relationship(
+                "%s:%s" % (dep_package_ref.externalDocumentId, 
dep_spdx_package.SPDXID),
+                "RUNTIME_DEPENDENCY_OF",
+                "%s:%s" % (package_ref.externalDocumentId, spdx_package.SPDXID)
+            )
+            seen_deps.add(dep)
+
+        oe.sbom.write_doc(d, runtime_doc, "runtime", spdx_deploy)
+}
+
+addtask do_create_runtime_spdx after do_create_spdx before do_build do_rm_work
+SSTATETASKS += "do_create_runtime_spdx"
+do_create_runtime_spdx[sstate-inputdirs] = "${SPDXRUNTIMEDEPLOY}"
+do_create_runtime_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}"
+
+python do_create_runtime_spdx_setscene () {
+    sstate_setscene(d)
+}
+addtask do_create_runtime_spdx_setscene
+
+do_create_runtime_spdx[dirs] = "${SPDXRUNTIMEDEPLOY}"
+do_create_runtime_spdx[cleandirs] = "${SPDXRUNTIMEDEPLOY}"
+do_create_runtime_spdx[rdeptask] = "do_create_spdx"
+
 def spdx_get_src(d):
     """
     save patched source of the recipe in SPDX_WORKDIR.
@@ -537,7 +696,7 @@ def spdx_get_src(d):
     finally:
         d.setVar("WORKDIR", workdir)
 
-do_rootfs[recrdeptask] += "do_create_spdx"
+do_rootfs[recrdeptask] += "do_create_spdx do_create_runtime_spdx"
 
 ROOTFS_POSTUNINSTALL_COMMAND =+ "image_combine_spdx ; "
 python image_combine_spdx() {
@@ -598,6 +757,24 @@ python image_combine_spdx() {
         else:
             bb.fatal("Unable to find package with name '%s' in SPDX file %s" % 
(name, pkg_spdx_path))
 
+        runtime_spdx_path = deploy_dir_spdx / "runtime" / ("runtime-" + name + 
".spdx.json")
+        runtime_doc, runtime_doc_sha1 = oe.sbom.read_doc(runtime_spdx_path)
+
+        runtime_ref = oe.spdx.SPDXExternalDocumentRef()
+        runtime_ref.externalDocumentId = "DocumentRef-%s" % runtime_doc.name
+        runtime_ref.spdxDocument = runtime_doc.documentNamespace
+        runtime_ref.checksum.algorithm = "SHA1"
+        runtime_ref.checksum.checksumValue = runtime_doc_sha1
+
+        # "OTHER" isn't ideal here, but I can't find a relationship that makes 
sense
+        doc.externalDocumentRefs.append(runtime_ref)
+        doc.add_relationship(
+            image,
+            "OTHER",
+            "%s:%s" % (runtime_ref.externalDocumentId, runtime_doc.SPDXID),
+            comment="Runtime dependencies for %s" % name
+        )
+
     image_spdx_path = imgdeploydir / (image_name + ".spdx.json")
 
     with image_spdx_path.open("wb") as f:
diff --git a/meta/lib/oe/sbom.py b/meta/lib/oe/sbom.py
index 294feee10b..848812c0b7 100644
--- a/meta/lib/oe/sbom.py
+++ b/meta/lib/oe/sbom.py
@@ -28,10 +28,11 @@ def get_image_spdxid(img):
     return "SPDXRef-Image-%s" % img
 
 
-def write_doc(d, spdx_doc, subdir):
+def write_doc(d, spdx_doc, subdir, spdx_deploy=None):
     from pathlib import Path
 
-    spdx_deploy = Path(d.getVar("SPDXDEPLOY"))
+    if spdx_deploy is None:
+        spdx_deploy = Path(d.getVar("SPDXDEPLOY"))
 
     dest = spdx_deploy / subdir / (spdx_doc.name + ".spdx.json")
     dest.parent.mkdir(exist_ok=True, parents=True)
-- 
2.32.0

-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#155567): 
https://lists.openembedded.org/g/openembedded-core/message/155567
Mute This Topic: https://lists.openembedded.org/mt/85302496/21656
Group Owner: openembedded-core+ow...@lists.openembedded.org
Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub 
[arch...@mail-archive.com]
-=-=-=-=-=-=-=-=-=-=-=-

Reply via email to