diff mbox series

bitbake-config-build: add a plugin for config fragments

Message ID 20240509160843.3283186-1-alex.kanavin@gmail.com
State New
Headers show
Series bitbake-config-build: add a plugin for config fragments | expand

Commit Message

Alexander Kanavin May 9, 2024, 4:08 p.m. UTC
From: Alexander Kanavin <alex@linutronix.de>

This allows fine-tuning local configurations with pre-frabricated
configuration snippets in a structured, controlled way. It's also
an important building block for bitbake-setup.

There are three operations (list/add/remove), and here's the list output:

alex@Zen2:/srv/storage/alex/yocto/build-64$ bitbake-config-build list-fragments
NOTE: Starting bitbake server...
Available fragments in selftest layer located in /srv/work/alex/poky/meta-selftest:

selftest/test-another-fragment	This is a second configuration fragment intended for testing in oe-selftest context
selftest/test-fragment	This is a configuration fragment intended for testing in oe-selftest context

The tool requires that each fragment contains a one-line summary
at the top, followed by multiple lines of description, as hash-prefixed
comments.

Signed-off-by: Alexander Kanavin <alex@linutronix.de>
---
 .../selftest/test-another-fragment.inc        |   4 +
 .../conf/fragments/selftest/test-fragment.inc |   4 +
 meta/lib/bbconfigbuild/configfragments.py     | 117 ++++++++++++++++++
 meta/lib/oeqa/selftest/cases/bblayers.py      |  21 ++++
 4 files changed, 146 insertions(+)
 create mode 100644 meta-selftest/conf/fragments/selftest/test-another-fragment.inc
 create mode 100644 meta-selftest/conf/fragments/selftest/test-fragment.inc
 create mode 100644 meta/lib/bbconfigbuild/configfragments.py

Comments

Richard Purdie June 10, 2024, 10:08 a.m. UTC | #1
On Thu, 2024-05-09 at 18:08 +0200, Alexander Kanavin via lists.openembedded.org wrote:
> From: Alexander Kanavin <alex@linutronix.de>
> 
> This allows fine-tuning local configurations with pre-frabricated
> configuration snippets in a structured, controlled way. It's also
> an important building block for bitbake-setup.
> 
> There are three operations (list/add/remove), and here's the list output:
> 
> alex@Zen2:/srv/storage/alex/yocto/build-64$ bitbake-config-build list-fragments
> NOTE: Starting bitbake server...
> Available fragments in selftest layer located in /srv/work/alex/poky/meta-selftest:
> 
> selftest/test-another-fragment	This is a second configuration fragment intended for testing in oe-selftest context
> selftest/test-fragment	This is a configuration fragment intended for testing in oe-selftest context
> 
> The tool requires that each fragment contains a one-line summary
> at the top, followed by multiple lines of description, as hash-prefixed
> comments.
> 
> Signed-off-by: Alexander Kanavin <alex@linutronix.de>

Firstly, thanks for putting this together, I'm very much in favour of
adding something like this. The main questions I have are around the
interface and naming. My feedback/open questions:

a) I'm not totally sold on "bitbake-config-build" as the command name.
I will give that a bit more thought.

b) The code adding/removing require lines from local.conf seems a
little fragile. I did wonder if we wanted a higher level bitbake API to
this, for example a BB_CONF_FRAGMENTS variable which basically tagges a
set of config files onto the end of the base configuration parsing?
This wouldn't be hard to implement in bitbake and would then make the
tooling easier and less fragile.

c) Do we want to have the config fragment name as a
"<layername>/<fragementname>" pair and have the fragments live directly
in conf/fragments in a layer?

d) How do you control ordering between the fragments? Do we need
dependencies? How does layer ordering or dependencies affect anything?
Can fragments conflict?

e) You've used comments at the start of the files as
summary/description. Should we use some kind of more variable like
syntax which allows for other information in the future?

Cheers,

Richard
Alexander Kanavin June 10, 2024, 10:58 a.m. UTC | #2
On Mon, 10 Jun 2024 at 12:08, Richard Purdie
<richard.purdie@linuxfoundation.org> wrote:

> b) The code adding/removing require lines from local.conf seems a
> little fragile. I did wonder if we wanted a higher level bitbake API to
> this, for example a BB_CONF_FRAGMENTS variable which basically tagges a
> set of config files onto the end of the base configuration parsing?
> This wouldn't be hard to implement in bitbake and would then make the
> tooling easier and less fragile.

