From: Joshua Watt <[email protected]>
Sent: Monday, June 29, 2026 10:03 PM
To: Marko, Peter (FT D EU SK BFS1) <[email protected]>
Cc: [email protected]
Subject: Re: [OE-core] [PATCH 2/2] rootfs,spdx: handle removed packages



On Mon, Jun 29, 2026 at 6:22 AM Marko, Peter 
<[email protected]<mailto:[email protected]>> wrote:


> -----Original Message-----
> From: Joshua Watt <[email protected]<mailto:[email protected]>>
> Sent: Friday, June 26, 2026 4:11 PM
> To: Marko, Peter (FT D EU SK BFS1) 
> <[email protected]<mailto:[email protected]>>
> Cc: 
> [email protected]<mailto:[email protected]>
> Subject: Re: [OE-core] [PATCH 2/2] rootfs,spdx: handle removed packages
>
> On Mon, Jun 8, 2026 at 7:56 AM Marko, Peter 
> <[email protected]<mailto:[email protected]>> wrote:
> >
> >
> >
> > > -----Original Message-----
> > > From: Joshua Watt <[email protected]<mailto:[email protected]>>
> > > Sent: Friday, June 5, 2026 4:16 PM
> > > To: Marko, Peter (FT D EU SK BFS1) 
> > > <[email protected]<mailto:[email protected]>>
> > > Cc: 
> > > [email protected]<mailto:[email protected]>
> > > Subject: Re: [OE-core] [PATCH 2/2] rootfs,spdx: handle removed packages
> > >
> > > On Fri, Jun 5, 2026 at 6:05 AM Peter Marko via 
> > > lists.openembedded.org<http://lists.openembedded.org>
> > > <[email protected]<mailto:[email protected]>>
> > >  wrote:
> > > >
> > > > From: Peter Marko 
> > > > <[email protected]<mailto:[email protected]>>
> > > >
> > > > SPDX should not list packages which were removed from rootfs as 
> > > > installed.
> > > >
> > > > The list of installed packages does not contain them directly, but as
> > > > dependencies of other installed packages.
> > > >
> > > > Siwtch them to "other" to keep them in SPDX as part of the build and
> > > > installation process.
> > >
> > > I agree the packages should be removed from the rootfs, but using the
> > > method described.
> > >
> > > Instead, the list of removed packages should be passed to
> > > collect_build_package_inputs(), and anything removed should skip
> > > having it's files added to files_by_hash (but, should still be added
> > > to build_deps, since it is a build dependency).
> >
> > I have tried several approaches here but all without success until now..
> > The problem is that these removed packages are "rdepends" and not "depends".
> > Thus in collect_build_package_inputs they are not present yet.
> > And even if they would be, the rdepends would anyway add it back later.
> >
> > SPDX entries for rdepend clauses are added in expand_collection function.
> > I obviously cannot remove the rdepends clause when the recipe sbom is 
> > created.
> >
> > The best approach probably is to remove the rdepends when iterating over 
> > sbomx
> in expand_collection.
> > Would you agree when I try to send a patch in that direction?
> > Or do you have any other suggestion?
>
> That's going to be tricky because you can't just remove the packages;
> you also need to remove any reference to the package (e.g. if they are
> in a relationship or somewhere else); otherwise the resulting document
> will not be fully linked and you'll get errors when creating the SBoM.
> For an example of what that would sort of look like, you can look at
> the "duplicate" function which is used to replace references to
> licenses and CreationInfo with equivalent ones to reduce the document
> size.
>
> But, this is going to complicate an otherwise pretty simple merging
> algorithm; I'm just not sure we can currently accurately express what
> is going on here (packages being uninstalled at install time), and
> until we have a good idea how to express it, I don't want to make it
> more confusing by doing something weird (because, then people rely on
> the weird behavior and get upset when it changes because we found a
> better solution).
>

Little sad that there is currently no clear vision how to handle this scenario 
in our classes.
In case you get a new idea, let me know and I can spend the effort to convert 
that to a patch.

I have created https://bugzilla.yoctoproject.org/show_bug.cgi?id=16336 for this 
so it's not forgotten.

I think we will need something like this: 
https://github.com/spdx/spdx-3-model/pull/1297 but I will see if the SPDX 
community has a better idea in the next few weeks. Basically the idea would be 
to add a relationship that indicates that the package was uninstalled from the 
rootfs, while preserving the runtime dependencies.

That however does not help scarthgap/wrynose as it will be in spdx 3.1.0, 
correct?


Peter

