* [OE-core][PATCH 0/5] Implement SPDX for deploy tasks
@ 2026-06-09 22:15 Joshua Watt
2026-06-09 22:15 ` [OE-core][PATCH 1/5] classes/baremetal-image: Remove "do_" prefix from image manifest Joshua Watt
` (5 more replies)
0 siblings, 6 replies; 11+ messages in thread
From: Joshua Watt @ 2026-06-09 22:15 UTC (permalink / raw)
To: openembedded-core; +Cc: Joshua Watt
The SPDX use case for file system image has been well defined since SPDX
was first implemented, however there has always been a desire to also
express SPDX output for other non-image deliverables (primarily, those
that have a do_deploy task or similar). These types of tasks cannot
easily use the traditional method of having a separate SPDX task that
runs to create their SPDX output as this causes lots of problems with
the way dependencies are specified. Instead, it is desirable for these
tasks to directly produce SPDX output that can be consumed by other
tasks that depend on them.
This patch series adds support for this. Any sstate task can now be
added to the SPDX_DEPLOY_TASKS list and it will run a postfunc to
generate SPDX output that describes what is being deployed. For
classical do_deploy tasks, this is setup to be easy by automatically
capturing all the deployed output files in the SPDX data, but other
tasks can be added as well.
Finally, the do_create_image_spdx task is removed and replaced with a
SPDX deploy postfunc using this new system. This means that any task
that depends on do_image_complete will automatically also get the SPDX
output for the image, simplifying the dependency handling.
Joshua Watt (5):
classes/baremetal-image: Remove "do_" prefix from image manifest
spdx: Reformat
spdx: Add ability for deploy tasks to create SPDX
Add SPDX deploy tasks
spdx: Replace do_create_image_spdx with deploy task
meta/classes-recipe/barebox.bbclass | 3 +-
meta/classes-recipe/baremetal-image.bbclass | 2 +-
.../create-spdx-image-3.0.bbclass | 30 +-
meta/classes-recipe/deploy.bbclass | 1 +
meta/classes-recipe/devicetree.bbclass | 3 +-
meta/classes-recipe/kernel-fit-image.bbclass | 3 +-
meta/classes-recipe/kernel.bbclass | 3 +-
meta/classes-recipe/nospdx.bbclass | 2 +-
meta/classes/create-spdx-3.0.bbclass | 155 +++++++
meta/classes/spdx-common.bbclass | 2 +-
meta/lib/oe/sbom30.py | 52 ++-
meta/lib/oe/spdx30_tasks.py | 377 ++++++++++++++----
meta/lib/oe/spdx_common.py | 2 +-
meta/recipes-bsp/grub/grub-efi_2.14.bb | 3 +-
meta/recipes-bsp/opensbi/opensbi_1.8.1.bb | 3 +-
meta/recipes-bsp/u-boot/u-boot.inc | 3 +-
.../systemd/systemd-boot_259.5.bb | 4 +-
17 files changed, 509 insertions(+), 139 deletions(-)
--
2.54.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [OE-core][PATCH 1/5] classes/baremetal-image: Remove "do_" prefix from image manifest
2026-06-09 22:15 [OE-core][PATCH 0/5] Implement SPDX for deploy tasks Joshua Watt
@ 2026-06-09 22:15 ` Joshua Watt
2026-06-09 22:15 ` [OE-core][PATCH 2/5] spdx: Reformat Joshua Watt
` (4 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Joshua Watt @ 2026-06-09 22:15 UTC (permalink / raw)
To: openembedded-core; +Cc: Joshua Watt
Removes the "do_" prefix from the image manifest to match the naming
convention of image.bbclass
Signed-off-by: Joshua Watt <JPEWhacker@gmail.com>
---
meta/classes-recipe/baremetal-image.bbclass | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/meta/classes-recipe/baremetal-image.bbclass b/meta/classes-recipe/baremetal-image.bbclass
index 4afc171314..7a6e71853b 100644
--- a/meta/classes-recipe/baremetal-image.bbclass
+++ b/meta/classes-recipe/baremetal-image.bbclass
@@ -45,7 +45,7 @@ python do_image_complete(){
import json
data = {
- "taskname": "do_image",
+ "taskname": "image",
"imagetype": "baremetal-image",
"images": []
}
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [OE-core][PATCH 2/5] spdx: Reformat
2026-06-09 22:15 [OE-core][PATCH 0/5] Implement SPDX for deploy tasks Joshua Watt
2026-06-09 22:15 ` [OE-core][PATCH 1/5] classes/baremetal-image: Remove "do_" prefix from image manifest Joshua Watt
@ 2026-06-09 22:15 ` Joshua Watt
2026-06-09 22:15 ` [OE-core][PATCH 3/5] spdx: Add ability for deploy tasks to create SPDX Joshua Watt
` (3 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Joshua Watt @ 2026-06-09 22:15 UTC (permalink / raw)
To: openembedded-core; +Cc: Joshua Watt
Reformats SPDX files with black
Signed-off-by: Joshua Watt <JPEWhacker@gmail.com>
---
meta/lib/oe/sbom30.py | 6 +--
meta/lib/oe/spdx30_tasks.py | 76 ++++++++++++++++++++-----------------
2 files changed, 44 insertions(+), 38 deletions(-)
diff --git a/meta/lib/oe/sbom30.py b/meta/lib/oe/sbom30.py
index b379ff947c..0926266295 100644
--- a/meta/lib/oe/sbom30.py
+++ b/meta/lib/oe/sbom30.py
@@ -712,7 +712,7 @@ class ObjectSet(oe.spdx30.SHACLObjectSet):
return self.add(v)
def new_vex_patched_relationship(self, from_, to, notes: None):
- props = {'security_statusNotes': notes} if notes else {}
+ props = {"security_statusNotes": notes} if notes else {}
return self._new_relationship(
oe.spdx30.security_VexFixedVulnAssessmentRelationship,
from_,
@@ -724,7 +724,7 @@ class ObjectSet(oe.spdx30.SHACLObjectSet):
)
def new_vex_unpatched_relationship(self, from_, to, notes: None):
- props = {'security_statusNotes': notes} if notes else {}
+ props = {"security_statusNotes": notes} if notes else {}
return self._new_relationship(
oe.spdx30.security_VexAffectedVulnAssessmentRelationship,
from_,
@@ -737,7 +737,7 @@ class ObjectSet(oe.spdx30.SHACLObjectSet):
)
def new_vex_ignored_relationship(self, from_, to, *, impact_statement, notes: None):
- props = {'security_statusNotes': notes} if notes else {}
+ props = {"security_statusNotes": notes} if notes else {}
return self._new_relationship(
oe.spdx30.security_VexNotAffectedVulnAssessmentRelationship,
from_,
diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py
index 7cc46d579b..72d17aade6 100644
--- a/meta/lib/oe/spdx30_tasks.py
+++ b/meta/lib/oe/spdx30_tasks.py
@@ -382,7 +382,6 @@ def collect_dep_sources(dep_objsets, dest):
index_sources_by_hash(e.to, dest)
-
def _generate_git_purl(d, download_location, srcrev):
"""Generate a Package URL for a Git source from its download location.
@@ -392,27 +391,29 @@ def _generate_git_purl(d, download_location, srcrev):
Returns the PURL string or None if no mapping matches.
"""
- if not download_location or not download_location.startswith('git+'):
+ if not download_location or not download_location.startswith("git+"):
return None
git_url = download_location[4:] # Remove 'git+' prefix
# Default handler: github.com
git_purl_handlers = {
- 'github.com': 'pkg:github',
+ "github.com": "pkg:github",
}
# Custom PURL mappings from SPDX_GIT_PURL_MAPPINGS
# Format: "domain1:purl_type1 domain2:purl_type2"
- custom_mappings = d.getVar('SPDX_GIT_PURL_MAPPINGS')
+ custom_mappings = d.getVar("SPDX_GIT_PURL_MAPPINGS")
if custom_mappings:
for mapping in custom_mappings.split():
- parts = mapping.split(':', 1)
+ parts = mapping.split(":", 1)
if len(parts) == 2:
git_purl_handlers[parts[0]] = parts[1]
bb.debug(2, f"Added custom Git PURL mapping: {parts[0]} -> {parts[1]}")
else:
- bb.warn(f"Invalid SPDX_GIT_PURL_MAPPINGS entry: {mapping} (expected format: domain:purl_type)")
+ bb.warn(
+ f"Invalid SPDX_GIT_PURL_MAPPINGS entry: {mapping} (expected format: domain:purl_type)"
+ )
try:
parsed = urllib.parse.urlparse(git_url)
@@ -425,11 +426,11 @@ def _generate_git_purl(d, download_location, srcrev):
for domain, purl_type in git_purl_handlers.items():
if hostname == domain:
- path = parsed.path.strip('/')
- path_parts = path.split('/')
+ path = parsed.path.strip("/")
+ path_parts = path.split("/")
if len(path_parts) >= 2:
owner = path_parts[0]
- repo = path_parts[1].replace('.git', '')
+ repo = path_parts[1].replace(".git", "")
return f"{purl_type}/{owner}/{repo}@{srcrev}"
break
@@ -448,12 +449,12 @@ def _enrich_source_package(d, dl, fd, file_name, primary_purpose):
if fd.type == "git":
# Use full SHA-1 from fd.revision
- srcrev = getattr(fd, 'revision', None)
- if srcrev and srcrev not in {'${AUTOREV}', 'AUTOINC', 'INVALID'}:
+ srcrev = getattr(fd, "revision", None)
+ if srcrev and srcrev not in {"${AUTOREV}", "AUTOINC", "INVALID"}:
version = srcrev
# Generate PURL for Git hosting services
- download_location = getattr(dl, 'software_downloadLocation', None)
+ download_location = getattr(dl, "software_downloadLocation", None)
if version and download_location:
purl = _generate_git_purl(d, download_location, version)
@@ -464,12 +465,12 @@ def _enrich_source_package(d, dl, fd, file_name, primary_purpose):
dl.software_packageUrl = purl
# Add VCS external reference for Git repositories
- download_location = getattr(dl, 'software_downloadLocation', None)
+ download_location = getattr(dl, "software_downloadLocation", None)
if download_location and isinstance(download_location, str):
- if download_location.startswith('git+'):
+ if download_location.startswith("git+"):
git_url = download_location[4:]
- if '@' in git_url:
- git_url = git_url.split('@')[0]
+ if "@" in git_url:
+ git_url = git_url.split("@")[0]
dl.externalRef = dl.externalRef or []
dl.externalRef.append(
@@ -480,7 +481,6 @@ def _enrich_source_package(d, dl, fd, file_name, primary_purpose):
)
-
def add_download_files(d, objset):
inputs = set()
@@ -726,8 +726,9 @@ def create_recipe_spdx(d):
if status == "Patched":
spdx_vex = recipe_objset.new_vex_patched_relationship(
- [spdx_cve_id], [recipe],
- notes=": ".join(v for v in (detail, description) if v)
+ [spdx_cve_id],
+ [recipe],
+ notes=": ".join(v for v in (detail, description) if v),
)
patches = []
for idx, filepath in enumerate(resources):
@@ -753,8 +754,9 @@ def create_recipe_spdx(d):
elif status == "Unpatched":
recipe_objset.new_vex_unpatched_relationship(
- [spdx_cve_id], [recipe],
- notes=": ".join(v for v in (detail, description) if v)
+ [spdx_cve_id],
+ [recipe],
+ notes=": ".join(v for v in (detail, description) if v),
)
elif status == "Ignored":
spdx_vex = recipe_objset.new_vex_ignored_relationship(
@@ -1060,7 +1062,11 @@ def create_spdx(d):
if include_sources:
debug_sources |= get_package_sources_from_debug(
- d, package, package_files, dep_sources, source_hash_cache,
+ d,
+ package,
+ package_files,
+ dep_sources,
+ source_hash_cache,
excluded_files=excluded_files,
)
@@ -1185,7 +1191,7 @@ def create_package_spdx(d):
if dep not in providers:
continue
- (dep, _) = providers[dep]
+ dep, _ = providers[dep]
if not oe.packagedata.packaged(dep, localdata):
continue
@@ -1455,17 +1461,17 @@ def create_image_spdx(d):
image_path = image_deploy_dir / image_filename
if os.path.isdir(image_path):
a, _ = add_package_files(
- d,
- objset,
- image_path,
- lambda file_counter: objset.new_spdxid(
- "imagefile", str(file_counter)
- ),
- lambda filepath: [],
- license_data=None,
- ignore_dirs=[],
- ignore_top_level_dirs=[],
- archive=None,
+ d,
+ objset,
+ image_path,
+ lambda file_counter: objset.new_spdxid(
+ "imagefile", str(file_counter)
+ ),
+ lambda filepath: [],
+ license_data=None,
+ ignore_dirs=[],
+ ignore_top_level_dirs=[],
+ archive=None,
)
artifacts.extend(a)
else:
@@ -1482,7 +1488,7 @@ def create_image_spdx(d):
oe.spdx30.Hash(
algorithm=oe.spdx30.HashAlgorithm.sha512,
hashValue=bb.utils.sha512_file(image_path),
- )
+ ),
],
)
)
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [OE-core][PATCH 3/5] spdx: Add ability for deploy tasks to create SPDX
2026-06-09 22:15 [OE-core][PATCH 0/5] Implement SPDX for deploy tasks Joshua Watt
2026-06-09 22:15 ` [OE-core][PATCH 1/5] classes/baremetal-image: Remove "do_" prefix from image manifest Joshua Watt
2026-06-09 22:15 ` [OE-core][PATCH 2/5] spdx: Reformat Joshua Watt
@ 2026-06-09 22:15 ` Joshua Watt
2026-06-09 22:15 ` [OE-core][PATCH 4/5] Add SPDX deploy tasks Joshua Watt
` (2 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Joshua Watt @ 2026-06-09 22:15 UTC (permalink / raw)
To: openembedded-core; +Cc: Joshua Watt
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 <JPEWhacker@gmail.com>
---
.../create-spdx-image-3.0.bbclass | 4 +-
meta/classes-recipe/deploy.bbclass | 1 +
meta/classes-recipe/nospdx.bbclass | 1 +
meta/classes/create-spdx-3.0.bbclass | 158 ++++++++++
meta/classes/spdx-common.bbclass | 1 +
meta/lib/oe/sbom30.py | 46 +--
meta/lib/oe/spdx30_tasks.py | 282 +++++++++++++++---
meta/lib/oe/spdx_common.py | 2 +-
8 files changed, 430 insertions(+), 65 deletions(-)
diff --git a/meta/classes-recipe/create-spdx-image-3.0.bbclass b/meta/classes-recipe/create-spdx-image-3.0.bbclass
index 15a91e90e2..a96cfb25ed 100644
--- a/meta/classes-recipe/create-spdx-image-3.0.bbclass
+++ b/meta/classes-recipe/create-spdx-image-3.0.bbclass
@@ -30,7 +30,7 @@ python do_create_rootfs_spdx() {
import oe.spdx30_tasks
oe.spdx30_tasks.create_rootfs_spdx(d)
}
-addtask do_create_rootfs_spdx after do_rootfs before do_image
+addtask do_create_rootfs_spdx after do_rootfs do_create_recipe_spdx before do_image
SSTATETASKS += "do_create_rootfs_spdx"
do_create_rootfs_spdx[sstate-inputdirs] = "${SPDXROOTFSDEPLOY}"
do_create_rootfs_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}"
@@ -47,7 +47,7 @@ python do_create_image_spdx() {
import oe.spdx30_tasks
oe.spdx30_tasks.create_image_spdx(d)
}
-addtask do_create_image_spdx after do_image_complete do_create_rootfs_spdx before do_build
+addtask do_create_image_spdx after do_image_complete do_create_rootfs_spdx do_create_recipe_spdx before do_build
SSTATETASKS += "do_create_image_spdx"
SSTATE_SKIP_CREATION:task-create-image-spdx = "1"
do_create_image_spdx[sstate-inputdirs] = "${SPDXIMAGEWORK}"
diff --git a/meta/classes-recipe/deploy.bbclass b/meta/classes-recipe/deploy.bbclass
index f56fe98d6d..f222a8560f 100644
--- a/meta/classes-recipe/deploy.bbclass
+++ b/meta/classes-recipe/deploy.bbclass
@@ -6,6 +6,7 @@
DEPLOYDIR = "${WORKDIR}/deploy-${PN}"
SSTATETASKS += "do_deploy"
+SPDX_DEPLOY_ARTIFACTS_DIR:task-deploy = "${DEPLOYDIR}"
do_deploy[sstate-inputdirs] = "${DEPLOYDIR}"
do_deploy[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}"
diff --git a/meta/classes-recipe/nospdx.bbclass b/meta/classes-recipe/nospdx.bbclass
index 7c99fcd1ec..b405f57d11 100644
--- a/meta/classes-recipe/nospdx.bbclass
+++ b/meta/classes-recipe/nospdx.bbclass
@@ -11,3 +11,4 @@ deltask do_create_package_spdx
deltask do_create_rootfs_spdx
deltask do_create_image_spdx
deltask do_create_image_sbom
+deltask do_create_deploy_sbom
diff --git a/meta/classes/create-spdx-3.0.bbclass b/meta/classes/create-spdx-3.0.bbclass
index 56fd01fd53..19d5a45eba 100644
--- a/meta/classes/create-spdx-3.0.bbclass
+++ b/meta/classes/create-spdx-3.0.bbclass
@@ -163,6 +163,34 @@ SPDX_GIT_PURL_MAPPINGS[doc] = "A space separated list of domain:purl_type \
on gitlab.example.com to the pkg:gitlab PURL type. \
github.com is always mapped to pkg:github by default."
+SPDX_DEPLOY_TASKS ?= ""
+SPDX_DEPLOY_TASKS[doc] = "A space separated list of sstate tasks that produce \
+ deployed output (usually written to DEPLOY_DIR_IMAGE). Tasks in this list \
+ will produce SPDX documents that describe the deployed output. If a task \
+ is in this list, see the SPDX_DEPLOY_ARITFACTS and SPDX_DEPLOY_ARTIFACTS_DIR \
+ for how to configure its SPDX output.\
+ \
+ Dependencies of deploy tasks that produce SPDX data will be automatically \
+ linked in as a build time dependency of the deploy task's SBoM. (for \
+ example, if one do_deploy depends on another recipes do_deploy, this will \
+ be reflected in the SPDX data). If the deploy task should depend on the \
+ primary build process of the recipe, the task should be declared \
+ 'after do_create_spdx'.\
+ "
+
+SPDX_DEPLOY_ARTIFACTS = "AUTO"
+SPDX_DEPLOY_ARITFACTS[doc] = "A space separated list of deployed artifacts, \
+ relative to SPDX_DEPLOY_ARTIFACTS_DIR that should be included in the SBoM. \
+ If 'AUTO' (the default), all files in SPDX_DEPLOY_ARITFACTS_DIR will be \
+ added. A :task- override *must* be used to set this value so that it is \
+ scoped to a specific task"
+
+SPDX_DEPLOY_ARTIFACTS_DIR = ""
+SPDX_DEPLOY_ARTIFACTS_DIR[doc] = "The directory recipe specific directory \
+ where artifacts are deployed for staging to sstate (e.g. for do_deploy, \
+ this is DEPLOY_DIR). A :task- override *must* be used to set this value so \
+ that it is scoped to a specific task."
+
IMAGE_CLASSES:append = " create-spdx-image-3.0"
SDK_CLASSES += "create-spdx-sdk-3.0"
@@ -291,3 +319,133 @@ python spdx30_build_started_handler () {
addhandler spdx30_build_started_handler
spdx30_build_started_handler[eventmask] = "bb.event.BuildStarted"
+python create_deploy_spdx() {
+ import oe.spdx30_tasks
+ from pathlib import Path
+ current_task = "do_" + d.getVar("BB_CURRENTTASK")
+
+ spdxdeploydir = Path(d.getVar("SPDXDIR") + "/deploy-" + current_task)
+
+ artifactsdir = d.getVar("SPDX_DEPLOY_ARTIFACTS_DIR")
+ if not artifactsdir:
+ bb.fatal(f"{pn}: spdx-artifactsdir must be set for task {current_task}")
+ return
+
+ artifacts = d.getVar("SPDX_DEPLOY_ARTIFACTS")
+
+ oe.spdx30_tasks.create_deploy_spdx(d, spdxdeploydir, artifactsdir, artifacts)
+}
+oe.spdx30_tasks.find_build_dep_objsets[vardepsexclude] += "BB_TASKDEPDATA"
+
+python () {
+ # Most recipes generate SPDX output in a distinct task from the task that
+ # actually is the relevant dependency. As such, we need to map the task
+ # that we care about to the task that generates the corresponding SPDX
+ # output so that we can rely on the SPDX output being present when the time
+ # comes to use it downstream.
+ #
+ # The down side of this is that only the first level of dependencies (e.g
+ # tasks listed in SPDX_DEPLOY_TASKS) will have the mapping done and thus
+ # find the dependencies. Transitive dependencies will not be mapped and
+ # thus the SPDX data will not be linked in.
+ #
+ # Ideally, this will be able to go away once more tasks directly generate
+ # SPDX files for their output instead of combining it into monolithic
+ # functions; tasks listed in this map are the best candidates to have this
+ # done first.
+ TASK_MAP = {
+ # If a task requires the RSS be extended, depend on the SPDX build task
+ # for the recipe, at least until it's possible for do_populate_sysroot
+ # to describe it's own output.
+ "do_populate_sysroot": "do_create_spdx",
+ # If an image is needed, also depend on the task to create the SBoM for
+ # the image
+ "do_image_complete": "do_create_image_spdx",
+ }
+
+ def map_task_deps(task, flag):
+ task_flags= (d.getVarFlag(task, flag) or "").split()
+ for t in task_flags:
+ if t in TASK_MAP and TASK_MAP[t] not in task_flags:
+ d.appendVarFlag(task, flag, f" {TASK_MAP[t]}")
+
+ def before_postfunc(f):
+ return f == "sstate_task_postfunc" or "buildhistory" in f
+
+ if bb.data.inherits_class("nospdx", d):
+ return
+
+ sstate_tasks = set((d.getVar("SSTATETASKS") or "").split())
+ spdx_tasks = (d.getVar("SPDX_DEPLOY_TASKS") or "").split()
+ deploy_sbom_tasks = []
+ for task in spdx_tasks:
+ if ":" in task:
+ task, func = task.split(":")
+ else:
+ func = "create_deploy_spdx"
+
+ deploy_sbom_tasks.append(task)
+
+ if task not in sstate_tasks:
+ bb.fatal(f"{task} is not an sstate task")
+
+ spdx_deploy = "${SPDXDIR}/deploy-" + task
+
+ # Ensure function is sorted properly. It should be right before
+ # sstate_task_postfunc
+ postfuncs = (d.getVarFlag(task, "postfuncs") or "").split()
+ d.setVarFlag(task, "postfuncs", " ".join(
+ [f for f in postfuncs if not before_postfunc(f)] +
+ [func] +
+ [f for f in postfuncs if before_postfunc(f)]
+ ))
+ d.prependVarFlag(task, "sstate-inputdirs", f"{spdx_deploy} ")
+ d.prependVarFlag(task, "sstate-outputdirs", "${DEPLOY_DIR_SPDX} ")
+ d.prependVarFlag(task, "file-checksums", "${SPDX3_DEP_FILES} ")
+ d.prependVarFlag(task, "dirs", f"{spdx_deploy} ")
+ d.prependVarFlag(task, "cleandirs", f"{spdx_deploy} ")
+
+ deps = (d.getVarFlag(task, "depends") or "").split()
+ extra_deps = ["${PN}:do_create_recipe_spdx"]
+ for dep in deps:
+ _, fn, taskname = bb.runqueue.split_tid(dep)
+ if taskname in TASK_MAP:
+ extra_deps.append(f"{fn}:{TASK_MAP[taskname]}")
+
+ d.prependVarFlag(task, "depends", " ".join(extra_deps) + " ")
+
+ map_task_deps(task, "deptask")
+ map_task_deps(task, "rdeptask")
+ map_task_deps(task, "recrdeptask")
+
+ # For now, if a recipe is directly built, deploy all of it's deploy tasks
+ # into a single SBoM. We may need an option in the future to have tasks
+ # that don't do this (e.g. because they do not deploy to a location that is
+ # intended to be consumed by the user)
+ if spdx_tasks:
+ bb.build.addtask("do_create_deploy_sbom", "do_build", " ".join(deploy_sbom_tasks), d)
+}
+
+python do_create_deploy_sbom() {
+ import oe.spdx30_tasks
+ from pathlib import Path
+ deploydir = Path(d.getVar("SPDXDEPLOYSBOMDEPLOY"))
+ deploy_tasks = []
+ for task in (d.getVar("SPDX_DEPLOY_TASKS") or "").split():
+ if ":" in task:
+ task, _ = task.split(":")
+ deploy_tasks.append(task)
+
+ oe.spdx30_tasks.create_deploy_sbom(d, deploydir, deploy_tasks)
+}
+do_create_deploy_sbom[sstate-inputdirs] = "${SPDXDEPLOYSBOMDEPLOY}"
+do_create_deploy_sbom[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}"
+do_create_deploy_sbom[recrdeptask] += "do_create_recipe_spdx do_create_spdx"
+do_create_deploy_sbom[cleandirs] += "${SPDXDEPLOYSBOMDEPLOY}"
+do_create_deploy_sbom[file-checksums] += "${SPDX3_DEP_FILES}"
+
+SSTATETASKS += "do_create_deploy_sbom"
+python do_create_deploy_sbom_setscene() {
+ sstate_setscene(d)
+}
+addtask do_create_deploy_sbom_setscene
diff --git a/meta/classes/spdx-common.bbclass b/meta/classes/spdx-common.bbclass
index 40701730a6..bca169670d 100644
--- a/meta/classes/spdx-common.bbclass
+++ b/meta/classes/spdx-common.bbclass
@@ -26,6 +26,7 @@ SPDX_TOOL_VERSION ??= "1.0"
SPDXRECIPEDEPLOY = "${SPDXDIR}/recipe-deploy"
SPDXRUNTIMEDEPLOY = "${SPDXDIR}/runtime-deploy"
SPDXRECIPESBOMDEPLOY = "${SPDXDIR}/recipes-bom-deploy"
+SPDXDEPLOYSBOMDEPLOY = "${SPDXDIR}/deploy-bom-deploy"
SPDX_INCLUDE_SOURCES ??= "0"
SPDX_INCLUDE_SOURCES[doc] = "If set to '1', include source code files in the \
diff --git a/meta/lib/oe/sbom30.py b/meta/lib/oe/sbom30.py
index 0926266295..16f42f41d6 100644
--- a/meta/lib/oe/sbom30.py
+++ b/meta/lib/oe/sbom30.py
@@ -1048,6 +1048,25 @@ def write_jsonld_doc(d, objset, dest):
objset.objects.remove(objset.doc)
+def make_jsonld_link(d, fn, subdir, name, deploydir):
+ pkg_arch = d.getVar("SSTATE_PKGARCH")
+
+ link_name = jsonld_arch_path(
+ d,
+ pkg_arch,
+ subdir,
+ name,
+ deploydir=deploydir,
+ )
+ try:
+ link_name.parent.mkdir(exist_ok=True, parents=True)
+ link_name.symlink_to(os.path.relpath(fn, link_name.parent))
+ except:
+ target = link_name.readlink()
+ bb.warn(f"Unable to link {fn} as {link_name}. Already points to {target}")
+ raise
+
+
def write_recipe_jsonld_doc(
d,
objset,
@@ -1055,6 +1074,7 @@ def write_recipe_jsonld_doc(
deploydir,
*,
create_spdx_id_links=True,
+ create_task_link=False,
):
pkg_arch = d.getVar("SSTATE_PKGARCH")
@@ -1062,23 +1082,7 @@ def write_recipe_jsonld_doc(
def link_id(_id):
hash_path = jsonld_hash_path(hash_id(_id))
-
- link_name = jsonld_arch_path(
- d,
- pkg_arch,
- *hash_path,
- deploydir=deploydir,
- )
- try:
- link_name.parent.mkdir(exist_ok=True, parents=True)
- link_name.symlink_to(os.path.relpath(dest, link_name.parent))
- except:
- target = link_name.readlink()
- bb.warn(
- f"Unable to link {_id} in {dest} as {link_name}. Already points to {target}"
- )
- raise
-
+ make_jsonld_link(d, dest, *hash_path, deploydir)
return hash_path[-1]
objset.add_aliases()
@@ -1094,6 +1098,14 @@ def write_recipe_jsonld_doc(
# out, so always do that even if there is an error making the links
write_jsonld_doc(d, objset, dest)
+ if create_task_link:
+ pn = d.getVar("PN")
+ current_task = "do_" + d.getVar("BB_CURRENTTASK")
+
+ make_jsonld_link(d, dest, "by-task", f"{pn}:{current_task}", deploydir)
+
+ return dest
+
def find_root_obj_in_jsonld(d, subdir, fn_name, obj_type, **attr_filter):
objset, fn = find_jsonld(d, subdir, fn_name, required=True)
diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py
index 72d17aade6..3dae502e64 100644
--- a/meta/lib/oe/spdx30_tasks.py
+++ b/meta/lib/oe/spdx30_tasks.py
@@ -605,6 +605,135 @@ def get_is_native(d):
return bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d)
+def set_var_field(d, var, obj, name, package=None):
+ val = None
+ if package:
+ val = d.getVar("%s:%s" % (var, package))
+
+ if not val:
+ val = d.getVar(var)
+
+ if val:
+ setattr(obj, name, val)
+
+
+def find_build_dep_objsets(d, start_task):
+ def find_deps(d, taskdepdata, current_dep, start_dep, visited, depth=0):
+ key = f"{current_dep.pn}:{current_dep.taskname}"
+
+ dep_objsets = []
+
+ if key not in visited:
+ visited.add(key)
+
+ for n in current_dep.deps:
+ dep = taskdepdata[n]
+ dep_name = f"{dep.pn}:{dep.taskname}"
+
+ dep_objset, dep_path = oe.sbom30.find_jsonld(d, "by-task", dep_name)
+ if dep_objset:
+ dep_objsets.append(dep_objset)
+
+ elif dep.pn == start_dep.pn:
+ # If this task is still part of the same recipe, continue
+ # searching up the dependency tree until a valid dependency
+ # is found. This detects transitive dependencies that may
+ # have been pulled in by previous tasks in the same recipe.
+ dep_objsets.extend(
+ find_deps(d, taskdepdata, dep, start_dep, visited, depth + 1)
+ )
+
+ return dep_objsets
+
+ pn = d.getVar("PN")
+ taskdepdata = d.getVar("BB_TASKDEPDATA", False)
+ for dep in taskdepdata.values():
+ if dep.pn == pn and dep.taskname == start_task:
+ start_dep = dep
+ break
+ else:
+ bb.fatal(f"Unable to find {pn}:{start_task} in taskdepdata")
+
+ return find_deps(d, taskdepdata, start_dep, start_dep, set())
+
+
+def create_deploy_package(d, objset, build, spdxid, name, start_task, files, **attrs):
+ recipe, _ = load_recipe_spdx(d)
+
+ deploy_package = objset.add_root(
+ oe.spdx30.software_Package(
+ _id=spdxid,
+ creationInfo=objset.doc.creationInfo,
+ name=name,
+ software_packageVersion=d.getVar("PV"),
+ )
+ )
+
+ objset.new_scoped_relationship(
+ [oe.sbom30.get_element_link_id(recipe)],
+ oe.spdx30.RelationshipType.generates,
+ oe.spdx30.LifecycleScopeType.build,
+ [deploy_package],
+ )
+
+ set_var_field(d, "HOMEPAGE", deploy_package, "software_homePage")
+ set_var_field(d, "SUMMARY", deploy_package, "summary")
+ set_var_field(d, "DESCRIPTION", deploy_package, "description")
+
+ set_purls(deploy_package, (d.getVar("SPDX_PACKAGE_URLS") or "").split())
+
+ set_timestamp_now(d, deploy_package, "builtTime")
+
+ supplier = objset.new_agent("SPDX_PACKAGE_SUPPLIER")
+ if supplier is not None:
+ deploy_package.suppliedBy = (
+ supplier if isinstance(supplier, str) else supplier._id
+ )
+
+ if files:
+ objset.new_relationship(
+ [deploy_package],
+ oe.spdx30.RelationshipType.contains,
+ sorted(list(files)),
+ )
+
+ objset.new_scoped_relationship(
+ [build],
+ oe.spdx30.RelationshipType.hasOutput,
+ oe.spdx30.LifecycleScopeType.build,
+ sorted(list(files) + [deploy_package]),
+ )
+
+ # Collect dependencies
+ if start_task is not None:
+ dep_builds = set()
+ dep_packages = set()
+ for o in find_build_dep_objsets(d, start_task):
+ if obj := o.find_root(oe.spdx30.software_Package):
+ dep_packages.add(oe.sbom30.get_element_link_id(obj))
+
+ if obj := o.find_root(oe.spdx30.build_Build):
+ dep_builds.add(oe.sbom30.get_element_link_id(obj))
+
+ if dep_packages:
+ objset.new_scoped_relationship(
+ [deploy_package],
+ oe.spdx30.RelationshipType.dependsOn,
+ oe.spdx30.LifecycleScopeType.build,
+ sorted(list(dep_packages)),
+ )
+
+ if dep_builds:
+ objset.new_scoped_relationship(
+ [build],
+ oe.spdx30.RelationshipType.dependsOn,
+ oe.spdx30.LifecycleScopeType.build,
+ sorted(list(dep_builds)),
+ )
+
+ return deploy_package
+
+
def create_recipe_spdx(d):
deploydir = Path(d.getVar("SPDXRECIPEDEPLOY"))
pn = d.getVar("PN")
@@ -795,7 +924,9 @@ def create_recipe_spdx(d):
sorted(list(all_cves)),
)
- oe.sbom30.write_recipe_jsonld_doc(d, recipe_objset, "static", deploydir)
+ oe.sbom30.write_recipe_jsonld_doc(
+ d, recipe_objset, "static", deploydir, create_task_link=True
+ )
def load_recipe_spdx(d):
@@ -809,17 +940,6 @@ def load_recipe_spdx(d):
def create_spdx(d):
- def set_var_field(var, obj, name, package=None):
- val = None
- if package:
- val = d.getVar("%s:%s" % (var, package))
-
- if not val:
- val = d.getVar(var)
-
- if val:
- setattr(obj, name, val)
-
license_data = oe.spdx_common.load_spdx_license_data(d)
pn = d.getVar("PN")
@@ -947,10 +1067,12 @@ def create_spdx(d):
)
set_var_field(
- "HOMEPAGE", spdx_package, "software_homePage", package=package
+ d, "HOMEPAGE", spdx_package, "software_homePage", package=package
+ )
+ set_var_field(d, "SUMMARY", spdx_package, "summary", package=package)
+ set_var_field(
+ d, "DESCRIPTION", spdx_package, "description", package=package
)
- set_var_field("SUMMARY", spdx_package, "summary", package=package)
- set_var_field("DESCRIPTION", spdx_package, "description", package=package)
purls = (
d.getVar("SPDX_PACKAGE_URLS:%s" % package)
@@ -1130,7 +1252,9 @@ def create_spdx(d):
f"Added PACKAGECONFIG entries: {len(enabled)} enabled, {len(disabled)} disabled"
)
- oe.sbom30.write_recipe_jsonld_doc(d, build_objset, "builds", deploydir)
+ oe.sbom30.write_recipe_jsonld_doc(
+ d, build_objset, "builds", deploydir, create_task_link=True
+ )
def create_package_spdx(d):
@@ -1364,26 +1488,9 @@ def create_rootfs_spdx(d):
d, "%s-%s-rootfs" % (image_basename, machine)
)
- rootfs = objset.add_root(
- oe.spdx30.software_Package(
- _id=objset.new_spdxid("rootfs", image_basename),
- creationInfo=objset.doc.creationInfo,
- name=image_basename,
- software_primaryPurpose=oe.spdx30.software_SoftwarePurpose.archive,
- )
- )
- set_timestamp_now(d, rootfs, "builtTime")
-
rootfs_build = objset.add_root(objset.new_task_build("rootfs", "rootfs"))
set_timestamp_now(d, rootfs_build, "build_buildEndTime")
- objset.new_scoped_relationship(
- [rootfs_build],
- oe.spdx30.RelationshipType.hasOutput,
- oe.spdx30.LifecycleScopeType.build,
- [rootfs],
- )
-
files_by_hash = {}
collect_build_package_inputs(d, objset, rootfs_build, packages, files_by_hash)
@@ -1416,14 +1523,20 @@ def create_rootfs_spdx(d):
)
)
- if files:
- objset.new_relationship(
- [rootfs],
- oe.spdx30.RelationshipType.contains,
- sorted(list(files)),
- )
+ rootfs = create_deploy_package(
+ d,
+ objset,
+ rootfs_build,
+ objset.new_spdxid("rootfs", image_basename),
+ image_basename,
+ None,
+ files,
+ )
+ rootfs.software_primaryPurpose = oe.spdx30.software_SoftwarePurpose.archive
- oe.sbom30.write_recipe_jsonld_doc(d, objset, "rootfs", deploydir)
+ oe.sbom30.write_recipe_jsonld_doc(
+ d, objset, "rootfs", deploydir, create_task_link=True
+ )
def create_image_spdx(d):
@@ -1503,10 +1616,13 @@ def create_image_spdx(d):
set_timestamp_now(d, a, "builtTime")
if artifacts:
- objset.new_scoped_relationship(
- [image_build],
- oe.spdx30.RelationshipType.hasOutput,
- oe.spdx30.LifecycleScopeType.build,
+ create_deploy_package(
+ d,
+ objset,
+ image_build,
+ objset.new_spdxid(taskname, "image", imagetype),
+ "image",
+ f"do_{taskname}",
artifacts,
)
@@ -1527,7 +1643,9 @@ def create_image_spdx(d):
objset.add_aliases()
objset.link()
- oe.sbom30.write_recipe_jsonld_doc(d, objset, "image", spdx_work_dir)
+ oe.sbom30.write_recipe_jsonld_doc(
+ d, objset, "image", spdx_work_dir, create_task_link=True
+ )
def create_image_sbom_spdx(d):
@@ -1705,3 +1823,77 @@ def create_recipe_sbom(d, deploydir):
objset, sbom = oe.sbom30.create_sbom(d, sbom_name, [recipe], [recipe_objset])
oe.sbom30.write_jsonld_doc(d, objset, deploydir / (sbom_name + ".spdx.json"))
+
+
+def create_deploy_spdx(d, spdxdeploydir, artifactsdir, artifacts):
+ pn = d.getVar("PN")
+ current_task = "do_" + d.getVar("BB_CURRENTTASK")
+
+ recipe, recipe_objset = load_recipe_spdx(d)
+
+ if artifacts == "AUTO":
+ artifacts = []
+ for root, dirs, files in os.walk(artifactsdir):
+ for p in [Path(os.path.join(root, f)) for f in files]:
+ if p.is_file():
+ artifacts.append(p)
+ else:
+ artifacts = [artifactsdir / p for p in artifacts.split()]
+
+ artifacts.sort(key=lambda p: (p.is_symlink(), p))
+
+ objset = oe.sbom30.ObjectSet.new_objset(d, f"{pn}-{current_task}-deploy")
+
+ build = objset.add_root(objset.new_task_build(current_task, "deploy"))
+ set_timestamp_now(d, build, "build_buildEndTime")
+ objset.set_is_native(get_is_native(d))
+
+ files = set()
+ for a in artifacts:
+ relpath = a.relative_to(artifactsdir)
+ f = objset.new_file(
+ objset.new_spdxid("deploy", str(relpath)),
+ a.name,
+ a,
+ )
+ files.add(f)
+
+ if not files:
+ bb.fatal(f"No deployed artifacts found in {artifactsdir}")
+ return
+
+ create_deploy_package(
+ d,
+ objset,
+ build,
+ objset.new_spdxid("deploy", pn, current_task),
+ pn,
+ current_task,
+ files,
+ )
+
+ # Create document
+ dest = oe.sbom30.write_recipe_jsonld_doc(
+ d,
+ objset,
+ "deploy",
+ spdxdeploydir,
+ create_task_link=True,
+ )
+
+
+def create_deploy_sbom(d, deploydir, deploy_tasks):
+ pn = d.getVar("PN")
+ sbom_name = f"{pn}-deploy-sbom"
+
+ objsets = []
+ for t in deploy_tasks:
+ o, _ = oe.sbom30.find_jsonld(d, "deploy", f"{pn}-{t}-deploy", required=True)
+ objsets.append(o)
+
+ root_objs = []
+ for o in objsets:
+ root_objs.extend(o.doc.rootElement)
+
+ objset, sbom = oe.sbom30.create_sbom(d, sbom_name, root_objs, objsets)
+ oe.sbom30.write_jsonld_doc(d, objset, deploydir / (sbom_name + ".spdx.json"))
diff --git a/meta/lib/oe/spdx_common.py b/meta/lib/oe/spdx_common.py
index 6b1a409c40..0337d1deb5 100644
--- a/meta/lib/oe/spdx_common.py
+++ b/meta/lib/oe/spdx_common.py
@@ -113,7 +113,7 @@ def collect_direct_deps(d, dep_task):
)
for this_dep in taskdepdata.values():
- if this_dep[0] == pn and this_dep[1] == current_task:
+ if this_dep.pn == pn and this_dep.taskname == current_task:
break
else:
bb.fatal(f"Unable to find this {pn}:{current_task} in taskdepdata")
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [OE-core][PATCH 4/5] Add SPDX deploy tasks
2026-06-09 22:15 [OE-core][PATCH 0/5] Implement SPDX for deploy tasks Joshua Watt
` (2 preceding siblings ...)
2026-06-09 22:15 ` [OE-core][PATCH 3/5] spdx: Add ability for deploy tasks to create SPDX Joshua Watt
@ 2026-06-09 22:15 ` Joshua Watt
2026-06-09 22:31 ` Patchtest results for " patchtest
` (2 more replies)
2026-06-09 22:15 ` [OE-core][PATCH 5/5] spdx: Replace do_create_image_spdx with deploy task Joshua Watt
2026-06-10 13:17 ` [OE-core][PATCH 0/5] Implement SPDX for deploy tasks Mathieu Dubois-Briand
5 siblings, 3 replies; 11+ messages in thread
From: Joshua Watt @ 2026-06-09 22:15 UTC (permalink / raw)
To: openembedded-core; +Cc: Joshua Watt
Adds SPDX deploy tasks to many recipes to record their output when
consumed in an SBoM
Signed-off-by: Joshua Watt <JPEWhacker@gmail.com>
---
meta/classes-recipe/barebox.bbclass | 3 ++-
meta/classes-recipe/devicetree.bbclass | 3 ++-
meta/classes-recipe/kernel-fit-image.bbclass | 3 ++-
meta/classes-recipe/kernel.bbclass | 3 ++-
meta/recipes-bsp/grub/grub-efi_2.14.bb | 3 ++-
meta/recipes-bsp/opensbi/opensbi_1.8.1.bb | 3 ++-
meta/recipes-bsp/u-boot/u-boot.inc | 3 ++-
meta/recipes-core/systemd/systemd-boot_259.5.bb | 4 ++--
8 files changed, 16 insertions(+), 9 deletions(-)
diff --git a/meta/classes-recipe/barebox.bbclass b/meta/classes-recipe/barebox.bbclass
index 73615999aa..0be611cf65 100644
--- a/meta/classes-recipe/barebox.bbclass
+++ b/meta/classes-recipe/barebox.bbclass
@@ -157,6 +157,7 @@ barebox_do_deploy () {
done
fi
}
-addtask deploy after do_compile
+addtask deploy after do_compile do_create_spdx
+SPDX_DEPLOY_TASKS += "do_deploy"
EXPORT_FUNCTIONS do_configure do_compile do_install do_deploy
diff --git a/meta/classes-recipe/devicetree.bbclass b/meta/classes-recipe/devicetree.bbclass
index ce9d008aac..a313507251 100644
--- a/meta/classes-recipe/devicetree.bbclass
+++ b/meta/classes-recipe/devicetree.bbclass
@@ -163,7 +163,8 @@ devicetree_do_deploy() {
install -Dm 0644 "${B}/$dtb_file" "${DEPLOYDIR}/devicetree/$dtb_file"
done
}
-addtask deploy before do_build after do_install
+addtask deploy before do_build after do_install do_create_spdx
+SPDX_DEPLOY_TASKS += "do_deploy"
EXPORT_FUNCTIONS do_compile do_install do_deploy
diff --git a/meta/classes-recipe/kernel-fit-image.bbclass b/meta/classes-recipe/kernel-fit-image.bbclass
index ae8f3c6688..becbc280ce 100644
--- a/meta/classes-recipe/kernel-fit-image.bbclass
+++ b/meta/classes-recipe/kernel-fit-image.bbclass
@@ -240,4 +240,5 @@ do_deploy() {
fi
fi
}
-addtask deploy after do_compile before do_build
+addtask deploy after do_compile do_create_spdx before do_build
+SPDX_DEPLOY_TASKS += "do_deploy"
diff --git a/meta/classes-recipe/kernel.bbclass b/meta/classes-recipe/kernel.bbclass
index 50cef17f69..5dc606048b 100644
--- a/meta/classes-recipe/kernel.bbclass
+++ b/meta/classes-recipe/kernel.bbclass
@@ -841,7 +841,8 @@ kernel_do_deploy() {
# ensure we get the right values for both
do_deploy[prefuncs] += "read_subpackage_metadata"
-addtask deploy after do_install do_populate_sysroot do_packagedata
+addtask deploy after do_install do_populate_sysroot do_packagedata do_create_spdx
+SPDX_DEPLOY_TASKS += "do_deploy"
EXPORT_FUNCTIONS do_deploy
diff --git a/meta/recipes-bsp/grub/grub-efi_2.14.bb b/meta/recipes-bsp/grub/grub-efi_2.14.bb
index 6354b43989..db187ac373 100644
--- a/meta/recipes-bsp/grub/grub-efi_2.14.bb
+++ b/meta/recipes-bsp/grub/grub-efi_2.14.bb
@@ -96,7 +96,8 @@ do_deploy() {
install -m 644 ${B}/${GRUB_IMAGE_PREFIX}${GRUB_IMAGE} ${DEPLOYDIR}
}
-addtask deploy after do_install before do_build
+addtask deploy after do_install do_create_spdx before do_build
+SPDX_DEPLOY_TASKS += "do_deploy"
FILES:${PN} = "${libdir}/grub/${GRUB_TARGET}-efi \
${datadir}/grub \
diff --git a/meta/recipes-bsp/opensbi/opensbi_1.8.1.bb b/meta/recipes-bsp/opensbi/opensbi_1.8.1.bb
index 0a9652c283..377a06fb78 100644
--- a/meta/recipes-bsp/opensbi/opensbi_1.8.1.bb
+++ b/meta/recipes-bsp/opensbi/opensbi_1.8.1.bb
@@ -44,7 +44,8 @@ do_deploy () {
install -m 755 ${D}/share/opensbi/*/${RISCV_SBI_PLAT}/firmware/fw_dynamic.* ${DEPLOYDIR}/
}
-addtask deploy before do_build after do_install
+addtask deploy before do_build after do_install do_create_spdx
+SPDX_DEPLOY_TASKS += "do_deploy"
FILES:${PN} += "/share/opensbi/*/${RISCV_SBI_PLAT}/firmware/fw_jump.*"
FILES:${PN} += "/share/opensbi/*/${RISCV_SBI_PLAT}/firmware/fw_payload.*"
diff --git a/meta/recipes-bsp/u-boot/u-boot.inc b/meta/recipes-bsp/u-boot/u-boot.inc
index a75948dfc3..1c3c9858df 100644
--- a/meta/recipes-bsp/u-boot/u-boot.inc
+++ b/meta/recipes-bsp/u-boot/u-boot.inc
@@ -470,4 +470,5 @@ uboot_deploy_spl () {
ln -sf ${SPL_IMAGE} ${DEPLOYDIR}/${SPL_SYMLINK}
}
-addtask deploy before do_build after do_compile
+addtask deploy before do_build after do_compile do_create_spdx
+SPDX_DEPLOY_TASKS += "do_deploy"
diff --git a/meta/recipes-core/systemd/systemd-boot_259.5.bb b/meta/recipes-core/systemd/systemd-boot_259.5.bb
index c6c443f929..e2c579db97 100644
--- a/meta/recipes-core/systemd/systemd-boot_259.5.bb
+++ b/meta/recipes-core/systemd/systemd-boot_259.5.bb
@@ -71,5 +71,5 @@ do_deploy () {
install ${B}/src/boot/addon*.efi.stub ${DEPLOYDIR}
}
-addtask deploy before do_build after do_compile
-
+addtask deploy before do_build after do_compile do_create_spdx
+SPDX_DEPLOY_TASKS += "do_deploy"
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [OE-core][PATCH 5/5] spdx: Replace do_create_image_spdx with deploy task
2026-06-09 22:15 [OE-core][PATCH 0/5] Implement SPDX for deploy tasks Joshua Watt
` (3 preceding siblings ...)
2026-06-09 22:15 ` [OE-core][PATCH 4/5] Add SPDX deploy tasks Joshua Watt
@ 2026-06-09 22:15 ` Joshua Watt
2026-06-10 13:17 ` [OE-core][PATCH 0/5] Implement SPDX for deploy tasks Mathieu Dubois-Briand
5 siblings, 0 replies; 11+ messages in thread
From: Joshua Watt @ 2026-06-09 22:15 UTC (permalink / raw)
To: openembedded-core; +Cc: Joshua Watt
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 <JPEWhacker@gmail.com>
---
.../create-spdx-image-3.0.bbclass | 30 +++++++------------
meta/classes-recipe/nospdx.bbclass | 1 -
meta/classes/create-spdx-3.0.bbclass | 3 --
meta/classes/spdx-common.bbclass | 1 -
meta/lib/oe/spdx30_tasks.py | 21 ++++++++-----
5 files changed, 24 insertions(+), 32 deletions(-)
diff --git a/meta/classes-recipe/create-spdx-image-3.0.bbclass b/meta/classes-recipe/create-spdx-image-3.0.bbclass
index a96cfb25ed..708981a0c0 100644
--- a/meta/classes-recipe/create-spdx-image-3.0.bbclass
+++ b/meta/classes-recipe/create-spdx-image-3.0.bbclass
@@ -30,7 +30,7 @@ python do_create_rootfs_spdx() {
import oe.spdx30_tasks
oe.spdx30_tasks.create_rootfs_spdx(d)
}
-addtask do_create_rootfs_spdx after do_rootfs do_create_recipe_spdx before do_image
+addtask do_create_rootfs_spdx after do_rootfs do_create_recipe_spdx before do_image do_image_complete
SSTATETASKS += "do_create_rootfs_spdx"
do_create_rootfs_spdx[sstate-inputdirs] = "${SPDXROOTFSDEPLOY}"
do_create_rootfs_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}"
@@ -43,33 +43,23 @@ python do_create_rootfs_spdx_setscene() {
}
addtask do_create_rootfs_spdx_setscene
-python do_create_image_spdx() {
+python create_image_spdx() {
import oe.spdx30_tasks
- oe.spdx30_tasks.create_image_spdx(d)
-}
-addtask do_create_image_spdx after do_image_complete do_create_rootfs_spdx do_create_recipe_spdx before do_build
-SSTATETASKS += "do_create_image_spdx"
-SSTATE_SKIP_CREATION:task-create-image-spdx = "1"
-do_create_image_spdx[sstate-inputdirs] = "${SPDXIMAGEWORK}"
-do_create_image_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}"
-do_create_image_spdx[cleandirs] = "${SPDXIMAGEWORK}"
-do_create_image_spdx[dirs] = "${SPDXIMAGEWORK}"
-do_create_image_spdx[file-checksums] += "${SPDX3_DEP_FILES}"
-do_create_image_spdx[vardeps] += "\
- SPDX_IMAGE_PURPOSE \
- "
+ from pathlib import Path
+ current_task = "do_" + d.getVar("BB_CURRENTTASK")
-python do_create_image_spdx_setscene() {
- sstate_setscene(d)
-}
-addtask do_create_image_spdx_setscene
+ spdxdeploydir = Path(d.getVar("SPDXDIR") + "/deploy-" + current_task)
+ oe.spdx30_tasks.create_image_spdx(d, spdxdeploydir)
+}
+oe.spdx30_tasks.create_image_spdx[vardeps] += "SPDX_IMAGE_PURPOSE"
+SPDX_DEPLOY_TASKS += "do_image_complete:create_image_spdx"
python do_create_image_sbom_spdx() {
import oe.spdx30_tasks
oe.spdx30_tasks.create_image_sbom_spdx(d)
}
-addtask do_create_image_sbom_spdx after do_create_rootfs_spdx do_create_image_spdx before do_build
+addtask do_create_image_sbom_spdx after do_create_rootfs_spdx do_image_complete before do_build
SSTATETASKS += "do_create_image_sbom_spdx"
SSTATE_SKIP_CREATION:task-create-image-sbom = "1"
do_create_image_sbom_spdx[sstate-inputdirs] = "${SPDXIMAGEDEPLOYDIR}"
diff --git a/meta/classes-recipe/nospdx.bbclass b/meta/classes-recipe/nospdx.bbclass
index b405f57d11..9c30c4d2c0 100644
--- a/meta/classes-recipe/nospdx.bbclass
+++ b/meta/classes-recipe/nospdx.bbclass
@@ -9,6 +9,5 @@ deltask do_create_spdx
deltask do_create_spdx_runtime
deltask do_create_package_spdx
deltask do_create_rootfs_spdx
-deltask do_create_image_spdx
deltask do_create_image_sbom
deltask do_create_deploy_sbom
diff --git a/meta/classes/create-spdx-3.0.bbclass b/meta/classes/create-spdx-3.0.bbclass
index 19d5a45eba..c0263e4277 100644
--- a/meta/classes/create-spdx-3.0.bbclass
+++ b/meta/classes/create-spdx-3.0.bbclass
@@ -358,9 +358,6 @@ python () {
# for the recipe, at least until it's possible for do_populate_sysroot
# to describe it's own output.
"do_populate_sysroot": "do_create_spdx",
- # If an image is needed, also depend on the task to create the SBoM for
- # the image
- "do_image_complete": "do_create_image_spdx",
}
def map_task_deps(task, flag):
diff --git a/meta/classes/spdx-common.bbclass b/meta/classes/spdx-common.bbclass
index bca169670d..13839aac3a 100644
--- a/meta/classes/spdx-common.bbclass
+++ b/meta/classes/spdx-common.bbclass
@@ -15,7 +15,6 @@ CVE_VERSION ??= "${PV}"
SPDXDIR ??= "${WORKDIR}/spdx/${SPDX_VERSION}"
SPDXDEPLOY = "${SPDXDIR}/deploy"
SPDXWORK = "${SPDXDIR}/work"
-SPDXIMAGEWORK = "${SPDXDIR}/image-work"
SPDXSDKWORK = "${SPDXDIR}/sdk-work"
SPDXSDKEXTWORK = "${SPDXDIR}/sdk-ext-work"
SPDXDEPS = "${SPDXDIR}/deps.json"
diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py
index 3dae502e64..f5717b3919 100644
--- a/meta/lib/oe/spdx30_tasks.py
+++ b/meta/lib/oe/spdx30_tasks.py
@@ -1539,19 +1539,19 @@ def create_rootfs_spdx(d):
)
-def create_image_spdx(d):
+def create_image_spdx(d, spdx_deploy_dir):
import oe.sbom30
+ pn = d.getVar("PN")
+ current_task = "do_" + d.getVar("BB_CURRENTTASK")
+
image_deploy_dir = Path(d.getVar("IMGDEPLOYDIR"))
manifest_path = Path(d.getVar("IMAGE_OUTPUT_MANIFEST"))
- spdx_work_dir = Path(d.getVar("SPDXIMAGEWORK"))
image_basename = d.getVar("IMAGE_BASENAME")
machine = d.getVar("MACHINE")
- objset = oe.sbom30.ObjectSet.new_objset(
- d, "%s-%s-image" % (image_basename, machine)
- )
+ objset = oe.sbom30.ObjectSet.new_objset(d, f"{pn}-{current_task}-deploy")
with manifest_path.open("r") as f:
manifest = json.load(f)
@@ -1644,13 +1644,18 @@ def create_image_spdx(d):
objset.add_aliases()
objset.link()
oe.sbom30.write_recipe_jsonld_doc(
- d, objset, "image", spdx_work_dir, create_task_link=True
+ d,
+ objset,
+ "deploy",
+ spdx_deploy_dir,
+ create_task_link=True,
)
def create_image_sbom_spdx(d):
import oe.sbom30
+ pn = d.getVar("PN")
image_name = d.getVar("IMAGE_NAME")
image_basename = d.getVar("IMAGE_BASENAME")
image_link_name = d.getVar("IMAGE_LINK_NAME")
@@ -1672,7 +1677,9 @@ def create_image_sbom_spdx(d):
root_elements.append(oe.sbom30.get_element_link_id(rootfs_image))
image_objset, _ = oe.sbom30.find_jsonld(
- d, "image", "%s-%s-image" % (image_basename, machine), required=True
+ d,
+ "deploy",
+ f"{pn}-do_image_complete-deploy",
)
for o in image_objset.foreach_root(oe.spdx30.software_File):
root_elements.append(oe.sbom30.get_element_link_id(o))
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Patchtest results for [OE-core][PATCH 4/5] Add SPDX deploy tasks
2026-06-09 22:15 ` [OE-core][PATCH 4/5] Add SPDX deploy tasks Joshua Watt
@ 2026-06-09 22:31 ` patchtest
2026-06-10 6:17 ` Mikko Rapeli
2026-06-10 7:46 ` Richard Purdie
2 siblings, 0 replies; 11+ messages in thread
From: patchtest @ 2026-06-09 22:31 UTC (permalink / raw)
To: Joshua Watt; +Cc: openembedded-core
[-- Attachment #1: Type: text/plain, Size: 2203 bytes --]
Thank you for your submission. Patchtest identified one
or more issues with the patch. Please see the log below for
more information:
---
Testing patch /home/patchtest/share/mboxes/4-5-Add-SPDX-deploy-tasks.patch
FAIL: test shortlog format: Commit shortlog (first line of commit message) should follow the format "<target>: <summary>" (test_mbox.TestMbox.test_shortlog_format)
PASS: test Signed-off-by presence (test_mbox.TestMbox.test_signed_off_by_presence)
PASS: test author valid (test_mbox.TestMbox.test_author_valid)
PASS: test commit message presence (test_mbox.TestMbox.test_commit_message_presence)
PASS: test commit message user tags (test_mbox.TestMbox.test_commit_message_user_tags)
PASS: test mbox format (test_mbox.TestMbox.test_mbox_format)
PASS: test non-AUH upgrade (test_mbox.TestMbox.test_non_auh_upgrade)
PASS: test shortlog length (test_mbox.TestMbox.test_shortlog_length)
PASS: test target mailing list (test_mbox.TestMbox.test_target_mailing_list)
SKIP: pretest pylint: No python related patches, skipping test (test_python_pylint.PyLint.pretest_pylint)
SKIP: test CVE tag format: No new CVE patches introduced (test_patch.TestPatch.test_cve_tag_format)
SKIP: test Signed-off-by presence: No new CVE patches introduced (test_patch.TestPatch.test_signed_off_by_presence)
SKIP: test Upstream-Status presence: No new CVE patches introduced (test_patch.TestPatch.test_upstream_status_presence_format)
SKIP: test bugzilla entry format: No bug ID found (test_mbox.TestMbox.test_bugzilla_entry_format)
SKIP: test pylint: No python related patches, skipping test (test_python_pylint.PyLint.test_pylint)
SKIP: test series merge on head: Merge test is disabled for now (test_mbox.TestMbox.test_series_merge_on_head)
---
Please address the issues identified and
submit a new revision of the patch, or alternatively, reply to this
email with an explanation of why the patch should be accepted. If you
believe these results are due to an error in patchtest, please submit a
bug at https://bugzilla.yoctoproject.org/ (use the 'Patchtest' category
under 'Yocto Project Subprojects'). For more information on specific
failures, see: https://wiki.yoctoproject.org/wiki/Patchtest. Thank
you!
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [OE-core][PATCH 4/5] Add SPDX deploy tasks
2026-06-09 22:15 ` [OE-core][PATCH 4/5] Add SPDX deploy tasks Joshua Watt
2026-06-09 22:31 ` Patchtest results for " patchtest
@ 2026-06-10 6:17 ` Mikko Rapeli
2026-06-10 7:46 ` Richard Purdie
2 siblings, 0 replies; 11+ messages in thread
From: Mikko Rapeli @ 2026-06-10 6:17 UTC (permalink / raw)
To: JPEWhacker; +Cc: openembedded-core
Hi,
On Tue, Jun 09, 2026 at 04:15:55PM -0600, Joshua Watt via lists.openembedded.org wrote:
> Adds SPDX deploy tasks to many recipes to record their output when
> consumed in an SBoM
>
> Signed-off-by: Joshua Watt <JPEWhacker@gmail.com>
> ---
> meta/classes-recipe/barebox.bbclass | 3 ++-
> meta/classes-recipe/devicetree.bbclass | 3 ++-
> meta/classes-recipe/kernel-fit-image.bbclass | 3 ++-
> meta/classes-recipe/kernel.bbclass | 3 ++-
> meta/recipes-bsp/grub/grub-efi_2.14.bb | 3 ++-
> meta/recipes-bsp/opensbi/opensbi_1.8.1.bb | 3 ++-
> meta/recipes-bsp/u-boot/u-boot.inc | 3 ++-
> meta/recipes-core/systemd/systemd-boot_259.5.bb | 4 ++--
> 8 files changed, 16 insertions(+), 9 deletions(-)
>
> diff --git a/meta/classes-recipe/barebox.bbclass b/meta/classes-recipe/barebox.bbclass
> index 73615999aa..0be611cf65 100644
> --- a/meta/classes-recipe/barebox.bbclass
> +++ b/meta/classes-recipe/barebox.bbclass
> @@ -157,6 +157,7 @@ barebox_do_deploy () {
> done
> fi
> }
> -addtask deploy after do_compile
> +addtask deploy after do_compile do_create_spdx
> +SPDX_DEPLOY_TASKS += "do_deploy"
>
> EXPORT_FUNCTIONS do_configure do_compile do_install do_deploy
> diff --git a/meta/classes-recipe/devicetree.bbclass b/meta/classes-recipe/devicetree.bbclass
> index ce9d008aac..a313507251 100644
> --- a/meta/classes-recipe/devicetree.bbclass
> +++ b/meta/classes-recipe/devicetree.bbclass
> @@ -163,7 +163,8 @@ devicetree_do_deploy() {
> install -Dm 0644 "${B}/$dtb_file" "${DEPLOYDIR}/devicetree/$dtb_file"
> done
> }
> -addtask deploy before do_build after do_install
> +addtask deploy before do_build after do_install do_create_spdx
> +SPDX_DEPLOY_TASKS += "do_deploy"
Could deploy.bbclass do this by default? There are plenty of similar changes needed in
BSP etc layers which already use deploy.bbclass.
Cheers,
-Mikko
> EXPORT_FUNCTIONS do_compile do_install do_deploy
>
> diff --git a/meta/classes-recipe/kernel-fit-image.bbclass b/meta/classes-recipe/kernel-fit-image.bbclass
> index ae8f3c6688..becbc280ce 100644
> --- a/meta/classes-recipe/kernel-fit-image.bbclass
> +++ b/meta/classes-recipe/kernel-fit-image.bbclass
> @@ -240,4 +240,5 @@ do_deploy() {
> fi
> fi
> }
> -addtask deploy after do_compile before do_build
> +addtask deploy after do_compile do_create_spdx before do_build
> +SPDX_DEPLOY_TASKS += "do_deploy"
> diff --git a/meta/classes-recipe/kernel.bbclass b/meta/classes-recipe/kernel.bbclass
> index 50cef17f69..5dc606048b 100644
> --- a/meta/classes-recipe/kernel.bbclass
> +++ b/meta/classes-recipe/kernel.bbclass
> @@ -841,7 +841,8 @@ kernel_do_deploy() {
> # ensure we get the right values for both
> do_deploy[prefuncs] += "read_subpackage_metadata"
>
> -addtask deploy after do_install do_populate_sysroot do_packagedata
> +addtask deploy after do_install do_populate_sysroot do_packagedata do_create_spdx
> +SPDX_DEPLOY_TASKS += "do_deploy"
>
> EXPORT_FUNCTIONS do_deploy
>
> diff --git a/meta/recipes-bsp/grub/grub-efi_2.14.bb b/meta/recipes-bsp/grub/grub-efi_2.14.bb
> index 6354b43989..db187ac373 100644
> --- a/meta/recipes-bsp/grub/grub-efi_2.14.bb
> +++ b/meta/recipes-bsp/grub/grub-efi_2.14.bb
> @@ -96,7 +96,8 @@ do_deploy() {
> install -m 644 ${B}/${GRUB_IMAGE_PREFIX}${GRUB_IMAGE} ${DEPLOYDIR}
> }
>
> -addtask deploy after do_install before do_build
> +addtask deploy after do_install do_create_spdx before do_build
> +SPDX_DEPLOY_TASKS += "do_deploy"
>
> FILES:${PN} = "${libdir}/grub/${GRUB_TARGET}-efi \
> ${datadir}/grub \
> diff --git a/meta/recipes-bsp/opensbi/opensbi_1.8.1.bb b/meta/recipes-bsp/opensbi/opensbi_1.8.1.bb
> index 0a9652c283..377a06fb78 100644
> --- a/meta/recipes-bsp/opensbi/opensbi_1.8.1.bb
> +++ b/meta/recipes-bsp/opensbi/opensbi_1.8.1.bb
> @@ -44,7 +44,8 @@ do_deploy () {
> install -m 755 ${D}/share/opensbi/*/${RISCV_SBI_PLAT}/firmware/fw_dynamic.* ${DEPLOYDIR}/
> }
>
> -addtask deploy before do_build after do_install
> +addtask deploy before do_build after do_install do_create_spdx
> +SPDX_DEPLOY_TASKS += "do_deploy"
>
> FILES:${PN} += "/share/opensbi/*/${RISCV_SBI_PLAT}/firmware/fw_jump.*"
> FILES:${PN} += "/share/opensbi/*/${RISCV_SBI_PLAT}/firmware/fw_payload.*"
> diff --git a/meta/recipes-bsp/u-boot/u-boot.inc b/meta/recipes-bsp/u-boot/u-boot.inc
> index a75948dfc3..1c3c9858df 100644
> --- a/meta/recipes-bsp/u-boot/u-boot.inc
> +++ b/meta/recipes-bsp/u-boot/u-boot.inc
> @@ -470,4 +470,5 @@ uboot_deploy_spl () {
> ln -sf ${SPL_IMAGE} ${DEPLOYDIR}/${SPL_SYMLINK}
> }
>
> -addtask deploy before do_build after do_compile
> +addtask deploy before do_build after do_compile do_create_spdx
> +SPDX_DEPLOY_TASKS += "do_deploy"
> diff --git a/meta/recipes-core/systemd/systemd-boot_259.5.bb b/meta/recipes-core/systemd/systemd-boot_259.5.bb
> index c6c443f929..e2c579db97 100644
> --- a/meta/recipes-core/systemd/systemd-boot_259.5.bb
> +++ b/meta/recipes-core/systemd/systemd-boot_259.5.bb
> @@ -71,5 +71,5 @@ do_deploy () {
> install ${B}/src/boot/addon*.efi.stub ${DEPLOYDIR}
> }
>
> -addtask deploy before do_build after do_compile
> -
> +addtask deploy before do_build after do_compile do_create_spdx
> +SPDX_DEPLOY_TASKS += "do_deploy"
> --
> 2.54.0
>
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#238307): https://lists.openembedded.org/g/openembedded-core/message/238307
> Mute This Topic: https://lists.openembedded.org/mt/119731707/7159507
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [mikko.rapeli@linaro.org]
> -=-=-=-=-=-=-=-=-=-=-=-
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [OE-core][PATCH 4/5] Add SPDX deploy tasks
2026-06-09 22:15 ` [OE-core][PATCH 4/5] Add SPDX deploy tasks Joshua Watt
2026-06-09 22:31 ` Patchtest results for " patchtest
2026-06-10 6:17 ` Mikko Rapeli
@ 2026-06-10 7:46 ` Richard Purdie
2 siblings, 0 replies; 11+ messages in thread
From: Richard Purdie @ 2026-06-10 7:46 UTC (permalink / raw)
To: JPEWhacker, openembedded-core
On Tue, 2026-06-09 at 16:15 -0600, Joshua Watt via lists.openembedded.org wrote:
> Adds SPDX deploy tasks to many recipes to record their output when
> consumed in an SBoM
>
> Signed-off-by: Joshua Watt <JPEWhacker@gmail.com>
> ---
> meta/classes-recipe/barebox.bbclass | 3 ++-
> meta/classes-recipe/devicetree.bbclass | 3 ++-
> meta/classes-recipe/kernel-fit-image.bbclass | 3 ++-
> meta/classes-recipe/kernel.bbclass | 3 ++-
> meta/recipes-bsp/grub/grub-efi_2.14.bb | 3 ++-
> meta/recipes-bsp/opensbi/opensbi_1.8.1.bb | 3 ++-
> meta/recipes-bsp/u-boot/u-boot.inc | 3 ++-
> meta/recipes-core/systemd/systemd-boot_259.5.bb | 4 ++--
> 8 files changed, 16 insertions(+), 9 deletions(-)
>
> diff --git a/meta/classes-recipe/barebox.bbclass b/meta/classes-recipe/barebox.bbclass
> index 73615999aa..0be611cf65 100644
> --- a/meta/classes-recipe/barebox.bbclass
> +++ b/meta/classes-recipe/barebox.bbclass
> @@ -157,6 +157,7 @@ barebox_do_deploy () {
> done
> fi
> }
> -addtask deploy after do_compile
> +addtask deploy after do_compile do_create_spdx
> +SPDX_DEPLOY_TASKS += "do_deploy"
>
> EXPORT_FUNCTIONS do_configure do_compile do_install do_deploy
> diff --git a/meta/classes-recipe/devicetree.bbclass b/meta/classes-recipe/devicetree.bbclass
> index ce9d008aac..a313507251 100644
> --- a/meta/classes-recipe/devicetree.bbclass
> +++ b/meta/classes-recipe/devicetree.bbclass
> @@ -163,7 +163,8 @@ devicetree_do_deploy() {
> install -Dm 0644 "${B}/$dtb_file" "${DEPLOYDIR}/devicetree/$dtb_file"
> done
> }
> -addtask deploy before do_build after do_install
> +addtask deploy before do_build after do_install do_create_spdx
> +SPDX_DEPLOY_TASKS += "do_deploy"
>
> EXPORT_FUNCTIONS do_compile do_install do_deploy
>
Adding this "after do_create_spdx" makes spdx mandatory and impossible
to disable which I'm not sure is what you intended?
Cheers,
Richard
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [OE-core][PATCH 0/5] Implement SPDX for deploy tasks
2026-06-09 22:15 [OE-core][PATCH 0/5] Implement SPDX for deploy tasks Joshua Watt
` (4 preceding siblings ...)
2026-06-09 22:15 ` [OE-core][PATCH 5/5] spdx: Replace do_create_image_spdx with deploy task Joshua Watt
@ 2026-06-10 13:17 ` Mathieu Dubois-Briand
2026-06-11 18:46 ` Joshua Watt
5 siblings, 1 reply; 11+ messages in thread
From: Mathieu Dubois-Briand @ 2026-06-10 13:17 UTC (permalink / raw)
To: JPEWhacker, openembedded-core
On Wed Jun 10, 2026 at 12:15 AM CEST, Joshua Watt via lists.openembedded.org wrote:
> The SPDX use case for file system image has been well defined since SPDX
> was first implemented, however there has always been a desire to also
> express SPDX output for other non-image deliverables (primarily, those
> that have a do_deploy task or similar). These types of tasks cannot
> easily use the traditional method of having a separate SPDX task that
> runs to create their SPDX output as this causes lots of problems with
> the way dependencies are specified. Instead, it is desirable for these
> tasks to directly produce SPDX output that can be consumed by other
> tasks that depend on them.
>
> This patch series adds support for this. Any sstate task can now be
> added to the SPDX_DEPLOY_TASKS list and it will run a postfunc to
> generate SPDX output that describes what is being deployed. For
> classical do_deploy tasks, this is setup to be easy by automatically
> capturing all the deployed output files in the SPDX data, but other
> tasks can be added as well.
>
> Finally, the do_create_image_spdx task is removed and replaced with a
> SPDX deploy postfunc using this new system. This means that any task
> that depends on do_image_complete will automatically also get the SPDX
> output for the image, simplifying the dependency handling.
>
> Joshua Watt (5):
Hi Joshua,
Thanks for your series. I believe we are seeing both a new error and new
warnings because of it.
ERROR: grub-efi-2.14-r0 do_deploy_setscene: Recipe grub-efi is trying to install files into a shared area when those files already exist. The files and the manifests listing them are:
/srv/pokybuild/yocto-worker/wic/build/build/tmp/deploy/spdx/3.0.1/core2-32/deploy/grub-efi-do_deploy-deploy.spdx.json
(matched in manifest-qemux86-grub-efi.deploy)
/srv/pokybuild/yocto-worker/wic/build/build/tmp/deploy/spdx/3.0.1/core2-32/by-task/grub-efi:do_deploy.spdx.json
(matched in manifest-qemux86-grub-efi.deploy)
/srv/pokybuild/yocto-worker/wic/build/build/tmp/deploy/spdx/3.0.1/core2-32/by-spdxid-hash/3c/3c29614c1a202bc0cc0a6f3dfd5b29235ea75ce5ee5bb0a847367bd8ce978004.spdx.json
(matched in manifest-qemux86-grub-efi.deploy)
Please adjust the recipes so only one recipe provides a given file.
https://autobuilder.yoctoproject.org/valkyrie/#/builders/15/builds/3852
WARNING: core-image-minimal-1.0-r0 do_create_deploy_sbom: The following SPDX IDs were unable to be resolved:
http://spdxdocs.org/openembedded-alias/by-doc-hash/500473e510f927d1a990e932c4942f978ce9da778692ab840dec17ad0ece09a1/pigz-native/UNIHASH/build/recipe
https://autobuilder.yoctoproject.org/valkyrie/#/builders/15/builds/3852
https://autobuilder.yoctoproject.org/valkyrie/#/builders/10/builds/3887
https://autobuilder.yoctoproject.org/valkyrie/#/builders/65/builds/3874
Can you have a look at the issues?
Thanks,
Mathieu
--
Mathieu Dubois-Briand, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [OE-core][PATCH 0/5] Implement SPDX for deploy tasks
2026-06-10 13:17 ` [OE-core][PATCH 0/5] Implement SPDX for deploy tasks Mathieu Dubois-Briand
@ 2026-06-11 18:46 ` Joshua Watt
0 siblings, 0 replies; 11+ messages in thread
From: Joshua Watt @ 2026-06-11 18:46 UTC (permalink / raw)
To: Mathieu Dubois-Briand; +Cc: openembedded-core
On Wed, Jun 10, 2026 at 7:17 AM Mathieu Dubois-Briand
<mathieu.dubois-briand@bootlin.com> wrote:
>
> On Wed Jun 10, 2026 at 12:15 AM CEST, Joshua Watt via lists.openembedded.org wrote:
> > The SPDX use case for file system image has been well defined since SPDX
> > was first implemented, however there has always been a desire to also
> > express SPDX output for other non-image deliverables (primarily, those
> > that have a do_deploy task or similar). These types of tasks cannot
> > easily use the traditional method of having a separate SPDX task that
> > runs to create their SPDX output as this causes lots of problems with
> > the way dependencies are specified. Instead, it is desirable for these
> > tasks to directly produce SPDX output that can be consumed by other
> > tasks that depend on them.
> >
> > This patch series adds support for this. Any sstate task can now be
> > added to the SPDX_DEPLOY_TASKS list and it will run a postfunc to
> > generate SPDX output that describes what is being deployed. For
> > classical do_deploy tasks, this is setup to be easy by automatically
> > capturing all the deployed output files in the SPDX data, but other
> > tasks can be added as well.
> >
> > Finally, the do_create_image_spdx task is removed and replaced with a
> > SPDX deploy postfunc using this new system. This means that any task
> > that depends on do_image_complete will automatically also get the SPDX
> > output for the image, simplifying the dependency handling.
> >
> > Joshua Watt (5):
>
> Hi Joshua,
>
> Thanks for your series. I believe we are seeing both a new error and new
> warnings because of it.
>
>
> ERROR: grub-efi-2.14-r0 do_deploy_setscene: Recipe grub-efi is trying to install files into a shared area when those files already exist. The files and the manifests listing them are:
> /srv/pokybuild/yocto-worker/wic/build/build/tmp/deploy/spdx/3.0.1/core2-32/deploy/grub-efi-do_deploy-deploy.spdx.json
> (matched in manifest-qemux86-grub-efi.deploy)
> /srv/pokybuild/yocto-worker/wic/build/build/tmp/deploy/spdx/3.0.1/core2-32/by-task/grub-efi:do_deploy.spdx.json
> (matched in manifest-qemux86-grub-efi.deploy)
> /srv/pokybuild/yocto-worker/wic/build/build/tmp/deploy/spdx/3.0.1/core2-32/by-spdxid-hash/3c/3c29614c1a202bc0cc0a6f3dfd5b29235ea75ce5ee5bb0a847367bd8ce978004.spdx.json
> (matched in manifest-qemux86-grub-efi.deploy)
> Please adjust the recipes so only one recipe provides a given file.
>
> https://autobuilder.yoctoproject.org/valkyrie/#/builders/15/builds/3852
This is caused because the grub-efi recipe changes subtly between
qemux86 and genericx86: From bitbake -S printdiff:
Variable GRUB_IMAGE value changed from 'grub-efi-bootia32.efi' to
'bootia32.efi'
Variable GRUB_IMAGE_PREFIX value changed from '' to 'grub-efi-
However, the recipe arch is PACKAGE_ARCH, which is the same for both
machines. I _think_ this means that grub-ef should be change to be
MACHINE_ARCH instead, since its expected(?) that it will change based
on machine and not just PACKAGE_ARCH, but I'm not sure about that.
Alternatively (or maybe in addition to), perhaps do_deploy SPDX output
should always be written to a per-machine SPDX directory instead of
using SSTATE_ARCH? This has congruence with DEPLOY_DIR_IMAGE
containing MACHINE. I think this wouldn't actually work though because
DEPLOY_DIR_IMAGE is an sstate output directory (so I think it can
change without invalidating the task), where as SPDX is writing to
SSTATE_ARCH as an sstate input directory.
>
>
> WARNING: core-image-minimal-1.0-r0 do_create_deploy_sbom: The following SPDX IDs were unable to be resolved:
> http://spdxdocs.org/openembedded-alias/by-doc-hash/500473e510f927d1a990e932c4942f978ce9da778692ab840dec17ad0ece09a1/pigz-native/UNIHASH/build/recipe
>
> https://autobuilder.yoctoproject.org/valkyrie/#/builders/15/builds/3852
> https://autobuilder.yoctoproject.org/valkyrie/#/builders/10/builds/3887
> https://autobuilder.yoctoproject.org/valkyrie/#/builders/65/builds/3874
>
> Can you have a look at the issues?
>
> Thanks,
> Mathieu
>
> --
> Mathieu Dubois-Briand, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
>
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-06-11 18:46 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-09 22:15 [OE-core][PATCH 0/5] Implement SPDX for deploy tasks Joshua Watt
2026-06-09 22:15 ` [OE-core][PATCH 1/5] classes/baremetal-image: Remove "do_" prefix from image manifest Joshua Watt
2026-06-09 22:15 ` [OE-core][PATCH 2/5] spdx: Reformat Joshua Watt
2026-06-09 22:15 ` [OE-core][PATCH 3/5] spdx: Add ability for deploy tasks to create SPDX Joshua Watt
2026-06-09 22:15 ` [OE-core][PATCH 4/5] Add SPDX deploy tasks Joshua Watt
2026-06-09 22:31 ` Patchtest results for " patchtest
2026-06-10 6:17 ` Mikko Rapeli
2026-06-10 7:46 ` Richard Purdie
2026-06-09 22:15 ` [OE-core][PATCH 5/5] spdx: Replace do_create_image_spdx with deploy task Joshua Watt
2026-06-10 13:17 ` [OE-core][PATCH 0/5] Implement SPDX for deploy tasks Mathieu Dubois-Briand
2026-06-11 18:46 ` Joshua Watt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox