12134342eSMasahiro Yamada#!/usr/bin/env python2 283d290c5STom Rini# SPDX-License-Identifier: GPL-2.0+ 33c08e8b8SMasahiro Yamada# 43c08e8b8SMasahiro Yamada# Author: Masahiro Yamada <yamada.m@jp.panasonic.com> 53c08e8b8SMasahiro Yamada# 63c08e8b8SMasahiro Yamada 73c08e8b8SMasahiro Yamada""" 8f6c8f38eSMasahiro YamadaConverter from Kconfig and MAINTAINERS to a board database. 93c08e8b8SMasahiro Yamada 10f6c8f38eSMasahiro YamadaRun 'tools/genboardscfg.py' to create a board database. 113c08e8b8SMasahiro Yamada 123c08e8b8SMasahiro YamadaRun 'tools/genboardscfg.py -h' for available options. 132134342eSMasahiro Yamada 14f6c8f38eSMasahiro YamadaPython 2.6 or later, but not Python 3.x is necessary to run this script. 153c08e8b8SMasahiro Yamada""" 163c08e8b8SMasahiro Yamada 173c08e8b8SMasahiro Yamadaimport errno 183c08e8b8SMasahiro Yamadaimport fnmatch 193c08e8b8SMasahiro Yamadaimport glob 20f6c8f38eSMasahiro Yamadaimport multiprocessing 213c08e8b8SMasahiro Yamadaimport optparse 223c08e8b8SMasahiro Yamadaimport os 233c08e8b8SMasahiro Yamadaimport sys 243c08e8b8SMasahiro Yamadaimport tempfile 253c08e8b8SMasahiro Yamadaimport time 263c08e8b8SMasahiro Yamada 27*60801702SPeng Fansys.path.insert(1, os.path.join(os.path.dirname(__file__), 'buildman')) 28f6c8f38eSMasahiro Yamadaimport kconfiglib 293c08e8b8SMasahiro Yamada 30f6c8f38eSMasahiro Yamada### constant variables ### 31f6c8f38eSMasahiro YamadaOUTPUT_FILE = 'boards.cfg' 32f6c8f38eSMasahiro YamadaCONFIG_DIR = 'configs' 33f6c8f38eSMasahiro YamadaSLEEP_TIME = 0.03 343c08e8b8SMasahiro YamadaCOMMENT_BLOCK = '''# 353c08e8b8SMasahiro Yamada# List of boards 363c08e8b8SMasahiro Yamada# Automatically generated by %s: don't edit 373c08e8b8SMasahiro Yamada# 38ca418dd7SMasahiro Yamada# Status, Arch, CPU, SoC, Vendor, Board, Target, Options, Maintainers 393c08e8b8SMasahiro Yamada 403c08e8b8SMasahiro Yamada''' % __file__ 413c08e8b8SMasahiro Yamada 423c08e8b8SMasahiro Yamada### helper functions ### 43f6c8f38eSMasahiro Yamadadef try_remove(f): 44f6c8f38eSMasahiro Yamada """Remove a file ignoring 'No such file or directory' error.""" 453c08e8b8SMasahiro Yamada try: 46f6c8f38eSMasahiro Yamada os.remove(f) 47f6c8f38eSMasahiro Yamada except OSError as exception: 48f6c8f38eSMasahiro Yamada # Ignore 'No such file or directory' error 49f6c8f38eSMasahiro Yamada if exception.errno != errno.ENOENT: 50f6c8f38eSMasahiro Yamada raise 513c08e8b8SMasahiro Yamada 523c08e8b8SMasahiro Yamadadef check_top_directory(): 533c08e8b8SMasahiro Yamada """Exit if we are not at the top of source directory.""" 543c08e8b8SMasahiro Yamada for f in ('README', 'Licenses'): 553c08e8b8SMasahiro Yamada if not os.path.exists(f): 5631e2141dSMasahiro Yamada sys.exit('Please run at the top of source directory.') 573c08e8b8SMasahiro Yamada 58f6c8f38eSMasahiro Yamadadef output_is_new(output): 59f6c8f38eSMasahiro Yamada """Check if the output file is up to date. 60d1bf4afdSMasahiro Yamada 61d1bf4afdSMasahiro Yamada Returns: 62f6c8f38eSMasahiro Yamada True if the given output file exists and is newer than any of 63d1bf4afdSMasahiro Yamada *_defconfig, MAINTAINERS and Kconfig*. False otherwise. 64d1bf4afdSMasahiro Yamada """ 65d1bf4afdSMasahiro Yamada try: 66f6c8f38eSMasahiro Yamada ctime = os.path.getctime(output) 67d1bf4afdSMasahiro Yamada except OSError as exception: 68d1bf4afdSMasahiro Yamada if exception.errno == errno.ENOENT: 69d1bf4afdSMasahiro Yamada # return False on 'No such file or directory' error 70d1bf4afdSMasahiro Yamada return False 71d1bf4afdSMasahiro Yamada else: 72d1bf4afdSMasahiro Yamada raise 73d1bf4afdSMasahiro Yamada 74d1bf4afdSMasahiro Yamada for (dirpath, dirnames, filenames) in os.walk(CONFIG_DIR): 75d1bf4afdSMasahiro Yamada for filename in fnmatch.filter(filenames, '*_defconfig'): 76d1bf4afdSMasahiro Yamada if fnmatch.fnmatch(filename, '.*'): 77d1bf4afdSMasahiro Yamada continue 78d1bf4afdSMasahiro Yamada filepath = os.path.join(dirpath, filename) 79d1bf4afdSMasahiro Yamada if ctime < os.path.getctime(filepath): 80d1bf4afdSMasahiro Yamada return False 81d1bf4afdSMasahiro Yamada 82d1bf4afdSMasahiro Yamada for (dirpath, dirnames, filenames) in os.walk('.'): 83d1bf4afdSMasahiro Yamada for filename in filenames: 84d1bf4afdSMasahiro Yamada if (fnmatch.fnmatch(filename, '*~') or 85d1bf4afdSMasahiro Yamada not fnmatch.fnmatch(filename, 'Kconfig*') and 86d1bf4afdSMasahiro Yamada not filename == 'MAINTAINERS'): 87d1bf4afdSMasahiro Yamada continue 88d1bf4afdSMasahiro Yamada filepath = os.path.join(dirpath, filename) 89d1bf4afdSMasahiro Yamada if ctime < os.path.getctime(filepath): 90d1bf4afdSMasahiro Yamada return False 91d1bf4afdSMasahiro Yamada 92f6c8f38eSMasahiro Yamada # Detect a board that has been removed since the current board database 93d1bf4afdSMasahiro Yamada # was generated 94f6c8f38eSMasahiro Yamada with open(output) as f: 95d1bf4afdSMasahiro Yamada for line in f: 96d1bf4afdSMasahiro Yamada if line[0] == '#' or line == '\n': 97d1bf4afdSMasahiro Yamada continue 98d1bf4afdSMasahiro Yamada defconfig = line.split()[6] + '_defconfig' 99d1bf4afdSMasahiro Yamada if not os.path.exists(os.path.join(CONFIG_DIR, defconfig)): 100d1bf4afdSMasahiro Yamada return False 101d1bf4afdSMasahiro Yamada 102d1bf4afdSMasahiro Yamada return True 103d1bf4afdSMasahiro Yamada 1043c08e8b8SMasahiro Yamada### classes ### 105f6c8f38eSMasahiro Yamadaclass KconfigScanner: 106f6c8f38eSMasahiro Yamada 107f6c8f38eSMasahiro Yamada """Kconfig scanner.""" 108f6c8f38eSMasahiro Yamada 109f6c8f38eSMasahiro Yamada ### constant variable only used in this class ### 110f6c8f38eSMasahiro Yamada _SYMBOL_TABLE = { 111f6c8f38eSMasahiro Yamada 'arch' : 'SYS_ARCH', 112f6c8f38eSMasahiro Yamada 'cpu' : 'SYS_CPU', 113f6c8f38eSMasahiro Yamada 'soc' : 'SYS_SOC', 114f6c8f38eSMasahiro Yamada 'vendor' : 'SYS_VENDOR', 115f6c8f38eSMasahiro Yamada 'board' : 'SYS_BOARD', 116f6c8f38eSMasahiro Yamada 'config' : 'SYS_CONFIG_NAME', 117f6c8f38eSMasahiro Yamada 'options' : 'SYS_EXTRA_OPTIONS' 118f6c8f38eSMasahiro Yamada } 119f6c8f38eSMasahiro Yamada 120f6c8f38eSMasahiro Yamada def __init__(self): 121f6c8f38eSMasahiro Yamada """Scan all the Kconfig files and create a Config object.""" 122f6c8f38eSMasahiro Yamada # Define environment variables referenced from Kconfig 123f6c8f38eSMasahiro Yamada os.environ['srctree'] = os.getcwd() 124f6c8f38eSMasahiro Yamada os.environ['UBOOTVERSION'] = 'dummy' 125f6c8f38eSMasahiro Yamada os.environ['KCONFIG_OBJDIR'] = '' 1268639f69aSSimon Glass self._conf = kconfiglib.Config(print_warnings=False) 127f6c8f38eSMasahiro Yamada 128f6c8f38eSMasahiro Yamada def __del__(self): 129f6c8f38eSMasahiro Yamada """Delete a leftover temporary file before exit. 130f6c8f38eSMasahiro Yamada 131f6c8f38eSMasahiro Yamada The scan() method of this class creates a temporay file and deletes 132f6c8f38eSMasahiro Yamada it on success. If scan() method throws an exception on the way, 133f6c8f38eSMasahiro Yamada the temporary file might be left over. In that case, it should be 134f6c8f38eSMasahiro Yamada deleted in this destructor. 135f6c8f38eSMasahiro Yamada """ 136f6c8f38eSMasahiro Yamada if hasattr(self, '_tmpfile') and self._tmpfile: 137f6c8f38eSMasahiro Yamada try_remove(self._tmpfile) 138f6c8f38eSMasahiro Yamada 139f6c8f38eSMasahiro Yamada def scan(self, defconfig): 140f6c8f38eSMasahiro Yamada """Load a defconfig file to obtain board parameters. 141f6c8f38eSMasahiro Yamada 142f6c8f38eSMasahiro Yamada Arguments: 143f6c8f38eSMasahiro Yamada defconfig: path to the defconfig file to be processed 144f6c8f38eSMasahiro Yamada 145f6c8f38eSMasahiro Yamada Returns: 146f6c8f38eSMasahiro Yamada A dictionary of board parameters. It has a form of: 147f6c8f38eSMasahiro Yamada { 148f6c8f38eSMasahiro Yamada 'arch': <arch_name>, 149f6c8f38eSMasahiro Yamada 'cpu': <cpu_name>, 150f6c8f38eSMasahiro Yamada 'soc': <soc_name>, 151f6c8f38eSMasahiro Yamada 'vendor': <vendor_name>, 152f6c8f38eSMasahiro Yamada 'board': <board_name>, 153f6c8f38eSMasahiro Yamada 'target': <target_name>, 154f6c8f38eSMasahiro Yamada 'config': <config_header_name>, 155f6c8f38eSMasahiro Yamada 'options': <extra_options> 156f6c8f38eSMasahiro Yamada } 157f6c8f38eSMasahiro Yamada """ 158f6c8f38eSMasahiro Yamada # strip special prefixes and save it in a temporary file 159f6c8f38eSMasahiro Yamada fd, self._tmpfile = tempfile.mkstemp() 160f6c8f38eSMasahiro Yamada with os.fdopen(fd, 'w') as f: 161f6c8f38eSMasahiro Yamada for line in open(defconfig): 162f6c8f38eSMasahiro Yamada colon = line.find(':CONFIG_') 163f6c8f38eSMasahiro Yamada if colon == -1: 164f6c8f38eSMasahiro Yamada f.write(line) 165f6c8f38eSMasahiro Yamada else: 166f6c8f38eSMasahiro Yamada f.write(line[colon + 1:]) 167f6c8f38eSMasahiro Yamada 1688639f69aSSimon Glass warnings = self._conf.load_config(self._tmpfile) 1698639f69aSSimon Glass if warnings: 1708639f69aSSimon Glass for warning in warnings: 1718639f69aSSimon Glass print '%s: %s' % (defconfig, warning) 172f6c8f38eSMasahiro Yamada 173f6c8f38eSMasahiro Yamada try_remove(self._tmpfile) 174f6c8f38eSMasahiro Yamada self._tmpfile = None 175f6c8f38eSMasahiro Yamada 176f6c8f38eSMasahiro Yamada params = {} 177f6c8f38eSMasahiro Yamada 178f6c8f38eSMasahiro Yamada # Get the value of CONFIG_SYS_ARCH, CONFIG_SYS_CPU, ... etc. 179f6c8f38eSMasahiro Yamada # Set '-' if the value is empty. 180f6c8f38eSMasahiro Yamada for key, symbol in self._SYMBOL_TABLE.items(): 181f6c8f38eSMasahiro Yamada value = self._conf.get_symbol(symbol).get_value() 182f6c8f38eSMasahiro Yamada if value: 183f6c8f38eSMasahiro Yamada params[key] = value 184f6c8f38eSMasahiro Yamada else: 185f6c8f38eSMasahiro Yamada params[key] = '-' 186f6c8f38eSMasahiro Yamada 187f6c8f38eSMasahiro Yamada defconfig = os.path.basename(defconfig) 188f6c8f38eSMasahiro Yamada params['target'], match, rear = defconfig.partition('_defconfig') 189f6c8f38eSMasahiro Yamada assert match and not rear, '%s : invalid defconfig' % defconfig 190f6c8f38eSMasahiro Yamada 191f6c8f38eSMasahiro Yamada # fix-up for aarch64 192f6c8f38eSMasahiro Yamada if params['arch'] == 'arm' and params['cpu'] == 'armv8': 193f6c8f38eSMasahiro Yamada params['arch'] = 'aarch64' 194f6c8f38eSMasahiro Yamada 195f6c8f38eSMasahiro Yamada # fix-up options field. It should have the form: 196f6c8f38eSMasahiro Yamada # <config name>[:comma separated config options] 197f6c8f38eSMasahiro Yamada if params['options'] != '-': 198f6c8f38eSMasahiro Yamada params['options'] = params['config'] + ':' + \ 199f6c8f38eSMasahiro Yamada params['options'].replace(r'\"', '"') 200f6c8f38eSMasahiro Yamada elif params['config'] != params['target']: 201f6c8f38eSMasahiro Yamada params['options'] = params['config'] 202f6c8f38eSMasahiro Yamada 203f6c8f38eSMasahiro Yamada return params 204f6c8f38eSMasahiro Yamada 205f6c8f38eSMasahiro Yamadadef scan_defconfigs_for_multiprocess(queue, defconfigs): 206f6c8f38eSMasahiro Yamada """Scan defconfig files and queue their board parameters 207f6c8f38eSMasahiro Yamada 208f6c8f38eSMasahiro Yamada This function is intended to be passed to 209f6c8f38eSMasahiro Yamada multiprocessing.Process() constructor. 210f6c8f38eSMasahiro Yamada 211f6c8f38eSMasahiro Yamada Arguments: 212f6c8f38eSMasahiro Yamada queue: An instance of multiprocessing.Queue(). 213f6c8f38eSMasahiro Yamada The resulting board parameters are written into it. 214f6c8f38eSMasahiro Yamada defconfigs: A sequence of defconfig files to be scanned. 215f6c8f38eSMasahiro Yamada """ 216f6c8f38eSMasahiro Yamada kconf_scanner = KconfigScanner() 217f6c8f38eSMasahiro Yamada for defconfig in defconfigs: 218f6c8f38eSMasahiro Yamada queue.put(kconf_scanner.scan(defconfig)) 219f6c8f38eSMasahiro Yamada 220f6c8f38eSMasahiro Yamadadef read_queues(queues, params_list): 221f6c8f38eSMasahiro Yamada """Read the queues and append the data to the paramers list""" 222f6c8f38eSMasahiro Yamada for q in queues: 223f6c8f38eSMasahiro Yamada while not q.empty(): 224f6c8f38eSMasahiro Yamada params_list.append(q.get()) 225f6c8f38eSMasahiro Yamada 226f6c8f38eSMasahiro Yamadadef scan_defconfigs(jobs=1): 227f6c8f38eSMasahiro Yamada """Collect board parameters for all defconfig files. 228f6c8f38eSMasahiro Yamada 229f6c8f38eSMasahiro Yamada This function invokes multiple processes for faster processing. 230f6c8f38eSMasahiro Yamada 231f6c8f38eSMasahiro Yamada Arguments: 232f6c8f38eSMasahiro Yamada jobs: The number of jobs to run simultaneously 233f6c8f38eSMasahiro Yamada """ 234f6c8f38eSMasahiro Yamada all_defconfigs = [] 235f6c8f38eSMasahiro Yamada for (dirpath, dirnames, filenames) in os.walk(CONFIG_DIR): 236f6c8f38eSMasahiro Yamada for filename in fnmatch.filter(filenames, '*_defconfig'): 237f6c8f38eSMasahiro Yamada if fnmatch.fnmatch(filename, '.*'): 238f6c8f38eSMasahiro Yamada continue 239f6c8f38eSMasahiro Yamada all_defconfigs.append(os.path.join(dirpath, filename)) 240f6c8f38eSMasahiro Yamada 241f6c8f38eSMasahiro Yamada total_boards = len(all_defconfigs) 242f6c8f38eSMasahiro Yamada processes = [] 243f6c8f38eSMasahiro Yamada queues = [] 244f6c8f38eSMasahiro Yamada for i in range(jobs): 245f6c8f38eSMasahiro Yamada defconfigs = all_defconfigs[total_boards * i / jobs : 246f6c8f38eSMasahiro Yamada total_boards * (i + 1) / jobs] 247f6c8f38eSMasahiro Yamada q = multiprocessing.Queue(maxsize=-1) 248f6c8f38eSMasahiro Yamada p = multiprocessing.Process(target=scan_defconfigs_for_multiprocess, 249f6c8f38eSMasahiro Yamada args=(q, defconfigs)) 250f6c8f38eSMasahiro Yamada p.start() 251f6c8f38eSMasahiro Yamada processes.append(p) 252f6c8f38eSMasahiro Yamada queues.append(q) 253f6c8f38eSMasahiro Yamada 254f6c8f38eSMasahiro Yamada # The resulting data should be accumulated to this list 255f6c8f38eSMasahiro Yamada params_list = [] 256f6c8f38eSMasahiro Yamada 257f6c8f38eSMasahiro Yamada # Data in the queues should be retrieved preriodically. 258f6c8f38eSMasahiro Yamada # Otherwise, the queues would become full and subprocesses would get stuck. 259f6c8f38eSMasahiro Yamada while any([p.is_alive() for p in processes]): 260f6c8f38eSMasahiro Yamada read_queues(queues, params_list) 261f6c8f38eSMasahiro Yamada # sleep for a while until the queues are filled 262f6c8f38eSMasahiro Yamada time.sleep(SLEEP_TIME) 263f6c8f38eSMasahiro Yamada 264f6c8f38eSMasahiro Yamada # Joining subprocesses just in case 265f6c8f38eSMasahiro Yamada # (All subprocesses should already have been finished) 266f6c8f38eSMasahiro Yamada for p in processes: 267f6c8f38eSMasahiro Yamada p.join() 268f6c8f38eSMasahiro Yamada 269f6c8f38eSMasahiro Yamada # retrieve leftover data 270f6c8f38eSMasahiro Yamada read_queues(queues, params_list) 271f6c8f38eSMasahiro Yamada 272f6c8f38eSMasahiro Yamada return params_list 273f6c8f38eSMasahiro Yamada 2743c08e8b8SMasahiro Yamadaclass MaintainersDatabase: 2753c08e8b8SMasahiro Yamada 2763c08e8b8SMasahiro Yamada """The database of board status and maintainers.""" 2773c08e8b8SMasahiro Yamada 2783c08e8b8SMasahiro Yamada def __init__(self): 2793c08e8b8SMasahiro Yamada """Create an empty database.""" 2803c08e8b8SMasahiro Yamada self.database = {} 2813c08e8b8SMasahiro Yamada 2823c08e8b8SMasahiro Yamada def get_status(self, target): 2833c08e8b8SMasahiro Yamada """Return the status of the given board. 2843c08e8b8SMasahiro Yamada 285f6c8f38eSMasahiro Yamada The board status is generally either 'Active' or 'Orphan'. 286f6c8f38eSMasahiro Yamada Display a warning message and return '-' if status information 287f6c8f38eSMasahiro Yamada is not found. 288f6c8f38eSMasahiro Yamada 2893c08e8b8SMasahiro Yamada Returns: 290f6c8f38eSMasahiro Yamada 'Active', 'Orphan' or '-'. 2913c08e8b8SMasahiro Yamada """ 292b8828e8fSMasahiro Yamada if not target in self.database: 293b8828e8fSMasahiro Yamada print >> sys.stderr, "WARNING: no status info for '%s'" % target 294b8828e8fSMasahiro Yamada return '-' 295b8828e8fSMasahiro Yamada 2963c08e8b8SMasahiro Yamada tmp = self.database[target][0] 2973c08e8b8SMasahiro Yamada if tmp.startswith('Maintained'): 2983c08e8b8SMasahiro Yamada return 'Active' 299dee7c68fSLokesh Vutla elif tmp.startswith('Supported'): 300dee7c68fSLokesh Vutla return 'Active' 3013c08e8b8SMasahiro Yamada elif tmp.startswith('Orphan'): 3023c08e8b8SMasahiro Yamada return 'Orphan' 3033c08e8b8SMasahiro Yamada else: 304b8828e8fSMasahiro Yamada print >> sys.stderr, ("WARNING: %s: unknown status for '%s'" % 305b8828e8fSMasahiro Yamada (tmp, target)) 306b8828e8fSMasahiro Yamada return '-' 3073c08e8b8SMasahiro Yamada 3083c08e8b8SMasahiro Yamada def get_maintainers(self, target): 3093c08e8b8SMasahiro Yamada """Return the maintainers of the given board. 3103c08e8b8SMasahiro Yamada 311f6c8f38eSMasahiro Yamada Returns: 312f6c8f38eSMasahiro Yamada Maintainers of the board. If the board has two or more maintainers, 313f6c8f38eSMasahiro Yamada they are separated with colons. 3143c08e8b8SMasahiro Yamada """ 315b8828e8fSMasahiro Yamada if not target in self.database: 316b8828e8fSMasahiro Yamada print >> sys.stderr, "WARNING: no maintainers for '%s'" % target 317b8828e8fSMasahiro Yamada return '' 318b8828e8fSMasahiro Yamada 3193c08e8b8SMasahiro Yamada return ':'.join(self.database[target][1]) 3203c08e8b8SMasahiro Yamada 3213c08e8b8SMasahiro Yamada def parse_file(self, file): 322f6c8f38eSMasahiro Yamada """Parse a MAINTAINERS file. 3233c08e8b8SMasahiro Yamada 324f6c8f38eSMasahiro Yamada Parse a MAINTAINERS file and accumulates board status and 325f6c8f38eSMasahiro Yamada maintainers information. 3263c08e8b8SMasahiro Yamada 3273c08e8b8SMasahiro Yamada Arguments: 3283c08e8b8SMasahiro Yamada file: MAINTAINERS file to be parsed 3293c08e8b8SMasahiro Yamada """ 3303c08e8b8SMasahiro Yamada targets = [] 3313c08e8b8SMasahiro Yamada maintainers = [] 3323c08e8b8SMasahiro Yamada status = '-' 3333c08e8b8SMasahiro Yamada for line in open(file): 3345dff844dSMasahiro Yamada # Check also commented maintainers 3355dff844dSMasahiro Yamada if line[:3] == '#M:': 3365dff844dSMasahiro Yamada line = line[1:] 3373c08e8b8SMasahiro Yamada tag, rest = line[:2], line[2:].strip() 3383c08e8b8SMasahiro Yamada if tag == 'M:': 3393c08e8b8SMasahiro Yamada maintainers.append(rest) 3403c08e8b8SMasahiro Yamada elif tag == 'F:': 3413c08e8b8SMasahiro Yamada # expand wildcard and filter by 'configs/*_defconfig' 3423c08e8b8SMasahiro Yamada for f in glob.glob(rest): 3433c08e8b8SMasahiro Yamada front, match, rear = f.partition('configs/') 3443c08e8b8SMasahiro Yamada if not front and match: 3453c08e8b8SMasahiro Yamada front, match, rear = rear.rpartition('_defconfig') 3463c08e8b8SMasahiro Yamada if match and not rear: 3473c08e8b8SMasahiro Yamada targets.append(front) 3483c08e8b8SMasahiro Yamada elif tag == 'S:': 3493c08e8b8SMasahiro Yamada status = rest 3509c2d60c3SMasahiro Yamada elif line == '\n': 3513c08e8b8SMasahiro Yamada for target in targets: 3523c08e8b8SMasahiro Yamada self.database[target] = (status, maintainers) 3533c08e8b8SMasahiro Yamada targets = [] 3543c08e8b8SMasahiro Yamada maintainers = [] 3553c08e8b8SMasahiro Yamada status = '-' 3563c08e8b8SMasahiro Yamada if targets: 3573c08e8b8SMasahiro Yamada for target in targets: 3583c08e8b8SMasahiro Yamada self.database[target] = (status, maintainers) 3593c08e8b8SMasahiro Yamada 360f6c8f38eSMasahiro Yamadadef insert_maintainers_info(params_list): 361f6c8f38eSMasahiro Yamada """Add Status and Maintainers information to the board parameters list. 3623c08e8b8SMasahiro Yamada 3633c08e8b8SMasahiro Yamada Arguments: 364f6c8f38eSMasahiro Yamada params_list: A list of the board parameters 3653c08e8b8SMasahiro Yamada """ 366f6c8f38eSMasahiro Yamada database = MaintainersDatabase() 3673c08e8b8SMasahiro Yamada for (dirpath, dirnames, filenames) in os.walk('.'): 3683c08e8b8SMasahiro Yamada if 'MAINTAINERS' in filenames: 369f6c8f38eSMasahiro Yamada database.parse_file(os.path.join(dirpath, 'MAINTAINERS')) 37079d45d32SMasahiro Yamada 371f6c8f38eSMasahiro Yamada for i, params in enumerate(params_list): 372f6c8f38eSMasahiro Yamada target = params['target'] 373f6c8f38eSMasahiro Yamada params['status'] = database.get_status(target) 374f6c8f38eSMasahiro Yamada params['maintainers'] = database.get_maintainers(target) 375f6c8f38eSMasahiro Yamada params_list[i] = params 37679d45d32SMasahiro Yamada 377f6c8f38eSMasahiro Yamadadef format_and_output(params_list, output): 378f6c8f38eSMasahiro Yamada """Write board parameters into a file. 37979d45d32SMasahiro Yamada 380f6c8f38eSMasahiro Yamada Columnate the board parameters, sort lines alphabetically, 381f6c8f38eSMasahiro Yamada and then write them to a file. 38279d45d32SMasahiro Yamada 38379d45d32SMasahiro Yamada Arguments: 384f6c8f38eSMasahiro Yamada params_list: The list of board parameters 385f6c8f38eSMasahiro Yamada output: The path to the output file 38679d45d32SMasahiro Yamada """ 387f6c8f38eSMasahiro Yamada FIELDS = ('status', 'arch', 'cpu', 'soc', 'vendor', 'board', 'target', 388f6c8f38eSMasahiro Yamada 'options', 'maintainers') 38979d45d32SMasahiro Yamada 390f6c8f38eSMasahiro Yamada # First, decide the width of each column 391f6c8f38eSMasahiro Yamada max_length = dict([ (f, 0) for f in FIELDS]) 392f6c8f38eSMasahiro Yamada for params in params_list: 393f6c8f38eSMasahiro Yamada for f in FIELDS: 394f6c8f38eSMasahiro Yamada max_length[f] = max(max_length[f], len(params[f])) 3953c08e8b8SMasahiro Yamada 396f6c8f38eSMasahiro Yamada output_lines = [] 397f6c8f38eSMasahiro Yamada for params in params_list: 398f6c8f38eSMasahiro Yamada line = '' 399f6c8f38eSMasahiro Yamada for f in FIELDS: 400f6c8f38eSMasahiro Yamada # insert two spaces between fields like column -t would 401f6c8f38eSMasahiro Yamada line += ' ' + params[f].ljust(max_length[f]) 402f6c8f38eSMasahiro Yamada output_lines.append(line.strip()) 4033c08e8b8SMasahiro Yamada 404f6c8f38eSMasahiro Yamada # ignore case when sorting 405f6c8f38eSMasahiro Yamada output_lines.sort(key=str.lower) 4063c08e8b8SMasahiro Yamada 407f6c8f38eSMasahiro Yamada with open(output, 'w') as f: 408f6c8f38eSMasahiro Yamada f.write(COMMENT_BLOCK + '\n'.join(output_lines) + '\n') 4093c08e8b8SMasahiro Yamada 410f6c8f38eSMasahiro Yamadadef gen_boards_cfg(output, jobs=1, force=False): 411f6c8f38eSMasahiro Yamada """Generate a board database file. 4123c08e8b8SMasahiro Yamada 4133c08e8b8SMasahiro Yamada Arguments: 414f6c8f38eSMasahiro Yamada output: The name of the output file 4153c08e8b8SMasahiro Yamada jobs: The number of jobs to run simultaneously 416f6c8f38eSMasahiro Yamada force: Force to generate the output even if it is new 4173c08e8b8SMasahiro Yamada """ 41879d45d32SMasahiro Yamada check_top_directory() 419f6c8f38eSMasahiro Yamada 420f6c8f38eSMasahiro Yamada if not force and output_is_new(output): 421f6c8f38eSMasahiro Yamada print "%s is up to date. Nothing to do." % output 422d1bf4afdSMasahiro Yamada sys.exit(0) 423d1bf4afdSMasahiro Yamada 424f6c8f38eSMasahiro Yamada params_list = scan_defconfigs(jobs) 425f6c8f38eSMasahiro Yamada insert_maintainers_info(params_list) 426f6c8f38eSMasahiro Yamada format_and_output(params_list, output) 4273c08e8b8SMasahiro Yamada 4283c08e8b8SMasahiro Yamadadef main(): 429f6c8f38eSMasahiro Yamada try: 430f6c8f38eSMasahiro Yamada cpu_count = multiprocessing.cpu_count() 431f6c8f38eSMasahiro Yamada except NotImplementedError: 432f6c8f38eSMasahiro Yamada cpu_count = 1 433f6c8f38eSMasahiro Yamada 4343c08e8b8SMasahiro Yamada parser = optparse.OptionParser() 4353c08e8b8SMasahiro Yamada # Add options here 436d1bf4afdSMasahiro Yamada parser.add_option('-f', '--force', action="store_true", default=False, 437d1bf4afdSMasahiro Yamada help='regenerate the output even if it is new') 438f6c8f38eSMasahiro Yamada parser.add_option('-j', '--jobs', type='int', default=cpu_count, 439f6c8f38eSMasahiro Yamada help='the number of jobs to run simultaneously') 440f6c8f38eSMasahiro Yamada parser.add_option('-o', '--output', default=OUTPUT_FILE, 441f6c8f38eSMasahiro Yamada help='output file [default=%s]' % OUTPUT_FILE) 4423c08e8b8SMasahiro Yamada (options, args) = parser.parse_args() 443d1bf4afdSMasahiro Yamada 444f6c8f38eSMasahiro Yamada gen_boards_cfg(options.output, jobs=options.jobs, force=options.force) 4453c08e8b8SMasahiro Yamada 4463c08e8b8SMasahiro Yamadaif __name__ == '__main__': 4473c08e8b8SMasahiro Yamada main() 448