[PATCHv3,1/3] lists.bbclass: add class

Message ID 20250124212040.473-1-chris.larson@siemens.com
State Superseded, archived
Headers show
Series [PATCHv3,1/3] lists.bbclass: add class | expand

Commit Message

chris.larson Jan. 24, 2025, 9:20 p.m. UTC
From: Christopher Larson <chris.larson@siemens.com>

This class provides functions to improve the functionality of bitbake
list variables.

- Add the ability to remove items from a list variable without using :remove.
- Add the ability for a list item to imply the addition of other list items.

Usage requires either adding the variable name to LIST_VARIABLES, or manually
adding a :remove and a :prepend to each fully supported list variable.

To remove items from a configured list, simply append the item to be removed
to the variable with a '-' or '~' prefix. For example, to remove 'alpha' from
a configured variable, append '-alpha' to it.

To support implied list items, create a mapping of items to be appended to
the variable when a specific item is present. For example, to append 'beta'
to ROOTFS_FEATURES when 'alpha' is present, configure ROOTFS_FEATURES as such,
then set IMPLIED_ROOTFS_FEATURES[alpha] = "beta".

Signed-off-by: Christopher Larson <chris.larson@siemens.com>
---
 meta/classes/lists.bbclass | 105 +++++++++++++++++++++++++++++++++++++
 1 file changed, 105 insertions(+)
 create mode 100644 meta/classes/lists.bbclass

Comments

