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 First, you must edit the Kconfig to add the menu entries for the configs
23 And then run this tool giving CONFIG names you want to move.
24 For example, if you want to move CONFIG_CMD_USB and CONFIG_SYS_TEXT_BASE,
25 simply type as follows:
27 $ tools/moveconfig.py CONFIG_CMD_USB CONFIG_SYS_TEXT_BASE
29 The tool walks through all the defconfig files and move the given CONFIGs.
31 The log is also displayed on the terminal.
33 The log is printed for each defconfig as follows:
41 <defconfig_name> is the name of the defconfig.
43 <action*> shows what the tool did for that defconfig.
44 It looks like one of the following:
47 This config option was moved to the defconfig
49 - CONFIG_... is not defined in Kconfig. Do nothing.
50 The entry for this CONFIG was not found in Kconfig. The option is not
51 defined in the config header, either. So, this case can be just skipped.
53 - CONFIG_... is not defined in Kconfig (suspicious). Do nothing.
54 This option is defined in the config header, but its entry was not found
56 There are two common cases:
57 - You forgot to create an entry for the CONFIG before running
58 this tool, or made a typo in a CONFIG passed to this tool.
59 - The entry was hidden due to unmet 'depends on'.
60 The tool does not know if the result is reasonable, so please check it
63 - 'CONFIG_...' is the same as the define in Kconfig. Do nothing.
64 The define in the config header matched the one in Kconfig.
65 We do not need to touch it.
67 - Compiler is missing. Do nothing.
68 The compiler specified for this architecture was not found
69 in your PATH environment.
70 (If -e option is passed, the tool exits immediately.)
73 An error occurred during processing this defconfig. Skipped.
74 (If -e option is passed, the tool exits immediately on error.)
76 Finally, you will be asked, Clean up headers? [y/n]:
78 If you say 'y' here, the unnecessary config defines are removed
79 from the config headers (include/configs/*.h).
80 It just uses the regex method, so you should not rely on it.
81 Just in case, please do 'git diff' to see what happened.
87 This tool runs configuration and builds include/autoconf.mk for every
88 defconfig. The config options defined in Kconfig appear in the .config
89 file (unless they are hidden because of unmet dependency.)
90 On the other hand, the config options defined by board headers are seen
91 in include/autoconf.mk. The tool looks for the specified options in both
92 of them to decide the appropriate action for the options. If the given
93 config option is found in the .config, but its value does not match the
94 one from the board header, the config option in the .config is replaced
95 with the define in the board header. Then, the .config is synced by
96 "make savedefconfig" and the defconfig is updated with it.
98 For faster processing, this tool handles multi-threading. It creates
99 separate build directories where the out-of-tree build is run. The
100 temporary build directories are automatically created and deleted as
101 needed. The number of threads are chosen based on the number of the CPU
102 cores of your system although you can change it via -j (--jobs) option.
108 Appropriate toolchain are necessary to generate include/autoconf.mk
109 for all the architectures supported by U-Boot. Most of them are available
110 at the kernel.org site, some are not provided by kernel.org.
112 The default per-arch CROSS_COMPILE used by this tool is specified by
113 the list below, CROSS_COMPILE. You may wish to update the list to
114 use your own. Instead of modifying the list directly, you can give
115 them via environments.
122 Surround each portion of the log with escape sequences to display it
123 in color on the terminal.
126 Create a git commit with the changes when the operation is complete. A
127 standard commit message is used which may need to be edited.
130 Specify a file containing a list of defconfigs to move
133 Perform a trial run that does not make any changes. It is useful to
134 see what is going to happen before one actually runs it.
137 Exit immediately if Make exits with a non-zero status while processing
141 Do "make savedefconfig" forcibly for all the defconfig files.
142 If not specified, "make savedefconfig" only occurs for cases
143 where at least one CONFIG was moved.
146 Look for moved config options in spl/include/autoconf.mk instead of
147 include/autoconf.mk. This is useful for moving options for SPL build
148 because SPL related options (mostly prefixed with CONFIG_SPL_) are
149 sometimes blocked by CONFIG_SPL_BUILD ifdef conditionals.
152 Only cleanup the headers; skip the defconfig processing
155 Specify the number of threads to run simultaneously. If not specified,
156 the number of threads is the same as the number of CPU cores.
159 Specify the git ref to clone for building the autoconf.mk. If unspecified
160 use the CWD. This is useful for when changes to the Kconfig affect the
161 default values and you want to capture the state of the defconfig from
162 before that change was in effect. If in doubt, specify a ref pre-Kconfig
163 changes (use HEAD if Kconfig changes are not committed). Worst case it will
164 take a bit longer to run, but will always do the right thing.
167 Show any build errors as boards are built
170 Instead of prompting, automatically go ahead with all operations. This
171 includes cleaning up headers and CONFIG_SYS_EXTRA_OPTIONS.
173 To see the complete list of supported options, run
175 $ tools/moveconfig.py -h
183 import multiprocessing
193 SHOW_GNU_MAKE = 'scripts/show-gnu-make'
196 # Here is the list of cross-tools I use.
197 # Most of them are available at kernel.org
198 # (https://www.kernel.org/pub/tools/crosstool/files/bin/), except the following:
199 # arc: https://github.com/foss-for-synopsys-dwc-arc-processors/toolchain/releases
200 # blackfin: http://sourceforge.net/projects/adi-toolchain/files/
201 # nds32: http://osdk.andestech.com/packages/nds32le-linux-glibc-v1.tgz
202 # nios2: https://sourcery.mentor.com/GNUToolchain/subscription42545
203 # sh: http://sourcery.mentor.com/public/gnu_toolchain/sh-linux-gnu
205 # openrisc kernel.org toolchain is out of date, download latest one from
206 # http://opencores.org/or1k/OpenRISC_GNU_tool_chain#Prebuilt_versions
209 'aarch64': 'aarch64-linux-',
210 'arm': 'arm-unknown-linux-gnueabi-',
211 'avr32': 'avr32-linux-',
212 'blackfin': 'bfin-elf-',
213 'm68k': 'm68k-linux-',
214 'microblaze': 'microblaze-linux-',
215 'mips': 'mips-linux-',
216 'nds32': 'nds32le-linux-',
217 'nios2': 'nios2-linux-gnu-',
218 'openrisc': 'or1k-elf-',
219 'powerpc': 'powerpc-linux-',
220 'sh': 'sh-linux-gnu-',
221 'sparc': 'sparc-linux-',
222 'x86': 'i386-linux-',
223 'xtensa': 'xtensa-linux-'
229 STATE_SAVEDEFCONFIG = 3
233 ACTION_NO_ENTRY_WARN = 2
241 COLOR_PURPLE = '0;35'
243 COLOR_LIGHT_GRAY = '0;37'
244 COLOR_DARK_GRAY = '1;30'
245 COLOR_LIGHT_RED = '1;31'
246 COLOR_LIGHT_GREEN = '1;32'
247 COLOR_YELLOW = '1;33'
248 COLOR_LIGHT_BLUE = '1;34'
249 COLOR_LIGHT_PURPLE = '1;35'
250 COLOR_LIGHT_CYAN = '1;36'
253 ### helper functions ###
255 """Get the file object of '/dev/null' device."""
257 devnull = subprocess.DEVNULL # py3k
258 except AttributeError:
259 devnull = open(os.devnull, 'wb')
262 def check_top_directory():
263 """Exit if we are not at the top of source directory."""
264 for f in ('README', 'Licenses'):
265 if not os.path.exists(f):
266 sys.exit('Please run at the top of source directory.')
268 def check_clean_directory():
269 """Exit if the source tree is not clean."""
270 for f in ('.config', 'include/config'):
271 if os.path.exists(f):
272 sys.exit("source tree is not clean, please run 'make mrproper'")
275 """Get the command name of GNU Make.
277 U-Boot needs GNU Make for building, but the command name is not
278 necessarily "make". (for example, "gmake" on FreeBSD).
279 Returns the most appropriate command name on your system.
281 process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE)
282 ret = process.communicate()
283 if process.returncode:
284 sys.exit('GNU Make not found')
285 return ret[0].rstrip()
287 def get_all_defconfigs():
288 """Get all the defconfig files under the configs/ directory."""
290 for (dirpath, dirnames, filenames) in os.walk('configs'):
291 dirpath = dirpath[len('configs') + 1:]
292 for filename in fnmatch.filter(filenames, '*_defconfig'):
293 defconfigs.append(os.path.join(dirpath, filename))
297 def color_text(color_enabled, color, string):
298 """Return colored string."""
300 # LF should not be surrounded by the escape sequence.
301 # Otherwise, additional whitespace or line-feed might be printed.
302 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
303 for s in string.split('\n') ])
307 def show_diff(a, b, file_path, color_enabled):
308 """Show unidified diff.
311 a: A list of lines (before)
312 b: A list of lines (after)
313 file_path: Path to the file
314 color_enabled: Display the diff in color
317 diff = difflib.unified_diff(a, b,
318 fromfile=os.path.join('a', file_path),
319 tofile=os.path.join('b', file_path))
322 if line[0] == '-' and line[1] != '-':
323 print color_text(color_enabled, COLOR_RED, line),
324 elif line[0] == '+' and line[1] != '+':
325 print color_text(color_enabled, COLOR_GREEN, line),
329 def update_cross_compile(color_enabled):
330 """Update per-arch CROSS_COMPILE via environment variables
332 The default CROSS_COMPILE values are available
333 in the CROSS_COMPILE list above.
335 You can override them via environment variables
336 CROSS_COMPILE_{ARCH}.
338 For example, if you want to override toolchain prefixes
339 for ARM and PowerPC, you can do as follows in your shell:
341 export CROSS_COMPILE_ARM=...
342 export CROSS_COMPILE_POWERPC=...
344 Then, this function checks if specified compilers really exist in your
349 for arch in os.listdir('arch'):
350 if os.path.exists(os.path.join('arch', arch, 'Makefile')):
353 # arm64 is a special case
354 archs.append('aarch64')
357 env = 'CROSS_COMPILE_' + arch.upper()
358 cross_compile = os.environ.get(env)
359 if not cross_compile:
360 cross_compile = CROSS_COMPILE.get(arch, '')
362 for path in os.environ["PATH"].split(os.pathsep):
363 gcc_path = os.path.join(path, cross_compile + 'gcc')
364 if os.path.isfile(gcc_path) and os.access(gcc_path, os.X_OK):
367 print >> sys.stderr, color_text(color_enabled, COLOR_YELLOW,
368 'warning: %sgcc: not found in PATH. %s architecture boards will be skipped'
369 % (cross_compile, arch))
372 CROSS_COMPILE[arch] = cross_compile
374 def extend_matched_lines(lines, matched, pre_patterns, post_patterns, extend_pre,
376 """Extend matched lines if desired patterns are found before/after already
380 lines: A list of lines handled.
381 matched: A list of line numbers that have been already matched.
382 (will be updated by this function)
383 pre_patterns: A list of regular expression that should be matched as
385 post_patterns: A list of regular expression that should be matched as
387 extend_pre: Add the line number of matched preamble to the matched list.
388 extend_post: Add the line number of matched postamble to the matched list.
390 extended_matched = []
403 for p in pre_patterns:
404 if p.search(lines[i - 1]):
410 for p in post_patterns:
411 if p.search(lines[j]):
418 extended_matched.append(i - 1)
420 extended_matched.append(j)
422 matched += extended_matched
425 def cleanup_one_header(header_path, patterns, options):
426 """Clean regex-matched lines away from a file.
429 header_path: path to the cleaned file.
430 patterns: list of regex patterns. Any lines matching to these
431 patterns are deleted.
432 options: option flags.
434 with open(header_path) as f:
435 lines = f.readlines()
438 for i, line in enumerate(lines):
439 if i - 1 in matched and lines[i - 1][-2:] == '\\\n':
442 for pattern in patterns:
443 if pattern.search(line):
450 # remove empty #ifdef ... #endif, successive blank lines
451 pattern_if = re.compile(r'#\s*if(def|ndef)?\W') # #if, #ifdef, #ifndef
452 pattern_elif = re.compile(r'#\s*el(if|se)\W') # #elif, #else
453 pattern_endif = re.compile(r'#\s*endif\W') # #endif
454 pattern_blank = re.compile(r'^\s*$') # empty line
457 old_matched = copy.copy(matched)
458 extend_matched_lines(lines, matched, [pattern_if],
459 [pattern_endif], True, True)
460 extend_matched_lines(lines, matched, [pattern_elif],
461 [pattern_elif, pattern_endif], True, False)
462 extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
463 [pattern_blank], False, True)
464 extend_matched_lines(lines, matched, [pattern_blank],
465 [pattern_elif, pattern_endif], True, False)
466 extend_matched_lines(lines, matched, [pattern_blank],
467 [pattern_blank], True, False)
468 if matched == old_matched:
471 tolines = copy.copy(lines)
473 for i in reversed(matched):
476 show_diff(lines, tolines, header_path, options.color)
481 with open(header_path, 'w') as f:
485 def cleanup_headers(configs, options):
486 """Delete config defines from board headers.
489 configs: A list of CONFIGs to remove.
490 options: option flags.
494 choice = raw_input('Clean up headers? [y/n]: ').lower()
496 if choice == 'y' or choice == 'n':
503 for config in configs:
504 patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
505 patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
507 for dir in 'include', 'arch', 'board':
508 for (dirpath, dirnames, filenames) in os.walk(dir):
509 if dirpath == os.path.join('include', 'generated'):
511 for filename in filenames:
512 if not fnmatch.fnmatch(filename, '*~'):
513 cleanup_one_header(os.path.join(dirpath, filename),
516 def cleanup_one_extra_option(defconfig_path, configs, options):
517 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file.
520 defconfig_path: path to the cleaned defconfig file.
521 configs: A list of CONFIGs to remove.
522 options: option flags.
525 start = 'CONFIG_SYS_EXTRA_OPTIONS="'
528 with open(defconfig_path) as f:
529 lines = f.readlines()
531 for i, line in enumerate(lines):
532 if line.startswith(start) and line.endswith(end):
535 # CONFIG_SYS_EXTRA_OPTIONS was not found in this defconfig
538 old_tokens = line[len(start):-len(end)].split(',')
541 for token in old_tokens:
542 pos = token.find('=')
543 if not (token[:pos] if pos >= 0 else token) in configs:
544 new_tokens.append(token)
546 if new_tokens == old_tokens:
549 tolines = copy.copy(lines)
552 tolines[i] = start + ','.join(new_tokens) + end
556 show_diff(lines, tolines, defconfig_path, options.color)
561 with open(defconfig_path, 'w') as f:
565 def cleanup_extra_options(configs, options):
566 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files.
569 configs: A list of CONFIGs to remove.
570 options: option flags.
574 choice = (raw_input('Clean up CONFIG_SYS_EXTRA_OPTIONS? [y/n]: ').
577 if choice == 'y' or choice == 'n':
583 configs = [ config[len('CONFIG_'):] for config in configs ]
585 defconfigs = get_all_defconfigs()
587 for defconfig in defconfigs:
588 cleanup_one_extra_option(os.path.join('configs', defconfig), configs,
594 """Progress Indicator"""
596 def __init__(self, total):
597 """Create a new progress indicator.
600 total: A number of defconfig files to process.
606 """Increment the number of processed defconfig files."""
611 """Display the progress."""
612 print ' %d defconfigs out of %d\r' % (self.current, self.total),
617 """A parser of .config and include/autoconf.mk."""
619 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
620 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
622 def __init__(self, configs, options, build_dir):
623 """Create a new parser.
626 configs: A list of CONFIGs to move.
627 options: option flags.
628 build_dir: Build directory.
630 self.configs = configs
631 self.options = options
632 self.dotconfig = os.path.join(build_dir, '.config')
633 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
634 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
636 self.config_autoconf = os.path.join(build_dir, 'include', 'config',
638 self.defconfig = os.path.join(build_dir, 'defconfig')
640 def get_cross_compile(self):
641 """Parse .config file and return CROSS_COMPILE.
644 A string storing the compiler prefix for the architecture.
645 Return a NULL string for architectures that do not require
646 compiler prefix (Sandbox and native build is the case).
647 Return None if the specified compiler is missing in your PATH.
648 Caller should distinguish '' and None.
652 for line in open(self.dotconfig):
653 m = self.re_arch.match(line)
657 m = self.re_cpu.match(line)
665 if arch == 'arm' and cpu == 'armv8':
668 return CROSS_COMPILE.get(arch, None)
670 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
671 """Parse .config, defconfig, include/autoconf.mk for one config.
673 This function looks for the config options in the lines from
674 defconfig, .config, and include/autoconf.mk in order to decide
675 which action should be taken for this defconfig.
678 config: CONFIG name to parse.
679 dotconfig_lines: lines from the .config file.
680 autoconf_lines: lines from the include/autoconf.mk file.
683 A tupple of the action for this defconfig and the line
684 matched for the config.
686 not_set = '# %s is not set' % config
688 for line in autoconf_lines:
690 if line.startswith(config + '='):
696 for line in dotconfig_lines:
698 if line.startswith(config + '=') or line == not_set:
702 if new_val == not_set:
703 return (ACTION_NO_ENTRY, config)
705 return (ACTION_NO_ENTRY_WARN, config)
707 # If this CONFIG is neither bool nor trisate
708 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
709 # tools/scripts/define2mk.sed changes '1' to 'y'.
710 # This is a problem if the CONFIG is int type.
711 # Check the type in Kconfig and handle it correctly.
712 if new_val[-2:] == '=y':
713 new_val = new_val[:-1] + '1'
715 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
718 def update_dotconfig(self):
719 """Parse files for the config options and update the .config.
721 This function parses the generated .config and include/autoconf.mk
722 searching the target options.
723 Move the config option(s) to the .config as needed.
726 defconfig: defconfig name.
729 Return a tuple of (updated flag, log string).
730 The "updated flag" is True if the .config was updated, False
731 otherwise. The "log string" shows what happend to the .config.
737 rm_files = [self.config_autoconf, self.autoconf]
740 if os.path.exists(self.spl_autoconf):
741 autoconf_path = self.spl_autoconf
742 rm_files.append(self.spl_autoconf)
746 return (updated, suspicious,
747 color_text(self.options.color, COLOR_BROWN,
748 "SPL is not enabled. Skipped.") + '\n')
750 autoconf_path = self.autoconf
752 with open(self.dotconfig) as f:
753 dotconfig_lines = f.readlines()
755 with open(autoconf_path) as f:
756 autoconf_lines = f.readlines()
758 for config in self.configs:
759 result = self.parse_one_config(config, dotconfig_lines,
761 results.append(result)
765 for (action, value) in results:
766 if action == ACTION_MOVE:
767 actlog = "Move '%s'" % value
768 log_color = COLOR_LIGHT_GREEN
769 elif action == ACTION_NO_ENTRY:
770 actlog = "%s is not defined in Kconfig. Do nothing." % value
771 log_color = COLOR_LIGHT_BLUE
772 elif action == ACTION_NO_ENTRY_WARN:
773 actlog = "%s is not defined in Kconfig (suspicious). Do nothing." % value
774 log_color = COLOR_YELLOW
776 elif action == ACTION_NO_CHANGE:
777 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
779 log_color = COLOR_LIGHT_PURPLE
780 elif action == ACTION_SPL_NOT_EXIST:
781 actlog = "SPL is not enabled for this defconfig. Skip."
782 log_color = COLOR_PURPLE
784 sys.exit("Internal Error. This should not happen.")
786 log += color_text(self.options.color, log_color, actlog) + '\n'
788 with open(self.dotconfig, 'a') as f:
789 for (action, value) in results:
790 if action == ACTION_MOVE:
791 f.write(value + '\n')
794 self.results = results
798 return (updated, suspicious, log)
800 def check_defconfig(self):
801 """Check the defconfig after savedefconfig
804 Return additional log if moved CONFIGs were removed again by
805 'make savedefconfig'.
810 with open(self.defconfig) as f:
811 defconfig_lines = f.readlines()
813 for (action, value) in self.results:
814 if action != ACTION_MOVE:
816 if not value + '\n' in defconfig_lines:
817 log += color_text(self.options.color, COLOR_YELLOW,
818 "'%s' was removed by savedefconfig.\n" %
825 """A slot to store a subprocess.
827 Each instance of this class handles one subprocess.
828 This class is useful to control multiple threads
829 for faster processing.
832 def __init__(self, configs, options, progress, devnull, make_cmd, reference_src_dir):
833 """Create a new process slot.
836 configs: A list of CONFIGs to move.
837 options: option flags.
838 progress: A progress indicator.
839 devnull: A file object of '/dev/null'.
840 make_cmd: command name of GNU Make.
841 reference_src_dir: Determine the true starting config state from this
844 self.options = options
845 self.progress = progress
846 self.build_dir = tempfile.mkdtemp()
847 self.devnull = devnull
848 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
849 self.reference_src_dir = reference_src_dir
850 self.parser = KconfigParser(configs, options, self.build_dir)
851 self.state = STATE_IDLE
852 self.failed_boards = set()
853 self.suspicious_boards = set()
856 """Delete the working directory
858 This function makes sure the temporary directory is cleaned away
859 even if Python suddenly dies due to error. It should be done in here
860 because it is guaranteed the destructor is always invoked when the
861 instance of the class gets unreferenced.
863 If the subprocess is still running, wait until it finishes.
865 if self.state != STATE_IDLE:
866 while self.ps.poll() == None:
868 shutil.rmtree(self.build_dir)
870 def add(self, defconfig):
871 """Assign a new subprocess for defconfig and add it to the slot.
873 If the slot is vacant, create a new subprocess for processing the
874 given defconfig and add it to the slot. Just returns False if
875 the slot is occupied (i.e. the current subprocess is still running).
878 defconfig: defconfig name.
881 Return True on success or False on failure
883 if self.state != STATE_IDLE:
886 self.defconfig = defconfig
888 self.current_src_dir = self.reference_src_dir
893 """Check the status of the subprocess and handle it as needed.
895 Returns True if the slot is vacant (i.e. in idle state).
896 If the configuration is successfully finished, assign a new
897 subprocess to build include/autoconf.mk.
898 If include/autoconf.mk is generated, invoke the parser to
899 parse the .config and the include/autoconf.mk, moving
900 config options to the .config as needed.
901 If the .config was updated, run "make savedefconfig" to sync
902 it, update the original defconfig, and then set the slot back
906 Return True if the subprocess is terminated, False otherwise
908 if self.state == STATE_IDLE:
911 if self.ps.poll() == None:
914 if self.ps.poll() != 0:
916 elif self.state == STATE_DEFCONFIG:
917 if self.reference_src_dir and not self.current_src_dir:
918 self.do_savedefconfig()
921 elif self.state == STATE_AUTOCONF:
922 if self.current_src_dir:
923 self.current_src_dir = None
926 self.do_savedefconfig()
927 elif self.state == STATE_SAVEDEFCONFIG:
928 self.update_defconfig()
930 sys.exit("Internal Error. This should not happen.")
932 return True if self.state == STATE_IDLE else False
934 def handle_error(self):
935 """Handle error cases."""
937 self.log += color_text(self.options.color, COLOR_LIGHT_RED,
938 "Failed to process.\n")
939 if self.options.verbose:
940 self.log += color_text(self.options.color, COLOR_LIGHT_CYAN,
941 self.ps.stderr.read())
944 def do_defconfig(self):
945 """Run 'make <board>_defconfig' to create the .config file."""
947 cmd = list(self.make_cmd)
948 cmd.append(self.defconfig)
949 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
950 stderr=subprocess.PIPE,
951 cwd=self.current_src_dir)
952 self.state = STATE_DEFCONFIG
954 def do_autoconf(self):
955 """Run 'make include/config/auto.conf'."""
957 self.cross_compile = self.parser.get_cross_compile()
958 if self.cross_compile is None:
959 self.log += color_text(self.options.color, COLOR_YELLOW,
960 "Compiler is missing. Do nothing.\n")
964 cmd = list(self.make_cmd)
965 if self.cross_compile:
966 cmd.append('CROSS_COMPILE=%s' % self.cross_compile)
967 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
968 cmd.append('include/config/auto.conf')
969 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
970 stderr=subprocess.PIPE,
971 cwd=self.current_src_dir)
972 self.state = STATE_AUTOCONF
974 def do_savedefconfig(self):
975 """Update the .config and run 'make savedefconfig'."""
977 (updated, suspicious, log) = self.parser.update_dotconfig()
979 self.suspicious_boards.add(self.defconfig)
982 if not self.options.force_sync and not updated:
986 self.log += color_text(self.options.color, COLOR_LIGHT_GREEN,
987 "Syncing by savedefconfig...\n")
989 self.log += "Syncing by savedefconfig (forced by option)...\n"
991 cmd = list(self.make_cmd)
992 cmd.append('savedefconfig')
993 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
994 stderr=subprocess.PIPE)
995 self.state = STATE_SAVEDEFCONFIG
997 def update_defconfig(self):
998 """Update the input defconfig and go back to the idle state."""
1000 log = self.parser.check_defconfig()
1002 self.suspicious_boards.add(self.defconfig)
1004 orig_defconfig = os.path.join('configs', self.defconfig)
1005 new_defconfig = os.path.join(self.build_dir, 'defconfig')
1006 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
1009 self.log += color_text(self.options.color, COLOR_LIGHT_BLUE,
1010 "defconfig was updated.\n")
1012 if not self.options.dry_run and updated:
1013 shutil.move(new_defconfig, orig_defconfig)
1016 def finish(self, success):
1017 """Display log along with progress and go to the idle state.
1020 success: Should be True when the defconfig was processed
1021 successfully, or False when it fails.
1023 # output at least 30 characters to hide the "* defconfigs out of *".
1024 log = self.defconfig.ljust(30) + '\n'
1026 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
1027 # Some threads are running in parallel.
1028 # Print log atomically to not mix up logs from different threads.
1029 print >> (sys.stdout if success else sys.stderr), log
1032 if self.options.exit_on_error:
1033 sys.exit("Exit on error.")
1034 # If --exit-on-error flag is not set, skip this board and continue.
1035 # Record the failed board.
1036 self.failed_boards.add(self.defconfig)
1039 self.progress.show()
1040 self.state = STATE_IDLE
1042 def get_failed_boards(self):
1043 """Returns a set of failed boards (defconfigs) in this slot.
1045 return self.failed_boards
1047 def get_suspicious_boards(self):
1048 """Returns a set of boards (defconfigs) with possible misconversion.
1050 return self.suspicious_boards - self.failed_boards
1054 """Controller of the array of subprocess slots."""
1056 def __init__(self, configs, options, progress, reference_src_dir):
1057 """Create a new slots controller.
1060 configs: A list of CONFIGs to move.
1061 options: option flags.
1062 progress: A progress indicator.
1063 reference_src_dir: Determine the true starting config state from this
1066 self.options = options
1068 devnull = get_devnull()
1069 make_cmd = get_make_cmd()
1070 for i in range(options.jobs):
1071 self.slots.append(Slot(configs, options, progress, devnull,
1072 make_cmd, reference_src_dir))
1074 def add(self, defconfig):
1075 """Add a new subprocess if a vacant slot is found.
1078 defconfig: defconfig name to be put into.
1081 Return True on success or False on failure
1083 for slot in self.slots:
1084 if slot.add(defconfig):
1088 def available(self):
1089 """Check if there is a vacant slot.
1092 Return True if at lease one vacant slot is found, False otherwise.
1094 for slot in self.slots:
1100 """Check if all slots are vacant.
1103 Return True if all the slots are vacant, False otherwise.
1106 for slot in self.slots:
1111 def show_failed_boards(self):
1112 """Display all of the failed boards (defconfigs)."""
1114 output_file = 'moveconfig.failed'
1116 for slot in self.slots:
1117 boards |= slot.get_failed_boards()
1120 boards = '\n'.join(boards) + '\n'
1121 msg = "The following boards were not processed due to error:\n"
1123 msg += "(the list has been saved in %s)\n" % output_file
1124 print >> sys.stderr, color_text(self.options.color, COLOR_LIGHT_RED,
1127 with open(output_file, 'w') as f:
1130 def show_suspicious_boards(self):
1131 """Display all boards (defconfigs) with possible misconversion."""
1133 output_file = 'moveconfig.suspicious'
1135 for slot in self.slots:
1136 boards |= slot.get_suspicious_boards()
1139 boards = '\n'.join(boards) + '\n'
1140 msg = "The following boards might have been converted incorrectly.\n"
1141 msg += "It is highly recommended to check them manually:\n"
1143 msg += "(the list has been saved in %s)\n" % output_file
1144 print >> sys.stderr, color_text(self.options.color, COLOR_YELLOW,
1147 with open(output_file, 'w') as f:
1150 class ReferenceSource:
1152 """Reference source against which original configs should be parsed."""
1154 def __init__(self, commit):
1155 """Create a reference source directory based on a specified commit.
1158 commit: commit to git-clone
1160 self.src_dir = tempfile.mkdtemp()
1161 print "Cloning git repo to a separate work directory..."
1162 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1164 print "Checkout '%s' to build the original autoconf.mk." % \
1165 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip()
1166 subprocess.check_output(['git', 'checkout', commit],
1167 stderr=subprocess.STDOUT, cwd=self.src_dir)
1170 """Delete the reference source directory
1172 This function makes sure the temporary directory is cleaned away
1173 even if Python suddenly dies due to error. It should be done in here
1174 because it is guaranteed the destructor is always invoked when the
1175 instance of the class gets unreferenced.
1177 shutil.rmtree(self.src_dir)
1180 """Return the absolute path to the reference source directory."""
1184 def move_config(configs, options):
1185 """Move config options to defconfig files.
1188 configs: A list of CONFIGs to move.
1189 options: option flags
1191 if len(configs) == 0:
1192 if options.force_sync:
1193 print 'No CONFIG is specified. You are probably syncing defconfigs.',
1195 print 'Neither CONFIG nor --force-sync is specified. Nothing will happen.',
1197 print 'Move ' + ', '.join(configs),
1198 print '(jobs: %d)\n' % options.jobs
1201 reference_src = ReferenceSource(options.git_ref)
1202 reference_src_dir = reference_src.get_dir()
1204 reference_src_dir = None
1206 if options.defconfigs:
1207 defconfigs = [line.strip() for line in open(options.defconfigs)]
1208 for i, defconfig in enumerate(defconfigs):
1209 if not defconfig.endswith('_defconfig'):
1210 defconfigs[i] = defconfig + '_defconfig'
1211 if not os.path.exists(os.path.join('configs', defconfigs[i])):
1212 sys.exit('%s - defconfig does not exist. Stopping.' %
1215 defconfigs = get_all_defconfigs()
1217 progress = Progress(len(defconfigs))
1218 slots = Slots(configs, options, progress, reference_src_dir)
1220 # Main loop to process defconfig files:
1221 # Add a new subprocess into a vacant slot.
1222 # Sleep if there is no available slot.
1223 for defconfig in defconfigs:
1224 while not slots.add(defconfig):
1225 while not slots.available():
1226 # No available slot: sleep for a while
1227 time.sleep(SLEEP_TIME)
1229 # wait until all the subprocesses finish
1230 while not slots.empty():
1231 time.sleep(SLEEP_TIME)
1234 slots.show_failed_boards()
1235 slots.show_suspicious_boards()
1239 cpu_count = multiprocessing.cpu_count()
1240 except NotImplementedError:
1243 parser = optparse.OptionParser()
1245 parser.add_option('-c', '--color', action='store_true', default=False,
1246 help='display the log in color')
1247 parser.add_option('-C', '--commit', action='store_true', default=False,
1248 help='Create a git commit for the operation')
1249 parser.add_option('-d', '--defconfigs', type='string',
1250 help='a file containing a list of defconfigs to move')
1251 parser.add_option('-n', '--dry-run', action='store_true', default=False,
1252 help='perform a trial run (show log with no changes)')
1253 parser.add_option('-e', '--exit-on-error', action='store_true',
1255 help='exit immediately on any error')
1256 parser.add_option('-s', '--force-sync', action='store_true', default=False,
1257 help='force sync by savedefconfig')
1258 parser.add_option('-S', '--spl', action='store_true', default=False,
1259 help='parse config options defined for SPL build')
1260 parser.add_option('-H', '--headers-only', dest='cleanup_headers_only',
1261 action='store_true', default=False,
1262 help='only cleanup the headers')
1263 parser.add_option('-j', '--jobs', type='int', default=cpu_count,
1264 help='the number of jobs to run simultaneously')
1265 parser.add_option('-r', '--git-ref', type='string',
1266 help='the git ref to clone for building the autoconf.mk')
1267 parser.add_option('-y', '--yes', action='store_true', default=False,
1268 help="respond 'yes' to any prompts")
1269 parser.add_option('-v', '--verbose', action='store_true', default=False,
1270 help='show any build errors as boards are built')
1271 parser.usage += ' CONFIG ...'
1273 (options, configs) = parser.parse_args()
1275 if len(configs) == 0 and not options.force_sync:
1276 parser.print_usage()
1279 # prefix the option name with CONFIG_ if missing
1280 configs = [ config if config.startswith('CONFIG_') else 'CONFIG_' + config
1281 for config in configs ]
1283 check_top_directory()
1285 if not options.cleanup_headers_only:
1286 check_clean_directory()
1287 update_cross_compile(options.color)
1288 move_config(configs, options)
1291 cleanup_headers(configs, options)
1292 cleanup_extra_options(configs, options)
1295 subprocess.call(['git', 'add', '-u'])
1297 msg = 'Convert %s %sto Kconfig' % (configs[0],
1298 'et al ' if len(configs) > 1 else '')
1299 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1300 '\n '.join(configs))
1302 msg = 'configs: Resync with savedefconfig'
1303 msg += '\n\nRsync all defconfig files using moveconfig.py'
1304 subprocess.call(['git', 'commit', '-s', '-m', msg])
1306 if __name__ == '__main__':