I don't quite follow. If BB_CONF_FRAGMENTS value is a space separated
list of fragments, then how would the tooling

- add to it?
- remove from it?

It would still need to look for an assignment to that variable in a
'standard location' (e.g. conf/local.conf), and then rewrite the file
with the new assignment. That doesn't seem any easier to me than just
using 'require path/to/fragment'.

On the other hand, there could be qa checks for presence of a certain
fragment(s) in that variable. E.g. a layer could stop a build if it
detects that something is not there.

Another option is to copy/symlink the fragments into
build/conf/fragments/ and have bitbake pick them up with a glob.

> c) Do we want to have the config fragment name as a
> "<layername>/<fragementname>" pair and have the fragments live directly
> in conf/fragments in a layer?

The idea is to allow deep slash-separated hierarchies here to be used
as categories, and map their names directly to file paths (stripping
the conf/fragments/ prefix and .inc suffix in the tooling UI). Think
of something like
meta-yocto-bsp/conf/fragments/machines/arm/beaglebone.inc that would
be presented as 'machines/arm/beaglebone' in the UI. Conversely, what
layer it's in is not relevant in the tooling UI. If someone wants to
enable systemd, they wouldn't care which layer would give them that.

This would come particularly handy in interactive UIs (e.g. something
curses-driven and mimicking buildroot).

> d) How do you control ordering between the fragments? Do we need
> dependencies? How does layer ordering or dependencies affect anything?
> Can fragments conflict?

I deliberately kept all of this out of scope to avoid going crazy with
thought experiments and resulting over-engineering. I feel we need to
start using the fragments in practice, particularly eat our own dog
food on the autobuilder with them, and then problems and answers to
them will arise from that practice.

> e) You've used comments at the start of the files as
> summary/description. Should we use some kind of more variable like
> syntax which allows for other information in the future?

I'm open to proposals, but this was by far the easiest and most
straightforward and obvious to anyone reading the fragment. Python has
no support for 'metadata in comments' that we can reuse. If there's
need for other metadata about fragments we can invent special syntax
for it, but summary/description pair can stay as #-comments.

Alex
Richard Purdie June 10, 2024, 2:51 p.m. UTC | #3
On Mon, 2024-06-10 at 12:58 +0200, Alexander Kanavin wrote:
> On Mon, 10 Jun 2024 at 12:08, Richard Purdie
> <richard.purdie@linuxfoundation.org> wrote:
> 
> > b) The code adding/removing require lines from local.conf seems a
> > little fragile. I did wonder if we wanted a higher level bitbake
> > API to
> > this, for example a BB_CONF_FRAGMENTS variable which basically
> > tagges a
> > set of config files onto the end of the base configuration parsing?
> > This wouldn't be hard to implement in bitbake and would then make
> > the
> > tooling easier and less fragile.
> 
> I don't quite follow. If BB_CONF_FRAGMENTS value is a space separated
> list of fragments, then how would the tooling
> 
> - add to it?
> - remove from it?

bb.utils.edit_metadata()/bb.utils.edit_metadata_file() ?

> It would still need to look for an assignment to that variable in a
> 'standard location' (e.g. conf/local.conf), and then rewrite the file
> with the new assignment. That doesn't seem any easier to me than just
> using 'require path/to/fragment'.

I can see this both ways but we do have the helper functions above that
can do this (as used in recipetool/devtool).

> On the other hand, there could be qa checks for presence of a certain
> fragment(s) in that variable. E.g. a layer could stop a build if it
> detects that something is not there.

I'm wondering about whether having a variable makes it easier to users
to understand/edit.

I meant to also mention that I'm a bit concerned about using local.conf
specifically in the tool, I'd prefer not to require a specific conf
file.

> Another option is to copy/symlink the fragments into
> build/conf/fragments/ and have bitbake pick them up with a glob.

I don't like the copy idea, the symlink approach is interesting. The
main downside there is capturing the give config and ordering.

