mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-04 05:50:17 +00:00 
			
		
		
		
	This tool only works on python 2 (python 2.6 or lator). Change the shebang to make sure the script is run by python 2 and clearly say the supported version in the comment block. Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
		
			
				
	
	
		
			624 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			624 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/env python2
 | 
						|
#
 | 
						|
# Author: Masahiro Yamada <yamada.m@jp.panasonic.com>
 | 
						|
#
 | 
						|
# SPDX-License-Identifier:	GPL-2.0+
 | 
						|
#
 | 
						|
 | 
						|
"""
 | 
						|
Converter from Kconfig and MAINTAINERS to boards.cfg
 | 
						|
 | 
						|
Run 'tools/genboardscfg.py' to create boards.cfg file.
 | 
						|
 | 
						|
Run 'tools/genboardscfg.py -h' for available options.
 | 
						|
 | 
						|
This script only works on python 2.6 or later, but not python 3.x.
 | 
						|
"""
 | 
						|
 | 
						|
import errno
 | 
						|
import fnmatch
 | 
						|
import glob
 | 
						|
import optparse
 | 
						|
import os
 | 
						|
import re
 | 
						|
import shutil
 | 
						|
import subprocess
 | 
						|
import sys
 | 
						|
import tempfile
 | 
						|
import time
 | 
						|
 | 
						|
BOARD_FILE = 'boards.cfg'
 | 
						|
CONFIG_DIR = 'configs'
 | 
						|
REFORMAT_CMD = [os.path.join('tools', 'reformat.py'),
 | 
						|
                '-i', '-d', '-', '-s', '8']
 | 
						|
SHOW_GNU_MAKE = 'scripts/show-gnu-make'
 | 
						|
SLEEP_TIME=0.003
 | 
						|
 | 
						|
COMMENT_BLOCK = '''#
 | 
						|
# List of boards
 | 
						|
#   Automatically generated by %s: don't edit
 | 
						|
#
 | 
						|
# Status, Arch, CPU, SoC, Vendor, Board, Target, Options, Maintainers
 | 
						|
 | 
						|
''' % __file__
 | 
						|
 | 
						|
### helper functions ###
 | 
						|
def get_terminal_columns():
 | 
						|
    """Get the width of the terminal.
 | 
						|
 | 
						|
    Returns:
 | 
						|
      The width of the terminal, or zero if the stdout is not
 | 
						|
      associated with tty.
 | 
						|
    """
 | 
						|
    try:
 | 
						|
        return shutil.get_terminal_size().columns # Python 3.3~
 | 
						|
    except AttributeError:
 | 
						|
        import fcntl
 | 
						|
        import termios
 | 
						|
        import struct
 | 
						|
        arg = struct.pack('hhhh', 0, 0, 0, 0)
 | 
						|
        try:
 | 
						|
            ret = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, arg)
 | 
						|
        except IOError as exception:
 | 
						|
            # If 'Inappropriate ioctl for device' error occurs,
 | 
						|
            # stdout is probably redirected. Return 0.
 | 
						|
            return 0
 | 
						|
        return struct.unpack('hhhh', ret)[1]
 | 
						|
 | 
						|
def get_devnull():
 | 
						|
    """Get the file object of '/dev/null' device."""
 | 
						|
    try:
 | 
						|
        devnull = subprocess.DEVNULL # py3k
 | 
						|
    except AttributeError:
 | 
						|
        devnull = open(os.devnull, 'wb')
 | 
						|
    return devnull
 | 
						|
 | 
						|
def check_top_directory():
 | 
						|
    """Exit if we are not at the top of source directory."""
 | 
						|
    for f in ('README', 'Licenses'):
 | 
						|
        if not os.path.exists(f):
 | 
						|
            sys.exit('Please run at the top of source directory.')
 | 
						|
 | 
						|
def get_make_cmd():
 | 
						|
    """Get the command name of GNU Make."""
 | 
						|
    process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE)
 | 
						|
    ret = process.communicate()
 | 
						|
    if process.returncode:
 | 
						|
        sys.exit('GNU Make not found')
 | 
						|
    return ret[0].rstrip()
 | 
						|
 | 
						|
