From patchwork Fri Feb 20 15:40:36 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 81495 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 37048C5AC9F for ; Fri, 20 Feb 2026 15:41:49 +0000 (UTC) Received: from mail-oo1-f66.google.com (mail-oo1-f66.google.com [209.85.161.66]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.318.1771602099702799581 for ; Fri, 20 Feb 2026 07:41:39 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=ZQ1XozG6; spf=pass (domain: gmail.com, ip: 209.85.161.66, mailfrom: jpewhacker@gmail.com) Received: by mail-oo1-f66.google.com with SMTP id 006d021491bc7-6775a46c6dfso720014eaf.2 for ; Fri, 20 Feb 2026 07:41:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771602099; x=1772206899; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=kZsTCBgME1PNRjxHczDMbLqJbIl+DF2WK2U4einEvRE=; b=ZQ1XozG6cqoiA5CbVVvvEjUbnftI6QkSYDV0Un/zOtYkRu1Y8a6cn8Byx0jF0Mshwa d5ASsrQXGEdwJZ5/f+Gq7651JaE7MdaP1Jtx7Jwk6/hh/tAJ/BI9bGNnddwUz34ekwen Gw2jU56Xv5bAPnexVomEAWqxWiIbFMW5pdnj6mhZgrIjdWSexaEevEsCraJAXhdTptv1 ekBnMIWyb4ERCASbhD07fIFHd6b1/kiZ/4AZIMu6d3cZcXAWHuWq/bd6ywKFnZp0lOlA iCp0f1PgKi+2wJM6QLOKMNcWtHYXD/pvTr7ic/i02NTXHPmAZAjjqg/6KyO3B3Bs2m9a ceiw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771602099; x=1772206899; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=kZsTCBgME1PNRjxHczDMbLqJbIl+DF2WK2U4einEvRE=; b=eDqSbw1AOt4T5Udi3djpan4B2LNfELn9h3R4bUded0Z2jx8hW1wJ9jN2/+ZM4vw4vc O0ZczYVP0fQNNzpPEtClnV7//MQJ38aVuJa0wi46eFU+RUFDxZ9CUY+yMdIpxFktic1l 7j0A+Fhu0ZxcCCt9tTkIXvvhOkXSAYDqYzwoj9TwRamH6gJ7A9PNnPUE5G+qvPxZRL3s vvBy4nwhdoMXoNBveBRnvmwjg6upyNVhUTX5R6yvD6qerTqIvaQb6O2AhfGXGQhWuCeN nYoU+Fxi8EnY2h51Ka/OGEGzfHUkDMXR7lTg3DY/KX20jheSNlFf9S4wuTSxHBUJ46iw kzcQ== X-Gm-Message-State: AOJu0Yz7OhNo+CVTeSvS9m0XwSiSbfWv3MVP85V5P0/bNCNJNI6r3RZy zfTYosm6ECnUgyczcLvIy4YpaLH5v8sXOShEiJGMa6Q+RAKVvwTJgeCFJKe4ho+m X-Gm-Gg: AZuq6aK0OohKjYCG/vlxI3XyoGYoVuvwngTpPnc/VN6FBpam7nB0GHEpwF3VkZ1r6H6 2bDKxU4cwqmWdG4VZNk8b15MjkY7sANqApVnuOh+n65UwSdngy58keWJrw3zQrKLnNwAYdNad7I Zx9xENSa5x+YtrKavpu7PQlLGxJsYEvcdNxjcV4RkcEUin0w7DDSexNFUIEyxAjd2ouHACcpPgp fyPLOc1vo0ZADQ2Pbyk5l6JsP4XcACvR3Tt9mkGIcDmUZcuJ+StA6d2fFRV2lyFFxeZAax3W/Nu Iqhq9CwxKRysGGGRhQY5BxFdu/EHVEbiVc/owXZMUM6X/dJviSbgC57WYhxXfM2CYzV0nxHVV/J F8AgnhAjRG1D9Q/p0Rd6baRL9y6L1KM4+ybeLa8KASRZFXdocALVdX71kfCV/NuAZCbLwimLEAc lJc6v8rLIWpdlW74wykZTK X-Received: by 2002:a05:6820:1ca8:b0:677:ad96:1b44 with SMTP id 006d021491bc7-679c424cd55mr189769eaf.10.1771602098703; Fri, 20 Feb 2026 07:41:38 -0800 (PST) Received: from localhost.localdomain ([2601:282:4200:11c0::6492]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6772583294bsm15127188eaf.14.2026.02.20.07.41.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Feb 2026 07:41:38 -0800 (PST) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: benjamin.robin@bootlin.com, Joshua Watt Subject: [OE-core][PATCH 1/9] llvm-project-source: Use allarch.bbclass Date: Fri, 20 Feb 2026 08:40:36 -0700 Message-ID: <20260220154123.376880-2-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260220154123.376880-1-JPEWhacker@gmail.com> References: <20260220154123.376880-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Fri, 20 Feb 2026 15:41:49 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231520 Converts the recipe to use allarch.bbclass. This is necessary because SSTATE_PKGARCH is set to "allarch" based on if allarch is inherited or not. If it is not, SSTATE_PKGARCH has the value "all", which means any data written out based on it cannot be found (because "all" is not in SSTATE_ARCHS) Signed-off-by: Joshua Watt --- meta/recipes-devtools/clang/llvm-project-source.inc | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/meta/recipes-devtools/clang/llvm-project-source.inc b/meta/recipes-devtools/clang/llvm-project-source.inc index 13e54efbc2..6bb595b7bc 100644 --- a/meta/recipes-devtools/clang/llvm-project-source.inc +++ b/meta/recipes-devtools/clang/llvm-project-source.inc @@ -5,7 +5,7 @@ deltask do_populate_sysroot deltask do_populate_lic RM_WORK_EXCLUDE += "${PN}" -inherit nopackages +inherit nopackages allarch PN = "llvm-project-source-${PV}" WORKDIR = "${TMPDIR}/work-shared/llvm-project-source-${PV}-${PR}" @@ -14,14 +14,8 @@ SSTATE_SWSPEC = "sstate:llvm-project-source::${PV}:${PR}::${SSTATE_VERSION}:" STAMP = "${STAMPS_DIR}/work-shared/llvm-project-source-${PV}-${PR}" STAMPCLEAN = "${STAMPS_DIR}/work-shared/llvm-project-source-${PV}-*" -INHIBIT_DEFAULT_DEPS = "1" DEPENDS = "" PACKAGES = "" -TARGET_ARCH = "allarch" -TARGET_AS_ARCH = "none" -TARGET_CC_ARCH = "none" -TARGET_LD_ARCH = "none" -TARGET_OS = "linux" baselib = "lib" PACKAGE_ARCH = "all" From patchwork Fri Feb 20 15:40:37 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 81496 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2A1CAC5ACA8 for ; Fri, 20 Feb 2026 15:41:49 +0000 (UTC) Received: from mail-oo1-f66.google.com (mail-oo1-f66.google.com [209.85.161.66]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.319.1771602100407910617 for ; Fri, 20 Feb 2026 07:41:40 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=gj3oLB9h; spf=pass (domain: gmail.com, ip: 209.85.161.66, mailfrom: jpewhacker@gmail.com) Received: by mail-oo1-f66.google.com with SMTP id 006d021491bc7-679b072ed3aso1050547eaf.1 for ; Fri, 20 Feb 2026 07:41:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771602099; x=1772206899; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=7bs/2ESNOjlKv5F2a1L5fY1cfh3Mb2GOypfpxUG+ELw=; b=gj3oLB9hw04zMXPTSBT8RipWmTcItu+rmCnPZpcxTE1EeqS+8Yt+2ZNsVJLYv6vHrG 4Q38cA3BoVR2xtT1dD1oTU09Z/9+DGtP31v2zLfQCppk/2xs50WG4JVw4C0Vrp411Zdk KYaJAPoYrXrN2jGlftX3Hr2wyz/FQMNucvATr/6WQ3KaYrEElhU6lasZa1w68Lo7NZcl +tSGrkxN0BaC/MO4nBD67FBcHIqx90ETBpFKqlpKsBKTwPya9JSF5hBpw/Y9MEY1uq5u 5ItvbjtQKT/ChgTD25HNGhT4HI6mEN2oREX+ulEyLkJo9RV1xXVDzT1I7F1TGTRarsfR sULw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771602099; x=1772206899; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=7bs/2ESNOjlKv5F2a1L5fY1cfh3Mb2GOypfpxUG+ELw=; b=s6HivIPU1DGAvpOoVOMyhEYFODPQB4M6yvKmcTZXOwClJjG2l+lCwDu+BSZZ0JfzMm dNs9MEpj/VsqA+Jx4bS0kYuqKO8KoAb9QuzYyv5fN1N5pknQAVUwVOz2c7qI8Y+dGIjx Wsvbn5X4Qa09empEd05m5ll/7CgtdCwe3/gL6KsRef9JsCzlCmT4+GXsGk6/xBBTgRdB GRE9vJ5otjhnj0l65Od+ySfbe08lQ8tftDJcl3OLyLxBdjU0Iu66WldxoD7zIw4rrgFw LAaI3h7XYYjWNjrCkxhsIfOwkOrKoJcEJaZyWJEm1C+KqEgD3Q2Oo3fVETVS/MEwAV1c Zllg== X-Gm-Message-State: AOJu0YzPzKt0LM8ZQqYF4oL9PqV81AtbftAnDddMozDY4r/l+NTL2fLa 69HLUGOXoz3NXBmS1PAZKU25vOyHrVlABKa7L/3BmoRlrvXavBkCvPqWTA54u3oq X-Gm-Gg: AZuq6aKFyacmC7BPHPFV+QIhleIgp2TyJJevpiH/dNP6ZcXHouvKyUgEsbmzEQr/Onr OkbIK8BFPXK4lRXTHfh+dEe/ffYPRlc8Ir0ULGrWJY2G0CaqUsibKQLBOW+Z8bpWJNs5tTY00Km DFCPjBARMPwZ8i6kC/Bh/1hf7xxyOJc6n8lXMeQLb285JmujCxrRYCEjBU+bVgr5YApJBB+uQ7T zQI2+Z4fit+kbzagHTPwHUk72XncOI84EsV2oQoKPIfNDydwodCd3XLRvgB8Er4THL3sDaEZEAc V0X2PeJHPHDSEPMSqTQcoYRB0+r/3lOERP2AO7NIhHxSLaJeKZe6R1XUJH8vXRK7A/gDp5TeMR8 FL97nlxN7ln8ufZPZXV1nyrkPT/I+xGMny1oGbIzQyE9SwnNz26RWlt2rCz0HyRRmy97BcDBOh/ G1HeGnnTjoRvsEvThTLXpE X-Received: by 2002:a05:6820:1903:b0:663:859:c41e with SMTP id 006d021491bc7-679c4612ff7mr128314eaf.71.1771602099397; Fri, 20 Feb 2026 07:41:39 -0800 (PST) Received: from localhost.localdomain ([2601:282:4200:11c0::6492]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6772583294bsm15127188eaf.14.2026.02.20.07.41.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Feb 2026 07:41:39 -0800 (PST) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: benjamin.robin@bootlin.com, Joshua Watt Subject: [OE-core][PATCH 2/9] gcc-source: Use allarch.bbclass Date: Fri, 20 Feb 2026 08:40:37 -0700 Message-ID: <20260220154123.376880-3-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260220154123.376880-1-JPEWhacker@gmail.com> References: <20260220154123.376880-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Fri, 20 Feb 2026 15:41:49 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231521 Converts the recipe to use allarch.bbclass. This is necessary because SSTATE_PKGARCH is set to "allarch" based on if allarch is inherited or not. If it is not, SSTATE_PKGARCH has the value "all", which means any data written out based on it cannot be found (because "all" is not in SSTATE_ARCHS) Signed-off-by: Joshua Watt --- meta/recipes-devtools/gcc/gcc-source.inc | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/meta/recipes-devtools/gcc/gcc-source.inc b/meta/recipes-devtools/gcc/gcc-source.inc index 265bcf4bef..3ac679b1a6 100644 --- a/meta/recipes-devtools/gcc/gcc-source.inc +++ b/meta/recipes-devtools/gcc/gcc-source.inc @@ -1,11 +1,11 @@ -deltask do_configure -deltask do_compile -deltask do_install +deltask do_configure +deltask do_compile +deltask do_install deltask do_populate_sysroot -deltask do_populate_lic +deltask do_populate_lic RM_WORK_EXCLUDE += "${PN}" -inherit nopackages +inherit nopackages allarch PN = "gcc-source-${PV}" WORKDIR = "${TMPDIR}/work-shared/gcc-${PV}-${PR}" @@ -14,14 +14,8 @@ SSTATE_SWSPEC = "sstate:gcc::${PV}:${PR}::${SSTATE_VERSION}:" STAMP = "${STAMPS_DIR}/work-shared/gcc-${PV}-${PR}" STAMPCLEAN = "${STAMPS_DIR}/work-shared/gcc-${PV}-*" -INHIBIT_DEFAULT_DEPS = "1" DEPENDS = "" PACKAGES = "" -TARGET_ARCH = "allarch" -TARGET_AS_ARCH = "none" -TARGET_CC_ARCH = "none" -TARGET_LD_ARCH = "none" -TARGET_OS = "linux" baselib = "lib" PACKAGE_ARCH = "all" From patchwork Fri Feb 20 15:40:38 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 81489 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id D40E6C5AC67 for ; Fri, 20 Feb 2026 15:41:48 +0000 (UTC) Received: from mail-oo1-f47.google.com (mail-oo1-f47.google.com [209.85.161.47]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.320.1771602101503509956 for ; Fri, 20 Feb 2026 07:41:41 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=Jb0cODjv; spf=pass (domain: gmail.com, ip: 209.85.161.47, mailfrom: jpewhacker@gmail.com) Received: by mail-oo1-f47.google.com with SMTP id 006d021491bc7-662f9aeb765so791900eaf.3 for ; Fri, 20 Feb 2026 07:41:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771602100; x=1772206900; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ydqPVGw1Sb1iktq2I5TBeL+Cqs2RuZkHA+NGyyR4zto=; b=Jb0cODjviCJmMeCQYYik+kt/ilNTCCqVHD2o3+QbbJlmNIX495ibaWiJrOvKS2/aif 8EPKHQOJkuMe5pQT9VY00FhEwVNgfStFnZLQ0He/FWigeJ2TiMMylnFcyxrPZgxI16cA 3J4ePc1c0Hwn27YYhFHssZwI8EMMxau47lIGESPpIh8AT6BoepWnmXyi8ukPosuxLUIn xkMICbLvOCo/6tlm4Lh2O7M+0HO4hCoqwxABaBmL+x9u4SZvkVAnTjqdhF297gk/Shv4 FoWBUz9ZW7mDjdi55+ilYyGe1f6HSSppVHz2vSuAcAeONfD2UW18SiHj/LOl8nlfkjpf cP0w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771602100; x=1772206900; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=ydqPVGw1Sb1iktq2I5TBeL+Cqs2RuZkHA+NGyyR4zto=; b=EwXlzfI6dVGdWfUPorOce5FeRfhG16f5QKapSoVKN8c6HcZ/9msWV67rfNq1f2F5J2 fAPprEv2gE5u8jCCGJQ43z2XLLYS47BXhuJqncdLIMRUKmIGfQhhxsi9aywurKkqrQnT dIro9vq3FaRwBtRi8n/y3cdQw/wQcFlMwM6YcoRoCxn78wX5nc/J3LgajIFslXGDo8YA RUpbQhD2O0HpsN5gVSFZqhZaYKTHkSdOVenzOtekBu1LVX7x0uYQ5lI4WrXy0noQm3va xh+H8YpkjDyjyx2YPX1qomdD3UL3rn5v8hEFLw3noSth8+D6+FtVaemK1XGPWSiRA1I9 HAQQ== X-Gm-Message-State: AOJu0Yw59/YrThypr8UDYSE+XQSlPRZx5Ip2B4RdYb9gjwigHUxjlw/+ pNKPY8hOmqJ7+bZ4Nm5j0R6wvJyw1yfvcbwO8SPag3l4LuNmHMqTVOK1PM2Q8w== X-Gm-Gg: AZuq6aJ4OrCW8Ku9jr3CXE7zJHbbsY8xciR6JfDdj1nw2GsOYiboTrOna6C9btK+uci jhw+8FtzGJieCVegB1JlQ1AfeYzgkt+YfgRu3aJsVZC+xw7SeNC9ODGv+Rt8SbWonr68Q2s61mR E9xYy8DPcMs6MACW9g+F0nmBzkrXLANmCkT4Rfa5etg52kgbPbVs7s/osa1FrpnJSCvp4xaDcBy uMS2eJC3N/5Y7tVUJ0Rm4GInHEC7cVojLUEzkzDYFW1lGKnGaeSpe5yen9W7w4VzMKk9HRjiT6o KQVBmns7xAKAnmBHgfTwRf2GKDo01N2s+iuVSLwDY11MRhWTFdfWlg8adwANtswE9n5c+Shn7mG MQOb/6lG5T8DYQQiVdrB0PC4a8Dl3pxUDHunsrAobs1lsbam2mweGL14i1cFkJAFOsPcIyiEKOI ehHrY6ZN9FPOx9T9sietu3 X-Received: by 2002:a05:6820:1c86:b0:678:4076:8930 with SMTP id 006d021491bc7-679c4729284mr95945eaf.75.1771602100375; Fri, 20 Feb 2026 07:41:40 -0800 (PST) Received: from localhost.localdomain ([2601:282:4200:11c0::6492]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6772583294bsm15127188eaf.14.2026.02.20.07.41.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Feb 2026 07:41:39 -0800 (PST) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: benjamin.robin@bootlin.com, Joshua Watt Subject: [OE-core][PATCH 3/9] spdx3: Add recipe SPDX data Date: Fri, 20 Feb 2026 08:40:38 -0700 Message-ID: <20260220154123.376880-4-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260220154123.376880-1-JPEWhacker@gmail.com> References: <20260220154123.376880-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Fri, 20 Feb 2026 15:41:48 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231522 Adds a new package to the SPDX output that represents the recipe data for a given recipe. Importantly, this data contains only things that can be determined statically from only the recipe, so it doesn't require fetching or building anything. This means that build time dependencies and CVE information for recipes can be analyzed without needing to actually do any builds. Sadly, license data cannot be included because NO_GENERIC_LICENSE means that actual license text might only be available after do_fetch Signed-off-by: Joshua Watt --- meta/classes-global/sstate.bbclass | 4 +- meta/classes-global/staging.bbclass | 2 +- .../create-spdx-image-3.0.bbclass | 4 +- .../create-spdx-sdk-3.0.bbclass | 4 +- meta/classes-recipe/kernel.bbclass | 2 +- meta/classes-recipe/nospdx.bbclass | 1 + meta/classes/create-spdx-2.2.bbclass | 12 +- meta/classes/create-spdx-3.0.bbclass | 51 ++- meta/classes/spdx-common.bbclass | 13 +- meta/lib/oe/spdx30_tasks.py | 402 ++++++++++++------ 10 files changed, 343 insertions(+), 152 deletions(-) diff --git a/meta/classes-global/sstate.bbclass b/meta/classes-global/sstate.bbclass index 2fd29d7323..95c44f404e 100644 --- a/meta/classes-global/sstate.bbclass +++ b/meta/classes-global/sstate.bbclass @@ -954,7 +954,7 @@ def sstate_checkhashes(sq_data, d, siginfo=False, currentcount=0, summary=True, extrapath = d.getVar("NATIVELSBSTRING") + "/" else: extrapath = "" - + tname = bb.runqueue.taskname_from_tid(task)[3:] if tname in ["fetch", "unpack", "patch", "populate_lic", "preconfigure"] and splithashfn[2]: @@ -1116,7 +1116,7 @@ def setscene_depvalid(task, taskdependees, notneeded, d, log=None): logit("Considering setscene task: %s" % (str(taskdependees[task])), log) - directtasks = ["do_populate_lic", "do_deploy_source_date_epoch", "do_shared_workdir", "do_stash_locale", "do_gcc_stash_builddir", "do_create_spdx", "do_deploy_archives"] + directtasks = ["do_populate_lic", "do_deploy_source_date_epoch", "do_shared_workdir", "do_stash_locale", "do_gcc_stash_builddir", "do_create_spdx", "do_create_recipe_spdx", "do_deploy_archives"] def isNativeCross(x): return x.endswith("-native") or "-cross-" in x or "-crosssdk" in x or x.endswith("-cross") diff --git a/meta/classes-global/staging.bbclass b/meta/classes-global/staging.bbclass index 1008867a6c..b35bc9037f 100644 --- a/meta/classes-global/staging.bbclass +++ b/meta/classes-global/staging.bbclass @@ -654,7 +654,7 @@ addtask do_prepare_recipe_sysroot before do_configure after do_fetch python staging_taskhandler() { EXCLUDED_TASKS = ( "do_prepare_recipe_sysroot", - "do_create_spdx", + "do_create_recipe_spdx", ) bbtasks = e.tasklist for task in bbtasks: diff --git a/meta/classes-recipe/create-spdx-image-3.0.bbclass b/meta/classes-recipe/create-spdx-image-3.0.bbclass index 636ab14eb0..15a91e90e2 100644 --- a/meta/classes-recipe/create-spdx-image-3.0.bbclass +++ b/meta/classes-recipe/create-spdx-image-3.0.bbclass @@ -34,7 +34,7 @@ addtask do_create_rootfs_spdx after do_rootfs before do_image SSTATETASKS += "do_create_rootfs_spdx" do_create_rootfs_spdx[sstate-inputdirs] = "${SPDXROOTFSDEPLOY}" do_create_rootfs_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}" -do_create_rootfs_spdx[recrdeptask] += "do_create_spdx do_create_package_spdx" +do_create_rootfs_spdx[recrdeptask] += "do_create_recipe_spdx do_create_spdx do_create_package_spdx" do_create_rootfs_spdx[cleandirs] += "${SPDXROOTFSDEPLOY}" do_create_rootfs_spdx[file-checksums] += "${SPDX3_DEP_FILES}" @@ -76,7 +76,7 @@ do_create_image_sbom_spdx[sstate-inputdirs] = "${SPDXIMAGEDEPLOYDIR}" do_create_image_sbom_spdx[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}" do_create_image_sbom_spdx[stamp-extra-info] = "${MACHINE_ARCH}" do_create_image_sbom_spdx[cleandirs] = "${SPDXIMAGEDEPLOYDIR}" -do_create_image_sbom_spdx[recrdeptask] += "do_create_spdx do_create_package_spdx" +do_create_image_sbom_spdx[recrdeptask] += "do_create_recipe_spdx do_create_spdx do_create_package_spdx" do_create_image_sbom_spdx[file-checksums] += "${SPDX3_DEP_FILES}" python do_create_image_sbom_spdx_setscene() { diff --git a/meta/classes-recipe/create-spdx-sdk-3.0.bbclass b/meta/classes-recipe/create-spdx-sdk-3.0.bbclass index e5f220cdfa..a4b8ed3bf9 100644 --- a/meta/classes-recipe/create-spdx-sdk-3.0.bbclass +++ b/meta/classes-recipe/create-spdx-sdk-3.0.bbclass @@ -5,14 +5,14 @@ # # SPDX SDK tasks -do_populate_sdk[recrdeptask] += "do_create_spdx do_create_package_spdx" +do_populate_sdk[recrdeptask] += "do_create_recipe_spdx do_create_spdx do_create_package_spdx" do_populate_sdk[cleandirs] += "${SPDXSDKWORK}" do_populate_sdk[postfuncs] += "sdk_create_sbom" do_populate_sdk[file-checksums] += "${SPDX3_DEP_FILES}" POPULATE_SDK_POST_HOST_COMMAND:append:task-populate-sdk = " sdk_host_create_spdx" POPULATE_SDK_POST_TARGET_COMMAND:append:task-populate-sdk = " sdk_target_create_spdx" -do_populate_sdk_ext[recrdeptask] += "do_create_spdx do_create_package_spdx" +do_populate_sdk_ext[recrdeptask] += "do_create_recipe_spdx do_create_spdx do_create_package_spdx" do_populate_sdk_ext[cleandirs] += "${SPDXSDKEXTWORK}" do_populate_sdk_ext[postfuncs] += "sdk_ext_create_sbom" do_populate_sdk_ext[file-checksums] += "${SPDX3_DEP_FILES}" diff --git a/meta/classes-recipe/kernel.bbclass b/meta/classes-recipe/kernel.bbclass index f989b31c47..3a2c20dec2 100644 --- a/meta/classes-recipe/kernel.bbclass +++ b/meta/classes-recipe/kernel.bbclass @@ -904,7 +904,7 @@ python do_create_kernel_config_spdx() { bb.error(f"Failed to parse kernel config file: {e}") build, build_objset = oe.sbom30.find_root_obj_in_jsonld( - d, "recipes", f"recipe-{pn}", oe.spdx30.build_Build + d, "builds", f"build-{pn}", oe.spdx30.build_Build ) kernel_build = build_objset.add_root( diff --git a/meta/classes-recipe/nospdx.bbclass b/meta/classes-recipe/nospdx.bbclass index b20e28218b..90e14442ba 100644 --- a/meta/classes-recipe/nospdx.bbclass +++ b/meta/classes-recipe/nospdx.bbclass @@ -5,6 +5,7 @@ # deltask do_collect_spdx_deps +deltask do_create_recipe_spdx deltask do_create_spdx deltask do_create_spdx_runtime deltask do_create_package_spdx diff --git a/meta/classes/create-spdx-2.2.bbclass b/meta/classes/create-spdx-2.2.bbclass index 65d10d86db..f1ee0f9afd 100644 --- a/meta/classes/create-spdx-2.2.bbclass +++ b/meta/classes/create-spdx-2.2.bbclass @@ -399,6 +399,15 @@ def get_license_list_version(license_data, d): return ".".join(license_data["licenseListVersion"].split(".")[:2]) +# This task is added for compatibility with tasks shared with SPDX 3, but +# doesn't do anything +do_create_recipe_spdx() { + : +} +do_create_recipe_spdx[noexec] = "1" +addtask do_create_recipe_spdx after do_collect_spdx_deps + + python do_create_spdx() { from datetime import datetime, timezone import oe.sbom @@ -594,7 +603,7 @@ python do_create_spdx() { } do_create_spdx[vardepsexclude] += "BB_NUMBER_THREADS" # NOTE: depending on do_unpack is a hack that is necessary to get it's dependencies for archive the source -addtask do_create_spdx after do_package do_packagedata do_unpack do_collect_spdx_deps before do_populate_sdk do_build do_rm_work +addtask do_create_spdx after do_create_recipe_spdx do_package do_packagedata do_unpack do_collect_spdx_deps before do_populate_sdk do_build do_rm_work SSTATETASKS += "do_create_spdx" do_create_spdx[sstate-inputdirs] = "${SPDXDEPLOY}" @@ -605,6 +614,7 @@ python do_create_spdx_setscene () { } addtask do_create_spdx_setscene +do_create_spdx[deptask] += "do_create_spdx" do_create_spdx[dirs] = "${SPDXWORK}" do_create_spdx[cleandirs] = "${SPDXDEPLOY} ${SPDXWORK}" do_create_spdx[depends] += " \ diff --git a/meta/classes/create-spdx-3.0.bbclass b/meta/classes/create-spdx-3.0.bbclass index d4575d61c4..cd70a07534 100644 --- a/meta/classes/create-spdx-3.0.bbclass +++ b/meta/classes/create-spdx-3.0.bbclass @@ -159,11 +159,18 @@ SPDX3_DEP_FILES = "\ ${SPDX_LICENSES}:True \ " -python do_create_spdx() { +python do_create_recipe_spdx() { import oe.spdx30_tasks - oe.spdx30_tasks.create_spdx(d) + oe.spdx30_tasks.create_recipe_spdx(d) } -do_create_spdx[vardeps] += "\ +addtask do_create_recipe_spdx after do_collect_spdx_deps + +SSTATETASKS += "do_create_recipe_spdx" +do_create_recipe_spdx[sstate-inputdirs] = "${SPDXRECIPEDEPLOY}" +do_create_recipe_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}" +do_create_recipe_spdx[file-checksums] += "${SPDX3_DEP_FILES}" +do_create_recipe_spdx[cleandirs] = "${SPDXRECIPEDEPLOY}" +do_create_recipe_spdx[vardeps] += "\ SPDX_INCLUDE_BITBAKE_PARENT_BUILD \ SPDX_PACKAGE_ADDITIONAL_PURPOSE \ SPDX_PROFILES \ @@ -171,7 +178,19 @@ do_create_spdx[vardeps] += "\ SPDX_UUID_NAMESPACE \ " +python do_create_recipe_spdx_setscene () { + sstate_setscene(d) +} +addtask do_create_recipe_spdx_setscene + +python do_create_spdx() { + import oe.spdx30_tasks + oe.spdx30_tasks.create_spdx(d) +} +# NOTE: depending on do_unpack is a hack that is necessary to get it's dependencies for archive the source addtask do_create_spdx after \ + do_unpack \ + do_create_recipe_spdx \ do_collect_spdx_deps \ do_deploy_source_date_epoch \ do_populate_sysroot do_package do_packagedata \ @@ -181,18 +200,25 @@ SSTATETASKS += "do_create_spdx" do_create_spdx[sstate-inputdirs] = "${SPDXDEPLOY}" do_create_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}" do_create_spdx[file-checksums] += "${SPDX3_DEP_FILES}" - -python do_create_spdx_setscene () { - sstate_setscene(d) -} -addtask do_create_spdx_setscene - +do_create_spdx[deptask] += "do_create_spdx" do_create_spdx[dirs] = "${SPDXWORK}" do_create_spdx[cleandirs] = "${SPDXDEPLOY} ${SPDXWORK}" do_create_spdx[depends] += " \ ${PATCHDEPENDENCY} \ ${@create_spdx_source_deps(d)} \ " +do_create_spdx[vardeps] += "\ + SPDX_INCLUDE_BITBAKE_PARENT_BUILD \ + SPDX_PACKAGE_ADDITIONAL_PURPOSE \ + SPDX_PROFILES \ + SPDX_NAMESPACE_PREFIX \ + SPDX_UUID_NAMESPACE \ + " + +python do_create_spdx_setscene () { + sstate_setscene(d) +} +addtask do_create_spdx_setscene python do_create_package_spdx() { import oe.spdx30_tasks @@ -205,16 +231,15 @@ SSTATETASKS += "do_create_package_spdx" do_create_package_spdx[sstate-inputdirs] = "${SPDXRUNTIMEDEPLOY}" do_create_package_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}" do_create_package_spdx[file-checksums] += "${SPDX3_DEP_FILES}" +do_create_package_spdx[dirs] = "${SPDXRUNTIMEDEPLOY}" +do_create_package_spdx[cleandirs] = "${SPDXRUNTIMEDEPLOY}" +do_create_package_spdx[rdeptask] = "do_create_spdx" python do_create_package_spdx_setscene () { sstate_setscene(d) } addtask do_create_package_spdx_setscene -do_create_package_spdx[dirs] = "${SPDXRUNTIMEDEPLOY}" -do_create_package_spdx[cleandirs] = "${SPDXRUNTIMEDEPLOY}" -do_create_package_spdx[rdeptask] = "do_create_spdx" - python spdx30_build_started_handler () { import oe.spdx30_tasks d = e.data.createCopy() diff --git a/meta/classes/spdx-common.bbclass b/meta/classes/spdx-common.bbclass index 3110230c9e..0c1fd09b6f 100644 --- a/meta/classes/spdx-common.bbclass +++ b/meta/classes/spdx-common.bbclass @@ -23,6 +23,7 @@ SPDXDEPS = "${SPDXDIR}/deps.json" SPDX_TOOL_NAME ??= "oe-spdx-creator" SPDX_TOOL_VERSION ??= "1.0" +SPDXRECIPEDEPLOY = "${SPDXDIR}/recipe-deploy" SPDXRUNTIMEDEPLOY = "${SPDXDIR}/runtime-deploy" SPDX_INCLUDE_SOURCES ??= "0" @@ -97,8 +98,8 @@ python do_collect_spdx_deps() { # This task calculates the build time dependencies of the recipe, and is # required because while a task can deptask on itself, those dependencies # do not show up in BB_TASKDEPDATA. To work around that, this task does the - # deptask on do_create_spdx and writes out the dependencies it finds, then - # do_create_spdx reads in the found dependencies when writing the actual + # deptask on do_create_recipe_spdx and writes out the dependencies it finds, then + # downstream tasks read in the found dependencies when writing the actual # SPDX document import json import oe.spdx_common @@ -106,15 +107,13 @@ python do_collect_spdx_deps() { spdx_deps_file = Path(d.getVar("SPDXDEPS")) - deps = oe.spdx_common.collect_direct_deps(d, "do_create_spdx") + deps = oe.spdx_common.collect_direct_deps(d, "do_create_recipe_spdx") with spdx_deps_file.open("w") as f: json.dump(deps, f) } -# NOTE: depending on do_unpack is a hack that is necessary to get it's dependencies for archive the source -addtask do_collect_spdx_deps after do_unpack -do_collect_spdx_deps[depends] += "${PATCHDEPENDENCY}" -do_collect_spdx_deps[deptask] = "do_create_spdx" +addtask do_collect_spdx_deps +do_collect_spdx_deps[deptask] = "do_create_recipe_spdx" do_collect_spdx_deps[dirs] = "${SPDXDIR}" oe.spdx_common.collect_direct_deps[vardepsexclude] += "BB_TASKDEPDATA" diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py index 99f2892dfb..a8b4525e3d 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py @@ -32,7 +32,9 @@ def set_timestamp_now(d, o, prop): delattr(o, prop) -def add_license_expression(d, objset, license_expression, license_data): +def add_license_expression( + d, objset, license_expression, license_data, search_objsets=[] +): simple_license_text = {} license_text_map = {} license_ref_idx = 0 @@ -44,14 +46,15 @@ def add_license_expression(d, objset, license_expression, license_data): if name in simple_license_text: return simple_license_text[name] - lic = objset.find_filter( - oe.spdx30.simplelicensing_SimpleLicensingText, - name=name, - ) + for o in [objset] + search_objsets: + lic = o.find_filter( + oe.spdx30.simplelicensing_SimpleLicensingText, + name=name, + ) - if lic is not None: - simple_license_text[name] = lic - return lic + if lic is not None: + simple_license_text[name] = lic + return lic lic = objset.add( oe.spdx30.simplelicensing_SimpleLicensingText( @@ -178,7 +181,9 @@ def add_package_files( # Check if file is compiled if check_compiled_sources: - if not oe.spdx_common.is_compiled_source(filename, compiled_sources, types): + if not oe.spdx_common.is_compiled_source( + filename, compiled_sources, types + ): continue spdx_file = objset.new_file( @@ -293,17 +298,16 @@ def get_package_sources_from_debug( return dep_source_files -def collect_dep_objsets(d, build): +def collect_dep_objsets(d, subdir, fn_prefix, obj_type, **attr_filter): deps = oe.spdx_common.get_spdx_deps(d) dep_objsets = [] - dep_builds = set() + dep_objs = set() - dep_build_spdxids = set() for dep in deps: bb.debug(1, "Fetching SPDX for dependency %s" % (dep.pn)) - dep_build, dep_objset = oe.sbom30.find_root_obj_in_jsonld( - d, "recipes", "recipe-" + dep.pn, oe.spdx30.build_Build + dep_obj, dep_objset = oe.sbom30.find_root_obj_in_jsonld( + d, subdir, fn_prefix + dep.pn, obj_type, **attr_filter ) # If the dependency is part of the taskhash, return it to be linked # against. Otherwise, it cannot be linked against because this recipe @@ -311,10 +315,10 @@ def collect_dep_objsets(d, build): if dep.in_taskhash: dep_objsets.append(dep_objset) - # The build _can_ be linked against (by alias) - dep_builds.add(dep_build) + # The object _can_ be linked against (by alias) + dep_objs.add(dep_obj) - return dep_objsets, dep_builds + return dep_objsets, dep_objs def index_sources_by_hash(sources, dest): @@ -423,9 +427,7 @@ def add_download_files(d, objset): if fd.method.supports_checksum(fd): # TODO Need something better than hard coding this for checksum_id in ["sha256", "sha1"]: - expected_checksum = getattr( - fd, "%s_expected" % checksum_id, None - ) + expected_checksum = getattr(fd, "%s_expected" % checksum_id, None) if expected_checksum is None: continue @@ -462,50 +464,96 @@ def set_purposes(d, element, *var_names, force_purposes=[]): ] -def create_spdx(d): - def set_var_field(var, obj, name, package=None): - val = None - if package: - val = d.getVar("%s:%s" % (var, package)) +def set_purls(spdx_package, purls): + if purls: + spdx_package.software_packageUrl = purls[0] - if not val: - val = d.getVar(var) + for p in sorted(set(purls)): + spdx_package.externalIdentifier.append( + oe.spdx30.ExternalIdentifier( + externalIdentifierType=oe.spdx30.ExternalIdentifierType.packageUrl, + identifier=p, + ) + ) - if val: - setattr(obj, name, val) + +def create_recipe_spdx(d): + deploydir = Path(d.getVar("SPDXRECIPEDEPLOY")) + deploy_dir_spdx = Path(d.getVar("DEPLOY_DIR_SPDX")) + pn = d.getVar("PN") license_data = oe.spdx_common.load_spdx_license_data(d) - deploydir = Path(d.getVar("SPDXDEPLOY")) - deploy_dir_spdx = Path(d.getVar("DEPLOY_DIR_SPDX")) - spdx_workdir = Path(d.getVar("SPDXWORK")) - include_sources = d.getVar("SPDX_INCLUDE_SOURCES") == "1" - pkg_arch = d.getVar("SSTATE_PKGARCH") - is_native = bb.data.inherits_class("native", d) or bb.data.inherits_class( - "cross", d - ) include_vex = d.getVar("SPDX_INCLUDE_VEX") if not include_vex in ("none", "current", "all"): bb.fatal("SPDX_INCLUDE_VEX must be one of 'none', 'current', 'all'") - build_objset = oe.sbom30.ObjectSet.new_objset(d, "recipe-" + d.getVar("PN")) + recipe_objset = oe.sbom30.ObjectSet.new_objset(d, "static-" + pn) - build = build_objset.new_task_build("recipe", "recipe") - build_objset.set_element_alias(build) + recipe = recipe_objset.add_root( + oe.spdx30.software_Package( + _id=recipe_objset.new_spdxid("recipe", pn), + creationInfo=recipe_objset.doc.creationInfo, + name=d.getVar("PN"), + software_packageVersion=d.getVar("PV"), + software_primaryPurpose=oe.spdx30.software_SoftwarePurpose.specification, + software_sourceInfo=json.dumps( + { + "FILENAME": os.path.basename(d.getVar("FILE")), + "FILE_LAYERNAME": d.getVar("FILE_LAYERNAME"), + }, + separators=(",", ":"), + ), + ) + ) - build_objset.doc.rootElement.append(build) + set_purls(recipe, (d.getVar("SPDX_PACKAGE_URLS") or "").split()) + + # TODO: This doesn't work before do_unpack because the license text has to + # be available for recipes with NO_GENERIC_LICENSE + # recipe_spdx_license = add_license_expression( + # d, + # recipe_objset, + # d.getVar("LICENSE"), + # license_data, + # ) + # recipe_objset.new_relationship( + # [recipe], + # oe.spdx30.RelationshipType.hasDeclaredLicense, + # [oe.sbom30.get_element_link_id(recipe_spdx_license)], + # ) + + if val := d.getVar("HOMEPAGE"): + recipe.software_homePage = val + + if val := d.getVar("SUMMARY"): + recipe.summary = val + + if val := d.getVar("DESCRIPTION"): + recipe.description = val + + for cpe_id in oe.cve_check.get_cpe_ids( + d.getVar("CVE_PRODUCT"), d.getVar("CVE_VERSION") + ): + recipe.externalIdentifier.append( + oe.spdx30.ExternalIdentifier( + externalIdentifierType=oe.spdx30.ExternalIdentifierType.cpe23, + identifier=cpe_id, + ) + ) - build_objset.set_is_native(is_native) + dep_objsets, dep_recipes = collect_dep_objsets( + d, "static", "static-", oe.spdx30.software_Package + ) - for var in (d.getVar("SPDX_CUSTOM_ANNOTATION_VARS") or "").split(): - build_objset.new_annotation( - build, - "%s=%s" % (var, d.getVar(var)), - oe.spdx30.AnnotationType.other, + if dep_recipes: + recipe_objset.new_scoped_relationship( + [recipe], + oe.spdx30.RelationshipType.dependsOn, + oe.spdx30.LifecycleScopeType.build, + sorted(oe.sbom30.get_element_link_id(dep) for dep in dep_recipes), ) - build_inputs = set() - # Add CVEs cve_by_status = {} if include_vex != "none": @@ -514,7 +562,7 @@ def create_spdx(d): decoded_status = { "mapping": patched_cve["abbrev-status"], "detail": patched_cve["status"], - "description": patched_cve.get("justification", None) + "description": patched_cve.get("justification", None), } # If this CVE is fixed upstream, skip it unless all CVEs are @@ -531,8 +579,7 @@ def create_spdx(d): bb.debug(1, "Skipping %s since it is already fixed upstream" % cve) continue - spdx_cve = build_objset.new_cve_vuln(cve) - build_objset.set_element_alias(spdx_cve) + spdx_cve = recipe_objset.new_cve_vuln(cve) cve_by_status.setdefault(decoded_status["mapping"], {})[cve] = ( spdx_cve, @@ -540,13 +587,118 @@ def create_spdx(d): decoded_status["description"], ) + all_cves = set() + for status, cves in cve_by_status.items(): + for cve, items in cves.items(): + spdx_cve, detail, description = items + spdx_cve_id = oe.sbom30.get_element_link_id(spdx_cve) + + all_cves.add(spdx_cve) + + if status == "Patched": + recipe_objset.new_vex_patched_relationship([spdx_cve_id], [recipe]) + elif status == "Unpatched": + recipe_objset.new_vex_unpatched_relationship([spdx_cve_id], [recipe]) + elif status == "Ignored": + spdx_vex = recipe_objset.new_vex_ignored_relationship( + [spdx_cve_id], + [recipe], + impact_statement=description, + ) + + vex_just_type = d.getVarFlag("CVE_CHECK_VEX_JUSTIFICATION", detail) + if vex_just_type: + if ( + vex_just_type + not in oe.spdx30.security_VexJustificationType.NAMED_INDIVIDUALS + ): + bb.fatal( + f"Unknown vex justification '{vex_just_type}', detail '{detail}', for ignored {cve}" + ) + + for v in spdx_vex: + v.security_justificationType = ( + oe.spdx30.security_VexJustificationType.NAMED_INDIVIDUALS[ + vex_just_type + ] + ) + + elif status == "Unknown": + bb.note(f"Skipping {cve} with status 'Unknown'") + else: + bb.fatal(f"Unknown {cve} status '{status}'") + + if all_cves: + recipe_objset.new_relationship( + [recipe], + oe.spdx30.RelationshipType.hasAssociatedVulnerability, + sorted(list(all_cves)), + ) + + oe.sbom30.write_recipe_jsonld_doc(d, recipe_objset, "static", deploydir) + + +def load_recipe_spdx(d): + + return oe.sbom30.find_root_obj_in_jsonld( + d, + "static", + "static-" + d.getVar("PN"), + oe.spdx30.software_Package, + ) + + +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") + deploydir = Path(d.getVar("SPDXDEPLOY")) + deploy_dir_spdx = Path(d.getVar("DEPLOY_DIR_SPDX")) + spdx_workdir = Path(d.getVar("SPDXWORK")) + include_sources = d.getVar("SPDX_INCLUDE_SOURCES") == "1" + pkg_arch = d.getVar("SSTATE_PKGARCH") + is_native = bb.data.inherits_class("native", d) or bb.data.inherits_class( + "cross", d + ) + + recipe, recipe_objset = load_recipe_spdx(d) + + build_objset = oe.sbom30.ObjectSet.new_objset(d, "build-" + pn) + + build = build_objset.new_task_build("recipe", "recipe") + build_objset.set_element_alias(build) + + build_objset.doc.rootElement.append(build) + + build_objset.set_is_native(is_native) + + for var in (d.getVar("SPDX_CUSTOM_ANNOTATION_VARS") or "").split(): + build_objset.new_annotation( + build, + "%s=%s" % (var, d.getVar(var)), + oe.spdx30.AnnotationType.other, + ) + + build_inputs = set() + cpe_ids = oe.cve_check.get_cpe_ids(d.getVar("CVE_PRODUCT"), d.getVar("CVE_VERSION")) source_files = add_download_files(d, build_objset) build_inputs |= source_files recipe_spdx_license = add_license_expression( - d, build_objset, d.getVar("LICENSE"), license_data + d, build_objset, d.getVar("LICENSE"), license_data, [recipe_objset] ) build_objset.new_relationship( source_files, @@ -575,7 +727,10 @@ def create_spdx(d): build_inputs |= files index_sources_by_hash(files, dep_sources) - dep_objsets, dep_builds = collect_dep_objsets(d, build) + dep_objsets, dep_builds = collect_dep_objsets( + d, "builds", "build-", oe.spdx30.build_Build + ) + if dep_builds: build_objset.new_scoped_relationship( [build], @@ -587,6 +742,22 @@ def create_spdx(d): debug_source_ids = set() source_hash_cache = {} + # Collect all VEX statements from the recipe + vex_statements = {} + for rel in recipe_objset.foreach_filter( + oe.spdx30.Relationship, + relationshipType=oe.spdx30.RelationshipType.hasAssociatedVulnerability, + ): + for cve in rel.to: + vex_statements[cve] = [] + + for cve in vex_statements.keys(): + for rel in recipe_objset.foreach_filter( + oe.spdx30.security_VexVulnAssessmentRelationship, + from_=cve, + ): + vex_statements[cve].append(rel) + # Write out the package SPDX data now. It is not complete as we cannot # write the runtime data, so write it to a staging area and a later task # will write out the final collection @@ -645,16 +816,7 @@ def create_spdx(d): or "" ).split() - if purls: - spdx_package.software_packageUrl = purls[0] - - for p in sorted(set(purls)): - spdx_package.externalIdentifier.append( - oe.spdx30.ExternalIdentifier( - externalIdentifierType=oe.spdx30.ExternalIdentifierType.packageUrl, - identifier=p, - ) - ) + set_purls(spdx_package, purls) pkg_objset.new_scoped_relationship( [oe.sbom30.get_element_link_id(build)], @@ -663,6 +825,13 @@ def create_spdx(d): [spdx_package], ) + pkg_objset.new_scoped_relationship( + [oe.sbom30.get_element_link_id(recipe)], + oe.spdx30.RelationshipType.generates, + oe.spdx30.LifecycleScopeType.build, + [spdx_package], + ) + for cpe_id in cpe_ids: spdx_package.externalIdentifier.append( oe.spdx30.ExternalIdentifier( @@ -696,7 +865,11 @@ def create_spdx(d): package_license = d.getVar("LICENSE:%s" % package) if package_license and package_license != d.getVar("LICENSE"): package_spdx_license = add_license_expression( - d, build_objset, package_license, license_data + d, + build_objset, + package_license, + license_data, + [recipe_objset], ) else: package_spdx_license = recipe_spdx_license @@ -721,58 +894,41 @@ def create_spdx(d): [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(): - for cve, items in cves.items(): - spdx_cve, detail, description = items - spdx_cve_id = oe.sbom30.get_element_link_id(spdx_cve) - - all_cves.add(spdx_cve_id) + # Copy CVEs from recipe + if vex_statements: + pkg_objset.new_relationship( + [spdx_package], + oe.spdx30.RelationshipType.hasAssociatedVulnerability, + sorted( + oe.sbom30.get_element_link_id(cve) + for cve in vex_statements.keys() + ), + ) - if status == "Patched": + for cve, vexes in vex_statements.items(): + for vex in vexes: + if vex.relationshipType == oe.spdx30.RelationshipType.fixedIn: pkg_objset.new_vex_patched_relationship( - [spdx_cve_id], [spdx_package] + [oe.sbom30.get_element_link_id(cve)], [spdx_package] ) - elif status == "Unpatched": + elif vex.relationshipType == oe.spdx30.RelationshipType.affects: pkg_objset.new_vex_unpatched_relationship( - [spdx_cve_id], [spdx_package] + [oe.sbom30.get_element_link_id(cve)], [spdx_package] ) - elif status == "Ignored": + elif ( + vex.relationshipType == oe.spdx30.RelationshipType.doesNotAffect + ): spdx_vex = pkg_objset.new_vex_ignored_relationship( - [spdx_cve_id], + [oe.sbom30.get_element_link_id(cve)], [spdx_package], - impact_statement=description, + impact_statement=vex.security_impactStatement, ) - vex_just_type = d.getVarFlag( - "CVE_CHECK_VEX_JUSTIFICATION", detail - ) - if vex_just_type: - if ( - vex_just_type - not in oe.spdx30.security_VexJustificationType.NAMED_INDIVIDUALS - ): - bb.fatal( - f"Unknown vex justification '{vex_just_type}', detail '{detail}', for ignored {cve}" - ) - + if vex.security_justificationType: for v in spdx_vex: - v.security_justificationType = oe.spdx30.security_VexJustificationType.NAMED_INDIVIDUALS[ - vex_just_type - ] - - elif status == "Unknown": - bb.note(f"Skipping {cve} with status 'Unknown'") - else: - bb.fatal(f"Unknown {cve} status '{status}'") - - if all_cves: - pkg_objset.new_relationship( - [spdx_package], - oe.spdx30.RelationshipType.hasAssociatedVulnerability, - sorted(list(all_cves)), - ) + v.security_justificationType = ( + vex.security_justificationType + ) bb.debug(1, "Adding package files to SPDX for package %s" % pkg_name) package_files = add_package_files( @@ -851,14 +1007,15 @@ def create_spdx(d): status = "enabled" if feature in enabled else "disabled" build.build_parameter.append( oe.spdx30.DictionaryEntry( - key=f"PACKAGECONFIG:{feature}", - value=status + key=f"PACKAGECONFIG:{feature}", value=status ) ) - bb.note(f"Added PACKAGECONFIG entries: {len(enabled)} enabled, {len(disabled)} disabled") + bb.note( + f"Added PACKAGECONFIG entries: {len(enabled)} enabled, {len(disabled)} disabled" + ) - oe.sbom30.write_recipe_jsonld_doc(d, build_objset, "recipes", deploydir) + oe.sbom30.write_recipe_jsonld_doc(d, build_objset, "builds", deploydir) def create_package_spdx(d): @@ -1197,17 +1354,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: @@ -1234,7 +1391,6 @@ def create_image_spdx(d): set_timestamp_now(d, a, "builtTime") - if artifacts: objset.new_scoped_relationship( [image_build], From patchwork Fri Feb 20 15:40:39 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 81488 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id E0C5FC5AC7A for ; Fri, 20 Feb 2026 15:41:48 +0000 (UTC) Received: from mail-oo1-f43.google.com (mail-oo1-f43.google.com [209.85.161.43]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.347.1771602102012757321 for ; Fri, 20 Feb 2026 07:41:42 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=dpDVaYnT; spf=pass (domain: gmail.com, ip: 209.85.161.43, mailfrom: jpewhacker@gmail.com) Received: by mail-oo1-f43.google.com with SMTP id 006d021491bc7-677a11d11e0so815115eaf.2 for ; Fri, 20 Feb 2026 07:41:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771602101; x=1772206901; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=XcUahur4GoX4QtzfOJWzF+fCRktws5InkjXE3UlFUjk=; b=dpDVaYnTYn2sH0/xeKltrG1ysgxmx6r/S4C13Yvg6NeLGwI6G3fVY6PRUAu+oKHq2c owrivU2Y3T4r2w06FA2Q4iN6/dDJHuT9+hIgajhkyD6Flg9ZI603iPgZDBaNLo4VCypi jkwnAtJyd6BeATszJ5pEQCceQZOJIplQwiBL2IxKJWk5//5OAlby+uwcj1947uFhUy5k aGvvWw+JWDNvjusybR9nq5c2GZsYYjyNt+wdV715ATHunFQg5dxm2eEv4P5FuL8N+8AE imsxxy9gAkXw/PCfQrgBRC1QuZoZt41sllV2VNgGm6v9TMVNVA26FkGyvp6iTWSFDlkm 4Vqg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771602101; x=1772206901; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=XcUahur4GoX4QtzfOJWzF+fCRktws5InkjXE3UlFUjk=; b=sdBYq7PzxbzFRfiD/ZyUsW0EPezOL0ShaJXoVMpOANcAmem5d8Uh5tjUCX5ro0M4em EcRag2fLGws6mrxEhsiuvaIjVa9Ny1zLfaW1Xxum6BnkgBKsObQQGRTpFHKQpWFr+Lih RFXEv75d9psiK28FdBbbuiAhkm2O062DrBi/573mp7484v+cfvt08jz7/klzgBtlEZG7 Vs08NsEim5+YWuU908RZxMhkIswPyunY6nF1FIPxJ8h1Hn+zba4ccJhuJvwG9BzCxdkb 2cM0ou/NvyCF9x0gI39N/HQGLlyKM9yzj2xgQwiSVL/0v/xnMgQImVtsWGQ/GNINhGsV KMLA== X-Gm-Message-State: AOJu0YyWudHkzWwi2kiigDAGvT5+fHCK0l/qTI65XCxInmZRlgZZtPLp vvXdBU2f0oy1ybx6k3qFDEo1NvRI2W3ZE7qcuz7UiBvwD10UbkP13P3nG/I8jA== X-Gm-Gg: AZuq6aKpX72/YYXK2q3h176P2a5UVJPEOizPyQmvYsVmo9QTGdsCUF06Elikgv/tIdu NLspw41oJ3Nwy2oQDGrsVKGEatM908Bmif/mMckz1gS96IJyb89CdSxJQoez6qPfT4dv64mmvEI nLxJGUWu3OCliAWQKHPrmy9P4zXlXhnuLbgl26DAITCNuJH9Yu/qwyHyp4yy/YAUWliDAhDs9GR HWmg1J8/qPcanNKs2pjOR+Pwyn/4ddtSRP81InegywtFrfuMmpmwv3bf+EPAIx3/R77qGbBl9VH JGPfiafZEZkSSZh8OgAQx33CqxfFIsOifltm+Cpjjctowx7m7q0TqScyNFVlmo1FJgkV6NHkWcM lWjvg3HaSHKMWfXvwbFFk5G0zlJnLBWXCCmBSEr61h8N3DesSIzHBMV3z0hI2qLqVRWMiZ9ivJh +mAptm0V4ekRFqCel6cMcF X-Received: by 2002:a05:6820:1c99:b0:662:c8a5:66bc with SMTP id 006d021491bc7-679c46fe92bmr122414eaf.50.1771602100984; Fri, 20 Feb 2026 07:41:40 -0800 (PST) Received: from localhost.localdomain ([2601:282:4200:11c0::6492]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6772583294bsm15127188eaf.14.2026.02.20.07.41.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Feb 2026 07:41:40 -0800 (PST) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: benjamin.robin@bootlin.com, Joshua Watt Subject: [OE-core][PATCH 4/9] spdx3: Add recipe SBoM task Date: Fri, 20 Feb 2026 08:40:39 -0700 Message-ID: <20260220154123.376880-5-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260220154123.376880-1-JPEWhacker@gmail.com> References: <20260220154123.376880-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Fri, 20 Feb 2026 15:41:48 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231523 Adds a task that will create the complete recipe-level SBoM for a given target recipe, following all dependencies. For example: ``` bitbake -c create_recipe_sbom zstd ``` Would produce the complete recipe SBoM for the zstd recipe, include all build time dependencies (recursively). The complete SBoM for all (target) recipes can be built with: ``` bitbake -c create_recipe_sbom meta-world-recipe-sbom ``` Signed-off-by: Joshua Watt --- meta/classes/create-spdx-3.0.bbclass | 28 +++++++++++++++++++ meta/classes/spdx-common.bbclass | 1 + meta/lib/oe/spdx30_tasks.py | 10 +++++++ .../meta/meta-world-recipe-sbom.bb | 26 +++++++++++++++++ 4 files changed, 65 insertions(+) create mode 100644 meta/recipes-core/meta/meta-world-recipe-sbom.bb diff --git a/meta/classes/create-spdx-3.0.bbclass b/meta/classes/create-spdx-3.0.bbclass index cd70a07534..e12c116486 100644 --- a/meta/classes/create-spdx-3.0.bbclass +++ b/meta/classes/create-spdx-3.0.bbclass @@ -240,6 +240,34 @@ python do_create_package_spdx_setscene () { } addtask do_create_package_spdx_setscene +addtask do_create_recipe_sbom after create_recipe_spdx +python do_create_recipe_sbom() { + import oe.spdx30_tasks + from pathlib import Path + deploydir = Path(d.getVar("SPDXRECIPESBOMDEPLOY")) + oe.spdx30_tasks.create_recipe_sbom(d, deploydir) +} + +SSTATETASKS += "do_create_recipe_sbom" +do_create_recipe_sbom[recrdeptask] = "do_create_recipe_spdx" +do_create_recipe_sbom[nostamp] = "1" +do_create_recipe_sbom[sstate-inputdirs] = "${SPDXRECIPESBOMDEPLOY}" +do_create_recipe_sbom[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}" +do_create_recipe_sbom[file-checksums] += "${SPDX3_DEP_FILES}" +do_create_recipe_sbom[cleandirs] = "${SPDXRECIPESBOMDEPLOY}" +do_create_recipe_sbom[vardeps] += "\ + SPDX_INCLUDE_BITBAKE_PARENT_BUILD \ + SPDX_PACKAGE_ADDITIONAL_PURPOSE \ + SPDX_PROFILES \ + SPDX_NAMESPACE_PREFIX \ + SPDX_UUID_NAMESPACE \ + " + +python do_create_recipe_sbom_setscene () { + sstate_setscene(d) +} +addtask do_create_recipe_sbom_setscene + python spdx30_build_started_handler () { import oe.spdx30_tasks d = e.data.createCopy() diff --git a/meta/classes/spdx-common.bbclass b/meta/classes/spdx-common.bbclass index 0c1fd09b6f..6f35dbf8f6 100644 --- a/meta/classes/spdx-common.bbclass +++ b/meta/classes/spdx-common.bbclass @@ -25,6 +25,7 @@ SPDX_TOOL_VERSION ??= "1.0" SPDXRECIPEDEPLOY = "${SPDXDIR}/recipe-deploy" SPDXRUNTIMEDEPLOY = "${SPDXDIR}/runtime-deploy" +SPDXRECIPESBOMDEPLOY = "${SPDXDIR}/recipes-bom-deploy" SPDX_INCLUDE_SOURCES ??= "0" SPDX_INCLUDE_COMPILED_SOURCES ??= "0" diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py index a8b4525e3d..9a312a870d 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py @@ -1564,3 +1564,13 @@ def create_sdk_sbom(d, sdk_deploydir, spdx_work_dir, toolchain_outputname): oe.sbom30.write_jsonld_doc( d, objset, sdk_deploydir / (toolchain_outputname + ".spdx.json") ) + + +def create_recipe_sbom(d, deploydir): + sbom_name = d.getVar("PN") + "-recipe-sbom" + + recipe, recipe_objset = load_recipe_spdx(d) + + objset, sbom = oe.sbom30.create_sbom(d, sbom_name, [recipe], [recipe_objset]) + + oe.sbom30.write_jsonld_doc(d, objset, deploydir / (sbom_name + ".spdx.json")) diff --git a/meta/recipes-core/meta/meta-world-recipe-sbom.bb b/meta/recipes-core/meta/meta-world-recipe-sbom.bb new file mode 100644 index 0000000000..60209fba7e --- /dev/null +++ b/meta/recipes-core/meta/meta-world-recipe-sbom.bb @@ -0,0 +1,26 @@ +SUMMARY = "Generates a combined SBoM for all world recipes" +LICENSE = "MIT" + +INHIBIT_DEFAULT_DEPS = "1" + +PACKAGE_ARCH = "${MACHINE_ARCH}" + +inherit nopackages +deltask do_fetch +deltask do_unpack +deltask do_patch +deltask do_configure +deltask do_compile +deltask do_install + +do_prepare_recipe_sysroot[deptask] = "" + +WORLD_SBOM_EXCLUDE ?= "" + +python calculate_extra_depends() { + exclude = set('${WORLD_SBOM_EXCLUDE}'.split()) + for p in world_target: + if p == self_pn or p in exclude: + continue + deps.append(p) +} From patchwork Fri Feb 20 15:40:40 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 81491 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id E0D2BC5AC7C for ; Fri, 20 Feb 2026 15:41:48 +0000 (UTC) Received: from mail-oo1-f44.google.com (mail-oo1-f44.google.com [209.85.161.44]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.321.1771602103010341097 for ; Fri, 20 Feb 2026 07:41:43 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=JGh8IdYZ; spf=pass (domain: gmail.com, ip: 209.85.161.44, mailfrom: jpewhacker@gmail.com) Received: by mail-oo1-f44.google.com with SMTP id 006d021491bc7-6799774d0fcso1238273eaf.3 for ; Fri, 20 Feb 2026 07:41:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771602102; x=1772206902; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=B26QEFGZ1BfU8osvAqsZdiqlbAEmeNAht5QsWjTxO+M=; b=JGh8IdYZV8mLUblrcQV0t0bJoJfGOYOptStL+kzh/RNi1cvAlbyXOyAcGnPZBRlfkE Ks42DDIenAi+/BHNilt7lZOy04GFyzMGDYe0ngZFnJY3u9eXDpQLblT9IAjPgX99S/Xk 4C+us7AvN/HlmIvDHVM8ynwCGLdMprMUz/SiWzoLZraqOl62PkPmCeCa3D2m51OQYRUp FH4ceMzOboFDrVYUp4Gv0oAWxE8xP2tGEt+vVTejJ3/WxKvg4QrBK44zUUAfHefAW0NA 1zj4fYMpq4J3YGTzYF8bI06n5ibgYsDTtv2Ua4tjivxOgeYUc4rfxCjeKBx/JDJPhaut 95xg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771602102; x=1772206902; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=B26QEFGZ1BfU8osvAqsZdiqlbAEmeNAht5QsWjTxO+M=; b=CDxdsvZRaJrUQpZevMd2yerP9PgoNrt7GLcAE2xjDzUH1GrejZ+SjmnvKKsZyKNXVB qgX/vvRCwrh5hQRNtpXvHZ7tgma4QKgVTNy000awkDdSdum7z5eBX8ESFufvW+zBzlr+ LlnBR8TJFP2GuWrCdnaZ5dCzdQt5NDJZTyaA9wLFMFLq4vBoSIwL7w4LNMYJLzK2LMsy dZbED/IZdNLd624xfUBvz5sOLeiEA8r3jKO5bEQcADvzZbw2/a9N89RuML2hxVkBXzAL IpJAqwRLOj8xBece6qCzBgvNWGRyvJiphd2XnCeZPvbvTkm1BtNP+pjywc6fPmpvO5nk JZrA== X-Gm-Message-State: AOJu0YxeLNntquvpPS7EQYwMYMHbfi2PHb8axLB2EAm9+T6Zri2kGXwh 2UdZjr+blzdyshfjdIChsArIz1nvfirZejnNPSYsVRD0X8EOpZltQm4+DhJDFw== X-Gm-Gg: AZuq6aIrXd/ZNgv8KzqfrYyiWoCWbpPSS6GM6DwxTcCQ/CSBj51P+eNCpuhhUf8zjOr rTAzwQPgnDfoWCYkSqBLc/0KBv8AOtDNu5NgGCaCPs3AElU55UrO2M535+JsRL+ovcnAKLQ6puu 6gGpAprqkqS5gJJfVlZxEBoqKDp+W5D2vUdco2T/uQkasv3Ai7m0xUPpwti06Zq546AJbRy5fmI bFQR1GNR3kYH7IL8XXMl96xa0B3pwGhXgLYtE/eTGi/H9wHtRkIj4LqJmXwzgTD16BvdBvhyqJh KvS4CVqbrnW/O8AFLhywoyfkrQzW95bDxvCJSSN+qGDzGDFuHOdQbMamwqLLR7JuIomPGcINGwI l9Y8yAgAtl8bcfOhbp1bZZohJP17u2oGAjWjokD87OkhgctqyTRx4Tx0UR+kXGikRuB5Hc0nQ56 4R+TrQJBXoATbqOXgsAYMw X-Received: by 2002:a05:6820:16a3:b0:679:94ff:6ea7 with SMTP id 006d021491bc7-679c4251e3dmr178380eaf.15.1771602102096; Fri, 20 Feb 2026 07:41:42 -0800 (PST) Received: from localhost.localdomain ([2601:282:4200:11c0::6492]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6772583294bsm15127188eaf.14.2026.02.20.07.41.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Feb 2026 07:41:41 -0800 (PST) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: benjamin.robin@bootlin.com, Joshua Watt Subject: [OE-core][PATCH 5/9] spdx3: Add is-native property Date: Fri, 20 Feb 2026 08:40:40 -0700 Message-ID: <20260220154123.376880-6-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260220154123.376880-1-JPEWhacker@gmail.com> References: <20260220154123.376880-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Fri, 20 Feb 2026 15:41:48 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231524 Adds a custom is-native property to the recipe package to indicate if it is a native recipe Signed-off-by: Joshua Watt --- meta/lib/oe/sbom30.py | 20 ++++++++++++++++++++ meta/lib/oe/spdx30_tasks.py | 18 +++++++++++------- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/meta/lib/oe/sbom30.py b/meta/lib/oe/sbom30.py index 227ac51877..50a72fce39 100644 --- a/meta/lib/oe/sbom30.py +++ b/meta/lib/oe/sbom30.py @@ -118,6 +118,26 @@ class OEDocumentExtension(oe.spdx30.extension_Extension): ) +@oe.spdx30.register(OE_SPDX_BASE + "recipe-extension") +class OERecipeExtension(oe.spdx30.extension_Extension): + """ + This extension is added to recipe software_Packages to indicate various + useful bits of information about the recipe + """ + + CLOSED = True + + @classmethod + def _register_props(cls): + super()._register_props() + cls._add_property( + "is_native", + oe.spdx30.BooleanProp(), + OE_SPDX_BASE + "is-native", + max_count=1, + ) + + def spdxid_hash(*items): h = hashlib.md5() for i in items: diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py index 9a312a870d..fff1ca6bea 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py @@ -477,6 +477,10 @@ def set_purls(spdx_package, purls): ) +def get_is_native(d): + return bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d) + + def create_recipe_spdx(d): deploydir = Path(d.getVar("SPDXRECIPEDEPLOY")) deploy_dir_spdx = Path(d.getVar("DEPLOY_DIR_SPDX")) @@ -507,6 +511,11 @@ def create_recipe_spdx(d): ) ) + if get_is_native(d): + ext = oe.sbom30.OERecipeExtension() + ext.is_native = True + recipe.extension.append(ext) + set_purls(recipe, (d.getVar("SPDX_PACKAGE_URLS") or "").split()) # TODO: This doesn't work before do_unpack because the license text has to @@ -668,9 +677,7 @@ def create_spdx(d): spdx_workdir = Path(d.getVar("SPDXWORK")) include_sources = d.getVar("SPDX_INCLUDE_SOURCES") == "1" pkg_arch = d.getVar("SSTATE_PKGARCH") - is_native = bb.data.inherits_class("native", d) or bb.data.inherits_class( - "cross", d - ) + is_native = get_is_native(d) recipe, recipe_objset = load_recipe_spdx(d) @@ -1021,14 +1028,11 @@ def create_spdx(d): def create_package_spdx(d): deploy_dir_spdx = Path(d.getVar("DEPLOY_DIR_SPDX")) deploydir = Path(d.getVar("SPDXRUNTIMEDEPLOY")) - is_native = bb.data.inherits_class("native", d) or bb.data.inherits_class( - "cross", d - ) providers = oe.spdx_common.collect_package_providers(d) pkg_arch = d.getVar("SSTATE_PKGARCH") - if is_native: + if get_is_native(d): return bb.build.exec_func("read_subpackage_metadata", d) From patchwork Fri Feb 20 15:40:41 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 81492 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 08FE2C5AC80 for ; Fri, 20 Feb 2026 15:41:49 +0000 (UTC) Received: from mail-oo1-f44.google.com (mail-oo1-f44.google.com [209.85.161.44]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.350.1771602104017634771 for ; Fri, 20 Feb 2026 07:41:44 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=SWD9bIB2; spf=pass (domain: gmail.com, ip: 209.85.161.44, mailfrom: jpewhacker@gmail.com) Received: by mail-oo1-f44.google.com with SMTP id 006d021491bc7-6769719821dso837193eaf.2 for ; Fri, 20 Feb 2026 07:41:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771602103; x=1772206903; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=6hV0ClIeawdfJviTL2VvpRpRo7713Nd0mIEcCehLwkw=; b=SWD9bIB2D57PqbsOHSW9Qj7ZPNdlE14ii/s21S/J+oMpm8pkKMqnTQ02G+uyicQtLe cPqJ0fE7qfyfF4xmHlgg7MTwgGw7uqwuYLPgNM1SrXANuHNeD8RekvadQWwO2j3w5jCJ GsZt8e3mpbxNnnCWCLV19WHEYoJcd5mOAhRG8LP5C9MlCqt9/AkRuCKkvCOpAtiZtm7t kUHHL8IVOhNAtWf/f+RRhmuo9sKF6Bu8FDyvSjtBjHAC1JYtV3laTCj9p6ag/g0VDG7l ja+Elg56vBj5Ip2vkM7zqWR6ynvnvNZOMpIdDZV1O4OrzbABW7dZAXIbnG1r+vSEqGtQ lJHA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771602103; x=1772206903; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=6hV0ClIeawdfJviTL2VvpRpRo7713Nd0mIEcCehLwkw=; b=gogIS00GjxjFUB5m+REwz2Lv29cOB0SSD3RYyEHHTevs6giwnfBHmcgZCCNylBxsoC lqyPQLzyw4QtwrqAaZO/9DGfaP6KU4hnFredu0fmvmBQ4xwSvVad4UmICicHC0s4PqOU WEdgdqVt2H/4X3lm/Lqw/LWxBMy55wiyVgu7DXrt797r6z1PihIkMRbt4mPrAzGFd6Wt mv90jFFjxADA9ZHdt7JDOGsfuuCpcXTNYs3c4vlaP2obz8nCPfXZQL9FVemy53kfnYwM et51ktBiQmLWJOznZKttCgBd98BdnLgLZwHOZgBbKiRUrvla1TR6i3qG7Mo69RMG0UP0 Hvvw== X-Gm-Message-State: AOJu0Yzih0rxTHGAHgyq8cDsCD8RsmGvzs/PmxtCe6Fx20aXfHqaAt2y FqVVRzNi9Ad3lsWRITAsudIuIg5p9lowYL3BmgoNCJ31UpDYeYMxrOwZcExVXA== X-Gm-Gg: AZuq6aJ0YSQvYdvJqNI3KdnwXhKPScp1KgTp5uEHCBq8ZBGQBxyYESUDGka0vUQ5ZQi +3MatSiOq2tKERMOEkaAVE2p3l2yzsvKTUll5R6snEyJBzEdETpA/NTS7koLBl0RwPNxwFiFNu4 J2Qww9ErHpS3Vsz8zGDnzvv9O/QG4GONfyPiohpQ/KXQajisnSkeXSIwPdNuJooq+cUHjePkztw L5qfQH1Km9CMSv/LUeQ5bTVFc5smHq+cJylLW+O8WaFTsV5L0CAwKCKf7ojIC+XwHJxIjYMiXZq th0//J9nomJDIVzQzUQ1F6VQ6dOV0s3zKW2ZoNATzKWwXJRMr28Tuc5NRAmcnrHI4232vPaQ29F veg1dMV5NOL0fvCJlsGkhp0B90kGbgL5+9Duf2jmgVtP9wpbHSNmvsRqGlT2SRQTGlhTI271jME ORqm3E4+S6NTRJlXOSytHE X-Received: by 2002:a05:6820:f06:b0:663:22b:1486 with SMTP id 006d021491bc7-679c44cf4fcmr135428eaf.41.1771602102998; Fri, 20 Feb 2026 07:41:42 -0800 (PST) Received: from localhost.localdomain ([2601:282:4200:11c0::6492]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6772583294bsm15127188eaf.14.2026.02.20.07.41.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Feb 2026 07:41:42 -0800 (PST) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: benjamin.robin@bootlin.com, Joshua Watt Subject: [OE-core][PATCH 6/9] spdx30: Include patch file information in VEX Date: Fri, 20 Feb 2026 08:40:41 -0700 Message-ID: <20260220154123.376880-7-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260220154123.376880-1-JPEWhacker@gmail.com> References: <20260220154123.376880-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Fri, 20 Feb 2026 15:41:48 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231525 Modifies the SPDX VEX output to include the patches that fix a particular vulnerability. This is done by adding a `patchedBy` relationship from the `VexFixedVulnAssessmentRelationship` to the `File` that provides the fix. If the file can be located without fetching (e.g. is a file:// in SRC_URI), the checksum will be included. Signed-off-by: Joshua Watt --- meta/lib/oe/sbom30.py | 60 ++++++++++++++------------- meta/lib/oe/spdx30_tasks.py | 81 ++++++++++++++++++++++++++++--------- 2 files changed, 92 insertions(+), 49 deletions(-) diff --git a/meta/lib/oe/sbom30.py b/meta/lib/oe/sbom30.py index 50a72fce39..21f084dc16 100644 --- a/meta/lib/oe/sbom30.py +++ b/meta/lib/oe/sbom30.py @@ -620,37 +620,38 @@ class ObjectSet(oe.spdx30.SHACLObjectSet): ) spdx_file.extension.append(OELicenseScannedExtension()) - def new_file(self, _id, name, path, *, purposes=[]): - sha256_hash = bb.utils.sha256_file(path) + def new_file(self, _id, name, path, *, purposes=[], hashfile=True): + if hashfile: + sha256_hash = bb.utils.sha256_file(path) - for f in self.by_sha256_hash.get(sha256_hash, []): - if not isinstance(f, oe.spdx30.software_File): - continue + for f in self.by_sha256_hash.get(sha256_hash, []): + if not isinstance(f, oe.spdx30.software_File): + continue - if purposes: - new_primary = purposes[0] - new_additional = [] + if purposes: + new_primary = purposes[0] + new_additional = [] - if f.software_primaryPurpose: - new_additional.append(f.software_primaryPurpose) - new_additional.extend(f.software_additionalPurpose) + if f.software_primaryPurpose: + new_additional.append(f.software_primaryPurpose) + new_additional.extend(f.software_additionalPurpose) - new_additional = sorted( - list(set(p for p in new_additional if p != new_primary)) - ) + new_additional = sorted( + list(set(p for p in new_additional if p != new_primary)) + ) - f.software_primaryPurpose = new_primary - f.software_additionalPurpose = new_additional + f.software_primaryPurpose = new_primary + f.software_additionalPurpose = new_additional - if f.name != name: - for e in f.extension: - if isinstance(e, OEFileNameAliasExtension): - e.aliases.append(name) - break - else: - f.extension.append(OEFileNameAliasExtension(aliases=[name])) + if f.name != name: + for e in f.extension: + if isinstance(e, OEFileNameAliasExtension): + e.aliases.append(name) + break + else: + f.extension.append(OEFileNameAliasExtension(aliases=[name])) - return f + return f spdx_file = oe.spdx30.software_File( _id=_id, @@ -661,12 +662,13 @@ class ObjectSet(oe.spdx30.SHACLObjectSet): spdx_file.software_primaryPurpose = purposes[0] spdx_file.software_additionalPurpose = purposes[1:] - spdx_file.verifiedUsing.append( - oe.spdx30.Hash( - algorithm=oe.spdx30.HashAlgorithm.sha256, - hashValue=sha256_hash, + if hashfile: + spdx_file.verifiedUsing.append( + oe.spdx30.Hash( + algorithm=oe.spdx30.HashAlgorithm.sha256, + hashValue=sha256_hash, + ) ) - ) return self.add(spdx_file) diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py index fff1ca6bea..1c9346128c 100644 --- a/meta/lib/oe/spdx30_tasks.py +++ b/meta/lib/oe/spdx30_tasks.py @@ -568,44 +568,63 @@ def create_recipe_spdx(d): if include_vex != "none": patched_cves = oe.cve_check.get_patched_cves(d) for cve, patched_cve in patched_cves.items(): - decoded_status = { - "mapping": patched_cve["abbrev-status"], - "detail": patched_cve["status"], - "description": patched_cve.get("justification", None), - } + mapping = patched_cve["abbrev-status"] + detail = patched_cve["status"] + description = patched_cve.get("justification", None) + resources = patched_cve.get("resource", []) # If this CVE is fixed upstream, skip it unless all CVEs are # specified. - if ( - include_vex != "all" - and "detail" in decoded_status - and decoded_status["detail"] - in ( - "fixed-version", - "cpe-stable-backport", - ) + if include_vex != "all" and detail in ( + "fixed-version", + "cpe-stable-backport", ): bb.debug(1, "Skipping %s since it is already fixed upstream" % cve) continue spdx_cve = recipe_objset.new_cve_vuln(cve) - cve_by_status.setdefault(decoded_status["mapping"], {})[cve] = ( + cve_by_status.setdefault(mapping, {})[cve] = ( spdx_cve, - decoded_status["detail"], - decoded_status["description"], + detail, + description, + resources, ) all_cves = set() for status, cves in cve_by_status.items(): for cve, items in cves.items(): - spdx_cve, detail, description = items + spdx_cve, detail, description, resources = items spdx_cve_id = oe.sbom30.get_element_link_id(spdx_cve) all_cves.add(spdx_cve) if status == "Patched": - recipe_objset.new_vex_patched_relationship([spdx_cve_id], [recipe]) + spdx_vex = recipe_objset.new_vex_patched_relationship( + [spdx_cve_id], [recipe] + ) + patches = [] + for idx, filepath in enumerate(resources): + patches.append( + recipe_objset.new_file( + recipe_objset.new_spdxid( + "patch", str(idx), os.path.basename(filepath) + ), + os.path.basename(filepath), + filepath, + purposes=[oe.spdx30.software_SoftwarePurpose.patch], + hashfile=os.path.isfile(filepath), + ) + ) + + if patches: + recipe_objset.new_scoped_relationship( + spdx_vex, + oe.spdx30.RelationshipType.patchedBy, + oe.spdx30.LifecycleScopeType.build, + patches, + ) + elif status == "Unpatched": recipe_objset.new_vex_unpatched_relationship([spdx_cve_id], [recipe]) elif status == "Ignored": @@ -751,12 +770,14 @@ def create_spdx(d): # Collect all VEX statements from the recipe vex_statements = {} + vex_patches = {} for rel in recipe_objset.foreach_filter( oe.spdx30.Relationship, relationshipType=oe.spdx30.RelationshipType.hasAssociatedVulnerability, ): for cve in rel.to: vex_statements[cve] = [] + vex_patches[cve] = [] for cve in vex_statements.keys(): for rel in recipe_objset.foreach_filter( @@ -764,6 +785,13 @@ def create_spdx(d): from_=cve, ): vex_statements[cve].append(rel) + if rel.relationshipType == oe.spdx30.RelationshipType.fixedIn: + for patch_rel in recipe_objset.foreach_filter( + oe.spdx30.Relationship, + relationshipType=oe.spdx30.RelationshipType.patchedBy, + from_=rel, + ): + vex_patches[cve].extend(patch_rel.to) # Write out the package SPDX data now. It is not complete as we cannot # write the runtime data, so write it to a staging area and a later task @@ -889,7 +917,9 @@ def create_spdx(d): # 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") + 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 @@ -915,9 +945,20 @@ def create_spdx(d): for cve, vexes in vex_statements.items(): for vex in vexes: if vex.relationshipType == oe.spdx30.RelationshipType.fixedIn: - pkg_objset.new_vex_patched_relationship( + spdx_vex = pkg_objset.new_vex_patched_relationship( [oe.sbom30.get_element_link_id(cve)], [spdx_package] ) + if vex_patches[cve]: + pkg_objset.new_scoped_relationship( + spdx_vex, + oe.spdx30.RelationshipType.patchedBy, + oe.spdx30.LifecycleScopeType.build, + [ + oe.sbom30.get_element_link_id(p) + for p in vex_patches[cve] + ], + ) + elif vex.relationshipType == oe.spdx30.RelationshipType.affects: pkg_objset.new_vex_unpatched_relationship( [oe.sbom30.get_element_link_id(cve)], [spdx_package] From patchwork Fri Feb 20 15:40:42 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 81494 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 12763C5AC7D for ; Fri, 20 Feb 2026 15:41:49 +0000 (UTC) Received: from mail-oo1-f46.google.com (mail-oo1-f46.google.com [209.85.161.46]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.351.1771602104717684570 for ; Fri, 20 Feb 2026 07:41:44 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=a65/UhZf; spf=pass (domain: gmail.com, ip: 209.85.161.46, mailfrom: jpewhacker@gmail.com) Received: by mail-oo1-f46.google.com with SMTP id 006d021491bc7-673ee2a98b1so1237228eaf.0 for ; Fri, 20 Feb 2026 07:41:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771602104; x=1772206904; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=RgZWWYUETcp+KAK54Y1gHYnk1xGLeXOCGllecsNhDhw=; b=a65/UhZfV5Ke7rkvf3eMHGXnZIEK5oPoNw4lE6VzMgjx3BQeoc2CqJ90bqH2XcZ2EQ XwO+tLoswlXRJU4KH77jiVGZrL6w9sQrorafEKCtQXxBbTnoxSyU2nHwoQ2Vjc+E7YLs ynK3pvV6iIqZixMnoHbrkM3Z/24L5gyEVmXrwO7d6aTlFWVgnh99Dy6EelNuT3zsccFF A/8J8eLOAPaRDbFmzesAq3C4JF2Kci/GKhGOHFzMGdNNQ0v5XiYpjbJ9OaUw/CrXCF2z c69qqKUrwuLEQzKk0h10z73iEIz5NnYVnvXUKJUB0EiilT3CmwpX4VcRgTRBm3Jgd2na 0e3g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771602104; x=1772206904; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=RgZWWYUETcp+KAK54Y1gHYnk1xGLeXOCGllecsNhDhw=; b=vEQJGG1y9BTFTMAkSTNR5LKt7RSKv0aZnQB7qPRFmtgKc8ISxc5MW/NZ9ab79AM+eS NUHwI9Om034FGKI90DiaIucK6LM9Qf+00kVDAVSNDwfVH1XwojNqq38BY8moVumesAeL V904W100l/A0fbbkEoE4/y2tlahNEExfvVb9KzZ3H2EyUJw48Di97txlLeq33KnI+FAd P4vCeMwdzW+ntKclrpRkuCN28nieyi3xj4qgDRcYoC/MFLJMdC1Y2KqJb/R3IwEecupF u5ohnjTedIgeWGi1BhamCt9WsjDx5ISXhLAvSY98PKN4MG+3AEzPRB1YlrF6MIDBSfJC O49g== X-Gm-Message-State: AOJu0YyTVerR+TaPMzcfF7MPlZmdhytKyJnJPqzwOT8s3j1R44LoncN3 pgngrvh+wr3QYCKIAkfxQrRWthNR11dyUqDi2wEFlSjl8GhQBkedud5BtN9ayw== X-Gm-Gg: AZuq6aJ2fWpXhDe6Cy5AgIJ3bJ4usAwC9jJvAInULulmS1ol19GetTQPFaHV1G5U3pT 6Ka+D8h1oWxaj9edZW8NP8PbUOHUy6PbgBmsR5fKYUN55vkHSV0u3mjz1lLeQbgr83uQp5lWHhP 97PEbYUjshWpGj7tp0Krwgy+6wpUqetWkrKNOX6r8UzBch4Vtb3W+vXjSWlKQCcjGIzlfNgK+Jl YMhhHN0TcK3uMYNafJplwkaYwJpHw2stD/yma0aqXSIKmLwTguKJFc7gGpKpAvnCvCyI+wtI7Uk zjX4Qfiid0/RoXk5CjVdxwwBJxhRAf5aYDVR4oMtYz2J2gwA67LNFD2lMObxeyqn5gCS6in/xef rR8jspjlOXbboXWKQWkq6F9fFkuDBBu2mLKV9aGQhGGYD8sS9Nq1E0zshhrRd1//76Xqv+kk9zs oPrRjKcns1pt+twB4Z/fuWOvK/M80HcEM= X-Received: by 2002:a4a:e906:0:b0:677:18e5:e5bb with SMTP id 006d021491bc7-679c449a216mr148567eaf.33.1771602103726; Fri, 20 Feb 2026 07:41:43 -0800 (PST) Received: from localhost.localdomain ([2601:282:4200:11c0::6492]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6772583294bsm15127188eaf.14.2026.02.20.07.41.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Feb 2026 07:41:43 -0800 (PST) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: benjamin.robin@bootlin.com, Joshua Watt Subject: [OE-core][PATCH 7/9] spdx: De-duplicate CreationInfo Date: Fri, 20 Feb 2026 08:40:42 -0700 Message-ID: <20260220154123.376880-8-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260220154123.376880-1-JPEWhacker@gmail.com> References: <20260220154123.376880-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Fri, 20 Feb 2026 15:41:49 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231526 De-duplicates CreationInfo objects that are identical (except for ID) when writing out an SBoM. This significantly reduces the number of CreationInfo objects that end up in the final document. Signed-off-by: Joshua Watt --- meta/lib/oe/sbom30.py | 112 ++++++++++++++++++++++++++++++------------ meta/lib/oe/spdx30.py | 2 +- 2 files changed, 81 insertions(+), 33 deletions(-) diff --git a/meta/lib/oe/sbom30.py b/meta/lib/oe/sbom30.py index 21f084dc16..55a2863d2d 100644 --- a/meta/lib/oe/sbom30.py +++ b/meta/lib/oe/sbom30.py @@ -14,6 +14,7 @@ import uuid import os import oe.spdx_common from datetime import datetime, timezone +from contextlib import contextmanager OE_SPDX_BASE = "https://rdf.openembedded.org/spdx/3.0/" @@ -191,6 +192,25 @@ def to_list(l): return l +class Dedup(object): + def __init__(self, objset): + self.unique = set() + self.dedup = {} + self.objset = objset + + def find_duplicates(self, cmp, typ, **kwargs): + for o in self.objset.foreach_filter(typ, **kwargs): + for u in self.unique: + if cmp(u, o): + self.dedup[o] = u + break + else: + self.unique.add(o) + + def get(self, o): + return self.dedup.get(o, o) + + class ObjectSet(oe.spdx30.SHACLObjectSet): def __init__(self, d): super().__init__() @@ -895,6 +915,45 @@ class ObjectSet(oe.spdx30.SHACLObjectSet): self.missing_ids -= set(imports.keys()) return self.missing_ids + @contextmanager + def deduplicate(self): + d = Dedup(self) + + yield d + + visited = set() + + def visit(o, path): + if isinstance(o, oe.spdx30.SHACLObject): + if o in visited: + return False + visited.add(o) + + for k in o: + v = o[k] + if isinstance(v, oe.spdx30.SHACLObject): + o[k] = d.get(v) + + elif isinstance(o, oe.spdx30.ListProxy): + for idx, v in enumerate(o): + if isinstance(v, oe.spdx30.SHACLObject): + o[idx] = d.get(v) + + return True + + if d.dedup: + for o in self.objects: + o.walk(visit) + + for k, v in d.dedup.items(): + bb.debug( + 1, + f"Removing duplicate {k.__class__.__name__} {k._id or id(k)} -> {v._id or id(v)}", + ) + self.objects.discard(k) + + self.create_index() + def load_jsonld(d, path, required=False): deserializer = oe.spdx30.JSONLDDeserializer() @@ -1080,39 +1139,28 @@ def create_sbom(d, name, root_elements, add_objectsets=[]): # SBoM should be the only root element of the document objset.doc.rootElement = [sbom] - # De-duplicate licenses - unique = set() - dedup = {} - for lic in objset.foreach_type(oe.spdx30.simplelicensing_LicenseExpression): - for u in unique: - if ( - u.simplelicensing_licenseExpression - == lic.simplelicensing_licenseExpression - and u.simplelicensing_licenseListVersion - == lic.simplelicensing_licenseListVersion - ): - dedup[lic] = u - break - else: - unique.add(lic) - - if dedup: - for rel in objset.foreach_filter( - oe.spdx30.Relationship, - relationshipType=oe.spdx30.RelationshipType.hasDeclaredLicense, - ): - rel.to = [dedup.get(to, to) for to in rel.to] - - for rel in objset.foreach_filter( - oe.spdx30.Relationship, - relationshipType=oe.spdx30.RelationshipType.hasConcludedLicense, - ): - rel.to = [dedup.get(to, to) for to in rel.to] + def cmp_license_expression(a, b): + return ( + a.simplelicensing_licenseExpression == b.simplelicensing_licenseExpression + and a.simplelicensing_licenseListVersion + == b.simplelicensing_licenseListVersion + ) - for k, v in dedup.items(): - bb.debug(1, f"Removing duplicate License {k._id} -> {v._id}") - objset.objects.remove(k) + def cmp_creation_info(a, b): + data_a = {k: a[k] for k in a} + data_b = {k: b[k] for k in b} + data_a["@id"] = "" + data_b["@id"] = "" + return data_a == data_b + + with objset.deduplicate() as dedup: + # De-duplicate licenses + dedup.find_duplicates( + cmp_license_expression, + oe.spdx30.simplelicensing_LicenseExpression, + ) - objset.create_index() + # Deduplicate creation info + dedup.find_duplicates(cmp_creation_info, oe.spdx30.CreationInfo) return objset, sbom diff --git a/meta/lib/oe/spdx30.py b/meta/lib/oe/spdx30.py index cd97eebd18..1f58402ffc 100644 --- a/meta/lib/oe/spdx30.py +++ b/meta/lib/oe/spdx30.py @@ -701,7 +701,7 @@ class SHACLObject(object): self.__dict__["_obj_data"][iri] = prop.init() def __iter__(self): - return self._OBJ_PROPERTIES.keys() + return iter(self._OBJ_PROPERTIES.keys()) def walk(self, callback, path=None): """ From patchwork Fri Feb 20 15:40:43 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 81490 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id D3489C5620A for ; Fri, 20 Feb 2026 15:41:48 +0000 (UTC) Received: from mail-oo1-f66.google.com (mail-oo1-f66.google.com [209.85.161.66]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.352.1771602105375669397 for ; Fri, 20 Feb 2026 07:41:45 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=PFmvkP3b; spf=pass (domain: gmail.com, ip: 209.85.161.66, mailfrom: jpewhacker@gmail.com) Received: by mail-oo1-f66.google.com with SMTP id 006d021491bc7-66ee7b9af94so824828eaf.0 for ; Fri, 20 Feb 2026 07:41:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771602104; x=1772206904; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=/5btMM1m1xoswc7pDz9OPzm0iIQ+9RQGJQ6XCnRd1M8=; b=PFmvkP3bg3fguwEwV4znGKScYJ3KAEzajN8h5Uszc5BLJqSK2Tu9TBRxnyWncpkk1N H4DK3aC5UB4vsY4fbjNQ4yiHWAh2YP4Qe5L14KPxmG73V9Q22Zj572dXcDmGDIYlK2xr GLPqxIBUgX+SZP09+QL2KaBVKoW1vgdW+kIkahap+HwWntttZWCv9UxhqF1R+S6FM5o/ pdFhH/QIxMeJP9Q5A+xWM9EeXhwi8taggrjQ2RkEJQJFYkuMlVXMpIe7qRB5vU323/vz YnuTy3xmIdIXiR9ZK8EItHMDe6SDNK5ZWBgnJFqT09NXOAHeJO77WHjWUvK55KTK26jb HxKA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771602104; x=1772206904; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=/5btMM1m1xoswc7pDz9OPzm0iIQ+9RQGJQ6XCnRd1M8=; b=W0EKUHEXt5yK7O2RoMMAyDKEMGg31anv4RKWdzxOaUn5GuzvhVAumVZbVv5U3QSpbY 9FX5LcXpWzhD7aH3fhM5Oqpt/7mYSfpr9FFKevBlmsZvlrDYDPKCNPLrgFZpg1qZTC/K bVY3yaw/9dUZvJjcT1Fnvcijqn/zx5rpz6N8VeDKo8K52mQrcalFHPA/zV7aNC/IimU9 9wV3idIny4W2kZU37W79FPFKO4GofeFAf+jOW59iLBkR53fTOMbiT3ANdejr7k7wD9AM iO9tw2SDeZ7cLfrgh822AwPn96QqBHFyusyaDNTz5BZADRo16iJxHFLx5A0o0kD7Cgr1 stRQ== X-Gm-Message-State: AOJu0YxWjFeZ0H77hFNjTR5xv49zt4f1jiyXXg59Dc4BWvbGD8bNkgyQ Gz+/mBWBeiitNKmPvOBII7VLYLNsrHCXvGgmK/JX/lDA5+g4rNGu3a1wisiirU7t X-Gm-Gg: AZuq6aI03YLa9NqTna8UYo+NVaPMLUKUtmJuEcDQlRQr9+3XDBiVCueP26mD55V8bpK A3RhNAE7kZH+HltzrRgYc2N3ALQ8LP+xFlnHC0chTiYvNquC1raZPFMfsIBlTtE4ASI94S2bySZ nvzM3+uWLL4zv9tUdi7GCuU3GD7/IP9Gs7tB1L1e7m4Ue8NseDcJC9hkUmYpnkUOofAg1AXBcqL NYEwgCt6K4yPRxX4JgGKLh8HRnaFSXs4d9njvdfG95RDJ9oyqbbRbQPVcq0qHaKNSgd04X8/s1l tluZDUvyKSE86sk3onXIO1n7dd0+07gQzXDa1ogMIy42u8ETcm8gcFq+nxCGlinQEq+MthgwcCh XPn8JXLQQ17h8u1Oc74nUqNhf3H9LlTPux4TwSDDxKXaz5efqO98KB7gZ+kjBKogCFHbyl+ASsQ fsqU/XhqbH01PA8yNxo9aP X-Received: by 2002:a05:6820:162b:b0:662:ede7:fdaa with SMTP id 006d021491bc7-679c4728b8emr86226eaf.74.1771602104440; Fri, 20 Feb 2026 07:41:44 -0800 (PST) Received: from localhost.localdomain ([2601:282:4200:11c0::6492]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6772583294bsm15127188eaf.14.2026.02.20.07.41.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Feb 2026 07:41:44 -0800 (PST) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: benjamin.robin@bootlin.com, Joshua Watt Subject: [OE-core][PATCH 8/9] spdx: Ignore ASSUME_PROVIDED recipes Date: Fri, 20 Feb 2026 08:40:43 -0700 Message-ID: <20260220154123.376880-9-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260220154123.376880-1-JPEWhacker@gmail.com> References: <20260220154123.376880-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Fri, 20 Feb 2026 15:41:48 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231527 Ignore recipes in ASSUME_PROVIDED when generating SPDX dependencies, since these recipes are not actually built Signed-off-by: Joshua Watt --- meta/lib/oe/spdx_common.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/meta/lib/oe/spdx_common.py b/meta/lib/oe/spdx_common.py index 72c24180d5..03863ef362 100644 --- a/meta/lib/oe/spdx_common.py +++ b/meta/lib/oe/spdx_common.py @@ -104,9 +104,12 @@ def collect_direct_deps(d, dep_task): deps = set() + ignore = set(d.getVar("ASSUME_PROVIDED").split()) + ignore.add(pn) + for dep_name in this_dep.deps: dep_data = taskdepdata[dep_name] - if dep_data.taskname == dep_task and dep_data.pn != pn: + if dep_data.taskname == dep_task and dep_data.pn not in ignore: deps.add((dep_data.pn, dep_data.hashfn, dep_name in this_dep.taskhash_deps)) return sorted(deps) From patchwork Fri Feb 20 15:40:44 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joshua Watt X-Patchwork-Id: 81493 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1D3A7C5AC9E for ; Fri, 20 Feb 2026 15:41:49 +0000 (UTC) Received: from mail-oo1-f49.google.com (mail-oo1-f49.google.com [209.85.161.49]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.322.1771602106299635682 for ; Fri, 20 Feb 2026 07:41:46 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=Nw6awIB6; spf=pass (domain: gmail.com, ip: 209.85.161.49, mailfrom: jpewhacker@gmail.com) Received: by mail-oo1-f49.google.com with SMTP id 006d021491bc7-66f3e7d9eccso1160601eaf.1 for ; Fri, 20 Feb 2026 07:41:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771602105; x=1772206905; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=LF6odpk95FsjyxMBfzPbzwWYGWajjqW2u5EdOHPcbC0=; b=Nw6awIB60uC2ZRQENxkFjSBjXCyo9d/TsZ5V2NlSgPAvJKZP/po4dWFxW47CjEhzwD 7x6MTP5fQFHIVDySNqlClBK2ykfhwI+GqMjn5/OV8Y+jsZHN7nQx+1IqzJOGAqmt7Ibl uELCg/DF29WebbjjYylXW61BGaWMqpQGOJBU3IX77fjqsJucm8IHcXUT1Te9r7uPAHti wjkJs8t8uPeoaSyj/bKyIlrALs+lJtouhRf7WTJnbJR/ZQN2/phE7hpkY8EyRjxSvsBR Ni1ZJNfNpkWcODqBMuri+960Z40swfkD5vohfUQ6dGT2odWdQmfgMRzcjULVAco46nqO +flg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771602105; x=1772206905; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=LF6odpk95FsjyxMBfzPbzwWYGWajjqW2u5EdOHPcbC0=; b=cd+TeASMoqjFhNi2NzqtlFbpmoturm8UzyyKXR8xc6scI2CMd7Zu9CBbmDWa9ZJ+In ON54SdZM+MGYGN8aWJrS5fcl6vPb1xRk7pVgyJlq8hR3PCTi4prrcalrY6aQAh1CtmQo c/0tEcYk5oWzeYmsVkLVgRLqjwSNAXudug4xKt4HyTNnkXpvkZ8EzukjcwAufeMTODbZ fUJVRtqVOtvW2P3SUljjlqrB0Fd/GFOMV10NNL8CMtEWCPStiqaTU5bsUBKhDXvR3yYe T6Pot8ZdImuGvq1qL8StvbA39o1cSXHrQubD1JthbC9Z45fb/Zssqzbtphqf63axpEij CGbA== X-Gm-Message-State: AOJu0YwptNUHYkHv5RLwLH0IjIa4poiqzlxk+/Mnzcjzv6LrNhjFRMbB R+BFPSg1F3ACDhh3L42ukfMgFq05jTIrUHyPmW5LpUzsAgMCVhxHOkT2JCbn5w== X-Gm-Gg: AZuq6aLQX1g8lVOS7oxiXEhsibA+Mzb4uUfyoJd0jQwMDo1LejvLkCCrUQmyIbLrmXh mbY0AnYmE+QXF9HQHYMPUH6JKvvWQ/h4ePADEJFpik85SeVA6ZX8fvfTvnv9tqI8tOl7GSfcGrU HR6g28OabmL8STsIiRKXcv6U8Z6QTIfiaGa8LcTj6at8Ymv7ipQBCSs+VlS9nL7c5gVVioOuTo9 X1N/0rmXbp5cXwwda0HhQkGfNiRdnYRP7aRH7oq9DRl6dY5t/PzD5cwlxBdRBQ4Mlpw310DKqn/ Svkm5IWpOocPwRdvV8fGE/40tknp8Q6hTivqR2QmKkZ5Y+kIiAUMVYxJZsyxlEXI4XkAvMfyIAE SIFLvFYWibTNtTio1eZL0kPDYhh8BWgMpV+Mf+6jEAvQa3k8/LNLRQZzvQsMDGmbCZT6jmvUgNV uhbPAj3LDKTV8jllbtFbtGX/InjVDrSPM= X-Received: by 2002:a05:6820:2222:b0:677:fc0b:c5 with SMTP id 006d021491bc7-679c44d3634mr149676eaf.31.1771602105301; Fri, 20 Feb 2026 07:41:45 -0800 (PST) Received: from localhost.localdomain ([2601:282:4200:11c0::6492]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-6772583294bsm15127188eaf.14.2026.02.20.07.41.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Feb 2026 07:41:44 -0800 (PST) From: Joshua Watt X-Google-Original-From: Joshua Watt To: openembedded-core@lists.openembedded.org Cc: benjamin.robin@bootlin.com, Joshua Watt Subject: [OE-core][PATCH 9/9] spdx_common: Check for dependent task in task flags Date: Fri, 20 Feb 2026 08:40:44 -0700 Message-ID: <20260220154123.376880-10-JPEWhacker@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260220154123.376880-1-JPEWhacker@gmail.com> References: <20260220154123.376880-1-JPEWhacker@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Fri, 20 Feb 2026 15:41:49 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/231528 Checks that the task being used to detect dependencies is present in at least one dependency task flag of the current task. This helps prevent errors where the wrong task is specified and never found. Signed-off-by: Joshua Watt --- meta/lib/oe/spdx_common.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/meta/lib/oe/spdx_common.py b/meta/lib/oe/spdx_common.py index 03863ef362..9d39b5e9ac 100644 --- a/meta/lib/oe/spdx_common.py +++ b/meta/lib/oe/spdx_common.py @@ -96,6 +96,17 @@ def collect_direct_deps(d, dep_task): taskdepdata = d.getVar("BB_TASKDEPDATA", False) + # Check that the task is listed one of the task dependency flags of the + # current task + depflags = ( + set((d.getVarFlag(current_task, "deptask") or "").split()) + | set((d.getVarFlag(current_task, "rdeptask") or "").split()) + | set((d.getVarFlag(current_task, "recrdeptask") or "").split()) + ) + + if not dep_task in depflags: + bb.fatal(f"Task {dep_task} was not found in any dependency flag of {pn}:{current_task}") + for this_dep in taskdepdata.values(): if this_dep[0] == pn and this_dep[1] == current_task: break