> > c) Do we want to have the config fragment name as a
> > "<layername>/<fragementname>" pair and have the fragments live
> > directly
> > in conf/fragments in a layer?
> 
> The idea is to allow deep slash-separated hierarchies here to be used
> as categories, and map their names directly to file paths (stripping
> the conf/fragments/ prefix and .inc suffix in the tooling UI). Think
> of something like
> meta-yocto-bsp/conf/fragments/machines/arm/beaglebone.inc that would
> be presented as 'machines/arm/beaglebone' in the UI. Conversely, what
> layer it's in is not relevant in the tooling UI. If someone wants to
> enable systemd, they wouldn't care which layer would give them that.

That seems reasonable but I wanted to avoid every layer needing to nest
by a directory level to make it clear where the fragments come from. I
think some kind of naming standard might ultimately make things clearer
to the user?

Requiring the first entry be a layer name doesn't preclude the rest...


> This would come particularly handy in interactive UIs (e.g. something
> curses-driven and mimicking buildroot).
> 
> > d) How do you control ordering between the fragments? Do we need
> > dependencies? How does layer ordering or dependencies affect
> > anything?
> > Can fragments conflict?
> 
> I deliberately kept all of this out of scope to avoid going crazy
> with thought experiments and resulting over-engineering. I feel we
> need to start using the fragments in practice, particularly eat our
> own dog food on the autobuilder with them, and then problems and
> answers to them will arise from that practice.

I ask the questions since we have been trapped by choices in the past
and it since these are likely things which will arise, we might want to
at least consider them now.

> > e) You've used comments at the start of the files as
> > summary/description. Should we use some kind of more variable like
> > syntax which allows for other information in the future?
> 
> I'm open to proposals, but this was by far the easiest and most
> straightforward and obvious to anyone reading the fragment. Python
> has no support for 'metadata in comments' that we can reuse. If
> there's need for other metadata about fragments we can invent special
> syntax for it, but summary/description pair can stay as #-comments.

We do have our parser which can easily handle:

CONFFRAG[summary] = "some summary"
CONFFRAG[description] = "Longer description with \
    multiple \
    lines \
"

or

CONFFRAGS += "myfragment_name"

myfragment_name[summary] = "some summary"
myfragment_name[description] = "Longer description with \
    multiple \
    lines \
"

both of which have some pros and cons. I'm not sold on either of the
above but I wanted to give some examples.

For examples on handling such files see lib/bb/tests/parse.py,
basically call bb.parse.handle(f.name, self.d)[''].

Cheers,

Richard
Alexander Kanavin June 11, 2024, 10:45 a.m. UTC | #4
On Mon, 10 Jun 2024 at 16:51, Richard Purdie
<richard.purdie@linuxfoundation.org> wrote:
> > I don't quite follow. If BB_CONF_FRAGMENTS value is a space separated
> > list of fragments, then how would the tooling
> >
> > - add to it?
> > - remove from it?
>
> bb.utils.edit_metadata()/bb.utils.edit_metadata_file() ?

Right, I had no idea this exists :)

> I'm wondering about whether having a variable makes it easier to users
> to understand/edit.

I think there's benefit in having a variable that makes it separate
and distinct from regular 'require' statements, but I can't form a
clear preference for either option. Should this be taken to some kind
of decision forum, e.g. oe-architecture or TSC?

> I meant to also mention that I'm a bit concerned about using local.conf
> specifically in the tool, I'd prefer not to require a specific conf
> file.

I agree that local.conf should be left alone. It's something coming
from templates and I would rather never touch it with tools after the
fact.

But either the 'require' statements or the variable containing the
fragment list has to be written into something that bitbake can see.
Should they go to build/conf/fragments.conf ?

> > Another option is to copy/symlink the fragments into
> > build/conf/fragments/ and have bitbake pick them up with a glob.
>
> I don't like the copy idea, the symlink approach is interesting. The
> main downside there is capturing the give config and ordering.

'give config'?

> That seems reasonable but I wanted to avoid every layer needing to nest
> by a directory level to make it clear where the fragments come from. I
> think some kind of naming standard might ultimately make things clearer
> to the user?
>
> Requiring the first entry be a layer name doesn't preclude the rest...

Okay, I'll rework to include the layer name upfront.

> I ask the questions since we have been trapped by choices in the past
> and it since these are likely things which will arise, we might want to
> at least consider them now.

Hopefully the current code makes no choices for these things. It just
adds/removes requested fragments, nothing else. But it can be extended
to read fragment metadata, and armed with that (and the list of
fragments already enabled) make decisions about what the new list
should be, or whether the request should be declined.

