xref: /openbmc/u-boot/tools/genboardscfg.py (revision 608017026571ea7ac29e5da200efeac71fb42d57)
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