On Wed, Jan 7, 2026 at 11:15 AM <[email protected]> wrote:
>
> From: Stefano Tondo <[email protected]>
>
> Add hasConcludedLicense relationship to SBOM packages with support for
> manual license conclusion override via SPDX_CONCLUDED_LICENSE variable.
>
> The concluded license represents the license determination after manual
> or external license analysis. This should be set manually in recipes or
> layers when:
>
> 1. Manual license review identifies differences from the declared LICENSE
> 2. External license scanning tools detect additional license information
> 3. Legal review concludes a different license applies
>
> The hasConcludedLicense relationship is ONLY added to the SBOM when
> SPDX_CONCLUDED_LICENSE is explicitly set. When unset or empty, no
> concluded license is included in the SBOM, correctly indicating that
> no license analysis was performed (per SPDX semantics).
>
> When differences from the declared LICENSE are found, users should:
>
> 1. Preferably: Correct the LICENSE field in the recipe and contribute
>    the fix upstream to OpenEmbedded
> 2. Alternatively: Set SPDX_CONCLUDED_LICENSE locally in your layer when
>    upstream contribution is not immediately possible or when the license
>    conclusion is environment-specific
>
> The implementation checks both package-specific overrides
> (SPDX_CONCLUDED_LICENSE:${PN}) and the global variable, allowing
> per-package license conclusions when needed.
>
> The concluded license expression is automatically de-duplicated by
> add_license_expression() to avoid redundant license objects in the SBOM.
>
> The variable is initialized in spdx-common.bbclass with comprehensive
> documentation explaining its purpose, usage guidelines, and examples.
>
> Example usage in recipe or layer:
>   SPDX_CONCLUDED_LICENSE = "MIT & Apache-2.0"
>   SPDX_CONCLUDED_LICENSE:${PN} = "MIT & Apache-2.0"
>
> Signed-off-by: Stefano Tondo <[email protected]>

LGTM, Thanks

Reviewed-by: Joshua Watt <[email protected]>

> ---
>  meta/classes/spdx-common.bbclass | 16 ++++++++++++++++
>  meta/lib/oe/spdx30_tasks.py      | 18 ++++++++++++++++--
>  2 files changed, 32 insertions(+), 2 deletions(-)
>
> diff --git a/meta/classes/spdx-common.bbclass 
> b/meta/classes/spdx-common.bbclass
> index ca0416d1c7..3110230c9e 100644
> --- a/meta/classes/spdx-common.bbclass
> +++ b/meta/classes/spdx-common.bbclass
> @@ -36,6 +36,22 @@ SPDX_LICENSES ??= 
> "${COREBASE}/meta/files/spdx-licenses.json"
>
>  SPDX_CUSTOM_ANNOTATION_VARS ??= ""
>
> +SPDX_CONCLUDED_LICENSE ??= ""
> +SPDX_CONCLUDED_LICENSE[doc] = "The license concluded by manual or external \
> +    license analysis. This should only be set when explicit license analysis 
> \
> +    (manual review or external scanning tools) has been performed and a 
> license \
> +    conclusion has been reached. When unset or empty, no concluded license 
> is \
> +    included in the SBOM, indicating that no license analysis was performed. 
> \
> +    When differences from the declared LICENSE are found, the preferred 
> approach \
> +    is to correct the LICENSE field in the recipe and contribute the fix 
> upstream \
> +    to OpenEmbedded. Use this variable locally only when upstream 
> contribution is \
> +    not immediately possible or when the license conclusion is 
> environment-specific. \
> +    Supports package-specific overrides via SPDX_CONCLUDED_LICENSE:${PN}. \
> +    This allows tracking license analysis results in SBOM while maintaining 
> recipe \
> +    LICENSE field for build compatibility. \
> +    Example: SPDX_CONCLUDED_LICENSE = 'MIT & Apache-2.0' or \
> +    SPDX_CONCLUDED_LICENSE:${PN} = 'MIT & Apache-2.0'"
> +
>  SPDX_MULTILIB_SSTATE_ARCHS ??= "${SSTATE_ARCHS}"
>
>  python () {
> diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py
> index 286a08ed9b..a99b017c26 100644
> --- a/meta/lib/oe/spdx30_tasks.py
> +++ b/meta/lib/oe/spdx30_tasks.py
> @@ -636,7 +636,7 @@ def create_spdx(d):
>              set_var_field(
>                  "HOMEPAGE", spdx_package, "software_homePage", 
> package=package
>              )
> -
> +
>              # Add summary with fallback to DESCRIPTION
>              summary = None
>              if package:
> @@ -651,7 +651,7 @@ def create_spdx(d):
>                  summary = f"Package {package or d.getVar('PN')}"
>              if summary:
>                  spdx_package.summary = summary
> -
> +
>              set_var_field("DESCRIPTION", spdx_package, "description", 
> package=package)
>
>              if d.getVar("SPDX_PACKAGE_URL:%s" % package) or 
> d.getVar("SPDX_PACKAGE_URL"):
> @@ -713,6 +713,20 @@ def create_spdx(d):
>                  [oe.sbom30.get_element_link_id(package_spdx_license)],
>              )
>
> +            # Add concluded license relationship if manually set
> +            # Only add when license analysis has been explicitly performed
> +            concluded_license_str = d.getVar("SPDX_CONCLUDED_LICENSE:%s" % 
> package) or d.getVar("SPDX_CONCLUDED_LICENSE")
> +            if concluded_license_str:
> +                concluded_spdx_license = add_license_expression(
> +                    d, build_objset, concluded_license_str, license_data
> +                )
> +
> +                pkg_objset.new_relationship(
> +                    [spdx_package],
> +                    oe.spdx30.RelationshipType.hasConcludedLicense,
> +                    [oe.sbom30.get_element_link_id(concluded_spdx_license)],
> +                )
> +
>              # NOTE: CVE Elements live in the recipe collection
>              all_cves = set()
>              for status, cves in cve_by_status.items():
> --
> 2.52.0
>
-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#229029): 
https://lists.openembedded.org/g/openembedded-core/message/229029
Mute This Topic: https://lists.openembedded.org/mt/117139043/21656
Group Owner: [email protected]
Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub 
[[email protected]]
-=-=-=-=-=-=-=-=-=-=-=-

Reply via email to