> We do have our parser which can easily handle:
>
> CONFFRAG[summary] = "some summary"
> CONFFRAG[description] = "Longer description with \
>     multiple \
>     lines \
> "

I must be missing something here. If this is defined in two different
fragments and both are included, how would one not step on the other
with these assignments?

> or
>
> CONFFRAGS += "myfragment_name"
>
> myfragment_name[summary] = "some summary"
> myfragment_name[description] = "Longer description with \
>     multiple \
>     lines \
> "

Okay, this one in my opinion is a big readability (and write-ability)
regression compared to #-comments. For extended metadata (such as
dependencies/conflicts) something like this would be needed, but I'd
rather not use it for summary/description.

Alex
Richard Purdie June 11, 2024, 10:56 a.m. UTC | #5
On Tue, 2024-06-11 at 12:45 +0200, Alexander Kanavin wrote:
> On Mon, 10 Jun 2024 at 16:51, Richard Purdie
> <richard.purdie@linuxfoundation.org> wrote:
> > > I don't quite follow. If BB_CONF_FRAGMENTS value is a space separated
> > > list of fragments, then how would the tooling
> > > 
> > > - add to it?
> > > - remove from it?
> > 
> > bb.utils.edit_metadata()/bb.utils.edit_metadata_file() ?
> 
> Right, I had no idea this exists :)
> 
> > I'm wondering about whether having a variable makes it easier to users
> > to understand/edit.
> 
> I think there's benefit in having a variable that makes it separate
> and distinct from regular 'require' statements, but I can't form a
> clear preference for either option. Should this be taken to some kind
> of decision forum, e.g. oe-architecture or TSC?

oe-arch is the right place to discuss with the OE TSC being the
ultimate decision maker if needed.

> > I meant to also mention that I'm a bit concerned about using local.conf
> > specifically in the tool, I'd prefer not to require a specific conf
> > file.
> 
> I agree that local.conf should be left alone. It's something coming
> from templates and I would rather never touch it with tools after the
> fact.
> 
> But either the 'require' statements or the variable containing the
> fragment list has to be written into something that bitbake can see.
> Should they go to build/conf/fragments.conf ?

I'd wondered about a dedicated file, yes. Perhaps with locked sigs also
becoming a dedicated conf file for similar reasons.

> > > Another option is to copy/symlink the fragments into
> > > build/conf/fragments/ and have bitbake pick them up with a glob.
> > 
> > I don't like the copy idea, the symlink approach is interesting. The
> > main downside there is capturing the give config and ordering.
> 
> 'give config'?

Sorry, it was meant to read "given config".

> 
> > That seems reasonable but I wanted to avoid every layer needing to nest
> > by a directory level to make it clear where the fragments come from. I
> > think some kind of naming standard might ultimately make things clearer
> > to the user?
> > 
> > Requiring the first entry be a layer name doesn't preclude the rest...
> 
> Okay, I'll rework to include the layer name upfront.
> 
> > I ask the questions since we have been trapped by choices in the past
> > and it since these are likely things which will arise, we might want to
> > at least consider them now.
> 
> Hopefully the current code makes no choices for these things. It just
> adds/removes requested fragments, nothing else. But it can be extended
> to read fragment metadata, and armed with that (and the list of
> fragments already enabled) make decisions about what the new list
> should be, or whether the request should be declined.

I'd like to explore these issues a little bit further now before we
lock in on a format as I think their need is probably inevitable.

> 
> > We do have our parser which can easily handle:
> > 
> > CONFFRAG[summary] = "some summary"
> > CONFFRAG[description] = "Longer description with \
> >     multiple \
> >     lines \
> > "
> 
> I must be missing something here. If this is defined in two different
> fragments and both are included, how would one not step on the other
> with these assignments?

They would overwrite each other but you can still parse the data from
each file in turn. layer.conf files did give bitbake a similar
challenge. It gives more readability at the cost of not being able to
store all the data in the same data store.

> > or
> > 
> > CONFFRAGS += "myfragment_name"
> > 
> > myfragment_name[summary] = "some summary"
> > myfragment_name[description] = "Longer description with \
> >     multiple \
> >     lines \
> > "
> 
> Okay, this one in my opinion is a big readability (and write-ability)
> regression compared to #-comments. For extended metadata (such as
> dependencies/conflicts) something like this would be needed, but I'd
> rather not use it for summary/description.

