@@ -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,16 @@ 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. This should be called from an anonymous Python function and uses
+ # d.setVar() to avoid unnecessarily evaluating this on every data store
+ # update.
+ default_features = set_difference(varname + "_DEFAULT_RAW",
+ varname + "_OPTED_OUT",
+ d)
+ d.setVar(varname + "_DEFAULT", default_features)
+
def all_distro_features(d, features, truevalue="1", falsevalue=""):
"""
Returns truevalue if *all* given features are set in DISTRO_FEATURES,
@@ -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,32 @@ 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_DEFAULT_RAW", "alpha beta gamma")
+ filter_default_features("DISTRO_FEATURES", d)
+ self.assertEqual(d.getVar("DISTRO_FEATURES_DEFAULT"), "alpha beta gamma")
+
+ # opt out of a single feature
+ d.setVar("DISTRO_FEATURES_OPTED_OUT", "beta")
+ filter_default_features("DISTRO_FEATURES", d)
+ self.assertEqual(d.getVar("DISTRO_FEATURES_DEFAULT"), "alpha gamma")
+
+ # opt out of everything
+ d.setVar("DISTRO_FEATURES_OPTED_OUT", "gamma alpha beta")
+ filter_default_features("DISTRO_FEATURES", d)
+ self.assertEqual(d.getVar("DISTRO_FEATURES_DEFAULT"), "")
+
+ # opt out of something that isn't in our defaults
+ d.setVar("DISTRO_FEATURES_OPTED_OUT", "omega")
+ filter_default_features("DISTRO_FEATURES", d)
+ self.assertEqual(d.getVar("DISTRO_FEATURES_DEFAULT"), "alpha beta gamma")
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 | 35 +++++++++++++++++++++++++++++ meta/lib/oeqa/selftest/cases/oelib/utils.py | 31 ++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-)