2 # SPDX-License-Identifier: GPL-2.0+
4 # Author: Masahiro Yamada <yamada.masahiro@socionext.com>
8 Move config options from headers to defconfig files.
10 See doc/develop/moveconfig.rst for documentation.
13 from argparse import ArgumentParser
16 from contextlib import ExitStack
23 import multiprocessing
35 from buildman import bsettings
36 from buildman import kconfiglib
37 from buildman import toolchain
39 SHOW_GNU_MAKE = 'scripts/show-gnu-make'
45 STATE_SAVEDEFCONFIG = 3
49 ACTION_NO_ENTRY_WARN = 2
59 COLOR_LIGHT_GRAY = '0;37'
60 COLOR_DARK_GRAY = '1;30'
61 COLOR_LIGHT_RED = '1;31'
62 COLOR_LIGHT_GREEN = '1;32'
64 COLOR_LIGHT_BLUE = '1;34'
65 COLOR_LIGHT_PURPLE = '1;35'
66 COLOR_LIGHT_CYAN = '1;36'
69 AUTO_CONF_PATH = 'include/config/auto.conf'
70 CONFIG_DATABASE = 'moveconfig.db'
72 CONFIG_LEN = len('CONFIG_')
75 'SZ_1': 0x00000001, 'SZ_2': 0x00000002,
76 'SZ_4': 0x00000004, 'SZ_8': 0x00000008,
77 'SZ_16': 0x00000010, 'SZ_32': 0x00000020,
78 'SZ_64': 0x00000040, 'SZ_128': 0x00000080,
79 'SZ_256': 0x00000100, 'SZ_512': 0x00000200,
80 'SZ_1K': 0x00000400, 'SZ_2K': 0x00000800,
81 'SZ_4K': 0x00001000, 'SZ_8K': 0x00002000,
82 'SZ_16K': 0x00004000, 'SZ_32K': 0x00008000,
83 'SZ_64K': 0x00010000, 'SZ_128K': 0x00020000,
84 'SZ_256K': 0x00040000, 'SZ_512K': 0x00080000,
85 'SZ_1M': 0x00100000, 'SZ_2M': 0x00200000,
86 'SZ_4M': 0x00400000, 'SZ_8M': 0x00800000,
87 'SZ_16M': 0x01000000, 'SZ_32M': 0x02000000,
88 'SZ_64M': 0x04000000, 'SZ_128M': 0x08000000,
89 'SZ_256M': 0x10000000, 'SZ_512M': 0x20000000,
90 'SZ_1G': 0x40000000, 'SZ_2G': 0x80000000,
94 RE_REMOVE_DEFCONFIG = re.compile(r'(.*)_defconfig')
96 ### helper functions ###
97 def remove_defconfig(defc):
98 """Drop the _defconfig suffix on a string
101 defc (str): String to convert
104 str: string with the '_defconfig' suffix removed
106 return RE_REMOVE_DEFCONFIG.match(defc)[1]
108 def check_top_directory():
109 """Exit if we are not at the top of source directory."""
110 for fname in 'README', 'Licenses':
111 if not os.path.exists(fname):
112 sys.exit('Please run at the top of source directory.')
114 def check_clean_directory():
115 """Exit if the source tree is not clean."""
116 for fname in '.config', 'include/config':
117 if os.path.exists(fname):
118 sys.exit("source tree is not clean, please run 'make mrproper'")
121 """Get the command name of GNU Make.
123 U-Boot needs GNU Make for building, but the command name is not
124 necessarily "make". (for example, "gmake" on FreeBSD).
125 Returns the most appropriate command name on your system.
127 with subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE) as proc:
128 ret = proc.communicate()
130 sys.exit('GNU Make not found')
131 return ret[0].rstrip()
133 def get_matched_defconfig(line):
134 """Get the defconfig files that match a pattern
137 line (str): Path or filename to match, e.g. 'configs/snow_defconfig' or
138 'k2*_defconfig'. If no directory is provided, 'configs/' is
142 list of str: a list of matching defconfig files
144 dirname = os.path.dirname(line)
148 pattern = os.path.join('configs', line)
149 return glob.glob(pattern) + glob.glob(pattern + '_defconfig')
151 def get_matched_defconfigs(defconfigs_file):
152 """Get all the defconfig files that match the patterns in a file.
155 defconfigs_file (str): File containing a list of defconfigs to process,
156 or '-' to read the list from stdin
159 list of str: A list of paths to defconfig files, with no duplicates
162 with ExitStack() as stack:
163 if defconfigs_file == '-':
165 defconfigs_file = 'stdin'
167 inf = stack.enter_context(open(defconfigs_file, encoding='utf-8'))
168 for i, line in enumerate(inf):
171 continue # skip blank lines silently
173 line = line.split(' ')[0] # handle 'git log' input
174 matched = get_matched_defconfig(line)
176 print(f"warning: {defconfigs_file}:{i + 1}: no defconfig matched '{line}'",
179 defconfigs += matched
181 # use set() to drop multiple matching
182 return [defconfig[len('configs') + 1:] for defconfig in set(defconfigs)]
184 def get_all_defconfigs():
185 """Get all the defconfig files under the configs/ directory.
188 list of str: List of paths to defconfig files
191 for (dirpath, _, filenames) in os.walk('configs'):
192 dirpath = dirpath[len('configs') + 1:]
193 for filename in fnmatch.filter(filenames, '*_defconfig'):
194 defconfigs.append(os.path.join(dirpath, filename))
198 def color_text(color_enabled, color, string):
199 """Return colored string."""
201 # LF should not be surrounded by the escape sequence.
202 # Otherwise, additional whitespace or line-feed might be printed.
203 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
204 for s in string.split('\n') ])
207 def show_diff(alines, blines, file_path, color_enabled):
208 """Show unidified diff.
211 alines (list of str): A list of lines (before)
212 blines (list of str): A list of lines (after)
213 file_path (str): Path to the file
214 color_enabled (bool): Display the diff in color
216 diff = difflib.unified_diff(alines, blines,
217 fromfile=os.path.join('a', file_path),
218 tofile=os.path.join('b', file_path))
221 if line.startswith('-') and not line.startswith('--'):
222 print(color_text(color_enabled, COLOR_RED, line))
223 elif line.startswith('+') and not line.startswith('++'):
224 print(color_text(color_enabled, COLOR_GREEN, line))
228 def extend_matched_lines(lines, matched, pre_patterns, post_patterns,
229 extend_pre, extend_post):
230 """Extend matched lines if desired patterns are found before/after already
234 lines (list of str): list of lines handled.
235 matched (list of int): list of line numbers that have been already
236 matched (will be updated by this function)
237 pre_patterns (list of re.Pattern): list of regular expression that should
238 be matched as preamble
239 post_patterns (list of re.Pattern): list of regular expression that should
240 be matched as postamble
241 extend_pre (bool): Add the line number of matched preamble to the matched
243 extend_post (bool): Add the line number of matched postamble to the
246 extended_matched = []
259 for pat in pre_patterns:
260 if pat.search(lines[i - 1]):
266 for pat in post_patterns:
267 if pat.search(lines[j]):
274 extended_matched.append(i - 1)
276 extended_matched.append(j)
278 matched += extended_matched
281 def confirm(args, prompt):
282 """Ask the user to confirm something
285 args (Namespace ): program arguments
288 bool: True to confirm, False to cancel/stop
292 choice = input(f'{prompt} [y/n]: ')
293 choice = choice.lower()
295 if choice in ('y', 'n'):
303 def write_file(fname, data):
304 """Write data to a file
307 fname (str): Filename to write to
308 data (list of str): Lines to write (with or without trailing newline);
311 with open(fname, 'w', encoding='utf-8') as out:
312 if isinstance(data, list):
314 print(line.rstrip('\n'), file=out)
318 def read_file(fname, as_lines=True, skip_unicode=False):
319 """Read a file and return the contents
322 fname (str): Filename to read from
323 as_lines: Return file contents as a list of lines
324 skip_unicode (bool): True to report unicode errors and continue
327 iter of str: List of ;ines from the file with newline removed; str if
328 as_lines is False with newlines intact; or None if a unicode error
332 UnicodeDecodeError: Unicode error occurred when reading
334 with open(fname, encoding='utf-8') as inf:
337 return [line.rstrip('\n') for line in inf.readlines()]
340 except UnicodeDecodeError as e:
343 print("Failed on file %s': %s" % (fname, e))
346 def cleanup_empty_blocks(header_path, args):
347 """Clean up empty conditional blocks
350 header_path (str): path to the cleaned file.
351 args (Namespace): program arguments
353 pattern = re.compile(r'^\s*#\s*if.*$\n^\s*#\s*endif.*$\n*', flags=re.M)
354 data = read_file(header_path, as_lines=False, skip_unicode=True)
358 new_data = pattern.sub('\n', data)
360 show_diff(data.splitlines(True), new_data.splitlines(True), header_path,
367 write_file(header_path, new_data)
369 def cleanup_one_header(header_path, patterns, args):
370 """Clean regex-matched lines away from a file.
373 header_path: path to the cleaned file.
374 patterns: list of regex patterns. Any lines matching to these
375 patterns are deleted.
376 args (Namespace): program arguments
378 lines = read_file(header_path, skip_unicode=True)
383 for i, line in enumerate(lines):
384 if i - 1 in matched and lines[i - 1].endswith('\\'):
387 for pattern in patterns:
388 if pattern.search(line):
395 # remove empty #ifdef ... #endif, successive blank lines
396 pattern_if = re.compile(r'#\s*if(def|ndef)?\b') # #if, #ifdef, #ifndef
397 pattern_elif = re.compile(r'#\s*el(if|se)\b') # #elif, #else
398 pattern_endif = re.compile(r'#\s*endif\b') # #endif
399 pattern_blank = re.compile(r'^\s*$') # empty line
402 old_matched = copy.copy(matched)
403 extend_matched_lines(lines, matched, [pattern_if],
404 [pattern_endif], True, True)
405 extend_matched_lines(lines, matched, [pattern_elif],
406 [pattern_elif, pattern_endif], True, False)
407 extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
408 [pattern_blank], False, True)
409 extend_matched_lines(lines, matched, [pattern_blank],
410 [pattern_elif, pattern_endif], True, False)
411 extend_matched_lines(lines, matched, [pattern_blank],
412 [pattern_blank], True, False)
413 if matched == old_matched:
416 tolines = copy.copy(lines)
418 for i in reversed(matched):
421 show_diff(lines, tolines, header_path, args.color)
426 write_file(header_path, tolines)
428 def cleanup_headers(configs, args):
429 """Delete config defines from board headers.
432 configs: A list of CONFIGs to remove.
433 args (Namespace): program arguments
435 if not confirm(args, 'Clean up headers?'):
439 for config in configs:
440 patterns.append(re.compile(r'#\s*define\s+%s\b' % config))
441 patterns.append(re.compile(r'#\s*undef\s+%s\b' % config))
443 for dir in 'include', 'arch', 'board':
444 for (dirpath, dirnames, filenames) in os.walk(dir):
445 if dirpath == os.path.join('include', 'generated'):
447 for filename in filenames:
448 if not filename.endswith(('~', '.dts', '.dtsi', '.bin',
449 '.elf','.aml','.dat')):
450 header_path = os.path.join(dirpath, filename)
451 # This file contains UTF-16 data and no CONFIG symbols
452 if header_path == 'include/video_font_data.h':
454 cleanup_one_header(header_path, patterns, args)
455 cleanup_empty_blocks(header_path, args)
457 def cleanup_one_extra_option(defconfig_path, configs, args):
458 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file.
461 defconfig_path: path to the cleaned defconfig file.
462 configs: A list of CONFIGs to remove.
463 args (Namespace): program arguments
466 start = 'CONFIG_SYS_EXTRA_OPTIONS="'
469 lines = read_file(defconfig_path)
471 for i, line in enumerate(lines):
472 if line.startswith(start) and line.endswith(end):
475 # CONFIG_SYS_EXTRA_OPTIONS was not found in this defconfig
478 old_tokens = line[len(start):-len(end)].split(',')
481 for token in old_tokens:
482 pos = token.find('=')
483 if not (token[:pos] if pos >= 0 else token) in configs:
484 new_tokens.append(token)
486 if new_tokens == old_tokens:
489 tolines = copy.copy(lines)
492 tolines[i] = start + ','.join(new_tokens) + end
496 show_diff(lines, tolines, defconfig_path, args.color)
501 write_file(defconfig_path, tolines)
503 def cleanup_extra_options(configs, args):
504 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files.
507 configs: A list of CONFIGs to remove.
508 args (Namespace): program arguments
510 if not confirm(args, 'Clean up CONFIG_SYS_EXTRA_OPTIONS?'):
513 configs = [ config[len('CONFIG_'):] for config in configs ]
515 defconfigs = get_all_defconfigs()
517 for defconfig in defconfigs:
518 cleanup_one_extra_option(os.path.join('configs', defconfig), configs,
521 def cleanup_whitelist(configs, args):
522 """Delete config whitelist entries
525 configs: A list of CONFIGs to remove.
526 args (Namespace): program arguments
528 if not confirm(args, 'Clean up whitelist entries?'):
531 lines = read_file(os.path.join('scripts', 'config_whitelist.txt'))
533 lines = [x for x in lines if x.strip() not in configs]
535 write_file(os.path.join('scripts', 'config_whitelist.txt'), lines)
537 def find_matching(patterns, line):
543 def cleanup_readme(configs, args):
544 """Delete config description in README
547 configs: A list of CONFIGs to remove.
548 args (Namespace): program arguments
550 if not confirm(args, 'Clean up README?'):
554 for config in configs:
555 patterns.append(re.compile(r'^\s+%s' % config))
557 lines = read_file('README')
563 found = find_matching(patterns, line)
567 if found and re.search(r'^\s+CONFIG', line):
571 newlines.append(line)
573 write_file('README', newlines)
575 def try_expand(line):
576 """If value looks like an expression, try expanding it
577 Otherwise just return the existing value
579 if line.find('=') == -1:
583 aeval = asteval.Interpreter( usersyms=SIZES, minimal=True )
584 cfg, val = re.split("=", line)
586 if re.search(r'[*+-/]|<<|SZ_+|\(([^\)]+)\)', val):
587 newval = hex(aeval(val))
588 print('\tExpanded expression %s to %s' % (val, newval))
589 return cfg+'='+newval
591 print('\tFailed to expand expression in %s' % line)
599 """Progress Indicator"""
601 def __init__(self, total):
602 """Create a new progress indicator.
605 total: A number of defconfig files to process.
611 """Increment the number of processed defconfig files."""
616 """Display the progress."""
617 print(' %d defconfigs out of %d\r' % (self.current, self.total), end=' ')
621 class KconfigScanner:
622 """Kconfig scanner."""
625 """Scan all the Kconfig files and create a Config object."""
626 # Define environment variables referenced from Kconfig
627 os.environ['srctree'] = os.getcwd()
628 os.environ['UBOOTVERSION'] = 'dummy'
629 os.environ['KCONFIG_OBJDIR'] = ''
630 self.conf = kconfiglib.Kconfig()
635 """A parser of .config and include/autoconf.mk."""
637 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
638 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
640 def __init__(self, configs, args, build_dir):
641 """Create a new parser.
644 configs: A list of CONFIGs to move.
645 args (Namespace): program arguments
646 build_dir: Build directory.
648 self.configs = configs
650 self.dotconfig = os.path.join(build_dir, '.config')
651 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
652 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
654 self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
655 self.defconfig = os.path.join(build_dir, 'defconfig')
658 """Parse .config file and return the architecture.
661 Architecture name (e.g. 'arm').
665 for line in read_file(self.dotconfig):
666 m = self.re_arch.match(line)
670 m = self.re_cpu.match(line)
678 if arch == 'arm' and cpu == 'armv8':
683 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
684 """Parse .config, defconfig, include/autoconf.mk for one config.
686 This function looks for the config options in the lines from
687 defconfig, .config, and include/autoconf.mk in order to decide
688 which action should be taken for this defconfig.
691 config: CONFIG name to parse.
692 dotconfig_lines: lines from the .config file.
693 autoconf_lines: lines from the include/autoconf.mk file.
696 A tupple of the action for this defconfig and the line
697 matched for the config.
699 not_set = '# %s is not set' % config
701 for line in autoconf_lines:
703 if line.startswith(config + '='):
709 new_val = try_expand(new_val)
711 for line in dotconfig_lines:
713 if line.startswith(config + '=') or line == not_set:
717 if new_val == not_set:
718 return (ACTION_NO_ENTRY, config)
720 return (ACTION_NO_ENTRY_WARN, config)
722 # If this CONFIG is neither bool nor trisate
723 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
724 # tools/scripts/define2mk.sed changes '1' to 'y'.
725 # This is a problem if the CONFIG is int type.
726 # Check the type in Kconfig and handle it correctly.
727 if new_val[-2:] == '=y':
728 new_val = new_val[:-1] + '1'
730 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
733 def update_dotconfig(self):
734 """Parse files for the config options and update the .config.
736 This function parses the generated .config and include/autoconf.mk
737 searching the target options.
738 Move the config option(s) to the .config as needed.
741 defconfig: defconfig name.
744 Return a tuple of (updated flag, log string).
745 The "updated flag" is True if the .config was updated, False
746 otherwise. The "log string" shows what happend to the .config.
752 rm_files = [self.config_autoconf, self.autoconf]
755 if os.path.exists(self.spl_autoconf):
756 autoconf_path = self.spl_autoconf
757 rm_files.append(self.spl_autoconf)
761 return (updated, suspicious,
762 color_text(self.args.color, COLOR_BROWN,
763 "SPL is not enabled. Skipped.") + '\n')
765 autoconf_path = self.autoconf
767 dotconfig_lines = read_file(self.dotconfig)
769 autoconf_lines = read_file(autoconf_path)
771 for config in self.configs:
772 result = self.parse_one_config(config, dotconfig_lines,
774 results.append(result)
778 for (action, value) in results:
779 if action == ACTION_MOVE:
780 actlog = "Move '%s'" % value
781 log_color = COLOR_LIGHT_GREEN
782 elif action == ACTION_NO_ENTRY:
783 actlog = '%s is not defined in Kconfig. Do nothing.' % value
784 log_color = COLOR_LIGHT_BLUE
785 elif action == ACTION_NO_ENTRY_WARN:
786 actlog = '%s is not defined in Kconfig (suspicious). Do nothing.' % value
787 log_color = COLOR_YELLOW
789 elif action == ACTION_NO_CHANGE:
790 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
792 log_color = COLOR_LIGHT_PURPLE
794 sys.exit('Internal Error. This should not happen.')
796 log += color_text(self.args.color, log_color, actlog) + '\n'
798 with open(self.dotconfig, 'a', encoding='utf-8') as out:
799 for (action, value) in results:
800 if action == ACTION_MOVE:
801 out.write(value + '\n')
804 self.results = results
808 return (updated, suspicious, log)
810 def check_defconfig(self):
811 """Check the defconfig after savedefconfig
814 Return additional log if moved CONFIGs were removed again by
815 'make savedefconfig'.
820 defconfig_lines = read_file(self.defconfig)
822 for (action, value) in self.results:
823 if action != ACTION_MOVE:
825 if not value in defconfig_lines:
826 log += color_text(self.args.color, COLOR_YELLOW,
827 "'%s' was removed by savedefconfig.\n" %
833 class DatabaseThread(threading.Thread):
834 """This thread processes results from Slot threads.
836 It collects the data in the master config directary. There is only one
837 result thread, and this helps to serialise the build output.
839 def __init__(self, config_db, db_queue):
840 """Set up a new result thread
843 builder: Builder which will be sent each result
845 threading.Thread.__init__(self)
846 self.config_db = config_db
847 self.db_queue= db_queue
850 """Called to start up the result thread.
852 We collect the next result job and pass it on to the build.
855 defconfig, configs = self.db_queue.get()
856 self.config_db[defconfig] = configs
857 self.db_queue.task_done()
862 """A slot to store a subprocess.
864 Each instance of this class handles one subprocess.
865 This class is useful to control multiple threads
866 for faster processing.
869 def __init__(self, toolchains, configs, args, progress, devnull,
870 make_cmd, reference_src_dir, db_queue):
871 """Create a new process slot.
874 toolchains: Toolchains object containing toolchains.
875 configs: A list of CONFIGs to move.
876 args: Program arguments
877 progress: A progress indicator.
878 devnull: A file object of '/dev/null'.
879 make_cmd: command name of GNU Make.
880 reference_src_dir: Determine the true starting config state from this
882 db_queue: output queue to write config info for the database
884 self.toolchains = toolchains
886 self.progress = progress
887 self.build_dir = tempfile.mkdtemp()
888 self.devnull = devnull
889 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
890 self.reference_src_dir = reference_src_dir
891 self.db_queue = db_queue
892 self.parser = KconfigParser(configs, args, self.build_dir)
893 self.state = STATE_IDLE
894 self.failed_boards = set()
895 self.suspicious_boards = set()
898 """Delete the working directory
900 This function makes sure the temporary directory is cleaned away
901 even if Python suddenly dies due to error. It should be done in here
902 because it is guaranteed the destructor is always invoked when the
903 instance of the class gets unreferenced.
905 If the subprocess is still running, wait until it finishes.
907 if self.state != STATE_IDLE:
908 while self.ps.poll() == None:
910 shutil.rmtree(self.build_dir)
912 def add(self, defconfig):
913 """Assign a new subprocess for defconfig and add it to the slot.
915 If the slot is vacant, create a new subprocess for processing the
916 given defconfig and add it to the slot. Just returns False if
917 the slot is occupied (i.e. the current subprocess is still running).
920 defconfig: defconfig name.
923 Return True on success or False on failure
925 if self.state != STATE_IDLE:
928 self.defconfig = defconfig
930 self.current_src_dir = self.reference_src_dir
935 """Check the status of the subprocess and handle it as needed.
937 Returns True if the slot is vacant (i.e. in idle state).
938 If the configuration is successfully finished, assign a new
939 subprocess to build include/autoconf.mk.
940 If include/autoconf.mk is generated, invoke the parser to
941 parse the .config and the include/autoconf.mk, moving
942 config options to the .config as needed.
943 If the .config was updated, run "make savedefconfig" to sync
944 it, update the original defconfig, and then set the slot back
948 Return True if the subprocess is terminated, False otherwise
950 if self.state == STATE_IDLE:
953 if self.ps.poll() == None:
956 if self.ps.poll() != 0:
958 elif self.state == STATE_DEFCONFIG:
959 if self.reference_src_dir and not self.current_src_dir:
960 self.do_savedefconfig()
963 elif self.state == STATE_AUTOCONF:
964 if self.current_src_dir:
965 self.current_src_dir = None
967 elif self.args.build_db:
970 self.do_savedefconfig()
971 elif self.state == STATE_SAVEDEFCONFIG:
972 self.update_defconfig()
974 sys.exit('Internal Error. This should not happen.')
976 return True if self.state == STATE_IDLE else False
978 def handle_error(self):
979 """Handle error cases."""
981 self.log += color_text(self.args.color, COLOR_LIGHT_RED,
982 'Failed to process.\n')
983 if self.args.verbose:
984 self.log += color_text(self.args.color, COLOR_LIGHT_CYAN,
985 self.ps.stderr.read().decode())
988 def do_defconfig(self):
989 """Run 'make <board>_defconfig' to create the .config file."""
991 cmd = list(self.make_cmd)
992 cmd.append(self.defconfig)
993 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
994 stderr=subprocess.PIPE,
995 cwd=self.current_src_dir)
996 self.state = STATE_DEFCONFIG
998 def do_autoconf(self):
999 """Run 'make AUTO_CONF_PATH'."""
1001 arch = self.parser.get_arch()
1003 toolchain = self.toolchains.Select(arch)
1005 self.log += color_text(self.args.color, COLOR_YELLOW,
1006 "Tool chain for '%s' is missing. Do nothing.\n" % arch)
1009 env = toolchain.MakeEnvironment(False)
1011 cmd = list(self.make_cmd)
1012 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
1013 cmd.append(AUTO_CONF_PATH)
1014 self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
1015 stderr=subprocess.PIPE,
1016 cwd=self.current_src_dir)
1017 self.state = STATE_AUTOCONF
1019 def do_build_db(self):
1020 """Add the board to the database"""
1022 for line in read_file(os.path.join(self.build_dir, AUTO_CONF_PATH)):
1023 if line.startswith('CONFIG'):
1024 config, value = line.split('=', 1)
1025 configs[config] = value.rstrip()
1026 self.db_queue.put([self.defconfig, configs])
1029 def do_savedefconfig(self):
1030 """Update the .config and run 'make savedefconfig'."""
1032 (updated, suspicious, log) = self.parser.update_dotconfig()
1034 self.suspicious_boards.add(self.defconfig)
1037 if not self.args.force_sync and not updated:
1041 self.log += color_text(self.args.color, COLOR_LIGHT_GREEN,
1042 'Syncing by savedefconfig...\n')
1044 self.log += 'Syncing by savedefconfig (forced by option)...\n'
1046 cmd = list(self.make_cmd)
1047 cmd.append('savedefconfig')
1048 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
1049 stderr=subprocess.PIPE)
1050 self.state = STATE_SAVEDEFCONFIG
1052 def update_defconfig(self):
1053 """Update the input defconfig and go back to the idle state."""
1055 log = self.parser.check_defconfig()
1057 self.suspicious_boards.add(self.defconfig)
1059 orig_defconfig = os.path.join('configs', self.defconfig)
1060 new_defconfig = os.path.join(self.build_dir, 'defconfig')
1061 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
1064 self.log += color_text(self.args.color, COLOR_LIGHT_BLUE,
1065 'defconfig was updated.\n')
1067 if not self.args.dry_run and updated:
1068 shutil.move(new_defconfig, orig_defconfig)
1071 def finish(self, success):
1072 """Display log along with progress and go to the idle state.
1075 success: Should be True when the defconfig was processed
1076 successfully, or False when it fails.
1078 # output at least 30 characters to hide the "* defconfigs out of *".
1079 log = self.defconfig.ljust(30) + '\n'
1081 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
1082 # Some threads are running in parallel.
1083 # Print log atomically to not mix up logs from different threads.
1084 print(log, file=(sys.stdout if success else sys.stderr))
1087 if self.args.exit_on_error:
1088 sys.exit('Exit on error.')
1089 # If --exit-on-error flag is not set, skip this board and continue.
1090 # Record the failed board.
1091 self.failed_boards.add(self.defconfig)
1094 self.progress.show()
1095 self.state = STATE_IDLE
1097 def get_failed_boards(self):
1098 """Returns a set of failed boards (defconfigs) in this slot.
1100 return self.failed_boards
1102 def get_suspicious_boards(self):
1103 """Returns a set of boards (defconfigs) with possible misconversion.
1105 return self.suspicious_boards - self.failed_boards
1109 """Controller of the array of subprocess slots."""
1111 def __init__(self, toolchains, configs, args, progress,
1112 reference_src_dir, db_queue):
1113 """Create a new slots controller.
1116 toolchains: Toolchains object containing toolchains.
1117 configs: A list of CONFIGs to move.
1118 args: Program arguments
1119 progress: A progress indicator.
1120 reference_src_dir: Determine the true starting config state from this
1122 db_queue: output queue to write config info for the database
1126 devnull = subprocess.DEVNULL
1127 make_cmd = get_make_cmd()
1128 for i in range(args.jobs):
1129 self.slots.append(Slot(toolchains, configs, args, progress,
1130 devnull, make_cmd, reference_src_dir,
1133 def add(self, defconfig):
1134 """Add a new subprocess if a vacant slot is found.
1137 defconfig: defconfig name to be put into.
1140 Return True on success or False on failure
1142 for slot in self.slots:
1143 if slot.add(defconfig):
1147 def available(self):
1148 """Check if there is a vacant slot.
1151 Return True if at lease one vacant slot is found, False otherwise.
1153 for slot in self.slots:
1159 """Check if all slots are vacant.
1162 Return True if all the slots are vacant, False otherwise.
1165 for slot in self.slots:
1170 def show_failed_boards(self):
1171 """Display all of the failed boards (defconfigs)."""
1173 output_file = 'moveconfig.failed'
1175 for slot in self.slots:
1176 boards |= slot.get_failed_boards()
1179 boards = '\n'.join(boards) + '\n'
1180 msg = 'The following boards were not processed due to error:\n'
1182 msg += '(the list has been saved in %s)\n' % output_file
1183 print(color_text(self.args.color, COLOR_LIGHT_RED,
1184 msg), file=sys.stderr)
1186 write_file(output_file, boards)
1188 def show_suspicious_boards(self):
1189 """Display all boards (defconfigs) with possible misconversion."""
1191 output_file = 'moveconfig.suspicious'
1193 for slot in self.slots:
1194 boards |= slot.get_suspicious_boards()
1197 boards = '\n'.join(boards) + '\n'
1198 msg = 'The following boards might have been converted incorrectly.\n'
1199 msg += 'It is highly recommended to check them manually:\n'
1201 msg += '(the list has been saved in %s)\n' % output_file
1202 print(color_text(self.args.color, COLOR_YELLOW,
1203 msg), file=sys.stderr)
1205 write_file(output_file, boards)
1207 class ReferenceSource:
1209 """Reference source against which original configs should be parsed."""
1211 def __init__(self, commit):
1212 """Create a reference source directory based on a specified commit.
1215 commit: commit to git-clone
1217 self.src_dir = tempfile.mkdtemp()
1218 print('Cloning git repo to a separate work directory...')
1219 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1221 print("Checkout '%s' to build the original autoconf.mk." % \
1222 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip())
1223 subprocess.check_output(['git', 'checkout', commit],
1224 stderr=subprocess.STDOUT, cwd=self.src_dir)
1227 """Delete the reference source directory
1229 This function makes sure the temporary directory is cleaned away
1230 even if Python suddenly dies due to error. It should be done in here
1231 because it is guaranteed the destructor is always invoked when the
1232 instance of the class gets unreferenced.
1234 shutil.rmtree(self.src_dir)
1237 """Return the absolute path to the reference source directory."""
1241 def move_config(toolchains, configs, args, db_queue):
1242 """Move config options to defconfig files.
1245 configs: A list of CONFIGs to move.
1246 args: Program arguments
1248 if len(configs) == 0:
1250 print('No CONFIG is specified. You are probably syncing defconfigs.', end=' ')
1252 print('Building %s database' % CONFIG_DATABASE)
1254 print('Neither CONFIG nor --force-sync is specified. Nothing will happen.', end=' ')
1256 print('Move ' + ', '.join(configs), end=' ')
1257 print('(jobs: %d)\n' % args.jobs)
1260 reference_src = ReferenceSource(args.git_ref)
1261 reference_src_dir = reference_src.get_dir()
1263 reference_src_dir = None
1266 defconfigs = get_matched_defconfigs(args.defconfigs)
1268 defconfigs = get_all_defconfigs()
1270 progress = Progress(len(defconfigs))
1271 slots = Slots(toolchains, configs, args, progress, reference_src_dir,
1274 # Main loop to process defconfig files:
1275 # Add a new subprocess into a vacant slot.
1276 # Sleep if there is no available slot.
1277 for defconfig in defconfigs:
1278 while not slots.add(defconfig):
1279 while not slots.available():
1280 # No available slot: sleep for a while
1281 time.sleep(SLEEP_TIME)
1283 # wait until all the subprocesses finish
1284 while not slots.empty():
1285 time.sleep(SLEEP_TIME)
1288 slots.show_failed_boards()
1289 slots.show_suspicious_boards()
1291 def find_kconfig_rules(kconf, config, imply_config):
1292 """Check whether a config has a 'select' or 'imply' keyword
1295 kconf: Kconfiglib.Kconfig object
1296 config: Name of config to check (without CONFIG_ prefix)
1297 imply_config: Implying config (without CONFIG_ prefix) which may or
1298 may not have an 'imply' for 'config')
1301 Symbol object for 'config' if found, else None
1303 sym = kconf.syms.get(imply_config)
1305 for sel, cond in (sym.selects + sym.implies):
1306 if sel.name == config:
1310 def check_imply_rule(kconf, config, imply_config):
1311 """Check if we can add an 'imply' option
1313 This finds imply_config in the Kconfig and looks to see if it is possible
1314 to add an 'imply' for 'config' to that part of the Kconfig.
1317 kconf: Kconfiglib.Kconfig object
1318 config: Name of config to check (without CONFIG_ prefix)
1319 imply_config: Implying config (without CONFIG_ prefix) which may or
1320 may not have an 'imply' for 'config')
1324 filename of Kconfig file containing imply_config, or None if none
1325 line number within the Kconfig file, or 0 if none
1326 message indicating the result
1328 sym = kconf.syms.get(imply_config)
1330 return 'cannot find sym'
1333 return '%d locations' % len(nodes)
1335 fname, linenum = node.filename, node.linenr
1337 if cwd and fname.startswith(cwd):
1338 fname = fname[len(cwd) + 1:]
1339 file_line = ' at %s:%d' % (fname, linenum)
1340 data = read_file(fname)
1341 if data[linenum - 1] != 'config %s' % imply_config:
1342 return None, 0, 'bad sym format %s%s' % (data[linenum], file_line)
1343 return fname, linenum, 'adding%s' % file_line
1345 def add_imply_rule(config, fname, linenum):
1346 """Add a new 'imply' option to a Kconfig
1349 config: config option to add an imply for (without CONFIG_ prefix)
1350 fname: Kconfig filename to update
1351 linenum: Line number to place the 'imply' before
1354 Message indicating the result
1356 file_line = ' at %s:%d' % (fname, linenum)
1357 data = read_file(fname)
1360 for offset, line in enumerate(data[linenum:]):
1361 if line.strip().startswith('help') or not line:
1362 data.insert(linenum + offset, '\timply %s' % config)
1363 write_file(fname, data)
1364 return 'added%s' % file_line
1366 return 'could not insert%s'
1368 (IMPLY_MIN_2, IMPLY_TARGET, IMPLY_CMD, IMPLY_NON_ARCH_BOARD) = (
1372 'min2': [IMPLY_MIN_2, 'Show options which imply >2 boards (normally >5)'],
1373 'target': [IMPLY_TARGET, 'Allow CONFIG_TARGET_... options to imply'],
1374 'cmd': [IMPLY_CMD, 'Allow CONFIG_CMD_... to imply'],
1376 IMPLY_NON_ARCH_BOARD,
1377 'Allow Kconfig options outside arch/ and /board/ to imply'],
1381 def read_database():
1382 """Read in the config database
1386 set of all config options seen (each a str)
1387 set of all defconfigs seen (each a str)
1388 dict of configs for each defconfig:
1389 key: defconfig name, e.g. "MPC8548CDS_legacy_defconfig"
1392 value: Value of option
1393 dict of defconfigs for each config:
1395 value: set of boards using that option
1400 # key is defconfig name, value is dict of (CONFIG_xxx, value)
1403 # Set of all config options we have seen
1406 # Set of all defconfigs we have seen
1407 all_defconfigs = set()
1409 defconfig_db = collections.defaultdict(set)
1410 for line in read_file(CONFIG_DATABASE):
1411 line = line.rstrip()
1412 if not line: # Separator between defconfigs
1413 config_db[defconfig] = configs
1414 all_defconfigs.add(defconfig)
1416 elif line[0] == ' ': # CONFIG line
1417 config, value = line.strip().split('=', 1)
1418 configs[config] = value
1419 defconfig_db[config].add(defconfig)
1420 all_configs.add(config)
1421 else: # New defconfig
1424 return all_configs, all_defconfigs, config_db, defconfig_db
1427 def do_imply_config(config_list, add_imply, imply_flags, skip_added,
1428 check_kconfig=True, find_superset=False):
1429 """Find CONFIG options which imply those in the list
1431 Some CONFIG options can be implied by others and this can help to reduce
1432 the size of the defconfig files. For example, CONFIG_X86 implies
1433 CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
1434 all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
1435 each of the x86 defconfig files.
1437 This function uses the moveconfig database to find such options. It
1438 displays a list of things that could possibly imply those in the list.
1439 The algorithm ignores any that start with CONFIG_TARGET since these
1440 typically refer to only a few defconfigs (often one). It also does not
1441 display a config with less than 5 defconfigs.
1443 The algorithm works using sets. For each target config in config_list:
1444 - Get the set 'defconfigs' which use that target config
1445 - For each config (from a list of all configs):
1446 - Get the set 'imply_defconfig' of defconfigs which use that config
1448 - If imply_defconfigs contains anything not in defconfigs then
1449 this config does not imply the target config
1452 config_list: List of CONFIG options to check (each a string)
1453 add_imply: Automatically add an 'imply' for each config.
1454 imply_flags: Flags which control which implying configs are allowed
1456 skip_added: Don't show options which already have an imply added.
1457 check_kconfig: Check if implied symbols already have an 'imply' or
1458 'select' for the target config, and show this information if so.
1459 find_superset: True to look for configs which are a superset of those
1460 already found. So for example if CONFIG_EXYNOS5 implies an option,
1461 but CONFIG_EXYNOS covers a larger set of defconfigs and also
1462 implies that option, this will drop the former in favour of the
1463 latter. In practice this option has not proved very used.
1465 Note the terminoloy:
1466 config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
1467 defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
1469 kconf = KconfigScanner().conf if check_kconfig else None
1470 if add_imply and add_imply != 'all':
1471 add_imply = add_imply.split(',')
1473 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
1475 # Work through each target config option in turn, independently
1476 for config in config_list:
1477 defconfigs = defconfig_db.get(config)
1479 print('%s not found in any defconfig' % config)
1482 # Get the set of defconfigs without this one (since a config cannot
1484 non_defconfigs = all_defconfigs - defconfigs
1485 num_defconfigs = len(defconfigs)
1486 print('%s found in %d/%d defconfigs' % (config, num_defconfigs,
1489 # This will hold the results: key=config, value=defconfigs containing it
1491 rest_configs = all_configs - set([config])
1493 # Look at every possible config, except the target one
1494 for imply_config in rest_configs:
1495 if 'ERRATUM' in imply_config:
1497 if not imply_flags & IMPLY_CMD:
1498 if 'CONFIG_CMD' in imply_config:
1500 if not imply_flags & IMPLY_TARGET:
1501 if 'CONFIG_TARGET' in imply_config:
1504 # Find set of defconfigs that have this config
1505 imply_defconfig = defconfig_db[imply_config]
1507 # Get the intersection of this with defconfigs containing the
1509 common_defconfigs = imply_defconfig & defconfigs
1511 # Get the set of defconfigs containing this config which DO NOT
1512 # also contain the taret config. If this set is non-empty it means
1513 # that this config affects other defconfigs as well as (possibly)
1514 # the ones affected by the target config. This means it implies
1515 # things we don't want to imply.
1516 not_common_defconfigs = imply_defconfig & non_defconfigs
1517 if not_common_defconfigs:
1520 # If there are common defconfigs, imply_config may be useful
1521 if common_defconfigs:
1524 for prev in list(imply_configs.keys()):
1525 prev_count = len(imply_configs[prev])
1526 count = len(common_defconfigs)
1527 if (prev_count > count and
1528 (imply_configs[prev] & common_defconfigs ==
1529 common_defconfigs)):
1530 # skip imply_config because prev is a superset
1533 elif count > prev_count:
1534 # delete prev because imply_config is a superset
1535 del imply_configs[prev]
1537 imply_configs[imply_config] = common_defconfigs
1539 # Now we have a dict imply_configs of configs which imply each config
1540 # The value of each dict item is the set of defconfigs containing that
1541 # config. Rank them so that we print the configs that imply the largest
1542 # number of defconfigs first.
1543 ranked_iconfigs = sorted(imply_configs,
1544 key=lambda k: len(imply_configs[k]), reverse=True)
1547 add_list = collections.defaultdict(list)
1548 for iconfig in ranked_iconfigs:
1549 num_common = len(imply_configs[iconfig])
1551 # Don't bother if there are less than 5 defconfigs affected.
1552 if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
1554 missing = defconfigs - imply_configs[iconfig]
1555 missing_str = ', '.join(missing) if missing else 'all'
1559 sym = find_kconfig_rules(kconf, config[CONFIG_LEN:],
1560 iconfig[CONFIG_LEN:])
1565 fname, linenum = nodes[0].filename, nodes[0].linenr
1566 if cwd and fname.startswith(cwd):
1567 fname = fname[len(cwd) + 1:]
1568 kconfig_info = '%s:%d' % (fname, linenum)
1572 sym = kconf.syms.get(iconfig[CONFIG_LEN:])
1577 fname, linenum = nodes[0].filename, nodes[0].linenr
1578 if cwd and fname.startswith(cwd):
1579 fname = fname[len(cwd) + 1:]
1580 in_arch_board = not sym or (fname.startswith('arch') or
1581 fname.startswith('board'))
1582 if (not in_arch_board and
1583 not imply_flags & IMPLY_NON_ARCH_BOARD):
1586 if add_imply and (add_imply == 'all' or
1587 iconfig in add_imply):
1588 fname, linenum, kconfig_info = (check_imply_rule(kconf,
1589 config[CONFIG_LEN:], iconfig[CONFIG_LEN:]))
1591 add_list[fname].append(linenum)
1593 if show and kconfig_info != 'skip':
1594 print('%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
1595 kconfig_info, missing_str))
1597 # Having collected a list of things to add, now we add them. We process
1598 # each file from the largest line number to the smallest so that
1599 # earlier additions do not affect our line numbers. E.g. if we added an
1600 # imply at line 20 it would change the position of each line after
1602 for fname, linenums in add_list.items():
1603 for linenum in sorted(linenums, reverse=True):
1604 add_imply_rule(config[CONFIG_LEN:], fname, linenum)
1606 def defconfig_matches(configs, re_match):
1607 """Check if any CONFIG option matches a regex
1609 The match must be complete, i.e. from the start to end of the CONFIG option.
1612 configs (dict): Dict of CONFIG options:
1614 value: Value of option
1615 re_match (re.Pattern): Match to check
1618 bool: True if any CONFIG matches the regex
1621 m_cfg = re_match.match(cfg)
1622 if m_cfg and m_cfg.span()[1] == len(cfg):
1626 def do_find_config(config_list):
1627 """Find boards with a given combination of CONFIGs
1630 config_list: List of CONFIG options to check (each a regex consisting
1631 of a config option, with or without a CONFIG_ prefix. If an option
1632 is preceded by a tilde (~) then it must be false, otherwise it must
1635 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
1638 adhoc_configs = set(read_file('scripts/config_whitelist.txt'))
1640 # Start with all defconfigs
1641 out = all_defconfigs
1643 # Work through each config in turn
1645 for item in config_list:
1646 # Get the real config name and whether we want this config or not
1653 if cfg in adhoc_configs:
1657 # Search everything that is still in the running. If it has a config
1658 # that we want, or doesn't have one that we don't, add it into the
1659 # running for the next stage
1662 re_match = re.compile(cfg)
1663 for defc in in_list:
1664 has_cfg = defconfig_matches(config_db[defc], re_match)
1668 print(f"Error: Not in Kconfig: %s" % ' '.join(adhoc))
1670 print(f'{len(out)} matches')
1671 print(' '.join([remove_defconfig(item) for item in out]))
1674 def prefix_config(cfg):
1675 """Prefix a config with CONFIG_ if needed
1677 This handles ~ operator, which indicates that the CONFIG should be disabled
1679 >>> prefix_config('FRED')
1681 >>> prefix_config('CONFIG_FRED')
1683 >>> prefix_config('~FRED')
1685 >>> prefix_config('~CONFIG_FRED')
1687 >>> prefix_config('A123')
1694 if not cfg.startswith('CONFIG_'):
1695 cfg = 'CONFIG_' + cfg
1701 cpu_count = multiprocessing.cpu_count()
1702 except NotImplementedError:
1705 epilog = '''Move config options from headers to defconfig files. See
1706 doc/develop/moveconfig.rst for documentation.'''
1708 parser = ArgumentParser(epilog=epilog)
1709 # Add arguments here
1710 parser.add_argument('-a', '--add-imply', type=str, default='',
1711 help='comma-separated list of CONFIG options to add '
1712 "an 'imply' statement to for the CONFIG in -i")
1713 parser.add_argument('-A', '--skip-added', action='store_true', default=False,
1714 help="don't show options which are already marked as "
1716 parser.add_argument('-b', '--build-db', action='store_true', default=False,
1717 help='build a CONFIG database')
1718 parser.add_argument('-c', '--color', action='store_true', default=False,
1719 help='display the log in color')
1720 parser.add_argument('-C', '--commit', action='store_true', default=False,
1721 help='Create a git commit for the operation')
1722 parser.add_argument('-d', '--defconfigs', type=str,
1723 help='a file containing a list of defconfigs to move, '
1724 "one per line (for example 'snow_defconfig') "
1725 "or '-' to read from stdin")
1726 parser.add_argument('-e', '--exit-on-error', action='store_true',
1728 help='exit immediately on any error')
1729 parser.add_argument('-f', '--find', action='store_true', default=False,
1730 help='Find boards with a given config combination')
1731 parser.add_argument('-H', '--headers-only', dest='cleanup_headers_only',
1732 action='store_true', default=False,
1733 help='only cleanup the headers')
1734 parser.add_argument('-i', '--imply', action='store_true', default=False,
1735 help='find options which imply others')
1736 parser.add_argument('-I', '--imply-flags', type=str, default='',
1737 help="control the -i option ('help' for help")
1738 parser.add_argument('-j', '--jobs', type=int, default=cpu_count,
1739 help='the number of jobs to run simultaneously')
1740 parser.add_argument('-n', '--dry-run', action='store_true', default=False,
1741 help='perform a trial run (show log with no changes)')
1742 parser.add_argument('-r', '--git-ref', type=str,
1743 help='the git ref to clone for building the autoconf.mk')
1744 parser.add_argument('-s', '--force-sync', action='store_true', default=False,
1745 help='force sync by savedefconfig')
1746 parser.add_argument('-S', '--spl', action='store_true', default=False,
1747 help='parse config options defined for SPL build')
1748 parser.add_argument('-t', '--test', action='store_true', default=False,
1749 help='run unit tests')
1750 parser.add_argument('-y', '--yes', action='store_true', default=False,
1751 help="respond 'yes' to any prompts")
1752 parser.add_argument('-v', '--verbose', action='store_true', default=False,
1753 help='show any build errors as boards are built')
1754 parser.add_argument('configs', nargs='*')
1756 args = parser.parse_args()
1757 configs = args.configs
1760 sys.argv = [sys.argv[0]]
1761 fail, count = doctest.testmod()
1766 if not any((len(configs), args.force_sync, args.build_db, args.imply,
1768 parser.print_usage()
1771 # prefix the option name with CONFIG_ if missing
1772 configs = [prefix_config(cfg) for cfg in configs]
1774 check_top_directory()
1778 if args.imply_flags == 'all':
1781 elif args.imply_flags:
1782 for flag in args.imply_flags.split(','):
1783 bad = flag not in IMPLY_FLAGS
1785 print("Invalid flag '%s'" % flag)
1786 if flag == 'help' or bad:
1787 print("Imply flags: (separate with ',')")
1788 for name, info in IMPLY_FLAGS.items():
1789 print(' %-15s: %s' % (name, info[1]))
1790 parser.print_usage()
1792 imply_flags |= IMPLY_FLAGS[flag][0]
1794 do_imply_config(configs, args.add_imply, imply_flags, args.skip_added)
1798 do_find_config(configs)
1802 db_queue = queue.Queue()
1803 t = DatabaseThread(config_db, db_queue)
1807 if not args.cleanup_headers_only:
1808 check_clean_directory()
1810 toolchains = toolchain.Toolchains()
1811 toolchains.GetSettings()
1812 toolchains.Scan(verbose=False)
1813 move_config(toolchains, configs, args, db_queue)
1817 cleanup_headers(configs, args)
1818 cleanup_extra_options(configs, args)
1819 cleanup_whitelist(configs, args)
1820 cleanup_readme(configs, args)
1823 subprocess.call(['git', 'add', '-u'])
1825 msg = 'Convert %s %sto Kconfig' % (configs[0],
1826 'et al ' if len(configs) > 1 else '')
1827 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1828 '\n '.join(configs))
1830 msg = 'configs: Resync with savedefconfig'
1831 msg += '\n\nRsync all defconfig files using moveconfig.py'
1832 subprocess.call(['git', 'commit', '-s', '-m', msg])
1835 with open(CONFIG_DATABASE, 'w', encoding='utf-8') as fd:
1836 for defconfig, configs in config_db.items():
1837 fd.write('%s\n' % defconfig)
1838 for config in sorted(configs.keys()):
1839 fd.write(' %s=%s\n' % (config, configs[config]))
1842 if __name__ == '__main__':