blob: d775cf80c2056481bf547bec0570bdcaa588776e [file] [log] [blame]
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001#!/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()