> >
> > >
> > > This will prevent that rootfs package from "containing" the files for
> > > removed packages, and break the linkage between the rootfs and the
> > > removed packages.
> > >
> > > I'm not exactly sure the reasoning behind changing the type the
> > > "other", but we should not be doing that.
> >
> > Well, "other" because there was no good value for that available in enum
> (installation helper).
> >
> > >
> > > >
> > > > Signed-off-by: Peter Marko 
> > > > <[email protected]<mailto:[email protected]>>
> > > > ---
> > > >  meta/classes-recipe/create-spdx-image-3.0.bbclass |  7 +++++++
> > > >  meta/lib/oe/rootfs.py                             |  6 ++++++
> > > >  meta/lib/oe/sbom30.py                             |  8 +++++++-
> > > >  meta/lib/oe/spdx30_tasks.py                       | 14 +++++++++++++-
> > > >  4 files changed, 33 insertions(+), 2 deletions(-)
> > > >
> > > > diff --git a/meta/classes-recipe/create-spdx-image-3.0.bbclass 
> > > > b/meta/classes-
> > > recipe/create-spdx-image-3.0.bbclass
> > > > index 15a91e90e2..dfbd2961b3 100644
> > > > --- a/meta/classes-recipe/create-spdx-image-3.0.bbclass
> > > > +++ b/meta/classes-recipe/create-spdx-image-3.0.bbclass
> > > > @@ -6,6 +6,7 @@
> > > >  # SPDX image tasks
> > > >
> > > >  SPDX_ROOTFS_PACKAGES = "${SPDXDIR}/rootfs-packages.json"
> > > > +SPDX_ROOTFS_REMOVED_PACKAGES = "${SPDXDIR}/rootfs-removed-
> > > packages.json"
> > > >  SPDXIMAGEDEPLOYDIR = "${SPDXDIR}/image-deploy"
> > > >  SPDXROOTFSDEPLOY = "${SPDXDIR}/rootfs-deploy"
> > > >
> > > > @@ -15,14 +16,20 @@ python spdx_collect_rootfs_packages() {
> > > >      from oe.rootfs import image_list_installed_packages
> > > >
> > > >      root_packages_file = Path(d.getVar("SPDX_ROOTFS_PACKAGES"))
> > > > +    root_removed_packages_file =
> > > Path(d.getVar("SPDX_ROOTFS_REMOVED_PACKAGES"))
> > > >
> > > >      packages = image_list_installed_packages(d)
> > > >      if not packages:
> > > >          packages = {}
> > > >
> > > > +    removed_packages = (d.getVar("ROOTFS_REMOVED_PACKAGES") or
> > > "").split()
> > > > +
> > > >      root_packages_file.parent.mkdir(parents=True, exist_ok=True)
> > > >      with root_packages_file.open("w") as f:
> > > >          json.dump(packages, f)
> > > > +
> > > > +    with root_removed_packages_file.open("w") as f:
> > > > +        json.dump(removed_packages, f)
> > > >  }
> > > >  ROOTFS_POSTUNINSTALL_COMMAND =+
> "spdx_collect_rootfs_packages"
> > > >
> > > > diff --git a/meta/lib/oe/rootfs.py b/meta/lib/oe/rootfs.py
> > > > index 5eee48f587..b8830596ed 100644
> > > > --- a/meta/lib/oe/rootfs.py
> > > > +++ b/meta/lib/oe/rootfs.py
> > > > @@ -261,10 +261,13 @@ class Rootfs(object, metaclass=ABCMeta):
> > > >
> > > >
> > > >      def _uninstall_unneeded(self):
> > > > +        removed_pkgs = set()
> > > > +
> > > >          # Remove the run-postinsts package if no delayed postinsts are 
> > > > found
> > > >          delayed_postinsts = self._get_delayed_postinsts()
> > > >          if delayed_postinsts is None:
> > > >              if
> > > os.path.exists(self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/init.d/run-
> postinsts"))
> > > or
> os.path.exists(self.d.expand("${IMAGE_ROOTFS}${systemd_system_unitdir}/run-
> > > postinsts.service")):
> > > > +                removed_pkgs.add("run-postinsts")
> > > >                  self.pm.remove(["run-postinsts"])
> > > >
> > > >          image_rorfs = bb.utils.contains("IMAGE_FEATURES", 
> > > > "read-only-rootfs",
> > > > @@ -285,6 +288,7 @@ class Rootfs(object, metaclass=ABCMeta):
> > > >              # to be uninstalled or to be managed correctly otherwise.
> > > >              provider = 
> > > > self.d.getVar("VIRTUAL-RUNTIME_update-alternatives")
> > > >              pkgs_to_remove = sorted([pkg for pkg in pkgs_installed if 
> > > > pkg in
> > > unneeded_pkgs], key=lambda x: x == provider)
> > > > +            removed_pkgs.update(pkgs_to_remove)
> > > >
> > > >              # update-alternatives provider is removed in its own 
> > > > remove()
> > > >              # call because all package managers do not guarantee the 
> > > > packages
> > > > @@ -296,6 +300,8 @@ class Rootfs(object, metaclass=ABCMeta):
> > > >              if len(pkgs_to_remove) > 0:
> > > >                  self.pm.remove([pkgs_to_remove[-1]], False)
> > > >
> > > > +        self.d.setVar("ROOTFS_REMOVED_PACKAGES", "
> > > ".join(sorted(removed_pkgs)))
> > > > +
> > > >          if delayed_postinsts:
> > > >              self._save_postinsts()
> > > >              if image_rorfs:
> > > > diff --git a/meta/lib/oe/sbom30.py b/meta/lib/oe/sbom30.py
> > > > index b379ff947c..4fa32266fa 100644
> > > > --- a/meta/lib/oe/sbom30.py
> > > > +++ b/meta/lib/oe/sbom30.py
> > > > @@ -1122,7 +1122,7 @@ def find_by_spdxid(d, spdxid, *, required=False):
> > > >      return find_jsonld(d, *jsonld_hash_path(hash_id(spdxid)), 
> > > > required=required)
> > > >
> > > >
> > > > -def create_sbom(d, name, root_elements, add_objectsets=[]):
> > > > +def create_sbom(d, name, root_elements, add_objectsets=[],
> > > removed_packages=[]):
> > > >      objset = ObjectSet.new_objset(d, name)
> > > >
> > > >      sbom = objset.add(
> > > > @@ -1142,6 +1142,12 @@ def create_sbom(d, name, root_elements,
> > > add_objectsets=[]):
> > > >              + "\n  ".join(sorted(list(missing_spdxids)))
> > > >          )
> > > >
> > > > +    if removed_packages:
> > > > +        for pkg in objset.foreach_type(oe.spdx30.software_Package):
> > > > +            if pkg.name<http://pkg.name> in removed_packages and 
> > > > pkg.software_primaryPurpose
> ==
> > > oe.spdx30.software_SoftwarePurpose.install:
> > > > +                pkg.software_primaryPurpose =
> > > oe.spdx30.software_SoftwarePurpose.other
> > > > +                bb.note("Reclassified removed package %s SPDX entry 
> > > > from install
> to
> > > other" % pkg.name<http://pkg.name>)
> > > > +
> > > >      # Filter out internal extensions from final SBoMs
> > > >      objset.remove_internal_extensions()
> > > >
> > > > diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py
> > > > index 7cc46d579b..18c68f47de 100644
> > > > --- a/meta/lib/oe/spdx30_tasks.py
> > > > +++ b/meta/lib/oe/spdx30_tasks.py
> > > > @@ -1532,6 +1532,7 @@ def create_image_sbom_spdx(d):
> > > >      image_link_name = d.getVar("IMAGE_LINK_NAME")
> > > >      imgdeploydir = Path(d.getVar("SPDXIMAGEDEPLOYDIR"))
> > > >      machine = d.getVar("MACHINE")
> > > > +    root_removed_packages_file =
> > > Path(d.getVar("SPDX_ROOTFS_REMOVED_PACKAGES"))
> > > >
> > > >      spdx_path = imgdeploydir / (image_name + ".spdx.json")
> > > >
> > > > @@ -1553,7 +1554,18 @@ def create_image_sbom_spdx(d):
> > > >      for o in image_objset.foreach_root(oe.spdx30.software_File):
> > > >          root_elements.append(oe.sbom30.get_element_link_id(o))
> > > >
> > > > -    objset, sbom = oe.sbom30.create_sbom(d, image_name, root_elements)
> > > > +    try:
> > > > +        with root_removed_packages_file.open("r") as f:
> > > > +            removed_packages = json.load(f)
> > > > +    except FileNotFoundError:
> > > > +        removed_packages = []
> > > > +
> > > > +    objset, sbom = oe.sbom30.create_sbom(
> > > > +        d,
> > > > +        image_name,
> > > > +        root_elements,
> > > > +        removed_packages=removed_packages,
> > > > +    )
> > > >
> > > >      # Set supplier on root elements if SPDX_IMAGE_SUPPLIER is defined
> > > >      supplier = objset.new_agent("SPDX_IMAGE_SUPPLIER", add=False)
> > > >
> > > > 
> > > >
-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#239829): 
https://lists.openembedded.org/g/openembedded-core/message/239829
Mute This Topic: https://lists.openembedded.org/mt/119660891/21656
Group Owner: [email protected]
Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub 
[[email protected]]
-=-=-=-=-=-=-=-=-=-=-=-

Reply via email to