def output_is_new():
 | 
						|
    """Check if the boards.cfg file is up to date.
 | 
						|
 | 
						|
    Returns:
 | 
						|
      True if the boards.cfg file exists and is newer than any of
 | 
						|
      *_defconfig, MAINTAINERS and Kconfig*.  False otherwise.
 | 
						|
    """
 | 
						|
    try:
 | 
						|
        ctime = os.path.getctime(BOARD_FILE)
 | 
						|
    except OSError as exception:
 | 
						|
        if exception.errno == errno.ENOENT:
 | 
						|
            # return False on 'No such file or directory' error
 | 
						|
            return False
 | 
						|
        else:
 | 
						|
            raise
 | 
						|
 | 
						|
    for (dirpath, dirnames, filenames) in os.walk(CONFIG_DIR):
 | 
						|
        for filename in fnmatch.filter(filenames, '*_defconfig'):
 | 
						|
            if fnmatch.fnmatch(filename, '.*'):
 | 
						|
                continue
 | 
						|
            filepath = os.path.join(dirpath, filename)
 | 
						|
            if ctime < os.path.getctime(filepath):
 | 
						|
                return False
 | 
						|
 | 
						|
    for (dirpath, dirnames, filenames) in os.walk('.'):
 | 
						|
        for filename in filenames:
 | 
						|
            if (fnmatch.fnmatch(filename, '*~') or
 | 
						|
                not fnmatch.fnmatch(filename, 'Kconfig*') and
 | 
						|
                not filename == 'MAINTAINERS'):
 | 
						|
                continue
 | 
						|
            filepath = os.path.join(dirpath, filename)
 | 
						|
            if ctime < os.path.getctime(filepath):
 | 
						|
                return False
 | 
						|
 | 
						|
    # Detect a board that has been removed since the current boards.cfg
 | 
						|
    # was generated
 | 
						|
    with open(BOARD_FILE) as f:
 | 
						|
        for line in f:
 | 
						|
            if line[0] == '#' or line == '\n':
 | 
						|
                continue
 | 
						|
            defconfig = line.split()[6] + '_defconfig'
 | 
						|
            if not os.path.exists(os.path.join(CONFIG_DIR, defconfig)):
 | 
						|
                return False
 | 
						|
 | 
						|
    return True
 | 
						|
 | 
						|
### classes ###
 | 
						|
class MaintainersDatabase:
 | 
						|
 | 
						|
    """The database of board status and maintainers."""
 | 
						|
 | 
						|
    def __init__(self):
 | 
						|
        """Create an empty database."""
 | 
						|
        self.database = {}
 | 
						|
 | 
						|
    def get_status(self, target):
 | 
						|
        """Return the status of the given board.
 | 
						|
 | 
						|
        Returns:
 | 
						|
          Either 'Active' or 'Orphan'
 | 
						|
        """
 | 
						|
        if not target in self.database:
 | 
						|
            print >> sys.stderr, "WARNING: no status info for '%s'" % target
 | 
						|
            return '-'
 | 
						|
 | 
						|
        tmp = self.database[target][0]
 | 
						|
        if tmp.startswith('Maintained'):
 | 
						|
            return 'Active'
 | 
						|
        elif tmp.startswith('Orphan'):
 | 
						|
            return 'Orphan'
 | 
						|
        else:
 | 
						|
            print >> sys.stderr, ("WARNING: %s: unknown status for '%s'" %
 | 
						|
                                  (tmp, target))
 | 
						|
            return '-'
 | 
						|
 | 
						|
    def get_maintainers(self, target):
 | 
						|
        """Return the maintainers of the given board.
 | 
						|
 | 
						|
        If the board has two or more maintainers, they are separated
 | 
						|
        with colons.
 | 
						|
        """
 | 
						|
        if not target in self.database:
 | 
						|
            print >> sys.stderr, "WARNING: no maintainers for '%s'" % target
 | 
						|
            return ''
 | 
						|
 | 
						|
        return ':'.join(self.database[target][1])
 | 
						|
 | 
						|
    def parse_file(self, file):
 | 
						|
        """Parse the given MAINTAINERS file.
 | 
						|
 | 
						|
        This method parses MAINTAINERS and add board status and
 | 
						|
        maintainers information to the database.
 | 
						|
 | 
						|
        Arguments:
 | 
						|
          file: MAINTAINERS file to be parsed
 | 
						|
        """
 | 
						|
        targets = []
 | 
						|
        maintainers = []
 | 
						|
        status = '-'
 | 
						|
        for line in open(file):
 | 
						|
            tag, rest = line[:2], line[2:].strip()
 | 
						|
            if tag == 'M:':
 | 
						|
                maintainers.append(rest)
 | 
						|
            elif tag == 'F:':
 | 
						|
                # expand wildcard and filter by 'configs/*_defconfig'
 | 
						|
                for f in glob.glob(rest):
 | 
						|
                    front, match, rear = f.partition('configs/')
 | 
						|
                    if not front and match:
 | 
						|
                        front, match, rear = rear.rpartition('_defconfig')
 | 
						|
                        if match and not rear:
 | 
						|
                            targets.append(front)
 | 
						|
            elif tag == 'S:':
 | 
						|
                status = rest
 | 
						|
            elif line == '\n':
 | 
						|
                for target in targets:
 | 
						|
                    self.database[target] = (status, maintainers)
 | 
						|
                targets = []
 | 
						|
                maintainers = []
 | 
						|
                status = '-'
 | 
						|
        if targets:
 | 
						|
            for target in targets:
 | 
						|
                self.database[target] = (status, maintainers)
 | 
						|
 | 
						|
