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)) 160