diff mbox series

[4/4] spdx30_tasks: Add source package PURL support

Message ID 20260107180951.140895-4-stondo@gmail.com
State Changes Requested
Headers show
Series None | expand

Commit Message

Stefano Tondo Jan. 7, 2026, 6:09 p.m. UTC
From: Stefano Tondo <stefano.tondo.ext@siemens.com>

- Move generate_purl() to module level for broader accessibility
  (fixes NameError when called from add_download_files)
- Add PURL generation for source packages with type=source qualifier
- Follows ECMA-427 PURL spec (similar to Maven's classifier=sources)

This allows source packages to be properly identified with PURLs
like pkg:yocto/core/glibc@2.42+git?type=source, distinguishing
them from runtime packages while maintaining compatibility with
the PURL specification.
---
 meta/lib/oe/spdx30_tasks.py | 68 ++++++++++++++++++++-----------------
 1 file changed, 37 insertions(+), 31 deletions(-)
diff mbox series

Patch

diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py
index c685b649b3..95ec4ebafb 100644
--- a/meta/lib/oe/spdx30_tasks.py
+++ b/meta/lib/oe/spdx30_tasks.py
@@ -576,9 +576,14 @@  def add_download_files(d, objset):
             if dep_version:
                 dl.software_packageVersion = dep_version
 
-            # Add PURL if generated
+            # Add PURL if generated for dependencies, otherwise generate recipe PURL for source
             if dep_purl:
                 dl.software_packageUrl = dep_purl
+            elif primary_purpose == oe.spdx30.software_SoftwarePurpose.source:
+                # For the main recipe source, generate PURL with type=source qualifier
+                recipe_purl = generate_purl(d)
+                # Add type=source qualifier to distinguish from runtime packages
+                dl.software_packageUrl = f"{recipe_purl}?type=source"
 
             if fd.method.supports_checksum(fd):
                 # TODO Need something better than hard coding this
@@ -601,6 +606,37 @@  def add_download_files(d, objset):
     return inputs
 
 
+def generate_purl(d, package=None):
+    """
+    Generate Package URL (purl) for a package according to Yocto PURL spec.
+    Format: pkg:yocto/<LAYERNAME>/<BPN>@<PV>
+
+    See: https://github.com/package-url/purl-spec/pull/372
+    """
+    bpn = d.getVar("BPN")
+    pv = d.getVar("PV")
+
+    # Get layer name using FILE_LAYERNAME
+    # This is the correct variable that contains the layer name from BBFILE_COLLECTIONS
+    # (BBFILE_COLLECTIONS itself is not available outside of layer.conf)
+    layer = d.getVar("FILE_LAYERNAME")
+
+    if not layer:
+        layer = "core"  # Default to core if layer detection fails
+
+    # For sub-packages, use BPN (base package name)
+    # Per spec: BPN has prefixes/suffixes removed
+    name = bpn
+
+    # Normalize name per PURL spec (lowercase only)
+    # Note: Underscores are not allowed in recipe names
+    name = name.lower()
+
+    purl = f"pkg:yocto/{layer}/{name}@{pv}"
+
+    return purl
+
+
 def set_purposes(d, element, *var_names, force_purposes=[]):
     purposes = force_purposes[:]
 
@@ -634,36 +670,6 @@  def create_spdx(d):
         if val:
             setattr(obj, name, val)
 
-    def generate_purl(d, package=None):
-        """
-        Generate Package URL (purl) for a package according to Yocto PURL spec.
-        Format: pkg:yocto/<LAYERNAME>/<BPN>@<PV>
-
-        See: https://github.com/package-url/purl-spec/pull/372
-        """
-        bpn = d.getVar("BPN")
-        pv = d.getVar("PV")
-
-        # Get layer name using FILE_LAYERNAME
-        # This is the correct variable that contains the layer name from BBFILE_COLLECTIONS
-        # (BBFILE_COLLECTIONS itself is not available outside of layer.conf)
-        layer = d.getVar("FILE_LAYERNAME")
-
-        if not layer:
-            layer = "core"  # Default to core if layer detection fails
-
-        # For sub-packages, use BPN (base package name)
-        # Per spec: BPN has prefixes/suffixes removed
-        name = bpn
-
-        # Normalize name per PURL spec (lowercase only)
-        # Note: Underscores are not allowed in recipe names
-        name = name.lower()
-
-        purl = f"pkg:yocto/{layer}/{name}@{pv}"
-
-        return purl
-
     license_data = oe.spdx_common.load_spdx_license_data(d)
 
     deploydir = Path(d.getVar("SPDXDEPLOY"))