class DotConfigParser:
 | 
						|
 | 
						|
    """A parser of .config file.
 | 
						|
 | 
						|
    Each line of the output should have the form of:
 | 
						|
    Status, Arch, CPU, SoC, Vendor, Board, Target, Options, Maintainers
 | 
						|
    Most of them are extracted from .config file.
 | 
						|
    MAINTAINERS files are also consulted for Status and Maintainers fields.
 | 
						|
    """
 | 
						|
 | 
						|
    re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
 | 
						|
    re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
 | 
						|
    re_soc = re.compile(r'CONFIG_SYS_SOC="(.*)"')
 | 
						|
    re_vendor = re.compile(r'CONFIG_SYS_VENDOR="(.*)"')
 | 
						|
    re_board = re.compile(r'CONFIG_SYS_BOARD="(.*)"')
 | 
						|
    re_config = re.compile(r'CONFIG_SYS_CONFIG_NAME="(.*)"')
 | 
						|
    re_options = re.compile(r'CONFIG_SYS_EXTRA_OPTIONS="(.*)"')
 | 
						|
    re_list = (('arch', re_arch), ('cpu', re_cpu), ('soc', re_soc),
 | 
						|
               ('vendor', re_vendor), ('board', re_board),
 | 
						|
               ('config', re_config), ('options', re_options))
 | 
						|
    must_fields = ('arch', 'config')
 | 
						|
 | 
						|
    def __init__(self, build_dir, output, maintainers_database):
 | 
						|
        """Create a new .config perser.
 | 
						|
 | 
						|
        Arguments:
 | 
						|
          build_dir: Build directory where .config is located
 | 
						|
          output: File object which the result is written to
 | 
						|
          maintainers_database: An instance of class MaintainersDatabase
 | 
						|
        """
 | 
						|
        self.dotconfig = os.path.join(build_dir, '.config')
 | 
						|
        self.output = output
 | 
						|
        self.database = maintainers_database
 | 
						|
 | 
						|
    def parse(self, defconfig):
 | 
						|
        """Parse .config file and output one-line database for the given board.
 | 
						|
 | 
						|
        Arguments:
 | 
						|
          defconfig: Board (defconfig) name
 | 
						|
        """
 | 
						|
        fields = {}
 | 
						|
        for line in open(self.dotconfig):
 | 
						|
            if not line.startswith('CONFIG_SYS_'):
 | 
						|
                continue
 | 
						|
            for (key, pattern) in self.re_list:
 | 
						|
                m = pattern.match(line)
 | 
						|
                if m and m.group(1):
 | 
						|
                    fields[key] = m.group(1)
 | 
						|
                    break
 | 
						|
 | 
						|
        # sanity check of '.config' file
 | 
						|
        for field in self.must_fields:
 | 
						|
            if not field in fields:
 | 
						|
                print >> sys.stderr, (
 | 
						|
                    "WARNING: '%s' is not defined in '%s'. Skip." %
 | 
						|
                    (field, defconfig))
 | 
						|
                return
 | 
						|
 | 
						|
        # fix-up for aarch64
 | 
						|
        if fields['arch'] == 'arm' and 'cpu' in fields:
 | 
						|
            if fields['cpu'] == 'armv8':
 | 
						|
                fields['arch'] = 'aarch64'
 | 
						|
 | 
						|
        target, match, rear = defconfig.partition('_defconfig')
 | 
						|
        assert match and not rear, \
 | 
						|
                                '%s : invalid defconfig file name' % defconfig
 | 
						|
 | 
						|
        fields['status'] = self.database.get_status(target)
 | 
						|
        fields['maintainers'] = self.database.get_maintainers(target)
 | 
						|
 | 
						|
        if 'options' in fields:
 | 
						|
            options = fields['config'] + ':' + \
 | 
						|
                      fields['options'].replace(r'\"', '"')
 | 
						|
        elif fields['config'] != target:
 | 
						|
            options = fields['config']
 | 
						|
        else:
 | 
						|
            options = '-'
 | 
						|
 | 
						|
        self.output.write((' '.join(['%s'] * 9) + '\n')  %
 | 
						|
                          (fields['status'],
 | 
						|
                           fields['arch'],
 | 
						|
                           fields.get('cpu', '-'),
 | 
						|
                           fields.get('soc', '-'),
 | 
						|
                           fields.get('vendor', '-'),
 | 
						|
                           fields.get('board', '-'),
 | 
						|
                           target,
 | 
						|
                           options,
 | 
						|
                           fields['maintainers']))
 | 
						|
 | 
						|
