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