diff options
author | Alexander Kanavin <alex@linutronix.de> | 2024-12-11 14:46:30 +0100 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2024-12-13 11:11:18 +0000 |
commit | 22f046d67c02823f2df2b26cb3c463f567eae3fb (patch) | |
tree | 2bc7fc6e26cea6a251628fa8c0c19bccb92f551e | |
parent | aadff6930b293da8d5ce312c62b9d63335cde424 (diff) | |
download | poky-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>
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 @@ | |||
1 | BB_CONF_FRAGMENT_SUMMARY = "This is a second configuration fragment intended for testing in oe-selftest context" | ||
2 | BB_CONF_FRAGMENT_DESCRIPTION = "It defines another variable that can be checked inside the test." | ||
3 | SELFTEST_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 @@ | |||
1 | BB_CONF_FRAGMENT_SUMMARY = "This is a configuration fragment intended for testing in oe-selftest context" | ||
2 | BB_CONF_FRAGMENT_DESCRIPTION = "It defines a variable that can be checked inside the test." | ||
3 | SELFTEST_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 | |||
7 | import logging | ||
8 | import os | ||
9 | import sys | ||
10 | import os.path | ||
11 | |||
12 | import bb.utils | ||
13 | |||
14 | from bblayers.common import LayerPlugin | ||
15 | |||
16 | logger = logging.getLogger('bitbake-config-layers') | ||
17 | |||
18 | sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) | ||
19 | |||
20 | def plugin_init(plugins): | ||
21 | return ConfigFragmentsPlugin() | ||
22 | |||
23 | class 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 | |||
244 | class 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) | ||