On Fri, Mar 6, 2026 at 7:00 AM Stefano Tondo via
lists.openembedded.org <[email protected]>
wrote:
>
> Add a function that identifies ecosystem-specific PURLs (cargo, golang,
> pypi, npm, cpan, nuget, maven) for dependency packages, working alongside
> oe.purl.get_base_purl() which provides pkg:yocto PURLs.
>
> Key design decision: Does NOT return pkg:generic fallback. This ensures:
> - No overlap with the base pkg:yocto generation
> - Packages get BOTH purls: pkg:yocto/layer/pkg@ver AND pkg:cargo/pkg@ver
> - Maximum traceability for compliance tools
>
> Detects ecosystems via:
> - Unambiguous file extensions (.crate for Rust)
> - Recipe inheritance (pypi, npm, cpan, nuget, maven classes)
> - BitBake variables (GO_IMPORT, PYPI_PACKAGE, MAVEN_GROUP_ID)
>
> Signed-off-by: Stefano Tondo <[email protected]>
> ---
>  meta/lib/oe/spdx30_tasks.py | 113 ++++++++++++++++++++++++++++++++++++
>  1 file changed, 113 insertions(+)
>
> diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py
> index 0888d9d7e4..11945a622d 100644
> --- a/meta/lib/oe/spdx30_tasks.py
> +++ b/meta/lib/oe/spdx30_tasks.py
> @@ -13,12 +13,125 @@ import oe.spdx30
>  import oe.spdx_common
>  import oe.sdk
>  import os
> +import re
>
>  from contextlib import contextmanager
>  from datetime import datetime, timezone
>  from pathlib import Path
>
>
> +
> +def extract_dependency_metadata(d, file_name):
> +    """Extract ecosystem-specific PURL for dependency packages.
> +
> +    Uses recipe metadata to identify ecosystem PURLs (cargo, golang, pypi,
> +    npm, cpan, nuget, maven). Returns (version, purl) or (None, None).
> +    Does NOT return pkg:generic; base pkg:yocto is handled by 
> get_base_purl().
> +    """
> +
> +    pv = d.getVar("PV")
> +    version = pv if pv else None
> +    purl = None
> +
> +    # Rust crate (.crate extension is unambiguous)
> +    if file_name.endswith('.crate'):
> +        crate_match = 
> re.match(r'^(.+?)-(\d+\.\d+\.\d+(?:\.\d+)?(?:[-+][\w.]+)?)\.crate$', 
> file_name)
> +        if crate_match:
> +            name = crate_match.group(1)
> +            version = crate_match.group(2)
> +            purl = f"pkg:cargo/{name}@{version}"
> +            return (version, purl)
> +
> +    # Go module via GO_IMPORT variable
> +    go_import = d.getVar("GO_IMPORT")
> +    if go_import and version:
> +        purl = f"pkg:golang/{go_import}@{version}"
> +        return (version, purl)
> +
> +    # Go module from filename with explicit hosting domain
> +    go_match = re.match(
> +        
> r'^((?:github|gitlab|gopkg|golang|go\.googlesource)\.com\.[\w.]+(?:\.[\w-]+)*?)-(v?\d+\.\d+\.\d+(?:[-+][\w.]+)?)\.',
> +        file_name
> +    )
> +    if go_match:
> +        module_path = go_match.group(1).replace('.', '/', 1)
> +        parts = module_path.split('/', 1)
> +        if len(parts) == 2:
> +            domain = parts[0]
> +            path = parts[1].replace('.', '/')
> +            module_path = f"{domain}/{path}"
> +
> +        version = go_match.group(2)
> +        purl = f"pkg:golang/{module_path}@{version}"
> +        return (version, purl)
> +
> +    # PyPI package
> +    if bb.data.inherits_class("pypi", d) and version:

I'm not really a big fan of bb.data.inherits_class. I think it makes
more sense for pypi.bbclass to implement a way to do this behavior and
set the recipe PURL when that class is inherited. Same for the rest of
these, and maybe the GO_IMPORT case above?

> +        pypi_package = d.getVar("PYPI_PACKAGE")
> +        if pypi_package:
> +            # Normalize per PEP 503
> +            name = re.sub(r"[-_.]+", "-", pypi_package).lower()
> +            purl = f"pkg:pypi/{name}@{version}"
> +            return (version, purl)
> +
> +    # NPM package
> +    if bb.data.inherits_class("npm", d) and version:
> +        bpn = d.getVar("BPN")
> +        if bpn:
> +            name = bpn[4:] if bpn.startswith('npm-') else bpn
> +            purl = f"pkg:npm/{name}@{version}"
> +            return (version, purl)
> +
> +    # CPAN package
> +    if bb.data.inherits_class("cpan", d) and version:
> +        bpn = d.getVar("BPN")
> +        if bpn:
> +            if bpn.startswith('perl-'):
> +                name = bpn[5:]
> +            elif bpn.startswith('libperl-'):
> +                name = bpn[8:]
> +            else:
> +                name = bpn
> +            purl = f"pkg:cpan/{name}@{version}"
> +            return (version, purl)
> +
> +    # NuGet package
> +    if (bb.data.inherits_class("nuget", d) or 
> bb.data.inherits_class("dotnet", d)) and version:
> +        bpn = d.getVar("BPN")
> +        if bpn:
> +            if bpn.startswith('dotnet-'):
> +                name = bpn[7:]
> +            elif bpn.startswith('nuget-'):
> +                name = bpn[6:]
> +            else:
> +                name = bpn
> +            purl = f"pkg:nuget/{name}@{version}"
> +            return (version, purl)
> +
> +    # Maven package
> +    if bb.data.inherits_class("maven", d) and version:
> +        group_id = d.getVar("MAVEN_GROUP_ID")
> +        artifact_id = d.getVar("MAVEN_ARTIFACT_ID")
> +
> +        if group_id and artifact_id:
> +            purl = f"pkg:maven/{group_id}/{artifact_id}@{version}"
> +            return (version, purl)
> +        else:
> +            bpn = d.getVar("BPN")
> +            if bpn:
> +                if bpn.startswith('maven-'):
> +                    name = bpn[6:]
> +                elif bpn.startswith('java-'):
> +                    name = bpn[5:]
> +                else:
> +                    name = bpn
> +                purl = f"pkg:maven/{name}@{version}"
> +                return (version, purl)
> +
> +    # Base pkg:yocto PURL is handled by oe.purl.get_base_purl()
> +    return (version, None)
> +
> +
>  def walk_error(err):
>      bb.error(f"ERROR walking {err.filename}: {err}")
>
> --
> 2.53.0
>
>
> 
>
-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#232620): 
https://lists.openembedded.org/g/openembedded-core/message/232620
Mute This Topic: https://lists.openembedded.org/mt/118170494/21656
Group Owner: [email protected]
Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub 
[[email protected]]
-=-=-=-=-=-=-=-=-=-=-=-

Reply via email to