3 # Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5 # SPDX-License-Identifier: GPL-2.0+
9 Move config options from headers to defconfig files.
11 Since Kconfig was introduced to U-Boot, we have worked on moving
12 config options from headers to Kconfig (defconfig).
14 This tool intends to help this tremendous work.
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.)
26 <config_name> is the name of config option.
28 <type> is the type of the option. It must be one of bool, tristate,
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).
36 You can add two or more lines in the recipe file, so you can move
37 multiple options at once.
39 Let's say, for example, you want to move CONFIG_CMD_USB and
42 The type should be bool, hex, respectively. So, the recipe file
43 should look like this:
47 CONFIG_SYS_TEXT_BASE hex 0x00000000
49 Next you must edit the Kconfig to add the menu entries for the configs
52 And then run this tool giving the file name of the recipe
54 $ tools/moveconfig.py recipe
56 The tool walks through all the defconfig files to move the config
57 options specified by the recipe file.
59 The log is also displayed on the terminal.
61 Each line is printed in the format
62 <defconfig_name> : <action>
64 <defconfig_name> is the name of the defconfig
65 (without the suffix _defconfig).
67 <action> shows what the tool did for that defconfig.
68 It looks like one of the followings:
71 This config option was moved to the defconfig
73 - CONFIG_... is not defined in Kconfig. Do nothing.
74 The entry for this CONFIG was not found in Kconfig.
75 There are two common cases:
76 - You forgot to create an entry for the CONFIG before running
77 this tool, or made a typo in a CONFIG passed to this tool.
78 - The entry was hidden due to unmet 'depends on'.
79 This is correct behavior.
81 - 'CONFIG_...' is the same as the define in Kconfig. Do nothing.
82 The define in the config header matched the one in Kconfig.
83 We do not need to touch it.
85 - Undefined. Do nothing.
86 This config option was not found in the config header.
89 - Compiler is missing. Do nothing.
90 The compiler specified for this architecture was not found
91 in your PATH environment.
92 (If -e option is passed, the tool exits immediately.)
95 An error occurred during processing this defconfig. Skipped.
96 (If -e option is passed, the tool exits immediately on error.)
98 Finally, you will be asked, Clean up headers? [y/n]:
100 If you say 'y' here, the unnecessary config defines are removed
101 from the config headers (include/configs/*.h).
102 It just uses the regex method, so you should not rely on it.
103 Just in case, please do 'git diff' to see what happened.
109 This tool runs configuration and builds include/autoconf.mk for every
110 defconfig. The config options defined in Kconfig appear in the .config
111 file (unless they are hidden because of unmet dependency.)
112 On the other hand, the config options defined by board headers are seen
113 in include/autoconf.mk. The tool looks for the specified options in both
114 of them to decide the appropriate action for the options. If the option
115 is found in the .config or the value is the same as the specified default,
116 the option does not need to be touched. If the option is found in
117 include/autoconf.mk, but not in the .config, and the value is different
118 from the default, the tools adds the option to the defconfig.
120 For faster processing, this tool handles multi-threading. It creates
121 separate build directories where the out-of-tree build is run. The
122 temporary build directories are automatically created and deleted as
123 needed. The number of threads are chosen based on the number of the CPU
124 cores of your system although you can change it via -j (--jobs) option.
130 Appropriate toolchain are necessary to generate include/autoconf.mk
131 for all the architectures supported by U-Boot. Most of them are available
132 at the kernel.org site, some are not provided by kernel.org.
134 The default per-arch CROSS_COMPILE used by this tool is specified by
135 the list below, CROSS_COMPILE. You may wish to update the list to
136 use your own. Instead of modifying the list directly, you can give
137 them via environments.
144 Surround each portion of the log with escape sequences to display it
145 in color on the terminal.
148 Specify a file containing a list of defconfigs to move
151 Peform a trial run that does not make any changes. It is useful to
152 see what is going to happen before one actually runs it.
155 Exit immediately if Make exits with a non-zero status while processing
159 Only cleanup the headers; skip the defconfig processing
162 Specify the number of threads to run simultaneously. If not specified,
163 the number of threads is the same as the number of CPU cores.
166 Show any build errors as boards are built
168 To see the complete list of supported options, run
170 $ tools/moveconfig.py -h
175 import multiprocessing
185 SHOW_GNU_MAKE = 'scripts/show-gnu-make'
188 # Here is the list of cross-tools I use.
189 # Most of them are available at kernel.org
190 # (https://www.kernel.org/pub/tools/crosstool/files/bin/), except the followings:
191 # arc: https://github.com/foss-for-synopsys-dwc-arc-processors/toolchain/releases
192 # blackfin: http://sourceforge.net/projects/adi-toolchain/files/
193 # nds32: http://osdk.andestech.com/packages/nds32le-linux-glibc-v1.tgz
194 # nios2: https://sourcery.mentor.com/GNUToolchain/subscription42545
195 # sh: http://sourcery.mentor.com/public/gnu_toolchain/sh-linux-gnu
197 # openrisc kernel.org toolchain is out of date, download latest one from
198 # http://opencores.org/or1k/OpenRISC_GNU_tool_chain#Prebuilt_versions
201 'aarch64': 'aarch64-linux-',
202 'arm': 'arm-unknown-linux-gnueabi-',
203 'avr32': 'avr32-linux-',
204 'blackfin': 'bfin-elf-',
205 'm68k': 'm68k-linux-',
206 'microblaze': 'microblaze-linux-',
207 'mips': 'mips-linux-',
208 'nds32': 'nds32le-linux-',
209 'nios2': 'nios2-linux-gnu-',
210 'openrisc': 'or1k-elf-',
211 'powerpc': 'powerpc-linux-',
212 'sh': 'sh-linux-gnu-',
213 'sparc': 'sparc-linux-',
220 STATE_SAVEDEFCONFIG = 3
231 COLOR_PURPLE = '0;35'
233 COLOR_LIGHT_GRAY = '0;37'
234 COLOR_DARK_GRAY = '1;30'
235 COLOR_LIGHT_RED = '1;31'
236 COLOR_LIGHT_GREEN = '1;32'
237 COLOR_YELLOW = '1;33'
238 COLOR_LIGHT_BLUE = '1;34'
239 COLOR_LIGHT_PURPLE = '1;35'
240 COLOR_LIGHT_CYAN = '1;36'
243 ### helper functions ###
245 """Get the file object of '/dev/null' device."""
247 devnull = subprocess.DEVNULL # py3k
248 except AttributeError:
249 devnull = open(os.devnull, 'wb')
252 def check_top_directory():
253 """Exit if we are not at the top of source directory."""
254 for f in ('README', 'Licenses'):
255 if not os.path.exists(f):
256 sys.exit('Please run at the top of source directory.')
258 def check_clean_directory():
259 """Exit if the source tree is not clean."""
260 for f in ('.config', 'include/config'):
261 if os.path.exists(f):
262 sys.exit("source tree is not clean, please run 'make mrproper'")
265 """Get the command name of GNU Make.
267 U-Boot needs GNU Make for building, but the command name is not
268 necessarily "make". (for example, "gmake" on FreeBSD).
269 Returns the most appropriate command name on your system.
271 process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE)
272 ret = process.communicate()
273 if process.returncode:
274 sys.exit('GNU Make not found')
275 return ret[0].rstrip()
277 def color_text(color_enabled, color, string):
278 """Return colored string."""
280 return '\033[' + color + 'm' + string + '\033[0m'
284 def log_msg(color_enabled, color, defconfig, msg):
285 """Return the formated line for the log."""
286 return defconfig[:-len('_defconfig')].ljust(37) + ': ' + \
287 color_text(color_enabled, color, msg) + '\n'
289 def update_cross_compile(color_enabled):
290 """Update per-arch CROSS_COMPILE via environment variables
292 The default CROSS_COMPILE values are available
293 in the CROSS_COMPILE list above.
295 You can override them via environment variables
296 CROSS_COMPILE_{ARCH}.
298 For example, if you want to override toolchain prefixes
299 for ARM and PowerPC, you can do as follows in your shell:
301 export CROSS_COMPILE_ARM=...
302 export CROSS_COMPILE_POWERPC=...
304 Then, this function checks if specified compilers really exist in your
309 for arch in os.listdir('arch'):
310 if os.path.exists(os.path.join('arch', arch, 'Makefile')):
313 # arm64 is a special case
314 archs.append('aarch64')
317 env = 'CROSS_COMPILE_' + arch.upper()
318 cross_compile = os.environ.get(env)
319 if not cross_compile:
320 cross_compile = CROSS_COMPILE.get(arch, '')
322 for path in os.environ["PATH"].split(os.pathsep):
323 gcc_path = os.path.join(path, cross_compile + 'gcc')
324 if os.path.isfile(gcc_path) and os.access(gcc_path, os.X_OK):
327 print >> sys.stderr, color_text(color_enabled, COLOR_YELLOW,
328 'warning: %sgcc: not found in PATH. %s architecture boards will be skipped'
329 % (cross_compile, arch))
332 CROSS_COMPILE[arch] = cross_compile
334 def cleanup_one_header(header_path, patterns, dry_run):
335 """Clean regex-matched lines away from a file.
338 header_path: path to the cleaned file.
339 patterns: list of regex patterns. Any lines matching to these
340 patterns are deleted.
341 dry_run: make no changes, but still display log.
343 with open(header_path) as f:
344 lines = f.readlines()
347 for i, line in enumerate(lines):
348 for pattern in patterns:
349 m = pattern.search(line)
351 print '%s: %s: %s' % (header_path, i + 1, line),
355 if dry_run or not matched:
358 with open(header_path, 'w') as f:
359 for i, line in enumerate(lines):
363 def cleanup_headers(config_attrs, dry_run):
364 """Delete config defines from board headers.
367 config_attrs: A list of dictionaris, each of them includes the name,
368 the type, and the default value of the target config.
369 dry_run: make no changes, but still display log.
372 choice = raw_input('Clean up headers? [y/n]: ').lower()
374 if choice == 'y' or choice == 'n':
381 for config_attr in config_attrs:
382 config = config_attr['config']
383 patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
384 patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
386 for dir in 'include', 'arch', 'board':
387 for (dirpath, dirnames, filenames) in os.walk(dir):
388 for filename in filenames:
389 if not fnmatch.fnmatch(filename, '*~'):
390 cleanup_one_header(os.path.join(dirpath, filename),
396 """Progress Indicator"""
398 def __init__(self, total):
399 """Create a new progress indicator.
402 total: A number of defconfig files to process.
408 """Increment the number of processed defconfig files."""
413 """Display the progress."""
414 print ' %d defconfigs out of %d\r' % (self.current, self.total),
419 """A parser of .config and include/autoconf.mk."""
421 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
422 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
424 def __init__(self, config_attrs, options, progress, build_dir):
425 """Create a new parser.
428 config_attrs: A list of dictionaris, each of them includes the name,
429 the type, and the default value of the target config.
430 options: option flags.
431 progress: A progress indicator
432 build_dir: Build directory.
434 self.config_attrs = config_attrs
435 self.options = options
436 self.progress = progress
437 self.build_dir = build_dir
439 def get_cross_compile(self):
440 """Parse .config file and return CROSS_COMPILE.
443 A string storing the compiler prefix for the architecture.
444 Return a NULL string for architectures that do not require
445 compiler prefix (Sandbox and native build is the case).
446 Return None if the specified compiler is missing in your PATH.
447 Caller should distinguish '' and None.
451 dotconfig = os.path.join(self.build_dir, '.config')
452 for line in open(dotconfig):
453 m = self.re_arch.match(line)
457 m = self.re_cpu.match(line)
465 if arch == 'arm' and cpu == 'armv8':
468 return CROSS_COMPILE.get(arch, None)
470 def parse_one_config(self, config_attr, dotconfig_lines, autoconf_lines):
471 """Parse .config, defconfig, include/autoconf.mk for one config.
473 This function looks for the config options in the lines from
474 defconfig, .config, and include/autoconf.mk in order to decide
475 which action should be taken for this defconfig.
478 config_attr: A dictionary including the name, the type,
479 and the default value of the target config.
480 dotconfig_lines: lines from the .config file.
481 autoconf_lines: lines from the include/autoconf.mk file.
484 A tupple of the action for this defconfig and the line
485 matched for the config.
487 config = config_attr['config']
488 not_set = '# %s is not set' % config
490 for line in dotconfig_lines:
492 if line.startswith(config + '=') or line == not_set:
496 return (ACTION_NO_ENTRY, config)
498 for line in autoconf_lines:
500 if line.startswith(config + '='):
506 if old_val == new_val:
507 return (ACTION_NO_CHANGE, new_val)
509 # If this CONFIG is neither bool nor trisate
510 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
511 # tools/scripts/define2mk.sed changes '1' to 'y'.
512 # This is a problem if the CONFIG is int type.
513 # Check the type in Kconfig and handle it correctly.
514 if new_val[-2:] == '=y':
515 new_val = new_val[:-1] + '1'
517 return (ACTION_MOVE, new_val)
519 def update_dotconfig(self, defconfig):
520 """Parse files for the config options and update the .config.
522 This function parses the generated .config and include/autoconf.mk
523 searching the target options.
524 Move the config option(s) to the .config as needed.
525 Also, display the log to show what happened to the .config.
528 defconfig: defconfig name.
531 dotconfig_path = os.path.join(self.build_dir, '.config')
532 autoconf_path = os.path.join(self.build_dir, 'include', 'autoconf.mk')
535 with open(dotconfig_path) as f:
536 dotconfig_lines = f.readlines()
538 with open(autoconf_path) as f:
539 autoconf_lines = f.readlines()
541 for config_attr in self.config_attrs:
542 result = self.parse_one_config(config_attr, dotconfig_lines,
544 results.append(result)
548 for (action, value) in results:
549 if action == ACTION_MOVE:
550 actlog = "Move '%s'" % value
551 log_color = COLOR_LIGHT_GREEN
552 elif action == ACTION_NO_ENTRY:
553 actlog = "%s is not defined in Kconfig. Do nothing." % value
554 log_color = COLOR_LIGHT_BLUE
555 elif action == ACTION_NO_CHANGE:
556 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
558 log_color = COLOR_LIGHT_PURPLE
560 sys.exit("Internal Error. This should not happen.")
562 log += log_msg(self.options.color, log_color, defconfig, actlog)
564 # Some threads are running in parallel.
565 # Print log in one shot to not mix up logs from different threads.
569 with open(dotconfig_path, 'a') as f:
570 for (action, value) in results:
571 if action == ACTION_MOVE:
572 f.write(value + '\n')
574 os.remove(os.path.join(self.build_dir, 'include', 'config', 'auto.conf'))
575 os.remove(autoconf_path)
579 """A slot to store a subprocess.
581 Each instance of this class handles one subprocess.
582 This class is useful to control multiple threads
583 for faster processing.
586 def __init__(self, config_attrs, options, progress, devnull, make_cmd):
587 """Create a new process slot.
590 config_attrs: A list of dictionaris, each of them includes the name,
591 the type, and the default value of the target config.
592 options: option flags.
593 progress: A progress indicator.
594 devnull: A file object of '/dev/null'.
595 make_cmd: command name of GNU Make.
597 self.options = options
598 self.progress = progress
599 self.build_dir = tempfile.mkdtemp()
600 self.devnull = devnull
601 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
602 self.parser = KconfigParser(config_attrs, options, progress,
604 self.state = STATE_IDLE
605 self.failed_boards = []
608 """Delete the working directory
610 This function makes sure the temporary directory is cleaned away
611 even if Python suddenly dies due to error. It should be done in here
612 because it is guranteed the destructor is always invoked when the
613 instance of the class gets unreferenced.
615 If the subprocess is still running, wait until it finishes.
617 if self.state != STATE_IDLE:
618 while self.ps.poll() == None:
620 shutil.rmtree(self.build_dir)
622 def add(self, defconfig):
623 """Assign a new subprocess for defconfig and add it to the slot.
625 If the slot is vacant, create a new subprocess for processing the
626 given defconfig and add it to the slot. Just returns False if
627 the slot is occupied (i.e. the current subprocess is still running).
630 defconfig: defconfig name.
633 Return True on success or False on failure
635 if self.state != STATE_IDLE:
637 cmd = list(self.make_cmd)
638 cmd.append(defconfig)
639 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
640 stderr=subprocess.PIPE)
641 self.defconfig = defconfig
642 self.state = STATE_DEFCONFIG
646 """Check the status of the subprocess and handle it as needed.
648 Returns True if the slot is vacant (i.e. in idle state).
649 If the configuration is successfully finished, assign a new
650 subprocess to build include/autoconf.mk.
651 If include/autoconf.mk is generated, invoke the parser to
652 parse the .config and the include/autoconf.mk, and then set the
653 slot back to the idle state.
656 Return True if the subprocess is terminated, False otherwise
658 if self.state == STATE_IDLE:
661 if self.ps.poll() == None:
664 if self.ps.poll() != 0:
665 print >> sys.stderr, log_msg(self.options.color, COLOR_LIGHT_RED,
666 self.defconfig, "Failed to process."),
667 if self.options.verbose:
668 print >> sys.stderr, color_text(self.options.color,
670 self.ps.stderr.read())
673 if self.options.exit_on_error:
674 sys.exit("Exit on error.")
675 # If --exit-on-error flag is not set, skip this board and continue.
676 # Record the failed board.
677 self.failed_boards.append(self.defconfig)
678 self.state = STATE_IDLE
681 if self.state == STATE_AUTOCONF:
682 self.parser.update_dotconfig(self.defconfig)
684 """Save off the defconfig in a consistent way"""
685 cmd = list(self.make_cmd)
686 cmd.append('savedefconfig')
687 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
688 stderr=subprocess.PIPE)
689 self.state = STATE_SAVEDEFCONFIG
692 if self.state == STATE_SAVEDEFCONFIG:
693 if not self.options.dry_run:
694 shutil.move(os.path.join(self.build_dir, 'defconfig'),
695 os.path.join('configs', self.defconfig))
697 self.state = STATE_IDLE
700 self.cross_compile = self.parser.get_cross_compile()
701 if self.cross_compile is None:
702 print >> sys.stderr, log_msg(self.options.color, COLOR_YELLOW,
704 "Compiler is missing. Do nothing."),
707 if self.options.exit_on_error:
708 sys.exit("Exit on error.")
709 # If --exit-on-error flag is not set, skip this board and continue.
710 # Record the failed board.
711 self.failed_boards.append(self.defconfig)
712 self.state = STATE_IDLE
715 cmd = list(self.make_cmd)
716 if self.cross_compile:
717 cmd.append('CROSS_COMPILE=%s' % self.cross_compile)
718 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
719 cmd.append('include/config/auto.conf')
720 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
721 stderr=subprocess.PIPE)
722 self.state = STATE_AUTOCONF
725 def get_failed_boards(self):
726 """Returns a list of failed boards (defconfigs) in this slot.
728 return self.failed_boards
732 """Controller of the array of subprocess slots."""
734 def __init__(self, config_attrs, options, progress):
735 """Create a new slots controller.
738 config_attrs: A list of dictionaris containing the name, the type,
739 and the default value of the target CONFIG.
740 options: option flags.
741 progress: A progress indicator.
743 self.options = options
745 devnull = get_devnull()
746 make_cmd = get_make_cmd()
747 for i in range(options.jobs):
748 self.slots.append(Slot(config_attrs, options, progress, devnull,
751 def add(self, defconfig):
752 """Add a new subprocess if a vacant slot is found.
755 defconfig: defconfig name to be put into.
758 Return True on success or False on failure
760 for slot in self.slots:
761 if slot.add(defconfig):
766 """Check if there is a vacant slot.
769 Return True if at lease one vacant slot is found, False otherwise.
771 for slot in self.slots:
777 """Check if all slots are vacant.
780 Return True if all the slots are vacant, False otherwise.
783 for slot in self.slots:
788 def show_failed_boards(self):
789 """Display all of the failed boards (defconfigs)."""
792 for slot in self.slots:
793 failed_boards += slot.get_failed_boards()
795 if len(failed_boards) > 0:
796 msg = [ "The following boards were not processed due to error:" ]
799 print >> sys.stderr, color_text(self.options.color,
800 COLOR_LIGHT_RED, line)
802 with open('moveconfig.failed', 'w') as f:
803 for board in failed_boards:
804 f.write(board + '\n')
806 def move_config(config_attrs, options):
807 """Move config options to defconfig files.
810 config_attrs: A list of dictionaris, each of them includes the name,
811 the type, and the default value of the target config.
812 options: option flags
814 if len(config_attrs) == 0:
815 print 'Nothing to do. exit.'
818 print 'Move the following CONFIG options (jobs: %d)' % options.jobs
819 for config_attr in config_attrs:
820 print ' %s (type: %s, default: %s)' % (config_attr['config'],
822 config_attr['default'])
824 if options.defconfigs:
825 defconfigs = [line.strip() for line in open(options.defconfigs)]
826 for i, defconfig in enumerate(defconfigs):
827 if not defconfig.endswith('_defconfig'):
828 defconfigs[i] = defconfig + '_defconfig'
829 if not os.path.exists(os.path.join('configs', defconfigs[i])):
830 sys.exit('%s - defconfig does not exist. Stopping.' %
833 # All the defconfig files to be processed
835 for (dirpath, dirnames, filenames) in os.walk('configs'):
836 dirpath = dirpath[len('configs') + 1:]
837 for filename in fnmatch.filter(filenames, '*_defconfig'):
838 defconfigs.append(os.path.join(dirpath, filename))
840 progress = Progress(len(defconfigs))
841 slots = Slots(config_attrs, options, progress)
843 # Main loop to process defconfig files:
844 # Add a new subprocess into a vacant slot.
845 # Sleep if there is no available slot.
846 for defconfig in defconfigs:
847 while not slots.add(defconfig):
848 while not slots.available():
849 # No available slot: sleep for a while
850 time.sleep(SLEEP_TIME)
852 # wait until all the subprocesses finish
853 while not slots.empty():
854 time.sleep(SLEEP_TIME)
858 slots.show_failed_boards()
860 def bad_recipe(filename, linenum, msg):
861 """Print error message with the file name and the line number and exit."""
862 sys.exit("%s: line %d: error : " % (filename, linenum) + msg)
864 def parse_recipe(filename):
865 """Parse the recipe file and retrieve the config attributes.
867 This function parses the given recipe file and gets the name,
868 the type, and the default value of the target config options.
871 filename: path to file to be parsed.
873 A list of dictionaris, each of them includes the name,
874 the type, and the default value of the target config.
879 for line in open(filename):
880 tokens = line.split()
882 bad_recipe(filename, linenum,
883 "%d fields in this line. Each line must contain 3 fields"
886 (config, type, default) = tokens
888 # prefix the option name with CONFIG_ if missing
889 if not config.startswith('CONFIG_'):
890 config = 'CONFIG_' + config
892 # sanity check of default values
894 if not default in ('y', 'n'):
895 bad_recipe(filename, linenum,
896 "default for bool type must be either y or n")
897 elif type == 'tristate':
898 if not default in ('y', 'm', 'n'):
899 bad_recipe(filename, linenum,
900 "default for tristate type must be y, m, or n")
901 elif type == 'string':
902 if default[0] != '"' or default[-1] != '"':
903 bad_recipe(filename, linenum,
904 "default for string type must be surrounded by double-quotations")
909 bad_recipe(filename, linenum,
910 "type is int, but default value is not decimal")
912 if len(default) < 2 or default[:2] != '0x':
913 bad_recipe(filename, linenum,
914 "default for hex type must be prefixed with 0x")
918 bad_recipe(filename, linenum,
919 "type is hex, but default value is not hexadecimal")
921 bad_recipe(filename, linenum,
922 "unsupported type '%s'. type must be one of bool, tristate, string, int, hex"
925 config_attrs.append({'config': config, 'type': type, 'default': default})
932 cpu_count = multiprocessing.cpu_count()
933 except NotImplementedError:
936 parser = optparse.OptionParser()
938 parser.add_option('-c', '--color', action='store_true', default=False,
939 help='display the log in color')
940 parser.add_option('-d', '--defconfigs', type='string',
941 help='a file containing a list of defconfigs to move')
942 parser.add_option('-n', '--dry-run', action='store_true', default=False,
943 help='perform a trial run (show log with no changes)')
944 parser.add_option('-e', '--exit-on-error', action='store_true',
946 help='exit immediately on any error')
947 parser.add_option('-H', '--headers-only', dest='cleanup_headers_only',
948 action='store_true', default=False,
949 help='only cleanup the headers')
950 parser.add_option('-j', '--jobs', type='int', default=cpu_count,
951 help='the number of jobs to run simultaneously')
952 parser.add_option('-v', '--verbose', action='store_true', default=False,
953 help='show any build errors as boards are built')
954 parser.usage += ' recipe_file\n\n' + \
955 'The recipe_file should describe config options you want to move.\n' + \
956 'Each line should contain config_name, type, default_value\n\n' + \
958 'CONFIG_FOO bool n\n' + \
959 'CONFIG_BAR int 100\n' + \
960 'CONFIG_BAZ string "hello"\n'
962 (options, args) = parser.parse_args()
968 config_attrs = parse_recipe(args[0])
970 check_top_directory()
972 check_clean_directory()
974 update_cross_compile(options.color)
976 if not options.cleanup_headers_only:
977 move_config(config_attrs, options)
979 cleanup_headers(config_attrs, options.dry_run)
981 if __name__ == '__main__':