# A Deep Dive into Yocto's SPDX 2.2 Pipeline

> Part 2 of the Yocto SBOM series. How the create-spdx-2.2.bbclass produces SPDX documents during a Yocto build, the three core BitBake tasks involved, and the document-linking model that ties everything together.

- **Author:** Joshua Watt
- **Published:** 2026-05-12
- **Categories:** Guide
- **Tags:** Sbom, Yocto, Openembedded, Spdx, Bitbake, Embedded-Linux
- **Updated:** 2026-09-20

**TL;DR:** The SPDX 2.2 pipeline in Yocto has been stable since Honister (Yocto 3.4). It produces standards-compliant SBOMs through three core BitBake tasks: do_create_spdx, do_create_runtime_spdx, and do_create_image_spdx. Each recipe gets its own SPDX document, linked together by external document references, and bundled into a tarball at image time. Its biggest gap: no native VEX support.

The SPDX 2.2 implementation in the Yocto Project has been stable since the Honister release (Yocto 3.4, October 2021). It is the established, widely understood pipeline that most Yocto users have been relying on, and for good reason. It produces standards-compliant SBOMs with rich per-package metadata that you can hand off to most SBOM tooling today.

This post is part 2 of a 6-part series on how Yocto generates SBOMs. If you have not read [Part 1: How Yocto Generates SBOMs Behind the Scenes](https://sbomify.com/2026/05/05/yocto-sbom-deep-dive-introduction/), it covers why build-time SBOM generation matters and the high-level architecture.

## Key Variables and Configuration

When `create-spdx-2.2.bbclass` is loaded, it sets up several directories and control variables:

```bash
DEPLOY_DIR_SPDX ??= "${DEPLOY_DIR}/spdx/${MACHINE}"
SPDXDIR ??= "${WORKDIR}/spdx"
SPDXDEPLOY = "${SPDXDIR}/deploy"
SPDXWORK = "${SPDXDIR}/work"

SPDX_UUID_NAMESPACE ??= "sbom.openembedded.org"
SPDX_NAMESPACE_PREFIX ??= "http://spdx.org/spdxdocs"
```

The `SPDXDIR` within each recipe's `WORKDIR` is the scratch space where intermediate SPDX data is assembled. `DEPLOY_DIR_SPDX` is the shared location where finalized SPDX documents are staged before being rolled up into the image-level output.

The `SPDX_UUID_NAMESPACE` and `SPDX_NAMESPACE_PREFIX` are used to generate document namespaces, the globally unique URIs that identify each SPDX document. These are constructed by combining the prefix with the recipe name and a UUID generated from a hash of the recipe's content.

If you are publishing SPDX documents generated by Yocto, it is recommended that you redefine `SPDX_UUID_NAMESPACE` and `SPDX_NAMESPACE_PREFIX` to a URL owned by your organization to differentiate them from other producers.

## Task 1: `do_create_spdx`, the Per-Recipe Core Task

The `do_create_spdx` task is the workhorse. It runs after `do_package` (so all packaging information is available) and produces SPDX documents for the recipe and each package it generates.

Here is what happens step by step.

### Step 1: Create the Recipe-Level Document

The task creates an SPDX document for the recipe itself. This "recipe document" contains:

- A document header with creation information (tool name, organization, timestamp)
- A document namespace constructed from the recipe name and a UUID
- A top-level `SPDXPackage` object representing the recipe, populated with:
  - `name` from the recipe name (`PN`)
  - `versionInfo` from the recipe version (`PV`)
  - `supplier` set to `Organization: OpenEmbedded`
  - `licenseDeclared` from the recipe's `LICENSE` variable
  - `SPDXID` which uniquely identifies the recipe within the document

### Step 2: Add Source Information

For each entry in `SRC_URI`, the task records the download location. If `SPDX_INCLUDE_SOURCES` is enabled, it goes further: it walks the unpacked source tree (`${S}`), calculates SHA-1 and SHA-256 checksums for each file, and creates `SPDXFile` objects for them. This is expensive (which is why it is off by default), but it allows you to trace any individual source file back through the SBOM.

### Step 3: Process Each Package

A single recipe may produce multiple packages (for example, `bash` produces `bash`, `bash-dbg`, `bash-doc`, and so on). For _each_ package, the task creates a separate SPDX document containing:

- A new `SPDXPackage` object representing the runtime package
- An `externalDocumentRef` linking back to the recipe-level document
- A `GENERATED_FROM` relationship from the package back to the recipe
- `SPDXFile` objects for every file installed by the package, including their checksums

The checksums are calculated by walking `${PKGDEST}/<package>/`, the staging area where BitBake has already laid out each package's files.

### Step 4: Write Output

The recipe document is written to `${SPDXDIR}/deploy/` as `recipe-<recipename>.spdx.json`. Each package document is written as `<packagename>.spdx.json`. These are then deployed to `${DEPLOY_DIR_SPDX}`.

## Task 2: `do_create_runtime_spdx`, Runtime Dependencies

A second task, `do_create_runtime_spdx`, handles the dependency relationships between packages. This task exists separately because runtime dependency information is not available until all recipes have been processed (BitBake needs the full dependency graph).

This task reads the `RDEPENDS` for each package and adds `DEPENDS_ON` relationships to the package's SPDX document via external document references. For example, if `bash` depends on `glibc`, the `bash` SPDX document will reference the `glibc` SPDX document and declare a dependency relationship.

## Task 3: `do_create_image_spdx`, Image Aggregation

When building an image recipe, the `create-spdx-image` class adds a `do_create_image_spdx` task that aggregates all per-recipe SPDX documents into the final deliverable.

Here is what the image task produces:

1. **`IMAGE-MACHINE.spdx.json`**: The top-level SPDX document describing the image. It contains a single `SPDXPackage` for the image itself, `externalDocumentRefs` pointing to every included package, and `CONTAINS` relationships linking the image package to each package.
2. **`IMAGE-MACHINE.spdx.index.json`**: An index file listing all the individual SPDX documents that make up the complete SBOM.
3. **`IMAGE-MACHINE.spdx.tar.zst`**: A compressed archive containing all individual SPDX documents (recipe-level and package-level).

## The Document-Linking Challenge in SPDX 2.2

One of the key architectural decisions in the SPDX 2.2 implementation is the use of separate _documents_ linked by external document references. Rather than producing one monolithic SPDX file, Yocto creates hundreds of individual SPDX documents (one per recipe, one per package) and links them together by SPDX external document references, then places all of the individual documents into a tarball with an index file for the final deliverable.

This was a pragmatic choice driven by some limitations in SPDX 2.2. The namespace and document structure of SPDX 2.2 makes it hard to combine documents without risking ID collisions or losing relationship information. The tarball approach side-steps this by simply bundling everything together and relying on the index file for navigation.

The downside is that tooling consuming these SBOMs needs to understand the document-linking model. You cannot just load a single JSON file and see everything. You need to extract the archive and follow the external document references.

## The VEX Gap in SPDX 2.2

One significant limitation of SPDX 2.2 is that it has no standard way to include vulnerability information. There is simply no mechanism in the SPDX 2.2 specification to embed VEX data or CVE assessments within the SBOM itself.

This means that if you are using SPDX 2.2 and want vulnerability tracking, you need a separate workflow. You can use the `cve-check` class to generate a standalone CVE report (typically as `cve-summary.json`), but that report lives outside of the SBOM and needs to be correlated with it manually or through external tooling. You can also produce a separate VEX document (for example, in OpenVEX or CycloneDX VEX format) and distribute it alongside your SPDX 2.2 SBOM, but the two are not structurally connected.

This separation is one of the primary motivations for moving to SPDX 3.0, which addresses the problem head-on.

## What the SPDX 2.2 Output Looks Like

A typical package SPDX document (e.g., `bash.spdx.json`) looks something like:

```json
{
  "SPDXID": "SPDXRef-DOCUMENT",
  "creationInfo": {
    "comment": "This document was created by analyzing packages created during the build.",
    "created": "2025-02-18T21:58:47Z",
    "creators": [
      "Tool: OpenEmbedded Core create-spdx.bbclass",
      "Organization: OpenEmbedded ()",
      "Person: N/A ()"
    ],
    "licenseListVersion": "3.14"
  },
  "dataLicense": "CC0-1.0",
  "documentNamespace": "http://spdx.org/spdxdocs/bash-0139ef99-a375-59f8-9ada-833f50f987d3",
  "externalDocumentRefs": [
    {
      "checksum": {
        "algorithm": "SHA1",
        "checksumValue": "e9353b8e26447ef425aa740060f57411420c817a"
      },
      "externalDocumentId": "DocumentRef-recipe-bash",
      "spdxDocument": "http://spdx.org/spdxdocs/recipe-bash-ad5747dc-7b5a-562d-aabf-dfb516e7095d"
    }
  ],
  "packages": [...],
  "files": [...],
  "relationships": [...]
}
```

Each file within the package gets a detailed entry with checksums:

```json
{
  "SPDXID": "SPDXRef-PackagedFile-bash-1",
  "checksums": [
    { "algorithm": "SHA1", "checksumValue": "14cd85414db85903029caca727345272de829a69" },
    { "algorithm": "SHA256", "checksumValue": "..." }
  ],
  "fileName": "/usr/bin/bash"
}
```

---

**Series: How Yocto Generates SBOMs Behind the Scenes**

- Part 1: [How Yocto Generates SBOMs Behind the Scenes](https://sbomify.com/2026/05/05/yocto-sbom-deep-dive-introduction/)
- Part 2: A Deep Dive into Yocto's SPDX 2.2 Pipeline _(this post)_
- Part 3: [SPDX 3.0 in Yocto: What Changed and Why It Matters](https://sbomify.com/2026/05/19/yocto-spdx-3-0-overview/)
- Part 4: VEX in the SBOM: How Yocto Embeds Vulnerability Assessments _(coming soon)_
- Part 5: Yocto SBOM in Production: Configuration, Tooling, and What's Still Missing _(coming soon)_
- Part 6: You Have an SBOM. Now What? Making Yocto SBOMs Operational for the CRA _(coming soon)_

---

From sbomify: https://sbomify.com/2026/05/12/yocto-spdx-2-2-pipeline/
