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
793 elif action == ACTION_SPL_NOT_EXIST:
794 actlog = 'SPL is not enabled for this defconfig. Skip.'
795 log_color = COLOR_PURPLE
797 sys.exit('Internal Error. This should not happen.')
799 log += color_text(self.args.color, log_color, actlog) + '\n'
801 with open(self.dotconfig, 'a', encoding='utf-8') as out:
802 for (action, value) in results:
803 if action == ACTION_MOVE:
804 out.write(value + '\n')
807 self.results = results
811 return (updated, suspicious, log)
813 def check_defconfig(self):
814 """Check the defconfig after savedefconfig
817 Return additional log if moved CONFIGs were removed again by
818 'make savedefconfig'.
823 defconfig_lines = read_file(self.defconfig)
825 for (action, value) in self.results:
826 if action != ACTION_MOVE:
828 if not value in defconfig_lines:
829 log += color_text(self.args.color, COLOR_YELLOW,
830 "'%s' was removed by savedefconfig.\n" %
836 class DatabaseThread(threading.Thread):
837 """This thread processes results from Slot threads.
839 It collects the data in the master config directary. There is only one
840 result thread, and this helps to serialise the build output.
842 def __init__(self, config_db, db_queue):
843 """Set up a new result thread
846 builder: Builder which will be sent each result
848 threading.Thread.__init__(self)
849 self.config_db = config_db
850 self.db_queue= db_queue
853 """Called to start up the result thread.
855 We collect the next result job and pass it on to the build.
858 defconfig, configs = self.db_queue.get()
859 self.config_db[defconfig] = configs
860 self.db_queue.task_done()
865 """A slot to store a subprocess.
867 Each instance of this class handles one subprocess.
868 This class is useful to control multiple threads
869 for faster processing.
872 def __init__(self, toolchains, configs, args, progress, devnull,
873 make_cmd, reference_src_dir, db_queue):
874 """Create a new process slot.
877 toolchains: Toolchains object containing toolchains.
878 configs: A list of CONFIGs to move.
879 args: Program arguments
880 progress: A progress indicator.
881 devnull: A file object of '/dev/null'.
882 make_cmd: command name of GNU Make.
883 reference_src_dir: Determine the true starting config state from this
885 db_queue: output queue to write config info for the database
887 self.toolchains = toolchains
889 self.progress = progress
890 self.build_dir = tempfile.mkdtemp()
891 self.devnull = devnull
892 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
893 self.reference_src_dir = reference_src_dir
894 self.db_queue = db_queue
895 self.parser = KconfigParser(configs, args, self.build_dir)
896 self.state = STATE_IDLE
897 self.failed_boards = set()
898 self.suspicious_boards = set()
901 """Delete the working directory
903 This function makes sure the temporary directory is cleaned away
904 even if Python suddenly dies due to error. It should be done in here
905 because it is guaranteed the destructor is always invoked when the
906 instance of the class gets unreferenced.
908 If the subprocess is still running, wait until it finishes.
910 if self.state != STATE_IDLE:
911 while self.ps.poll() == None:
913 shutil.rmtree(self.build_dir)
915 def add(self, defconfig):
916 """Assign a new subprocess for defconfig and add it to the slot.
918 If the slot is vacant, create a new subprocess for processing the
919 given defconfig and add it to the slot. Just returns False if
920 the slot is occupied (i.e. the current subprocess is still running).
923 defconfig: defconfig name.
926 Return True on success or False on failure
928 if self.state != STATE_IDLE:
931 self.defconfig = defconfig
933 self.current_src_dir = self.reference_src_dir
938 """Check the status of the subprocess and handle it as needed.
940 Returns True if the slot is vacant (i.e. in idle state).
941 If the configuration is successfully finished, assign a new
942 subprocess to build include/autoconf.mk.
943 If include/autoconf.mk is generated, invoke the parser to
944 parse the .config and the include/autoconf.mk, moving
945 config options to the .config as needed.
946 If the .config was updated, run "make savedefconfig" to sync
947 it, update the original defconfig, and then set the slot back
951 Return True if the subprocess is terminated, False otherwise
953 if self.state == STATE_IDLE:
956 if self.ps.poll() == None:
959 if self.ps.poll() != 0:
961 elif self.state == STATE_DEFCONFIG:
962 if self.reference_src_dir and not self.current_src_dir:
963 self.do_savedefconfig()
966 elif self.state == STATE_AUTOCONF:
967 if self.current_src_dir:
968 self.current_src_dir = None
970 elif self.args.build_db:
973 self.do_savedefconfig()
974 elif self.state == STATE_SAVEDEFCONFIG:
975 self.update_defconfig()
977 sys.exit('Internal Error. This should not happen.')
979 return True if self.state == STATE_IDLE else False
981 def handle_error(self):
982 """Handle error cases."""
984 self.log += color_text(self.args.color, COLOR_LIGHT_RED,
985 'Failed to process.\n')
986 if self.args.verbose:
987 self.log += color_text(self.args.color, COLOR_LIGHT_CYAN,
988 self.ps.stderr.read().decode())
991 def do_defconfig(self):
992 """Run 'make <board>_defconfig' to create the .config file."""
994 cmd = list(self.make_cmd)
995 cmd.append(self.defconfig)
996 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
997 stderr=subprocess.PIPE,
998 cwd=self.current_src_dir)
999 self.state = STATE_DEFCONFIG
1001 def do_autoconf(self):
1002 """Run 'make AUTO_CONF_PATH'."""
1004 arch = self.parser.get_arch()
1006 toolchain = self.toolchains.Select(arch)
1008 self.log += color_text(self.args.color, COLOR_YELLOW,
1009 "Tool chain for '%s' is missing. Do nothing.\n" % arch)
1012 env = toolchain.MakeEnvironment(False)
1014 cmd = list(self.make_cmd)
1015 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
1016 cmd.append(AUTO_CONF_PATH)
1017 self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
1018 stderr=subprocess.PIPE,
1019 cwd=self.current_src_dir)
1020 self.state = STATE_AUTOCONF
1022 def do_build_db(self):
1023 """Add the board to the database"""
1025 for line in read_file(os.path.join(self.build_dir, AUTO_CONF_PATH)):
1026 if line.startswith('CONFIG'):
1027 config, value = line.split('=', 1)
1028 configs[config] = value.rstrip()
1029 self.db_queue.put([self.defconfig, configs])
1032 def do_savedefconfig(self):
1033 """Update the .config and run 'make savedefconfig'."""
1035 (updated, suspicious, log) = self.parser.update_dotconfig()
1037 self.suspicious_boards.add(self.defconfig)
1040 if not self.args.force_sync and not updated:
1044 self.log += color_text(self.args.color, COLOR_LIGHT_GREEN,
1045 'Syncing by savedefconfig...\n')
1047 self.log += 'Syncing by savedefconfig (forced by option)...\n'
1049 cmd = list(self.make_cmd)
1050 cmd.append('savedefconfig')
1051 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
1052 stderr=subprocess.PIPE)
1053 self.state = STATE_SAVEDEFCONFIG
1055 def update_defconfig(self):
1056 """Update the input defconfig and go back to the idle state."""
1058 log = self.parser.check_defconfig()
1060 self.suspicious_boards.add(self.defconfig)
1062 orig_defconfig = os.path.join('configs', self.defconfig)
1063 new_defconfig = os.path.join(self.build_dir, 'defconfig')
1064 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
1067 self.log += color_text(self.args.color, COLOR_LIGHT_BLUE,
1068 'defconfig was updated.\n')
1070 if not self.args.dry_run and updated:
1071 shutil.move(new_defconfig, orig_defconfig)
1074 def finish(self, success):
1075 """Display log along with progress and go to the idle state.
1078 success: Should be True when the defconfig was processed
1079 successfully, or False when it fails.
1081 # output at least 30 characters to hide the "* defconfigs out of *".
1082 log = self.defconfig.ljust(30) + '\n'
1084 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
1085 # Some threads are running in parallel.
1086 # Print log atomically to not mix up logs from different threads.
1087 print(log, file=(sys.stdout if success else sys.stderr))
1090 if self.args.exit_on_error:
1091 sys.exit('Exit on error.')
1092 # If --exit-on-error flag is not set, skip this board and continue.
1093 # Record the failed board.
1094 self.failed_boards.add(self.defconfig)
1097 self.progress.show()
1098 self.state = STATE_IDLE
1100 def get_failed_boards(self):
1101 """Returns a set of failed boards (defconfigs) in this slot.
1103 return self.failed_boards
1105 def get_suspicious_boards(self):
1106 """Returns a set of boards (defconfigs) with possible misconversion.
1108 return self.suspicious_boards - self.failed_boards
1112 """Controller of the array of subprocess slots."""
1114 def __init__(self, toolchains, configs, args, progress,
1115 reference_src_dir, db_queue):
1116 """Create a new slots controller.
1119 toolchains: Toolchains object containing toolchains.
1120 configs: A list of CONFIGs to move.
1121 args: Program arguments
1122 progress: A progress indicator.
1123 reference_src_dir: Determine the true starting config state from this
1125 db_queue: output queue to write config info for the database
1129 devnull = subprocess.DEVNULL
1130 make_cmd = get_make_cmd()
1131 for i in range(args.jobs):
1132 self.slots.append(Slot(toolchains, configs, args, progress,
1133 devnull, make_cmd, reference_src_dir,
1136 def add(self, defconfig):
1137 """Add a new subprocess if a vacant slot is found.
1140 defconfig: defconfig name to be put into.
1143 Return True on success or False on failure
1145 for slot in self.slots:
1146 if slot.add(defconfig):
1150 def available(self):
1151 """Check if there is a vacant slot.
1154 Return True if at lease one vacant slot is found, False otherwise.
1156 for slot in self.slots:
1162 """Check if all slots are vacant.
1165 Return True if all the slots are vacant, False otherwise.
1168 for slot in self.slots:
1173 def show_failed_boards(self):
1174 """Display all of the failed boards (defconfigs)."""
1176 output_file = 'moveconfig.failed'
1178 for slot in self.slots:
1179 boards |= slot.get_failed_boards()
1182 boards = '\n'.join(boards) + '\n'
1183 msg = 'The following boards were not processed due to error:\n'
1185 msg += '(the list has been saved in %s)\n' % output_file
1186 print(color_text(self.args.color, COLOR_LIGHT_RED,
1187 msg), file=sys.stderr)
1189 write_file(output_file, boards)
1191 def show_suspicious_boards(self):
1192 """Display all boards (defconfigs) with possible misconversion."""
1194 output_file = 'moveconfig.suspicious'
1196 for slot in self.slots:
1197 boards |= slot.get_suspicious_boards()
1200 boards = '\n'.join(boards) + '\n'
1201 msg = 'The following boards might have been converted incorrectly.\n'
1202 msg += 'It is highly recommended to check them manually:\n'
1204 msg += '(the list has been saved in %s)\n' % output_file
1205 print(color_text(self.args.color, COLOR_YELLOW,
1206 msg), file=sys.stderr)
1208 write_file(output_file, boards)
1210 class ReferenceSource:
1212 """Reference source against which original configs should be parsed."""
1214 def __init__(self, commit):
1215 """Create a reference source directory based on a specified commit.
1218 commit: commit to git-clone
1220 self.src_dir = tempfile.mkdtemp()
1221 print('Cloning git repo to a separate work directory...')
1222 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1224 print("Checkout '%s' to build the original autoconf.mk." % \
1225 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip())
1226 subprocess.check_output(['git', 'checkout', commit],
1227 stderr=subprocess.STDOUT, cwd=self.src_dir)
1230 """Delete the reference source directory
1232 This function makes sure the temporary directory is cleaned away
1233 even if Python suddenly dies due to error. It should be done in here
1234 because it is guaranteed the destructor is always invoked when the
1235 instance of the class gets unreferenced.
1237 shutil.rmtree(self.src_dir)
1240 """Return the absolute path to the reference source directory."""
1244 def move_config(toolchains, configs, args, db_queue):
1245 """Move config options to defconfig files.
1248 configs: A list of CONFIGs to move.
1249 args: Program arguments
1251 if len(configs) == 0:
1253 print('No CONFIG is specified. You are probably syncing defconfigs.', end=' ')
1255 print('Building %s database' % CONFIG_DATABASE)
1257 print('Neither CONFIG nor --force-sync is specified. Nothing will happen.', end=' ')
1259 print('Move ' + ', '.join(configs), end=' ')
1260 print('(jobs: %d)\n' % args.jobs)
1263 reference_src = ReferenceSource(args.git_ref)
1264 reference_src_dir = reference_src.get_dir()
1266 reference_src_dir = None
1269 defconfigs = get_matched_defconfigs(args.defconfigs)
1271 defconfigs = get_all_defconfigs()
1273 progress = Progress(len(defconfigs))
1274 slots = Slots(toolchains, configs, args, progress, reference_src_dir,
1277 # Main loop to process defconfig files:
1278 # Add a new subprocess into a vacant slot.
1279 # Sleep if there is no available slot.
1280 for defconfig in defconfigs:
1281 while not slots.add(defconfig):
1282 while not slots.available():
1283 # No available slot: sleep for a while
1284 time.sleep(SLEEP_TIME)
1286 # wait until all the subprocesses finish
1287 while not slots.empty():
1288 time.sleep(SLEEP_TIME)
1291 slots.show_failed_boards()
1292 slots.show_suspicious_boards()
1294 def find_kconfig_rules(kconf, config, imply_config):
1295 """Check whether a config has a 'select' or 'imply' keyword
1298 kconf: Kconfiglib.Kconfig object
1299 config: Name of config to check (without CONFIG_ prefix)
1300 imply_config: Implying config (without CONFIG_ prefix) which may or
1301 may not have an 'imply' for 'config')
1304 Symbol object for 'config' if found, else None
1306 sym = kconf.syms.get(imply_config)
1308 for sel, cond in (sym.selects + sym.implies):
1309 if sel.name == config:
1313 def check_imply_rule(kconf, config, imply_config):
1314 """Check if we can add an 'imply' option
1316 This finds imply_config in the Kconfig and looks to see if it is possible
1317 to add an 'imply' for 'config' to that part of the Kconfig.
1320 kconf: Kconfiglib.Kconfig object
1321 config: Name of config to check (without CONFIG_ prefix)
1322 imply_config: Implying config (without CONFIG_ prefix) which may or
1323 may not have an 'imply' for 'config')
1327 filename of Kconfig file containing imply_config, or None if none
1328 line number within the Kconfig file, or 0 if none
1329 message indicating the result
1331 sym = kconf.syms.get(imply_config)
1333 return 'cannot find sym'
1336 return '%d locations' % len(nodes)
1338 fname, linenum = node.filename, node.linenr
1340 if cwd and fname.startswith(cwd):
1341 fname = fname[len(cwd) + 1:]
1342 file_line = ' at %s:%d' % (fname, linenum)
1343 data = read_file(fname)
1344 if data[linenum - 1] != 'config %s' % imply_config:
1345 return None, 0, 'bad sym format %s%s' % (data[linenum], file_line)
1346 return fname, linenum, 'adding%s' % file_line
1348 def add_imply_rule(config, fname, linenum):
1349 """Add a new 'imply' option to a Kconfig
1352 config: config option to add an imply for (without CONFIG_ prefix)
1353 fname: Kconfig filename to update
1354 linenum: Line number to place the 'imply' before
1357 Message indicating the result
1359 file_line = ' at %s:%d' % (fname, linenum)
1360 data = read_file(fname)
1363 for offset, line in enumerate(data[linenum:]):
1364 if line.strip().startswith('help') or not line:
1365 data.insert(linenum + offset, '\timply %s' % config)
1366 write_file(fname, data)
1367 return 'added%s' % file_line
1369 return 'could not insert%s'
1371 (IMPLY_MIN_2, IMPLY_TARGET, IMPLY_CMD, IMPLY_NON_ARCH_BOARD) = (
1375 'min2': [IMPLY_MIN_2, 'Show options which imply >2 boards (normally >5)'],
1376 'target': [IMPLY_TARGET, 'Allow CONFIG_TARGET_... options to imply'],
1377 'cmd': [IMPLY_CMD, 'Allow CONFIG_CMD_... to imply'],
1379 IMPLY_NON_ARCH_BOARD,
1380 'Allow Kconfig options outside arch/ and /board/ to imply'],
1384 def read_database():
1385 """Read in the config database
1389 set of all config options seen (each a str)
1390 set of all defconfigs seen (each a str)
1391 dict of configs for each defconfig:
1392 key: defconfig name, e.g. "MPC8548CDS_legacy_defconfig"
1395 value: Value of option
1396 dict of defconfigs for each config:
1398 value: set of boards using that option
1403 # key is defconfig name, value is dict of (CONFIG_xxx, value)
1406 # Set of all config options we have seen
1409 # Set of all defconfigs we have seen
1410 all_defconfigs = set()
1412 defconfig_db = collections.defaultdict(set)
1413 for line in read_file(CONFIG_DATABASE):
1414 line = line.rstrip()
1415 if not line: # Separator between defconfigs
1416 config_db[defconfig] = configs
1417 all_defconfigs.add(defconfig)
1419 elif line[0] == ' ': # CONFIG line
1420 config, value = line.strip().split('=', 1)
1421 configs[config] = value
1422 defconfig_db[config].add(defconfig)
1423 all_configs.add(config)
1424 else: # New defconfig
1427 return all_configs, all_defconfigs, config_db, defconfig_db
1430 def do_imply_config(config_list, add_imply, imply_flags, skip_added,
1431 check_kconfig=True, find_superset=False):
1432 """Find CONFIG options which imply those in the list
1434 Some CONFIG options can be implied by others and this can help to reduce
1435 the size of the defconfig files. For example, CONFIG_X86 implies
1436 CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
1437 all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
1438 each of the x86 defconfig files.
1440 This function uses the moveconfig database to find such options. It
1441 displays a list of things that could possibly imply those in the list.
1442 The algorithm ignores any that start with CONFIG_TARGET since these
1443 typically refer to only a few defconfigs (often one). It also does not
1444 display a config with less than 5 defconfigs.
1446 The algorithm works using sets. For each target config in config_list:
1447 - Get the set 'defconfigs' which use that target config
1448 - For each config (from a list of all configs):
1449 - Get the set 'imply_defconfig' of defconfigs which use that config
1451 - If imply_defconfigs contains anything not in defconfigs then
1452 this config does not imply the target config
1455 config_list: List of CONFIG options to check (each a string)
1456 add_imply: Automatically add an 'imply' for each config.
1457 imply_flags: Flags which control which implying configs are allowed
1459 skip_added: Don't show options which already have an imply added.
1460 check_kconfig: Check if implied symbols already have an 'imply' or
1461 'select' for the target config, and show this information if so.
1462 find_superset: True to look for configs which are a superset of those
1463 already found. So for example if CONFIG_EXYNOS5 implies an option,
1464 but CONFIG_EXYNOS covers a larger set of defconfigs and also
1465 implies that option, this will drop the former in favour of the
1466 latter. In practice this option has not proved very used.
1468 Note the terminoloy:
1469 config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
1470 defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
1472 kconf = KconfigScanner().conf if check_kconfig else None
1473 if add_imply and add_imply != 'all':
1474 add_imply = add_imply.split(',')
1476 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
1478 # Work through each target config option in turn, independently
1479 for config in config_list:
1480 defconfigs = defconfig_db.get(config)
1482 print('%s not found in any defconfig' % config)
1485 # Get the set of defconfigs without this one (since a config cannot
1487 non_defconfigs = all_defconfigs - defconfigs
1488 num_defconfigs = len(defconfigs)
1489 print('%s found in %d/%d defconfigs' % (config, num_defconfigs,
1492 # This will hold the results: key=config, value=defconfigs containing it
1494 rest_configs = all_configs - set([config])
1496 # Look at every possible config, except the target one
1497 for imply_config in rest_configs:
1498 if 'ERRATUM' in imply_config:
1500 if not imply_flags & IMPLY_CMD:
1501 if 'CONFIG_CMD' in imply_config:
1503 if not imply_flags & IMPLY_TARGET:
1504 if 'CONFIG_TARGET' in imply_config:
1507 # Find set of defconfigs that have this config
1508 imply_defconfig = defconfig_db[imply_config]
1510 # Get the intersection of this with defconfigs containing the
1512 common_defconfigs = imply_defconfig & defconfigs
1514 # Get the set of defconfigs containing this config which DO NOT
1515 # also contain the taret config. If this set is non-empty it means
1516 # that this config affects other defconfigs as well as (possibly)
1517 # the ones affected by the target config. This means it implies
1518 # things we don't want to imply.
1519 not_common_defconfigs = imply_defconfig & non_defconfigs
1520 if not_common_defconfigs:
1523 # If there are common defconfigs, imply_config may be useful
1524 if common_defconfigs:
1527 for prev in list(imply_configs.keys()):
1528 prev_count = len(imply_configs[prev])
1529 count = len(common_defconfigs)
1530 if (prev_count > count and
1531 (imply_configs[prev] & common_defconfigs ==
1532 common_defconfigs)):
1533 # skip imply_config because prev is a superset
1536 elif count > prev_count:
1537 # delete prev because imply_config is a superset
1538 del imply_configs[prev]
1540 imply_configs[imply_config] = common_defconfigs
1542 # Now we have a dict imply_configs of configs which imply each config
1543 # The value of each dict item is the set of defconfigs containing that
1544 # config. Rank them so that we print the configs that imply the largest
1545 # number of defconfigs first.
1546 ranked_iconfigs = sorted(imply_configs,
1547 key=lambda k: len(imply_configs[k]), reverse=True)
1550 add_list = collections.defaultdict(list)
1551 for iconfig in ranked_iconfigs:
1552 num_common = len(imply_configs[iconfig])
1554 # Don't bother if there are less than 5 defconfigs affected.
1555 if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
1557 missing = defconfigs - imply_configs[iconfig]
1558 missing_str = ', '.join(missing) if missing else 'all'
1562 sym = find_kconfig_rules(kconf, config[CONFIG_LEN:],
1563 iconfig[CONFIG_LEN:])
1568 fname, linenum = nodes[0].filename, nodes[0].linenr
1569 if cwd and fname.startswith(cwd):
1570 fname = fname[len(cwd) + 1:]
1571 kconfig_info = '%s:%d' % (fname, linenum)
1575 sym = kconf.syms.get(iconfig[CONFIG_LEN:])
1580 fname, linenum = nodes[0].filename, nodes[0].linenr
1581 if cwd and fname.startswith(cwd):
1582 fname = fname[len(cwd) + 1:]
1583 in_arch_board = not sym or (fname.startswith('arch') or
1584 fname.startswith('board'))
1585 if (not in_arch_board and
1586 not imply_flags & IMPLY_NON_ARCH_BOARD):
1589 if add_imply and (add_imply == 'all' or
1590 iconfig in add_imply):
1591 fname, linenum, kconfig_info = (check_imply_rule(kconf,
1592 config[CONFIG_LEN:], iconfig[CONFIG_LEN:]))
1594 add_list[fname].append(linenum)
1596 if show and kconfig_info != 'skip':
1597 print('%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
1598 kconfig_info, missing_str))
1600 # Having collected a list of things to add, now we add them. We process
1601 # each file from the largest line number to the smallest so that
1602 # earlier additions do not affect our line numbers. E.g. if we added an
1603 # imply at line 20 it would change the position of each line after
1605 for fname, linenums in add_list.items():
1606 for linenum in sorted(linenums, reverse=True):
1607 add_imply_rule(config[CONFIG_LEN:], fname, linenum)
1609 def defconfig_matches(configs, re_match):
1610 """Check if any CONFIG option matches a regex
1612 The match must be complete, i.e. from the start to end of the CONFIG option.
1615 configs (dict): Dict of CONFIG options:
1617 value: Value of option
1618 re_match (re.Pattern): Match to check
1621 bool: True if any CONFIG matches the regex
1624 m_cfg = re_match.match(cfg)
1625 if m_cfg and m_cfg.span()[1] == len(cfg):
1629 def do_find_config(config_list):
1630 """Find boards with a given combination of CONFIGs
1633 config_list: List of CONFIG options to check (each a regex consisting
1634 of a config option, with or without a CONFIG_ prefix. If an option
1635 is preceded by a tilde (~) then it must be false, otherwise it must
1638 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
1641 adhoc_configs = set(read_file('scripts/config_whitelist.txt'))
1643 # Start with all defconfigs
1644 out = all_defconfigs
1646 # Work through each config in turn
1648 for item in config_list:
1649 # Get the real config name and whether we want this config or not
1656 if cfg in adhoc_configs:
1660 # Search everything that is still in the running. If it has a config
1661 # that we want, or doesn't have one that we don't, add it into the
1662 # running for the next stage
1665 re_match = re.compile(cfg)
1666 for defc in in_list:
1667 has_cfg = defconfig_matches(config_db[defc], re_match)
1671 print(f"Error: Not in Kconfig: %s" % ' '.join(adhoc))
1673 print(f'{len(out)} matches')
1674 print(' '.join([remove_defconfig(item) for item in out]))
1677 def prefix_config(cfg):
1678 """Prefix a config with CONFIG_ if needed
1680 This handles ~ operator, which indicates that the CONFIG should be disabled
1682 >>> prefix_config('FRED')
1684 >>> prefix_config('CONFIG_FRED')
1686 >>> prefix_config('~FRED')
1688 >>> prefix_config('~CONFIG_FRED')
1690 >>> prefix_config('A123')
1697 if not cfg.startswith('CONFIG_'):
1698 cfg = 'CONFIG_' + cfg
1704 cpu_count = multiprocessing.cpu_count()
1705 except NotImplementedError:
1708 epilog = '''Move config options from headers to defconfig files. See
1709 doc/develop/moveconfig.rst for documentation.'''
1711 parser = ArgumentParser(epilog=epilog)
1712 # Add arguments here
1713 parser.add_argument('-a', '--add-imply', type=str, default='',
1714 help='comma-separated list of CONFIG options to add '
1715 "an 'imply' statement to for the CONFIG in -i")
1716 parser.add_argument('-A', '--skip-added', action='store_true', default=False,
1717 help="don't show options which are already marked as "
1719 parser.add_argument('-b', '--build-db', action='store_true', default=False,
1720 help='build a CONFIG database')
1721 parser.add_argument('-c', '--color', action='store_true', default=False,
1722 help='display the log in color')
1723 parser.add_argument('-C', '--commit', action='store_true', default=False,
1724 help='Create a git commit for the operation')
1725 parser.add_argument('-d', '--defconfigs', type=str,
1726 help='a file containing a list of defconfigs to move, '
1727 "one per line (for example 'snow_defconfig') "
1728 "or '-' to read from stdin")
1729 parser.add_argument('-e', '--exit-on-error', action='store_true',
1731 help='exit immediately on any error')
1732 parser.add_argument('-f', '--find', action='store_true', default=False,
1733 help='Find boards with a given config combination')
1734 parser.add_argument('-H', '--headers-only', dest='cleanup_headers_only',
1735 action='store_true', default=False,
1736 help='only cleanup the headers')
1737 parser.add_argument('-i', '--imply', action='store_true', default=False,
1738 help='find options which imply others')
1739 parser.add_argument('-I', '--imply-flags', type=str, default='',
1740 help="control the -i option ('help' for help")
1741 parser.add_argument('-j', '--jobs', type=int, default=cpu_count,
1742 help='the number of jobs to run simultaneously')
1743 parser.add_argument('-n', '--dry-run', action='store_true', default=False,
1744 help='perform a trial run (show log with no changes)')
1745 parser.add_argument('-r', '--git-ref', type=str,
1746 help='the git ref to clone for building the autoconf.mk')
1747 parser.add_argument('-s', '--force-sync', action='store_true', default=False,
1748 help='force sync by savedefconfig')
1749 parser.add_argument('-S', '--spl', action='store_true', default=False,
1750 help='parse config options defined for SPL build')
1751 parser.add_argument('-t', '--test', action='store_true', default=False,
1752 help='run unit tests')
1753 parser.add_argument('-y', '--yes', action='store_true', default=False,
1754 help="respond 'yes' to any prompts")
1755 parser.add_argument('-v', '--verbose', action='store_true', default=False,
1756 help='show any build errors as boards are built')
1757 parser.add_argument('configs', nargs='*')
1759 args = parser.parse_args()
1760 configs = args.configs
1763 sys.argv = [sys.argv[0]]
1764 fail, count = doctest.testmod()
1769 if not any((len(configs), args.force_sync, args.build_db, args.imply,
1771 parser.print_usage()
1774 # prefix the option name with CONFIG_ if missing
1775 configs = [prefix_config(cfg) for cfg in configs]
1777 check_top_directory()
1781 if args.imply_flags == 'all':
1784 elif args.imply_flags:
1785 for flag in args.imply_flags.split(','):
1786 bad = flag not in IMPLY_FLAGS
1788 print("Invalid flag '%s'" % flag)
1789 if flag == 'help' or bad:
1790 print("Imply flags: (separate with ',')")
1791 for name, info in IMPLY_FLAGS.items():
1792 print(' %-15s: %s' % (name, info[1]))
1793 parser.print_usage()
1795 imply_flags |= IMPLY_FLAGS[flag][0]
1797 do_imply_config(configs, args.add_imply, imply_flags, args.skip_added)
1801 do_find_config(configs)
1805 db_queue = queue.Queue()
1806 t = DatabaseThread(config_db, db_queue)
1810 if not args.cleanup_headers_only:
1811 check_clean_directory()
1813 toolchains = toolchain.Toolchains()
1814 toolchains.GetSettings()
1815 toolchains.Scan(verbose=False)
1816 move_config(toolchains, configs, args, db_queue)
1820 cleanup_headers(configs, args)
1821 cleanup_extra_options(configs, args)
1822 cleanup_whitelist(configs, args)
1823 cleanup_readme(configs, args)
1826 subprocess.call(['git', 'add', '-u'])
1828 msg = 'Convert %s %sto Kconfig' % (configs[0],
1829 'et al ' if len(configs) > 1 else '')
1830 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1831 '\n '.join(configs))
1833 msg = 'configs: Resync with savedefconfig'
1834 msg += '\n\nRsync all defconfig files using moveconfig.py'
1835 subprocess.call(['git', 'commit', '-s', '-m', msg])
1838 with open(CONFIG_DATABASE, 'w', encoding='utf-8') as fd:
1839 for defconfig, configs in config_db.items():
1840 fd.write('%s\n' % defconfig)
1841 for config in sorted(configs.keys()):
1842 fd.write(' %s=%s\n' % (config, configs[config]))
1845 if __name__ == '__main__':