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