From patchwork Mon Oct 26 21:45: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: 102018 Received: from mail-oa1-f47.google.com (mail-oa1-f47.google.com [209.85.160.47]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.5896.1793051191597547841 for ; Mon, 26 Oct 2026 14:46:31 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=LpkpNyN3; spf=pass (domain: gmail.com, ip: 209.85.160.47, mailfrom: jpewhacker@gmail.com) Received: by mail-oa1-f47.google.com with SMTP id 586e51a60fabf-499f20c7054so1787871fac.0 for ; Mon, 26 Oct 2026 14:46:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1793051190; x=1793655990; 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:content-type; bh=gIS2bnYzcbQkMxQgoekph5CH8vdF5nI/voXUkG5onsE=; b=LpkpNyN3/Uim1gyA68s0KFyBPLn6CCTrYgpR1FlC/oh+Su97EekipYFsD2FHx872pf C+ZSCKtA19zQYTsVgl+FA6+p/mr1kjEgS28RpdrkNASR4oInnqgzntF359wzgierZoub lq/Mw8DZA43vzZ/F9qdW/0KUqDLr6chNyu7IYcCuUIMbGZtKW5G4pSq3YVpmYIG7dRN7 I8Fp2lXbYRjxa4qynRxDsMc6yVy7uIRh1okDO/5yTOrmVjetgPK9FDNU2HjSKDL9f26h di8N9cLu8W2BM8v6IR0aPeh9NmuYByBTU48tZ85ltXse0tSEJmL9Bzoy3v5GC0kjXEg9 rj0A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793051190; x=1793655990; 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:content-type; bh=gIS2bnYzcbQkMxQgoekph5CH8vdF5nI/voXUkG5onsE=; b=jvx1WhBTa6DMptsJvXqn67DjtsfNa47CtF2mwVIu0kv9Hf+pVtuDsh/p0GQHE+Ows7 2EiD+bqfMDELz0d2RO2vcXIY2P4DdJ4XkP5hieUFYGQExdK0T0Nbhtf++UPsMbZBzD8m gmfm8VS2Gyi1GiGhbt49zlwBJw1EYUTtVjftT38LI/YjpBtQiuE2VbLxaiKvsmMauGPI 2qgO9NxNV6y5O95CwAceCPHxaXLkLcvCtDdKwVw2T1VR78NuzmSn+rigwZSqAhUJ6L5+ Rnz+xmWfPwhe+ySW39GHOzKzN5zmch4BC6fVvA+XIn64dyTE/B2HsvyA348m3NWx/Tou aqrg== X-Gm-Message-State: AFq9FYIyAasZQQUwXS+U/Z9dYg1KwIo4WsPX4KRLQ5cY3bN0DK2QE8Wl docrCHkdWXOABcZtlr3lCBwvJ8sO7kPa98jc0x03WPUOzoQDMo4FrLqA5pl42w== X-Gm-Gg: AYBFou3SjlB4dUaSEwIPJdW65jdOvCX89rreQCkyiHhb0khVX8g1xwQGn24nQELpKSv CG144uW5cBmEhwekkPk+MEaz03t6922+18DvseKUolJWVOEEmGlEmF5/ghv6qce41xdzFROzpnx SDez3vnwsHT674CuuVFtxA4f7USdArYl1QFm/uQXeEQnTUMuTs5B7AlncPBf+BBKfCMrOm+iWda IGNwdoCvPDq55LcYhaMTmVKOEX+IaW/KGu2d/IsfTPjl7YBIGcOnz8HwZ2VF+XjzbUwa8LAcJ8d u7xXoAmBla/C7uWTh536EtM48d7yaTmOgs3pJogiIfYPsBItWPZZWJ8GvYFiKXxeyQYS3e1uqCU kMqbIc3QK3gxgdiGbyJLosNVbDUq0C/C32gfOcWlBUPrnavWYHX5B5FlNhnGzCRCOsmrIa97SOC IDM7dBlXJyKG0+9Od8dxGqQ2+8QBfqEYMtvG6qEO5Tmu4vRT9r9qY3pW0kP2iw567c4tv04aqrh k5NKuRy7/wwUQ4QHTuIukUxOUrX5d9kMdYUrUdiekxLw+P+jrlGRQ== X-Received: by 2002:a05:6808:251a:b0:4b9:a829:f00e with SMTP id 5614622812f47-55c0027cbe5mr5448431b6e.26.1793051190528; Mon, 26 Oct 2026 14:46:30 -0700 (PDT) Return-Path: Received: from localhost.localdomain ([2601:283:4b01:ba50::283f]) by smtp.gmail.com with ESMTPSA id 5614622812f47-55c0424a05dsm6304219b6e.4.2026.10.26.14.46.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 26 Oct 2026 14:46:30 -0700 (PDT) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: Joshua Watt Subject: [OE-core][PATCH v5 1/6] spdx: Skip dependencies that are not in the taskhash Date: Mon, 26 Oct 2026 15:45:21 -0600 Message-ID: <20261026214627.2388650-2-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261026214627.2388650-1-JPEWhacker@gmail.com> References: <20260630210422.1903245-1-JPEWhacker@gmail.com> <20261026214627.2388650-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 lin-tor-korg-lkml-alma10-1 with HTTPS for ; Mon, 26 Oct 2026 21:46:37 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/248694 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 1d11d8d1d2..3dd87c2a75 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py @@ -306,13 +306,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 Mon Oct 26 21:45: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: 102013 Received: from mail-oi1-f171.google.com (mail-oi1-f171.google.com [209.85.167.171]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.5897.1793051192594689359 for ; Mon, 26 Oct 2026 14:46:32 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=ad7VO4qC; spf=pass (domain: gmail.com, ip: 209.85.167.171, mailfrom: jpewhacker@gmail.com) Received: by mail-oi1-f171.google.com with SMTP id 5614622812f47-53656465d1dso1804802b6e.2 for ; Mon, 26 Oct 2026 14:46:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1793051192; x=1793655992; 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:content-type; bh=GkHE/er36Hqv89KTCOB7k6z8xrYAAh2+zMxhj+bKTYE=; b=ad7VO4qC41XVd+QfnveKFSBxjZgsCg0tTSnhRJPw41DlblqWxuBtyu+5RXX3i4dNTj ewlAJJdxSr5DIzaWUBUKg1HpQ8tNDj69ARyCqMAAXsjDUX+mz9tqjMnavJ1OVx8nOE2o PGl+O2jIfDc/woQ08Blm2EDpG6aQA0KaOHO/lJtUDBdwkbEZphHIlpHUoFNPIwHtngWe nOQAdJo57eGVB4cn+e3q8uQpi5viEsFh6d1OoAdHK0vhjyTrcxadmv8ZWDH+x1i7t3dw JP3AQuMWdaQZfHtHsajB0znV9PixlORurCVMvFEenifjxB9sj8LmIn9trvUVexXuy2+p +YxQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793051192; x=1793655992; 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:content-type; bh=GkHE/er36Hqv89KTCOB7k6z8xrYAAh2+zMxhj+bKTYE=; b=lsf9Klk3F44JCthTpX3mBBseQmMuSoeXWjsjtYQUDl84PPc2K//EFCfiCiqYt3SXJn 7TrbVpHz10r1M0RtRtpbyzEMggEQrtv3/+QpOr9clRsaPc6U/XCHLMFVv/LTruE32taJ Xy/KQ4CjMm2l59wNWJGwcFhKsxDlTY9lL5aGXQOCSieK4RdbGqkiS9ywSVsIsboLjeZu sKTNClvnmUwA9Mwu0EmGMVgyy1MY+okCwm14TjSFlwVzZwRhVsA+jrJu+h0P24/t3ZY7 Oh45EE9NfOwQOWoHkJun2QNsoD34PAd0JtQTqaW6qd2KJRgH3R7Ki12cNrqtOkaM9C6/ MbDw== X-Gm-Message-State: AFq9FYJYZJkmbZF4QueUXbkUxQoTFCGkvfW30L+U27/5nCUXMqmsq6mT U0tiEdhhMuHiEtRP+QyS3fMlnIIgvhErggXODi4FNxtCmVfqw5C3Gyb2V9aVVg== X-Gm-Gg: AYBFou1sQDjN/7VpjucM5NjDolH/L+ZYoXd9OhPTeqDGf5KvTnNkExO/SOLwGZ9dBv6 7UDY9YL4R4uVMqwQwSPbsMHL+UYqbsXSYR7+NeERDJtSI2C+3PVz+c6QO3B1VJUjTGnOOgpBnQK Xzac4EB7sW78Q3LBgzvz+BHG8dgdqdpY1asmoQur28AvVXwFFtAPROxFlYGJE8qNX/w39w9Rwkb zT1w92+PyfvsoYuWxG5kKoLmsFxsVQTKtVoVvr9j6TKAa2ZQ3HTTu6g0io8U+LgLApMmACtouVj G0KqkWQnbosAKMg7RAzNbRq8RPaCwKoR4yRbzN5iA1JVMWhxzggkDu+gZh0UhHD1ENsHQ2JTDwG +54nxdocsBcNjmx6JudI8wZdj1EmQAs29aD3HbAH3LvfQ54CWeAwZMwj98MBs6gMkAmAYirJS7U DflaBACF6hM9YnX6lhVd5YU/VgmgGEJF9MepxpQRU0SrI7SGs7buh5I95zQ0scKSF29gqIvDjXo BCUbWsDhtApTMt8fwPbWA4E9ziqE46wXVm+5yBNlQDU6h7952yj/oc= X-Received: by 2002:a05:6808:1798:b0:4fb:6996:aac9 with SMTP id 5614622812f47-55c0030c83amr5269557b6e.35.1793051191538; Mon, 26 Oct 2026 14:46:31 -0700 (PDT) Return-Path: Received: from localhost.localdomain ([2601:283:4b01:ba50::283f]) by smtp.gmail.com with ESMTPSA id 5614622812f47-55c0424a05dsm6304219b6e.4.2026.10.26.14.46.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 26 Oct 2026 14:46:31 -0700 (PDT) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: Joshua Watt Subject: [OE-core][PATCH v5 2/6] spdx: Add ability for deploy tasks to create SPDX Date: Mon, 26 Oct 2026 15:45:22 -0600 Message-ID: <20261026214627.2388650-3-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261026214627.2388650-1-JPEWhacker@gmail.com> References: <20260630210422.1903245-1-JPEWhacker@gmail.com> <20261026214627.2388650-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 lin-tor-korg-lkml-alma10-1 with HTTPS for ; Mon, 26 Oct 2026 21:46:36 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/248695 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 | 181 +++++++++++ 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, 453 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 cf79ef5b01..b60cdd826f 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 6ccb93ba01..723194da2d 100644 --- a/meta/classes-recipe/nospdx.bbclass +++ b/meta/classes-recipe/nospdx.bbclass @@ -12,3 +12,4 @@ deltask do_create_package_spdx deltask do_create_rootfs_spdx deltask do_create_image_spdx deltask do_create_image_sbom_spdx +deltask do_create_deploy_sbom diff --git a/meta/classes/create-spdx-3.0.bbclass b/meta/classes/create-spdx-3.0.bbclass index 6d47aa1301..6077105094 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" @@ -293,3 +336,141 @@ 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() + + new_postfuncs = [] + while True: + if not postfuncs or before_postfunc(postfuncs[0]): + break + new_postfuncs.append(postfuncs.pop(0)) + new_postfuncs.append(func) + new_postfuncs.extend(postfuncs) + + d.setVarFlag(task, "postfuncs", " ".join(new_postfuncs)) + 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 b33860e4aa..2d3b54069c 100644 --- a/meta/lib/oe/sbom30.py +++ b/meta/lib/oe/sbom30.py @@ -1054,6 +1054,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, @@ -1061,6 +1080,7 @@ def write_recipe_jsonld_doc( deploydir, *, create_spdx_id_links=True, + create_task_link=False, ): pkg_arch = d.getVar("SSTATE_PKGARCH") @@ -1068,23 +1088,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() @@ -1100,6 +1104,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 3dd87c2a75..c1c0733508 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py @@ -598,6 +598,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") @@ -790,7 +919,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): @@ -804,17 +935,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") @@ -943,10 +1063,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) @@ -1133,7 +1255,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): @@ -1369,26 +1493,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) @@ -1421,14 +1528,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): @@ -1508,10 +1621,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, ) @@ -1532,7 +1648,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): @@ -1710,3 +1828,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 07bf9650e2..9a862d9f5a 100644 --- a/meta/lib/oe/spdx_common.py +++ b/meta/lib/oe/spdx_common.py @@ -118,7 +118,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 Mon Oct 26 21:45: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: 102015 Received: from mail-ot1-f54.google.com (mail-ot1-f54.google.com [209.85.210.54]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.5898.1793051193209008374 for ; Mon, 26 Oct 2026 14:46:33 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=nl4XTfEe; spf=pass (domain: gmail.com, ip: 209.85.210.54, mailfrom: jpewhacker@gmail.com) Received: by mail-ot1-f54.google.com with SMTP id 46e09a7af769-8490a7d6696so2093222a34.3 for ; Mon, 26 Oct 2026 14:46:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1793051192; x=1793655992; 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:content-type; bh=Z1y3RBDJoDIw4KgYSPBJM4umu5zeYQMe7A/eNaaMe0w=; b=nl4XTfEeB5raKKD1WQ75pmgmaz7gTNzfnw1cvp3QhKkWZ5KKtUpFF7Y68eJmmQBHMy jvf4Vzja1tPKjUTm2MtHA3QFebwp47YS0onTJi8PsFiUCSMgLybN+09LwOMUKcwGL745 WESOU6Peh2knYKVOSrZHhOmnB4p+pePK7A8lHV7+c581TrXi+hNuQSDS5blVLO3zmAS8 jeBKkExtsDk7M5PagyYPjqfi45TA691HJzV+dSc1+HJhTdWtfeQUEdRR9nCNksK8Ol7m C26Z3HQzj1YMjFVi0/s8Lxo+/FRIFbtDQgs9hzdUF3PV0R9YMPGAOzKeGTcIl/7v8oSa 7DQA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793051192; x=1793655992; 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:content-type; bh=Z1y3RBDJoDIw4KgYSPBJM4umu5zeYQMe7A/eNaaMe0w=; b=fyvlQdyqu6Yvj8V0AUsIqKSiy0wAKLFQtgWxQ0Mc3XszToNo7PLwoq43B2A8ojF9LW 5q5lmwwMo80LIk1oYBRhfGzwoGoeNCEJyv6rA3aIGeWCmmPesiv243QKaQimevzQB3DK XMRDsWL3ZszIZ892BXPj2vRPvUva5+iZ6/Wh3bysywUmUKuR4doez7bKOO8YDyCVhPWS yZfamJMZ6t7kvo2my7dFz7FJ1mqS+1pjKgTfyGBMG5e4hGGraj0nXrKrx76uwoD8dNsj J6hviXK6yIMWJ9YX1PE8VTxqg5UoxEr9ic6w05vwxMhJgVsSMPNa1oOKIWaJLWuTnEFQ NtAw== X-Gm-Message-State: AFq9FYJC8Il1M9PXaO89WY5V05Q/9av3TwFWmgnfkG98Yxv79AWNuh23 z5RPVx/9UVh0aqtO/m0bediETTYr0aFDz8YtoaYK/NNpdPggrdY3yoJt9qBudQ== X-Gm-Gg: AYBFou0y+iMKOt3gun38ypJiB/wVoveEU6d1N/LUqcnO/ZIpDNb0p3z9koxZlJoNp6P Q2ZqX47bxXrgcVaEZRakddkg7tByZzkOBVvldrD2X/LvFzWUjM+X+BXDoGPe+CdrtAsR8VWthvh ybnQShsNjvN3wdMZk5tbiPWG3ePZ3PJvJRcnGe4Y9NfsNI+ZF3IoAo0j5PpDMtJwSOBRC8hS+1l 4iAYeKM+hy5+1BzFH7nUCwxTZumZ9K6AL5FPDbQpnttUEpPcZBbfKmvySABGKlbJd9EfWOpAbJC XJQ526GHC99Dv4/j3p3b40TJXTv8cdq/+zUjAgEwMfHcEPEqc7HGsLjItFz22EYj0cK2saa+2vk KqnlqcyQRozhVztLI5j9Rzgxv8uRSvMHcIagrkTyXtuL5XNEC7w9smfJBCA5c0d80mphBYEqC+N 5DQmXLNoAQwkODm/8K7V6IQ2E+Ip9so/YRVMVowgc/UiE5FWfZRfqmi97Uvz9skKSrKdUyRiITH Hp2B3VRmMA0cq3j7JQ4Lp5HF+C+z36WGk0CZfV/CjGanxQOTR95g8/3ekCr6AMdRPfNpwqjQHc= X-Received: by 2002:a05:6808:4fdf:b0:4f0:a3f9:9f89 with SMTP id 5614622812f47-55c0020a86emr6247328b6e.18.1793051192353; Mon, 26 Oct 2026 14:46:32 -0700 (PDT) Return-Path: Received: from localhost.localdomain ([2601:283:4b01:ba50::283f]) by smtp.gmail.com with ESMTPSA id 5614622812f47-55c0424a05dsm6304219b6e.4.2026.10.26.14.46.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 26 Oct 2026 14:46:32 -0700 (PDT) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: Joshua Watt Subject: [OE-core][PATCH v5 3/6] oeqa: Add SPDX deploy SBoM test Date: Mon, 26 Oct 2026 15:45:23 -0600 Message-ID: <20261026214627.2388650-4-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261026214627.2388650-1-JPEWhacker@gmail.com> References: <20260630210422.1903245-1-JPEWhacker@gmail.com> <20261026214627.2388650-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 lin-tor-korg-lkml-alma10-1 with HTTPS for ; Mon, 26 Oct 2026 21:46:36 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/248696 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 c900558b44..8cc51c1369 100644 --- a/meta/lib/oeqa/selftest/cases/spdx.py +++ b/meta/lib/oeqa/selftest/cases/spdx.py @@ -485,3 +485,14 @@ class SPDX30Check(SPDX3CheckBase, OESelftestTestCase): for candidate in objset.foreach_type(oe.spdx30.software_Package): self.assertIsNone(candidate.releaseTime) + + 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 Mon Oct 26 21:45:24 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 102016 Received: from mail-oa1-f41.google.com (mail-oa1-f41.google.com [209.85.160.41]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.5899.1793051194056273462 for ; Mon, 26 Oct 2026 14:46:34 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=gG2Byi5I; spf=pass (domain: gmail.com, ip: 209.85.160.41, mailfrom: jpewhacker@gmail.com) Received: by mail-oa1-f41.google.com with SMTP id 586e51a60fabf-48e2d5a69c4so1973161fac.3 for ; Mon, 26 Oct 2026 14:46:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1793051193; x=1793655993; 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:content-type; bh=/2WIZpwPimqavDkzbT1ZpVYufGFe+CtcAal/HZACf5Y=; b=gG2Byi5IUAfzoDTXZVoBfrnPLEfC2LVKyv0SFCvbIeybkHD+BJl/R1H6eZvE18yZlL gwGehIuUHPzzMMAnjodNJJZXFK1f8K7axql7fW+TC5Lq3tUa8Ugz7N3vF4Avx+i+0huG 4MuwZIquFVdv3hLYwtJ5UjNm2w57wT1Yz/5ihJZ+BOdJpVUYwVTZbYEhK1dflsFLgvCU 53Z9qQVIf7ynVw3wGAvVCGydu7ozBXtAj5VkE+Itq2MSBe+9TpJPTyji2urOerLvYsRg wFeiXyH6l2xLRttMG1OoB0lq26j0nqR218jCZClnTaT/izhmRMMngFTGasV0tJ1qjVNh fKIw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793051193; x=1793655993; 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:content-type; bh=/2WIZpwPimqavDkzbT1ZpVYufGFe+CtcAal/HZACf5Y=; b=ZuG1EftFiL9wixCYy5J/j38mIDdwNWa4AxapSnSbk9iK3WfGhW4AOjszO4KE84GrBM TyQmDKXJpvUNp2GBG+sPcRGR3qJwB34TB5xOBbw1FO/3hxhkH49nm8LQXdv2zX1kf/4A kQI/X5JxWNoNiH2k0+dHndv5Il0i96++2Dist8p8DH8OLcDAxyyYw5+VmXWYCaBN5BEF 7bD6lHlzkEZKYGQS83UinCkrbVtSPVZXs8E8LS4t2WKKbgmYMVqjPSdD/3S857G9zsPW fzKg/ovvTMmpUFersn4b+y402myZFD0q0ZHxJnOw5Xyg5cfaxiV84R9MbVzl6kpfM+/q A7Aw== X-Gm-Message-State: AFq9FYIjVRv/CI3AQ/EiVzedteVQ2gGPCy734J1zRCMxv2a+54vNDmt3 5LVnHOdFqWe6xPQZYc7NYFS5TgmVa2wu22VAaP/gOrRfKHHJK6L1Ne/u/YNZQw== X-Gm-Gg: AYBFou1vBrCSZwMNSOQRpZgQeEBI5gNrYE2b+Xq5Wn2E2paIZtbzPYa0muGFFrp+plA jwYTVzEvGtiX0KNj4y/ezAnP31lzgH0jL17IVExDYENihturPqP/6bIEOOQ/rE252eFeBU8gYhl XhX/9w7P8ZFexLa584I/iH346/z5191S9T4fwAiMi6Rg9GRvja5iaFtJvFgnegWmrWliVSt6pKh SrwHQMReFAoHMQ/IK38Pcu8p2dxRPkZtBXF3QduZxsZ8zSPKgHcTkJkm1vVM5k+WgSFubcio01l jJ28sA0HqHnZyMA++iMWX2D8cmSJFdQ1QEh9ZB2vsxr1pMY+wynHxIQgqUdEesPrFVZUGgz6bGZ cYnhhV54P3frJ5FKcX53cMCaiHuhDBU+KOj9lo0NOTg4fGi4se6GjlFgf1LYy33WiE75aQ11ZSh V/iZp2u8lfJVY0c31cW45+pWyDj7LTNzWpxt4AeVNqtG6+oKfh6wEGygRxcxSV/gddnQJS6eUOe CAwRtkKeHzvmgImI8q8Ly1UXL+AWfITjetKWW8Rs7eYOwUaX27oHKpfppXzt1ZFiZnt46RFgAHy wyOVwMcp36hdFlZhDAowITJtlUc= X-Received: by 2002:a05:6808:1998:b0:53b:ccda:fb8e with SMTP id 5614622812f47-55c0020a7e1mr5883834b6e.23.1793051193161; Mon, 26 Oct 2026 14:46:33 -0700 (PDT) Return-Path: Received: from localhost.localdomain ([2601:283:4b01:ba50::283f]) by smtp.gmail.com with ESMTPSA id 5614622812f47-55c0424a05dsm6304219b6e.4.2026.10.26.14.46.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 26 Oct 2026 14:46:32 -0700 (PDT) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: Joshua Watt Subject: [OE-core][PATCH v5 4/6] classes-global/sstate: Keep SPDX generating setscene dependencies Date: Mon, 26 Oct 2026 15:45:24 -0600 Message-ID: <20261026214627.2388650-5-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261026214627.2388650-1-JPEWhacker@gmail.com> References: <20260630210422.1903245-1-JPEWhacker@gmail.com> <20261026214627.2388650-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 lin-tor-korg-lkml-alma10-1 with HTTPS for ; Mon, 26 Oct 2026 21:46:37 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/248697 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 b2fa93650a..c6d9459182 100644 --- a/meta/classes-global/sstate.bbclass +++ b/meta/classes-global/sstate.bbclass @@ -1125,11 +1125,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 @@ -1142,6 +1159,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: @@ -1198,12 +1217,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 Mon Oct 26 21:45:25 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 102014 Received: from mail-oi1-f173.google.com (mail-oi1-f173.google.com [209.85.167.173]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.5900.1793051194808077230 for ; Mon, 26 Oct 2026 14:46:34 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=lk9qrrUq; spf=pass (domain: gmail.com, ip: 209.85.167.173, mailfrom: jpewhacker@gmail.com) Received: by mail-oi1-f173.google.com with SMTP id 5614622812f47-4b190cdc9feso1929589b6e.0 for ; Mon, 26 Oct 2026 14:46:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1793051194; x=1793655994; 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:content-type; bh=i+ouQaMFgDYI3fQrwZTzLr1CA7PrKoLTGWyvwbX5LxU=; b=lk9qrrUqttJmKrVuFd5xPnf7kAdR+b985vfrgM0T7vZj2AW2XyOSfwp7JRmK6q5X2/ H8mSWfSPHEaBxetSdRBZrDX8EMdI/6SYA3icOUerdw+6sl1tBM3BrYLViP32J+Grl36D QBPc0+FYdqfO3X2zE6q9lGfmDVAUSftAI9iloEBE7S+eb1OfubbMXTbenN/OJPQWXn4y jL95Vu3+XI/2c/8YceRRRUOBAIKNyX+qpv6JVFuy8tSrP5YdRCU3eoZ8hE2sGGUdBm/n 82RWBcykdBDPPs2Ua6d0Qt0K5eMQ1LVzY+5pgnGV2JaAXnZtrkPBpI7N0/xNtvHu7FhA KVrA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793051194; x=1793655994; 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:content-type; bh=i+ouQaMFgDYI3fQrwZTzLr1CA7PrKoLTGWyvwbX5LxU=; b=xe7eiWdFjo0MlUQwmu2dilgA845H9ES46QGJ60gjcLGXGCCs2zKhjJh3F2mCxckz0t rakemjgSvCZIoAaHakzeGLQDwn8SCj2SqOi9KgtJtg3iPCeEDoise/IIXB9oX45QhoUj Co9GhFWNfjwc7OUTeErvPqiH/k87CgP9rZmL6XsCUhBBfkINe1Xe8IXjleG9ou4eozfn 6wuqf+TZNGg+K/4jIlJBrDJffIu5sA94Xl9UiWbEEUHOvJ8UAuwlh/l+Y3xXSOi3+/Mj hc49i6T5BfXk45aZ6Xo2Wbb5YfGOwrBIFYe/XkDjlF1ybwQoUh5gYapN/BBg7kQhWxpe qtJA== X-Gm-Message-State: AFq9FYItojeA8sLyOO2FSiyCCM+g5d949yRE8QDfEcpQPl8V2VfhE31c ZEif0fDjzoTj/I7q4ZoEXtoW2ZxgLnmexM55SCycZg3bx7TF3ehYWgqhaqeQkA== X-Gm-Gg: AYBFou0Mk5jHjdqNoaTkVD1lHT6VGqJG0Ep8NDOWjmtdMYuQLjjuhAmnaK2SAjq6P46 Xr0RraqfS4KMOEFObFw3BWS1iXCquYCbg3YIBgsfQrUPj9Jz0F0V1FBLEQV7Kb7l09H6QNnXxSM EfZPGHocY04Mrv1lX+X2jnOnAM+b0UUbuo7qpng06eTO0tmSSQwRKe3v6MFHI9WOkfd02NaSa23 5fir/8/q/aXqppMZe6BZ4sexU4fIXT8FblbhCS+BKnVBQ04JWj7w0Lt+u3i1fMttB8Q2YPt2Yls ahqBbdc4eRfjuvQD8Flm3lgYRVvNQ3cByRckCv6seJn3Udi+tndnvTW/uBcc+gkJHF/+YrQdFPd JoCjtsV/NNGu1ni4llNh9yThy1VVWt1wJTSsPpB8AkoVBSUKCQYqV2H97rjn0OcHqE/jXIfgXmB iw8hcdE6YC/eiCHeCpzLFOPRxt1y9IukV6G5r/vzATH5RvtU8CnWv9Dz+Y8H4Welj9/OXKPJ1yW HXKRJs9/4BbYFvW3HgxAozUmexJNrct0EaetvRRMZVdg+3Zu7DMht4kPCKuSUH2m+/LRU2giJI= X-Received: by 2002:a05:6808:4f14:b0:4cb:f21b:6f8e with SMTP id 5614622812f47-55c001f9e4dmr5599653b6e.33.1793051193896; Mon, 26 Oct 2026 14:46:33 -0700 (PDT) Return-Path: Received: from localhost.localdomain ([2601:283:4b01:ba50::283f]) by smtp.gmail.com with ESMTPSA id 5614622812f47-55c0424a05dsm6304219b6e.4.2026.10.26.14.46.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 26 Oct 2026 14:46:33 -0700 (PDT) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: Joshua Watt Subject: [OE-core][PATCH v5 5/6] Add SPDX deploy tasks to various recipes Date: Mon, 26 Oct 2026 15:45:25 -0600 Message-ID: <20261026214627.2388650-6-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261026214627.2388650-1-JPEWhacker@gmail.com> References: <20260630210422.1903245-1-JPEWhacker@gmail.com> <20261026214627.2388650-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 lin-tor-korg-lkml-alma10-1 with HTTPS for ; Mon, 26 Oct 2026 21:46:36 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/248698 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.9.bb | 1 + meta/recipes-bsp/u-boot/u-boot.inc | 1 + meta/recipes-core/systemd/systemd-boot_262.bb | 2 +- 8 files changed, 8 insertions(+), 1 deletion(-) diff --git a/meta/classes-recipe/barebox.bbclass b/meta/classes-recipe/barebox.bbclass index 3980849b53..252e97fa65 100644 --- a/meta/classes-recipe/barebox.bbclass +++ b/meta/classes-recipe/barebox.bbclass @@ -165,5 +165,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 4fe389bed8..affbf115c2 100644 --- a/meta/classes-recipe/devicetree.bbclass +++ b/meta/classes-recipe/devicetree.bbclass @@ -166,6 +166,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 7b2bcd4673..d0f7274e70 100644 --- a/meta/classes-recipe/kernel-fit-image.bbclass +++ b/meta/classes-recipe/kernel-fit-image.bbclass @@ -260,3 +260,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 0a6d754108..14c71dec54 100644 --- a/meta/classes-recipe/kernel.bbclass +++ b/meta/classes-recipe/kernel.bbclass @@ -840,6 +840,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 d19b33b504..8d8671614a 100644 --- a/meta/recipes-bsp/grub/grub-efi_2.14.bb +++ b/meta/recipes-bsp/grub/grub-efi_2.14.bb @@ -99,6 +99,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.9.bb b/meta/recipes-bsp/opensbi/opensbi_1.9.bb index 96167f7c73..bb35f906b2 100644 --- a/meta/recipes-bsp/opensbi/opensbi_1.9.bb +++ b/meta/recipes-bsp/opensbi/opensbi_1.9.bb @@ -49,6 +49,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 b9f61a185c..1c9911ff6e 100644 --- a/meta/recipes-bsp/u-boot/u-boot.inc +++ b/meta/recipes-bsp/u-boot/u-boot.inc @@ -474,3 +474,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_262.bb b/meta/recipes-core/systemd/systemd-boot_262.bb index bab2e7ae27..c9bc3e2a91 100644 --- a/meta/recipes-core/systemd/systemd-boot_262.bb +++ b/meta/recipes-core/systemd/systemd-boot_262.bb @@ -71,4 +71,4 @@ do_deploy () { } addtask deploy before do_build after do_compile - +SPDX_DEPLOY_TASKS += "do_deploy" From patchwork Mon Oct 26 21:45:26 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 102017 Received: from mail-oa1-f42.google.com (mail-oa1-f42.google.com [209.85.160.42]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.6068.1793051195747913951 for ; Mon, 26 Oct 2026 14:46:35 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=oSbwVeu0; spf=pass (domain: gmail.com, ip: 209.85.160.42, mailfrom: jpewhacker@gmail.com) Received: by mail-oa1-f42.google.com with SMTP id 586e51a60fabf-48eee44b908so2107201fac.1 for ; Mon, 26 Oct 2026 14:46:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1793051195; x=1793655995; 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:content-type; bh=/ixwZywXXbhp5HqkpDBB0XHk0fgGBqlbqBA8TTGVPvA=; b=oSbwVeu02QtWd/k6UePS1x3V3WJBrTdy5HnyjCMCevy8MmSIVJOeIflFudF+nnvd24 XeHK62TqEQSHMV5Pe09/n34SgIqcRORKLtPqWkbl0WfXr/m9WKloFjDgN2U0+aNpqV4l hR0mlZMY/GwhVjacR3/769yzLEGjgs0c74aHPoszsJxJp6oSHD8tBXp13iJ+47dDJ/vm 0kayE5zjNy3nn6Jjh58MmucRdJRer+g8Dv2Gakf88obp99lrFOPDZcfE/586TRNEeqaU rjKB6Ui+YndgaP3FKsub99ZJAOULseuR4apGO/HdG/gxTHNBjJAakarpdZvkZUWJBSsK MAww== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793051195; x=1793655995; 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:content-type; bh=/ixwZywXXbhp5HqkpDBB0XHk0fgGBqlbqBA8TTGVPvA=; b=hMHWQKkyrUuB+q2DDXtLa88UM0ZsjqePp9/jM3fKwRWb0vCdKC+XA4vkAeMeR2KFMs 0j1tBkYyxeB71jm9OSXmv3sY5rm9fk+B/U9fL8AsXzQ8Qj+dcN3TAQarahe9b2N87XYC fBhmo9cJ+kYYrquvvTmDawwfR3nR2bjPBtmFPYAt6Y3T+bxtA+iuwjh2SVIZHi9lDmB/ 27NSi3VqYyErwbTkRtrWt2h0CjutlQxYi3wub+t8D5A2RSGbiU3mL3MNxHBp7jNZxS4b 0jgk9uwtZeDZEq7ZDhLVZqC+Y3EgJhoqNfYazoOO4joAD5j6dot221DPeUrB6tgifJSe i09w== X-Gm-Message-State: AFq9FYLK3egJlR6G+ELID7J+T4R9A4mOPNzocSbpK2alACjUWckFHU9N zYtkqeG8AdOgx3udUHk0+5LV9IGG4Yr2zabvG/+NrRmapUrCYsRzCzP3ZEvR/Q== X-Gm-Gg: AYBFou02st4gkMi6mWe69MIjdZ3b1LnYkAwALrYGIpHbCi1uWNEHRSypbbBS+y5MHEX UmiKpcqYwCuqptfJPCFo58qjgJLSNpf5k+awtLgbb2p70ZJL2RZgxs9XjDzCkWoTMCYxl6oH2oK jaMx0Igi3zmX8Jh6EoCTVtUcAXUnu7PZugwdgaVBTSIFDTcSftwXF6NZ89CqcFCuCBL1BGyJAe4 B3b2xSDj4BNVmDVyFCwu4KBC1gXvHe0EUiF4410+DoPKoiGOaLY7xl39AImUn21vv4HJ1q1r72L y58wcglOu0LTEsc9JpO3TvbjZkXo9rfoZxAxJpH9yaCFdireq55UMXN+Vv9T9l+5WPwnAcFUNGV GUGIYrVrqDMf2CtjK/TgZpxsm/S69Yy0PfiiITRmi0jKWcB4xYlD19oU7s6zWeNTav7WZADOvcy ATSJO4c8/KHHO3EpuKatlfWoh38Ad3eYGcbe7BU7nJtZVfES3/QJQp6VJ/Nn5GqL79j+I5fmv6D jTettQa4Ns6nbxu2XfxRiuDoqm0lsMzyjGGfPwaYJTk7u5aDyIjng== X-Received: by 2002:a05:6808:c1b4:b0:4b9:a8ac:479 with SMTP id 5614622812f47-55c0026d2c6mr5659200b6e.23.1793051194732; Mon, 26 Oct 2026 14:46:34 -0700 (PDT) Return-Path: Received: from localhost.localdomain ([2601:283:4b01:ba50::283f]) by smtp.gmail.com with ESMTPSA id 5614622812f47-55c0424a05dsm6304219b6e.4.2026.10.26.14.46.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 26 Oct 2026 14:46:34 -0700 (PDT) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: Joshua Watt Subject: [OE-core][PATCH v5 6/6] spdx: Replace do_create_image_spdx with deploy task Date: Mon, 26 Oct 2026 15:45:26 -0600 Message-ID: <20261026214627.2388650-7-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261026214627.2388650-1-JPEWhacker@gmail.com> References: <20260630210422.1903245-1-JPEWhacker@gmail.com> <20261026214627.2388650-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 lin-tor-korg-lkml-alma10-1 with HTTPS for ; Mon, 26 Oct 2026 21:46:37 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/248699 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 | 23 ++++++++----- 5 files changed, 27 insertions(+), 33 deletions(-) diff --git a/meta/classes-recipe/create-spdx-image-3.0.bbclass b/meta/classes-recipe/create-spdx-image-3.0.bbclass index b60cdd826f..64cb065632 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-spdx = "1" do_create_image_sbom_spdx[sstate-inputdirs] = "${SPDXIMAGEDEPLOYDIR}" diff --git a/meta/classes-recipe/nospdx.bbclass b/meta/classes-recipe/nospdx.bbclass index 723194da2d..fafcdd0a13 100644 --- a/meta/classes-recipe/nospdx.bbclass +++ b/meta/classes-recipe/nospdx.bbclass @@ -10,6 +10,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_spdx deltask do_create_deploy_sbom diff --git a/meta/classes/create-spdx-3.0.bbclass b/meta/classes/create-spdx-3.0.bbclass index 6077105094..f79ecac153 100644 --- a/meta/classes/create-spdx-3.0.bbclass +++ b/meta/classes/create-spdx-3.0.bbclass @@ -375,9 +375,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 c1c0733508..567613cebc 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py @@ -1544,19 +1544,19 @@ def create_rootfs_spdx(d): ) -def create_image_spdx(d): +def create_image_spdx(d, spdx_deploy_dir): import oe.sbom30 - image_deploy_dir = Path(d.getVar("DEPLOY_DIR_IMAGE")) + 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) @@ -1649,13 +1649,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") @@ -1677,7 +1682,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))