1*4882a593Smuzhiyun#!/usr/bin/env python2 2*4882a593Smuzhiyun# 3*4882a593Smuzhiyun# Author: Masahiro Yamada <yamada.m@jp.panasonic.com> 4*4882a593Smuzhiyun# 5*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0+ 6*4882a593Smuzhiyun# 7*4882a593Smuzhiyun 8*4882a593Smuzhiyun""" 9*4882a593SmuzhiyunConverter from Kconfig and MAINTAINERS to a board database. 10*4882a593Smuzhiyun 11*4882a593SmuzhiyunRun 'tools/genboardscfg.py' to create a board database. 12*4882a593Smuzhiyun 13*4882a593SmuzhiyunRun 'tools/genboardscfg.py -h' for available options. 14*4882a593Smuzhiyun 15*4882a593SmuzhiyunPython 2.6 or later, but not Python 3.x is necessary to run this script. 16*4882a593Smuzhiyun""" 17*4882a593Smuzhiyun 18*4882a593Smuzhiyunimport errno 19*4882a593Smuzhiyunimport fnmatch 20*4882a593Smuzhiyunimport glob 21*4882a593Smuzhiyunimport multiprocessing 22*4882a593Smuzhiyunimport optparse 23*4882a593Smuzhiyunimport os 24*4882a593Smuzhiyunimport sys 25*4882a593Smuzhiyunimport tempfile 26*4882a593Smuzhiyunimport time 27*4882a593Smuzhiyun 28*4882a593Smuzhiyunsys.path.append(os.path.join(os.path.dirname(__file__), 'buildman')) 29*4882a593Smuzhiyunimport kconfiglib 30*4882a593Smuzhiyun 31*4882a593Smuzhiyun### constant variables ### 32*4882a593SmuzhiyunOUTPUT_FILE = 'boards.cfg' 33*4882a593SmuzhiyunCONFIG_DIR = 'configs' 34*4882a593SmuzhiyunSLEEP_TIME = 0.03 35*4882a593SmuzhiyunCOMMENT_BLOCK = '''# 36*4882a593Smuzhiyun# List of boards 37*4882a593Smuzhiyun# Automatically generated by %s: don't edit 38*4882a593Smuzhiyun# 39*4882a593Smuzhiyun# Status, Arch, CPU, SoC, Vendor, Board, Target, Options, Maintainers 40*4882a593Smuzhiyun 41*4882a593Smuzhiyun''' % __file__ 42*4882a593Smuzhiyun 43*4882a593Smuzhiyun### helper functions ### 44*4882a593Smuzhiyundef try_remove(f): 45*4882a593Smuzhiyun """Remove a file ignoring 'No such file or directory' error.""" 46*4882a593Smuzhiyun try: 47*4882a593Smuzhiyun os.remove(f) 48*4882a593Smuzhiyun except OSError as exception: 49*4882a593Smuzhiyun # Ignore 'No such file or directory' error 50*4882a593Smuzhiyun if exception.errno != errno.ENOENT: 51*4882a593Smuzhiyun raise 52*4882a593Smuzhiyun 53*4882a593Smuzhiyundef check_top_directory(): 54*4882a593Smuzhiyun """Exit if we are not at the top of source directory.""" 55*4882a593Smuzhiyun for f in ('README', 'Licenses'): 56*4882a593Smuzhiyun if not os.path.exists(f): 57*4882a593Smuzhiyun sys.exit('Please run at the top of source directory.') 58*4882a593Smuzhiyun 59*4882a593Smuzhiyundef output_is_new(output): 60*4882a593Smuzhiyun """Check if the output file is up to date. 61*4882a593Smuzhiyun 62*4882a593Smuzhiyun Returns: 63*4882a593Smuzhiyun True if the given output file exists and is newer than any of 64*4882a593Smuzhiyun *_defconfig, MAINTAINERS and Kconfig*. False otherwise. 65*4882a593Smuzhiyun """ 66*4882a593Smuzhiyun try: 67*4882a593Smuzhiyun ctime = os.path.getctime(output) 68*4882a593Smuzhiyun except OSError as exception: 69*4882a593Smuzhiyun if exception.errno == errno.ENOENT: 70*4882a593Smuzhiyun # return False on 'No such file or directory' error 71*4882a593Smuzhiyun return False 72*4882a593Smuzhiyun else: 73*4882a593Smuzhiyun raise 74*4882a593Smuzhiyun 75*4882a593Smuzhiyun for (dirpath, dirnames, filenames) in os.walk(CONFIG_DIR): 76*4882a593Smuzhiyun for filename in fnmatch.filter(filenames, '*_defconfig'): 77*4882a593Smuzhiyun if fnmatch.fnmatch(filename, '.*'): 78*4882a593Smuzhiyun continue 79*4882a593Smuzhiyun filepath = os.path.join(dirpath, filename) 80*4882a593Smuzhiyun if ctime < os.path.getctime(filepath): 81*4882a593Smuzhiyun return False 82*4882a593Smuzhiyun 83*4882a593Smuzhiyun for (dirpath, dirnames, filenames) in os.walk('.'): 84*4882a593Smuzhiyun for filename in filenames: 85*4882a593Smuzhiyun if (fnmatch.fnmatch(filename, '*~') or 86*4882a593Smuzhiyun not fnmatch.fnmatch(filename, 'Kconfig*') and 87*4882a593Smuzhiyun not filename == 'MAINTAINERS'): 88*4882a593Smuzhiyun continue 89*4882a593Smuzhiyun filepath = os.path.join(dirpath, filename) 90*4882a593Smuzhiyun if ctime < os.path.getctime(filepath): 91*4882a593Smuzhiyun return False 92*4882a593Smuzhiyun 93*4882a593Smuzhiyun # Detect a board that has been removed since the current board database 94*4882a593Smuzhiyun # was generated 95*4882a593Smuzhiyun with open(output) as f: 96*4882a593Smuzhiyun for line in f: 97*4882a593Smuzhiyun if line[0] == '#' or line == '\n': 98*4882a593Smuzhiyun continue 99*4882a593Smuzhiyun defconfig = line.split()[6] + '_defconfig' 100*4882a593Smuzhiyun if not os.path.exists(os.path.join(CONFIG_DIR, defconfig)): 101*4882a593Smuzhiyun return False 102*4882a593Smuzhiyun 103*4882a593Smuzhiyun return True 104*4882a593Smuzhiyun 105*4882a593Smuzhiyun### classes ### 106*4882a593Smuzhiyunclass KconfigScanner: 107*4882a593Smuzhiyun 108*4882a593Smuzhiyun """Kconfig scanner.""" 109*4882a593Smuzhiyun 110*4882a593Smuzhiyun ### constant variable only used in this class ### 111*4882a593Smuzhiyun _SYMBOL_TABLE = { 112*4882a593Smuzhiyun 'arch' : 'SYS_ARCH', 113*4882a593Smuzhiyun 'cpu' : 'SYS_CPU', 114*4882a593Smuzhiyun 'soc' : 'SYS_SOC', 115*4882a593Smuzhiyun 'vendor' : 'SYS_VENDOR', 116*4882a593Smuzhiyun 'board' : 'SYS_BOARD', 117*4882a593Smuzhiyun 'config' : 'SYS_CONFIG_NAME', 118*4882a593Smuzhiyun 'options' : 'SYS_EXTRA_OPTIONS' 119*4882a593Smuzhiyun } 120*4882a593Smuzhiyun 121*4882a593Smuzhiyun def __init__(self): 122*4882a593Smuzhiyun """Scan all the Kconfig files and create a Config object.""" 123*4882a593Smuzhiyun # Define environment variables referenced from Kconfig 124*4882a593Smuzhiyun os.environ['srctree'] = os.getcwd() 125*4882a593Smuzhiyun os.environ['UBOOTVERSION'] = 'dummy' 126*4882a593Smuzhiyun os.environ['KCONFIG_OBJDIR'] = '' 127*4882a593Smuzhiyun self._conf = kconfiglib.Config(print_warnings=False) 128*4882a593Smuzhiyun 129*4882a593Smuzhiyun def __del__(self): 130*4882a593Smuzhiyun """Delete a leftover temporary file before exit. 131*4882a593Smuzhiyun 132*4882a593Smuzhiyun The scan() method of this class creates a temporay file and deletes 133*4882a593Smuzhiyun it on success. If scan() method throws an exception on the way, 134*4882a593Smuzhiyun the temporary file might be left over. In that case, it should be 135*4882a593Smuzhiyun deleted in this destructor. 136*4882a593Smuzhiyun """ 137*4882a593Smuzhiyun if hasattr(self, '_tmpfile') and self._tmpfile: 138*4882a593Smuzhiyun try_remove(self._tmpfile) 139*4882a593Smuzhiyun 140*4882a593Smuzhiyun def scan(self, defconfig): 141*4882a593Smuzhiyun """Load a defconfig file to obtain board parameters. 142*4882a593Smuzhiyun 143*4882a593Smuzhiyun Arguments: 144*4882a593Smuzhiyun defconfig: path to the defconfig file to be processed 145*4882a593Smuzhiyun 146*4882a593Smuzhiyun Returns: 147*4882a593Smuzhiyun A dictionary of board parameters. It has a form of: 148*4882a593Smuzhiyun { 149*4882a593Smuzhiyun 'arch': <arch_name>, 150*4882a593Smuzhiyun 'cpu': <cpu_name>, 151*4882a593Smuzhiyun 'soc': <soc_name>, 152*4882a593Smuzhiyun 'vendor': <vendor_name>, 153*4882a593Smuzhiyun 'board': <board_name>, 154*4882a593Smuzhiyun 'target': <target_name>, 155*4882a593Smuzhiyun 'config': <config_header_name>, 156*4882a593Smuzhiyun 'options': <extra_options> 157*4882a593Smuzhiyun } 158*4882a593Smuzhiyun """ 159*4882a593Smuzhiyun # strip special prefixes and save it in a temporary file 160*4882a593Smuzhiyun fd, self._tmpfile = tempfile.mkstemp() 161*4882a593Smuzhiyun with os.fdopen(fd, 'w') as f: 162*4882a593Smuzhiyun for line in open(defconfig): 163*4882a593Smuzhiyun colon = line.find(':CONFIG_') 164*4882a593Smuzhiyun if colon == -1: 165*4882a593Smuzhiyun f.write(line) 166*4882a593Smuzhiyun else: 167*4882a593Smuzhiyun f.write(line[colon + 1:]) 168*4882a593Smuzhiyun 169*4882a593Smuzhiyun warnings = self._conf.load_config(self._tmpfile) 170*4882a593Smuzhiyun if warnings: 171*4882a593Smuzhiyun for warning in warnings: 172*4882a593Smuzhiyun print '%s: %s' % (defconfig, warning) 173*4882a593Smuzhiyun 174*4882a593Smuzhiyun try_remove(self._tmpfile) 175*4882a593Smuzhiyun self._tmpfile = None 176*4882a593Smuzhiyun 177*4882a593Smuzhiyun params = {} 178*4882a593Smuzhiyun 179*4882a593Smuzhiyun # Get the value of CONFIG_SYS_ARCH, CONFIG_SYS_CPU, ... etc. 180*4882a593Smuzhiyun # Set '-' if the value is empty. 181*4882a593Smuzhiyun for key, symbol in self._SYMBOL_TABLE.items(): 182*4882a593Smuzhiyun value = self._conf.get_symbol(symbol).get_value() 183*4882a593Smuzhiyun if value: 184*4882a593Smuzhiyun params[key] = value 185*4882a593Smuzhiyun else: 186*4882a593Smuzhiyun params[key] = '-' 187*4882a593Smuzhiyun 188*4882a593Smuzhiyun defconfig = os.path.basename(defconfig) 189*4882a593Smuzhiyun params['target'], match, rear = defconfig.partition('_defconfig') 190*4882a593Smuzhiyun assert match and not rear, '%s : invalid defconfig' % defconfig 191*4882a593Smuzhiyun 192*4882a593Smuzhiyun # fix-up for aarch64 193*4882a593Smuzhiyun if params['arch'] == 'arm' and params['cpu'] == 'armv8': 194*4882a593Smuzhiyun params['arch'] = 'aarch64' 195*4882a593Smuzhiyun 196*4882a593Smuzhiyun # fix-up options field. It should have the form: 197*4882a593Smuzhiyun # <config name>[:comma separated config options] 198*4882a593Smuzhiyun if params['options'] != '-': 199*4882a593Smuzhiyun params['options'] = params['config'] + ':' + \ 200*4882a593Smuzhiyun params['options'].replace(r'\"', '"') 201*4882a593Smuzhiyun elif params['config'] != params['target']: 202*4882a593Smuzhiyun params['options'] = params['config'] 203*4882a593Smuzhiyun 204*4882a593Smuzhiyun return params 205*4882a593Smuzhiyun 206*4882a593Smuzhiyundef scan_defconfigs_for_multiprocess(queue, defconfigs): 207*4882a593Smuzhiyun """Scan defconfig files and queue their board parameters 208*4882a593Smuzhiyun 209*4882a593Smuzhiyun This function is intended to be passed to 210*4882a593Smuzhiyun multiprocessing.Process() constructor. 211*4882a593Smuzhiyun 212*4882a593Smuzhiyun Arguments: 213*4882a593Smuzhiyun queue: An instance of multiprocessing.Queue(). 214*4882a593Smuzhiyun The resulting board parameters are written into it. 215*4882a593Smuzhiyun defconfigs: A sequence of defconfig files to be scanned. 216*4882a593Smuzhiyun """ 217*4882a593Smuzhiyun kconf_scanner = KconfigScanner() 218*4882a593Smuzhiyun for defconfig in defconfigs: 219*4882a593Smuzhiyun queue.put(kconf_scanner.scan(defconfig)) 220*4882a593Smuzhiyun 221*4882a593Smuzhiyundef read_queues(queues, params_list): 222*4882a593Smuzhiyun """Read the queues and append the data to the paramers list""" 223*4882a593Smuzhiyun for q in queues: 224*4882a593Smuzhiyun while not q.empty(): 225*4882a593Smuzhiyun params_list.append(q.get()) 226*4882a593Smuzhiyun 227*4882a593Smuzhiyundef scan_defconfigs(jobs=1): 228*4882a593Smuzhiyun """Collect board parameters for all defconfig files. 229*4882a593Smuzhiyun 230*4882a593Smuzhiyun This function invokes multiple processes for faster processing. 231*4882a593Smuzhiyun 232*4882a593Smuzhiyun Arguments: 233*4882a593Smuzhiyun jobs: The number of jobs to run simultaneously 234*4882a593Smuzhiyun """ 235*4882a593Smuzhiyun all_defconfigs = [] 236*4882a593Smuzhiyun for (dirpath, dirnames, filenames) in os.walk(CONFIG_DIR): 237*4882a593Smuzhiyun for filename in fnmatch.filter(filenames, '*_defconfig'): 238*4882a593Smuzhiyun if fnmatch.fnmatch(filename, '.*'): 239*4882a593Smuzhiyun continue 240*4882a593Smuzhiyun all_defconfigs.append(os.path.join(dirpath, filename)) 241*4882a593Smuzhiyun 242*4882a593Smuzhiyun total_boards = len(all_defconfigs) 243*4882a593Smuzhiyun processes = [] 244*4882a593Smuzhiyun queues = [] 245*4882a593Smuzhiyun for i in range(jobs): 246*4882a593Smuzhiyun defconfigs = all_defconfigs[total_boards * i / jobs : 247*4882a593Smuzhiyun total_boards * (i + 1) / jobs] 248*4882a593Smuzhiyun q = multiprocessing.Queue(maxsize=-1) 249*4882a593Smuzhiyun p = multiprocessing.Process(target=scan_defconfigs_for_multiprocess, 250*4882a593Smuzhiyun args=(q, defconfigs)) 251*4882a593Smuzhiyun p.start() 252*4882a593Smuzhiyun processes.append(p) 253*4882a593Smuzhiyun queues.append(q) 254*4882a593Smuzhiyun 255*4882a593Smuzhiyun # The resulting data should be accumulated to this list 256*4882a593Smuzhiyun params_list = [] 257*4882a593Smuzhiyun 258*4882a593Smuzhiyun # Data in the queues should be retrieved preriodically. 259*4882a593Smuzhiyun # Otherwise, the queues would become full and subprocesses would get stuck. 260*4882a593Smuzhiyun while any([p.is_alive() for p in processes]): 261*4882a593Smuzhiyun read_queues(queues, params_list) 262*4882a593Smuzhiyun # sleep for a while until the queues are filled 263*4882a593Smuzhiyun time.sleep(SLEEP_TIME) 264*4882a593Smuzhiyun 265*4882a593Smuzhiyun # Joining subprocesses just in case 266*4882a593Smuzhiyun # (All subprocesses should already have been finished) 267*4882a593Smuzhiyun for p in processes: 268*4882a593Smuzhiyun p.join() 269*4882a593Smuzhiyun 270*4882a593Smuzhiyun # retrieve leftover data 271*4882a593Smuzhiyun read_queues(queues, params_list) 272*4882a593Smuzhiyun 273*4882a593Smuzhiyun return params_list 274*4882a593Smuzhiyun 275*4882a593Smuzhiyunclass MaintainersDatabase: 276*4882a593Smuzhiyun 277*4882a593Smuzhiyun """The database of board status and maintainers.""" 278*4882a593Smuzhiyun 279*4882a593Smuzhiyun def __init__(self): 280*4882a593Smuzhiyun """Create an empty database.""" 281*4882a593Smuzhiyun self.database = {} 282*4882a593Smuzhiyun 283*4882a593Smuzhiyun def get_status(self, target): 284*4882a593Smuzhiyun """Return the status of the given board. 285*4882a593Smuzhiyun 286*4882a593Smuzhiyun The board status is generally either 'Active' or 'Orphan'. 287*4882a593Smuzhiyun Display a warning message and return '-' if status information 288*4882a593Smuzhiyun is not found. 289*4882a593Smuzhiyun 290*4882a593Smuzhiyun Returns: 291*4882a593Smuzhiyun 'Active', 'Orphan' or '-'. 292*4882a593Smuzhiyun """ 293*4882a593Smuzhiyun if not target in self.database: 294*4882a593Smuzhiyun print >> sys.stderr, "WARNING: no status info for '%s'" % target 295*4882a593Smuzhiyun return '-' 296*4882a593Smuzhiyun 297*4882a593Smuzhiyun tmp = self.database[target][0] 298*4882a593Smuzhiyun if tmp.startswith('Maintained'): 299*4882a593Smuzhiyun return 'Active' 300*4882a593Smuzhiyun elif tmp.startswith('Supported'): 301*4882a593Smuzhiyun return 'Active' 302*4882a593Smuzhiyun elif tmp.startswith('Orphan'): 303*4882a593Smuzhiyun return 'Orphan' 304*4882a593Smuzhiyun else: 305*4882a593Smuzhiyun print >> sys.stderr, ("WARNING: %s: unknown status for '%s'" % 306*4882a593Smuzhiyun (tmp, target)) 307*4882a593Smuzhiyun return '-' 308*4882a593Smuzhiyun 309*4882a593Smuzhiyun def get_maintainers(self, target): 310*4882a593Smuzhiyun """Return the maintainers of the given board. 311*4882a593Smuzhiyun 312*4882a593Smuzhiyun Returns: 313*4882a593Smuzhiyun Maintainers of the board. If the board has two or more maintainers, 314*4882a593Smuzhiyun they are separated with colons. 315*4882a593Smuzhiyun """ 316*4882a593Smuzhiyun if not target in self.database: 317*4882a593Smuzhiyun print >> sys.stderr, "WARNING: no maintainers for '%s'" % target 318*4882a593Smuzhiyun return '' 319*4882a593Smuzhiyun 320*4882a593Smuzhiyun return ':'.join(self.database[target][1]) 321*4882a593Smuzhiyun 322*4882a593Smuzhiyun def parse_file(self, file): 323*4882a593Smuzhiyun """Parse a MAINTAINERS file. 324*4882a593Smuzhiyun 325*4882a593Smuzhiyun Parse a MAINTAINERS file and accumulates board status and 326*4882a593Smuzhiyun maintainers information. 327*4882a593Smuzhiyun 328*4882a593Smuzhiyun Arguments: 329*4882a593Smuzhiyun file: MAINTAINERS file to be parsed 330*4882a593Smuzhiyun """ 331*4882a593Smuzhiyun targets = [] 332*4882a593Smuzhiyun maintainers = [] 333*4882a593Smuzhiyun status = '-' 334*4882a593Smuzhiyun for line in open(file): 335*4882a593Smuzhiyun # Check also commented maintainers 336*4882a593Smuzhiyun if line[:3] == '#M:': 337*4882a593Smuzhiyun line = line[1:] 338*4882a593Smuzhiyun tag, rest = line[:2], line[2:].strip() 339*4882a593Smuzhiyun if tag == 'M:': 340*4882a593Smuzhiyun maintainers.append(rest) 341*4882a593Smuzhiyun elif tag == 'F:': 342*4882a593Smuzhiyun # expand wildcard and filter by 'configs/*_defconfig' 343*4882a593Smuzhiyun for f in glob.glob(rest): 344*4882a593Smuzhiyun front, match, rear = f.partition('configs/') 345*4882a593Smuzhiyun if not front and match: 346*4882a593Smuzhiyun front, match, rear = rear.rpartition('_defconfig') 347*4882a593Smuzhiyun if match and not rear: 348*4882a593Smuzhiyun targets.append(front) 349*4882a593Smuzhiyun elif tag == 'S:': 350*4882a593Smuzhiyun status = rest 351*4882a593Smuzhiyun elif line == '\n': 352*4882a593Smuzhiyun for target in targets: 353*4882a593Smuzhiyun self.database[target] = (status, maintainers) 354*4882a593Smuzhiyun targets = [] 355*4882a593Smuzhiyun maintainers = [] 356*4882a593Smuzhiyun status = '-' 357*4882a593Smuzhiyun if targets: 358*4882a593Smuzhiyun for target in targets: 359*4882a593Smuzhiyun self.database[target] = (status, maintainers) 360*4882a593Smuzhiyun 361*4882a593Smuzhiyundef insert_maintainers_info(params_list): 362*4882a593Smuzhiyun """Add Status and Maintainers information to the board parameters list. 363*4882a593Smuzhiyun 364*4882a593Smuzhiyun Arguments: 365*4882a593Smuzhiyun params_list: A list of the board parameters 366*4882a593Smuzhiyun """ 367*4882a593Smuzhiyun database = MaintainersDatabase() 368*4882a593Smuzhiyun for (dirpath, dirnames, filenames) in os.walk('.'): 369*4882a593Smuzhiyun if 'MAINTAINERS' in filenames: 370*4882a593Smuzhiyun database.parse_file(os.path.join(dirpath, 'MAINTAINERS')) 371*4882a593Smuzhiyun 372*4882a593Smuzhiyun for i, params in enumerate(params_list): 373*4882a593Smuzhiyun target = params['target'] 374*4882a593Smuzhiyun params['status'] = database.get_status(target) 375*4882a593Smuzhiyun params['maintainers'] = database.get_maintainers(target) 376*4882a593Smuzhiyun params_list[i] = params 377*4882a593Smuzhiyun 378*4882a593Smuzhiyundef format_and_output(params_list, output): 379*4882a593Smuzhiyun """Write board parameters into a file. 380*4882a593Smuzhiyun 381*4882a593Smuzhiyun Columnate the board parameters, sort lines alphabetically, 382*4882a593Smuzhiyun and then write them to a file. 383*4882a593Smuzhiyun 384*4882a593Smuzhiyun Arguments: 385*4882a593Smuzhiyun params_list: The list of board parameters 386*4882a593Smuzhiyun output: The path to the output file 387*4882a593Smuzhiyun """ 388*4882a593Smuzhiyun FIELDS = ('status', 'arch', 'cpu', 'soc', 'vendor', 'board', 'target', 389*4882a593Smuzhiyun 'options', 'maintainers') 390*4882a593Smuzhiyun 391*4882a593Smuzhiyun # First, decide the width of each column 392*4882a593Smuzhiyun max_length = dict([ (f, 0) for f in FIELDS]) 393*4882a593Smuzhiyun for params in params_list: 394*4882a593Smuzhiyun for f in FIELDS: 395*4882a593Smuzhiyun max_length[f] = max(max_length[f], len(params[f])) 396*4882a593Smuzhiyun 397*4882a593Smuzhiyun output_lines = [] 398*4882a593Smuzhiyun for params in params_list: 399*4882a593Smuzhiyun line = '' 400*4882a593Smuzhiyun for f in FIELDS: 401*4882a593Smuzhiyun # insert two spaces between fields like column -t would 402*4882a593Smuzhiyun line += ' ' + params[f].ljust(max_length[f]) 403*4882a593Smuzhiyun output_lines.append(line.strip()) 404*4882a593Smuzhiyun 405*4882a593Smuzhiyun # ignore case when sorting 406*4882a593Smuzhiyun output_lines.sort(key=str.lower) 407*4882a593Smuzhiyun 408*4882a593Smuzhiyun with open(output, 'w') as f: 409*4882a593Smuzhiyun f.write(COMMENT_BLOCK + '\n'.join(output_lines) + '\n') 410*4882a593Smuzhiyun 411*4882a593Smuzhiyundef gen_boards_cfg(output, jobs=1, force=False): 412*4882a593Smuzhiyun """Generate a board database file. 413*4882a593Smuzhiyun 414*4882a593Smuzhiyun Arguments: 415*4882a593Smuzhiyun output: The name of the output file 416*4882a593Smuzhiyun jobs: The number of jobs to run simultaneously 417*4882a593Smuzhiyun force: Force to generate the output even if it is new 418*4882a593Smuzhiyun """ 419*4882a593Smuzhiyun check_top_directory() 420*4882a593Smuzhiyun 421*4882a593Smuzhiyun if not force and output_is_new(output): 422*4882a593Smuzhiyun print "%s is up to date. Nothing to do." % output 423*4882a593Smuzhiyun sys.exit(0) 424*4882a593Smuzhiyun 425*4882a593Smuzhiyun params_list = scan_defconfigs(jobs) 426*4882a593Smuzhiyun insert_maintainers_info(params_list) 427*4882a593Smuzhiyun format_and_output(params_list, output) 428*4882a593Smuzhiyun 429*4882a593Smuzhiyundef main(): 430*4882a593Smuzhiyun try: 431*4882a593Smuzhiyun cpu_count = multiprocessing.cpu_count() 432*4882a593Smuzhiyun except NotImplementedError: 433*4882a593Smuzhiyun cpu_count = 1 434*4882a593Smuzhiyun 435*4882a593Smuzhiyun parser = optparse.OptionParser() 436*4882a593Smuzhiyun # Add options here 437*4882a593Smuzhiyun parser.add_option('-f', '--force', action="store_true", default=False, 438*4882a593Smuzhiyun help='regenerate the output even if it is new') 439*4882a593Smuzhiyun parser.add_option('-j', '--jobs', type='int', default=cpu_count, 440*4882a593Smuzhiyun help='the number of jobs to run simultaneously') 441*4882a593Smuzhiyun parser.add_option('-o', '--output', default=OUTPUT_FILE, 442*4882a593Smuzhiyun help='output file [default=%s]' % OUTPUT_FILE) 443*4882a593Smuzhiyun (options, args) = parser.parse_args() 444*4882a593Smuzhiyun 445*4882a593Smuzhiyun gen_boards_cfg(options.output, jobs=options.jobs, force=options.force) 446*4882a593Smuzhiyun 447*4882a593Smuzhiyunif __name__ == '__main__': 448*4882a593Smuzhiyun main() 449