class Slot:
 | 
						|
 | 
						|
    """A slot to store a subprocess.
 | 
						|
 | 
						|
    Each instance of this class handles one subprocess.
 | 
						|
    This class is useful to control multiple processes
 | 
						|
    for faster processing.
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, output, maintainers_database, devnull, make_cmd):
 | 
						|
        """Create a new slot.
 | 
						|
 | 
						|
        Arguments:
 | 
						|
          output: File object which the result is written to
 | 
						|
          maintainers_database: An instance of class MaintainersDatabase
 | 
						|
          devnull: file object of 'dev/null'
 | 
						|
          make_cmd: the command name of Make
 | 
						|
        """
 | 
						|
        self.build_dir = tempfile.mkdtemp()
 | 
						|
        self.devnull = devnull
 | 
						|
        self.ps = subprocess.Popen([make_cmd, 'O=' + self.build_dir,
 | 
						|
                                    'allnoconfig'], stdout=devnull)
 | 
						|
        self.occupied = True
 | 
						|
        self.parser = DotConfigParser(self.build_dir, output,
 | 
						|
                                      maintainers_database)
 | 
						|
        self.env = os.environ.copy()
 | 
						|
        self.env['srctree'] = os.getcwd()
 | 
						|
        self.env['UBOOTVERSION'] = 'dummy'
 | 
						|
        self.env['KCONFIG_OBJDIR'] = ''
 | 
						|
 | 
						|
    def __del__(self):
 | 
						|
        """Delete the working directory"""
 | 
						|
        if not self.occupied:
 | 
						|
            while self.ps.poll() == None:
 | 
						|
                pass
 | 
						|
        shutil.rmtree(self.build_dir)
 | 
						|
 | 
						|
    def add(self, defconfig):
 | 
						|
        """Add a new subprocess to the slot.
 | 
						|
 | 
						|
        Fails if the slot is occupied, that is, the current subprocess
 | 
						|
        is still running.
 | 
						|
 | 
						|
        Arguments:
 | 
						|
          defconfig: Board (defconfig) name
 | 
						|
 | 
						|
        Returns:
 | 
						|
          Return True on success or False on fail
 | 
						|
        """
 | 
						|
        if self.occupied:
 | 
						|
            return False
 | 
						|
 | 
						|
        with open(os.path.join(self.build_dir, '.tmp_defconfig'), 'w') as f:
 | 
						|
            for line in open(os.path.join(CONFIG_DIR, defconfig)):
 | 
						|
                colon = line.find(':CONFIG_')
 | 
						|
                if colon == -1:
 | 
						|
                    f.write(line)
 | 
						|
                else:
 | 
						|
                    f.write(line[colon + 1:])
 | 
						|
 | 
						|
        self.ps = subprocess.Popen([os.path.join('scripts', 'kconfig', 'conf'),
 | 
						|
                                    '--defconfig=.tmp_defconfig', 'Kconfig'],
 | 
						|
                                   stdout=self.devnull,
 | 
						|
                                   cwd=self.build_dir,
 | 
						|
                                   env=self.env)
 | 
						|
 | 
						|
        self.defconfig = defconfig
 | 
						|
        self.occupied = True
 | 
						|
        return True
 | 
						|
 | 
						|
    def wait(self):
 | 
						|
        """Wait until the current subprocess finishes."""
 | 
						|
        while self.occupied and self.ps.poll() == None:
 | 
						|
            time.sleep(SLEEP_TIME)
 | 
						|
        self.occupied = False
 | 
						|
 | 
						|
    def poll(self):
 | 
						|
        """Check if the subprocess is running and invoke the .config
 | 
						|
        parser if the subprocess is terminated.
 | 
						|
 | 
						|
        Returns:
 | 
						|
          Return True if the subprocess is terminated, False otherwise
 | 
						|
        """
 | 
						|
        if not self.occupied:
 | 
						|
            return True
 | 
						|
        if self.ps.poll() == None:
 | 
						|
            return False
 | 
						|
        if self.ps.poll() == 0:
 | 
						|
            self.parser.parse(self.defconfig)
 | 
						|
        else:
 | 
						|
            print >> sys.stderr, ("WARNING: failed to process '%s'. skip." %
 | 
						|
                                  self.defconfig)
 | 
						|
        self.occupied = False
 | 
						|
        return True
 | 
						|
 | 
						|
class Slots:
 | 
						|
 | 
						|
    """Controller of the array of subprocess slots."""
 | 
						|
 | 
						|
    def __init__(self, jobs, output, maintainers_database):
 | 
						|
        """Create a new slots controller.
 | 
						|
 | 
						|
        Arguments:
 | 
						|
          jobs: A number of slots to instantiate
 | 
						|
          output: File object which the result is written to
 | 
						|
          maintainers_database: An instance of class MaintainersDatabase
 | 
						|
        """
 | 
						|
        self.slots = []
 | 
						|
        devnull = get_devnull()
 | 
						|
        make_cmd = get_make_cmd()
 | 
						|
        for i in range(jobs):
 | 
						|
            self.slots.append(Slot(output, maintainers_database,
 | 
						|
                                   devnull, make_cmd))
 | 
						|
        for slot in self.slots:
 | 
						|
            slot.wait()
 | 
						|
 | 
						|
    def add(self, defconfig):
 | 
						|
        """Add a new subprocess if a vacant slot is available.
 | 
						|
 | 
						|
        Arguments:
 | 
						|
          defconfig: Board (defconfig) name
 | 
						|
 | 
						|
        Returns:
 | 
						|
          Return True on success or False on fail
 | 
						|
        """
 | 
						|
        for slot in self.slots:
 | 
						|
            if slot.add(defconfig):
 | 
						|
                return True
 | 
						|
        return False
 | 
						|
 | 
						|
    def available(self):
 | 
						|
        """Check if there is a vacant slot.
 | 
						|
 | 
						|
        Returns:
 | 
						|
          Return True if a vacant slot is found, False if all slots are full
 | 
						|
        """
 | 
						|
        for slot in self.slots:
 | 
						|
            if slot.poll():
 | 
						|
                return True
 | 
						|
        return False
 | 
						|
 | 
						|
    def empty(self):
 | 
						|
        """Check if all slots are vacant.
 | 
						|
 | 
						|
        Returns:
 | 
						|
          Return True if all slots are vacant, False if at least one slot
 | 
						|
          is running
 | 
						|
        """
 | 
						|
        ret = True
 | 
						|
        for slot in self.slots:
 | 
						|
            if not slot.poll():
 | 
						|
                ret = False
 | 
						|
        return ret
 | 
						|
 | 
						|
class Indicator:
 | 
						|
 | 
						|
    """A class to control the progress indicator."""
 | 
						|
 | 
						|
    MIN_WIDTH = 15
 | 
						|
    MAX_WIDTH = 70
 | 
						|
 | 
						|
    def __init__(self, total):
 | 
						|
        """Create an instance.
 | 
						|
 | 
						|
        Arguments:
 | 
						|
          total: A number of boards
 | 
						|
        """
 | 
						|
        self.total = total
 | 
						|
        self.cur = 0
 | 
						|
        width = get_terminal_columns()
 | 
						|
        width = min(width, self.MAX_WIDTH)
 | 
						|
        width -= self.MIN_WIDTH
 | 
						|
        if width > 0:
 | 
						|
            self.enabled = True
 | 
						|
        else:
 | 
						|
            self.enabled = False
 | 
						|
        self.width = width
 | 
						|
 | 
						|
    def inc(self):
 | 
						|
        """Increment the counter and show the progress bar."""
 | 
						|
        if not self.enabled:
 | 
						|
            return
 | 
						|
        self.cur += 1
 | 
						|
        arrow_len = self.width * self.cur // self.total
 | 
						|
        msg = '%4d/%d [' % (self.cur, self.total)
 | 
						|
        msg += '=' * arrow_len + '>' + ' ' * (self.width - arrow_len) + ']'
 | 
						|
        sys.stdout.write('\r' + msg)
 | 
						|
        sys.stdout.flush()
 | 
						|
 | 
						|
class BoardsFileGenerator:
 | 
						|
 | 
						|
    """Generator of boards.cfg."""
 | 
						|
 | 
						|
    def __init__(self):
 | 
						|
        """Prepare basic things for generating boards.cfg."""
 | 
						|
        # All the defconfig files to be processed
 | 
						|
        defconfigs = []
 | 
						|
        for (dirpath, dirnames, filenames) in os.walk(CONFIG_DIR):
 | 
						|
            dirpath = dirpath[len(CONFIG_DIR) + 1:]
 | 
						|
            for filename in fnmatch.filter(filenames, '*_defconfig'):
 | 
						|
                if fnmatch.fnmatch(filename, '.*'):
 | 
						|
                    continue
 | 
						|
                defconfigs.append(os.path.join(dirpath, filename))
 | 
						|
        self.defconfigs = defconfigs
 | 
						|
        self.indicator = Indicator(len(defconfigs))
 | 
						|
 | 
						|
        # Parse all the MAINTAINERS files
 | 
						|
        maintainers_database = MaintainersDatabase()
 | 
						|
        for (dirpath, dirnames, filenames) in os.walk('.'):
 | 
						|
            if 'MAINTAINERS' in filenames:
 | 
						|
                maintainers_database.parse_file(os.path.join(dirpath,
 | 
						|
                                                             'MAINTAINERS'))
 | 
						|
        self.maintainers_database = maintainers_database
 | 
						|
 | 
						|
    def __del__(self):
 | 
						|
        """Delete the incomplete boards.cfg
 | 
						|
 | 
						|
        This destructor deletes boards.cfg if the private member 'in_progress'
 | 
						|
        is defined as True.  The 'in_progress' member is set to True at the
 | 
						|
        beginning of the generate() method and set to False at its end.
 | 
						|
        So, in_progress==True means generating boards.cfg was terminated
 | 
						|
        on the way.
 | 
						|
        """
 | 
						|
 | 
						|
        if hasattr(self, 'in_progress') and self.in_progress:
 | 
						|
            try:
 | 
						|
                os.remove(BOARD_FILE)
 | 
						|
            except OSError as exception:
 | 
						|
                # Ignore 'No such file or directory' error
 | 
						|
                if exception.errno != errno.ENOENT:
 | 
						|
                    raise
 | 
						|
            print 'Removed incomplete %s' % BOARD_FILE
 | 
						|
 | 
						|
    def generate(self, jobs):
 | 
						|
        """Generate boards.cfg
 | 
						|
 | 
						|
        This method sets the 'in_progress' member to True at the beginning
 | 
						|
        and sets it to False on success.  The boards.cfg should not be
 | 
						|
        touched before/after this method because 'in_progress' is used
 | 
						|
        to detect the incomplete boards.cfg.
 | 
						|
 | 
						|
        Arguments:
 | 
						|
          jobs: The number of jobs to run simultaneously
 | 
						|
        """
 | 
						|
 | 
						|
        self.in_progress = True
 | 
						|
        print 'Generating %s ...  (jobs: %d)' % (BOARD_FILE, jobs)
 | 
						|
 | 
						|
        # Output lines should be piped into the reformat tool
 | 
						|
        reformat_process = subprocess.Popen(REFORMAT_CMD,
 | 
						|
                                            stdin=subprocess.PIPE,
 | 
						|
                                            stdout=open(BOARD_FILE, 'w'))
 | 
						|
        pipe = reformat_process.stdin
 | 
						|
        pipe.write(COMMENT_BLOCK)
 | 
						|
 | 
						|
        slots = Slots(jobs, pipe, self.maintainers_database)
 | 
						|
 | 
						|
        # Main loop to process defconfig files:
 | 
						|
        #  Add a new subprocess into a vacant slot.
 | 
						|
        #  Sleep if there is no available slot.
 | 
						|
        for defconfig in self.defconfigs:
 | 
						|
            while not slots.add(defconfig):
 | 
						|
                while not slots.available():
 | 
						|
                    # No available slot: sleep for a while
 | 
						|
                    time.sleep(SLEEP_TIME)
 | 
						|
            self.indicator.inc()
 | 
						|
 | 
						|
        # wait until all the subprocesses finish
 | 
						|
        while not slots.empty():
 | 
						|
            time.sleep(SLEEP_TIME)
 | 
						|
        print ''
 | 
						|
 | 
						|
        # wait until the reformat tool finishes
 | 
						|
        reformat_process.communicate()
 | 
						|
        if reformat_process.returncode != 0:
 | 
						|
            sys.exit('"%s" failed' % REFORMAT_CMD[0])
 | 
						|
 | 
						|
        self.in_progress = False
 | 
						|
 | 
						|
def gen_boards_cfg(jobs=1, force=False):
 | 
						|
    """Generate boards.cfg file.
 | 
						|
 | 
						|
    The incomplete boards.cfg is deleted if an error (including
 | 
						|
    the termination by the keyboard interrupt) occurs on the halfway.
 | 
						|
 | 
						|
    Arguments:
 | 
						|
      jobs: The number of jobs to run simultaneously
 | 
						|
    """
 | 
						|
    check_top_directory()
 | 
						|
    if not force and output_is_new():
 | 
						|
        print "%s is up to date. Nothing to do." % BOARD_FILE
 | 
						|
        sys.exit(0)
 | 
						|
 | 
						|
    generator = BoardsFileGenerator()
 | 
						|
    generator.generate(jobs)
 | 
						|
 | 
						|
def main():
 | 
						|
    parser = optparse.OptionParser()
 | 
						|
    # Add options here
 | 
						|
    parser.add_option('-j', '--jobs',
 | 
						|
                      help='the number of jobs to run simultaneously')
 | 
						|
    parser.add_option('-f', '--force', action="store_true", default=False,
 | 
						|
                      help='regenerate the output even if it is new')
 | 
						|
    (options, args) = parser.parse_args()
 | 
						|
 | 
						|
    if options.jobs:
 | 
						|
        try:
 | 
						|
            jobs = int(options.jobs)
 | 
						|
        except ValueError:
 | 
						|
            sys.exit('Option -j (--jobs) takes a number')
 | 
						|
    else:
 | 
						|
        try:
 | 
						|
            jobs = int(subprocess.Popen(['getconf', '_NPROCESSORS_ONLN'],
 | 
						|
                                     stdout=subprocess.PIPE).communicate()[0])
 | 
						|
        except (OSError, ValueError):
 | 
						|
            print 'info: failed to get the number of CPUs. Set jobs to 1'
 | 
						|
            jobs = 1
 | 
						|
 | 
						|
    gen_boards_cfg(jobs, force=options.force)
 | 
						|
 | 
						|
if __name__ == '__main__':
 | 
						|
    main()
 |