[PATCH v3 1/4] oelib: utils: Support filtering default features

Paul Barker
2026-04-01T17:42:05Z

The new library function filter_default_features() allows us to support
opting out of any default features without having to use :remove.

Signed-off-by: Paul Barker <paul@pbarker.dev>
---
meta/lib/oe/utils.py | 34 ++++++++++++++++++++++++++
meta/lib/oeqa/selftest/cases/oelib/utils.py | 38 ++++++++++++++++++++++++++++-
2 files changed, 71 insertions(+), 1 deletion(-)

diff --git a/meta/lib/oe/utils.py b/meta/lib/oe/utils.py
index afcfeda0c6d5..52f2f1bd55e0 100644
--- a/meta/lib/oe/utils.py
+++ b/meta/lib/oe/utils.py
@@ -83,6 +83,31 @@ def set_intersect(variable1, variable2, d):
val2 = set(d.getVar(variable2).split())
return " ".join(val1 & val2)

+def set_difference(variable1, variable2, d):
+ """
+ Expand both variables, interpret them as lists of strings, and return the
+ intersection as a flattened string.
+
+ For example:
+ s1 = "a b c"
+ s2 = "b c d"
+ s3 = set_difference(s1, s2)
+ => s3 = "a"
+ """
+ val1 = d.getVar(variable1)
+ if not val1:
+ return ""
+ val2 = d.getVar(variable2)
+ if not val2:
+ return val1
+
+ val1 = set(val1.split())
+ val2 = set(val2.split())
+
+ # Return a sorted string to ensure that the result is consistent between
+ # parser runs.
+ return " ".join(sorted(val1 - val2))
+
def prune_suffix(var, suffixes, d):
# See if var ends with any of the suffixes listed and
# remove it if found
@@ -133,6 +158,15 @@ def features_backfill(var,d):
if addfeatures:
d.appendVar(var, " " + " ".join(addfeatures))

+def filter_default_features(varname, d):
+ # Process default features to exclude features which the user has opted out
+ # of. The result is appended to the target variable (e.g. DISTRO_FEATURES
+ # or MACHINE_FEATURES).
+ default_features = set_difference(varname + "_DEFAULTS",
+ varname + "_OPTED_OUT",
+ d)
+ d.appendVar(varname, " " + default_features)
+
def all_distro_features(d, features, truevalue="1", falsevalue=""):
"""
Returns truevalue if *all* given features are set in DISTRO_FEATURES,
diff --git a/meta/lib/oeqa/selftest/cases/oelib/utils.py b/meta/lib/oeqa/selftest/cases/oelib/utils.py
index 0cb46425a02d..a72a0f5983f1 100644
--- a/meta/lib/oeqa/selftest/cases/oelib/utils.py
+++ b/meta/lib/oeqa/selftest/cases/oelib/utils.py
@@ -8,7 +8,7 @@ import sys
from unittest.case import TestCase
from contextlib import contextmanager
from io import StringIO
-from oe.utils import packages_filter_out_system, trim_version, multiprocess_launch
+from oe.utils import packages_filter_out_system, trim_version, multiprocess_launch, filter_default_features

class TestPackagesFilterOutSystem(TestCase):
def test_filter(self):
@@ -102,3 +102,39 @@ class TestMultiprocessLaunch(TestCase):
with captured_output() as (out, err):
self.assertRaises(bb.BBHandledException, multiprocess_launch, testfunction, ["1", "2", "3", "4", "5", "6"], d, extraargs=(d,))
self.assertIn("KeyError: 'Invalid number 2'", out.getvalue())
+
+
+class TestDefaultFeatures(TestCase):
+ def test_filter_default_features(self):
+ try:
+ import bb
+ d = bb.data_smart.DataSmart()
+ except ImportError:
+ self.skipTest("Cannot import bb")
+
+ # Test with nothing opted out
+ d.setVar("DISTRO_FEATURES", "")
+ d.setVar("DISTRO_FEATURES_DEFAULTS", "alpha beta gamma")
+ filter_default_features("DISTRO_FEATURES", d)
+ self.assertEqual(d.getVar("DISTRO_FEATURES").strip(), "alpha beta gamma")
+
+ # opt out of a single feature
+ d.setVar("DISTRO_FEATURES", "")
+ d.setVar("DISTRO_FEATURES_DEFAULTS", "alpha beta gamma")
+ d.setVar("DISTRO_FEATURES_OPTED_OUT", "beta")
+ filter_default_features("DISTRO_FEATURES", d)
+ self.assertEqual(d.getVar("DISTRO_FEATURES").strip(), "alpha gamma")
+
+ # opt out of everything
+ d.setVar("DISTRO_FEATURES", "")
+ d.setVar("DISTRO_FEATURES_DEFAULTS", "alpha beta gamma")
+ d.setVar("DISTRO_FEATURES_OPTED_OUT", "gamma alpha beta")
+ filter_default_features("DISTRO_FEATURES", d)
+ self.assertEqual(d.getVar("DISTRO_FEATURES").strip(), "")
+
+ # opt out of something that isn't in our defaults
+ d.setVar("DISTRO_FEATURES", "")
+ d.setVar("DISTRO_FEATURES_DEFAULTS", "alpha beta gamma")
+ d.setVar("DISTRO_FEATURES_OPTED_OUT", "omega")
+ filter_default_features("DISTRO_FEATURES", d)
+ self.assertEqual(d.getVar("DISTRO_FEATURES").strip(), "alpha beta gamma")

--
2.43.0