From patchwork Wed Jun 24 14:15:18 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 90850 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 123C3CDB481 for ; Wed, 24 Jun 2026 14:17:18 +0000 (UTC) Received: from mail-oo1-f46.google.com (mail-oo1-f46.google.com [209.85.161.46]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.8324.1782310631984445762 for ; Wed, 24 Jun 2026 07:17:12 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=syIZKnSd; spf=pass (domain: gmail.com, ip: 209.85.161.46, mailfrom: jpewhacker@gmail.com) Received: by mail-oo1-f46.google.com with SMTP id 006d021491bc7-69e1f777bcaso944259eaf.1 for ; Wed, 24 Jun 2026 07:17:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1782310631; x=1782915431; 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=++NZ7kP5+FKwUt7mt/GFaoHNn/3GJp5DT7XWKOlW0d4=; b=syIZKnSdgQ8671gFOn8Iza4HaFWk8fAI5IZ6HqEJqxB3VOuAq/HbzoLD/rYWCP2A1H nFtWFrIj1cPUg3ZHYxDwrN5CVoYy8Bm3l8yH25j8GCnoMtoJrqR8hPL42Zd32V8NhjzG 6tTZCXGnyA2ZhgKoBwzvdtITmybaZ5wk7oyML49RHEuzNegTAA8GeMPhRR7muxRrZv3I qnRbrjCzc9Wq8t97gXKAOOH6KAIveaIuLeuhUzpLtdV7RaLYmxoV1OAvUlx0kWBSNHd4 9s4wudy2PAkuXIOT1Uzi4+cC5CJkGEtjfs0SWPtg3nHThQmZM0H9eU5hdTmqP6joZYiZ /bSg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1782310631; x=1782915431; 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=++NZ7kP5+FKwUt7mt/GFaoHNn/3GJp5DT7XWKOlW0d4=; b=BjH02UtvbE+8y64vVLs4cKvHSm+EBaLMugsNa9cazlgS5VmGdIaHR5i1ThjgcN5Ljp aSCV3cvqr6Rb7j+QWj+0XW1ti42LzwOnbkyMRUm0Y17M4JqDXHade01hpbNNyDvmQq1p FbQF2r1DZfeUNWO3TCEYOUaKSkuXp8om9a9gupCGVtr7b8Hg9hGigZTo70lKZ0d0sx0B NRqy7rYB+4ypm2amkL5Ori/ESdCWbDeE/quB9ucCKQgJRMItcSW7J1ybiJeF1OO4enIn MNb5vjYc/ZjwglIF4OnqDEnMueIH6ZyMpYy8uNgKLT66Wbs41zNYqzpkhgxTeDRBioHU kDcA== X-Gm-Message-State: AOJu0YzXQXjxE0d2DAEcLG3d0+nhllQC1zPZP7Ou0QJ1+YnI07sSeCZG pMVJVDohVEaSzAVqzNrQlumGlNx7SfaLBLrDKSgb4/pvJ/kflhO25L9b0Ix/NA== X-Gm-Gg: AfdE7clS9Mypb4Fi9xOXKMhjJ+POZ26gaSCBAA2jBR5FhgEyObOnNNxWqELilSkD98U g4+ahIKIYODMdtfxSb+Uw0ikFVJUR7dBDrFOIWAqkxZg/+B7wz+jgWLWIwLteKbSpoEXyfxT2th ryyuCnlkGqUtXhhuUqhCNTLtXyVQqBZjI7PbZsIDDYCpYtmfjptkG7Cvri0nce3erB9Sr5HEXrU JOAY+z9zd3glwqVMYwMn7Em3rDsENm50bqNm0GXZI2hb3G8g8y+kR7Q+ilDOAlpoibg1K3fmqQ0 ABK69HTuEs2amH6zGV7bm/hZ/q9XuamR+jvDYB+o35TBZR1QoYxSsmXf+vW9+z5qvzSKH4u9Ekb wzimM7eEcJxPd5Y28E8LxazRntUJbKpZijU3Ca0Fr2ZjqHQjLLcGgJnRnaE4fRrvDLRomjR8JG3 42456BQReT2AYSdeMBjrOu X-Received: by 2002:a05:6820:2d02:b0:69e:390e:b98b with SMTP id 006d021491bc7-6a12d7893aemr299516eaf.1.1782310630811; Wed, 24 Jun 2026 07:17:10 -0700 (PDT) Received: from localhost.localdomain ([2601:283:4b02:22d0::87cd]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6a0e9f2a4e9sm8946149eaf.2.2026.06.24.07.17.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Jun 2026 07:17:10 -0700 (PDT) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: Joshua Watt Subject: [OE-core][PATCH v3 1/8] spdx: Skip dependencies that are not in the taskhash Date: Wed, 24 Jun 2026 08:15:18 -0600 Message-ID: <20260624141706.2164567-2-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260624141706.2164567-1-JPEWhacker@gmail.com> References: <20260618165032.347436-1-JPEWhacker@gmail.com> <20260624141706.2164567-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 ; Wed, 24 Jun 2026 14:17:18 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/239510 If a dependency is not in the taskhash, it cannot be included in the SPDX data because the dependency may not trigger the recipe to rebuild if it changes (although aliases help with this), but more importantly bitbake may not restore the sstate object associated with the dependency which causes errors when constructing the final SBoM Signed-off-by: Joshua Watt --- meta/lib/oe/spdx30_tasks.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py index 72d17aade6..fc78586cf2 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py @@ -330,13 +330,14 @@ def collect_dep_objsets(d, direct_deps, subdir, fn_prefix, obj_type, **attr_filt dep_obj, dep_objset = oe.sbom30.find_root_obj_in_jsonld( d, subdir, fn_prefix + dep.pn, obj_type, **attr_filter ) - # If the dependency is part of the taskhash, return it to be linked - # against. Otherwise, it cannot be linked against because this recipe - # will not rebuilt if dependency changes - if dep.in_taskhash: - dep_objsets.append(dep_objset) + # If the dependency is not part of the task hash, do not include it + # since the dependency may not be present in subsequent runs, and may + # not rebuild if it changes + if not dep.in_taskhash: + bb.debug(1, f"Skipping dependency {dep.pn} (not in taskhash)") + continue - # The object _can_ be linked against (by alias) + dep_objsets.append(dep_objset) dep_objs.add(dep_obj) return dep_objsets, dep_objs From patchwork Wed Jun 24 14:15:19 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 90855 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 8FC96CDE006 for ; Wed, 24 Jun 2026 14:17:18 +0000 (UTC) Received: from mail-oo1-f41.google.com (mail-oo1-f41.google.com [209.85.161.41]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.8325.1782310633054321941 for ; Wed, 24 Jun 2026 07:17:13 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=sYBfoBrb; spf=pass (domain: gmail.com, ip: 209.85.161.41, mailfrom: jpewhacker@gmail.com) Received: by mail-oo1-f41.google.com with SMTP id 006d021491bc7-6a1133217easo456521eaf.2 for ; Wed, 24 Jun 2026 07:17:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1782310632; x=1782915432; 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=6PeGI4krp6esSXhKRBWAzeV5XeKGmJztglg4iO2cAyE=; b=sYBfoBrbVk0RT2fbwR20MgysYVBHJJkQL80f+ApmIYFZ3UVFQW2pCHt3ov0NoS7rJG 91zJuskPguhiOxSRFJ34HKUXaD5CH2e24zoXeK//NDULui2IiEAWTjYaHo2hpmbfb7Gq jTb1T5e2JMDGB3Tzz9f3yDJxBetfYq04AJHzMPp+II3V78WDf9DnphfMDu7nNi7tw6fs abX8NB8Un0yJjfvm4DW+emsMtsGE0j3z46Qa8AINVMpRkkzHdwYt1pWcz+RSUI66/M4n JAggeX3W0CPV/i1O6x6OsbIsTWPI3HkeshKEZAFP7bJvVh8l5Dxpuj0P2J5ZAetzz7fm HI0A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1782310632; x=1782915432; 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=6PeGI4krp6esSXhKRBWAzeV5XeKGmJztglg4iO2cAyE=; b=UwpDycWTysgky/z4cyJfzlfHL6Ijfwrod+UD8ftri4i1nExBbK29rrRqp14cWPjfh0 J5i2psbmgxeHUK+aRr8qP1ZXY9jzu3N8/ovMEwjxkaEUq2vo66uButxrFUMGmMAblT4h wksHC3oOtw6JIDNZM50rW8cI2Oys5uaR9sBdrLwKEQz2Pw/sWYzeEZLmuDoVMLvP202Y zO630At3M91NZ1IPUBTif0pXWCLOue9V35izYIQKO9LzODQRjVWMHceMgqJnjn1njwu3 OfhEHe3DOFVsZPcnXzNMcLHEyCaoMVzKXyqcb/MghvKgrzrDA6/vOKNNz2267enmEjvv 6aqw== X-Gm-Message-State: AOJu0Yyni+KPR8PcvlNaSNfgBzyRqA9tGknqLnge69zpuAu/3ujOXR9U bpbGXuEL8GJ5viu3iMw4PaHdz7Vk9R3mTuIE9eake4Mtd+MwOEF/3jKkQZM6jQ== X-Gm-Gg: AfdE7clFs5kgNtW8phlls1U1+fm1lphimsMx/14Qep+vms3UZm2tOTR52L8DYXQ8nb3 4LYdWSGWFPvncYFJkvFoctFQo+omJmYrBF6syS+wlAmeFvAXcW8cN6QBDNoAVX98qEEvZ2klKIR P9eH31KVcUUZ2LJxSEukE96oZLP0WHrsxdy+Lr5cOYxi3u0c72ou01b+vqPj5r2EflT7/0AdBnt Jfnru6hBDfzzDSI9/j3ixNmVuqgqNQ+GkWlI1EJRKGlfWRdgZxV8j5KIYbuqPt80x8zIa1wpLLy UYZYjJVKrY+LeEZgTALeL4AmlFMqWhiJZyNLJ0NzhcYX9x65/T5/3R0jhyOgAdtPyy3lHMan3VG wG2Rndi35M/YeVw1xxm7ij2QkB9YtX+lmxI2vPK1xHyB4DSzRpj1KXMGDtp1spSisjOaqNN0nl0 yoyOtQFmOUJg== X-Received: by 2002:a05:6820:81cc:b0:67e:f8c:6bcc with SMTP id 006d021491bc7-6a114b615e8mr5123507eaf.19.1782310631748; Wed, 24 Jun 2026 07:17:11 -0700 (PDT) Received: from localhost.localdomain ([2601:283:4b02:22d0::87cd]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6a0e9f2a4e9sm8946149eaf.2.2026.06.24.07.17.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Jun 2026 07:17:11 -0700 (PDT) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: Joshua Watt Subject: [OE-core][PATCH v3 2/8] spdx: Add ability for deploy tasks to create SPDX Date: Wed, 24 Jun 2026 08:15:19 -0600 Message-ID: <20260624141706.2164567-3-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260624141706.2164567-1-JPEWhacker@gmail.com> References: <20260618165032.347436-1-JPEWhacker@gmail.com> <20260624141706.2164567-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 ; Wed, 24 Jun 2026 14:17:18 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/239511 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 | 176 +++++++++++ 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, 448 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..13d1de2774 100644 --- a/meta/classes/create-spdx-3.0.bbclass +++ b/meta/classes/create-spdx-3.0.bbclass @@ -163,6 +163,49 @@ 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. Items in \ + list have the format 'TASK(:FUNCTION)' where 'TASK' is the sstate task \ + (which must start with 'do_deploy'), and 'FUNCTION' is the optional \ + function to call to produce the deploy SPDX. If 'FUNCTION' is omitted \ + a default function is provided that uses SPDX_DEPLOY_ARTIFACTS and \ + SPDX_DEPLOY_ARTIFACTS_DIR to configure what deploy artifacts it describes \ + in the 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)." + +SPDX_DEPLOY_SBOM ??= "1" +SPDX_DEPLOY_SBOM[doc] = "If '1' (the default) a task named \ + do_create_deploy_sbom will be created to automatically collect all deploy \ + SPDX documents (from SPDX_DEPLOY_TASKS) and combine them into a SBoM \ + which will be placed in DEPLOY_DIR_IMAGE. This task is added as a \ + dependency of do_build, so it will run whenever the recipe is built \ + directly. The name of the document can be set with SPDX_DEPLOY_SBOM_NAME. \ + If the recipe already has another task to create an SBoM, this should be \ + set to '0' to prevent multiple SBoMs from being created." + +SPDX_DEPLOY_SBOM_NAME ?= "${PN}-deploy-sbom" +SPDX_DEPLOY_SBOM_NAME[doc] = "The name of the output deploy SBoM when using \ + create_deploy_sbom" + +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 +334,136 @@ 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()) + deploy_sbom_tasks = [] + for task in (d.getVar("SPDX_DEPLOY_TASKS") or "").split(): + if ":" in task: + task, func = task.split(":") + else: + func = "create_deploy_spdx" + + if not task.startswith("do_deploy") and not task == "do_image_complete": + bb.fatal(f"Task {task} is not allowed to deploy SPDX data. Must start with 'do_deploy'") + + 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", "${PN}:do_create_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 deploy_sbom_tasks and (d.getVar("SPDX_DEPLOY_SBOM") or "") == "1": + 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 +SSTATE_SKIP_CREATION:task-create-deploy-sbom = "1" 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 fc78586cf2..c58c8c30e1 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py @@ -606,6 +606,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") @@ -796,7 +925,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): @@ -810,17 +941,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") @@ -948,10 +1068,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) @@ -1131,7 +1253,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): @@ -1365,26 +1489,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) @@ -1417,14 +1524,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): @@ -1504,10 +1617,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, ) @@ -1528,7 +1644,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): @@ -1706,3 +1824,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 = d.getVar("SPDX_DEPLOY_SBOM_NAME") + + 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 Wed Jun 24 14:15:20 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 90853 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 B1704CDE005 for ; Wed, 24 Jun 2026 14:17:18 +0000 (UTC) Received: from mail-oo1-f46.google.com (mail-oo1-f46.google.com [209.85.161.46]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.8326.1782310633385970246 for ; Wed, 24 Jun 2026 07:17:13 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=lhloCiox; spf=pass (domain: gmail.com, ip: 209.85.161.46, mailfrom: jpewhacker@gmail.com) Received: by mail-oo1-f46.google.com with SMTP id 006d021491bc7-6a11c68ae41so540033eaf.3 for ; Wed, 24 Jun 2026 07:17:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1782310632; x=1782915432; 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=9Z2EPDVyBQGcOUVHtdSyMrcOgnpxSNgURZL34IR3pSk=; b=lhloCiox1LM8VB6WFBrAMKS3V6LSo3WAOZOKRb40EesGEJgoVru1KhRS5kw8YVrC0y wjxDLM9om3X5a7lsQVOuu0firiDM7h73YUnPgKnCgJcJyyjrhKP7CG+Kd7FlzcMJ/14a kvBsGnnyrcYcBWQ26f9NBrq2m5mKu7rr4PmjiNx0lrR5R8ThpO1dBu4nEay6QM+KwqbZ AYCH0+hh0okqJkXttKYTs2/nAj6JdoJ6iLMYnD1AcCeXhcflFuE48ouXwbNeDLYPpOe9 sxnM3MSHQOR5+JhAqaHz4sLIPpkoYlG2300klZOz+E47H2ZN9f+4YBEmoFn+wZGOEDqn hg+w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1782310632; x=1782915432; 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=9Z2EPDVyBQGcOUVHtdSyMrcOgnpxSNgURZL34IR3pSk=; b=LKcbBa3jRmK/5D03+jvHl3Z6UFBYCjCK1qOS4GDFFd/Qxm4BAQnbLW+tFLFPUkXD8m CnZh4IVUheT7gFzslPnJx5nl4bioGtiFKyF3Rp1Ujzjgk3MvIhUi3G3RjMPvNfi0KuUL 0cH13aiR06kgtlKk6KDlj8qGiOCmWsSO5GXzJ0WFXZgEqIR4VHuP3ndb36WaHZj0WKNY ACuhM6PaQc4wZtuBTHTPAO/RkujZQugnEWmxZSrcV0brFxkm/AghRGN9N/ijujnfVjRc k31YF8CqepQsOACFnwHibbwcrGtdn6SMRcbieYwbF/8T9a3OilZaBamo3kqaSJY0mqb5 w7eA== X-Gm-Message-State: AOJu0YwLUncgn2iPyLxvz2ZikqmVvR5yqCrvPh02F+97oNGDMAq9OWO+ w7zO17Uy9Xi2TBu3k4dNax73ZFn72M1covXsw59WFW0p6B4fqqR1ya1RsaSOQQ== X-Gm-Gg: AfdE7ckb65G7e1c2GGGUq5/h/PohdQXuwdCXXtsiZRTtIRobNxhYhbmGVfMr0HGabjj oYpv0OjjotlYhS7loZm4ZqGk/ha6rEib4rqZFRFJkOq513EEJn1uSvlD1OooYljM0YKZZgMmr8J ykewa72FbEL3mO63QnSk3wBiC2tdSVJivx+gG7FIC/Pj7b9WDmy7EUTUC8VDtYCPWHnn4udLJtI 7thhc/9ZJKCzzD+f1gvfm5Yasica1hyhhER+Gt3sVY7tL2AlrK7cjP+3FQ2TCXK1tKYByLMmTfJ Jc1fy162wvMlAlyVGDTiDfcMNyhnelP1RD19ZK+Mq/VRIbIQJVccYi5LfqR4oyibU44Bjr7h45T MdOtzP0qgH/vzTsn67jil3tuXwTRd0txxt6/B9XENl5VTMa3qxCoW1f19iwZNj5x/U2caHbl5WD aTxaOjyXqtIw== X-Received: by 2002:a05:6820:190d:b0:69e:3e2a:a83f with SMTP id 006d021491bc7-6a122e1066amr2310881eaf.9.1782310632394; Wed, 24 Jun 2026 07:17:12 -0700 (PDT) Received: from localhost.localdomain ([2601:283:4b02:22d0::87cd]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6a0e9f2a4e9sm8946149eaf.2.2026.06.24.07.17.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Jun 2026 07:17:12 -0700 (PDT) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: Joshua Watt Subject: [OE-core][PATCH v3 3/8] oeqa: Add SPDX deploy SBoM test Date: Wed, 24 Jun 2026 08:15:20 -0600 Message-ID: <20260624141706.2164567-4-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260624141706.2164567-1-JPEWhacker@gmail.com> References: <20260618165032.347436-1-JPEWhacker@gmail.com> <20260624141706.2164567-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 ; Wed, 24 Jun 2026 14:17:18 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/239512 Adds a test that verifies that the deploy SBoM is created correctly Signed-off-by: Joshua Watt --- meta/lib/oeqa/selftest/cases/spdx.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/meta/lib/oeqa/selftest/cases/spdx.py b/meta/lib/oeqa/selftest/cases/spdx.py index 8285189382..af2a9dc236 100644 --- a/meta/lib/oeqa/selftest/cases/spdx.py +++ b/meta/lib/oeqa/selftest/cases/spdx.py @@ -443,3 +443,14 @@ class SPDX30Check(SPDX3CheckBase, OESelftestTestCase): r'\d', f"Version '{version}' for package '{name}' should contain digits" ) + + def test_deploy_sbom(self): + kernel_recipe = get_bb_var("PREFERRED_PROVIDER_virtual/kernel") + + objset = self.check_recipe_spdx( + "virtual/kernel", + f"{{DEPLOY_DIR_IMAGE}}/{kernel_recipe}-deploy-sbom.spdx.json", + ) + + # Document should be fully linked + self.check_objset_missing_ids(objset) From patchwork Wed Jun 24 14:15:21 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 90854 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 DC7FFCDE008 for ; Wed, 24 Jun 2026 14:17:18 +0000 (UTC) Received: from mail-oo1-f54.google.com (mail-oo1-f54.google.com [209.85.161.54]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.8223.1782310633953107609 for ; Wed, 24 Jun 2026 07:17:14 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=leoVOTdg; spf=pass (domain: gmail.com, ip: 209.85.161.54, mailfrom: jpewhacker@gmail.com) Received: by mail-oo1-f54.google.com with SMTP id 006d021491bc7-69de16f5e80so628470eaf.0 for ; Wed, 24 Jun 2026 07:17:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1782310633; x=1782915433; 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=2tZbtQ9Kak64LuP8YK6BtHiCqVZb9HgkqaLZ/peLdzM=; b=leoVOTdg1n6tosl/tHoDDWuWOKHEhHpa4hSpFw6IALEPdrIdhCfCdvydZMftuqiKh4 ZFVAvi7SyXP2qPnZyoEOWPn8zz+pQNES2CjQiH1pSz92hXXeZ8FR1ElUeKTi2N5IcYoq LVfunF8REBls7zAGx23ep/cRcQTgXJ+K572eSi8Qz4F2blN0aBGNvXtpOgNzB6gj02NH OWsyCgAKYuVde2KLCH4Txu126AYh45O1UPpQMt+ddk/ry86Eohz9dvEP1URrs4Mn/vcF oLQvBnqxmVofzsOD+npGISTFH6TjoBjFBe5oCE+Vqhj2oNE23zyF394tfq7EceuqOzcq BJ2A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1782310633; x=1782915433; 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=2tZbtQ9Kak64LuP8YK6BtHiCqVZb9HgkqaLZ/peLdzM=; b=rnVv2VhFVkCK7tVBVE8yQib8wJZiiLnY2euSCJinmoFgPRTvynirjje3mr7is5Y7Er L5lSoXUm0QqcwIxwexjfh5I9BKTZ2s0iZecH2FETxZSPUEgYcB03MoXkT0Z9kcU301tx hkPlfxwpScmeegGVNhJ5VfwGtdYB+et+4dzBOU8XCLFzhkNlbQeBgLM9f4OLGJf1FdMH +xUQvCOp7EpT7VBeSFM0Utb3SqReUL2qlu0dH7oNOyrmhato0up9A6HJRmEC/zdGC5rv 7sBmAI+DiQBQlfmD34/h9Yr55j+Rc1ZZe8Cz2lBvoldbRpist1HWZmEL2WuMNhLwh/UC Oerg== X-Gm-Message-State: AOJu0YwrsYOH23jzlEr5i2oMBVEG++W4CkJ6W/Pkc2odfmeSBfijs4gk uo1xipP9PKDDZmJkJkR736v/D7frqEMgDBwT0nbD/Ai+ziSYZ+Bw1gA52MI22A== X-Gm-Gg: AfdE7clS3OIe63I3lkNOJbCmClmQasnbeeV7KvbaXDVRBl9JhQBXqbB9m3CJaQ1A8fz 13oStntOAJ6R7gWOvVkTkKpmj2MMvkp9KBJXlRNpom85/AHE6AcyBLt5o3N6+VAe1PEiN/r/bhu N3kKD2Vy9lfqwdYTE8PqUpkGTzEe6SWLGdukEjhoBASPj5LzaeMZ2XTifAEKtMuieaTUQoaeXEV bf97bi5vfKHjqcHMZ/VrHcrv2sWfEgvFO8UU2InshI34C6fRS13lRGf8EmCgJFhhaZeHkD/uW2i ScFS4lYbb979c6dvvzE6Mc/O6qvotwiC6N0LcV2AzbVl/t6q0OCvKGGMgsZnVruTmjDsMeEwraW TEns0rqcPHRVSR97X/AoEZQZTqg6xFFx/Fb6ks5KimvEabELqPH4ZWyieKQyJD4PLvpld62mQfu cWoOh10t6//w== X-Received: by 2002:a05:6820:2001:b0:69b:196a:de67 with SMTP id 006d021491bc7-6a122cece79mr2301504eaf.0.1782310633025; Wed, 24 Jun 2026 07:17:13 -0700 (PDT) Received: from localhost.localdomain ([2601:283:4b02:22d0::87cd]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6a0e9f2a4e9sm8946149eaf.2.2026.06.24.07.17.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Jun 2026 07:17:12 -0700 (PDT) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: Joshua Watt Subject: [OE-core][PATCH v3 4/8] classes-global/sstate: Keep SPDX generating setscene dependencies Date: Wed, 24 Jun 2026 08:15:21 -0600 Message-ID: <20260624141706.2164567-5-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260624141706.2164567-1-JPEWhacker@gmail.com> References: <20260618165032.347436-1-JPEWhacker@gmail.com> <20260624141706.2164567-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 ; Wed, 24 Jun 2026 14:17:18 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/239513 Tasks that create SPDX documents can reference SPDX ids from documents created by any task they depend on. When it comes time to create the final SBoM, these referenced SPDX ids must be present so that they can be merged into the SBoM. Specifically, when a task that restores from sstate (a setscene task) is one that can create an SPDX document, and that task is depended on by at least one other task that can create an SPDX document, it must always be restored. Signed-off-by: Joshua Watt --- meta/classes-global/sstate.bbclass | 38 ++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/meta/classes-global/sstate.bbclass b/meta/classes-global/sstate.bbclass index 4ad71a70da..4d744a887b 100644 --- a/meta/classes-global/sstate.bbclass +++ b/meta/classes-global/sstate.bbclass @@ -1119,11 +1119,28 @@ def setscene_depvalid(task, taskdependees, notneeded, d, log=None): logit("Considering setscene task: %s" % (str(taskdependees[task])), log) - directtasks = ["do_populate_lic", "do_deploy_source_date_epoch", "do_shared_workdir", "do_stash_locale", "do_gcc_stash_builddir", "do_create_spdx", "do_create_recipe_spdx", "do_deploy_archives"] + directtasks = ["do_populate_lic", "do_deploy_source_date_epoch", "do_shared_workdir", "do_stash_locale", "do_gcc_stash_builddir", "do_deploy_archives"] + + # SPDX tasks are only needed if depended on directly, unless they are + # needed by another task that is creating SPDX documents + SPDX_TASKS = {"do_create_spdx", "do_create_recipe_spdx", "do_create_package_spdx"} def isNativeCross(x): return x.endswith("-native") or "-cross-" in x or "-crosssdk" in x or x.endswith("-cross") + def isSpdxTask(key): + taskname = taskdependees[key][1] + # Tasks that start with "do_deploy" might be included in + # SPDX_DEPLOY_TASKS and should be kept + if taskname.startswith("do_deploy"): + return True + + # Tasks that are don't start with do_deploy, but still deploy SPDX data + if taskname in {"do_image_complete", "do_populate_sdk", "do_populate_sdk_ext"}: + return True + + return taskname in SPDX_TASKS + # We only need to trigger deploy_source_date_epoch through direct dependencies if taskdependees[task][1] in directtasks: return True @@ -1136,6 +1153,8 @@ def setscene_depvalid(task, taskdependees, notneeded, d, log=None): return False return True + spdx_deps = [] + for dep in taskdependees: logit(" considering dependency: %s" % (str(taskdependees[dep])), log) if task == dep: @@ -1192,12 +1211,27 @@ def setscene_depvalid(task, taskdependees, notneeded, d, log=None): # Target populate_sysroot need their dependencies return False - if taskdependees[dep][1] in directtasks: + # Collect dependees that create SPDX documents + if isSpdxTask(dep): + spdx_deps.append(dep) + continue + + if taskdependees[dep][1] in directtasks or taskdependees[dep][1] in SPDX_TASKS: continue # Safe fallthrough default logit(" Default setscene dependency fall through due to dependency: %s" % (str(taskdependees[dep])), log) return False + + # SPDX generating tasks can refer to the SPDXID from the SPDX output of + # their dependencies. Therefore if the current task can generate SPDX and + # is depended on by another SPDX generating task, it must be kept so that + # the IDs can be correctly resolved when merging SPDX output into a + # complete SBoM + if spdx_deps and isSpdxTask(task): + logit(f"Keeping SPDX task {taskdependees[task]} needed by SPDX task(s) {', '.join(str(taskdependees[k]) for k in spdx_deps)}", log) + return False + return True addhandler sstate_eventhandler From patchwork Wed Jun 24 14:15:22 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 90851 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 578AFCDE004 for ; Wed, 24 Jun 2026 14:17:18 +0000 (UTC) Received: from mail-oo1-f42.google.com (mail-oo1-f42.google.com [209.85.161.42]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.8224.1782310634479241455 for ; Wed, 24 Jun 2026 07:17:14 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=AIyBtUgb; spf=pass (domain: gmail.com, ip: 209.85.161.42, mailfrom: jpewhacker@gmail.com) Received: by mail-oo1-f42.google.com with SMTP id 006d021491bc7-6a0ddedcd00so498064eaf.1 for ; Wed, 24 Jun 2026 07:17:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1782310634; x=1782915434; 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=htmmbviTFKFO6CGnAjLliWc0qZi9TjaXMmIgPhE5RD0=; b=AIyBtUgbbIDbv3rkr63fMDZ2hrcFIsl1BqPkc0C7xAZttwI/EIOqU587fLItTwx1Ll mr3u11EatXQBDQuVBpbiBCF7EtQLzfcQbf3YkSfV5h76tEyO7Gc+pH7uDdS+DD2Xls3C sE/pqok5x3HeDMy/W+UTZBSL43KWrntU7tXrGOX03Xar86rZUXdH7Zg8OrGepbJWZD1Z JgC6G+pJL2jVFIWoDukd200WI8QHhKYYvnRndOXFbbEKQUSR9jjUoVKO4+jeEwFNqpxh aGzylmv0OreIaybYFv25BL/AyjM9nr4clSrGK1pN8JHFBr8m4TP21wj17K487xT1P52R Qqiw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1782310634; x=1782915434; 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=htmmbviTFKFO6CGnAjLliWc0qZi9TjaXMmIgPhE5RD0=; b=oyBLT4zc97ideBvRwBSwzJVTUBqGwI+fJbnf0lVN+x7b4QdKxF20dAx0rvwRmiNVkD ssRk4hxDcNSGC0BqJC/k8eow//NxEOwo6LfI5Wx12urHmNZAYMSw8PAWc7u2UIXZ+0Jv LGnX6UdCFtm+oTC/6qK5fTrWUoLOBZSFFIyvfOggDxitVUP2lGVN9gCTluQIk8kAMVew ZXf1Ss1EBtJgYzuF2oIIQ14I79YOby+VrwA9UpDCnhQtIhErLdhcJNjSM042HfuQ+MCg PbmEF8I/6n2Ydr9fXR8FQIcZRTbIH9kgnSkurOV7jP6ia+4XMjGyk0i2d7EWHDE8Gjot cqIQ== X-Gm-Message-State: AOJu0YwOPkn6U2oDy7BWoC2og6gdP+qiBNwUSU+2Jrfq9PEfODmG33Jj iB/WwfcY+YtRNb8uvd7mzxVLpAcHLnbkJc6tZj2JjZbjFEd0tlcWMOwWj2jfXQ== X-Gm-Gg: AfdE7cldcoNTf+MuF19o4XAyOANnUPvJRynpx1UVsnBNm+OpXJUXfaXXKinCU4j+EPB zOEDSVoi3tTt5iQg5KHe29SRDwk1nMNO4yBExsSiHy1mPFZAiajinK7Xww0/ZU84SFVndF/RRDd 0x10C4rZNSXC+DZ+9cNU/KWR9TWrJxah5dl+016GwjCaC3wqu22hFeLyB3iCnaM/QEXdChjMJrb TKd24pO4Qe94UQCX0nzG87PjgZewjYVpz1vGgtzGh7/oA1sspjbKlAW+wrmm8tCSNG5Q1bJs+Iy gItMNX+bZLPutXffru42STJkmimEgBRD9O3kVW4ZriUW/Ve5ot7Lo+3+LedbbxaEqSHWekpPFxf nEVsB62vUd3mge6DqJ53BREarzCS6FPwYwyGjCzLGn3fAF7/O9eJw4nfiKPFoGA2ctCT86i2KS1 21t3mn0M+S+w== X-Received: by 2002:a05:6820:169f:b0:6a0:df5e:7f64 with SMTP id 006d021491bc7-6a123027f3dmr2324136eaf.49.1782310633608; Wed, 24 Jun 2026 07:17:13 -0700 (PDT) Received: from localhost.localdomain ([2601:283:4b02:22d0::87cd]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6a0e9f2a4e9sm8946149eaf.2.2026.06.24.07.17.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Jun 2026 07:17:13 -0700 (PDT) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: Joshua Watt Subject: [OE-core][PATCH v3 5/8] Add SPDX deploy tasks to various recipes Date: Wed, 24 Jun 2026 08:15:22 -0600 Message-ID: <20260624141706.2164567-6-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260624141706.2164567-1-JPEWhacker@gmail.com> References: <20260618165032.347436-1-JPEWhacker@gmail.com> <20260624141706.2164567-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 ; Wed, 24 Jun 2026 14:17:18 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/239514 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 | 1 + meta/classes-recipe/devicetree.bbclass | 1 + meta/classes-recipe/kernel-fit-image.bbclass | 1 + meta/classes-recipe/kernel.bbclass | 1 + meta/recipes-bsp/grub/grub-efi_2.14.bb | 1 + meta/recipes-bsp/opensbi/opensbi_1.8.1.bb | 1 + meta/recipes-bsp/u-boot/u-boot.inc | 1 + meta/recipes-core/systemd/systemd-boot_259.5.bb | 2 +- 8 files changed, 8 insertions(+), 1 deletion(-) diff --git a/meta/classes-recipe/barebox.bbclass b/meta/classes-recipe/barebox.bbclass index 2411fb5caa..60437c1ad1 100644 --- a/meta/classes-recipe/barebox.bbclass +++ b/meta/classes-recipe/barebox.bbclass @@ -158,5 +158,6 @@ barebox_do_deploy () { fi } addtask deploy after do_compile +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..35c2499bdb 100644 --- a/meta/classes-recipe/devicetree.bbclass +++ b/meta/classes-recipe/devicetree.bbclass @@ -164,6 +164,7 @@ devicetree_do_deploy() { done } addtask deploy before do_build after do_install +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..5115482eff 100644 --- a/meta/classes-recipe/kernel-fit-image.bbclass +++ b/meta/classes-recipe/kernel-fit-image.bbclass @@ -241,3 +241,4 @@ do_deploy() { fi } addtask deploy after do_compile before do_build +SPDX_DEPLOY_TASKS += "do_deploy" diff --git a/meta/classes-recipe/kernel.bbclass b/meta/classes-recipe/kernel.bbclass index 50cef17f69..8d0cb91688 100644 --- a/meta/classes-recipe/kernel.bbclass +++ b/meta/classes-recipe/kernel.bbclass @@ -842,6 +842,7 @@ kernel_do_deploy() { do_deploy[prefuncs] += "read_subpackage_metadata" addtask deploy after do_install do_populate_sysroot do_packagedata +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..e535d99710 100644 --- a/meta/recipes-bsp/grub/grub-efi_2.14.bb +++ b/meta/recipes-bsp/grub/grub-efi_2.14.bb @@ -97,6 +97,7 @@ do_deploy() { } addtask deploy after do_install 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..93646a97df 100644 --- a/meta/recipes-bsp/opensbi/opensbi_1.8.1.bb +++ b/meta/recipes-bsp/opensbi/opensbi_1.8.1.bb @@ -45,6 +45,7 @@ do_deploy () { } addtask deploy before do_build after do_install +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..acc2bf9819 100644 --- a/meta/recipes-bsp/u-boot/u-boot.inc +++ b/meta/recipes-bsp/u-boot/u-boot.inc @@ -471,3 +471,4 @@ uboot_deploy_spl () { } addtask deploy before do_build after do_compile +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..c887835806 100644 --- a/meta/recipes-core/systemd/systemd-boot_259.5.bb +++ b/meta/recipes-core/systemd/systemd-boot_259.5.bb @@ -72,4 +72,4 @@ do_deploy () { } addtask deploy before do_build after do_compile - +SPDX_DEPLOY_TASKS += "do_deploy" From patchwork Wed Jun 24 14:15:23 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 90852 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 20EE1CDE003 for ; Wed, 24 Jun 2026 14:17:18 +0000 (UTC) Received: from mail-oo1-f45.google.com (mail-oo1-f45.google.com [209.85.161.45]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.8327.1782310635416988955 for ; Wed, 24 Jun 2026 07:17:15 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=njxEoGbw; spf=pass (domain: gmail.com, ip: 209.85.161.45, mailfrom: jpewhacker@gmail.com) Received: by mail-oo1-f45.google.com with SMTP id 006d021491bc7-69d7cdd3b8eso492680eaf.2 for ; Wed, 24 Jun 2026 07:17:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1782310634; x=1782915434; 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=+1rDxp64fdPgbXM5Zf3bNxtr08INlnOQhd4YktZ+4OA=; b=njxEoGbwNmIJ/Rx2E2ZQ9c8Z4VJ7KIKzt/gRXnM2JrXDwV5Nzewcj4BWocs7EbVa0Z MF0fzaw5mpIzRKFIYW6n1ukXbJkPiXFVwYVVpWcX0Ds1Du17/aHTP8Ym+pWpNDvHMSe+ TMmxixX/aWDMPNvly07m9U1XYzwMijqK788fjZ3H9tr8bw5teVbbyEAAutYoTeYTp6+L cSgP4ekBh7usMc9vCGfvbvh9CgjfyVWybUlpGQu0ozlgwyHk2EkyLRzqfDt3SCS1Z1z0 GOtiZHDRNCb/UyfU8ymMr4AuB/emlVvGjIW6+q+yJCqGJeS/BTysFYhC/u82g7LG3I43 orRA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1782310634; x=1782915434; 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=+1rDxp64fdPgbXM5Zf3bNxtr08INlnOQhd4YktZ+4OA=; b=njM8Lm+uZgoMp3iz98EhH2y5oKZzqD0d8oPi4uk5CBfkCLrI6uRFRKmxtrnI/Twlr+ NQPU+rx3SSTEgIDvoMRLzZS7o8rBe++fMBKCE5k9V/Jz9rX5pRpWIy4yjma8RNu6fT0K gRgRDKOesePZ1aofUsn0c9E0+95ZGFryRh5+WwTvTdh7oGYQgI1KPDrMBAQTmIf/XHH2 RI+jctFkpFhYWxINgJtjQip3y82CjSTBijPDSWXRSZPp6Z3lfrFdIwxxW7ECaoVHkQdd X89fMC+qAZBO8WVU8hkij6EHjPralnqgrwRcgQtnqhDJG8FEscGQNN/ET4dRTGL4Bm8v /uKw== X-Gm-Message-State: AOJu0YxARy0QZTjCh9LOXr00b36URznkuKjH8l189QrvlRXSvT8GzQoC fREvsKpI9x7Qe8rrbOyC//UsomkR/YUmjfnX+FFCnnFx4w22Dck7EbAn1RlOEw== X-Gm-Gg: AfdE7cnt1GiJcPSq5wPdGB7VSXBdlkOBpOsdXGh0XDLuBCT/13dwaJoktF8P94x0Pzd hTlKYXYT5inGZuVZubUc6ur9y9m1mHIFMUBXlPBn0fVN74YgFQaWJSCwMI+fU4pPnRBOt2OyFVq LS9D18d13qd/1b0PCL7B8WK49F9JwTxd9ydi5EYMXUjn5ApgYauPd+7V60xqmKcdBC7BYXU6vsQ KJB/zxU4oEbofsWfaBofDU4dfp/Oo1GI9Q68jolI7fgglPwgCaxExaa5yUXrJsdVxkKMkzEd3aq Sw3dRC+2u+y+WdLvs9UqYg3k4WGrDcHnGbxyTENVUCutYyJR1ycYWv6zlWe0RQlcr18DhvYjeb6 DQyBS67Lx2bG0oljDrv9s+dUnm1eWdioW1SD1IcP53mjf52nGAPJ0JIZGzZGk4yrQKKeF4nMstd 5TzC2JHTPxtA== X-Received: by 2002:a05:6820:2211:b0:6a1:1a19:3f9c with SMTP id 006d021491bc7-6a123029614mr2309389eaf.54.1782310634492; Wed, 24 Jun 2026 07:17:14 -0700 (PDT) Received: from localhost.localdomain ([2601:283:4b02:22d0::87cd]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6a0e9f2a4e9sm8946149eaf.2.2026.06.24.07.17.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Jun 2026 07:17:13 -0700 (PDT) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: Joshua Watt Subject: [OE-core][PATCH v3 6/8] spdx: Replace do_create_image_spdx with deploy task Date: Wed, 24 Jun 2026 08:15:23 -0600 Message-ID: <20260624141706.2164567-7-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260624141706.2164567-1-JPEWhacker@gmail.com> References: <20260618165032.347436-1-JPEWhacker@gmail.com> <20260624141706.2164567-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 ; Wed, 24 Jun 2026 14:17:18 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/239515 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 | 32 +++++++------------ 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, 26 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..838a82c802 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,25 @@ 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" +# No deploy sbom is needed since do_create_image_sbom_spdx() is used instead +SPDX_DEPLOY_SBOM = "0" 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 13d1de2774..919de094f8 100644 --- a/meta/classes/create-spdx-3.0.bbclass +++ b/meta/classes/create-spdx-3.0.bbclass @@ -373,9 +373,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 c58c8c30e1..79c608d8e5 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py @@ -1540,19 +1540,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) @@ -1645,13 +1645,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") @@ -1673,7 +1678,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))