summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Kanavin <alex@linutronix.de>2024-12-11 14:46:30 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2024-12-13 11:11:18 +0000
commit22f046d67c02823f2df2b26cb3c463f567eae3fb (patch)
tree2bc7fc6e26cea6a251628fa8c0c19bccb92f551e
parentaadff6930b293da8d5ce312c62b9d63335cde424 (diff)
downloadpoky-22f046d67c02823f2df2b26cb3c463f567eae3fb.tar.gz
bitbake-config-build: add a plugin for config fragments
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. The tool requires that each fragment contains a one-line summary, and one or more lines of description, as BB_CONF_FRAGMENT_SUMMARY style metadata. There are three (and a half) operations (list/enable/disable/disable all), 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: Enabled fragments: selftest/test-fragment This is a configuration fragment intended for testing in oe-selftest context Unused fragments: selftest/more-fragments-here/test-another-fragment This is a second configuration fragment intended for testing in oe-selftest context (From OE-Core rev: fdb611e13bd7aa00360d3a68e4818ef5f05c8944) Signed-off-by: Alexander Kanavin <alex@linutronix.de> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--meta-selftest/conf/fragments/more-fragments-here/test-another-fragment.conf3
-rw-r--r--meta-selftest/conf/fragments/test-fragment.conf3
-rw-r--r--meta/lib/bbconfigbuild/configfragments.py159
-rw-r--r--meta/lib/oeqa/selftest/cases/bblayers.py31
4 files changed, 196 insertions, 0 deletions
diff --git a/meta-selftest/conf/fragments/more-fragments-here/test-another-fragment.conf b/meta-selftest/conf/fragments/more-fragments-here/test-another-fragment.conf
new file mode 100644
index 0000000000..3bf0459047
--- /dev/null
+++ b/meta-selftest/conf/fragments/more-fragments-here/test-another-fragment.conf
@@ -0,0 +1,3 @@
1BB_CONF_FRAGMENT_SUMMARY = "This is a second configuration fragment intended for testing in oe-selftest context"
2BB_CONF_FRAGMENT_DESCRIPTION = "It defines another variable that can be checked inside the test."
3SELFTEST_FRAGMENT_ANOTHER_VARIABLE = "someothervalue"
diff --git a/meta-selftest/conf/fragments/test-fragment.conf b/meta-selftest/conf/fragments/test-fragment.conf
new file mode 100644
index 0000000000..4c1d240945
--- /dev/null
+++ b/meta-selftest/conf/fragments/test-fragment.conf
@@ -0,0 +1,3 @@
1BB_CONF_FRAGMENT_SUMMARY = "This is a configuration fragment intended for testing in oe-selftest context"
2BB_CONF_FRAGMENT_DESCRIPTION = "It defines a variable that can be checked inside the test."
3SELFTEST_FRAGMENT_VARIABLE = "somevalue"
diff --git a/meta/lib/bbconfigbuild/configfragments.py b/meta/lib/bbconfigbuild/configfragments.py
new file mode 100644
index 0000000000..30cc5ece07
--- /dev/null
+++ b/meta/lib/bbconfigbuild/configfragments.py
@@ -0,0 +1,159 @@
1#
2# Copyright OpenEmbedded Contributors
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6
7import logging
8import os
9import sys
10import os.path
11
12import bb.utils
13
14from bblayers.common import LayerPlugin
15
16logger = logging.getLogger('bitbake-config-layers')
17
18sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
19
20def plugin_init(plugins):
21 return ConfigFragmentsPlugin()
22
23class ConfigFragmentsPlugin(LayerPlugin):
24 def get_fragment_info(self, path, name):
25 d = bb.data.init()
26 bb.parse.handle(path, d, True)
27 summary = d.getVar('BB_CONF_FRAGMENT_SUMMARY')
28 description = d.getVar('BB_CONF_FRAGMENT_DESCRIPTION')
29 if not summary:
30 raise Exception('Please add a one-line summary as BB_CONF_FRAGMENT_SUMMARY = \"...\" variable at the beginning of {}'.format(path))
31
32 if not description:
33 raise Exception('Please add a description as BB_CONF_FRAGMENT_DESCRIPTION = \"...\" variable at the beginning of {}'.format(path))
34
35 return summary, description
36
37 def discover_fragments(self):
38 fragments_path_prefix = self.tinfoil.config_data.getVar('OE_FRAGMENTS_PREFIX')
39 allfragments = {}
40 for layername in self.bbfile_collections:
41 layerdir = self.bbfile_collections[layername]
42 fragments = []
43 for topdir, dirs, files in os.walk(os.path.join(layerdir, fragments_path_prefix)):
44 fragmentdir = os.path.relpath(topdir, os.path.join(layerdir, fragments_path_prefix))
45 for fragmentfile in sorted(files):
46 fragmentname = os.path.normpath("/".join((layername, fragmentdir, fragmentfile.split('.')[0])))
47 fragmentpath = os.path.join(topdir, fragmentfile)
48 fragmentsummary, fragmentdesc = self.get_fragment_info(fragmentpath, fragmentname)
49 fragments.append({'path':fragmentpath, 'name':fragmentname, 'summary':fragmentsummary, 'description':fragmentdesc})
50 if fragments:
51 allfragments[layername] = {'layerdir':layerdir,'fragments':fragments}
52 return allfragments
53
54 def do_list_fragments(self, args):
55 """ List available configuration fragments """
56 def print_fragment(f, verbose, is_enabled):
57 if not verbose:
58 print('{}\t{}'.format(f['name'], f['summary']))
59 else:
60 print('Name: {}\nPath: {}\nEnabled: {}\nSummary: {}\nDescription:\n{}\n'.format(f['name'], f['path'], 'yes' if is_enabled else 'no', f['summary'],''.join(f['description'])))
61
62 all_enabled_fragments = (self.tinfoil.config_data.getVar('OE_FRAGMENTS') or "").split()
63
64 for layername, layerdata in self.discover_fragments().items():
65 layerdir = layerdata['layerdir']
66 fragments = layerdata['fragments']
67 enabled_fragments = [f for f in fragments if f['name'] in all_enabled_fragments]
68 disabled_fragments = [f for f in fragments if f['name'] not in all_enabled_fragments]
69
70 print('Available fragments in {} layer located in {}:\n'.format(layername, layerdir))
71 if enabled_fragments:
72 print('Enabled fragments:')
73 for f in enabled_fragments:
74 print_fragment(f, args.verbose, is_enabled=True)
75 print('')
76 if disabled_fragments:
77 print('Unused fragments:')
78 for f in disabled_fragments:
79 print_fragment(f, args.verbose, is_enabled=False)
80 print('')
81
82 def fragment_exists(self, fragmentname):
83 for layername, layerdata in self.discover_fragments().items():
84 for f in layerdata['fragments']:
85 if f['name'] == fragmentname:
86 return True
87 return False
88
89 def create_conf(self, confpath):
90 if not os.path.exists(confpath):
91 with open(confpath, 'w') as f:
92 f.write('')
93 with open(confpath, 'r') as f:
94 lines = f.read()
95 if "OE_FRAGMENTS += " not in lines:
96 lines += "\nOE_FRAGMENTS += \"\"\n"
97 with open(confpath, 'w') as f:
98 f.write(lines)
99
100 def do_enable_fragment(self, args):
101 """ Enable a fragment in the local build configuration """
102 def enable_helper(varname, origvalue, op, newlines):
103 enabled_fragments = origvalue.split()
104 if args.fragmentname in enabled_fragments:
105 print("Fragment {} already included in {}".format(args.fragmentname, args.confpath))
106 else:
107 enabled_fragments.append(args.fragmentname)
108 return " ".join(enabled_fragments), None, 0, True
109
110 if not self.fragment_exists(args.fragmentname):
111 raise Exception("Fragment {} does not exist; use 'list-fragments' to see the full list.".format(args.fragmentname))
112
113 self.create_conf(args.confpath)
114 modified = bb.utils.edit_metadata_file(args.confpath, ["OE_FRAGMENTS"], enable_helper)
115 if modified:
116 print("Fragment {} added to {}.".format(args.fragmentname, args.confpath))
117
118 def do_disable_fragment(self, args):
119 """ Disable a fragment in the local build configuration """
120 def disable_helper(varname, origvalue, op, newlines):
121 enabled_fragments = origvalue.split()
122 if args.fragmentname in enabled_fragments:
123 enabled_fragments.remove(args.fragmentname)
124 else:
125 print("Fragment {} not currently enabled in {}".format(args.fragmentname, args.confpath))
126 return " ".join(enabled_fragments), None, 0, True
127
128 self.create_conf(args.confpath)
129 modified = bb.utils.edit_metadata_file(args.confpath, ["OE_FRAGMENTS"], disable_helper)
130 if modified:
131 print("Fragment {} removed from {}.".format(args.fragmentname, args.confpath))
132
133 def do_disable_all_fragments(self, args):
134 """ Disable all fragments in the local build configuration """
135 def disable_all_helper(varname, origvalue, op, newlines):
136 return "", None, 0, True
137
138 self.create_conf(args.confpath)
139 modified = bb.utils.edit_metadata_file(args.confpath, ["OE_FRAGMENTS"], disable_all_helper)
140 if modified:
141 print("All fragments removed from {}.".format(args.confpath))
142
143 def register_commands(self, sp):
144 default_confpath = os.path.join(os.environ["BBPATH"], "conf/auto.conf")
145
146 parser_list_fragments = self.add_command(sp, 'list-fragments', self.do_list_fragments, parserecipes=False)
147 parser_list_fragments.add_argument("--confpath", default=default_confpath, help='Configuration file which contains a list of enabled fragments (default is {}).'.format(default_confpath))
148 parser_list_fragments.add_argument('--verbose', '-v', action='store_true', help='Print extended descriptions of the fragments')
149
150 parser_enable_fragment = self.add_command(sp, 'enable-fragment', self.do_enable_fragment, parserecipes=False)
151 parser_enable_fragment.add_argument("--confpath", default=default_confpath, help='Configuration file which contains a list of enabled fragments (default is {}).'.format(default_confpath))
152 parser_enable_fragment.add_argument('fragmentname', help='The name of the fragment (use list-fragments to see them)')
153
154 parser_disable_fragment = self.add_command(sp, 'disable-fragment', self.do_disable_fragment, parserecipes=False)
155 parser_disable_fragment.add_argument("--confpath", default=default_confpath, help='Configuration file which contains a list of enabled fragments (default is {}).'.format(default_confpath))
156 parser_disable_fragment.add_argument('fragmentname', help='The name of the fragment')
157
158 parser_disable_all = self.add_command(sp, 'disable-all-fragments', self.do_disable_all_fragments, parserecipes=False)
159 parser_disable_all.add_argument("--confpath", default=default_confpath, help='Configuration file which contains a list of enabled fragments (default is {}).'.format(default_confpath))
diff --git a/meta/lib/oeqa/selftest/cases/bblayers.py b/meta/lib/oeqa/selftest/cases/bblayers.py
index 695d17377d..68b0377720 100644
--- a/meta/lib/oeqa/selftest/cases/bblayers.py
+++ b/meta/lib/oeqa/selftest/cases/bblayers.py
@@ -240,3 +240,34 @@ class BitbakeLayers(OESelftestTestCase):
240 self.assertEqual(first_desc_2, '', "Describe not cleared: '{}'".format(first_desc_2)) 240 self.assertEqual(first_desc_2, '', "Describe not cleared: '{}'".format(first_desc_2))
241 self.assertEqual(second_rev_2, second_rev_1, "Revision should not be updated: '{}'".format(second_rev_2)) 241 self.assertEqual(second_rev_2, second_rev_1, "Revision should not be updated: '{}'".format(second_rev_2))
242 self.assertEqual(second_desc_2, second_desc_1, "Describe should not be updated: '{}'".format(second_desc_2)) 242 self.assertEqual(second_desc_2, second_desc_1, "Describe should not be updated: '{}'".format(second_desc_2))
243
244class BitbakeConfigBuild(OESelftestTestCase):
245 def test_enable_disable_fragments(self):
246 self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_VARIABLE'), None)
247 self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_ANOTHER_VARIABLE'), None)
248
249 runCmd('bitbake-config-build enable-fragment selftest/test-fragment')
250 self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_VARIABLE'), 'somevalue')
251 self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_ANOTHER_VARIABLE'), None)
252
253 runCmd('bitbake-config-build enable-fragment selftest/more-fragments-here/test-another-fragment')
254 self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_VARIABLE'), 'somevalue')
255 self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_ANOTHER_VARIABLE'), 'someothervalue')
256
257 fragment_metadata_command = "bitbake-getvar -f {} --value {}"
258 result = runCmd(fragment_metadata_command.format("selftest/test-fragment", "BB_CONF_FRAGMENT_SUMMARY"))
259 self.assertIn("This is a configuration fragment intended for testing in oe-selftest context", result.output)
260 result = runCmd(fragment_metadata_command.format("selftest/test-fragment", "BB_CONF_FRAGMENT_DESCRIPTION"))
261 self.assertIn("It defines a variable that can be checked inside the test.", result.output)
262 result = runCmd(fragment_metadata_command.format("selftest/more-fragments-here/test-another-fragment", "BB_CONF_FRAGMENT_SUMMARY"))
263 self.assertIn("This is a second configuration fragment intended for testing in oe-selftest context", result.output)
264 result = runCmd(fragment_metadata_command.format("selftest/more-fragments-here/test-another-fragment", "BB_CONF_FRAGMENT_DESCRIPTION"))
265 self.assertIn("It defines another variable that can be checked inside the test.", result.output)
266
267 runCmd('bitbake-config-build disable-fragment selftest/test-fragment')
268 self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_VARIABLE'), None)
269 self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_ANOTHER_VARIABLE'), 'someothervalue')
270
271 runCmd('bitbake-config-build disable-fragment selftest/more-fragments-here/test-another-fragment')
272 self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_VARIABLE'), None)
273 self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_ANOTHER_VARIABLE'), None)