From patchwork Tue Jun 9 22:15:52 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 89607 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5E881CD8CAD for ; Tue, 9 Jun 2026 22:23:39 +0000 (UTC) Received: from mail-oa1-f46.google.com (mail-oa1-f46.google.com [209.85.160.46]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.7671.1781043817149766057 for ; Tue, 09 Jun 2026 15:23:37 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=SrZiXICa; spf=pass (domain: gmail.com, ip: 209.85.160.46, mailfrom: jpewhacker@gmail.com) Received: by mail-oa1-f46.google.com with SMTP id 586e51a60fabf-43bfe209e45so3853911fac.0 for ; Tue, 09 Jun 2026 15:23:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1781043816; x=1781648616; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Fv5OKQop7Wo7gff8+1BGhZIiH0L4lHFQ+T/i52i5NQw=; b=SrZiXICaIIuqdTG25Sg7g5kuomZvloF6PZma7oWAWlzLOyuB5gvBcTGIacxC2m0+xV bIhMr7fl0yPa8Zs0ROsreCNKNA5auiSECSR8MeEo4RNiOI8mEVEM4u5MPa4z8BuYgVwV XY9dg8+SpCOpBoJGb6VMPLYvcsGngQjPzOod/QqphSL/50olxjPp8whkCq33zMAmm7cc gPxgMc22P75DNvBZFxxu00QGYThLvALDH5Plm8ibmcSod64PFmrAUP6QWzu1+Z4oPCly 79wpt4xtv+UivsgfzMqGo0YRKOlvElrtHqaLbk1R3+v3oXvjONHBFlq/Dyo+TN+d+CXz 6N7w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781043816; x=1781648616; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=Fv5OKQop7Wo7gff8+1BGhZIiH0L4lHFQ+T/i52i5NQw=; b=UZMfJNV2lcr0S7YMmMgKmeaIH1chlEGF59zBxewJ5cQ4xzr5WNb5PmGgu3SKrZWFDw 774UtAmWV3y0gnDNbpOgE6j1lCAS+s3+DCnky+5+XOzsbP/SHYsssDvCqjxb/Dn9yFDc RCpMBFLhYIYpUiqWWDiM/PkXoZcBXDfJ0aQaPRvx4zW/KDnwuN8pL9o0HfbvI3Gt57/+ aQkhJQ1FsKNixdOEL9hiK2eouUyZXVIVTt7WDNyeGHsRglAZAYXD94TuAYMl78+BvkN3 7nApyD9Il1gd2yk7MM4Am/RtVXvTpyWMEO6ADn3uPVj8EJ58CjxNy1kEF1A+SDqIfNcN nmXg== X-Gm-Message-State: AOJu0YwNc5dZf83M46ZG6I+axIBFmdEItvg0z4NiP9glC2PVwqBNcH9d FR0X7XQbP5dlte7MNG8K/zPZtOUY5m8DlXtvVOaJINDw9ytPpKEn3/LnJgYwKA== X-Gm-Gg: Acq92OGesDlr5tK6UnPgSdJYVgCWLKQ0Jble7S9efu4w7wmp0vrDb2tkF226zMyrEEo yyqt/tlSAOJ+eKb+boq7OXtIdp0B/dCqCEy3wH7D8Oz0LJsaSeD1+I+zroA42ybSZZ9XeOfQ9bN 1mZCvsW89YwTb6ZNj7P6zUIEoemaZaUAX7UR2FMdU5NMS0QN0uemoM6PfIF4AgJM+I+5XIeJ3zY mwI2OOs0qnusHnXNouniC5DfLOp0gi3QoKI/v2+10fwrBhi3+xKlSkRv7fEmBm6Ep8cdnbgAiI3 vEOaEr4TcAwpyUusVqtfIA6mlJhBOHBCkDc/68sVjcwABSuOGMoiTF4ZlaAM8zUDVD1vXPmEM+P dvrKBy9bf1cVk/JL4bvOuBj6s4ARyXFtWIAnQPoRP/qGnWEcXTnlmVuPOhF1ACRJdDIW5ff8i1i aqovhIzg6zWHwnV8ViRh26EAKBXf+9Gg== X-Received: by 2002:a05:6870:390a:b0:43d:2f82:beaf with SMTP id 586e51a60fabf-4413da90f3dmr12227441fac.13.1781043816286; Tue, 09 Jun 2026 15:23:36 -0700 (PDT) Received: from localhost.localdomain ([2601:283:4b02:22d0::ce1]) by smtp.gmail.com with ESMTPSA id 586e51a60fabf-440d7d4f449sm19122610fac.8.2026.06.09.15.23.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jun 2026 15:23:35 -0700 (PDT) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: Joshua Watt Subject: [OE-core][PATCH 1/5] classes/baremetal-image: Remove "do_" prefix from image manifest Date: Tue, 9 Jun 2026 16:15:52 -0600 Message-ID: <20260609222331.1293007-2-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260609222331.1293007-1-JPEWhacker@gmail.com> References: <20260609222331.1293007-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 09 Jun 2026 22:23:39 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/238304 Removes the "do_" prefix from the image manifest to match the naming convention of image.bbclass Signed-off-by: Joshua Watt --- meta/classes-recipe/baremetal-image.bbclass | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meta/classes-recipe/baremetal-image.bbclass b/meta/classes-recipe/baremetal-image.bbclass index 4afc171314..7a6e71853b 100644 --- a/meta/classes-recipe/baremetal-image.bbclass +++ b/meta/classes-recipe/baremetal-image.bbclass @@ -45,7 +45,7 @@ python do_image_complete(){ import json data = { - "taskname": "do_image", + "taskname": "image", "imagetype": "baremetal-image", "images": [] } From patchwork Tue Jun 9 22:15:53 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 89608 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6D871CD98C6 for ; Tue, 9 Jun 2026 22:23:39 +0000 (UTC) Received: from mail-oa1-f44.google.com (mail-oa1-f44.google.com [209.85.160.44]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.7672.1781043818587382404 for ; Tue, 09 Jun 2026 15:23:38 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=X5OtAhYW; spf=pass (domain: gmail.com, ip: 209.85.160.44, mailfrom: jpewhacker@gmail.com) Received: by mail-oa1-f44.google.com with SMTP id 586e51a60fabf-43d1470491aso2015693fac.2 for ; Tue, 09 Jun 2026 15:23:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1781043818; x=1781648618; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=zeYF99Bpux33RKKOg7Mc272AtA7FgoMxbog0f8WPF+o=; b=X5OtAhYWaY1lurRpDs9viXeNY4v/x7n147hJe9J4kLWImTBvPsa8v2YglJtzbmsvu5 XKBGI3XwvaGbZXW+lFirzmVwx/LM4Q1mh/dEXH2EPHf2U8e1tDaLPSK7o62hxr0LJlyD +s4ZaxfzrHZXX7RMjT2hxIkGUvi4oUx8xOLVU4Xa19mUq8GevbjAH36YjG7jmLHH3z3b WwIp1hY2ifqIxAzReqrVZdnfQtzY1UIEH5dhcKQxDZR/ib9zbx4AYLbkLHWuhoalDde0 LxYQPqo6QvpTSL7hliiA+lj8KvANmpIDe99OCyhn2/S7DZlKTPZ0WV+OcIBh54qIDTYv dYQg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781043818; x=1781648618; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=zeYF99Bpux33RKKOg7Mc272AtA7FgoMxbog0f8WPF+o=; b=rKgJO5K5MrL3WsRQW+j/rDPfb6718gTAzp5h87joyNuD5Z2qWkJPBLLk45XMODf+Nd /EtetDfUGoYydWK5kYjePZ2k1lGTBCHUcKuMTeuuZYnqwEaiRacuxWz3qgAYh0am3v/R pTnVIRefk1HaddhJ/k9D8fU7SwgCAf9MrXZ/briiYqAJORoLl3MTl47BBTOUmlFJ+eKP GVCdvn2AF+nLkF032JIGto2D/OcuZjwaGLtTzSqkD+Eh75S32U4WdJslaQoSKSJm5a/6 +y9GciagjFmzsgLxDNJoLbEPKtnZbrrUHOiwINE+416HOkk9Axh+qq5Oo6wOQ2mhKxL4 fZjA== X-Gm-Message-State: AOJu0Yy/IpQRfRX/rhjG1vou5m2z6imH0ldoSUTX5SCbOO11N1ug1+4l Kit8pzbxY4pR8UD4fzS5THet5UMYgRAm/3TOuwA23XCu7EY8Tfbm39t3uBjpBQ== X-Gm-Gg: Acq92OHvLm7CMjN60trwoj5ZxnkHrjQr7Pzau9FLUSlMKsiO/yXAlB2hK6MTORJ5gUT 5SG42n5dq61NWHl3p0LVpiyhA5QmV3+ngvQE5yGEKGOPMjU4rfOrlyHYUOpYbkTidx00yOGDreG XbRMwDIO23YG4QpTTX8PNmg2nezuziUkfZcxUKzmKSRvwQ73kzTSGy2JHZU4FDROVz00ZX6ZBRQ jYNdAPQGwF8hchLRhZ6CjwGrMgWcEutApVhOxkN7ZlDfq+NhJuY75940JgxikHzQ67GWjgrCv0p KJw302K0ECsr+cxdvq0Ti1X2A2vTc+HqZg1woGcSIGThxk2MVx9PuIhtPH7Ldy2HWLzJjimjqYA odjays2URywQ00tWADqhZpiksIxBN/cQ3a/a6F5P2TcO2ilKGuGJWClhH/3ljof1KbGVH76g09q m6UiwsUPRfIPG7SL+B5fccLKbgQ8sPvw== X-Received: by 2002:a05:6871:6d11:b0:43d:1689:ed9c with SMTP id 586e51a60fabf-4413d6b1a1dmr12546848fac.18.1781043817501; Tue, 09 Jun 2026 15:23:37 -0700 (PDT) Received: from localhost.localdomain ([2601:283:4b02:22d0::ce1]) by smtp.gmail.com with ESMTPSA id 586e51a60fabf-440d7d4f449sm19122610fac.8.2026.06.09.15.23.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jun 2026 15:23:36 -0700 (PDT) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: Joshua Watt Subject: [OE-core][PATCH 2/5] spdx: Reformat Date: Tue, 9 Jun 2026 16:15:53 -0600 Message-ID: <20260609222331.1293007-3-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260609222331.1293007-1-JPEWhacker@gmail.com> References: <20260609222331.1293007-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 09 Jun 2026 22:23:39 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/238305 Reformats SPDX files with black Signed-off-by: Joshua Watt --- meta/lib/oe/sbom30.py | 6 +-- meta/lib/oe/spdx30_tasks.py | 76 ++++++++++++++++++++----------------- 2 files changed, 44 insertions(+), 38 deletions(-) diff --git a/meta/lib/oe/sbom30.py b/meta/lib/oe/sbom30.py index b379ff947c..0926266295 100644 --- a/meta/lib/oe/sbom30.py +++ b/meta/lib/oe/sbom30.py @@ -712,7 +712,7 @@ class ObjectSet(oe.spdx30.SHACLObjectSet): return self.add(v) def new_vex_patched_relationship(self, from_, to, notes: None): - props = {'security_statusNotes': notes} if notes else {} + props = {"security_statusNotes": notes} if notes else {} return self._new_relationship( oe.spdx30.security_VexFixedVulnAssessmentRelationship, from_, @@ -724,7 +724,7 @@ class ObjectSet(oe.spdx30.SHACLObjectSet): ) def new_vex_unpatched_relationship(self, from_, to, notes: None): - props = {'security_statusNotes': notes} if notes else {} + props = {"security_statusNotes": notes} if notes else {} return self._new_relationship( oe.spdx30.security_VexAffectedVulnAssessmentRelationship, from_, @@ -737,7 +737,7 @@ class ObjectSet(oe.spdx30.SHACLObjectSet): ) def new_vex_ignored_relationship(self, from_, to, *, impact_statement, notes: None): - props = {'security_statusNotes': notes} if notes else {} + props = {"security_statusNotes": notes} if notes else {} return self._new_relationship( oe.spdx30.security_VexNotAffectedVulnAssessmentRelationship, from_, diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py index 7cc46d579b..72d17aade6 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py @@ -382,7 +382,6 @@ def collect_dep_sources(dep_objsets, dest): index_sources_by_hash(e.to, dest) - def _generate_git_purl(d, download_location, srcrev): """Generate a Package URL for a Git source from its download location. @@ -392,27 +391,29 @@ def _generate_git_purl(d, download_location, srcrev): Returns the PURL string or None if no mapping matches. """ - if not download_location or not download_location.startswith('git+'): + if not download_location or not download_location.startswith("git+"): return None git_url = download_location[4:] # Remove 'git+' prefix # Default handler: github.com git_purl_handlers = { - 'github.com': 'pkg:github', + "github.com": "pkg:github", } # Custom PURL mappings from SPDX_GIT_PURL_MAPPINGS # Format: "domain1:purl_type1 domain2:purl_type2" - custom_mappings = d.getVar('SPDX_GIT_PURL_MAPPINGS') + custom_mappings = d.getVar("SPDX_GIT_PURL_MAPPINGS") if custom_mappings: for mapping in custom_mappings.split(): - parts = mapping.split(':', 1) + parts = mapping.split(":", 1) if len(parts) == 2: git_purl_handlers[parts[0]] = parts[1] bb.debug(2, f"Added custom Git PURL mapping: {parts[0]} -> {parts[1]}") else: - bb.warn(f"Invalid SPDX_GIT_PURL_MAPPINGS entry: {mapping} (expected format: domain:purl_type)") + bb.warn( + f"Invalid SPDX_GIT_PURL_MAPPINGS entry: {mapping} (expected format: domain:purl_type)" + ) try: parsed = urllib.parse.urlparse(git_url) @@ -425,11 +426,11 @@ def _generate_git_purl(d, download_location, srcrev): for domain, purl_type in git_purl_handlers.items(): if hostname == domain: - path = parsed.path.strip('/') - path_parts = path.split('/') + path = parsed.path.strip("/") + path_parts = path.split("/") if len(path_parts) >= 2: owner = path_parts[0] - repo = path_parts[1].replace('.git', '') + repo = path_parts[1].replace(".git", "") return f"{purl_type}/{owner}/{repo}@{srcrev}" break @@ -448,12 +449,12 @@ def _enrich_source_package(d, dl, fd, file_name, primary_purpose): if fd.type == "git": # Use full SHA-1 from fd.revision - srcrev = getattr(fd, 'revision', None) - if srcrev and srcrev not in {'${AUTOREV}', 'AUTOINC', 'INVALID'}: + srcrev = getattr(fd, "revision", None) + if srcrev and srcrev not in {"${AUTOREV}", "AUTOINC", "INVALID"}: version = srcrev # Generate PURL for Git hosting services - download_location = getattr(dl, 'software_downloadLocation', None) + download_location = getattr(dl, "software_downloadLocation", None) if version and download_location: purl = _generate_git_purl(d, download_location, version) @@ -464,12 +465,12 @@ def _enrich_source_package(d, dl, fd, file_name, primary_purpose): dl.software_packageUrl = purl # Add VCS external reference for Git repositories - download_location = getattr(dl, 'software_downloadLocation', None) + download_location = getattr(dl, "software_downloadLocation", None) if download_location and isinstance(download_location, str): - if download_location.startswith('git+'): + if download_location.startswith("git+"): git_url = download_location[4:] - if '@' in git_url: - git_url = git_url.split('@')[0] + if "@" in git_url: + git_url = git_url.split("@")[0] dl.externalRef = dl.externalRef or [] dl.externalRef.append( @@ -480,7 +481,6 @@ def _enrich_source_package(d, dl, fd, file_name, primary_purpose): ) - def add_download_files(d, objset): inputs = set() @@ -726,8 +726,9 @@ def create_recipe_spdx(d): if status == "Patched": spdx_vex = recipe_objset.new_vex_patched_relationship( - [spdx_cve_id], [recipe], - notes=": ".join(v for v in (detail, description) if v) + [spdx_cve_id], + [recipe], + notes=": ".join(v for v in (detail, description) if v), ) patches = [] for idx, filepath in enumerate(resources): @@ -753,8 +754,9 @@ def create_recipe_spdx(d): elif status == "Unpatched": recipe_objset.new_vex_unpatched_relationship( - [spdx_cve_id], [recipe], - notes=": ".join(v for v in (detail, description) if v) + [spdx_cve_id], + [recipe], + notes=": ".join(v for v in (detail, description) if v), ) elif status == "Ignored": spdx_vex = recipe_objset.new_vex_ignored_relationship( @@ -1060,7 +1062,11 @@ def create_spdx(d): if include_sources: debug_sources |= get_package_sources_from_debug( - d, package, package_files, dep_sources, source_hash_cache, + d, + package, + package_files, + dep_sources, + source_hash_cache, excluded_files=excluded_files, ) @@ -1185,7 +1191,7 @@ def create_package_spdx(d): if dep not in providers: continue - (dep, _) = providers[dep] + dep, _ = providers[dep] if not oe.packagedata.packaged(dep, localdata): continue @@ -1455,17 +1461,17 @@ def create_image_spdx(d): image_path = image_deploy_dir / image_filename if os.path.isdir(image_path): a, _ = add_package_files( - d, - objset, - image_path, - lambda file_counter: objset.new_spdxid( - "imagefile", str(file_counter) - ), - lambda filepath: [], - license_data=None, - ignore_dirs=[], - ignore_top_level_dirs=[], - archive=None, + d, + objset, + image_path, + lambda file_counter: objset.new_spdxid( + "imagefile", str(file_counter) + ), + lambda filepath: [], + license_data=None, + ignore_dirs=[], + ignore_top_level_dirs=[], + archive=None, ) artifacts.extend(a) else: @@ -1482,7 +1488,7 @@ def create_image_spdx(d): oe.spdx30.Hash( algorithm=oe.spdx30.HashAlgorithm.sha512, hashValue=bb.utils.sha512_file(image_path), - ) + ), ], ) ) From patchwork Tue Jun 9 22:15:54 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 89611 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id A345ECD8CAD for ; Tue, 9 Jun 2026 22:23:49 +0000 (UTC) Received: from mail-ot1-f41.google.com (mail-ot1-f41.google.com [209.85.210.41]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.7675.1781043822526094417 for ; Tue, 09 Jun 2026 15:23:42 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=NpaKFRHh; spf=pass (domain: gmail.com, ip: 209.85.210.41, mailfrom: jpewhacker@gmail.com) Received: by mail-ot1-f41.google.com with SMTP id 46e09a7af769-7e6b5dfde3cso3181233a34.3 for ; Tue, 09 Jun 2026 15:23:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1781043822; x=1781648622; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=wrYcfiwhDMBjl1gbu8GGtUbIG/r9/yQiFylLpvQbJjE=; b=NpaKFRHh08l4X3zM+h8H1a1ue0wAkg25Wsbzq95qCdN3t+7AR/9xba/b7qUcH0PReq zbNHCKa8HIMzatbK1zyG52cArB1M00AE27xWxBEhNBaQLHzrAjSCCG1CbHrWlaY79qnE NUoQY5213ya83WmfQNlQwcPskKHUSybzxgSM7Ej57aOFNSXT9EcH2AhcixinTwF6TWtM GBXzZo782giDknNddeRsOR+4WNNLtCANu8Hf4TMgAQcE/ZzTRNSBlKRtFuH04YJ8SgK0 Gc15o8yMoqyXixynyuVakJiZ7JFGA+G6kizSmOXBgcOq6+91KTqesq9bvJOYzrccz2ST iOGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781043822; x=1781648622; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=wrYcfiwhDMBjl1gbu8GGtUbIG/r9/yQiFylLpvQbJjE=; b=WNWbgbhmmcADJdcWmJz3R+vx9ewjDJfD/ldOsc6OvOlHCCC1jsgrowWgnpNuaTMgEf AiDx3K9aYp6mmrP1cdoAmIPKwsOqx3ESrXAx5gib942tp5ib5pkRJmKmLc52z0qRRuYZ KakmprOkRjwZXVsto3p4AuVy1JYqJAWRlxrxHsMMz8RY5OWXEjWJ7/oRTERvwbHjTJ9o MtBT1dTMmW8TMV122UZDmfe/GgBhfmzueapfYFmh35HGWTzK9RhqoMmjGHV535Ce7SHS INILeQsGHpB6ejWi2b9GSh1H3pI4Zte8QT+Cm1J4olTThvo4Fek3S662W76m3A+RSD1r PTpQ== X-Gm-Message-State: AOJu0YwJDvzflrgMpEJhy5gca4Gilh4kk+oRizKEKwtJS2hIIkJFeC+q B2LCTSMk/09sn0JUA7X5bUCQQYAXeQPxo9DxBBG7UvhPus3Fq3Jby62EauHJMA== X-Gm-Gg: Acq92OEpjsJO8VMaEQMvFGiVyPslo17iq6UsTI2o1OHqO97NLSL8xDADgWBqLksgPdA bZAaODyubzkQDr93+mp6+7CoYS3TBSGfKSv301Vgwri5RNv8hie6zLDB7nX6kxgEuX4Xqm1VY1b xMmF/gdjO7P6u4UI2BMzGg1MmtCPMqaKZTW4TC4B+0tzVKp9GVqL2SBSWKo5ijSZgg4Ig/FqyJv vgGCPAHnqhKFhoDtxD38DOTrNN+ASOM28wwGmvCj1qJIp5tUo2WCMlZRzKmbUix8KY4+fwb74KT QxDPO6yVUmRg09v/+i6IXAtrTOjR0ySj2ZkFnk/zMOXrI3vJOm6hE2/xMzsr/dIrEmo4jO/TYFM quwbJSDbL+kd7ROA3OJ3rvBnP98V6fOtm31QG9/303XgIBJ6XqjFwE0KrrPssQlsZXElrU/2Ayl 4IDOkpAc42GyLMaIPdMJxVUaST9VebQA== X-Received: by 2002:a05:6820:8188:b0:69e:14a:f303 with SMTP id 006d021491bc7-69e68c645d4mr13068728eaf.41.1781043821324; Tue, 09 Jun 2026 15:23:41 -0700 (PDT) Received: from localhost.localdomain ([2601:283:4b02:22d0::ce1]) by smtp.gmail.com with ESMTPSA id 586e51a60fabf-440d7d4f449sm19122610fac.8.2026.06.09.15.23.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jun 2026 15:23:38 -0700 (PDT) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: Joshua Watt Subject: [OE-core][PATCH 3/5] spdx: Add ability for deploy tasks to create SPDX Date: Tue, 9 Jun 2026 16:15:54 -0600 Message-ID: <20260609222331.1293007-4-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260609222331.1293007-1-JPEWhacker@gmail.com> References: <20260609222331.1293007-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 09 Jun 2026 22:23:49 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/238306 Adds support for "deploy" tasks (like do_deploy) to write out SPDX documents that describe what has been deployed. Deploy tasks will automatically detect many dependencies on other recipes; specifically they will correctly detect dependencies on any do_create_spdx task, and also other deploy tasks that generate SPDX output. The only known notable exception are transitive (e.g. originating from other upstream tasks) dependencies on do_image_complete, and do_populate_sysroot. However, these are detected if a direct dependency of the deploy task (via translation of the task dependencies). This same dependency finding algorithm is now applied to the image generation SBoM; this means that if an image creation task depends on a task that generates a deploy SBoM, it will show up in the dependency graph of the image. A typical example is a wic file that consumes the kernel, u-boot, etc. will now correctly list those as a dependency, as long as their do_deploy step is added to SPDX_DEPLOY_TASKS. Signed-off-by: Joshua Watt --- .../create-spdx-image-3.0.bbclass | 4 +- meta/classes-recipe/deploy.bbclass | 1 + meta/classes-recipe/nospdx.bbclass | 1 + meta/classes/create-spdx-3.0.bbclass | 158 ++++++++++ meta/classes/spdx-common.bbclass | 1 + meta/lib/oe/sbom30.py | 46 +-- meta/lib/oe/spdx30_tasks.py | 282 +++++++++++++++--- meta/lib/oe/spdx_common.py | 2 +- 8 files changed, 430 insertions(+), 65 deletions(-) diff --git a/meta/classes-recipe/create-spdx-image-3.0.bbclass b/meta/classes-recipe/create-spdx-image-3.0.bbclass index 15a91e90e2..a96cfb25ed 100644 --- a/meta/classes-recipe/create-spdx-image-3.0.bbclass +++ b/meta/classes-recipe/create-spdx-image-3.0.bbclass @@ -30,7 +30,7 @@ python do_create_rootfs_spdx() { import oe.spdx30_tasks oe.spdx30_tasks.create_rootfs_spdx(d) } -addtask do_create_rootfs_spdx after do_rootfs before do_image +addtask do_create_rootfs_spdx after do_rootfs do_create_recipe_spdx before do_image SSTATETASKS += "do_create_rootfs_spdx" do_create_rootfs_spdx[sstate-inputdirs] = "${SPDXROOTFSDEPLOY}" do_create_rootfs_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}" @@ -47,7 +47,7 @@ python do_create_image_spdx() { import oe.spdx30_tasks oe.spdx30_tasks.create_image_spdx(d) } -addtask do_create_image_spdx after do_image_complete do_create_rootfs_spdx before do_build +addtask do_create_image_spdx after do_image_complete do_create_rootfs_spdx do_create_recipe_spdx before do_build SSTATETASKS += "do_create_image_spdx" SSTATE_SKIP_CREATION:task-create-image-spdx = "1" do_create_image_spdx[sstate-inputdirs] = "${SPDXIMAGEWORK}" diff --git a/meta/classes-recipe/deploy.bbclass b/meta/classes-recipe/deploy.bbclass index f56fe98d6d..f222a8560f 100644 --- a/meta/classes-recipe/deploy.bbclass +++ b/meta/classes-recipe/deploy.bbclass @@ -6,6 +6,7 @@ DEPLOYDIR = "${WORKDIR}/deploy-${PN}" SSTATETASKS += "do_deploy" +SPDX_DEPLOY_ARTIFACTS_DIR:task-deploy = "${DEPLOYDIR}" do_deploy[sstate-inputdirs] = "${DEPLOYDIR}" do_deploy[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}" diff --git a/meta/classes-recipe/nospdx.bbclass b/meta/classes-recipe/nospdx.bbclass index 7c99fcd1ec..b405f57d11 100644 --- a/meta/classes-recipe/nospdx.bbclass +++ b/meta/classes-recipe/nospdx.bbclass @@ -11,3 +11,4 @@ deltask do_create_package_spdx deltask do_create_rootfs_spdx deltask do_create_image_spdx deltask do_create_image_sbom +deltask do_create_deploy_sbom diff --git a/meta/classes/create-spdx-3.0.bbclass b/meta/classes/create-spdx-3.0.bbclass index 56fd01fd53..19d5a45eba 100644 --- a/meta/classes/create-spdx-3.0.bbclass +++ b/meta/classes/create-spdx-3.0.bbclass @@ -163,6 +163,34 @@ SPDX_GIT_PURL_MAPPINGS[doc] = "A space separated list of domain:purl_type \ on gitlab.example.com to the pkg:gitlab PURL type. \ github.com is always mapped to pkg:github by default." +SPDX_DEPLOY_TASKS ?= "" +SPDX_DEPLOY_TASKS[doc] = "A space separated list of sstate tasks that produce \ + deployed output (usually written to DEPLOY_DIR_IMAGE). Tasks in this list \ + will produce SPDX documents that describe the deployed output. If a task \ + is in this list, see the SPDX_DEPLOY_ARITFACTS and SPDX_DEPLOY_ARTIFACTS_DIR \ + for how to configure its SPDX output.\ + \ + Dependencies of deploy tasks that produce SPDX data will be automatically \ + linked in as a build time dependency of the deploy task's SBoM. (for \ + example, if one do_deploy depends on another recipes do_deploy, this will \ + be reflected in the SPDX data). If the deploy task should depend on the \ + primary build process of the recipe, the task should be declared \ + 'after do_create_spdx'.\ + " + +SPDX_DEPLOY_ARTIFACTS = "AUTO" +SPDX_DEPLOY_ARITFACTS[doc] = "A space separated list of deployed artifacts, \ + relative to SPDX_DEPLOY_ARTIFACTS_DIR that should be included in the SBoM. \ + If 'AUTO' (the default), all files in SPDX_DEPLOY_ARITFACTS_DIR will be \ + added. A :task- override *must* be used to set this value so that it is \ + scoped to a specific task" + +SPDX_DEPLOY_ARTIFACTS_DIR = "" +SPDX_DEPLOY_ARTIFACTS_DIR[doc] = "The directory recipe specific directory \ + where artifacts are deployed for staging to sstate (e.g. for do_deploy, \ + this is DEPLOY_DIR). A :task- override *must* be used to set this value so \ + that it is scoped to a specific task." + IMAGE_CLASSES:append = " create-spdx-image-3.0" SDK_CLASSES += "create-spdx-sdk-3.0" @@ -291,3 +319,133 @@ python spdx30_build_started_handler () { addhandler spdx30_build_started_handler spdx30_build_started_handler[eventmask] = "bb.event.BuildStarted" +python create_deploy_spdx() { + import oe.spdx30_tasks + from pathlib import Path + current_task = "do_" + d.getVar("BB_CURRENTTASK") + + spdxdeploydir = Path(d.getVar("SPDXDIR") + "/deploy-" + current_task) + + artifactsdir = d.getVar("SPDX_DEPLOY_ARTIFACTS_DIR") + if not artifactsdir: + bb.fatal(f"{pn}: spdx-artifactsdir must be set for task {current_task}") + return + + artifacts = d.getVar("SPDX_DEPLOY_ARTIFACTS") + + oe.spdx30_tasks.create_deploy_spdx(d, spdxdeploydir, artifactsdir, artifacts) +} +oe.spdx30_tasks.find_build_dep_objsets[vardepsexclude] += "BB_TASKDEPDATA" + +python () { + # Most recipes generate SPDX output in a distinct task from the task that + # actually is the relevant dependency. As such, we need to map the task + # that we care about to the task that generates the corresponding SPDX + # output so that we can rely on the SPDX output being present when the time + # comes to use it downstream. + # + # The down side of this is that only the first level of dependencies (e.g + # tasks listed in SPDX_DEPLOY_TASKS) will have the mapping done and thus + # find the dependencies. Transitive dependencies will not be mapped and + # thus the SPDX data will not be linked in. + # + # Ideally, this will be able to go away once more tasks directly generate + # SPDX files for their output instead of combining it into monolithic + # functions; tasks listed in this map are the best candidates to have this + # done first. + TASK_MAP = { + # If a task requires the RSS be extended, depend on the SPDX build task + # for the recipe, at least until it's possible for do_populate_sysroot + # to describe it's own output. + "do_populate_sysroot": "do_create_spdx", + # If an image is needed, also depend on the task to create the SBoM for + # the image + "do_image_complete": "do_create_image_spdx", + } + + def map_task_deps(task, flag): + task_flags= (d.getVarFlag(task, flag) or "").split() + for t in task_flags: + if t in TASK_MAP and TASK_MAP[t] not in task_flags: + d.appendVarFlag(task, flag, f" {TASK_MAP[t]}") + + def before_postfunc(f): + return f == "sstate_task_postfunc" or "buildhistory" in f + + if bb.data.inherits_class("nospdx", d): + return + + sstate_tasks = set((d.getVar("SSTATETASKS") or "").split()) + spdx_tasks = (d.getVar("SPDX_DEPLOY_TASKS") or "").split() + deploy_sbom_tasks = [] + for task in spdx_tasks: + if ":" in task: + task, func = task.split(":") + else: + func = "create_deploy_spdx" + + deploy_sbom_tasks.append(task) + + if task not in sstate_tasks: + bb.fatal(f"{task} is not an sstate task") + + spdx_deploy = "${SPDXDIR}/deploy-" + task + + # Ensure function is sorted properly. It should be right before + # sstate_task_postfunc + postfuncs = (d.getVarFlag(task, "postfuncs") or "").split() + d.setVarFlag(task, "postfuncs", " ".join( + [f for f in postfuncs if not before_postfunc(f)] + + [func] + + [f for f in postfuncs if before_postfunc(f)] + )) + d.prependVarFlag(task, "sstate-inputdirs", f"{spdx_deploy} ") + d.prependVarFlag(task, "sstate-outputdirs", "${DEPLOY_DIR_SPDX} ") + d.prependVarFlag(task, "file-checksums", "${SPDX3_DEP_FILES} ") + d.prependVarFlag(task, "dirs", f"{spdx_deploy} ") + d.prependVarFlag(task, "cleandirs", f"{spdx_deploy} ") + + deps = (d.getVarFlag(task, "depends") or "").split() + extra_deps = ["${PN}:do_create_recipe_spdx"] + for dep in deps: + _, fn, taskname = bb.runqueue.split_tid(dep) + if taskname in TASK_MAP: + extra_deps.append(f"{fn}:{TASK_MAP[taskname]}") + + d.prependVarFlag(task, "depends", " ".join(extra_deps) + " ") + + map_task_deps(task, "deptask") + map_task_deps(task, "rdeptask") + map_task_deps(task, "recrdeptask") + + # For now, if a recipe is directly built, deploy all of it's deploy tasks + # into a single SBoM. We may need an option in the future to have tasks + # that don't do this (e.g. because they do not deploy to a location that is + # intended to be consumed by the user) + if spdx_tasks: + bb.build.addtask("do_create_deploy_sbom", "do_build", " ".join(deploy_sbom_tasks), d) +} + +python do_create_deploy_sbom() { + import oe.spdx30_tasks + from pathlib import Path + deploydir = Path(d.getVar("SPDXDEPLOYSBOMDEPLOY")) + deploy_tasks = [] + for task in (d.getVar("SPDX_DEPLOY_TASKS") or "").split(): + if ":" in task: + task, _ = task.split(":") + deploy_tasks.append(task) + + oe.spdx30_tasks.create_deploy_sbom(d, deploydir, deploy_tasks) +} +do_create_deploy_sbom[sstate-inputdirs] = "${SPDXDEPLOYSBOMDEPLOY}" +do_create_deploy_sbom[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}" +do_create_deploy_sbom[recrdeptask] += "do_create_recipe_spdx do_create_spdx" +do_create_deploy_sbom[cleandirs] += "${SPDXDEPLOYSBOMDEPLOY}" +do_create_deploy_sbom[file-checksums] += "${SPDX3_DEP_FILES}" + +SSTATETASKS += "do_create_deploy_sbom" +python do_create_deploy_sbom_setscene() { + sstate_setscene(d) +} +addtask do_create_deploy_sbom_setscene diff --git a/meta/classes/spdx-common.bbclass b/meta/classes/spdx-common.bbclass index 40701730a6..bca169670d 100644 --- a/meta/classes/spdx-common.bbclass +++ b/meta/classes/spdx-common.bbclass @@ -26,6 +26,7 @@ SPDX_TOOL_VERSION ??= "1.0" SPDXRECIPEDEPLOY = "${SPDXDIR}/recipe-deploy" SPDXRUNTIMEDEPLOY = "${SPDXDIR}/runtime-deploy" SPDXRECIPESBOMDEPLOY = "${SPDXDIR}/recipes-bom-deploy" +SPDXDEPLOYSBOMDEPLOY = "${SPDXDIR}/deploy-bom-deploy" SPDX_INCLUDE_SOURCES ??= "0" SPDX_INCLUDE_SOURCES[doc] = "If set to '1', include source code files in the \ diff --git a/meta/lib/oe/sbom30.py b/meta/lib/oe/sbom30.py index 0926266295..16f42f41d6 100644 --- a/meta/lib/oe/sbom30.py +++ b/meta/lib/oe/sbom30.py @@ -1048,6 +1048,25 @@ def write_jsonld_doc(d, objset, dest): objset.objects.remove(objset.doc) +def make_jsonld_link(d, fn, subdir, name, deploydir): + pkg_arch = d.getVar("SSTATE_PKGARCH") + + link_name = jsonld_arch_path( + d, + pkg_arch, + subdir, + name, + deploydir=deploydir, + ) + try: + link_name.parent.mkdir(exist_ok=True, parents=True) + link_name.symlink_to(os.path.relpath(fn, link_name.parent)) + except: + target = link_name.readlink() + bb.warn(f"Unable to link {fn} as {link_name}. Already points to {target}") + raise + + def write_recipe_jsonld_doc( d, objset, @@ -1055,6 +1074,7 @@ def write_recipe_jsonld_doc( deploydir, *, create_spdx_id_links=True, + create_task_link=False, ): pkg_arch = d.getVar("SSTATE_PKGARCH") @@ -1062,23 +1082,7 @@ def write_recipe_jsonld_doc( def link_id(_id): hash_path = jsonld_hash_path(hash_id(_id)) - - link_name = jsonld_arch_path( - d, - pkg_arch, - *hash_path, - deploydir=deploydir, - ) - try: - link_name.parent.mkdir(exist_ok=True, parents=True) - link_name.symlink_to(os.path.relpath(dest, link_name.parent)) - except: - target = link_name.readlink() - bb.warn( - f"Unable to link {_id} in {dest} as {link_name}. Already points to {target}" - ) - raise - + make_jsonld_link(d, dest, *hash_path, deploydir) return hash_path[-1] objset.add_aliases() @@ -1094,6 +1098,14 @@ def write_recipe_jsonld_doc( # out, so always do that even if there is an error making the links write_jsonld_doc(d, objset, dest) + if create_task_link: + pn = d.getVar("PN") + current_task = "do_" + d.getVar("BB_CURRENTTASK") + + make_jsonld_link(d, dest, "by-task", f"{pn}:{current_task}", deploydir) + + return dest + def find_root_obj_in_jsonld(d, subdir, fn_name, obj_type, **attr_filter): objset, fn = find_jsonld(d, subdir, fn_name, required=True) diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py index 72d17aade6..3dae502e64 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py @@ -605,6 +605,135 @@ def get_is_native(d): return bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d) +def set_var_field(d, var, obj, name, package=None): + val = None + if package: + val = d.getVar("%s:%s" % (var, package)) + + if not val: + val = d.getVar(var) + + if val: + setattr(obj, name, val) + + +def find_build_dep_objsets(d, start_task): + def find_deps(d, taskdepdata, current_dep, start_dep, visited, depth=0): + key = f"{current_dep.pn}:{current_dep.taskname}" + + dep_objsets = [] + + if key not in visited: + visited.add(key) + + for n in current_dep.deps: + dep = taskdepdata[n] + dep_name = f"{dep.pn}:{dep.taskname}" + + dep_objset, dep_path = oe.sbom30.find_jsonld(d, "by-task", dep_name) + if dep_objset: + dep_objsets.append(dep_objset) + + elif dep.pn == start_dep.pn: + # If this task is still part of the same recipe, continue + # searching up the dependency tree until a valid dependency + # is found. This detects transitive dependencies that may + # have been pulled in by previous tasks in the same recipe. + dep_objsets.extend( + find_deps(d, taskdepdata, dep, start_dep, visited, depth + 1) + ) + + return dep_objsets + + pn = d.getVar("PN") + taskdepdata = d.getVar("BB_TASKDEPDATA", False) + for dep in taskdepdata.values(): + if dep.pn == pn and dep.taskname == start_task: + start_dep = dep + break + else: + bb.fatal(f"Unable to find {pn}:{start_task} in taskdepdata") + + return find_deps(d, taskdepdata, start_dep, start_dep, set()) + + +def create_deploy_package(d, objset, build, spdxid, name, start_task, files, **attrs): + recipe, _ = load_recipe_spdx(d) + + deploy_package = objset.add_root( + oe.spdx30.software_Package( + _id=spdxid, + creationInfo=objset.doc.creationInfo, + name=name, + software_packageVersion=d.getVar("PV"), + ) + ) + + objset.new_scoped_relationship( + [oe.sbom30.get_element_link_id(recipe)], + oe.spdx30.RelationshipType.generates, + oe.spdx30.LifecycleScopeType.build, + [deploy_package], + ) + + set_var_field(d, "HOMEPAGE", deploy_package, "software_homePage") + set_var_field(d, "SUMMARY", deploy_package, "summary") + set_var_field(d, "DESCRIPTION", deploy_package, "description") + + set_purls(deploy_package, (d.getVar("SPDX_PACKAGE_URLS") or "").split()) + + set_timestamp_now(d, deploy_package, "builtTime") + + supplier = objset.new_agent("SPDX_PACKAGE_SUPPLIER") + if supplier is not None: + deploy_package.suppliedBy = ( + supplier if isinstance(supplier, str) else supplier._id + ) + + if files: + objset.new_relationship( + [deploy_package], + oe.spdx30.RelationshipType.contains, + sorted(list(files)), + ) + + objset.new_scoped_relationship( + [build], + oe.spdx30.RelationshipType.hasOutput, + oe.spdx30.LifecycleScopeType.build, + sorted(list(files) + [deploy_package]), + ) + + # Collect dependencies + if start_task is not None: + dep_builds = set() + dep_packages = set() + for o in find_build_dep_objsets(d, start_task): + if obj := o.find_root(oe.spdx30.software_Package): + dep_packages.add(oe.sbom30.get_element_link_id(obj)) + + if obj := o.find_root(oe.spdx30.build_Build): + dep_builds.add(oe.sbom30.get_element_link_id(obj)) + + if dep_packages: + objset.new_scoped_relationship( + [deploy_package], + oe.spdx30.RelationshipType.dependsOn, + oe.spdx30.LifecycleScopeType.build, + sorted(list(dep_packages)), + ) + + if dep_builds: + objset.new_scoped_relationship( + [build], + oe.spdx30.RelationshipType.dependsOn, + oe.spdx30.LifecycleScopeType.build, + sorted(list(dep_builds)), + ) + + return deploy_package + + def create_recipe_spdx(d): deploydir = Path(d.getVar("SPDXRECIPEDEPLOY")) pn = d.getVar("PN") @@ -795,7 +924,9 @@ def create_recipe_spdx(d): sorted(list(all_cves)), ) - oe.sbom30.write_recipe_jsonld_doc(d, recipe_objset, "static", deploydir) + oe.sbom30.write_recipe_jsonld_doc( + d, recipe_objset, "static", deploydir, create_task_link=True + ) def load_recipe_spdx(d): @@ -809,17 +940,6 @@ def load_recipe_spdx(d): def create_spdx(d): - def set_var_field(var, obj, name, package=None): - val = None - if package: - val = d.getVar("%s:%s" % (var, package)) - - if not val: - val = d.getVar(var) - - if val: - setattr(obj, name, val) - license_data = oe.spdx_common.load_spdx_license_data(d) pn = d.getVar("PN") @@ -947,10 +1067,12 @@ def create_spdx(d): ) set_var_field( - "HOMEPAGE", spdx_package, "software_homePage", package=package + d, "HOMEPAGE", spdx_package, "software_homePage", package=package + ) + set_var_field(d, "SUMMARY", spdx_package, "summary", package=package) + set_var_field( + d, "DESCRIPTION", spdx_package, "description", package=package ) - set_var_field("SUMMARY", spdx_package, "summary", package=package) - set_var_field("DESCRIPTION", spdx_package, "description", package=package) purls = ( d.getVar("SPDX_PACKAGE_URLS:%s" % package) @@ -1130,7 +1252,9 @@ def create_spdx(d): f"Added PACKAGECONFIG entries: {len(enabled)} enabled, {len(disabled)} disabled" ) - oe.sbom30.write_recipe_jsonld_doc(d, build_objset, "builds", deploydir) + oe.sbom30.write_recipe_jsonld_doc( + d, build_objset, "builds", deploydir, create_task_link=True + ) def create_package_spdx(d): @@ -1364,26 +1488,9 @@ def create_rootfs_spdx(d): d, "%s-%s-rootfs" % (image_basename, machine) ) - rootfs = objset.add_root( - oe.spdx30.software_Package( - _id=objset.new_spdxid("rootfs", image_basename), - creationInfo=objset.doc.creationInfo, - name=image_basename, - software_primaryPurpose=oe.spdx30.software_SoftwarePurpose.archive, - ) - ) - set_timestamp_now(d, rootfs, "builtTime") - rootfs_build = objset.add_root(objset.new_task_build("rootfs", "rootfs")) set_timestamp_now(d, rootfs_build, "build_buildEndTime") - objset.new_scoped_relationship( - [rootfs_build], - oe.spdx30.RelationshipType.hasOutput, - oe.spdx30.LifecycleScopeType.build, - [rootfs], - ) - files_by_hash = {} collect_build_package_inputs(d, objset, rootfs_build, packages, files_by_hash) @@ -1416,14 +1523,20 @@ def create_rootfs_spdx(d): ) ) - if files: - objset.new_relationship( - [rootfs], - oe.spdx30.RelationshipType.contains, - sorted(list(files)), - ) + rootfs = create_deploy_package( + d, + objset, + rootfs_build, + objset.new_spdxid("rootfs", image_basename), + image_basename, + None, + files, + ) + rootfs.software_primaryPurpose = oe.spdx30.software_SoftwarePurpose.archive - oe.sbom30.write_recipe_jsonld_doc(d, objset, "rootfs", deploydir) + oe.sbom30.write_recipe_jsonld_doc( + d, objset, "rootfs", deploydir, create_task_link=True + ) def create_image_spdx(d): @@ -1503,10 +1616,13 @@ def create_image_spdx(d): set_timestamp_now(d, a, "builtTime") if artifacts: - objset.new_scoped_relationship( - [image_build], - oe.spdx30.RelationshipType.hasOutput, - oe.spdx30.LifecycleScopeType.build, + create_deploy_package( + d, + objset, + image_build, + objset.new_spdxid(taskname, "image", imagetype), + "image", + f"do_{taskname}", artifacts, ) @@ -1527,7 +1643,9 @@ def create_image_spdx(d): objset.add_aliases() objset.link() - oe.sbom30.write_recipe_jsonld_doc(d, objset, "image", spdx_work_dir) + oe.sbom30.write_recipe_jsonld_doc( + d, objset, "image", spdx_work_dir, create_task_link=True + ) def create_image_sbom_spdx(d): @@ -1705,3 +1823,77 @@ def create_recipe_sbom(d, deploydir): objset, sbom = oe.sbom30.create_sbom(d, sbom_name, [recipe], [recipe_objset]) oe.sbom30.write_jsonld_doc(d, objset, deploydir / (sbom_name + ".spdx.json")) + + +def create_deploy_spdx(d, spdxdeploydir, artifactsdir, artifacts): + pn = d.getVar("PN") + current_task = "do_" + d.getVar("BB_CURRENTTASK") + + recipe, recipe_objset = load_recipe_spdx(d) + + if artifacts == "AUTO": + artifacts = [] + for root, dirs, files in os.walk(artifactsdir): + for p in [Path(os.path.join(root, f)) for f in files]: + if p.is_file(): + artifacts.append(p) + else: + artifacts = [artifactsdir / p for p in artifacts.split()] + + artifacts.sort(key=lambda p: (p.is_symlink(), p)) + + objset = oe.sbom30.ObjectSet.new_objset(d, f"{pn}-{current_task}-deploy") + + build = objset.add_root(objset.new_task_build(current_task, "deploy")) + set_timestamp_now(d, build, "build_buildEndTime") + objset.set_is_native(get_is_native(d)) + + files = set() + for a in artifacts: + relpath = a.relative_to(artifactsdir) + f = objset.new_file( + objset.new_spdxid("deploy", str(relpath)), + a.name, + a, + ) + files.add(f) + + if not files: + bb.fatal(f"No deployed artifacts found in {artifactsdir}") + return + + create_deploy_package( + d, + objset, + build, + objset.new_spdxid("deploy", pn, current_task), + pn, + current_task, + files, + ) + + # Create document + dest = oe.sbom30.write_recipe_jsonld_doc( + d, + objset, + "deploy", + spdxdeploydir, + create_task_link=True, + ) + + +def create_deploy_sbom(d, deploydir, deploy_tasks): + pn = d.getVar("PN") + sbom_name = f"{pn}-deploy-sbom" + + objsets = [] + for t in deploy_tasks: + o, _ = oe.sbom30.find_jsonld(d, "deploy", f"{pn}-{t}-deploy", required=True) + objsets.append(o) + + root_objs = [] + for o in objsets: + root_objs.extend(o.doc.rootElement) + + objset, sbom = oe.sbom30.create_sbom(d, sbom_name, root_objs, objsets) + oe.sbom30.write_jsonld_doc(d, objset, deploydir / (sbom_name + ".spdx.json")) diff --git a/meta/lib/oe/spdx_common.py b/meta/lib/oe/spdx_common.py index 6b1a409c40..0337d1deb5 100644 --- a/meta/lib/oe/spdx_common.py +++ b/meta/lib/oe/spdx_common.py @@ -113,7 +113,7 @@ def collect_direct_deps(d, dep_task): ) for this_dep in taskdepdata.values(): - if this_dep[0] == pn and this_dep[1] == current_task: + if this_dep.pn == pn and this_dep.taskname == current_task: break else: bb.fatal(f"Unable to find this {pn}:{current_task} in taskdepdata") From patchwork Tue Jun 9 22:15:55 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 89609 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 83448CD8CB2 for ; Tue, 9 Jun 2026 22:23:49 +0000 (UTC) Received: from mail-oo1-f44.google.com (mail-oo1-f44.google.com [209.85.161.44]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.7676.1781043823302218723 for ; Tue, 09 Jun 2026 15:23:43 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=MklKrNnI; spf=pass (domain: gmail.com, ip: 209.85.161.44, mailfrom: jpewhacker@gmail.com) Received: by mail-oo1-f44.google.com with SMTP id 006d021491bc7-69d92dbc420so4299045eaf.3 for ; Tue, 09 Jun 2026 15:23:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1781043822; x=1781648622; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=nkyEMgBDuOwhUu4d2UNjJdeinlwawvhieVh0+OjSHOI=; b=MklKrNnIKIyOq0VGJXDCimR0dlRxjgR8FBJGyhKmVHijbYaknL+/ReopCO4pYju0ad E/A6DvTxej9KzNdVJjxocojmJhoJEc2SUMtiyEqDvlZcpZx56sScDRM22GAiGMKEEjfX 2HPuDPctLQIIT5VxG5z5pVBahZDOayGs5jiYGLra4z+QaHtaq7wqsTOFV9v3vHhXP9lF 1+IVruOcNxx5UWQFEpx2UPQHAxIsmPMmX3veBjCKu/1fWdthKxcx9BlH/d/NcUr9ku8O h8CH6dGlPbTextOpq5+L3jiznahklgM71AtGaMcvj8ju1VRFBPGy7Fww/4JI2qOxD0h9 t6MA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781043822; x=1781648622; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=nkyEMgBDuOwhUu4d2UNjJdeinlwawvhieVh0+OjSHOI=; b=D2JgAxDSNC5LeBeW1+rDdMNrehfPwzdu4ZfI2K+8KhGaPAI9AFm46WOu6WL3KfV3lh cD22mHD1TZrTpAPn0qVcEawRe9sKoNyyxpWi7gleWMiJXr1gHDCNGzrCflfEGrZz4Iob UxERNYft3ZC0MmF6OJ83L77ENb/7XsjH9I4xEurwJHO5mmBe/aPRIXdX+vGd2ocybGGu 704bCYgm10SDOjPLNNkkeNQnrOV15zjsxsA6CMgcoEzRjodPruLyoTduggizs454uoUl H+QxqdeYVq3c6su3ol6MFsOqKWF75HjPIKPHBcQEnRMrl4R8ptvFNaTvlfo9gOYswHAE Driw== X-Gm-Message-State: AOJu0Yy2JkF9sD6BapGu4tsf/xMGAVwTnTPABWHKCfWti8Ag2h6OtkGs omZDl9GTyclmX7sEeFeBp+GsvdVWv6SZZYtBg34113xFPtTwTPD9Y5173IxZ8g== X-Gm-Gg: Acq92OEAuSy2EKW4pLTuXrvAxJP88kYci07wpoLrw1mGytD+COXMstNocMZDBMOucQq FgOOe53YVsg/CbHnb7Wv075hEQff7e/yoF2bW8RlkIc/M/gdyEyLSlK6cOlADkmpFuYVulWd14Q svtXPuk2VIutHZZWmgZbVcz4wgCWMBNk9j4eiYbzo0roeD1Je12TMdmINzER6LT3BxCABhaL49d 8oilWFjIZ5fYq4TU0P0xSOYh6x8dZ882J0YklfH5DGZeYxKLaN3pfO2ehFa9iXFTdv4grDQu3Zl LU+XDCvbvEGA3oVgfjLI/iC070a8jfC1ULZqtCfbDskZrB57xKtMthdUc/lRancKJKSwnzfgmAD XSYfqNTpejwJQ+PnRdDrNN3g9gKAwrEbeScF1WyBewNzgruX0VfSvYUAylNRmQVnUYjFGzH8GiW uqiaoMsLwfhvRIB9+8DKuRkCjuEG8xaingeJ1+5BsV X-Received: by 2002:a05:6820:1614:b0:69d:ef3c:7e34 with SMTP id 006d021491bc7-69e685f4849mr12596842eaf.0.1781043822476; Tue, 09 Jun 2026 15:23:42 -0700 (PDT) Received: from localhost.localdomain ([2601:283:4b02:22d0::ce1]) by smtp.gmail.com with ESMTPSA id 586e51a60fabf-440d7d4f449sm19122610fac.8.2026.06.09.15.23.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jun 2026 15:23:41 -0700 (PDT) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: Joshua Watt Subject: [OE-core][PATCH 4/5] Add SPDX deploy tasks Date: Tue, 9 Jun 2026 16:15:55 -0600 Message-ID: <20260609222331.1293007-5-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260609222331.1293007-1-JPEWhacker@gmail.com> References: <20260609222331.1293007-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 09 Jun 2026 22:23:49 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/238307 Adds SPDX deploy tasks to many recipes to record their output when consumed in an SBoM Signed-off-by: Joshua Watt --- meta/classes-recipe/barebox.bbclass | 3 ++- meta/classes-recipe/devicetree.bbclass | 3 ++- meta/classes-recipe/kernel-fit-image.bbclass | 3 ++- meta/classes-recipe/kernel.bbclass | 3 ++- meta/recipes-bsp/grub/grub-efi_2.14.bb | 3 ++- meta/recipes-bsp/opensbi/opensbi_1.8.1.bb | 3 ++- meta/recipes-bsp/u-boot/u-boot.inc | 3 ++- meta/recipes-core/systemd/systemd-boot_259.5.bb | 4 ++-- 8 files changed, 16 insertions(+), 9 deletions(-) diff --git a/meta/classes-recipe/barebox.bbclass b/meta/classes-recipe/barebox.bbclass index 73615999aa..0be611cf65 100644 --- a/meta/classes-recipe/barebox.bbclass +++ b/meta/classes-recipe/barebox.bbclass @@ -157,6 +157,7 @@ barebox_do_deploy () { done fi } -addtask deploy after do_compile +addtask deploy after do_compile do_create_spdx +SPDX_DEPLOY_TASKS += "do_deploy" EXPORT_FUNCTIONS do_configure do_compile do_install do_deploy diff --git a/meta/classes-recipe/devicetree.bbclass b/meta/classes-recipe/devicetree.bbclass index ce9d008aac..a313507251 100644 --- a/meta/classes-recipe/devicetree.bbclass +++ b/meta/classes-recipe/devicetree.bbclass @@ -163,7 +163,8 @@ devicetree_do_deploy() { install -Dm 0644 "${B}/$dtb_file" "${DEPLOYDIR}/devicetree/$dtb_file" done } -addtask deploy before do_build after do_install +addtask deploy before do_build after do_install do_create_spdx +SPDX_DEPLOY_TASKS += "do_deploy" EXPORT_FUNCTIONS do_compile do_install do_deploy diff --git a/meta/classes-recipe/kernel-fit-image.bbclass b/meta/classes-recipe/kernel-fit-image.bbclass index ae8f3c6688..becbc280ce 100644 --- a/meta/classes-recipe/kernel-fit-image.bbclass +++ b/meta/classes-recipe/kernel-fit-image.bbclass @@ -240,4 +240,5 @@ do_deploy() { fi fi } -addtask deploy after do_compile before do_build +addtask deploy after do_compile do_create_spdx before do_build +SPDX_DEPLOY_TASKS += "do_deploy" diff --git a/meta/classes-recipe/kernel.bbclass b/meta/classes-recipe/kernel.bbclass index 50cef17f69..5dc606048b 100644 --- a/meta/classes-recipe/kernel.bbclass +++ b/meta/classes-recipe/kernel.bbclass @@ -841,7 +841,8 @@ kernel_do_deploy() { # ensure we get the right values for both do_deploy[prefuncs] += "read_subpackage_metadata" -addtask deploy after do_install do_populate_sysroot do_packagedata +addtask deploy after do_install do_populate_sysroot do_packagedata do_create_spdx +SPDX_DEPLOY_TASKS += "do_deploy" EXPORT_FUNCTIONS do_deploy diff --git a/meta/recipes-bsp/grub/grub-efi_2.14.bb b/meta/recipes-bsp/grub/grub-efi_2.14.bb index 6354b43989..db187ac373 100644 --- a/meta/recipes-bsp/grub/grub-efi_2.14.bb +++ b/meta/recipes-bsp/grub/grub-efi_2.14.bb @@ -96,7 +96,8 @@ do_deploy() { install -m 644 ${B}/${GRUB_IMAGE_PREFIX}${GRUB_IMAGE} ${DEPLOYDIR} } -addtask deploy after do_install before do_build +addtask deploy after do_install do_create_spdx before do_build +SPDX_DEPLOY_TASKS += "do_deploy" FILES:${PN} = "${libdir}/grub/${GRUB_TARGET}-efi \ ${datadir}/grub \ diff --git a/meta/recipes-bsp/opensbi/opensbi_1.8.1.bb b/meta/recipes-bsp/opensbi/opensbi_1.8.1.bb index 0a9652c283..377a06fb78 100644 --- a/meta/recipes-bsp/opensbi/opensbi_1.8.1.bb +++ b/meta/recipes-bsp/opensbi/opensbi_1.8.1.bb @@ -44,7 +44,8 @@ do_deploy () { install -m 755 ${D}/share/opensbi/*/${RISCV_SBI_PLAT}/firmware/fw_dynamic.* ${DEPLOYDIR}/ } -addtask deploy before do_build after do_install +addtask deploy before do_build after do_install do_create_spdx +SPDX_DEPLOY_TASKS += "do_deploy" FILES:${PN} += "/share/opensbi/*/${RISCV_SBI_PLAT}/firmware/fw_jump.*" FILES:${PN} += "/share/opensbi/*/${RISCV_SBI_PLAT}/firmware/fw_payload.*" diff --git a/meta/recipes-bsp/u-boot/u-boot.inc b/meta/recipes-bsp/u-boot/u-boot.inc index a75948dfc3..1c3c9858df 100644 --- a/meta/recipes-bsp/u-boot/u-boot.inc +++ b/meta/recipes-bsp/u-boot/u-boot.inc @@ -470,4 +470,5 @@ uboot_deploy_spl () { ln -sf ${SPL_IMAGE} ${DEPLOYDIR}/${SPL_SYMLINK} } -addtask deploy before do_build after do_compile +addtask deploy before do_build after do_compile do_create_spdx +SPDX_DEPLOY_TASKS += "do_deploy" diff --git a/meta/recipes-core/systemd/systemd-boot_259.5.bb b/meta/recipes-core/systemd/systemd-boot_259.5.bb index c6c443f929..e2c579db97 100644 --- a/meta/recipes-core/systemd/systemd-boot_259.5.bb +++ b/meta/recipes-core/systemd/systemd-boot_259.5.bb @@ -71,5 +71,5 @@ do_deploy () { install ${B}/src/boot/addon*.efi.stub ${DEPLOYDIR} } -addtask deploy before do_build after do_compile - +addtask deploy before do_build after do_compile do_create_spdx +SPDX_DEPLOY_TASKS += "do_deploy" From patchwork Tue Jun 9 22:15:56 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 89610 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8FD77CD98C6 for ; Tue, 9 Jun 2026 22:23:49 +0000 (UTC) Received: from mail-oa1-f51.google.com (mail-oa1-f51.google.com [209.85.160.51]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.7677.1781043824914108722 for ; Tue, 09 Jun 2026 15:23:45 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=IksIcCFx; spf=pass (domain: gmail.com, ip: 209.85.160.51, mailfrom: jpewhacker@gmail.com) Received: by mail-oa1-f51.google.com with SMTP id 586e51a60fabf-43d2ff651f2so4940713fac.2 for ; Tue, 09 Jun 2026 15:23:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1781043824; x=1781648624; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=qTsexwYYowGfqXylTEFJrylMlmDVzBfXHqyb2GoRavE=; b=IksIcCFxD9ldLCIo0juvMHOM209ao9/XRfnh31I//t17G3dAeVZ9Qe9WZEKbyZDcHC RoG9zNdT5fTNg8cSuuLE8gCpY9T6VpHxAhRU8UHeLD3wkuyynpNHY/K2izB8NZkk6dUU 1ll3zVwtoFprzYtwl2LN+JydYPGh3rftItFajQUFj/jBEHBuLMfDF8C/mzQwYDD0jlfH yrcKTm1NfYfWzJfDAUZSsoSU5szrekBE3XYJ92xc1lM+Pq7HEix6h/LMS1leRXFD0X0h sFs5Tkw5Ic1nRQa7hIezBhO4EgRg8q60ROOhkdA7TG7v14INOLG0M0sDL7Z/OntuIlh1 AVwA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781043824; x=1781648624; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=qTsexwYYowGfqXylTEFJrylMlmDVzBfXHqyb2GoRavE=; b=GF3LgPQCGmjGT94+MUCWN6BgybqbMNd7u3Nv5eUEO2mdO1xM3xeamc4fp6O7zM00UO wPPno9jnNVX9knrwDWmBjxAKWcGgcotibuLeudvXwg6oK4g2bDEzeYNvgx9NxmBMH9OT X75i/noOs1r315TIJf4VTrdkUEsFSfTgaMcyOg/hz39t7oHzfqXVBR6O2Igj6KhJhMeQ LaH/WSt7H4NYGR0mh/BOxnOhfUGLN8ozIMu+7SDSz4ll3dWKx4iX+vbWPRxkhjfJpDqQ NKoqXlOkbas3eoSpHpH8l3Ywep95lmSNMA1KR77XQvIg6w2Rae60g1Dgfnx2+xnvf69P 15xQ== X-Gm-Message-State: AOJu0Yy8+AROBt5vpuzeGGtmMaQNMytRIetByZXcxBxvaREaIMBIQ2PQ cB41KtvlurN0KMhmkw3ukTzJRNwkvdEeA0xvKPhNAhQHdSF8AbVD+syOn5nNog== X-Gm-Gg: Acq92OGtuGXZVeuizzFHxdnLKrM+ZY5ZOtC9HGV1Fwh60WKp4/8YmHaCXCLdIEb2AjL VDuk+7SqNuAaAGc4R7MEMKrPmPxMnpHUBDgbeW4Owg04NJyIzDdrnQZhHNhjJEYyQ3XaLk7K8li m/L9ArSbHheRIqLDpInyi2AnXeRwxwQSAce9g34dAMtHIK1c/5szOSwQVjQllvMOsvyfr+vyme7 /q0l5bbTzuTzbnHGdHAtUXdEQl+iXCyT1lIBQTEbwsfqwflJYojzME0u8Ae1T+99KBmbWOR2cxi gvdkekN5hSxs6zL99rfpEiddSYP/UIzJX1SH9ZfUBDpefYuU44jlAr0fJpM59J+yGaWBB35jctR 85dpXzxmA8D4kGqroUZIOinYAUoR6Qptp2TXcctCDDU8BTE5UfZfnaf1RHsR0IFFPX94hB14U4a nDHQ/7qRzvtsU7uFQvc+acFl1bKk/5WQ== X-Received: by 2002:a05:687c:22d4:20b0:442:892:9aea with SMTP id 586e51a60fabf-44208936b87mr975752fac.32.1781043824060; Tue, 09 Jun 2026 15:23:44 -0700 (PDT) Received: from localhost.localdomain ([2601:283:4b02:22d0::ce1]) by smtp.gmail.com with ESMTPSA id 586e51a60fabf-440d7d4f449sm19122610fac.8.2026.06.09.15.23.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jun 2026 15:23:42 -0700 (PDT) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: Joshua Watt Subject: [OE-core][PATCH 5/5] spdx: Replace do_create_image_spdx with deploy task Date: Tue, 9 Jun 2026 16:15:56 -0600 Message-ID: <20260609222331.1293007-6-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260609222331.1293007-1-JPEWhacker@gmail.com> References: <20260609222331.1293007-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 09 Jun 2026 22:23:49 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/238308 Replaces the dedicated do_create_image_spdx task with a deploy task tied to do_image_complete (which is task that deploys images). This has the advantage that images recipe SPDX dependencies are now completely automatically detected in the task graph, and the SPDX documents are merged in automatically when dependencies on do_image_complete are detected Signed-off-by: Joshua Watt --- .../create-spdx-image-3.0.bbclass | 30 +++++++------------ meta/classes-recipe/nospdx.bbclass | 1 - meta/classes/create-spdx-3.0.bbclass | 3 -- meta/classes/spdx-common.bbclass | 1 - meta/lib/oe/spdx30_tasks.py | 21 ++++++++----- 5 files changed, 24 insertions(+), 32 deletions(-) diff --git a/meta/classes-recipe/create-spdx-image-3.0.bbclass b/meta/classes-recipe/create-spdx-image-3.0.bbclass index a96cfb25ed..708981a0c0 100644 --- a/meta/classes-recipe/create-spdx-image-3.0.bbclass +++ b/meta/classes-recipe/create-spdx-image-3.0.bbclass @@ -30,7 +30,7 @@ python do_create_rootfs_spdx() { import oe.spdx30_tasks oe.spdx30_tasks.create_rootfs_spdx(d) } -addtask do_create_rootfs_spdx after do_rootfs do_create_recipe_spdx before do_image +addtask do_create_rootfs_spdx after do_rootfs do_create_recipe_spdx before do_image do_image_complete SSTATETASKS += "do_create_rootfs_spdx" do_create_rootfs_spdx[sstate-inputdirs] = "${SPDXROOTFSDEPLOY}" do_create_rootfs_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}" @@ -43,33 +43,23 @@ python do_create_rootfs_spdx_setscene() { } addtask do_create_rootfs_spdx_setscene -python do_create_image_spdx() { +python create_image_spdx() { import oe.spdx30_tasks - oe.spdx30_tasks.create_image_spdx(d) -} -addtask do_create_image_spdx after do_image_complete do_create_rootfs_spdx do_create_recipe_spdx before do_build -SSTATETASKS += "do_create_image_spdx" -SSTATE_SKIP_CREATION:task-create-image-spdx = "1" -do_create_image_spdx[sstate-inputdirs] = "${SPDXIMAGEWORK}" -do_create_image_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}" -do_create_image_spdx[cleandirs] = "${SPDXIMAGEWORK}" -do_create_image_spdx[dirs] = "${SPDXIMAGEWORK}" -do_create_image_spdx[file-checksums] += "${SPDX3_DEP_FILES}" -do_create_image_spdx[vardeps] += "\ - SPDX_IMAGE_PURPOSE \ - " + from pathlib import Path + current_task = "do_" + d.getVar("BB_CURRENTTASK") -python do_create_image_spdx_setscene() { - sstate_setscene(d) -} -addtask do_create_image_spdx_setscene + spdxdeploydir = Path(d.getVar("SPDXDIR") + "/deploy-" + current_task) + oe.spdx30_tasks.create_image_spdx(d, spdxdeploydir) +} +oe.spdx30_tasks.create_image_spdx[vardeps] += "SPDX_IMAGE_PURPOSE" +SPDX_DEPLOY_TASKS += "do_image_complete:create_image_spdx" python do_create_image_sbom_spdx() { import oe.spdx30_tasks oe.spdx30_tasks.create_image_sbom_spdx(d) } -addtask do_create_image_sbom_spdx after do_create_rootfs_spdx do_create_image_spdx before do_build +addtask do_create_image_sbom_spdx after do_create_rootfs_spdx do_image_complete before do_build SSTATETASKS += "do_create_image_sbom_spdx" SSTATE_SKIP_CREATION:task-create-image-sbom = "1" do_create_image_sbom_spdx[sstate-inputdirs] = "${SPDXIMAGEDEPLOYDIR}" diff --git a/meta/classes-recipe/nospdx.bbclass b/meta/classes-recipe/nospdx.bbclass index b405f57d11..9c30c4d2c0 100644 --- a/meta/classes-recipe/nospdx.bbclass +++ b/meta/classes-recipe/nospdx.bbclass @@ -9,6 +9,5 @@ deltask do_create_spdx deltask do_create_spdx_runtime deltask do_create_package_spdx deltask do_create_rootfs_spdx -deltask do_create_image_spdx deltask do_create_image_sbom deltask do_create_deploy_sbom diff --git a/meta/classes/create-spdx-3.0.bbclass b/meta/classes/create-spdx-3.0.bbclass index 19d5a45eba..c0263e4277 100644 --- a/meta/classes/create-spdx-3.0.bbclass +++ b/meta/classes/create-spdx-3.0.bbclass @@ -358,9 +358,6 @@ python () { # for the recipe, at least until it's possible for do_populate_sysroot # to describe it's own output. "do_populate_sysroot": "do_create_spdx", - # If an image is needed, also depend on the task to create the SBoM for - # the image - "do_image_complete": "do_create_image_spdx", } def map_task_deps(task, flag): diff --git a/meta/classes/spdx-common.bbclass b/meta/classes/spdx-common.bbclass index bca169670d..13839aac3a 100644 --- a/meta/classes/spdx-common.bbclass +++ b/meta/classes/spdx-common.bbclass @@ -15,7 +15,6 @@ CVE_VERSION ??= "${PV}" SPDXDIR ??= "${WORKDIR}/spdx/${SPDX_VERSION}" SPDXDEPLOY = "${SPDXDIR}/deploy" SPDXWORK = "${SPDXDIR}/work" -SPDXIMAGEWORK = "${SPDXDIR}/image-work" SPDXSDKWORK = "${SPDXDIR}/sdk-work" SPDXSDKEXTWORK = "${SPDXDIR}/sdk-ext-work" SPDXDEPS = "${SPDXDIR}/deps.json" diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py index 3dae502e64..f5717b3919 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py @@ -1539,19 +1539,19 @@ def create_rootfs_spdx(d): ) -def create_image_spdx(d): +def create_image_spdx(d, spdx_deploy_dir): import oe.sbom30 + pn = d.getVar("PN") + current_task = "do_" + d.getVar("BB_CURRENTTASK") + image_deploy_dir = Path(d.getVar("IMGDEPLOYDIR")) manifest_path = Path(d.getVar("IMAGE_OUTPUT_MANIFEST")) - spdx_work_dir = Path(d.getVar("SPDXIMAGEWORK")) image_basename = d.getVar("IMAGE_BASENAME") machine = d.getVar("MACHINE") - objset = oe.sbom30.ObjectSet.new_objset( - d, "%s-%s-image" % (image_basename, machine) - ) + objset = oe.sbom30.ObjectSet.new_objset(d, f"{pn}-{current_task}-deploy") with manifest_path.open("r") as f: manifest = json.load(f) @@ -1644,13 +1644,18 @@ def create_image_spdx(d): objset.add_aliases() objset.link() oe.sbom30.write_recipe_jsonld_doc( - d, objset, "image", spdx_work_dir, create_task_link=True + d, + objset, + "deploy", + spdx_deploy_dir, + create_task_link=True, ) def create_image_sbom_spdx(d): import oe.sbom30 + pn = d.getVar("PN") image_name = d.getVar("IMAGE_NAME") image_basename = d.getVar("IMAGE_BASENAME") image_link_name = d.getVar("IMAGE_LINK_NAME") @@ -1672,7 +1677,9 @@ def create_image_sbom_spdx(d): root_elements.append(oe.sbom30.get_element_link_id(rootfs_image)) image_objset, _ = oe.sbom30.find_jsonld( - d, "image", "%s-%s-image" % (image_basename, machine), required=True + d, + "deploy", + f"{pn}-do_image_complete-deploy", ) for o in image_objset.foreach_root(oe.spdx30.software_File): root_elements.append(oe.sbom30.get_element_link_id(o))