Uladzimir Bely Feb. 12, 2025, 7:49 a.m. UTC | #1
On Fri, 2025-01-24 at 14:20 -0700, chris.larson via isar-users wrote:
> From: Christopher Larson <chris.larson@siemens.com>
> 
> This class provides functions to improve the functionality of bitbake
> list variables.
> 
> - Add the ability to remove items from a list variable without using
> :remove.
> - Add the ability for a list item to imply the addition of other list
> items.
> 
> Usage requires either adding the variable name to LIST_VARIABLES, or
> manually
> adding a :remove and a :prepend to each fully supported list
> variable.
> 
> To remove items from a configured list, simply append the item to be
> removed
> to the variable with a '-' or '~' prefix. For example, to remove
> 'alpha' from
> a configured variable, append '-alpha' to it.
> 
> To support implied list items, create a mapping of items to be
> appended to
> the variable when a specific item is present. For example, to append
> 'beta'
> to ROOTFS_FEATURES when 'alpha' is present, configure ROOTFS_FEATURES
> as such,
> then set IMPLIED_ROOTFS_FEATURES[alpha] = "beta".
> 
> Signed-off-by: Christopher Larson <chris.larson@siemens.com>
> ---
>  meta/classes/lists.bbclass | 105
> +++++++++++++++++++++++++++++++++++++
>  1 file changed, 105 insertions(+)
>  create mode 100644 meta/classes/lists.bbclass
> 
> diff --git a/meta/classes/lists.bbclass b/meta/classes/lists.bbclass
> new file mode 100644
> index 00000000..db8f5837
> --- /dev/null
> +++ b/meta/classes/lists.bbclass
> @@ -0,0 +1,105 @@
> +# Functions to improve the functionality of bitbake list variables.
> +#
> +# - Add the ability to remove items from a list variable without
> using :remove.
> +# - Add the ability for a list item to imply the addition of other
> list items.
> +#
> +
> +# Usage requires either adding the variable name to LIST_VARIABLES,
> or manually
> +# adding a :remove and a :prepend to each fully supported list
> variable.
> +#
> +# To remove items from a configured list, simply append the item to
> be removed
> +# to the variable with a '-' or '~' prefix. For example, to remove
> 'alpha' from
> +# ROOTFS_FEATURES, add '-alpha' to ROOTFS_FEATURES.
> +#
> +# To support implied list items, create a mapping of items to be
> appended to
> +# the variable when a specific item is present. For example, to
> append 'beta'
> +# to ROOTFS_FEATURES when 'alpha' is present, configure
> ROOTFS_FEATURES as such,
> +# then set IMPLIED_ROOTFS_FEATURES[alpha] = "beta".
> +#
> +# Boilerplate example:
> +#
> +#   # Either this:
> +#   LIST_VARIABLES += "ROOTFS_FEATURES"
> +#
> +#   # Or this:
> +#   ROOTFS_FEATURES:remove =
> "${@remove_prefixed_items('ROOTFS_FEATURES', d)}"
> +#   ROOTFS_FEATURES:prepend =
> "${@add_implied_items('ROOTFS_FEATURES', 'IMPLIED_ROOTFS_FEATURES',
> d)} "
> +#
> +# Usage example:
> +#
> +#   # ROOTFS_FEATURES will be "beta alpha" if the following
> configuration is used:
> +#   IMPLIED_ROOTFS_FEATURES[alpha] = "beta"
> +#   ROOTFS_FEATURES += "alpha"
> +#
> +#   # ROOTFS_FEATURES will be "first" if the following configuration
> is used:
> +#   ROOTFS_FEATURES = "first second"
> +#   ROOTFS_FEATURES += "-second"
> +
> +python enable_list_variables() {
> +    """Enable list variable functionality."""
> +    for variable in d.getVar("LIST_VARIABLES").split():
> +        d.setVar(variable + ':remove', '
> ${@remove_prefixed_items("%s", d)}' % variable)
> +        d.setVar(variable + ':prepend', '${@add_implied_items("%s",
> "IMPLIED_%s", d)} ' % (variable, variable))
> +}
> +enable_list_variables[eventmask] = "bb.event.ConfigParsed"
> +addhandler enable_list_variables
> +
> +def remove_prefixed_items(var, d):
> +    """Return the items to be removed from var with :remove.
> +
> +    This function is intended to be used in a :remove handler to
> remove
> +    items from a variable. It will interpret items prefixed with a
> '-'
> +    or '~' as items to be removed.
> +    """
> +    # Use a flag to avoid infinite recursion.
> +    if d.getVarFlag(var, 'remove_prefixed_items_internal') == '1':
> +        return ''
> +
> +    from collections import Counter
> +
> +    d.setVarFlag(var, 'remove_prefixed_items_internal', '1')
> +    try:
> +        value = d.getVar(var)
> +        counter = Counter()
> +        for v in value.split():
> +            if v.startswith('-') or v.startswith('~'):
> +                counter[v[1:]] -= 1
> +                counter[v] -= 1
> +            else:
> +                counter[v] += 1
> +        return ' '.join(v for v, c in counter.items() if c < 1)
> +    finally:
> +        d.delVarFlag(var, 'remove_prefixed_items_internal')
> +
> +
> +def add_implied_items(var, implied_var, d):
> +    """Return the items to be appended due to the presence of other
> items in var.
> +
> +    This function is intended to be used in a :append handler to
> append
> +    items from a variable. It will rely on the supplied mapping of
> implied items
> +    to append the corresponding items.
> +    """
> +    # Use a flag to avoid infinite recursion.
> +    if d.getVarFlag(var, 'add_implied_items_internal') == '1':
> +        return ''
> +
> +    def implied_items(item, implied_mapping, d, seen=None):
> +        """Return the implied items for a given item."""
> +        if seen is None:
> +            seen = set()
> +        if item in seen:
> +            return ''
> +        seen.add(item)
> +        implied = implied_mapping.get(item, '').split()
> +        return ' '.join(implied + [implied_items(f, implied_mapping,
> d, seen) for f in implied])
> +
> +    d.setVarFlag(var, 'add_implied_items_internal', '1')
> +    try:
> +        value = d.getVar(var)
> +        implied_mapping = d.getVarFlags(implied_var)
> +        if implied_mapping is None:
> +            return ''
> +
> +        return ' '.join(implied_items(f, implied_mapping, d) for f
> in value.split())
> +    finally:
> +        d.delVarFlag(var, 'add_implied_items_internal')
> -- 
> 2.47.1
> 

Hello, Chris.

Could this be resend ones more as one series (v4) of patches?

V3 for some reasons was sent as two different series:
 - https://patchwork.isar-build.org/project/isar/list/?series=1476 -
this includes only 1st patch
 - https://patchwork.isar-build.org/project/isar/list/?series=1440 -
this includes two other patches.

