+class KconfigScanner:
+
+ """Kconfig scanner."""
+
+ ### constant variable only used in this class ###
+ _SYMBOL_TABLE = {
+ 'arch' : 'SYS_ARCH',
+ 'cpu' : 'SYS_CPU',
+ 'soc' : 'SYS_SOC',
+ 'vendor' : 'SYS_VENDOR',
+ 'board' : 'SYS_BOARD',
+ 'config' : 'SYS_CONFIG_NAME',
+ 'options' : 'SYS_EXTRA_OPTIONS'
+ }
+
+ def __init__(self):
+ """Scan all the Kconfig files and create a Config object."""
+ # Define environment variables referenced from Kconfig
+ os.environ['srctree'] = os.getcwd()
+ os.environ['UBOOTVERSION'] = 'dummy'
+ os.environ['KCONFIG_OBJDIR'] = ''
+ self._conf = kconfiglib.Config(print_warnings=False)
+
+ def __del__(self):
+ """Delete a leftover temporary file before exit.
+
+ The scan() method of this class creates a temporay file and deletes
+ it on success. If scan() method throws an exception on the way,
+ the temporary file might be left over. In that case, it should be
+ deleted in this destructor.
+ """
+ if hasattr(self, '_tmpfile') and self._tmpfile:
+ try_remove(self._tmpfile)
+
+ def scan(self, defconfig):
+ """Load a defconfig file to obtain board parameters.
+
+ Arguments:
+ defconfig: path to the defconfig file to be processed
+
+ Returns:
+ A dictionary of board parameters. It has a form of:
+ {
+ 'arch': <arch_name>,
+ 'cpu': <cpu_name>,
+ 'soc': <soc_name>,
+ 'vendor': <vendor_name>,
+ 'board': <board_name>,
+ 'target': <target_name>,
+ 'config': <config_header_name>,
+ 'options': <extra_options>
+ }
+ """
+ # strip special prefixes and save it in a temporary file
+ fd, self._tmpfile = tempfile.mkstemp()
+ with os.fdopen(fd, 'w') as f:
+ for line in open(defconfig):
+ colon = line.find(':CONFIG_')
+ if colon == -1:
+ f.write(line)
+ else:
+ f.write(line[colon + 1:])
+
+ warnings = self._conf.load_config(self._tmpfile)
+ if warnings:
+ for warning in warnings:
+ print '%s: %s' % (defconfig, warning)
+
+ try_remove(self._tmpfile)
+ self._tmpfile = None
+
+ params = {}
+
+ # Get the value of CONFIG_SYS_ARCH, CONFIG_SYS_CPU, ... etc.
+ # Set '-' if the value is empty.
+ for key, symbol in self._SYMBOL_TABLE.items():
+ value = self._conf.get_symbol(symbol).get_value()
+ if value:
+ params[key] = value
+ else:
+ params[key] = '-'
+
+ defconfig = os.path.basename(defconfig)
+ params['target'], match, rear = defconfig.partition('_defconfig')
+ assert match and not rear, '%s : invalid defconfig' % defconfig
+
+ # fix-up for aarch64
+ if params['arch'] == 'arm' and params['cpu'] == 'armv8':
+ params['arch'] = 'aarch64'
+
+ # fix-up options field. It should have the form:
+ # <config name>[:comma separated config options]
+ if params['options'] != '-':
+ params['options'] = params['config'] + ':' + \
+ params['options'].replace(r'\"', '"')
+ elif params['config'] != params['target']:
+ params['options'] = params['config']
+
+ return params
+
+def scan_defconfigs_for_multiprocess(queue, defconfigs):
+ """Scan defconfig files and queue their board parameters
+
+ This function is intended to be passed to
+ multiprocessing.Process() constructor.
+
+ Arguments:
+ queue: An instance of multiprocessing.Queue().
+ The resulting board parameters are written into it.
+ defconfigs: A sequence of defconfig files to be scanned.
+ """
+ kconf_scanner = KconfigScanner()
+ for defconfig in defconfigs:
+ queue.put(kconf_scanner.scan(defconfig))
+
+def read_queues(queues, params_list):
+ """Read the queues and append the data to the paramers list"""
+ for q in queues:
+ while not q.empty():
+ params_list.append(q.get())
+
+def scan_defconfigs(jobs=1):
+ """Collect board parameters for all defconfig files.
+
+ This function invokes multiple processes for faster processing.
+
+ Arguments:
+ jobs: The number of jobs to run simultaneously
+ """
+ all_defconfigs = []
+ for (dirpath, dirnames, filenames) in os.walk(CONFIG_DIR):
+ for filename in fnmatch.filter(filenames, '*_defconfig'):
+ if fnmatch.fnmatch(filename, '.*'):
+ continue
+ all_defconfigs.append(os.path.join(dirpath, filename))
+
+ total_boards = len(all_defconfigs)
+ processes = []
+ queues = []
+ for i in range(jobs):
+ defconfigs = all_defconfigs[total_boards * i / jobs :
+ total_boards * (i + 1) / jobs]
+ q = multiprocessing.Queue(maxsize=-1)
+ p = multiprocessing.Process(target=scan_defconfigs_for_multiprocess,
+ args=(q, defconfigs))
+ p.start()
+ processes.append(p)
+ queues.append(q)
+
+ # The resulting data should be accumulated to this list
+ params_list = []
+
+ # Data in the queues should be retrieved preriodically.
+ # Otherwise, the queues would become full and subprocesses would get stuck.
+ while any([p.is_alive() for p in processes]):
+ read_queues(queues, params_list)
+ # sleep for a while until the queues are filled
+ time.sleep(SLEEP_TIME)
+
+ # Joining subprocesses just in case
+ # (All subprocesses should already have been finished)
+ for p in processes:
+ p.join()
+
+ # retrieve leftover data
+ read_queues(queues, params_list)
+
+ return params_list
+