blob: 476603ce1472e665e36c2dc682c4f79d8f87198b [file] [log] [blame]
Simon Glass793dca32019-10-31 07:42:57 -06001#!/usr/bin/env python3
Tom Rini83d290c2018-05-06 17:58:06 -04002# SPDX-License-Identifier: GPL-2.0+
Masahiro Yamada5a27c732015-05-20 11:36:07 +09003#
4# Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5#
Masahiro Yamada5a27c732015-05-20 11:36:07 +09006
7"""
8Move config options from headers to defconfig files.
9
Simon Glass5c72c0e2021-07-21 21:35:51 -060010See doc/develop/moveconfig.rst for documentation.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090011"""
12
Simon Glassb2e83c62021-12-18 14:54:31 -070013from argparse import ArgumentParser
Simon Glass99b66602017-06-01 19:39:03 -060014import collections
Simon Glass91197aa2021-12-18 14:54:35 -070015from contextlib import ExitStack
Simon Glass84067a52021-12-18 08:09:45 -070016import doctest
Masahiro Yamadac8e1b102016-05-19 15:52:07 +090017import filecmp
Masahiro Yamada5a27c732015-05-20 11:36:07 +090018import fnmatch
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +090019import glob
Masahiro Yamada5a27c732015-05-20 11:36:07 +090020import multiprocessing
Masahiro Yamada5a27c732015-05-20 11:36:07 +090021import os
Simon Glass793dca32019-10-31 07:42:57 -060022import queue
Masahiro Yamada5a27c732015-05-20 11:36:07 +090023import re
24import shutil
25import subprocess
26import sys
27import tempfile
Simon Glassd73fcb12017-06-01 19:39:02 -060028import threading
Masahiro Yamada5a27c732015-05-20 11:36:07 +090029import time
Simon Glass84067a52021-12-18 08:09:45 -070030import unittest
Masahiro Yamada5a27c732015-05-20 11:36:07 +090031
Simon Glassb5aa5a32023-09-23 13:43:52 -060032import asteval
Simon Glass0ede00f2020-04-17 18:09:02 -060033from buildman import bsettings
34from buildman import kconfiglib
35from buildman import toolchain
Simon Glasscb008832017-06-15 21:39:33 -060036
Masahiro Yamada5a27c732015-05-20 11:36:07 +090037SHOW_GNU_MAKE = 'scripts/show-gnu-make'
38SLEEP_TIME=0.03
39
Masahiro Yamada5a27c732015-05-20 11:36:07 +090040STATE_IDLE = 0
41STATE_DEFCONFIG = 1
42STATE_AUTOCONF = 2
Joe Hershberger96464ba2015-05-19 13:21:17 -050043STATE_SAVEDEFCONFIG = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +090044
45ACTION_MOVE = 0
Masahiro Yamadacc008292016-05-19 15:51:56 +090046ACTION_NO_ENTRY = 1
Masahiro Yamada916224c2016-08-22 22:18:21 +090047ACTION_NO_ENTRY_WARN = 2
48ACTION_NO_CHANGE = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +090049
50COLOR_BLACK = '0;30'
51COLOR_RED = '0;31'
52COLOR_GREEN = '0;32'
53COLOR_BROWN = '0;33'
54COLOR_BLUE = '0;34'
55COLOR_PURPLE = '0;35'
56COLOR_CYAN = '0;36'
57COLOR_LIGHT_GRAY = '0;37'
58COLOR_DARK_GRAY = '1;30'
59COLOR_LIGHT_RED = '1;31'
60COLOR_LIGHT_GREEN = '1;32'
61COLOR_YELLOW = '1;33'
62COLOR_LIGHT_BLUE = '1;34'
63COLOR_LIGHT_PURPLE = '1;35'
64COLOR_LIGHT_CYAN = '1;36'
65COLOR_WHITE = '1;37'
66
Simon Glassf3b8e642017-06-01 19:39:01 -060067AUTO_CONF_PATH = 'include/config/auto.conf'
Simon Glassd73fcb12017-06-01 19:39:02 -060068CONFIG_DATABASE = 'moveconfig.db'
Simon Glassf3b8e642017-06-01 19:39:01 -060069
Simon Glasscb008832017-06-15 21:39:33 -060070CONFIG_LEN = len('CONFIG_')
Simon Glassf3b8e642017-06-01 19:39:01 -060071
Markus Klotzbuecherb237d352019-05-15 15:15:52 +020072SIZES = {
Simon Glassdaa694d2021-12-18 14:54:30 -070073 'SZ_1': 0x00000001, 'SZ_2': 0x00000002,
74 'SZ_4': 0x00000004, 'SZ_8': 0x00000008,
75 'SZ_16': 0x00000010, 'SZ_32': 0x00000020,
76 'SZ_64': 0x00000040, 'SZ_128': 0x00000080,
77 'SZ_256': 0x00000100, 'SZ_512': 0x00000200,
78 'SZ_1K': 0x00000400, 'SZ_2K': 0x00000800,
79 'SZ_4K': 0x00001000, 'SZ_8K': 0x00002000,
80 'SZ_16K': 0x00004000, 'SZ_32K': 0x00008000,
81 'SZ_64K': 0x00010000, 'SZ_128K': 0x00020000,
82 'SZ_256K': 0x00040000, 'SZ_512K': 0x00080000,
83 'SZ_1M': 0x00100000, 'SZ_2M': 0x00200000,
84 'SZ_4M': 0x00400000, 'SZ_8M': 0x00800000,
85 'SZ_16M': 0x01000000, 'SZ_32M': 0x02000000,
86 'SZ_64M': 0x04000000, 'SZ_128M': 0x08000000,
87 'SZ_256M': 0x10000000, 'SZ_512M': 0x20000000,
88 'SZ_1G': 0x40000000, 'SZ_2G': 0x80000000,
89 'SZ_4G': 0x100000000
Markus Klotzbuecherb237d352019-05-15 15:15:52 +020090}
91
Simon Glassb8d11da2022-02-08 11:49:45 -070092RE_REMOVE_DEFCONFIG = re.compile(r'(.*)_defconfig')
93
Simon Glass65e62032023-02-01 13:19:12 -070094# CONFIG symbols present in the build system (from Linux) but not actually used
95# in U-Boot; KCONFIG symbols
96IGNORE_SYMS = ['DEBUG_SECTION_MISMATCH', 'FTRACE_MCOUNT_RECORD', 'GCOV_KERNEL',
97 'GCOV_PROFILE_ALL', 'KALLSYMS', 'KASAN', 'MODVERSIONS', 'SHELL',
98 'TPL_BUILD', 'VPL_BUILD', 'IS_ENABLED', 'FOO', 'IF_ENABLED_INT',
99 'IS_ENABLED_', 'IS_ENABLED_1', 'IS_ENABLED_2', 'IS_ENABLED_3',
100 'SPL_', 'TPL_', 'SPL_FOO', 'TPL_FOO', 'TOOLS_FOO',
101 'ACME', 'SPL_ACME', 'TPL_ACME', 'TRACE_BRANCH_PROFILING',
102 'VAL', '_UNDEFINED', 'SPL_BUILD', ]
103
104SPL_PREFIXES = ['SPL_', 'TPL_', 'VPL_', 'TOOLS_']
105
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900106### helper functions ###
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900107def check_top_directory():
108 """Exit if we are not at the top of source directory."""
Simon Glass91197aa2021-12-18 14:54:35 -0700109 for fname in 'README', 'Licenses':
110 if not os.path.exists(fname):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900111 sys.exit('Please run at the top of source directory.')
112
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900113def check_clean_directory():
114 """Exit if the source tree is not clean."""
Simon Glass91197aa2021-12-18 14:54:35 -0700115 for fname in '.config', 'include/config':
116 if os.path.exists(fname):
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900117 sys.exit("source tree is not clean, please run 'make mrproper'")
118
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900119def get_make_cmd():
120 """Get the command name of GNU Make.
121
122 U-Boot needs GNU Make for building, but the command name is not
123 necessarily "make". (for example, "gmake" on FreeBSD).
124 Returns the most appropriate command name on your system.
125 """
Simon Glass91197aa2021-12-18 14:54:35 -0700126 with subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE) as proc:
127 ret = proc.communicate()
128 if proc.returncode:
129 sys.exit('GNU Make not found')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900130 return ret[0].rstrip()
131
Simon Glass25f978c2017-06-01 19:38:58 -0600132def get_matched_defconfig(line):
133 """Get the defconfig files that match a pattern
134
135 Args:
Simon Glass91197aa2021-12-18 14:54:35 -0700136 line (str): Path or filename to match, e.g. 'configs/snow_defconfig' or
Simon Glass25f978c2017-06-01 19:38:58 -0600137 'k2*_defconfig'. If no directory is provided, 'configs/' is
138 prepended
139
140 Returns:
Simon Glass91197aa2021-12-18 14:54:35 -0700141 list of str: a list of matching defconfig files
Simon Glass25f978c2017-06-01 19:38:58 -0600142 """
143 dirname = os.path.dirname(line)
144 if dirname:
145 pattern = line
146 else:
147 pattern = os.path.join('configs', line)
148 return glob.glob(pattern) + glob.glob(pattern + '_defconfig')
149
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900150def get_matched_defconfigs(defconfigs_file):
Simon Glassee4e61b2017-06-01 19:38:59 -0600151 """Get all the defconfig files that match the patterns in a file.
152
153 Args:
Simon Glass91197aa2021-12-18 14:54:35 -0700154 defconfigs_file (str): File containing a list of defconfigs to process,
155 or '-' to read the list from stdin
Simon Glassee4e61b2017-06-01 19:38:59 -0600156
157 Returns:
Simon Glass91197aa2021-12-18 14:54:35 -0700158 list of str: A list of paths to defconfig files, with no duplicates
Simon Glassee4e61b2017-06-01 19:38:59 -0600159 """
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900160 defconfigs = []
Simon Glass91197aa2021-12-18 14:54:35 -0700161 with ExitStack() as stack:
162 if defconfigs_file == '-':
163 inf = sys.stdin
164 defconfigs_file = 'stdin'
165 else:
166 inf = stack.enter_context(open(defconfigs_file, encoding='utf-8'))
167 for i, line in enumerate(inf):
168 line = line.strip()
169 if not line:
170 continue # skip blank lines silently
171 if ' ' in line:
172 line = line.split(' ')[0] # handle 'git log' input
173 matched = get_matched_defconfig(line)
174 if not matched:
175 print(f"warning: {defconfigs_file}:{i + 1}: no defconfig matched '{line}'",
176 file=sys.stderr)
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900177
Simon Glass91197aa2021-12-18 14:54:35 -0700178 defconfigs += matched
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900179
180 # use set() to drop multiple matching
Simon Glass91197aa2021-12-18 14:54:35 -0700181 return [defconfig[len('configs') + 1:] for defconfig in set(defconfigs)]
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900182
Masahiro Yamada684c3062016-07-25 19:15:28 +0900183def get_all_defconfigs():
Simon Glass91197aa2021-12-18 14:54:35 -0700184 """Get all the defconfig files under the configs/ directory.
185
186 Returns:
187 list of str: List of paths to defconfig files
188 """
Masahiro Yamada684c3062016-07-25 19:15:28 +0900189 defconfigs = []
Simon Glass91197aa2021-12-18 14:54:35 -0700190 for (dirpath, _, filenames) in os.walk('configs'):
Masahiro Yamada684c3062016-07-25 19:15:28 +0900191 dirpath = dirpath[len('configs') + 1:]
192 for filename in fnmatch.filter(filenames, '*_defconfig'):
193 defconfigs.append(os.path.join(dirpath, filename))
194
195 return defconfigs
196
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900197def color_text(color_enabled, color, string):
198 """Return colored string."""
199 if color_enabled:
Masahiro Yamada1d085562016-05-19 15:52:02 +0900200 # LF should not be surrounded by the escape sequence.
201 # Otherwise, additional whitespace or line-feed might be printed.
202 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
203 for s in string.split('\n') ])
Simon Glass91197aa2021-12-18 14:54:35 -0700204 return string
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900205
Simon Glass2fd85bd2021-12-18 14:54:33 -0700206def write_file(fname, data):
207 """Write data to a file
208
209 Args:
210 fname (str): Filename to write to
211 data (list of str): Lines to write (with or without trailing newline);
212 or str to write
213 """
214 with open(fname, 'w', encoding='utf-8') as out:
215 if isinstance(data, list):
216 for line in data:
217 print(line.rstrip('\n'), file=out)
218 else:
219 out.write(data)
220
Simon Glass37f815c2021-12-18 14:54:34 -0700221def read_file(fname, as_lines=True, skip_unicode=False):
222 """Read a file and return the contents
223
224 Args:
225 fname (str): Filename to read from
226 as_lines: Return file contents as a list of lines
227 skip_unicode (bool): True to report unicode errors and continue
228
229 Returns:
230 iter of str: List of ;ines from the file with newline removed; str if
231 as_lines is False with newlines intact; or None if a unicode error
232 occurred
233
234 Raises:
235 UnicodeDecodeError: Unicode error occurred when reading
236 """
237 with open(fname, encoding='utf-8') as inf:
238 try:
239 if as_lines:
240 return [line.rstrip('\n') for line in inf.readlines()]
241 else:
242 return inf.read()
243 except UnicodeDecodeError as e:
244 if not skip_unicode:
Simon Glass68a0b712022-02-11 13:23:22 -0700245 raise
Simon Glass37f815c2021-12-18 14:54:34 -0700246 print("Failed on file %s': %s" % (fname, e))
247 return None
248
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200249def try_expand(line):
250 """If value looks like an expression, try expanding it
251 Otherwise just return the existing value
252 """
253 if line.find('=') == -1:
254 return line
255
256 try:
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +0100257 aeval = asteval.Interpreter( usersyms=SIZES, minimal=True )
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200258 cfg, val = re.split("=", line)
259 val= val.strip('\"')
Simon Glassdaa694d2021-12-18 14:54:30 -0700260 if re.search(r'[*+-/]|<<|SZ_+|\(([^\)]+)\)', val):
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +0100261 newval = hex(aeval(val))
Simon Glassdaa694d2021-12-18 14:54:30 -0700262 print('\tExpanded expression %s to %s' % (val, newval))
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200263 return cfg+'='+newval
264 except:
Simon Glassdaa694d2021-12-18 14:54:30 -0700265 print('\tFailed to expand expression in %s' % line)
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200266
267 return line
268
Chris Packhamca438342017-05-02 21:30:47 +1200269
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900270### classes ###
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900271class Progress:
272
273 """Progress Indicator"""
274
275 def __init__(self, total):
276 """Create a new progress indicator.
277
Simon Glass91197aa2021-12-18 14:54:35 -0700278 Args:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900279 total: A number of defconfig files to process.
280 """
281 self.current = 0
282 self.total = total
283
284 def inc(self):
285 """Increment the number of processed defconfig files."""
286
287 self.current += 1
288
289 def show(self):
290 """Display the progress."""
Simon Glass793dca32019-10-31 07:42:57 -0600291 print(' %d defconfigs out of %d\r' % (self.current, self.total), end=' ')
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900292 sys.stdout.flush()
293
Simon Glasscb008832017-06-15 21:39:33 -0600294
295class KconfigScanner:
296 """Kconfig scanner."""
297
298 def __init__(self):
299 """Scan all the Kconfig files and create a Config object."""
300 # Define environment variables referenced from Kconfig
301 os.environ['srctree'] = os.getcwd()
302 os.environ['UBOOTVERSION'] = 'dummy'
303 os.environ['KCONFIG_OBJDIR'] = ''
Simon Glass65e62032023-02-01 13:19:12 -0700304 os.environ['CC'] = 'gcc'
Tom Rini65e05dd2019-09-20 17:42:09 -0400305 self.conf = kconfiglib.Kconfig()
Simon Glasscb008832017-06-15 21:39:33 -0600306
307
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900308class KconfigParser:
309
310 """A parser of .config and include/autoconf.mk."""
311
312 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
313 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
314
Simon Glassb2e83c62021-12-18 14:54:31 -0700315 def __init__(self, configs, args, build_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900316 """Create a new parser.
317
Simon Glass91197aa2021-12-18 14:54:35 -0700318 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900319 configs: A list of CONFIGs to move.
Simon Glass91197aa2021-12-18 14:54:35 -0700320 args (Namespace): program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900321 build_dir: Build directory.
322 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900323 self.configs = configs
Simon Glassb2e83c62021-12-18 14:54:31 -0700324 self.args = args
Masahiro Yamada1f169922016-05-19 15:52:00 +0900325 self.dotconfig = os.path.join(build_dir, '.config')
326 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
Masahiro Yamada07913d12016-08-22 22:18:22 +0900327 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
328 'autoconf.mk')
Simon Glassf3b8e642017-06-01 19:39:01 -0600329 self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900330 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900331
Simon Glass6821a742017-07-10 14:47:47 -0600332 def get_arch(self):
333 """Parse .config file and return the architecture.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900334
335 Returns:
Simon Glass6821a742017-07-10 14:47:47 -0600336 Architecture name (e.g. 'arm').
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900337 """
338 arch = ''
339 cpu = ''
Simon Glass37f815c2021-12-18 14:54:34 -0700340 for line in read_file(self.dotconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900341 m = self.re_arch.match(line)
342 if m:
343 arch = m.group(1)
344 continue
345 m = self.re_cpu.match(line)
346 if m:
347 cpu = m.group(1)
348
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900349 if not arch:
350 return None
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900351
352 # fix-up for aarch64
353 if arch == 'arm' and cpu == 'armv8':
354 arch = 'aarch64'
355
Simon Glass6821a742017-07-10 14:47:47 -0600356 return arch
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900357
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900358 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900359 """Parse .config, defconfig, include/autoconf.mk for one config.
360
361 This function looks for the config options in the lines from
362 defconfig, .config, and include/autoconf.mk in order to decide
363 which action should be taken for this defconfig.
364
Simon Glass91197aa2021-12-18 14:54:35 -0700365 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900366 config: CONFIG name to parse.
Masahiro Yamadacc008292016-05-19 15:51:56 +0900367 dotconfig_lines: lines from the .config file.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900368 autoconf_lines: lines from the include/autoconf.mk file.
369
370 Returns:
371 A tupple of the action for this defconfig and the line
372 matched for the config.
373 """
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900374 not_set = '# %s is not set' % config
375
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900376 for line in autoconf_lines:
377 line = line.rstrip()
378 if line.startswith(config + '='):
Masahiro Yamadacc008292016-05-19 15:51:56 +0900379 new_val = line
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900380 break
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900381 else:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900382 new_val = not_set
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900383
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200384 new_val = try_expand(new_val)
385
Masahiro Yamada916224c2016-08-22 22:18:21 +0900386 for line in dotconfig_lines:
387 line = line.rstrip()
388 if line.startswith(config + '=') or line == not_set:
389 old_val = line
390 break
391 else:
392 if new_val == not_set:
393 return (ACTION_NO_ENTRY, config)
394 else:
395 return (ACTION_NO_ENTRY_WARN, config)
396
Masahiro Yamadacc008292016-05-19 15:51:56 +0900397 # If this CONFIG is neither bool nor trisate
398 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
399 # tools/scripts/define2mk.sed changes '1' to 'y'.
400 # This is a problem if the CONFIG is int type.
401 # Check the type in Kconfig and handle it correctly.
402 if new_val[-2:] == '=y':
403 new_val = new_val[:-1] + '1'
404
Masahiro Yamada50301592016-06-15 14:33:50 +0900405 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
406 new_val)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900407
Masahiro Yamada1d085562016-05-19 15:52:02 +0900408 def update_dotconfig(self):
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900409 """Parse files for the config options and update the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900410
Masahiro Yamadacc008292016-05-19 15:51:56 +0900411 This function parses the generated .config and include/autoconf.mk
412 searching the target options.
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900413 Move the config option(s) to the .config as needed.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900414
Simon Glass91197aa2021-12-18 14:54:35 -0700415 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900416 defconfig: defconfig name.
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900417
418 Returns:
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900419 Return a tuple of (updated flag, log string).
420 The "updated flag" is True if the .config was updated, False
421 otherwise. The "log string" shows what happend to the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900422 """
423
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900424 results = []
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900425 updated = False
Masahiro Yamada916224c2016-08-22 22:18:21 +0900426 suspicious = False
Masahiro Yamada07913d12016-08-22 22:18:22 +0900427 rm_files = [self.config_autoconf, self.autoconf]
428
Simon Glassb2e83c62021-12-18 14:54:31 -0700429 if self.args.spl:
Masahiro Yamada07913d12016-08-22 22:18:22 +0900430 if os.path.exists(self.spl_autoconf):
431 autoconf_path = self.spl_autoconf
432 rm_files.append(self.spl_autoconf)
433 else:
434 for f in rm_files:
435 os.remove(f)
436 return (updated, suspicious,
Simon Glassb2e83c62021-12-18 14:54:31 -0700437 color_text(self.args.color, COLOR_BROWN,
Masahiro Yamada07913d12016-08-22 22:18:22 +0900438 "SPL is not enabled. Skipped.") + '\n')
439 else:
440 autoconf_path = self.autoconf
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900441
Simon Glass37f815c2021-12-18 14:54:34 -0700442 dotconfig_lines = read_file(self.dotconfig)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900443
Simon Glass37f815c2021-12-18 14:54:34 -0700444 autoconf_lines = read_file(autoconf_path)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900445
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900446 for config in self.configs:
447 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger96464ba2015-05-19 13:21:17 -0500448 autoconf_lines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900449 results.append(result)
450
451 log = ''
452
453 for (action, value) in results:
454 if action == ACTION_MOVE:
455 actlog = "Move '%s'" % value
456 log_color = COLOR_LIGHT_GREEN
Masahiro Yamadacc008292016-05-19 15:51:56 +0900457 elif action == ACTION_NO_ENTRY:
Simon Glassdaa694d2021-12-18 14:54:30 -0700458 actlog = '%s is not defined in Kconfig. Do nothing.' % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900459 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada916224c2016-08-22 22:18:21 +0900460 elif action == ACTION_NO_ENTRY_WARN:
Simon Glassdaa694d2021-12-18 14:54:30 -0700461 actlog = '%s is not defined in Kconfig (suspicious). Do nothing.' % value
Masahiro Yamada916224c2016-08-22 22:18:21 +0900462 log_color = COLOR_YELLOW
463 suspicious = True
Masahiro Yamadacc008292016-05-19 15:51:56 +0900464 elif action == ACTION_NO_CHANGE:
465 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
466 % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900467 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900468 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700469 sys.exit('Internal Error. This should not happen.')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900470
Simon Glassb2e83c62021-12-18 14:54:31 -0700471 log += color_text(self.args.color, log_color, actlog) + '\n'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900472
Simon Glass91197aa2021-12-18 14:54:35 -0700473 with open(self.dotconfig, 'a', encoding='utf-8') as out:
Masahiro Yamadae423d172016-05-19 15:51:49 +0900474 for (action, value) in results:
475 if action == ACTION_MOVE:
Simon Glass91197aa2021-12-18 14:54:35 -0700476 out.write(value + '\n')
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900477 updated = True
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900478
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900479 self.results = results
Masahiro Yamada07913d12016-08-22 22:18:22 +0900480 for f in rm_files:
481 os.remove(f)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900482
Masahiro Yamada916224c2016-08-22 22:18:21 +0900483 return (updated, suspicious, log)
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900484
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900485 def check_defconfig(self):
486 """Check the defconfig after savedefconfig
487
488 Returns:
489 Return additional log if moved CONFIGs were removed again by
490 'make savedefconfig'.
491 """
492
493 log = ''
494
Simon Glass37f815c2021-12-18 14:54:34 -0700495 defconfig_lines = read_file(self.defconfig)
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900496
497 for (action, value) in self.results:
498 if action != ACTION_MOVE:
499 continue
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300500 if not value in defconfig_lines:
Simon Glassb2e83c62021-12-18 14:54:31 -0700501 log += color_text(self.args.color, COLOR_YELLOW,
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900502 "'%s' was removed by savedefconfig.\n" %
503 value)
504
505 return log
506
Simon Glassd73fcb12017-06-01 19:39:02 -0600507
508class DatabaseThread(threading.Thread):
509 """This thread processes results from Slot threads.
510
511 It collects the data in the master config directary. There is only one
512 result thread, and this helps to serialise the build output.
513 """
514 def __init__(self, config_db, db_queue):
515 """Set up a new result thread
516
517 Args:
518 builder: Builder which will be sent each result
519 """
520 threading.Thread.__init__(self)
521 self.config_db = config_db
522 self.db_queue= db_queue
523
524 def run(self):
525 """Called to start up the result thread.
526
527 We collect the next result job and pass it on to the build.
528 """
529 while True:
530 defconfig, configs = self.db_queue.get()
531 self.config_db[defconfig] = configs
532 self.db_queue.task_done()
533
534
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900535class Slot:
536
537 """A slot to store a subprocess.
538
539 Each instance of this class handles one subprocess.
540 This class is useful to control multiple threads
541 for faster processing.
542 """
543
Simon Glassb2e83c62021-12-18 14:54:31 -0700544 def __init__(self, toolchains, configs, args, progress, devnull,
Simon Glass6821a742017-07-10 14:47:47 -0600545 make_cmd, reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900546 """Create a new process slot.
547
Simon Glass91197aa2021-12-18 14:54:35 -0700548 Args:
Simon Glass6821a742017-07-10 14:47:47 -0600549 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900550 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -0700551 args: Program arguments
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900552 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900553 devnull: A file object of '/dev/null'.
554 make_cmd: command name of GNU Make.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500555 reference_src_dir: Determine the true starting config state from this
556 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -0600557 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900558 """
Simon Glass6821a742017-07-10 14:47:47 -0600559 self.toolchains = toolchains
Simon Glassb2e83c62021-12-18 14:54:31 -0700560 self.args = args
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900561 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900562 self.build_dir = tempfile.mkdtemp()
563 self.devnull = devnull
564 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500565 self.reference_src_dir = reference_src_dir
Simon Glassd73fcb12017-06-01 19:39:02 -0600566 self.db_queue = db_queue
Simon Glassb2e83c62021-12-18 14:54:31 -0700567 self.parser = KconfigParser(configs, args, self.build_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900568 self.state = STATE_IDLE
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900569 self.failed_boards = set()
570 self.suspicious_boards = set()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900571
572 def __del__(self):
573 """Delete the working directory
574
575 This function makes sure the temporary directory is cleaned away
576 even if Python suddenly dies due to error. It should be done in here
Joe Hershbergerf2dae752016-06-10 14:53:29 -0500577 because it is guaranteed the destructor is always invoked when the
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900578 instance of the class gets unreferenced.
579
580 If the subprocess is still running, wait until it finishes.
581 """
582 if self.state != STATE_IDLE:
583 while self.ps.poll() == None:
584 pass
585 shutil.rmtree(self.build_dir)
586
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900587 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900588 """Assign a new subprocess for defconfig and add it to the slot.
589
590 If the slot is vacant, create a new subprocess for processing the
591 given defconfig and add it to the slot. Just returns False if
592 the slot is occupied (i.e. the current subprocess is still running).
593
Simon Glass91197aa2021-12-18 14:54:35 -0700594 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900595 defconfig: defconfig name.
596
597 Returns:
598 Return True on success or False on failure
599 """
600 if self.state != STATE_IDLE:
601 return False
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900602
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900603 self.defconfig = defconfig
Masahiro Yamada1d085562016-05-19 15:52:02 +0900604 self.log = ''
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900605 self.current_src_dir = self.reference_src_dir
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900606 self.do_defconfig()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900607 return True
608
609 def poll(self):
610 """Check the status of the subprocess and handle it as needed.
611
612 Returns True if the slot is vacant (i.e. in idle state).
613 If the configuration is successfully finished, assign a new
614 subprocess to build include/autoconf.mk.
615 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900616 parse the .config and the include/autoconf.mk, moving
617 config options to the .config as needed.
618 If the .config was updated, run "make savedefconfig" to sync
619 it, update the original defconfig, and then set the slot back
620 to the idle state.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900621
622 Returns:
623 Return True if the subprocess is terminated, False otherwise
624 """
625 if self.state == STATE_IDLE:
626 return True
627
628 if self.ps.poll() == None:
629 return False
630
631 if self.ps.poll() != 0:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900632 self.handle_error()
633 elif self.state == STATE_DEFCONFIG:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900634 if self.reference_src_dir and not self.current_src_dir:
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500635 self.do_savedefconfig()
636 else:
637 self.do_autoconf()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900638 elif self.state == STATE_AUTOCONF:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900639 if self.current_src_dir:
640 self.current_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500641 self.do_defconfig()
Simon Glassb2e83c62021-12-18 14:54:31 -0700642 elif self.args.build_db:
Simon Glassd73fcb12017-06-01 19:39:02 -0600643 self.do_build_db()
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500644 else:
645 self.do_savedefconfig()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900646 elif self.state == STATE_SAVEDEFCONFIG:
647 self.update_defconfig()
648 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700649 sys.exit('Internal Error. This should not happen.')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900650
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900651 return True if self.state == STATE_IDLE else False
Joe Hershberger96464ba2015-05-19 13:21:17 -0500652
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900653 def handle_error(self):
654 """Handle error cases."""
Masahiro Yamada8513dc02016-05-19 15:52:08 +0900655
Simon Glassb2e83c62021-12-18 14:54:31 -0700656 self.log += color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glassdaa694d2021-12-18 14:54:30 -0700657 'Failed to process.\n')
Simon Glassb2e83c62021-12-18 14:54:31 -0700658 if self.args.verbose:
659 self.log += color_text(self.args.color, COLOR_LIGHT_CYAN,
Markus Klotzbuecher4f5c5e92020-02-12 20:46:45 +0100660 self.ps.stderr.read().decode())
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900661 self.finish(False)
Joe Hershberger96464ba2015-05-19 13:21:17 -0500662
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900663 def do_defconfig(self):
664 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900665
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900666 cmd = list(self.make_cmd)
667 cmd.append(self.defconfig)
668 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900669 stderr=subprocess.PIPE,
670 cwd=self.current_src_dir)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900671 self.state = STATE_DEFCONFIG
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900672
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900673 def do_autoconf(self):
Simon Glassf3b8e642017-06-01 19:39:01 -0600674 """Run 'make AUTO_CONF_PATH'."""
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900675
Simon Glass6821a742017-07-10 14:47:47 -0600676 arch = self.parser.get_arch()
677 try:
678 toolchain = self.toolchains.Select(arch)
679 except ValueError:
Simon Glassb2e83c62021-12-18 14:54:31 -0700680 self.log += color_text(self.args.color, COLOR_YELLOW,
Chris Packhamce3ba452017-08-27 20:00:51 +1200681 "Tool chain for '%s' is missing. Do nothing.\n" % arch)
Masahiro Yamada4efef992016-05-19 15:52:03 +0900682 self.finish(False)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900683 return
Simon Glass793dca32019-10-31 07:42:57 -0600684 env = toolchain.MakeEnvironment(False)
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900685
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900686 cmd = list(self.make_cmd)
Joe Hershberger7740f652015-05-19 13:21:18 -0500687 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Simon Glassf3b8e642017-06-01 19:39:01 -0600688 cmd.append(AUTO_CONF_PATH)
Simon Glass6821a742017-07-10 14:47:47 -0600689 self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900690 stderr=subprocess.PIPE,
691 cwd=self.current_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900692 self.state = STATE_AUTOCONF
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900693
Simon Glassd73fcb12017-06-01 19:39:02 -0600694 def do_build_db(self):
695 """Add the board to the database"""
696 configs = {}
Simon Glass37f815c2021-12-18 14:54:34 -0700697 for line in read_file(os.path.join(self.build_dir, AUTO_CONF_PATH)):
698 if line.startswith('CONFIG'):
699 config, value = line.split('=', 1)
700 configs[config] = value.rstrip()
Simon Glassd73fcb12017-06-01 19:39:02 -0600701 self.db_queue.put([self.defconfig, configs])
702 self.finish(True)
703
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900704 def do_savedefconfig(self):
705 """Update the .config and run 'make savedefconfig'."""
706
Masahiro Yamada916224c2016-08-22 22:18:21 +0900707 (updated, suspicious, log) = self.parser.update_dotconfig()
708 if suspicious:
709 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900710 self.log += log
711
Simon Glassb2e83c62021-12-18 14:54:31 -0700712 if not self.args.force_sync and not updated:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900713 self.finish(True)
714 return
715 if updated:
Simon Glassb2e83c62021-12-18 14:54:31 -0700716 self.log += color_text(self.args.color, COLOR_LIGHT_GREEN,
Simon Glassdaa694d2021-12-18 14:54:30 -0700717 'Syncing by savedefconfig...\n')
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900718 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700719 self.log += 'Syncing by savedefconfig (forced by option)...\n'
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900720
721 cmd = list(self.make_cmd)
722 cmd.append('savedefconfig')
723 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
724 stderr=subprocess.PIPE)
725 self.state = STATE_SAVEDEFCONFIG
726
727 def update_defconfig(self):
728 """Update the input defconfig and go back to the idle state."""
729
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900730 log = self.parser.check_defconfig()
731 if log:
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900732 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900733 self.log += log
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900734 orig_defconfig = os.path.join('configs', self.defconfig)
735 new_defconfig = os.path.join(self.build_dir, 'defconfig')
736 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
737
738 if updated:
Simon Glassb2e83c62021-12-18 14:54:31 -0700739 self.log += color_text(self.args.color, COLOR_LIGHT_BLUE,
Simon Glassdaa694d2021-12-18 14:54:30 -0700740 'defconfig was updated.\n')
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900741
Simon Glassb2e83c62021-12-18 14:54:31 -0700742 if not self.args.dry_run and updated:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900743 shutil.move(new_defconfig, orig_defconfig)
744 self.finish(True)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900745
Masahiro Yamada4efef992016-05-19 15:52:03 +0900746 def finish(self, success):
747 """Display log along with progress and go to the idle state.
Masahiro Yamada1d085562016-05-19 15:52:02 +0900748
Simon Glass91197aa2021-12-18 14:54:35 -0700749 Args:
Masahiro Yamada4efef992016-05-19 15:52:03 +0900750 success: Should be True when the defconfig was processed
751 successfully, or False when it fails.
Masahiro Yamada1d085562016-05-19 15:52:02 +0900752 """
753 # output at least 30 characters to hide the "* defconfigs out of *".
754 log = self.defconfig.ljust(30) + '\n'
755
756 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
757 # Some threads are running in parallel.
758 # Print log atomically to not mix up logs from different threads.
Simon Glass793dca32019-10-31 07:42:57 -0600759 print(log, file=(sys.stdout if success else sys.stderr))
Masahiro Yamada4efef992016-05-19 15:52:03 +0900760
761 if not success:
Simon Glassb2e83c62021-12-18 14:54:31 -0700762 if self.args.exit_on_error:
Simon Glassdaa694d2021-12-18 14:54:30 -0700763 sys.exit('Exit on error.')
Masahiro Yamada4efef992016-05-19 15:52:03 +0900764 # If --exit-on-error flag is not set, skip this board and continue.
765 # Record the failed board.
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900766 self.failed_boards.add(self.defconfig)
Masahiro Yamada4efef992016-05-19 15:52:03 +0900767
Masahiro Yamada1d085562016-05-19 15:52:02 +0900768 self.progress.inc()
769 self.progress.show()
Masahiro Yamada4efef992016-05-19 15:52:03 +0900770 self.state = STATE_IDLE
Masahiro Yamada1d085562016-05-19 15:52:02 +0900771
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900772 def get_failed_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900773 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900774 """
775 return self.failed_boards
776
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900777 def get_suspicious_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900778 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900779 """
Masahiro Yamada916224c2016-08-22 22:18:21 +0900780 return self.suspicious_boards - self.failed_boards
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900781
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900782class Slots:
783
784 """Controller of the array of subprocess slots."""
785
Simon Glassb2e83c62021-12-18 14:54:31 -0700786 def __init__(self, toolchains, configs, args, progress,
Simon Glass6821a742017-07-10 14:47:47 -0600787 reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900788 """Create a new slots controller.
789
Simon Glass91197aa2021-12-18 14:54:35 -0700790 Args:
Simon Glass6821a742017-07-10 14:47:47 -0600791 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900792 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -0700793 args: Program arguments
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900794 progress: A progress indicator.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500795 reference_src_dir: Determine the true starting config state from this
796 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -0600797 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900798 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700799 self.args = args
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900800 self.slots = []
Simon Glass478920d2021-12-18 14:54:32 -0700801 devnull = subprocess.DEVNULL
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900802 make_cmd = get_make_cmd()
Simon Glassb2e83c62021-12-18 14:54:31 -0700803 for i in range(args.jobs):
804 self.slots.append(Slot(toolchains, configs, args, progress,
Simon Glass6821a742017-07-10 14:47:47 -0600805 devnull, make_cmd, reference_src_dir,
806 db_queue))
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900807
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900808 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900809 """Add a new subprocess if a vacant slot is found.
810
Simon Glass91197aa2021-12-18 14:54:35 -0700811 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900812 defconfig: defconfig name to be put into.
813
814 Returns:
815 Return True on success or False on failure
816 """
817 for slot in self.slots:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900818 if slot.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900819 return True
820 return False
821
822 def available(self):
823 """Check if there is a vacant slot.
824
825 Returns:
826 Return True if at lease one vacant slot is found, False otherwise.
827 """
828 for slot in self.slots:
829 if slot.poll():
830 return True
831 return False
832
833 def empty(self):
834 """Check if all slots are vacant.
835
836 Returns:
837 Return True if all the slots are vacant, False otherwise.
838 """
839 ret = True
840 for slot in self.slots:
841 if not slot.poll():
842 ret = False
843 return ret
844
845 def show_failed_boards(self):
846 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900847 boards = set()
Masahiro Yamada96dccd92016-06-15 14:33:53 +0900848 output_file = 'moveconfig.failed'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900849
850 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900851 boards |= slot.get_failed_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900852
Masahiro Yamada96dccd92016-06-15 14:33:53 +0900853 if boards:
854 boards = '\n'.join(boards) + '\n'
Simon Glassdaa694d2021-12-18 14:54:30 -0700855 msg = 'The following boards were not processed due to error:\n'
Masahiro Yamada96dccd92016-06-15 14:33:53 +0900856 msg += boards
Simon Glassdaa694d2021-12-18 14:54:30 -0700857 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassb2e83c62021-12-18 14:54:31 -0700858 print(color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glass793dca32019-10-31 07:42:57 -0600859 msg), file=sys.stderr)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900860
Simon Glass2fd85bd2021-12-18 14:54:33 -0700861 write_file(output_file, boards)
Joe Hershberger2559cd82015-05-19 13:21:22 -0500862
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900863 def show_suspicious_boards(self):
864 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900865 boards = set()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900866 output_file = 'moveconfig.suspicious'
867
868 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900869 boards |= slot.get_suspicious_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900870
871 if boards:
872 boards = '\n'.join(boards) + '\n'
Simon Glassdaa694d2021-12-18 14:54:30 -0700873 msg = 'The following boards might have been converted incorrectly.\n'
874 msg += 'It is highly recommended to check them manually:\n'
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900875 msg += boards
Simon Glassdaa694d2021-12-18 14:54:30 -0700876 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassb2e83c62021-12-18 14:54:31 -0700877 print(color_text(self.args.color, COLOR_YELLOW,
Simon Glass793dca32019-10-31 07:42:57 -0600878 msg), file=sys.stderr)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900879
Simon Glass2fd85bd2021-12-18 14:54:33 -0700880 write_file(output_file, boards)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900881
Masahiro Yamada5cc42a52016-06-15 14:33:51 +0900882class ReferenceSource:
883
884 """Reference source against which original configs should be parsed."""
885
886 def __init__(self, commit):
887 """Create a reference source directory based on a specified commit.
888
Simon Glass91197aa2021-12-18 14:54:35 -0700889 Args:
Masahiro Yamada5cc42a52016-06-15 14:33:51 +0900890 commit: commit to git-clone
891 """
892 self.src_dir = tempfile.mkdtemp()
Simon Glassdaa694d2021-12-18 14:54:30 -0700893 print('Cloning git repo to a separate work directory...')
Masahiro Yamada5cc42a52016-06-15 14:33:51 +0900894 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
895 cwd=self.src_dir)
Simon Glass793dca32019-10-31 07:42:57 -0600896 print("Checkout '%s' to build the original autoconf.mk." % \
897 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip())
Masahiro Yamada5cc42a52016-06-15 14:33:51 +0900898 subprocess.check_output(['git', 'checkout', commit],
899 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500900
901 def __del__(self):
Masahiro Yamada5cc42a52016-06-15 14:33:51 +0900902 """Delete the reference source directory
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500903
904 This function makes sure the temporary directory is cleaned away
905 even if Python suddenly dies due to error. It should be done in here
906 because it is guaranteed the destructor is always invoked when the
907 instance of the class gets unreferenced.
908 """
Masahiro Yamada5cc42a52016-06-15 14:33:51 +0900909 shutil.rmtree(self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500910
Masahiro Yamada5cc42a52016-06-15 14:33:51 +0900911 def get_dir(self):
912 """Return the absolute path to the reference source directory."""
913
914 return self.src_dir
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500915
Simon Glassb2e83c62021-12-18 14:54:31 -0700916def move_config(toolchains, configs, args, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900917 """Move config options to defconfig files.
918
Simon Glass91197aa2021-12-18 14:54:35 -0700919 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900920 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -0700921 args: Program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900922 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900923 if len(configs) == 0:
Simon Glassb2e83c62021-12-18 14:54:31 -0700924 if args.force_sync:
Simon Glass793dca32019-10-31 07:42:57 -0600925 print('No CONFIG is specified. You are probably syncing defconfigs.', end=' ')
Simon Glassb2e83c62021-12-18 14:54:31 -0700926 elif args.build_db:
Simon Glass793dca32019-10-31 07:42:57 -0600927 print('Building %s database' % CONFIG_DATABASE)
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +0900928 else:
Simon Glass793dca32019-10-31 07:42:57 -0600929 print('Neither CONFIG nor --force-sync is specified. Nothing will happen.', end=' ')
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +0900930 else:
Simon Glass793dca32019-10-31 07:42:57 -0600931 print('Move ' + ', '.join(configs), end=' ')
Simon Glassb2e83c62021-12-18 14:54:31 -0700932 print('(jobs: %d)\n' % args.jobs)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900933
Simon Glassb2e83c62021-12-18 14:54:31 -0700934 if args.git_ref:
935 reference_src = ReferenceSource(args.git_ref)
Masahiro Yamada5cc42a52016-06-15 14:33:51 +0900936 reference_src_dir = reference_src.get_dir()
937 else:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900938 reference_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500939
Simon Glassb2e83c62021-12-18 14:54:31 -0700940 if args.defconfigs:
941 defconfigs = get_matched_defconfigs(args.defconfigs)
Joe Hershberger91040e82015-05-19 13:21:19 -0500942 else:
Masahiro Yamada684c3062016-07-25 19:15:28 +0900943 defconfigs = get_all_defconfigs()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900944
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900945 progress = Progress(len(defconfigs))
Simon Glassb2e83c62021-12-18 14:54:31 -0700946 slots = Slots(toolchains, configs, args, progress, reference_src_dir,
Simon Glass91197aa2021-12-18 14:54:35 -0700947 db_queue)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900948
949 # Main loop to process defconfig files:
950 # Add a new subprocess into a vacant slot.
951 # Sleep if there is no available slot.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900952 for defconfig in defconfigs:
953 while not slots.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900954 while not slots.available():
955 # No available slot: sleep for a while
956 time.sleep(SLEEP_TIME)
957
958 # wait until all the subprocesses finish
959 while not slots.empty():
960 time.sleep(SLEEP_TIME)
961
Simon Glass793dca32019-10-31 07:42:57 -0600962 print('')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900963 slots.show_failed_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900964 slots.show_suspicious_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900965
Simon Glasscb008832017-06-15 21:39:33 -0600966def find_kconfig_rules(kconf, config, imply_config):
967 """Check whether a config has a 'select' or 'imply' keyword
968
969 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -0400970 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -0600971 config: Name of config to check (without CONFIG_ prefix)
972 imply_config: Implying config (without CONFIG_ prefix) which may or
973 may not have an 'imply' for 'config')
974
975 Returns:
976 Symbol object for 'config' if found, else None
977 """
Tom Rini65e05dd2019-09-20 17:42:09 -0400978 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -0600979 if sym:
Simon Glassea40b202021-07-21 21:35:53 -0600980 for sel, cond in (sym.selects + sym.implies):
Simon Glassa3627082021-12-18 08:09:42 -0700981 if sel.name == config:
Simon Glasscb008832017-06-15 21:39:33 -0600982 return sym
983 return None
984
985def check_imply_rule(kconf, config, imply_config):
986 """Check if we can add an 'imply' option
987
988 This finds imply_config in the Kconfig and looks to see if it is possible
989 to add an 'imply' for 'config' to that part of the Kconfig.
990
991 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -0400992 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -0600993 config: Name of config to check (without CONFIG_ prefix)
994 imply_config: Implying config (without CONFIG_ prefix) which may or
995 may not have an 'imply' for 'config')
996
997 Returns:
998 tuple:
999 filename of Kconfig file containing imply_config, or None if none
1000 line number within the Kconfig file, or 0 if none
1001 message indicating the result
1002 """
Tom Rini65e05dd2019-09-20 17:42:09 -04001003 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -06001004 if not sym:
1005 return 'cannot find sym'
Simon Glassea40b202021-07-21 21:35:53 -06001006 nodes = sym.nodes
1007 if len(nodes) != 1:
1008 return '%d locations' % len(nodes)
Simon Glassa3627082021-12-18 08:09:42 -07001009 node = nodes[0]
1010 fname, linenum = node.filename, node.linenr
Simon Glasscb008832017-06-15 21:39:33 -06001011 cwd = os.getcwd()
1012 if cwd and fname.startswith(cwd):
1013 fname = fname[len(cwd) + 1:]
1014 file_line = ' at %s:%d' % (fname, linenum)
Simon Glass37f815c2021-12-18 14:54:34 -07001015 data = read_file(fname)
Simon Glasscb008832017-06-15 21:39:33 -06001016 if data[linenum - 1] != 'config %s' % imply_config:
1017 return None, 0, 'bad sym format %s%s' % (data[linenum], file_line)
1018 return fname, linenum, 'adding%s' % file_line
1019
1020def add_imply_rule(config, fname, linenum):
1021 """Add a new 'imply' option to a Kconfig
1022
1023 Args:
1024 config: config option to add an imply for (without CONFIG_ prefix)
1025 fname: Kconfig filename to update
1026 linenum: Line number to place the 'imply' before
1027
1028 Returns:
1029 Message indicating the result
1030 """
1031 file_line = ' at %s:%d' % (fname, linenum)
Simon Glass37f815c2021-12-18 14:54:34 -07001032 data = read_file(fname)
Simon Glasscb008832017-06-15 21:39:33 -06001033 linenum -= 1
1034
1035 for offset, line in enumerate(data[linenum:]):
1036 if line.strip().startswith('help') or not line:
1037 data.insert(linenum + offset, '\timply %s' % config)
Simon Glass2fd85bd2021-12-18 14:54:33 -07001038 write_file(fname, data)
Simon Glasscb008832017-06-15 21:39:33 -06001039 return 'added%s' % file_line
1040
1041 return 'could not insert%s'
1042
1043(IMPLY_MIN_2, IMPLY_TARGET, IMPLY_CMD, IMPLY_NON_ARCH_BOARD) = (
1044 1, 2, 4, 8)
Simon Glass9b2a2e82017-06-15 21:39:32 -06001045
1046IMPLY_FLAGS = {
1047 'min2': [IMPLY_MIN_2, 'Show options which imply >2 boards (normally >5)'],
1048 'target': [IMPLY_TARGET, 'Allow CONFIG_TARGET_... options to imply'],
1049 'cmd': [IMPLY_CMD, 'Allow CONFIG_CMD_... to imply'],
Simon Glasscb008832017-06-15 21:39:33 -06001050 'non-arch-board': [
1051 IMPLY_NON_ARCH_BOARD,
1052 'Allow Kconfig options outside arch/ and /board/ to imply'],
Simon Glass91197aa2021-12-18 14:54:35 -07001053}
Simon Glass9b2a2e82017-06-15 21:39:32 -06001054
Simon Glass9d603392021-12-18 08:09:43 -07001055
1056def read_database():
1057 """Read in the config database
1058
1059 Returns:
1060 tuple:
1061 set of all config options seen (each a str)
1062 set of all defconfigs seen (each a str)
1063 dict of configs for each defconfig:
1064 key: defconfig name, e.g. "MPC8548CDS_legacy_defconfig"
1065 value: dict:
1066 key: CONFIG option
1067 value: Value of option
1068 dict of defconfigs for each config:
1069 key: CONFIG option
1070 value: set of boards using that option
1071
1072 """
1073 configs = {}
1074
1075 # key is defconfig name, value is dict of (CONFIG_xxx, value)
1076 config_db = {}
1077
1078 # Set of all config options we have seen
1079 all_configs = set()
1080
1081 # Set of all defconfigs we have seen
1082 all_defconfigs = set()
1083
1084 defconfig_db = collections.defaultdict(set)
Simon Glass37f815c2021-12-18 14:54:34 -07001085 for line in read_file(CONFIG_DATABASE):
1086 line = line.rstrip()
1087 if not line: # Separator between defconfigs
1088 config_db[defconfig] = configs
1089 all_defconfigs.add(defconfig)
1090 configs = {}
1091 elif line[0] == ' ': # CONFIG line
1092 config, value = line.strip().split('=', 1)
1093 configs[config] = value
1094 defconfig_db[config].add(defconfig)
1095 all_configs.add(config)
1096 else: # New defconfig
1097 defconfig = line
Simon Glass9d603392021-12-18 08:09:43 -07001098
1099 return all_configs, all_defconfigs, config_db, defconfig_db
1100
1101
Simon Glasscb008832017-06-15 21:39:33 -06001102def do_imply_config(config_list, add_imply, imply_flags, skip_added,
1103 check_kconfig=True, find_superset=False):
Simon Glass99b66602017-06-01 19:39:03 -06001104 """Find CONFIG options which imply those in the list
1105
1106 Some CONFIG options can be implied by others and this can help to reduce
1107 the size of the defconfig files. For example, CONFIG_X86 implies
1108 CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
1109 all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
1110 each of the x86 defconfig files.
1111
1112 This function uses the moveconfig database to find such options. It
1113 displays a list of things that could possibly imply those in the list.
1114 The algorithm ignores any that start with CONFIG_TARGET since these
1115 typically refer to only a few defconfigs (often one). It also does not
1116 display a config with less than 5 defconfigs.
1117
1118 The algorithm works using sets. For each target config in config_list:
1119 - Get the set 'defconfigs' which use that target config
1120 - For each config (from a list of all configs):
1121 - Get the set 'imply_defconfig' of defconfigs which use that config
1122 -
1123 - If imply_defconfigs contains anything not in defconfigs then
1124 this config does not imply the target config
1125
1126 Params:
1127 config_list: List of CONFIG options to check (each a string)
Simon Glasscb008832017-06-15 21:39:33 -06001128 add_imply: Automatically add an 'imply' for each config.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001129 imply_flags: Flags which control which implying configs are allowed
1130 (IMPLY_...)
Simon Glasscb008832017-06-15 21:39:33 -06001131 skip_added: Don't show options which already have an imply added.
1132 check_kconfig: Check if implied symbols already have an 'imply' or
1133 'select' for the target config, and show this information if so.
Simon Glass99b66602017-06-01 19:39:03 -06001134 find_superset: True to look for configs which are a superset of those
1135 already found. So for example if CONFIG_EXYNOS5 implies an option,
1136 but CONFIG_EXYNOS covers a larger set of defconfigs and also
1137 implies that option, this will drop the former in favour of the
1138 latter. In practice this option has not proved very used.
1139
1140 Note the terminoloy:
1141 config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
1142 defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
1143 """
Simon Glasscb008832017-06-15 21:39:33 -06001144 kconf = KconfigScanner().conf if check_kconfig else None
1145 if add_imply and add_imply != 'all':
Simon Glassa3627082021-12-18 08:09:42 -07001146 add_imply = add_imply.split(',')
Simon Glasscb008832017-06-15 21:39:33 -06001147
Simon Glass9d603392021-12-18 08:09:43 -07001148 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
Simon Glass99b66602017-06-01 19:39:03 -06001149
Simon Glassa3627082021-12-18 08:09:42 -07001150 # Work through each target config option in turn, independently
Simon Glass99b66602017-06-01 19:39:03 -06001151 for config in config_list:
1152 defconfigs = defconfig_db.get(config)
1153 if not defconfigs:
Simon Glass793dca32019-10-31 07:42:57 -06001154 print('%s not found in any defconfig' % config)
Simon Glass99b66602017-06-01 19:39:03 -06001155 continue
1156
1157 # Get the set of defconfigs without this one (since a config cannot
1158 # imply itself)
1159 non_defconfigs = all_defconfigs - defconfigs
1160 num_defconfigs = len(defconfigs)
Simon Glass793dca32019-10-31 07:42:57 -06001161 print('%s found in %d/%d defconfigs' % (config, num_defconfigs,
1162 len(all_configs)))
Simon Glass99b66602017-06-01 19:39:03 -06001163
1164 # This will hold the results: key=config, value=defconfigs containing it
1165 imply_configs = {}
1166 rest_configs = all_configs - set([config])
1167
1168 # Look at every possible config, except the target one
1169 for imply_config in rest_configs:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001170 if 'ERRATUM' in imply_config:
Simon Glass99b66602017-06-01 19:39:03 -06001171 continue
Simon Glass91197aa2021-12-18 14:54:35 -07001172 if not imply_flags & IMPLY_CMD:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001173 if 'CONFIG_CMD' in imply_config:
1174 continue
Simon Glass91197aa2021-12-18 14:54:35 -07001175 if not imply_flags & IMPLY_TARGET:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001176 if 'CONFIG_TARGET' in imply_config:
1177 continue
Simon Glass99b66602017-06-01 19:39:03 -06001178
1179 # Find set of defconfigs that have this config
1180 imply_defconfig = defconfig_db[imply_config]
1181
1182 # Get the intersection of this with defconfigs containing the
1183 # target config
1184 common_defconfigs = imply_defconfig & defconfigs
1185
1186 # Get the set of defconfigs containing this config which DO NOT
1187 # also contain the taret config. If this set is non-empty it means
1188 # that this config affects other defconfigs as well as (possibly)
1189 # the ones affected by the target config. This means it implies
1190 # things we don't want to imply.
1191 not_common_defconfigs = imply_defconfig & non_defconfigs
1192 if not_common_defconfigs:
1193 continue
1194
1195 # If there are common defconfigs, imply_config may be useful
1196 if common_defconfigs:
1197 skip = False
1198 if find_superset:
Simon Glass793dca32019-10-31 07:42:57 -06001199 for prev in list(imply_configs.keys()):
Simon Glass99b66602017-06-01 19:39:03 -06001200 prev_count = len(imply_configs[prev])
1201 count = len(common_defconfigs)
1202 if (prev_count > count and
1203 (imply_configs[prev] & common_defconfigs ==
1204 common_defconfigs)):
1205 # skip imply_config because prev is a superset
1206 skip = True
1207 break
1208 elif count > prev_count:
1209 # delete prev because imply_config is a superset
1210 del imply_configs[prev]
1211 if not skip:
1212 imply_configs[imply_config] = common_defconfigs
1213
1214 # Now we have a dict imply_configs of configs which imply each config
1215 # The value of each dict item is the set of defconfigs containing that
1216 # config. Rank them so that we print the configs that imply the largest
1217 # number of defconfigs first.
Simon Glasscb008832017-06-15 21:39:33 -06001218 ranked_iconfigs = sorted(imply_configs,
Simon Glass99b66602017-06-01 19:39:03 -06001219 key=lambda k: len(imply_configs[k]), reverse=True)
Simon Glasscb008832017-06-15 21:39:33 -06001220 kconfig_info = ''
1221 cwd = os.getcwd()
1222 add_list = collections.defaultdict(list)
1223 for iconfig in ranked_iconfigs:
1224 num_common = len(imply_configs[iconfig])
Simon Glass99b66602017-06-01 19:39:03 -06001225
1226 # Don't bother if there are less than 5 defconfigs affected.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001227 if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
Simon Glass99b66602017-06-01 19:39:03 -06001228 continue
Simon Glasscb008832017-06-15 21:39:33 -06001229 missing = defconfigs - imply_configs[iconfig]
Simon Glass99b66602017-06-01 19:39:03 -06001230 missing_str = ', '.join(missing) if missing else 'all'
1231 missing_str = ''
Simon Glasscb008832017-06-15 21:39:33 -06001232 show = True
1233 if kconf:
1234 sym = find_kconfig_rules(kconf, config[CONFIG_LEN:],
1235 iconfig[CONFIG_LEN:])
1236 kconfig_info = ''
1237 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001238 nodes = sym.nodes
1239 if len(nodes) == 1:
1240 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001241 if cwd and fname.startswith(cwd):
1242 fname = fname[len(cwd) + 1:]
1243 kconfig_info = '%s:%d' % (fname, linenum)
1244 if skip_added:
1245 show = False
1246 else:
Tom Rini65e05dd2019-09-20 17:42:09 -04001247 sym = kconf.syms.get(iconfig[CONFIG_LEN:])
Simon Glasscb008832017-06-15 21:39:33 -06001248 fname = ''
1249 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001250 nodes = sym.nodes
1251 if len(nodes) == 1:
1252 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001253 if cwd and fname.startswith(cwd):
1254 fname = fname[len(cwd) + 1:]
1255 in_arch_board = not sym or (fname.startswith('arch') or
1256 fname.startswith('board'))
1257 if (not in_arch_board and
Simon Glass91197aa2021-12-18 14:54:35 -07001258 not imply_flags & IMPLY_NON_ARCH_BOARD):
Simon Glasscb008832017-06-15 21:39:33 -06001259 continue
1260
1261 if add_imply and (add_imply == 'all' or
1262 iconfig in add_imply):
1263 fname, linenum, kconfig_info = (check_imply_rule(kconf,
1264 config[CONFIG_LEN:], iconfig[CONFIG_LEN:]))
1265 if fname:
1266 add_list[fname].append(linenum)
1267
1268 if show and kconfig_info != 'skip':
Simon Glass793dca32019-10-31 07:42:57 -06001269 print('%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
1270 kconfig_info, missing_str))
Simon Glasscb008832017-06-15 21:39:33 -06001271
1272 # Having collected a list of things to add, now we add them. We process
1273 # each file from the largest line number to the smallest so that
1274 # earlier additions do not affect our line numbers. E.g. if we added an
1275 # imply at line 20 it would change the position of each line after
1276 # that.
Simon Glass793dca32019-10-31 07:42:57 -06001277 for fname, linenums in add_list.items():
Simon Glasscb008832017-06-15 21:39:33 -06001278 for linenum in sorted(linenums, reverse=True):
1279 add_imply_rule(config[CONFIG_LEN:], fname, linenum)
Simon Glass99b66602017-06-01 19:39:03 -06001280
Simon Glass941671a2022-02-08 11:49:46 -07001281def defconfig_matches(configs, re_match):
1282 """Check if any CONFIG option matches a regex
1283
1284 The match must be complete, i.e. from the start to end of the CONFIG option.
1285
1286 Args:
1287 configs (dict): Dict of CONFIG options:
1288 key: CONFIG option
1289 value: Value of option
1290 re_match (re.Pattern): Match to check
1291
1292 Returns:
1293 bool: True if any CONFIG matches the regex
1294 """
1295 for cfg in configs:
Simon Glassd9c958f2022-03-05 20:18:54 -07001296 if re_match.fullmatch(cfg):
Simon Glass941671a2022-02-08 11:49:46 -07001297 return True
1298 return False
Simon Glass99b66602017-06-01 19:39:03 -06001299
Simon Glass65d7fce2021-12-18 08:09:46 -07001300def do_find_config(config_list):
1301 """Find boards with a given combination of CONFIGs
1302
1303 Params:
Simon Glass941671a2022-02-08 11:49:46 -07001304 config_list: List of CONFIG options to check (each a regex consisting
Simon Glass65d7fce2021-12-18 08:09:46 -07001305 of a config option, with or without a CONFIG_ prefix. If an option
1306 is preceded by a tilde (~) then it must be false, otherwise it must
1307 be true)
1308 """
1309 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
1310
Simon Glass65d7fce2021-12-18 08:09:46 -07001311 # Start with all defconfigs
1312 out = all_defconfigs
1313
1314 # Work through each config in turn
Simon Glass65d7fce2021-12-18 08:09:46 -07001315 for item in config_list:
1316 # Get the real config name and whether we want this config or not
1317 cfg = item
1318 want = True
1319 if cfg[0] == '~':
1320 want = False
1321 cfg = cfg[1:]
1322
Simon Glass65d7fce2021-12-18 08:09:46 -07001323 # Search everything that is still in the running. If it has a config
1324 # that we want, or doesn't have one that we don't, add it into the
1325 # running for the next stage
1326 in_list = out
1327 out = set()
Simon Glass941671a2022-02-08 11:49:46 -07001328 re_match = re.compile(cfg)
Simon Glass65d7fce2021-12-18 08:09:46 -07001329 for defc in in_list:
Simon Glass941671a2022-02-08 11:49:46 -07001330 has_cfg = defconfig_matches(config_db[defc], re_match)
Simon Glass65d7fce2021-12-18 08:09:46 -07001331 if has_cfg == want:
1332 out.add(defc)
Tom Rini9ef3ba82022-12-04 10:14:16 -05001333 print(f'{len(out)} matches')
1334 print(' '.join(item.split('_defconfig')[0] for item in out))
Simon Glass65d7fce2021-12-18 08:09:46 -07001335
1336
1337def prefix_config(cfg):
1338 """Prefix a config with CONFIG_ if needed
1339
1340 This handles ~ operator, which indicates that the CONFIG should be disabled
1341
1342 >>> prefix_config('FRED')
1343 'CONFIG_FRED'
1344 >>> prefix_config('CONFIG_FRED')
1345 'CONFIG_FRED'
1346 >>> prefix_config('~FRED')
1347 '~CONFIG_FRED'
1348 >>> prefix_config('~CONFIG_FRED')
1349 '~CONFIG_FRED'
1350 >>> prefix_config('A123')
1351 'CONFIG_A123'
1352 """
1353 op = ''
1354 if cfg[0] == '~':
1355 op = cfg[0]
1356 cfg = cfg[1:]
1357 if not cfg.startswith('CONFIG_'):
1358 cfg = 'CONFIG_' + cfg
1359 return op + cfg
1360
1361
Simon Glass65e62032023-02-01 13:19:12 -07001362RE_MK_CONFIGS = re.compile('CONFIG_(\$\(SPL_(?:TPL_)?\))?([A-Za-z0-9_]*)')
1363RE_IFDEF = re.compile('(ifdef|ifndef)')
1364RE_C_CONFIGS = re.compile('CONFIG_([A-Za-z0-9_]*)')
1365RE_CONFIG_IS = re.compile('CONFIG_IS_ENABLED\(([A-Za-z0-9_]*)\)')
1366
1367class ConfigUse:
1368 def __init__(self, cfg, is_spl, fname, rest):
1369 self.cfg = cfg
1370 self.is_spl = is_spl
1371 self.fname = fname
1372 self.rest = rest
1373
1374 def __hash__(self):
1375 return hash((self.cfg, self.is_spl))
1376
1377def scan_makefiles(fnames):
1378 """Scan Makefiles looking for Kconfig options
1379
1380 Looks for uses of CONFIG options in Makefiles
1381
1382 Args:
1383 fnames (list of tuple):
1384 str: Makefile filename where the option was found
1385 str: Line of the Makefile
1386
1387 Returns:
1388 tuple:
1389 dict: all_uses
1390 key (ConfigUse): object
1391 value (list of str): matching lines
1392 dict: Uses by filename
1393 key (str): filename
1394 value (set of ConfigUse): uses in that filename
1395
1396 >>> RE_MK_CONFIGS.search('CONFIG_FRED').groups()
1397 (None, 'FRED')
1398 >>> RE_MK_CONFIGS.search('CONFIG_$(SPL_)MARY').groups()
1399 ('$(SPL_)', 'MARY')
1400 >>> RE_MK_CONFIGS.search('CONFIG_$(SPL_TPL_)MARY').groups()
1401 ('$(SPL_TPL_)', 'MARY')
1402 """
1403 all_uses = collections.defaultdict(list)
1404 fname_uses = {}
1405 for fname, rest in fnames:
1406 m_iter = RE_MK_CONFIGS.finditer(rest)
1407 found = False
1408 for m in m_iter:
1409 found = True
1410 real_opt = m.group(2)
1411 if real_opt == '':
1412 continue
1413 is_spl = False
1414 if m.group(1):
1415 is_spl = True
1416 use = ConfigUse(real_opt, is_spl, fname, rest)
1417 if fname not in fname_uses:
1418 fname_uses[fname] = set()
1419 fname_uses[fname].add(use)
1420 all_uses[use].append(rest)
1421 return all_uses, fname_uses
1422
1423
1424def scan_src_files(fnames):
1425 """Scan source files (other than Makefiles) looking for Kconfig options
1426
1427 Looks for uses of CONFIG options
1428
1429 Args:
1430 fnames (list of tuple):
1431 str: Makefile filename where the option was found
1432 str: Line of the Makefile
1433
1434 Returns:
1435 tuple:
1436 dict: all_uses
1437 key (ConfigUse): object
1438 value (list of str): matching lines
1439 dict: Uses by filename
1440 key (str): filename
1441 value (set of ConfigUse): uses in that filename
1442
1443 >>> RE_C_CONFIGS.search('CONFIG_FRED').groups()
1444 ('FRED',)
1445 >>> RE_CONFIG_IS.search('CONFIG_IS_ENABLED(MARY)').groups()
1446 ('MARY',)
1447 >>> RE_CONFIG_IS.search('#if CONFIG_IS_ENABLED(OF_PLATDATA)').groups()
1448 ('OF_PLATDATA',)
1449 """
1450 def add_uses(m_iter, is_spl):
1451 for m in m_iter:
1452 found = True
1453 real_opt = m.group(1)
1454 if real_opt == '':
1455 continue
1456 use = ConfigUse(real_opt, is_spl, fname, rest)
1457 if fname not in fname_uses:
1458 fname_uses[fname] = set()
1459 fname_uses[fname].add(use)
1460 all_uses[use].append(rest)
1461
1462 all_uses = collections.defaultdict(list)
1463 fname_uses = {}
1464 for fname, rest in fnames:
1465 m_iter = RE_C_CONFIGS.finditer(rest)
1466 add_uses(m_iter, False)
1467
1468 m_iter2 = RE_CONFIG_IS.finditer(rest)
1469 add_uses(m_iter2, True)
1470
1471 return all_uses, fname_uses
1472
1473
1474MODE_NORMAL, MODE_SPL, MODE_PROPER = range(3)
1475
1476def do_scan_source(path, do_update):
1477 """Scan the source tree for Kconfig inconsistencies
1478
1479 Args:
1480 path (str): Path to source tree
1481 do_update (bool) : True to write to scripts/kconf_... files
1482 """
1483 def is_not_proper(name):
1484 for prefix in SPL_PREFIXES:
1485 if name.startswith(prefix):
1486 return name[len(prefix):]
1487 return False
1488
1489 def check_not_found(all_uses, spl_mode):
1490 """Check for Kconfig options mentioned in the source but not in Kconfig
1491
1492 Args:
1493 all_uses (dict):
1494 key (ConfigUse): object
1495 value (list of str): matching lines
1496 spl_mode (int): If MODE_SPL, look at source code which implies
1497 an SPL_ option, but for which there is none;
1498 for MOD_PROPER, look at source code which implies a Proper
1499 option (i.e. use of CONFIG_IS_ENABLED() or $(SPL_) or
1500 $(SPL_TPL_) but for which there none;
1501 if MODE_NORMAL, ignore SPL
1502
1503 Returns:
1504 dict:
1505 key (str): CONFIG name (without 'CONFIG_' prefix
1506 value (list of ConfigUse): List of uses of this CONFIG
1507 """
1508 # Make sure we know about all the options
1509 not_found = collections.defaultdict(list)
1510 for use, rest in all_uses.items():
1511 name = use.cfg
1512 if name in IGNORE_SYMS:
1513 continue
1514 check = True
1515
1516 if spl_mode == MODE_SPL:
1517 check = use.is_spl
1518
1519 # If it is an SPL symbol, try prepending all SPL_ prefixes to
1520 # find at least one SPL symbol
1521 if use.is_spl:
1522 add_to_dict = False
1523 for prefix in SPL_PREFIXES:
1524 try_name = prefix + name
1525 sym = kconf.syms.get(try_name)
1526 if sym:
1527 break
1528 if not sym:
1529 not_found[f'SPL_{name}'].append(use)
1530 continue
1531 elif spl_mode == MODE_PROPER:
1532 # Try to find the Proper version of this symbol, i.e. without
1533 # the SPL_ prefix
1534 proper_name = is_not_proper(name)
1535 if proper_name:
1536 name = proper_name
1537 elif not use.is_spl:
1538 check = False
1539 else: # MODE_NORMAL
1540 debug = False
1541 sym = kconf.syms.get(name)
1542 if not sym:
1543 proper_name = is_not_proper(name)
1544 if proper_name:
1545 name = proper_name
1546 sym = kconf.syms.get(name)
1547 if not sym:
1548 for prefix in SPL_PREFIXES:
1549 try_name = prefix + name
1550 sym = kconf.syms.get(try_name)
1551 if sym:
1552 break
1553 if not sym:
1554 not_found[name].append(use)
1555 continue
1556
1557 sym = kconf.syms.get(name)
1558 if not sym and check:
1559 not_found[name].append(use)
1560 return not_found
1561
1562 def show_uses(uses):
1563 """Show a list of uses along with their filename and code snippet
1564
1565 Args:
1566 uses (dict):
1567 key (str): CONFIG name (without 'CONFIG_' prefix
1568 value (list of ConfigUse): List of uses of this CONFIG
1569 """
1570 for name in sorted(uses):
1571 print(f'{name}: ', end='')
1572 for i, use in enumerate(uses[name]):
1573 print(f'{" " if i else ""}{use.fname}: {use.rest.strip()}')
1574
1575
1576 print('Scanning Kconfig')
1577 kconf = KconfigScanner().conf
1578 print(f'Scanning source in {path}')
1579 args = ['git', 'grep', '-E', r'IS_ENABLED|\bCONFIG']
1580 with subprocess.Popen(args, stdout=subprocess.PIPE) as proc:
1581 out, err = proc.communicate()
1582 lines = out.splitlines()
1583 re_fname = re.compile('^([^:]*):(.*)')
1584 src_list = []
1585 mk_list = []
1586 for line in lines:
1587 linestr = line.decode('utf-8')
1588 m_fname = re_fname.search(linestr)
1589 if not m_fname:
1590 continue
1591 fname, rest = m_fname.groups()
1592 dirname, leaf = os.path.split(fname)
1593 root, ext = os.path.splitext(leaf)
1594 if ext == '.autoconf':
1595 pass
1596 elif ext in ['.c', '.h', '.S', '.lds', '.dts', '.dtsi', '.asl', '.cfg',
1597 '.env', '.tmpl']:
1598 src_list.append([fname, rest])
1599 elif 'Makefile' in root or ext == '.mk':
1600 mk_list.append([fname, rest])
1601 elif ext in ['.yml', '.sh', '.py', '.awk', '.pl', '.rst', '', '.sed']:
1602 pass
1603 elif 'Kconfig' in root or 'Kbuild' in root:
1604 pass
1605 elif 'README' in root:
1606 pass
1607 elif dirname in ['configs']:
1608 pass
1609 elif dirname.startswith('doc') or dirname.startswith('scripts/kconfig'):
1610 pass
1611 else:
1612 print(f'Not sure how to handle file {fname}')
1613
1614 # Scan the Makefiles
1615 all_uses, fname_uses = scan_makefiles(mk_list)
1616
1617 spl_not_found = set()
1618 proper_not_found = set()
1619
1620 # Make sure we know about all the options
1621 print('\nCONFIG options present in Makefiles but not Kconfig:')
1622 not_found = check_not_found(all_uses, MODE_NORMAL)
1623 show_uses(not_found)
1624
1625 print('\nCONFIG options present in Makefiles but not Kconfig (SPL):')
1626 not_found = check_not_found(all_uses, MODE_SPL)
1627 show_uses(not_found)
1628 spl_not_found |= set([is_not_proper(key) or key for key in not_found.keys()])
1629
1630 print('\nCONFIG options used as Proper in Makefiles but without a non-SPL_ variant:')
1631 not_found = check_not_found(all_uses, MODE_PROPER)
1632 show_uses(not_found)
1633 proper_not_found |= set([key for key in not_found.keys()])
1634
1635 # Scan the source code
1636 all_uses, fname_uses = scan_src_files(src_list)
1637
1638 # Make sure we know about all the options
1639 print('\nCONFIG options present in source but not Kconfig:')
1640 not_found = check_not_found(all_uses, MODE_NORMAL)
1641 show_uses(not_found)
1642
1643 print('\nCONFIG options present in source but not Kconfig (SPL):')
1644 not_found = check_not_found(all_uses, MODE_SPL)
1645 show_uses(not_found)
1646 spl_not_found |= set([is_not_proper(key) or key for key in not_found.keys()])
1647
1648 print('\nCONFIG options used as Proper in source but without a non-SPL_ variant:')
1649 not_found = check_not_found(all_uses, MODE_PROPER)
1650 show_uses(not_found)
1651 proper_not_found |= set([key for key in not_found.keys()])
1652
1653 print('\nCONFIG options used as SPL but without an SPL_ variant:')
1654 for item in sorted(spl_not_found):
1655 print(f' {item}')
1656
1657 print('\nCONFIG options used as Proper but without a non-SPL_ variant:')
1658 for item in sorted(proper_not_found):
1659 print(f' {item}')
1660
1661 # Write out the updated information
1662 if do_update:
1663 with open(os.path.join(path, 'scripts', 'conf_nospl'), 'w') as out:
1664 print('# These options should not be enabled in SPL builds\n',
1665 file=out)
1666 for item in sorted(spl_not_found):
1667 print(item, file=out)
1668 with open(os.path.join(path, 'scripts', 'conf_noproper'), 'w') as out:
1669 print('# These options should not be enabled in Proper builds\n',
1670 file=out)
1671 for item in sorted(proper_not_found):
1672 print(item, file=out)
1673
1674
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001675def main():
1676 try:
1677 cpu_count = multiprocessing.cpu_count()
1678 except NotImplementedError:
1679 cpu_count = 1
1680
Simon Glassb2e83c62021-12-18 14:54:31 -07001681 epilog = '''Move config options from headers to defconfig files. See
1682doc/develop/moveconfig.rst for documentation.'''
1683
1684 parser = ArgumentParser(epilog=epilog)
1685 # Add arguments here
1686 parser.add_argument('-a', '--add-imply', type=str, default='',
Simon Glasscb008832017-06-15 21:39:33 -06001687 help='comma-separated list of CONFIG options to add '
1688 "an 'imply' statement to for the CONFIG in -i")
Simon Glassb2e83c62021-12-18 14:54:31 -07001689 parser.add_argument('-A', '--skip-added', action='store_true', default=False,
Simon Glasscb008832017-06-15 21:39:33 -06001690 help="don't show options which are already marked as "
1691 'implying others')
Simon Glassb2e83c62021-12-18 14:54:31 -07001692 parser.add_argument('-b', '--build-db', action='store_true', default=False,
Simon Glassd73fcb12017-06-01 19:39:02 -06001693 help='build a CONFIG database')
Simon Glassb2e83c62021-12-18 14:54:31 -07001694 parser.add_argument('-c', '--color', action='store_true', default=False,
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001695 help='display the log in color')
Simon Glassb2e83c62021-12-18 14:54:31 -07001696 parser.add_argument('-C', '--commit', action='store_true', default=False,
Simon Glass9ede2122016-09-12 23:18:21 -06001697 help='Create a git commit for the operation')
Simon Glassb2e83c62021-12-18 14:54:31 -07001698 parser.add_argument('-d', '--defconfigs', type=str,
Simon Glassee4e61b2017-06-01 19:38:59 -06001699 help='a file containing a list of defconfigs to move, '
1700 "one per line (for example 'snow_defconfig') "
1701 "or '-' to read from stdin")
Simon Glassb2e83c62021-12-18 14:54:31 -07001702 parser.add_argument('-e', '--exit-on-error', action='store_true',
Simon Glasse1ae5632021-12-18 08:09:44 -07001703 default=False,
1704 help='exit immediately on any error')
Simon Glassb2e83c62021-12-18 14:54:31 -07001705 parser.add_argument('-f', '--find', action='store_true', default=False,
Simon Glass65d7fce2021-12-18 08:09:46 -07001706 help='Find boards with a given config combination')
Simon Glassb2e83c62021-12-18 14:54:31 -07001707 parser.add_argument('-i', '--imply', action='store_true', default=False,
Simon Glass99b66602017-06-01 19:39:03 -06001708 help='find options which imply others')
Simon Glassb2e83c62021-12-18 14:54:31 -07001709 parser.add_argument('-I', '--imply-flags', type=str, default='',
Simon Glass9b2a2e82017-06-15 21:39:32 -06001710 help="control the -i option ('help' for help")
Simon Glassb2e83c62021-12-18 14:54:31 -07001711 parser.add_argument('-j', '--jobs', type=int, default=cpu_count,
Simon Glasse1ae5632021-12-18 08:09:44 -07001712 help='the number of jobs to run simultaneously')
Simon Glassb2e83c62021-12-18 14:54:31 -07001713 parser.add_argument('-n', '--dry-run', action='store_true', default=False,
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001714 help='perform a trial run (show log with no changes)')
Simon Glassb2e83c62021-12-18 14:54:31 -07001715 parser.add_argument('-r', '--git-ref', type=str,
Simon Glasse1ae5632021-12-18 08:09:44 -07001716 help='the git ref to clone for building the autoconf.mk')
Simon Glassb2e83c62021-12-18 14:54:31 -07001717 parser.add_argument('-s', '--force-sync', action='store_true', default=False,
Masahiro Yamada8513dc02016-05-19 15:52:08 +09001718 help='force sync by savedefconfig')
Simon Glassb2e83c62021-12-18 14:54:31 -07001719 parser.add_argument('-S', '--spl', action='store_true', default=False,
Masahiro Yamada07913d12016-08-22 22:18:22 +09001720 help='parse config options defined for SPL build')
Simon Glass65e62032023-02-01 13:19:12 -07001721 parser.add_argument('--scan-source', action='store_true', default=False,
1722 help='scan source for uses of CONFIG options')
Simon Glassb2e83c62021-12-18 14:54:31 -07001723 parser.add_argument('-t', '--test', action='store_true', default=False,
Simon Glasse1ae5632021-12-18 08:09:44 -07001724 help='run unit tests')
Simon Glassb2e83c62021-12-18 14:54:31 -07001725 parser.add_argument('-y', '--yes', action='store_true', default=False,
Simon Glass6b403df2016-09-12 23:18:20 -06001726 help="respond 'yes' to any prompts")
Simon Glass65e62032023-02-01 13:19:12 -07001727 parser.add_argument('-u', '--update', action='store_true', default=False,
1728 help="update scripts/ files (use with --scan-source)")
Simon Glassb2e83c62021-12-18 14:54:31 -07001729 parser.add_argument('-v', '--verbose', action='store_true', default=False,
Joe Hershberger95bf9c72015-05-19 13:21:24 -05001730 help='show any build errors as boards are built')
Simon Glassb2e83c62021-12-18 14:54:31 -07001731 parser.add_argument('configs', nargs='*')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001732
Simon Glassb2e83c62021-12-18 14:54:31 -07001733 args = parser.parse_args()
1734 configs = args.configs
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001735
Simon Glassb2e83c62021-12-18 14:54:31 -07001736 if args.test:
Simon Glass84067a52021-12-18 08:09:45 -07001737 sys.argv = [sys.argv[0]]
1738 fail, count = doctest.testmod()
1739 if fail:
1740 return 1
1741 unittest.main()
1742
Simon Glass65e62032023-02-01 13:19:12 -07001743 if args.scan_source:
1744 do_scan_source(os.getcwd(), args.update)
1745 return
1746
Simon Glassb2e83c62021-12-18 14:54:31 -07001747 if not any((len(configs), args.force_sync, args.build_db, args.imply,
1748 args.find)):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001749 parser.print_usage()
1750 sys.exit(1)
1751
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001752 # prefix the option name with CONFIG_ if missing
Simon Glass65d7fce2021-12-18 08:09:46 -07001753 configs = [prefix_config(cfg) for cfg in configs]
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001754
Joe Hershberger2144f882015-05-19 13:21:20 -05001755 check_top_directory()
1756
Simon Glassb2e83c62021-12-18 14:54:31 -07001757 if args.imply:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001758 imply_flags = 0
Simon Glassb2e83c62021-12-18 14:54:31 -07001759 if args.imply_flags == 'all':
Simon Glassdee36c72017-07-10 14:47:46 -06001760 imply_flags = -1
1761
Simon Glassb2e83c62021-12-18 14:54:31 -07001762 elif args.imply_flags:
1763 for flag in args.imply_flags.split(','):
Simon Glassdee36c72017-07-10 14:47:46 -06001764 bad = flag not in IMPLY_FLAGS
1765 if bad:
Simon Glass793dca32019-10-31 07:42:57 -06001766 print("Invalid flag '%s'" % flag)
Simon Glassdee36c72017-07-10 14:47:46 -06001767 if flag == 'help' or bad:
Simon Glass793dca32019-10-31 07:42:57 -06001768 print("Imply flags: (separate with ',')")
1769 for name, info in IMPLY_FLAGS.items():
1770 print(' %-15s: %s' % (name, info[1]))
Simon Glassdee36c72017-07-10 14:47:46 -06001771 parser.print_usage()
1772 sys.exit(1)
1773 imply_flags |= IMPLY_FLAGS[flag][0]
Simon Glass9b2a2e82017-06-15 21:39:32 -06001774
Simon Glassb2e83c62021-12-18 14:54:31 -07001775 do_imply_config(configs, args.add_imply, imply_flags, args.skip_added)
Simon Glass99b66602017-06-01 19:39:03 -06001776 return
1777
Simon Glassb2e83c62021-12-18 14:54:31 -07001778 if args.find:
Simon Glass65d7fce2021-12-18 08:09:46 -07001779 do_find_config(configs)
1780 return
1781
Simon Glass3481e892023-09-23 13:43:53 -06001782 # We are either building the database or forcing a sync of defconfigs
Simon Glassd73fcb12017-06-01 19:39:02 -06001783 config_db = {}
Simon Glass793dca32019-10-31 07:42:57 -06001784 db_queue = queue.Queue()
Simon Glassd73fcb12017-06-01 19:39:02 -06001785 t = DatabaseThread(config_db, db_queue)
Simon Glassad1f1872023-09-23 13:43:51 -06001786 t.daemon = True
Simon Glassd73fcb12017-06-01 19:39:02 -06001787 t.start()
1788
Simon Glass63df2022023-09-23 13:43:50 -06001789 check_clean_directory()
1790 bsettings.setup('')
1791 toolchains = toolchain.Toolchains()
1792 toolchains.GetSettings()
1793 toolchains.Scan(verbose=False)
Simon Glass3481e892023-09-23 13:43:53 -06001794 move_config(toolchains, [], args, db_queue)
Simon Glass63df2022023-09-23 13:43:50 -06001795 db_queue.join()
Joe Hershberger2144f882015-05-19 13:21:20 -05001796
Simon Glassb2e83c62021-12-18 14:54:31 -07001797 if args.commit:
Simon Glass9ede2122016-09-12 23:18:21 -06001798 subprocess.call(['git', 'add', '-u'])
1799 if configs:
1800 msg = 'Convert %s %sto Kconfig' % (configs[0],
1801 'et al ' if len(configs) > 1 else '')
1802 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1803 '\n '.join(configs))
1804 else:
1805 msg = 'configs: Resync with savedefconfig'
1806 msg += '\n\nRsync all defconfig files using moveconfig.py'
1807 subprocess.call(['git', 'commit', '-s', '-m', msg])
1808
Simon Glassb2e83c62021-12-18 14:54:31 -07001809 if args.build_db:
Simon Glass91197aa2021-12-18 14:54:35 -07001810 with open(CONFIG_DATABASE, 'w', encoding='utf-8') as fd:
Simon Glass793dca32019-10-31 07:42:57 -06001811 for defconfig, configs in config_db.items():
Simon Glassc79d18c2017-08-13 16:02:54 -06001812 fd.write('%s\n' % defconfig)
Simon Glassd73fcb12017-06-01 19:39:02 -06001813 for config in sorted(configs.keys()):
Simon Glassc79d18c2017-08-13 16:02:54 -06001814 fd.write(' %s=%s\n' % (config, configs[config]))
1815 fd.write('\n')
Simon Glassd73fcb12017-06-01 19:39:02 -06001816
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001817if __name__ == '__main__':
Simon Glass65d7fce2021-12-18 08:09:46 -07001818 sys.exit(main())