]> git.sur5r.net Git - u-boot/blob - tools/moveconfig.py
tools: moveconfig: check compilers before starting defconfig walk
[u-boot] / tools / moveconfig.py
1 #!/usr/bin/env python2
2 #
3 # Author: Masahiro Yamada <yamada.masahiro@socionext.com>
4 #
5 # SPDX-License-Identifier:      GPL-2.0+
6 #
7
8 """
9 Move config options from headers to defconfig files.
10
11 Since Kconfig was introduced to U-Boot, we have worked on moving
12 config options from headers to Kconfig (defconfig).
13
14 This tool intends to help this tremendous work.
15
16
17 Usage
18 -----
19
20 This tool takes one input file.  (let's say 'recipe' file here.)
21 The recipe describes the list of config options you want to move.
22 Each line takes the form:
23 <config_name> <type> <default>
24 (the fields must be separated with whitespaces.)
25
26 <config_name> is the name of config option.
27
28 <type> is the type of the option.  It must be one of bool, tristate,
29 string, int, and hex.
30
31 <default> is the default value of the option.  It must be appropriate
32 value corresponding to the option type.  It must be either y or n for
33 the bool type.  Tristate options can also take m (although U-Boot has
34 not supported the module feature).
35
36 You can add two or more lines in the recipe file, so you can move
37 multiple options at once.
38
39 Let's say, for example, you want to move CONFIG_CMD_USB and
40 CONFIG_SYS_TEXT_BASE.
41
42 The type should be bool, hex, respectively.  So, the recipe file
43 should look like this:
44
45   $ cat recipe
46   CONFIG_CMD_USB bool n
47   CONFIG_SYS_TEXT_BASE hex 0x00000000
48
49 Next you must edit the Kconfig to add the menu entries for the configs
50 you are moving.
51
52 And then run this tool giving the file name of the recipe
53
54   $ tools/moveconfig.py recipe
55
56 The tool walks through all the defconfig files to move the config
57 options specified by the recipe file.
58
59 The log is also displayed on the terminal.
60
61 Each line is printed in the format
62 <defconfig_name>   :  <action>
63
64 <defconfig_name> is the name of the defconfig
65 (without the suffix _defconfig).
66
67 <action> shows what the tool did for that defconfig.
68 It looks like one of the followings:
69
70  - Move 'CONFIG_... '
71    This config option was moved to the defconfig
72
73  - Default value 'CONFIG_...'.  Do nothing.
74    The value of this option is the same as default.
75    We do not have to add it to the defconfig.
76
77  - 'CONFIG_...' already exists in Kconfig.  Do nothing.
78    This config option is already defined in Kconfig.
79    We do not need/want to touch it.
80
81  - Undefined.  Do nothing.
82    This config option was not found in the config header.
83    Nothing to do.
84
85  - Compiler is missing.  Do nothing.
86    The compiler specified for this architecture was not found
87    in your PATH environment.
88    (If -e option is passed, the tool exits immediately.)
89
90  - Failed to process.
91    An error occurred during processing this defconfig.  Skipped.
92    (If -e option is passed, the tool exits immediately on error.)
93
94 Finally, you will be asked, Clean up headers? [y/n]:
95
96 If you say 'y' here, the unnecessary config defines are removed
97 from the config headers (include/configs/*.h).
98 It just uses the regex method, so you should not rely on it.
99 Just in case, please do 'git diff' to see what happened.
100
101
102 How does it works?
103 ------------------
104
105 This tool runs configuration and builds include/autoconf.mk for every
106 defconfig.  The config options defined in Kconfig appear in the .config
107 file (unless they are hidden because of unmet dependency.)
108 On the other hand, the config options defined by board headers are seen
109 in include/autoconf.mk.  The tool looks for the specified options in both
110 of them to decide the appropriate action for the options.  If the option
111 is found in the .config or the value is the same as the specified default,
112 the option does not need to be touched.  If the option is found in
113 include/autoconf.mk, but not in the .config, and the value is different
114 from the default, the tools adds the option to the defconfig.
115
116 For faster processing, this tool handles multi-threading.  It creates
117 separate build directories where the out-of-tree build is run.  The
118 temporary build directories are automatically created and deleted as
119 needed.  The number of threads are chosen based on the number of the CPU
120 cores of your system although you can change it via -j (--jobs) option.
121
122
123 Toolchains
124 ----------
125
126 Appropriate toolchain are necessary to generate include/autoconf.mk
127 for all the architectures supported by U-Boot.  Most of them are available
128 at the kernel.org site, some are not provided by kernel.org.
129
130 The default per-arch CROSS_COMPILE used by this tool is specified by
131 the list below, CROSS_COMPILE.  You may wish to update the list to
132 use your own.  Instead of modifying the list directly, you can give
133 them via environments.
134
135
136 Available options
137 -----------------
138
139  -c, --color
140    Surround each portion of the log with escape sequences to display it
141    in color on the terminal.
142
143  -d, --defconfigs
144   Specify a file containing a list of defconfigs to move
145
146  -n, --dry-run
147    Peform a trial run that does not make any changes.  It is useful to
148    see what is going to happen before one actually runs it.
149
150  -e, --exit-on-error
151    Exit immediately if Make exits with a non-zero status while processing
152    a defconfig file.
153
154  -H, --headers-only
155    Only cleanup the headers; skip the defconfig processing
156
157  -j, --jobs
158    Specify the number of threads to run simultaneously.  If not specified,
159    the number of threads is the same as the number of CPU cores.
160
161  -v, --verbose
162    Show any build errors as boards are built
163
164 To see the complete list of supported options, run
165
166   $ tools/moveconfig.py -h
167
168 """
169
170 import fnmatch
171 import multiprocessing
172 import optparse
173 import os
174 import re
175 import shutil
176 import subprocess
177 import sys
178 import tempfile
179 import time
180
181 SHOW_GNU_MAKE = 'scripts/show-gnu-make'
182 SLEEP_TIME=0.03
183
184 # Here is the list of cross-tools I use.
185 # Most of them are available at kernel.org
186 # (https://www.kernel.org/pub/tools/crosstool/files/bin/), except the followings:
187 # arc: https://github.com/foss-for-synopsys-dwc-arc-processors/toolchain/releases
188 # blackfin: http://sourceforge.net/projects/adi-toolchain/files/
189 # nds32: http://osdk.andestech.com/packages/nds32le-linux-glibc-v1.tgz
190 # nios2: https://sourcery.mentor.com/GNUToolchain/subscription42545
191 # sh: http://sourcery.mentor.com/public/gnu_toolchain/sh-linux-gnu
192 #
193 # openrisc kernel.org toolchain is out of date, download latest one from
194 # http://opencores.org/or1k/OpenRISC_GNU_tool_chain#Prebuilt_versions
195 CROSS_COMPILE = {
196     'arc': 'arc-linux-',
197     'aarch64': 'aarch64-linux-',
198     'arm': 'arm-unknown-linux-gnueabi-',
199     'avr32': 'avr32-linux-',
200     'blackfin': 'bfin-elf-',
201     'm68k': 'm68k-linux-',
202     'microblaze': 'microblaze-linux-',
203     'mips': 'mips-linux-',
204     'nds32': 'nds32le-linux-',
205     'nios2': 'nios2-linux-gnu-',
206     'openrisc': 'or1k-elf-',
207     'powerpc': 'powerpc-linux-',
208     'sh': 'sh-linux-gnu-',
209     'sparc': 'sparc-linux-',
210     'x86': 'i386-linux-'
211 }
212
213 STATE_IDLE = 0
214 STATE_DEFCONFIG = 1
215 STATE_AUTOCONF = 2
216 STATE_SAVEDEFCONFIG = 3
217
218 ACTION_MOVE = 0
219 ACTION_DEFAULT_VALUE = 1
220 ACTION_ALREADY_EXIST = 2
221 ACTION_UNDEFINED = 3
222
223 COLOR_BLACK        = '0;30'
224 COLOR_RED          = '0;31'
225 COLOR_GREEN        = '0;32'
226 COLOR_BROWN        = '0;33'
227 COLOR_BLUE         = '0;34'
228 COLOR_PURPLE       = '0;35'
229 COLOR_CYAN         = '0;36'
230 COLOR_LIGHT_GRAY   = '0;37'
231 COLOR_DARK_GRAY    = '1;30'
232 COLOR_LIGHT_RED    = '1;31'
233 COLOR_LIGHT_GREEN  = '1;32'
234 COLOR_YELLOW       = '1;33'
235 COLOR_LIGHT_BLUE   = '1;34'
236 COLOR_LIGHT_PURPLE = '1;35'
237 COLOR_LIGHT_CYAN   = '1;36'
238 COLOR_WHITE        = '1;37'
239
240 ### helper functions ###
241 def get_devnull():
242     """Get the file object of '/dev/null' device."""
243     try:
244         devnull = subprocess.DEVNULL # py3k
245     except AttributeError:
246         devnull = open(os.devnull, 'wb')
247     return devnull
248
249 def check_top_directory():
250     """Exit if we are not at the top of source directory."""
251     for f in ('README', 'Licenses'):
252         if not os.path.exists(f):
253             sys.exit('Please run at the top of source directory.')
254
255 def get_make_cmd():
256     """Get the command name of GNU Make.
257
258     U-Boot needs GNU Make for building, but the command name is not
259     necessarily "make". (for example, "gmake" on FreeBSD).
260     Returns the most appropriate command name on your system.
261     """
262     process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE)
263     ret = process.communicate()
264     if process.returncode:
265         sys.exit('GNU Make not found')
266     return ret[0].rstrip()
267
268 def color_text(color_enabled, color, string):
269     """Return colored string."""
270     if color_enabled:
271         return '\033[' + color + 'm' + string + '\033[0m'
272     else:
273         return string
274
275 def log_msg(color_enabled, color, defconfig, msg):
276     """Return the formated line for the log."""
277     return defconfig[:-len('_defconfig')].ljust(37) + ': ' + \
278         color_text(color_enabled, color, msg) + '\n'
279
280 def update_cross_compile(color_enabled):
281     """Update per-arch CROSS_COMPILE via environment variables
282
283     The default CROSS_COMPILE values are available
284     in the CROSS_COMPILE list above.
285
286     You can override them via environment variables
287     CROSS_COMPILE_{ARCH}.
288
289     For example, if you want to override toolchain prefixes
290     for ARM and PowerPC, you can do as follows in your shell:
291
292     export CROSS_COMPILE_ARM=...
293     export CROSS_COMPILE_POWERPC=...
294
295     Then, this function checks if specified compilers really exist in your
296     PATH environment.
297     """
298     archs = []
299
300     for arch in os.listdir('arch'):
301         if os.path.exists(os.path.join('arch', arch, 'Makefile')):
302             archs.append(arch)
303
304     # arm64 is a special case
305     archs.append('aarch64')
306
307     for arch in archs:
308         env = 'CROSS_COMPILE_' + arch.upper()
309         cross_compile = os.environ.get(env)
310         if not cross_compile:
311             cross_compile = CROSS_COMPILE.get(arch, '')
312
313         for path in os.environ["PATH"].split(os.pathsep):
314             gcc_path = os.path.join(path, cross_compile + 'gcc')
315             if os.path.isfile(gcc_path) and os.access(gcc_path, os.X_OK):
316                 break
317         else:
318             print >> sys.stderr, color_text(color_enabled, COLOR_YELLOW,
319                  'warning: %sgcc: not found in PATH.  %s architecture boards will be skipped'
320                                             % (cross_compile, arch))
321             cross_compile = None
322
323         CROSS_COMPILE[arch] = cross_compile
324
325 def cleanup_one_header(header_path, patterns, dry_run):
326     """Clean regex-matched lines away from a file.
327
328     Arguments:
329       header_path: path to the cleaned file.
330       patterns: list of regex patterns.  Any lines matching to these
331                 patterns are deleted.
332       dry_run: make no changes, but still display log.
333     """
334     with open(header_path) as f:
335         lines = f.readlines()
336
337     matched = []
338     for i, line in enumerate(lines):
339         for pattern in patterns:
340             m = pattern.search(line)
341             if m:
342                 print '%s: %s: %s' % (header_path, i + 1, line),
343                 matched.append(i)
344                 break
345
346     if dry_run or not matched:
347         return
348
349     with open(header_path, 'w') as f:
350         for i, line in enumerate(lines):
351             if not i in matched:
352                 f.write(line)
353
354 def cleanup_headers(config_attrs, dry_run):
355     """Delete config defines from board headers.
356
357     Arguments:
358       config_attrs: A list of dictionaris, each of them includes the name,
359                     the type, and the default value of the target config.
360       dry_run: make no changes, but still display log.
361     """
362     while True:
363         choice = raw_input('Clean up headers? [y/n]: ').lower()
364         print choice
365         if choice == 'y' or choice == 'n':
366             break
367
368     if choice == 'n':
369         return
370
371     patterns = []
372     for config_attr in config_attrs:
373         config = config_attr['config']
374         patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
375         patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
376
377     for dir in 'include', 'arch', 'board':
378         for (dirpath, dirnames, filenames) in os.walk(dir):
379             for filename in filenames:
380                 if not fnmatch.fnmatch(filename, '*~'):
381                     cleanup_one_header(os.path.join(dirpath, filename),
382                                        patterns, dry_run)
383
384 ### classes ###
385 class KconfigParser:
386
387     """A parser of .config and include/autoconf.mk."""
388
389     re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
390     re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
391
392     def __init__(self, config_attrs, options, build_dir):
393         """Create a new parser.
394
395         Arguments:
396           config_attrs: A list of dictionaris, each of them includes the name,
397                         the type, and the default value of the target config.
398           options: option flags.
399           build_dir: Build directory.
400         """
401         self.config_attrs = config_attrs
402         self.options = options
403         self.build_dir = build_dir
404
405     def get_cross_compile(self):
406         """Parse .config file and return CROSS_COMPILE.
407
408         Returns:
409           A string storing the compiler prefix for the architecture.
410           Return a NULL string for architectures that do not require
411           compiler prefix (Sandbox and native build is the case).
412           Return None if the specified compiler is missing in your PATH.
413           Caller should distinguish '' and None.
414         """
415         arch = ''
416         cpu = ''
417         dotconfig = os.path.join(self.build_dir, '.config')
418         for line in open(dotconfig):
419             m = self.re_arch.match(line)
420             if m:
421                 arch = m.group(1)
422                 continue
423             m = self.re_cpu.match(line)
424             if m:
425                 cpu = m.group(1)
426
427         if not arch:
428             return None
429
430         # fix-up for aarch64
431         if arch == 'arm' and cpu == 'armv8':
432             arch = 'aarch64'
433
434         return CROSS_COMPILE.get(arch, None)
435
436     def parse_one_config(self, config_attr, defconfig_lines, autoconf_lines):
437         """Parse .config, defconfig, include/autoconf.mk for one config.
438
439         This function looks for the config options in the lines from
440         defconfig, .config, and include/autoconf.mk in order to decide
441         which action should be taken for this defconfig.
442
443         Arguments:
444           config_attr: A dictionary including the name, the type,
445                        and the default value of the target config.
446           defconfig_lines: lines from the original defconfig file.
447           autoconf_lines: lines from the include/autoconf.mk file.
448
449         Returns:
450           A tupple of the action for this defconfig and the line
451           matched for the config.
452         """
453         config = config_attr['config']
454         not_set = '# %s is not set' % config
455
456         if config_attr['type'] in ('bool', 'tristate') and \
457            config_attr['default'] == 'n':
458             default = not_set
459         else:
460             default = config + '=' + config_attr['default']
461
462         for line in defconfig_lines:
463             line = line.rstrip()
464             if line.startswith(config + '=') or line == not_set:
465                 return (ACTION_ALREADY_EXIST, line)
466
467         if config_attr['type'] in ('bool', 'tristate'):
468             value = not_set
469         else:
470             value = '(undefined)'
471
472         for line in autoconf_lines:
473             line = line.rstrip()
474             if line.startswith(config + '='):
475                 value = line
476                 break
477
478         if value == default:
479             action = ACTION_DEFAULT_VALUE
480         elif value == '(undefined)':
481             action = ACTION_UNDEFINED
482         else:
483             action = ACTION_MOVE
484
485         return (action, value)
486
487     def update_dotconfig(self, defconfig):
488         """Parse files for the config options and update the .config.
489
490         This function parses the given defconfig, the generated .config
491         and include/autoconf.mk searching the target options.
492         Move the config option(s) to the .config as needed.
493         Also, display the log to show what happened to the .config.
494
495         Arguments:
496           defconfig: defconfig name.
497         """
498
499         defconfig_path = os.path.join('configs', defconfig)
500         dotconfig_path = os.path.join(self.build_dir, '.config')
501         autoconf_path = os.path.join(self.build_dir, 'include', 'autoconf.mk')
502         results = []
503
504         with open(defconfig_path) as f:
505             defconfig_lines = f.readlines()
506
507         with open(autoconf_path) as f:
508             autoconf_lines = f.readlines()
509
510         for config_attr in self.config_attrs:
511             result = self.parse_one_config(config_attr, defconfig_lines,
512                                            autoconf_lines)
513             results.append(result)
514
515         log = ''
516
517         for (action, value) in results:
518             if action == ACTION_MOVE:
519                 actlog = "Move '%s'" % value
520                 log_color = COLOR_LIGHT_GREEN
521             elif action == ACTION_DEFAULT_VALUE:
522                 actlog = "Default value '%s'.  Do nothing." % value
523                 log_color = COLOR_LIGHT_BLUE
524             elif action == ACTION_ALREADY_EXIST:
525                 actlog = "'%s' already defined in Kconfig.  Do nothing." % value
526                 log_color = COLOR_LIGHT_PURPLE
527             elif action == ACTION_UNDEFINED:
528                 actlog = "Undefined.  Do nothing."
529                 log_color = COLOR_DARK_GRAY
530             else:
531                 sys.exit("Internal Error. This should not happen.")
532
533             log += log_msg(self.options.color, log_color, defconfig, actlog)
534
535         # Some threads are running in parallel.
536         # Print log in one shot to not mix up logs from different threads.
537         print log,
538
539         with open(dotconfig_path, 'a') as f:
540             for (action, value) in results:
541                 if action == ACTION_MOVE:
542                     f.write(value + '\n')
543
544         os.remove(os.path.join(self.build_dir, 'include', 'config', 'auto.conf'))
545         os.remove(autoconf_path)
546
547 class Slot:
548
549     """A slot to store a subprocess.
550
551     Each instance of this class handles one subprocess.
552     This class is useful to control multiple threads
553     for faster processing.
554     """
555
556     def __init__(self, config_attrs, options, devnull, make_cmd):
557         """Create a new process slot.
558
559         Arguments:
560           config_attrs: A list of dictionaris, each of them includes the name,
561                         the type, and the default value of the target config.
562           options: option flags.
563           devnull: A file object of '/dev/null'.
564           make_cmd: command name of GNU Make.
565         """
566         self.options = options
567         self.build_dir = tempfile.mkdtemp()
568         self.devnull = devnull
569         self.make_cmd = (make_cmd, 'O=' + self.build_dir)
570         self.parser = KconfigParser(config_attrs, options, self.build_dir)
571         self.state = STATE_IDLE
572         self.failed_boards = []
573
574     def __del__(self):
575         """Delete the working directory
576
577         This function makes sure the temporary directory is cleaned away
578         even if Python suddenly dies due to error.  It should be done in here
579         because it is guranteed the destructor is always invoked when the
580         instance of the class gets unreferenced.
581
582         If the subprocess is still running, wait until it finishes.
583         """
584         if self.state != STATE_IDLE:
585             while self.ps.poll() == None:
586                 pass
587         shutil.rmtree(self.build_dir)
588
589     def add(self, defconfig, num, total):
590         """Assign a new subprocess for defconfig and add it to the slot.
591
592         If the slot is vacant, create a new subprocess for processing the
593         given defconfig and add it to the slot.  Just returns False if
594         the slot is occupied (i.e. the current subprocess is still running).
595
596         Arguments:
597           defconfig: defconfig name.
598
599         Returns:
600           Return True on success or False on failure
601         """
602         if self.state != STATE_IDLE:
603             return False
604         cmd = list(self.make_cmd)
605         cmd.append(defconfig)
606         self.ps = subprocess.Popen(cmd, stdout=self.devnull,
607                                    stderr=subprocess.PIPE)
608         self.defconfig = defconfig
609         self.state = STATE_DEFCONFIG
610         self.num = num
611         self.total = total
612         return True
613
614     def poll(self):
615         """Check the status of the subprocess and handle it as needed.
616
617         Returns True if the slot is vacant (i.e. in idle state).
618         If the configuration is successfully finished, assign a new
619         subprocess to build include/autoconf.mk.
620         If include/autoconf.mk is generated, invoke the parser to
621         parse the .config and the include/autoconf.mk, and then set the
622         slot back to the idle state.
623
624         Returns:
625           Return True if the subprocess is terminated, False otherwise
626         """
627         if self.state == STATE_IDLE:
628             return True
629
630         if self.ps.poll() == None:
631             return False
632
633         if self.ps.poll() != 0:
634             print >> sys.stderr, log_msg(self.options.color, COLOR_LIGHT_RED,
635                                          self.defconfig, "Failed to process."),
636             if self.options.verbose:
637                 print >> sys.stderr, color_text(self.options.color,
638                                                 COLOR_LIGHT_CYAN,
639                                                 self.ps.stderr.read())
640             if self.options.exit_on_error:
641                 sys.exit("Exit on error.")
642             # If --exit-on-error flag is not set, skip this board and continue.
643             # Record the failed board.
644             self.failed_boards.append(self.defconfig)
645             self.state = STATE_IDLE
646             return True
647
648         if self.state == STATE_AUTOCONF:
649             self.parser.update_dotconfig(self.defconfig)
650
651             print ' %d defconfigs out of %d\r' % (self.num + 1, self.total),
652             sys.stdout.flush()
653
654             """Save off the defconfig in a consistent way"""
655             cmd = list(self.make_cmd)
656             cmd.append('savedefconfig')
657             self.ps = subprocess.Popen(cmd, stdout=self.devnull,
658                                        stderr=subprocess.PIPE)
659             self.state = STATE_SAVEDEFCONFIG
660             return False
661
662         if self.state == STATE_SAVEDEFCONFIG:
663             if not self.options.dry_run:
664                 shutil.move(os.path.join(self.build_dir, 'defconfig'),
665                             os.path.join('configs', self.defconfig))
666             self.state = STATE_IDLE
667             return True
668
669         self.cross_compile = self.parser.get_cross_compile()
670         if self.cross_compile is None:
671             print >> sys.stderr, log_msg(self.options.color, COLOR_YELLOW,
672                                          self.defconfig,
673                                          "Compiler is missing.  Do nothing."),
674             if self.options.exit_on_error:
675                 sys.exit("Exit on error.")
676             # If --exit-on-error flag is not set, skip this board and continue.
677             # Record the failed board.
678             self.failed_boards.append(self.defconfig)
679             self.state = STATE_IDLE
680             return True
681
682         cmd = list(self.make_cmd)
683         if self.cross_compile:
684             cmd.append('CROSS_COMPILE=%s' % self.cross_compile)
685         cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
686         cmd.append('include/config/auto.conf')
687         self.ps = subprocess.Popen(cmd, stdout=self.devnull,
688                                    stderr=subprocess.PIPE)
689         self.state = STATE_AUTOCONF
690         return False
691
692     def get_failed_boards(self):
693         """Returns a list of failed boards (defconfigs) in this slot.
694         """
695         return self.failed_boards
696
697 class Slots:
698
699     """Controller of the array of subprocess slots."""
700
701     def __init__(self, config_attrs, options):
702         """Create a new slots controller.
703
704         Arguments:
705           config_attrs: A list of dictionaris containing the name, the type,
706                         and the default value of the target CONFIG.
707           options: option flags.
708         """
709         self.options = options
710         self.slots = []
711         devnull = get_devnull()
712         make_cmd = get_make_cmd()
713         for i in range(options.jobs):
714             self.slots.append(Slot(config_attrs, options, devnull, make_cmd))
715
716     def add(self, defconfig, num, total):
717         """Add a new subprocess if a vacant slot is found.
718
719         Arguments:
720           defconfig: defconfig name to be put into.
721
722         Returns:
723           Return True on success or False on failure
724         """
725         for slot in self.slots:
726             if slot.add(defconfig, num, total):
727                 return True
728         return False
729
730     def available(self):
731         """Check if there is a vacant slot.
732
733         Returns:
734           Return True if at lease one vacant slot is found, False otherwise.
735         """
736         for slot in self.slots:
737             if slot.poll():
738                 return True
739         return False
740
741     def empty(self):
742         """Check if all slots are vacant.
743
744         Returns:
745           Return True if all the slots are vacant, False otherwise.
746         """
747         ret = True
748         for slot in self.slots:
749             if not slot.poll():
750                 ret = False
751         return ret
752
753     def show_failed_boards(self):
754         """Display all of the failed boards (defconfigs)."""
755         failed_boards = []
756
757         for slot in self.slots:
758             failed_boards += slot.get_failed_boards()
759
760         if len(failed_boards) > 0:
761             msg = [ "The following boards were not processed due to error:" ]
762             msg += failed_boards
763             for line in msg:
764                 print >> sys.stderr, color_text(self.options.color,
765                                                 COLOR_LIGHT_RED, line)
766
767             with open('moveconfig.failed', 'w') as f:
768                 for board in failed_boards:
769                     f.write(board + '\n')
770
771 def move_config(config_attrs, options):
772     """Move config options to defconfig files.
773
774     Arguments:
775       config_attrs: A list of dictionaris, each of them includes the name,
776                     the type, and the default value of the target config.
777       options: option flags
778     """
779     if len(config_attrs) == 0:
780         print 'Nothing to do. exit.'
781         sys.exit(0)
782
783     print 'Move the following CONFIG options (jobs: %d)' % options.jobs
784     for config_attr in config_attrs:
785         print '  %s (type: %s, default: %s)' % (config_attr['config'],
786                                                 config_attr['type'],
787                                                 config_attr['default'])
788
789     if options.defconfigs:
790         defconfigs = [line.strip() for line in open(options.defconfigs)]
791         for i, defconfig in enumerate(defconfigs):
792             if not defconfig.endswith('_defconfig'):
793                 defconfigs[i] = defconfig + '_defconfig'
794             if not os.path.exists(os.path.join('configs', defconfigs[i])):
795                 sys.exit('%s - defconfig does not exist. Stopping.' %
796                          defconfigs[i])
797     else:
798         # All the defconfig files to be processed
799         defconfigs = []
800         for (dirpath, dirnames, filenames) in os.walk('configs'):
801             dirpath = dirpath[len('configs') + 1:]
802             for filename in fnmatch.filter(filenames, '*_defconfig'):
803                 defconfigs.append(os.path.join(dirpath, filename))
804
805     slots = Slots(config_attrs, options)
806
807     # Main loop to process defconfig files:
808     #  Add a new subprocess into a vacant slot.
809     #  Sleep if there is no available slot.
810     for i, defconfig in enumerate(defconfigs):
811         while not slots.add(defconfig, i, len(defconfigs)):
812             while not slots.available():
813                 # No available slot: sleep for a while
814                 time.sleep(SLEEP_TIME)
815
816     # wait until all the subprocesses finish
817     while not slots.empty():
818         time.sleep(SLEEP_TIME)
819
820     print ''
821     slots.show_failed_boards()
822
823 def bad_recipe(filename, linenum, msg):
824     """Print error message with the file name and the line number and exit."""
825     sys.exit("%s: line %d: error : " % (filename, linenum) + msg)
826
827 def parse_recipe(filename):
828     """Parse the recipe file and retrieve the config attributes.
829
830     This function parses the given recipe file and gets the name,
831     the type, and the default value of the target config options.
832
833     Arguments:
834       filename: path to file to be parsed.
835     Returns:
836       A list of dictionaris, each of them includes the name,
837       the type, and the default value of the target config.
838     """
839     config_attrs = []
840     linenum = 1
841
842     for line in open(filename):
843         tokens = line.split()
844         if len(tokens) != 3:
845             bad_recipe(filename, linenum,
846                        "%d fields in this line.  Each line must contain 3 fields"
847                        % len(tokens))
848
849         (config, type, default) = tokens
850
851         # prefix the option name with CONFIG_ if missing
852         if not config.startswith('CONFIG_'):
853             config = 'CONFIG_' + config
854
855         # sanity check of default values
856         if type == 'bool':
857             if not default in ('y', 'n'):
858                 bad_recipe(filename, linenum,
859                            "default for bool type must be either y or n")
860         elif type == 'tristate':
861             if not default in ('y', 'm', 'n'):
862                 bad_recipe(filename, linenum,
863                            "default for tristate type must be y, m, or n")
864         elif type == 'string':
865             if default[0] != '"' or default[-1] != '"':
866                 bad_recipe(filename, linenum,
867                            "default for string type must be surrounded by double-quotations")
868         elif type == 'int':
869             try:
870                 int(default)
871             except:
872                 bad_recipe(filename, linenum,
873                            "type is int, but default value is not decimal")
874         elif type == 'hex':
875             if len(default) < 2 or default[:2] != '0x':
876                 bad_recipe(filename, linenum,
877                            "default for hex type must be prefixed with 0x")
878             try:
879                 int(default, 16)
880             except:
881                 bad_recipe(filename, linenum,
882                            "type is hex, but default value is not hexadecimal")
883         else:
884             bad_recipe(filename, linenum,
885                        "unsupported type '%s'. type must be one of bool, tristate, string, int, hex"
886                        % type)
887
888         config_attrs.append({'config': config, 'type': type, 'default': default})
889         linenum += 1
890
891     return config_attrs
892
893 def main():
894     try:
895         cpu_count = multiprocessing.cpu_count()
896     except NotImplementedError:
897         cpu_count = 1
898
899     parser = optparse.OptionParser()
900     # Add options here
901     parser.add_option('-c', '--color', action='store_true', default=False,
902                       help='display the log in color')
903     parser.add_option('-d', '--defconfigs', type='string',
904                       help='a file containing a list of defconfigs to move')
905     parser.add_option('-n', '--dry-run', action='store_true', default=False,
906                       help='perform a trial run (show log with no changes)')
907     parser.add_option('-e', '--exit-on-error', action='store_true',
908                       default=False,
909                       help='exit immediately on any error')
910     parser.add_option('-H', '--headers-only', dest='cleanup_headers_only',
911                       action='store_true', default=False,
912                       help='only cleanup the headers')
913     parser.add_option('-j', '--jobs', type='int', default=cpu_count,
914                       help='the number of jobs to run simultaneously')
915     parser.add_option('-v', '--verbose', action='store_true', default=False,
916                       help='show any build errors as boards are built')
917     parser.usage += ' recipe_file\n\n' + \
918                     'The recipe_file should describe config options you want to move.\n' + \
919                     'Each line should contain config_name, type, default_value\n\n' + \
920                     'Example:\n' + \
921                     'CONFIG_FOO bool n\n' + \
922                     'CONFIG_BAR int 100\n' + \
923                     'CONFIG_BAZ string "hello"\n'
924
925     (options, args) = parser.parse_args()
926
927     if len(args) != 1:
928         parser.print_usage()
929         sys.exit(1)
930
931     config_attrs = parse_recipe(args[0])
932
933     check_top_directory()
934
935     update_cross_compile(options.color)
936
937     if not options.cleanup_headers_only:
938         move_config(config_attrs, options)
939
940     cleanup_headers(config_attrs, options.dry_run)
941
942 if __name__ == '__main__':
943     main()