The problem is that both series, if taken as "series" from patchwork
(i.e. https://patchwork.isar-build.org/series/1476/mbox/ and
https://patchwork.isar-build.org/series/1440/mbox/ , conflict with each
other.

I'm currently testing in CI all three latest (v3) patches picked
separately, but I'm not sure that they are exactly what should be meant
for merge.
chris.larson Feb. 13, 2025, 9:45 p.m. UTC | #2
On 2/12/2025 12:49 AM, Uladzimir Bely wrote:
> On Fri, 2025-01-24 at 14:20 -0700, chris.larson via isar-users wrote:
>> From: Christopher Larson <chris.larson@siemens.com>
>>
>> This class provides functions to improve the functionality of bitbake
>> list variables.
[snip]
>>
> 
> Hello, Chris.
> 
> Could this be resend ones more as one series (v4) of patches?
Re-sent as v4 in a new thread, thanks!

Patch

diff --git a/meta/classes/lists.bbclass b/meta/classes/lists.bbclass
new file mode 100644
index 00000000..db8f5837
--- /dev/null
+++ b/meta/classes/lists.bbclass
@@ -0,0 +1,105 @@ 
+# Functions to improve the functionality of bitbake list variables.
+#
+# - Add the ability to remove items from a list variable without using :remove.
+# - Add the ability for a list item to imply the addition of other list items.
+#
+
+# Usage requires either adding the variable name to LIST_VARIABLES, or manually
+# adding a :remove and a :prepend to each fully supported list variable.
+#
+# To remove items from a configured list, simply append the item to be removed
+# to the variable with a '-' or '~' prefix. For example, to remove 'alpha' from
+# ROOTFS_FEATURES, add '-alpha' to ROOTFS_FEATURES.
+#
+# To support implied list items, create a mapping of items to be appended to
+# the variable when a specific item is present. For example, to append 'beta'
+# to ROOTFS_FEATURES when 'alpha' is present, configure ROOTFS_FEATURES as such,
+# then set IMPLIED_ROOTFS_FEATURES[alpha] = "beta".
+#
+# Boilerplate example:
+#
+#   # Either this:
+#   LIST_VARIABLES += "ROOTFS_FEATURES"
+#
+#   # Or this:
+#   ROOTFS_FEATURES:remove = "${@remove_prefixed_items('ROOTFS_FEATURES', d)}"
+#   ROOTFS_FEATURES:prepend = "${@add_implied_items('ROOTFS_FEATURES', 'IMPLIED_ROOTFS_FEATURES', d)} "
+#
+# Usage example:
+#
+#   # ROOTFS_FEATURES will be "beta alpha" if the following configuration is used:
+#   IMPLIED_ROOTFS_FEATURES[alpha] = "beta"
+#   ROOTFS_FEATURES += "alpha"
+#
+#   # ROOTFS_FEATURES will be "first" if the following configuration is used:
+#   ROOTFS_FEATURES = "first second"
+#   ROOTFS_FEATURES += "-second"
+
+python enable_list_variables() {
+    """Enable list variable functionality."""
+    for variable in d.getVar("LIST_VARIABLES").split():
+        d.setVar(variable + ':remove', ' ${@remove_prefixed_items("%s", d)}' % variable)
+        d.setVar(variable + ':prepend', '${@add_implied_items("%s", "IMPLIED_%s", d)} ' % (variable, variable))
+}
+enable_list_variables[eventmask] = "bb.event.ConfigParsed"
+addhandler enable_list_variables
+
+def remove_prefixed_items(var, d):
+    """Return the items to be removed from var with :remove.
+
+    This function is intended to be used in a :remove handler to remove
+    items from a variable. It will interpret items prefixed with a '-'
+    or '~' as items to be removed.
+    """
+    # Use a flag to avoid infinite recursion.
+    if d.getVarFlag(var, 'remove_prefixed_items_internal') == '1':
+        return ''
+
+    from collections import Counter
+
+    d.setVarFlag(var, 'remove_prefixed_items_internal', '1')
+    try:
+        value = d.getVar(var)
+        counter = Counter()
+        for v in value.split():
+            if v.startswith('-') or v.startswith('~'):
+                counter[v[1:]] -= 1
+                counter[v] -= 1
+            else:
+                counter[v] += 1
+        return ' '.join(v for v, c in counter.items() if c < 1)
+    finally:
+        d.delVarFlag(var, 'remove_prefixed_items_internal')
+
+
+def add_implied_items(var, implied_var, d):
+    """Return the items to be appended due to the presence of other items in var.
+
+    This function is intended to be used in a :append handler to append
+    items from a variable. It will rely on the supplied mapping of implied items
+    to append the corresponding items.
+    """
+    # Use a flag to avoid infinite recursion.
+    if d.getVarFlag(var, 'add_implied_items_internal') == '1':
+        return ''
+
+    def implied_items(item, implied_mapping, d, seen=None):
+        """Return the implied items for a given item."""
+        if seen is None:
+            seen = set()
+        if item in seen:
+            return ''
+        seen.add(item)
+        implied = implied_mapping.get(item, '').split()
+        return ' '.join(implied + [implied_items(f, implied_mapping, d, seen) for f in implied])
+
+    d.setVarFlag(var, 'add_implied_items_internal', '1')
+    try:
+        value = d.getVar(var)
+        implied_mapping = d.getVarFlags(implied_var)
+        if implied_mapping is None:
+            return ''
+
+        return ' '.join(implied_items(f, implied_mapping, d) for f in value.split())
+    finally:
+        d.delVarFlag(var, 'add_implied_items_internal')