I'm not happy with the above either but I do think we should fine a
format which is parsable as I really dislike 'magic' comments
(similarly in machine conf files too). They don't scale and have
machine readability issues.

I'm open to other other ideas. I'd even consider some kind of new
syntax if needed and we can't make the existing syntax work.

Cheers,

Richard
Alexander Kanavin June 11, 2024, 12:20 p.m. UTC | #6
On Tue, 11 Jun 2024 at 12:56, Richard Purdie
<richard.purdie@linuxfoundation.org> wrote:
> oe-arch is the right place to discuss with the OE TSC being the
> ultimate decision maker if needed.

I've written a summary reply to oe-arch so that we can continue there.

Alex
diff mbox series

Patch

diff --git a/meta-selftest/conf/fragments/selftest/test-another-fragment.inc b/meta-selftest/conf/fragments/selftest/test-another-fragment.inc
new file mode 100644
index 00000000000..4f6c535de2d
--- /dev/null
+++ b/meta-selftest/conf/fragments/selftest/test-another-fragment.inc
@@ -0,0 +1,4 @@ 
+# This is a second configuration fragment intended for testing in oe-selftest context
+#
+# It defines another variable that can be checked inside the test.
+SELFTEST_FRAGMENT_ANOTHER_VARIABLE = "someothervalue"
diff --git a/meta-selftest/conf/fragments/selftest/test-fragment.inc b/meta-selftest/conf/fragments/selftest/test-fragment.inc
new file mode 100644
index 00000000000..0ad62db9e13
--- /dev/null
+++ b/meta-selftest/conf/fragments/selftest/test-fragment.inc
@@ -0,0 +1,4 @@ 
+# This is a configuration fragment intended for testing in oe-selftest context
+#
+# It defines a variable that can be checked inside the test.
+SELFTEST_FRAGMENT_VARIABLE = "somevalue"
diff --git a/meta/lib/bbconfigbuild/configfragments.py b/meta/lib/bbconfigbuild/configfragments.py
new file mode 100644
index 00000000000..d7e4f54dba2
--- /dev/null
+++ b/meta/lib/bbconfigbuild/configfragments.py
@@ -0,0 +1,117 @@ 
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+
+import logging
+import os
+import sys
+
+import bb.utils
+
+from bblayers.common import LayerPlugin
+
+logger = logging.getLogger('bitbake-config-layers')
+
+sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
+
+def plugin_init(plugins):
+    return ConfigFragmentsPlugin()
+
+class ConfigFragmentsPlugin(LayerPlugin):
+    def get_fragment_info(self, path):
+        summary = ""
+        description = []
+        with open(path) as f:
+            for l in f.readlines():
+                if not l.startswith('#'):
+                    break
+                if not summary:
+                    summary = l[1:].strip()
+                else:
+                    description.append(l[1:].strip())
+        if not summary or not description:
+            raise Exception('Please add a one-line summary followed by a description as #-prefixed comments at the beginning of {}'.format(path))
+
+        return summary, description
+
+
+    def discover_fragments(self):
+        allfragments = {}
+        for layername in self.bbfile_collections:
+             layerdir = self.bbfile_collections[layername]
+             fragments = []
+             for topdir, dirs, files in os.walk(os.path.join(layerdir, 'conf/fragments')):
+                 fragmentdir = topdir.split('conf/fragments/')[-1]
+                 for fragmentfile in sorted(files):
+                     fragmentname = "/".join((fragmentdir, fragmentfile.split('.')[0]))
+                     fragmentpath = os.path.join(topdir, fragmentfile)
+                     fragmentsummary, fragmentdesc = self.get_fragment_info(fragmentpath)
+                     fragments.append({'path':fragmentpath, 'name':fragmentname, 'summary':fragmentsummary, 'description':fragmentdesc})
+             if fragments:
+                 allfragments[layername] = {'layerdir':layerdir,'fragments':fragments}
+        return allfragments
+
+
+    def do_list_fragments(self, args):
+        """ List available configuration fragments """
+        for layername, layerdata in self.discover_fragments().items():
+            layerdir = layerdata['layerdir']
+            fragments = layerdata['fragments']
+
+            print('Available fragments in {} layer located in {}:\n'.format(layername, layerdir))
+            for f in fragments:
+                if not args.verbose:
+                    print('{}\t{}'.format(f['name'], f['summary']))
+                else:
+                    print('Name: {}\nPath: {}\nSummary: {}\nDescription:\n{}\n'.format(f['name'], f['path'], f['summary'],''.join(f['description'])))
+            print('')
+
+    def fragment_exists(self, fragmentname):
+        for layername, layerdata in self.discover_fragments().items():
+            for f in layerdata['fragments']:
+              if f['name'] == fragmentname:
+                  return True
+        return False
+
+    def do_add_fragment(self, args):
+        """ Add a fragment to the local build configuration """
+        if not self.fragment_exists(args.fragmentname):
+            raise Exception("Fragment {} does not exist; use 'list-fragments' to see the full list.".format(args.fragmentname))
+
+        confpath = os.path.join(os.environ["BBPATH"], "conf/local.conf")
+        appendline = "require conf/fragments/{}.inc\n".format(args.fragmentname)
+
+        with open(confpath) as f:
+            lines = f.readlines()
+            for l in lines:
+                if l == appendline:
+                    print("Fragment {} already included in {}".format(args.fragmentname, confpath))
+                    return
+
+        lines.append(appendline)
+        with open(confpath, 'w') as f:
+            f.write(''.join(lines))
+
+    def do_remove_fragment(self, args):
+        """ Remove a fragment from the local build configuration """
+        confpath = os.path.join(os.environ["BBPATH"], "conf/local.conf")
+        appendline = "require conf/fragments/{}.inc\n".format(args.fragmentname)
+
+        with open(confpath) as f:
+            lines = f.readlines()
+        lines = [l for l in lines if l != appendline]
+
+        with open(confpath, 'w') as f:
+            f.write(''.join(lines))
+
+    def register_commands(self, sp):
+        parser_list_fragments = self.add_command(sp, 'list-fragments', self.do_list_fragments, parserecipes=False)
+        parser_list_fragments.add_argument('--verbose', '-v', action='store_true', help='Print extended descriptions of the fragments')
+
+        parser_add_fragment = self.add_command(sp, 'add-fragment', self.do_add_fragment, parserecipes=False)
+        parser_add_fragment.add_argument('fragmentname', help='The name of the fragment (use list-fragments to see them)')
+
+        parser_remove_fragment = self.add_command(sp, 'remove-fragment', self.do_remove_fragment, parserecipes=False)
+        parser_remove_fragment.add_argument('fragmentname', help='The name of the fragment')
diff --git a/meta/lib/oeqa/selftest/cases/bblayers.py b/meta/lib/oeqa/selftest/cases/bblayers.py
index 8b2bc319d50..9e75ef6928e 100644
--- a/meta/lib/oeqa/selftest/cases/bblayers.py
+++ b/meta/lib/oeqa/selftest/cases/bblayers.py
@@ -253,3 +253,24 @@  class BitbakeLayers(OESelftestTestCase):
                 meta_selftest_found = True
         self.assertTrue(oe_core_found, "meta/conf/layer.conf not found in {}".format(testcopydir))
         self.assertTrue(meta_selftest_found, "meta-selftest/conf/layer.conf not found in {}".format(testcopydir))
+
+class BitbakeConfigBuild(OESelftestTestCase):
+    def test_add_remove_fragments(self):
+        self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_VARIABLE'), None)
+        self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_ANOTHER_VARIABLE'), None)
+
+        runCmd('bitbake-config-build add-fragment selftest/test-fragment')
+        self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_VARIABLE'), 'somevalue')
+        self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_ANOTHER_VARIABLE'), None)
+
+        runCmd('bitbake-config-build add-fragment selftest/test-another-fragment')
+        self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_VARIABLE'), 'somevalue')
+        self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_ANOTHER_VARIABLE'), 'someothervalue')
+
+        runCmd('bitbake-config-build remove-fragment selftest/test-fragment')
+        self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_VARIABLE'), None)
+        self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_ANOTHER_VARIABLE'), 'someothervalue')
+
+        runCmd('bitbake-config-build remove-fragment selftest/test-another-fragment')
+        self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_VARIABLE'), None)
+        self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_ANOTHER_VARIABLE'), None)