blob: 84bc875fff8efcf15d7acadce3a0944bf3c1c65d [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
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +010014import asteval
Simon Glass99b66602017-06-01 19:39:03 -060015import collections
Simon Glass91197aa2021-12-18 14:54:35 -070016from contextlib import ExitStack
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +090017import copy
Masahiro Yamadaf2f69812016-07-25 19:15:25 +090018import difflib
Simon Glass84067a52021-12-18 08:09:45 -070019import doctest
Masahiro Yamadac8e1b102016-05-19 15:52:07 +090020import filecmp
Masahiro Yamada5a27c732015-05-20 11:36:07 +090021import fnmatch
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +090022import glob
Masahiro Yamada5a27c732015-05-20 11:36:07 +090023import multiprocessing
Masahiro Yamada5a27c732015-05-20 11:36:07 +090024import os
Simon Glass793dca32019-10-31 07:42:57 -060025import queue
Masahiro Yamada5a27c732015-05-20 11:36:07 +090026import re
27import shutil
28import subprocess
29import sys
30import tempfile
Simon Glassd73fcb12017-06-01 19:39:02 -060031import threading
Masahiro Yamada5a27c732015-05-20 11:36:07 +090032import time
Simon Glass84067a52021-12-18 08:09:45 -070033import unittest
Masahiro Yamada5a27c732015-05-20 11:36:07 +090034
Simon Glass0ede00f2020-04-17 18:09:02 -060035from buildman import bsettings
36from buildman import kconfiglib
37from buildman import toolchain
Simon Glasscb008832017-06-15 21:39:33 -060038
Masahiro Yamada5a27c732015-05-20 11:36:07 +090039SHOW_GNU_MAKE = 'scripts/show-gnu-make'
40SLEEP_TIME=0.03
41
Masahiro Yamada5a27c732015-05-20 11:36:07 +090042STATE_IDLE = 0
43STATE_DEFCONFIG = 1
44STATE_AUTOCONF = 2
Joe Hershberger96464ba2015-05-19 13:21:17 -050045STATE_SAVEDEFCONFIG = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +090046
47ACTION_MOVE = 0
Masahiro Yamadacc008292016-05-19 15:51:56 +090048ACTION_NO_ENTRY = 1
Masahiro Yamada916224c2016-08-22 22:18:21 +090049ACTION_NO_ENTRY_WARN = 2
50ACTION_NO_CHANGE = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +090051
52COLOR_BLACK = '0;30'
53COLOR_RED = '0;31'
54COLOR_GREEN = '0;32'
55COLOR_BROWN = '0;33'
56COLOR_BLUE = '0;34'
57COLOR_PURPLE = '0;35'
58COLOR_CYAN = '0;36'
59COLOR_LIGHT_GRAY = '0;37'
60COLOR_DARK_GRAY = '1;30'
61COLOR_LIGHT_RED = '1;31'
62COLOR_LIGHT_GREEN = '1;32'
63COLOR_YELLOW = '1;33'
64COLOR_LIGHT_BLUE = '1;34'
65COLOR_LIGHT_PURPLE = '1;35'
66COLOR_LIGHT_CYAN = '1;36'
67COLOR_WHITE = '1;37'
68
Simon Glassf3b8e642017-06-01 19:39:01 -060069AUTO_CONF_PATH = 'include/config/auto.conf'
Simon Glassd73fcb12017-06-01 19:39:02 -060070CONFIG_DATABASE = 'moveconfig.db'
Simon Glassf3b8e642017-06-01 19:39:01 -060071
Simon Glasscb008832017-06-15 21:39:33 -060072CONFIG_LEN = len('CONFIG_')
Simon Glassf3b8e642017-06-01 19:39:01 -060073
Markus Klotzbuecherb237d352019-05-15 15:15:52 +020074SIZES = {
Simon Glassdaa694d2021-12-18 14:54:30 -070075 '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,
91 'SZ_4G': 0x100000000
Markus Klotzbuecherb237d352019-05-15 15:15:52 +020092}
93
Simon Glassb8d11da2022-02-08 11:49:45 -070094RE_REMOVE_DEFCONFIG = re.compile(r'(.*)_defconfig')
95
Masahiro Yamada5a27c732015-05-20 11:36:07 +090096### helper functions ###
Masahiro Yamada5a27c732015-05-20 11:36:07 +090097def check_top_directory():
98 """Exit if we are not at the top of source directory."""
Simon Glass91197aa2021-12-18 14:54:35 -070099 for fname in 'README', 'Licenses':
100 if not os.path.exists(fname):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900101 sys.exit('Please run at the top of source directory.')
102
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900103def check_clean_directory():
104 """Exit if the source tree is not clean."""
Simon Glass91197aa2021-12-18 14:54:35 -0700105 for fname in '.config', 'include/config':
106 if os.path.exists(fname):
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900107 sys.exit("source tree is not clean, please run 'make mrproper'")
108
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900109def get_make_cmd():
110 """Get the command name of GNU Make.
111
112 U-Boot needs GNU Make for building, but the command name is not
113 necessarily "make". (for example, "gmake" on FreeBSD).
114 Returns the most appropriate command name on your system.
115 """
Simon Glass91197aa2021-12-18 14:54:35 -0700116 with subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE) as proc:
117 ret = proc.communicate()
118 if proc.returncode:
119 sys.exit('GNU Make not found')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900120 return ret[0].rstrip()
121
Simon Glass25f978c2017-06-01 19:38:58 -0600122def get_matched_defconfig(line):
123 """Get the defconfig files that match a pattern
124
125 Args:
Simon Glass91197aa2021-12-18 14:54:35 -0700126 line (str): Path or filename to match, e.g. 'configs/snow_defconfig' or
Simon Glass25f978c2017-06-01 19:38:58 -0600127 'k2*_defconfig'. If no directory is provided, 'configs/' is
128 prepended
129
130 Returns:
Simon Glass91197aa2021-12-18 14:54:35 -0700131 list of str: a list of matching defconfig files
Simon Glass25f978c2017-06-01 19:38:58 -0600132 """
133 dirname = os.path.dirname(line)
134 if dirname:
135 pattern = line
136 else:
137 pattern = os.path.join('configs', line)
138 return glob.glob(pattern) + glob.glob(pattern + '_defconfig')
139
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900140def get_matched_defconfigs(defconfigs_file):
Simon Glassee4e61b2017-06-01 19:38:59 -0600141 """Get all the defconfig files that match the patterns in a file.
142
143 Args:
Simon Glass91197aa2021-12-18 14:54:35 -0700144 defconfigs_file (str): File containing a list of defconfigs to process,
145 or '-' to read the list from stdin
Simon Glassee4e61b2017-06-01 19:38:59 -0600146
147 Returns:
Simon Glass91197aa2021-12-18 14:54:35 -0700148 list of str: A list of paths to defconfig files, with no duplicates
Simon Glassee4e61b2017-06-01 19:38:59 -0600149 """
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900150 defconfigs = []
Simon Glass91197aa2021-12-18 14:54:35 -0700151 with ExitStack() as stack:
152 if defconfigs_file == '-':
153 inf = sys.stdin
154 defconfigs_file = 'stdin'
155 else:
156 inf = stack.enter_context(open(defconfigs_file, encoding='utf-8'))
157 for i, line in enumerate(inf):
158 line = line.strip()
159 if not line:
160 continue # skip blank lines silently
161 if ' ' in line:
162 line = line.split(' ')[0] # handle 'git log' input
163 matched = get_matched_defconfig(line)
164 if not matched:
165 print(f"warning: {defconfigs_file}:{i + 1}: no defconfig matched '{line}'",
166 file=sys.stderr)
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900167
Simon Glass91197aa2021-12-18 14:54:35 -0700168 defconfigs += matched
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900169
170 # use set() to drop multiple matching
Simon Glass91197aa2021-12-18 14:54:35 -0700171 return [defconfig[len('configs') + 1:] for defconfig in set(defconfigs)]
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900172
Masahiro Yamada684c3062016-07-25 19:15:28 +0900173def get_all_defconfigs():
Simon Glass91197aa2021-12-18 14:54:35 -0700174 """Get all the defconfig files under the configs/ directory.
175
176 Returns:
177 list of str: List of paths to defconfig files
178 """
Masahiro Yamada684c3062016-07-25 19:15:28 +0900179 defconfigs = []
Simon Glass91197aa2021-12-18 14:54:35 -0700180 for (dirpath, _, filenames) in os.walk('configs'):
Masahiro Yamada684c3062016-07-25 19:15:28 +0900181 dirpath = dirpath[len('configs') + 1:]
182 for filename in fnmatch.filter(filenames, '*_defconfig'):
183 defconfigs.append(os.path.join(dirpath, filename))
184
185 return defconfigs
186
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900187def color_text(color_enabled, color, string):
188 """Return colored string."""
189 if color_enabled:
Masahiro Yamada1d085562016-05-19 15:52:02 +0900190 # LF should not be surrounded by the escape sequence.
191 # Otherwise, additional whitespace or line-feed might be printed.
192 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
193 for s in string.split('\n') ])
Simon Glass91197aa2021-12-18 14:54:35 -0700194 return string
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900195
Simon Glass91197aa2021-12-18 14:54:35 -0700196def show_diff(alines, blines, file_path, color_enabled):
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900197 """Show unidified diff.
198
Simon Glass91197aa2021-12-18 14:54:35 -0700199 Args:
200 alines (list of str): A list of lines (before)
201 blines (list of str): A list of lines (after)
202 file_path (str): Path to the file
203 color_enabled (bool): Display the diff in color
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900204 """
Simon Glass91197aa2021-12-18 14:54:35 -0700205 diff = difflib.unified_diff(alines, blines,
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900206 fromfile=os.path.join('a', file_path),
207 tofile=os.path.join('b', file_path))
208
209 for line in diff:
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300210 if line.startswith('-') and not line.startswith('--'):
211 print(color_text(color_enabled, COLOR_RED, line))
212 elif line.startswith('+') and not line.startswith('++'):
213 print(color_text(color_enabled, COLOR_GREEN, line))
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900214 else:
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300215 print(line)
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900216
Simon Glass91197aa2021-12-18 14:54:35 -0700217def extend_matched_lines(lines, matched, pre_patterns, post_patterns,
218 extend_pre, extend_post):
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900219 """Extend matched lines if desired patterns are found before/after already
220 matched lines.
221
Simon Glass91197aa2021-12-18 14:54:35 -0700222 Args:
223 lines (list of str): list of lines handled.
224 matched (list of int): list of line numbers that have been already
225 matched (will be updated by this function)
226 pre_patterns (list of re.Pattern): list of regular expression that should
227 be matched as preamble
228 post_patterns (list of re.Pattern): list of regular expression that should
229 be matched as postamble
230 extend_pre (bool): Add the line number of matched preamble to the matched
231 list
232 extend_post (bool): Add the line number of matched postamble to the
233 matched list
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900234 """
235 extended_matched = []
236
237 j = matched[0]
238
239 for i in matched:
240 if i == 0 or i < j:
241 continue
242 j = i
243 while j in matched:
244 j += 1
245 if j >= len(lines):
246 break
247
Simon Glass91197aa2021-12-18 14:54:35 -0700248 for pat in pre_patterns:
249 if pat.search(lines[i - 1]):
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900250 break
251 else:
252 # not matched
253 continue
254
Simon Glass91197aa2021-12-18 14:54:35 -0700255 for pat in post_patterns:
256 if pat.search(lines[j]):
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900257 break
258 else:
259 # not matched
260 continue
261
262 if extend_pre:
263 extended_matched.append(i - 1)
264 if extend_post:
265 extended_matched.append(j)
266
267 matched += extended_matched
268 matched.sort()
269
Simon Glassb2e83c62021-12-18 14:54:31 -0700270def confirm(args, prompt):
Simon Glass91197aa2021-12-18 14:54:35 -0700271 """Ask the user to confirm something
272
273 Args:
274 args (Namespace ): program arguments
275
276 Returns:
277 bool: True to confirm, False to cancel/stop
278 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700279 if not args.yes:
Chris Packham85edfc12017-05-02 21:30:46 +1200280 while True:
Simon Glass91197aa2021-12-18 14:54:35 -0700281 choice = input(f'{prompt} [y/n]: ')
Chris Packham85edfc12017-05-02 21:30:46 +1200282 choice = choice.lower()
Simon Glass793dca32019-10-31 07:42:57 -0600283 print(choice)
Simon Glass91197aa2021-12-18 14:54:35 -0700284 if choice in ('y', 'n'):
Chris Packham85edfc12017-05-02 21:30:46 +1200285 break
286
287 if choice == 'n':
288 return False
289
290 return True
291
Simon Glass2fd85bd2021-12-18 14:54:33 -0700292def write_file(fname, data):
293 """Write data to a file
294
295 Args:
296 fname (str): Filename to write to
297 data (list of str): Lines to write (with or without trailing newline);
298 or str to write
299 """
300 with open(fname, 'w', encoding='utf-8') as out:
301 if isinstance(data, list):
302 for line in data:
303 print(line.rstrip('\n'), file=out)
304 else:
305 out.write(data)
306
Simon Glass37f815c2021-12-18 14:54:34 -0700307def read_file(fname, as_lines=True, skip_unicode=False):
308 """Read a file and return the contents
309
310 Args:
311 fname (str): Filename to read from
312 as_lines: Return file contents as a list of lines
313 skip_unicode (bool): True to report unicode errors and continue
314
315 Returns:
316 iter of str: List of ;ines from the file with newline removed; str if
317 as_lines is False with newlines intact; or None if a unicode error
318 occurred
319
320 Raises:
321 UnicodeDecodeError: Unicode error occurred when reading
322 """
323 with open(fname, encoding='utf-8') as inf:
324 try:
325 if as_lines:
326 return [line.rstrip('\n') for line in inf.readlines()]
327 else:
328 return inf.read()
329 except UnicodeDecodeError as e:
330 if not skip_unicode:
Simon Glass68a0b712022-02-11 13:23:22 -0700331 raise
Simon Glass37f815c2021-12-18 14:54:34 -0700332 print("Failed on file %s': %s" % (fname, e))
333 return None
334
Simon Glassb2e83c62021-12-18 14:54:31 -0700335def cleanup_empty_blocks(header_path, args):
Chris Packham4d9dbb12019-01-30 20:23:16 +1300336 """Clean up empty conditional blocks
337
Simon Glass91197aa2021-12-18 14:54:35 -0700338 Args:
339 header_path (str): path to the cleaned file.
340 args (Namespace): program arguments
Chris Packham4d9dbb12019-01-30 20:23:16 +1300341 """
342 pattern = re.compile(r'^\s*#\s*if.*$\n^\s*#\s*endif.*$\n*', flags=re.M)
Simon Glass37f815c2021-12-18 14:54:34 -0700343 data = read_file(header_path, as_lines=False, skip_unicode=True)
344 if data is None:
345 return
Chris Packham4d9dbb12019-01-30 20:23:16 +1300346
347 new_data = pattern.sub('\n', data)
348
349 show_diff(data.splitlines(True), new_data.splitlines(True), header_path,
Simon Glassb2e83c62021-12-18 14:54:31 -0700350 args.color)
Chris Packham4d9dbb12019-01-30 20:23:16 +1300351
Simon Glassb2e83c62021-12-18 14:54:31 -0700352 if args.dry_run:
Chris Packham4d9dbb12019-01-30 20:23:16 +1300353 return
354
Simon Glass37f815c2021-12-18 14:54:34 -0700355 if new_data != data:
356 write_file(header_path, new_data)
Chris Packham4d9dbb12019-01-30 20:23:16 +1300357
Simon Glassb2e83c62021-12-18 14:54:31 -0700358def cleanup_one_header(header_path, patterns, args):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900359 """Clean regex-matched lines away from a file.
360
Simon Glass91197aa2021-12-18 14:54:35 -0700361 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900362 header_path: path to the cleaned file.
363 patterns: list of regex patterns. Any lines matching to these
364 patterns are deleted.
Simon Glass91197aa2021-12-18 14:54:35 -0700365 args (Namespace): program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900366 """
Simon Glass37f815c2021-12-18 14:54:34 -0700367 lines = read_file(header_path, skip_unicode=True)
368 if lines is None:
369 return
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900370
371 matched = []
372 for i, line in enumerate(lines):
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300373 if i - 1 in matched and lines[i - 1].endswith('\\'):
Masahiro Yamadaa3a779f2016-07-25 19:15:27 +0900374 matched.append(i)
375 continue
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900376 for pattern in patterns:
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900377 if pattern.search(line):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900378 matched.append(i)
379 break
380
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900381 if not matched:
382 return
383
384 # remove empty #ifdef ... #endif, successive blank lines
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300385 pattern_if = re.compile(r'#\s*if(def|ndef)?\b') # #if, #ifdef, #ifndef
386 pattern_elif = re.compile(r'#\s*el(if|se)\b') # #elif, #else
387 pattern_endif = re.compile(r'#\s*endif\b') # #endif
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900388 pattern_blank = re.compile(r'^\s*$') # empty line
389
390 while True:
391 old_matched = copy.copy(matched)
392 extend_matched_lines(lines, matched, [pattern_if],
393 [pattern_endif], True, True)
394 extend_matched_lines(lines, matched, [pattern_elif],
395 [pattern_elif, pattern_endif], True, False)
396 extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
397 [pattern_blank], False, True)
398 extend_matched_lines(lines, matched, [pattern_blank],
399 [pattern_elif, pattern_endif], True, False)
400 extend_matched_lines(lines, matched, [pattern_blank],
401 [pattern_blank], True, False)
402 if matched == old_matched:
403 break
404
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900405 tolines = copy.copy(lines)
406
407 for i in reversed(matched):
408 tolines.pop(i)
409
Simon Glassb2e83c62021-12-18 14:54:31 -0700410 show_diff(lines, tolines, header_path, args.color)
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900411
Simon Glassb2e83c62021-12-18 14:54:31 -0700412 if args.dry_run:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900413 return
414
Simon Glass2fd85bd2021-12-18 14:54:33 -0700415 write_file(header_path, tolines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900416
Simon Glassb2e83c62021-12-18 14:54:31 -0700417def cleanup_headers(configs, args):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900418 """Delete config defines from board headers.
419
Simon Glass91197aa2021-12-18 14:54:35 -0700420 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900421 configs: A list of CONFIGs to remove.
Simon Glass91197aa2021-12-18 14:54:35 -0700422 args (Namespace): program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900423 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700424 if not confirm(args, 'Clean up headers?'):
Chris Packham85edfc12017-05-02 21:30:46 +1200425 return
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900426
427 patterns = []
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900428 for config in configs:
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300429 patterns.append(re.compile(r'#\s*define\s+%s\b' % config))
430 patterns.append(re.compile(r'#\s*undef\s+%s\b' % config))
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900431
Joe Hershberger60727f52015-05-19 13:21:21 -0500432 for dir in 'include', 'arch', 'board':
433 for (dirpath, dirnames, filenames) in os.walk(dir):
Masahiro Yamadadc6de502016-07-25 19:15:22 +0900434 if dirpath == os.path.join('include', 'generated'):
435 continue
Joe Hershberger60727f52015-05-19 13:21:21 -0500436 for filename in filenames:
Simon Glassa38cc172020-08-11 11:23:34 -0600437 if not filename.endswith(('~', '.dts', '.dtsi', '.bin',
Trevor Woernerdc514d72021-03-15 12:01:33 -0400438 '.elf','.aml','.dat')):
Chris Packham4d9dbb12019-01-30 20:23:16 +1300439 header_path = os.path.join(dirpath, filename)
Tom Rini02b56702019-11-10 21:19:37 -0500440 # This file contains UTF-16 data and no CONFIG symbols
441 if header_path == 'include/video_font_data.h':
442 continue
Simon Glassb2e83c62021-12-18 14:54:31 -0700443 cleanup_one_header(header_path, patterns, args)
444 cleanup_empty_blocks(header_path, args)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900445
Tom Rini25b8ace2022-04-02 18:18:57 -0400446def cleanup_one_extra_option(defconfig_path, configs, args):
447 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file.
448
449 Args:
450 defconfig_path: path to the cleaned defconfig file.
451 configs: A list of CONFIGs to remove.
452 args (Namespace): program arguments
453 """
454
455 start = 'CONFIG_SYS_EXTRA_OPTIONS="'
456 end = '"'
457
458 lines = read_file(defconfig_path)
459
460 for i, line in enumerate(lines):
461 if line.startswith(start) and line.endswith(end):
462 break
463 else:
464 # CONFIG_SYS_EXTRA_OPTIONS was not found in this defconfig
465 return
466
467 old_tokens = line[len(start):-len(end)].split(',')
468 new_tokens = []
469
470 for token in old_tokens:
471 pos = token.find('=')
472 if not (token[:pos] if pos >= 0 else token) in configs:
473 new_tokens.append(token)
474
475 if new_tokens == old_tokens:
476 return
477
478 tolines = copy.copy(lines)
479
480 if new_tokens:
481 tolines[i] = start + ','.join(new_tokens) + end
482 else:
483 tolines.pop(i)
484
485 show_diff(lines, tolines, defconfig_path, args.color)
486
487 if args.dry_run:
488 return
489
490 write_file(defconfig_path, tolines)
491
492def cleanup_extra_options(configs, args):
493 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files.
494
495 Args:
496 configs: A list of CONFIGs to remove.
497 args (Namespace): program arguments
498 """
499 if not confirm(args, 'Clean up CONFIG_SYS_EXTRA_OPTIONS?'):
500 return
501
502 configs = [ config[len('CONFIG_'):] for config in configs ]
503
504 defconfigs = get_all_defconfigs()
505
506 for defconfig in defconfigs:
507 cleanup_one_extra_option(os.path.join('configs', defconfig), configs,
508 args)
509
Simon Glassb2e83c62021-12-18 14:54:31 -0700510def cleanup_whitelist(configs, args):
Chris Packhamca438342017-05-02 21:30:47 +1200511 """Delete config whitelist entries
512
Simon Glass91197aa2021-12-18 14:54:35 -0700513 Args:
Chris Packhamca438342017-05-02 21:30:47 +1200514 configs: A list of CONFIGs to remove.
Simon Glass91197aa2021-12-18 14:54:35 -0700515 args (Namespace): program arguments
Chris Packhamca438342017-05-02 21:30:47 +1200516 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700517 if not confirm(args, 'Clean up whitelist entries?'):
Chris Packhamca438342017-05-02 21:30:47 +1200518 return
519
Simon Glass37f815c2021-12-18 14:54:34 -0700520 lines = read_file(os.path.join('scripts', 'config_whitelist.txt'))
Chris Packhamca438342017-05-02 21:30:47 +1200521
522 lines = [x for x in lines if x.strip() not in configs]
523
Simon Glass2fd85bd2021-12-18 14:54:33 -0700524 write_file(os.path.join('scripts', 'config_whitelist.txt'), lines)
Chris Packhamca438342017-05-02 21:30:47 +1200525
Chris Packhamf90df592017-05-02 21:30:48 +1200526def find_matching(patterns, line):
527 for pat in patterns:
528 if pat.search(line):
529 return True
530 return False
531
Simon Glassb2e83c62021-12-18 14:54:31 -0700532def cleanup_readme(configs, args):
Chris Packhamf90df592017-05-02 21:30:48 +1200533 """Delete config description in README
534
Simon Glass91197aa2021-12-18 14:54:35 -0700535 Args:
Chris Packhamf90df592017-05-02 21:30:48 +1200536 configs: A list of CONFIGs to remove.
Simon Glass91197aa2021-12-18 14:54:35 -0700537 args (Namespace): program arguments
Chris Packhamf90df592017-05-02 21:30:48 +1200538 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700539 if not confirm(args, 'Clean up README?'):
Chris Packhamf90df592017-05-02 21:30:48 +1200540 return
541
542 patterns = []
543 for config in configs:
544 patterns.append(re.compile(r'^\s+%s' % config))
545
Simon Glass37f815c2021-12-18 14:54:34 -0700546 lines = read_file('README')
Chris Packhamf90df592017-05-02 21:30:48 +1200547
548 found = False
549 newlines = []
550 for line in lines:
551 if not found:
552 found = find_matching(patterns, line)
553 if found:
554 continue
555
556 if found and re.search(r'^\s+CONFIG', line):
557 found = False
558
559 if not found:
560 newlines.append(line)
561
Simon Glass2fd85bd2021-12-18 14:54:33 -0700562 write_file('README', newlines)
Chris Packhamf90df592017-05-02 21:30:48 +1200563
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200564def try_expand(line):
565 """If value looks like an expression, try expanding it
566 Otherwise just return the existing value
567 """
568 if line.find('=') == -1:
569 return line
570
571 try:
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +0100572 aeval = asteval.Interpreter( usersyms=SIZES, minimal=True )
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200573 cfg, val = re.split("=", line)
574 val= val.strip('\"')
Simon Glassdaa694d2021-12-18 14:54:30 -0700575 if re.search(r'[*+-/]|<<|SZ_+|\(([^\)]+)\)', val):
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +0100576 newval = hex(aeval(val))
Simon Glassdaa694d2021-12-18 14:54:30 -0700577 print('\tExpanded expression %s to %s' % (val, newval))
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200578 return cfg+'='+newval
579 except:
Simon Glassdaa694d2021-12-18 14:54:30 -0700580 print('\tFailed to expand expression in %s' % line)
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200581
582 return line
583
Chris Packhamca438342017-05-02 21:30:47 +1200584
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900585### classes ###
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900586class Progress:
587
588 """Progress Indicator"""
589
590 def __init__(self, total):
591 """Create a new progress indicator.
592
Simon Glass91197aa2021-12-18 14:54:35 -0700593 Args:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900594 total: A number of defconfig files to process.
595 """
596 self.current = 0
597 self.total = total
598
599 def inc(self):
600 """Increment the number of processed defconfig files."""
601
602 self.current += 1
603
604 def show(self):
605 """Display the progress."""
Simon Glass793dca32019-10-31 07:42:57 -0600606 print(' %d defconfigs out of %d\r' % (self.current, self.total), end=' ')
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900607 sys.stdout.flush()
608
Simon Glasscb008832017-06-15 21:39:33 -0600609
610class KconfigScanner:
611 """Kconfig scanner."""
612
613 def __init__(self):
614 """Scan all the Kconfig files and create a Config object."""
615 # Define environment variables referenced from Kconfig
616 os.environ['srctree'] = os.getcwd()
617 os.environ['UBOOTVERSION'] = 'dummy'
618 os.environ['KCONFIG_OBJDIR'] = ''
Tom Rini65e05dd2019-09-20 17:42:09 -0400619 self.conf = kconfiglib.Kconfig()
Simon Glasscb008832017-06-15 21:39:33 -0600620
621
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900622class KconfigParser:
623
624 """A parser of .config and include/autoconf.mk."""
625
626 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
627 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
628
Simon Glassb2e83c62021-12-18 14:54:31 -0700629 def __init__(self, configs, args, build_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900630 """Create a new parser.
631
Simon Glass91197aa2021-12-18 14:54:35 -0700632 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900633 configs: A list of CONFIGs to move.
Simon Glass91197aa2021-12-18 14:54:35 -0700634 args (Namespace): program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900635 build_dir: Build directory.
636 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900637 self.configs = configs
Simon Glassb2e83c62021-12-18 14:54:31 -0700638 self.args = args
Masahiro Yamada1f169922016-05-19 15:52:00 +0900639 self.dotconfig = os.path.join(build_dir, '.config')
640 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
Masahiro Yamada07913d12016-08-22 22:18:22 +0900641 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
642 'autoconf.mk')
Simon Glassf3b8e642017-06-01 19:39:01 -0600643 self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900644 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900645
Simon Glass6821a742017-07-10 14:47:47 -0600646 def get_arch(self):
647 """Parse .config file and return the architecture.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900648
649 Returns:
Simon Glass6821a742017-07-10 14:47:47 -0600650 Architecture name (e.g. 'arm').
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900651 """
652 arch = ''
653 cpu = ''
Simon Glass37f815c2021-12-18 14:54:34 -0700654 for line in read_file(self.dotconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900655 m = self.re_arch.match(line)
656 if m:
657 arch = m.group(1)
658 continue
659 m = self.re_cpu.match(line)
660 if m:
661 cpu = m.group(1)
662
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900663 if not arch:
664 return None
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900665
666 # fix-up for aarch64
667 if arch == 'arm' and cpu == 'armv8':
668 arch = 'aarch64'
669
Simon Glass6821a742017-07-10 14:47:47 -0600670 return arch
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900671
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900672 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900673 """Parse .config, defconfig, include/autoconf.mk for one config.
674
675 This function looks for the config options in the lines from
676 defconfig, .config, and include/autoconf.mk in order to decide
677 which action should be taken for this defconfig.
678
Simon Glass91197aa2021-12-18 14:54:35 -0700679 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900680 config: CONFIG name to parse.
Masahiro Yamadacc008292016-05-19 15:51:56 +0900681 dotconfig_lines: lines from the .config file.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900682 autoconf_lines: lines from the include/autoconf.mk file.
683
684 Returns:
685 A tupple of the action for this defconfig and the line
686 matched for the config.
687 """
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900688 not_set = '# %s is not set' % config
689
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900690 for line in autoconf_lines:
691 line = line.rstrip()
692 if line.startswith(config + '='):
Masahiro Yamadacc008292016-05-19 15:51:56 +0900693 new_val = line
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900694 break
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900695 else:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900696 new_val = not_set
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900697
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200698 new_val = try_expand(new_val)
699
Masahiro Yamada916224c2016-08-22 22:18:21 +0900700 for line in dotconfig_lines:
701 line = line.rstrip()
702 if line.startswith(config + '=') or line == not_set:
703 old_val = line
704 break
705 else:
706 if new_val == not_set:
707 return (ACTION_NO_ENTRY, config)
708 else:
709 return (ACTION_NO_ENTRY_WARN, config)
710
Masahiro Yamadacc008292016-05-19 15:51:56 +0900711 # If this CONFIG is neither bool nor trisate
712 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
713 # tools/scripts/define2mk.sed changes '1' to 'y'.
714 # This is a problem if the CONFIG is int type.
715 # Check the type in Kconfig and handle it correctly.
716 if new_val[-2:] == '=y':
717 new_val = new_val[:-1] + '1'
718
Masahiro Yamada50301592016-06-15 14:33:50 +0900719 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
720 new_val)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900721
Masahiro Yamada1d085562016-05-19 15:52:02 +0900722 def update_dotconfig(self):
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900723 """Parse files for the config options and update the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900724
Masahiro Yamadacc008292016-05-19 15:51:56 +0900725 This function parses the generated .config and include/autoconf.mk
726 searching the target options.
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900727 Move the config option(s) to the .config as needed.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900728
Simon Glass91197aa2021-12-18 14:54:35 -0700729 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900730 defconfig: defconfig name.
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900731
732 Returns:
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900733 Return a tuple of (updated flag, log string).
734 The "updated flag" is True if the .config was updated, False
735 otherwise. The "log string" shows what happend to the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900736 """
737
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900738 results = []
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900739 updated = False
Masahiro Yamada916224c2016-08-22 22:18:21 +0900740 suspicious = False
Masahiro Yamada07913d12016-08-22 22:18:22 +0900741 rm_files = [self.config_autoconf, self.autoconf]
742
Simon Glassb2e83c62021-12-18 14:54:31 -0700743 if self.args.spl:
Masahiro Yamada07913d12016-08-22 22:18:22 +0900744 if os.path.exists(self.spl_autoconf):
745 autoconf_path = self.spl_autoconf
746 rm_files.append(self.spl_autoconf)
747 else:
748 for f in rm_files:
749 os.remove(f)
750 return (updated, suspicious,
Simon Glassb2e83c62021-12-18 14:54:31 -0700751 color_text(self.args.color, COLOR_BROWN,
Masahiro Yamada07913d12016-08-22 22:18:22 +0900752 "SPL is not enabled. Skipped.") + '\n')
753 else:
754 autoconf_path = self.autoconf
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900755
Simon Glass37f815c2021-12-18 14:54:34 -0700756 dotconfig_lines = read_file(self.dotconfig)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900757
Simon Glass37f815c2021-12-18 14:54:34 -0700758 autoconf_lines = read_file(autoconf_path)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900759
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900760 for config in self.configs:
761 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger96464ba2015-05-19 13:21:17 -0500762 autoconf_lines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900763 results.append(result)
764
765 log = ''
766
767 for (action, value) in results:
768 if action == ACTION_MOVE:
769 actlog = "Move '%s'" % value
770 log_color = COLOR_LIGHT_GREEN
Masahiro Yamadacc008292016-05-19 15:51:56 +0900771 elif action == ACTION_NO_ENTRY:
Simon Glassdaa694d2021-12-18 14:54:30 -0700772 actlog = '%s is not defined in Kconfig. Do nothing.' % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900773 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada916224c2016-08-22 22:18:21 +0900774 elif action == ACTION_NO_ENTRY_WARN:
Simon Glassdaa694d2021-12-18 14:54:30 -0700775 actlog = '%s is not defined in Kconfig (suspicious). Do nothing.' % value
Masahiro Yamada916224c2016-08-22 22:18:21 +0900776 log_color = COLOR_YELLOW
777 suspicious = True
Masahiro Yamadacc008292016-05-19 15:51:56 +0900778 elif action == ACTION_NO_CHANGE:
779 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
780 % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900781 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900782 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700783 sys.exit('Internal Error. This should not happen.')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900784
Simon Glassb2e83c62021-12-18 14:54:31 -0700785 log += color_text(self.args.color, log_color, actlog) + '\n'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900786
Simon Glass91197aa2021-12-18 14:54:35 -0700787 with open(self.dotconfig, 'a', encoding='utf-8') as out:
Masahiro Yamadae423d172016-05-19 15:51:49 +0900788 for (action, value) in results:
789 if action == ACTION_MOVE:
Simon Glass91197aa2021-12-18 14:54:35 -0700790 out.write(value + '\n')
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900791 updated = True
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900792
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900793 self.results = results
Masahiro Yamada07913d12016-08-22 22:18:22 +0900794 for f in rm_files:
795 os.remove(f)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900796
Masahiro Yamada916224c2016-08-22 22:18:21 +0900797 return (updated, suspicious, log)
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900798
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900799 def check_defconfig(self):
800 """Check the defconfig after savedefconfig
801
802 Returns:
803 Return additional log if moved CONFIGs were removed again by
804 'make savedefconfig'.
805 """
806
807 log = ''
808
Simon Glass37f815c2021-12-18 14:54:34 -0700809 defconfig_lines = read_file(self.defconfig)
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900810
811 for (action, value) in self.results:
812 if action != ACTION_MOVE:
813 continue
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300814 if not value in defconfig_lines:
Simon Glassb2e83c62021-12-18 14:54:31 -0700815 log += color_text(self.args.color, COLOR_YELLOW,
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900816 "'%s' was removed by savedefconfig.\n" %
817 value)
818
819 return log
820
Simon Glassd73fcb12017-06-01 19:39:02 -0600821
822class DatabaseThread(threading.Thread):
823 """This thread processes results from Slot threads.
824
825 It collects the data in the master config directary. There is only one
826 result thread, and this helps to serialise the build output.
827 """
828 def __init__(self, config_db, db_queue):
829 """Set up a new result thread
830
831 Args:
832 builder: Builder which will be sent each result
833 """
834 threading.Thread.__init__(self)
835 self.config_db = config_db
836 self.db_queue= db_queue
837
838 def run(self):
839 """Called to start up the result thread.
840
841 We collect the next result job and pass it on to the build.
842 """
843 while True:
844 defconfig, configs = self.db_queue.get()
845 self.config_db[defconfig] = configs
846 self.db_queue.task_done()
847
848
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900849class Slot:
850
851 """A slot to store a subprocess.
852
853 Each instance of this class handles one subprocess.
854 This class is useful to control multiple threads
855 for faster processing.
856 """
857
Simon Glassb2e83c62021-12-18 14:54:31 -0700858 def __init__(self, toolchains, configs, args, progress, devnull,
Simon Glass6821a742017-07-10 14:47:47 -0600859 make_cmd, reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900860 """Create a new process slot.
861
Simon Glass91197aa2021-12-18 14:54:35 -0700862 Args:
Simon Glass6821a742017-07-10 14:47:47 -0600863 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900864 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -0700865 args: Program arguments
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900866 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900867 devnull: A file object of '/dev/null'.
868 make_cmd: command name of GNU Make.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500869 reference_src_dir: Determine the true starting config state from this
870 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -0600871 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900872 """
Simon Glass6821a742017-07-10 14:47:47 -0600873 self.toolchains = toolchains
Simon Glassb2e83c62021-12-18 14:54:31 -0700874 self.args = args
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900875 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900876 self.build_dir = tempfile.mkdtemp()
877 self.devnull = devnull
878 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500879 self.reference_src_dir = reference_src_dir
Simon Glassd73fcb12017-06-01 19:39:02 -0600880 self.db_queue = db_queue
Simon Glassb2e83c62021-12-18 14:54:31 -0700881 self.parser = KconfigParser(configs, args, self.build_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900882 self.state = STATE_IDLE
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900883 self.failed_boards = set()
884 self.suspicious_boards = set()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900885
886 def __del__(self):
887 """Delete the working directory
888
889 This function makes sure the temporary directory is cleaned away
890 even if Python suddenly dies due to error. It should be done in here
Joe Hershbergerf2dae752016-06-10 14:53:29 -0500891 because it is guaranteed the destructor is always invoked when the
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900892 instance of the class gets unreferenced.
893
894 If the subprocess is still running, wait until it finishes.
895 """
896 if self.state != STATE_IDLE:
897 while self.ps.poll() == None:
898 pass
899 shutil.rmtree(self.build_dir)
900
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900901 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900902 """Assign a new subprocess for defconfig and add it to the slot.
903
904 If the slot is vacant, create a new subprocess for processing the
905 given defconfig and add it to the slot. Just returns False if
906 the slot is occupied (i.e. the current subprocess is still running).
907
Simon Glass91197aa2021-12-18 14:54:35 -0700908 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900909 defconfig: defconfig name.
910
911 Returns:
912 Return True on success or False on failure
913 """
914 if self.state != STATE_IDLE:
915 return False
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900916
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900917 self.defconfig = defconfig
Masahiro Yamada1d085562016-05-19 15:52:02 +0900918 self.log = ''
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900919 self.current_src_dir = self.reference_src_dir
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900920 self.do_defconfig()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900921 return True
922
923 def poll(self):
924 """Check the status of the subprocess and handle it as needed.
925
926 Returns True if the slot is vacant (i.e. in idle state).
927 If the configuration is successfully finished, assign a new
928 subprocess to build include/autoconf.mk.
929 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900930 parse the .config and the include/autoconf.mk, moving
931 config options to the .config as needed.
932 If the .config was updated, run "make savedefconfig" to sync
933 it, update the original defconfig, and then set the slot back
934 to the idle state.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900935
936 Returns:
937 Return True if the subprocess is terminated, False otherwise
938 """
939 if self.state == STATE_IDLE:
940 return True
941
942 if self.ps.poll() == None:
943 return False
944
945 if self.ps.poll() != 0:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900946 self.handle_error()
947 elif self.state == STATE_DEFCONFIG:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900948 if self.reference_src_dir and not self.current_src_dir:
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500949 self.do_savedefconfig()
950 else:
951 self.do_autoconf()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900952 elif self.state == STATE_AUTOCONF:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900953 if self.current_src_dir:
954 self.current_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500955 self.do_defconfig()
Simon Glassb2e83c62021-12-18 14:54:31 -0700956 elif self.args.build_db:
Simon Glassd73fcb12017-06-01 19:39:02 -0600957 self.do_build_db()
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500958 else:
959 self.do_savedefconfig()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900960 elif self.state == STATE_SAVEDEFCONFIG:
961 self.update_defconfig()
962 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700963 sys.exit('Internal Error. This should not happen.')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900964
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900965 return True if self.state == STATE_IDLE else False
Joe Hershberger96464ba2015-05-19 13:21:17 -0500966
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900967 def handle_error(self):
968 """Handle error cases."""
Masahiro Yamada8513dc02016-05-19 15:52:08 +0900969
Simon Glassb2e83c62021-12-18 14:54:31 -0700970 self.log += color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glassdaa694d2021-12-18 14:54:30 -0700971 'Failed to process.\n')
Simon Glassb2e83c62021-12-18 14:54:31 -0700972 if self.args.verbose:
973 self.log += color_text(self.args.color, COLOR_LIGHT_CYAN,
Markus Klotzbuecher4f5c5e92020-02-12 20:46:45 +0100974 self.ps.stderr.read().decode())
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900975 self.finish(False)
Joe Hershberger96464ba2015-05-19 13:21:17 -0500976
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900977 def do_defconfig(self):
978 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900979
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900980 cmd = list(self.make_cmd)
981 cmd.append(self.defconfig)
982 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900983 stderr=subprocess.PIPE,
984 cwd=self.current_src_dir)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900985 self.state = STATE_DEFCONFIG
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900986
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900987 def do_autoconf(self):
Simon Glassf3b8e642017-06-01 19:39:01 -0600988 """Run 'make AUTO_CONF_PATH'."""
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900989
Simon Glass6821a742017-07-10 14:47:47 -0600990 arch = self.parser.get_arch()
991 try:
992 toolchain = self.toolchains.Select(arch)
993 except ValueError:
Simon Glassb2e83c62021-12-18 14:54:31 -0700994 self.log += color_text(self.args.color, COLOR_YELLOW,
Chris Packhamce3ba452017-08-27 20:00:51 +1200995 "Tool chain for '%s' is missing. Do nothing.\n" % arch)
Masahiro Yamada4efef992016-05-19 15:52:03 +0900996 self.finish(False)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900997 return
Simon Glass793dca32019-10-31 07:42:57 -0600998 env = toolchain.MakeEnvironment(False)
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900999
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001000 cmd = list(self.make_cmd)
Joe Hershberger7740f652015-05-19 13:21:18 -05001001 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Simon Glassf3b8e642017-06-01 19:39:01 -06001002 cmd.append(AUTO_CONF_PATH)
Simon Glass6821a742017-07-10 14:47:47 -06001003 self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001004 stderr=subprocess.PIPE,
1005 cwd=self.current_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001006 self.state = STATE_AUTOCONF
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001007
Simon Glassd73fcb12017-06-01 19:39:02 -06001008 def do_build_db(self):
1009 """Add the board to the database"""
1010 configs = {}
Simon Glass37f815c2021-12-18 14:54:34 -07001011 for line in read_file(os.path.join(self.build_dir, AUTO_CONF_PATH)):
1012 if line.startswith('CONFIG'):
1013 config, value = line.split('=', 1)
1014 configs[config] = value.rstrip()
Simon Glassd73fcb12017-06-01 19:39:02 -06001015 self.db_queue.put([self.defconfig, configs])
1016 self.finish(True)
1017
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001018 def do_savedefconfig(self):
1019 """Update the .config and run 'make savedefconfig'."""
1020
Masahiro Yamada916224c2016-08-22 22:18:21 +09001021 (updated, suspicious, log) = self.parser.update_dotconfig()
1022 if suspicious:
1023 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001024 self.log += log
1025
Simon Glassb2e83c62021-12-18 14:54:31 -07001026 if not self.args.force_sync and not updated:
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001027 self.finish(True)
1028 return
1029 if updated:
Simon Glassb2e83c62021-12-18 14:54:31 -07001030 self.log += color_text(self.args.color, COLOR_LIGHT_GREEN,
Simon Glassdaa694d2021-12-18 14:54:30 -07001031 'Syncing by savedefconfig...\n')
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001032 else:
Simon Glassdaa694d2021-12-18 14:54:30 -07001033 self.log += 'Syncing by savedefconfig (forced by option)...\n'
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001034
1035 cmd = list(self.make_cmd)
1036 cmd.append('savedefconfig')
1037 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
1038 stderr=subprocess.PIPE)
1039 self.state = STATE_SAVEDEFCONFIG
1040
1041 def update_defconfig(self):
1042 """Update the input defconfig and go back to the idle state."""
1043
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001044 log = self.parser.check_defconfig()
1045 if log:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001046 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001047 self.log += log
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001048 orig_defconfig = os.path.join('configs', self.defconfig)
1049 new_defconfig = os.path.join(self.build_dir, 'defconfig')
1050 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
1051
1052 if updated:
Simon Glassb2e83c62021-12-18 14:54:31 -07001053 self.log += color_text(self.args.color, COLOR_LIGHT_BLUE,
Simon Glassdaa694d2021-12-18 14:54:30 -07001054 'defconfig was updated.\n')
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001055
Simon Glassb2e83c62021-12-18 14:54:31 -07001056 if not self.args.dry_run and updated:
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001057 shutil.move(new_defconfig, orig_defconfig)
1058 self.finish(True)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001059
Masahiro Yamada4efef992016-05-19 15:52:03 +09001060 def finish(self, success):
1061 """Display log along with progress and go to the idle state.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001062
Simon Glass91197aa2021-12-18 14:54:35 -07001063 Args:
Masahiro Yamada4efef992016-05-19 15:52:03 +09001064 success: Should be True when the defconfig was processed
1065 successfully, or False when it fails.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001066 """
1067 # output at least 30 characters to hide the "* defconfigs out of *".
1068 log = self.defconfig.ljust(30) + '\n'
1069
1070 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
1071 # Some threads are running in parallel.
1072 # Print log atomically to not mix up logs from different threads.
Simon Glass793dca32019-10-31 07:42:57 -06001073 print(log, file=(sys.stdout if success else sys.stderr))
Masahiro Yamada4efef992016-05-19 15:52:03 +09001074
1075 if not success:
Simon Glassb2e83c62021-12-18 14:54:31 -07001076 if self.args.exit_on_error:
Simon Glassdaa694d2021-12-18 14:54:30 -07001077 sys.exit('Exit on error.')
Masahiro Yamada4efef992016-05-19 15:52:03 +09001078 # If --exit-on-error flag is not set, skip this board and continue.
1079 # Record the failed board.
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001080 self.failed_boards.add(self.defconfig)
Masahiro Yamada4efef992016-05-19 15:52:03 +09001081
Masahiro Yamada1d085562016-05-19 15:52:02 +09001082 self.progress.inc()
1083 self.progress.show()
Masahiro Yamada4efef992016-05-19 15:52:03 +09001084 self.state = STATE_IDLE
Masahiro Yamada1d085562016-05-19 15:52:02 +09001085
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001086 def get_failed_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001087 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001088 """
1089 return self.failed_boards
1090
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001091 def get_suspicious_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001092 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001093 """
Masahiro Yamada916224c2016-08-22 22:18:21 +09001094 return self.suspicious_boards - self.failed_boards
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001095
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001096class Slots:
1097
1098 """Controller of the array of subprocess slots."""
1099
Simon Glassb2e83c62021-12-18 14:54:31 -07001100 def __init__(self, toolchains, configs, args, progress,
Simon Glass6821a742017-07-10 14:47:47 -06001101 reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001102 """Create a new slots controller.
1103
Simon Glass91197aa2021-12-18 14:54:35 -07001104 Args:
Simon Glass6821a742017-07-10 14:47:47 -06001105 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001106 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -07001107 args: Program arguments
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001108 progress: A progress indicator.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001109 reference_src_dir: Determine the true starting config state from this
1110 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -06001111 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001112 """
Simon Glassb2e83c62021-12-18 14:54:31 -07001113 self.args = args
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001114 self.slots = []
Simon Glass478920d2021-12-18 14:54:32 -07001115 devnull = subprocess.DEVNULL
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001116 make_cmd = get_make_cmd()
Simon Glassb2e83c62021-12-18 14:54:31 -07001117 for i in range(args.jobs):
1118 self.slots.append(Slot(toolchains, configs, args, progress,
Simon Glass6821a742017-07-10 14:47:47 -06001119 devnull, make_cmd, reference_src_dir,
1120 db_queue))
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001121
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001122 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001123 """Add a new subprocess if a vacant slot is found.
1124
Simon Glass91197aa2021-12-18 14:54:35 -07001125 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001126 defconfig: defconfig name to be put into.
1127
1128 Returns:
1129 Return True on success or False on failure
1130 """
1131 for slot in self.slots:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001132 if slot.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001133 return True
1134 return False
1135
1136 def available(self):
1137 """Check if there is a vacant slot.
1138
1139 Returns:
1140 Return True if at lease one vacant slot is found, False otherwise.
1141 """
1142 for slot in self.slots:
1143 if slot.poll():
1144 return True
1145 return False
1146
1147 def empty(self):
1148 """Check if all slots are vacant.
1149
1150 Returns:
1151 Return True if all the slots are vacant, False otherwise.
1152 """
1153 ret = True
1154 for slot in self.slots:
1155 if not slot.poll():
1156 ret = False
1157 return ret
1158
1159 def show_failed_boards(self):
1160 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001161 boards = set()
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001162 output_file = 'moveconfig.failed'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001163
1164 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001165 boards |= slot.get_failed_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001166
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001167 if boards:
1168 boards = '\n'.join(boards) + '\n'
Simon Glassdaa694d2021-12-18 14:54:30 -07001169 msg = 'The following boards were not processed due to error:\n'
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001170 msg += boards
Simon Glassdaa694d2021-12-18 14:54:30 -07001171 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassb2e83c62021-12-18 14:54:31 -07001172 print(color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glass793dca32019-10-31 07:42:57 -06001173 msg), file=sys.stderr)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001174
Simon Glass2fd85bd2021-12-18 14:54:33 -07001175 write_file(output_file, boards)
Joe Hershberger2559cd82015-05-19 13:21:22 -05001176
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001177 def show_suspicious_boards(self):
1178 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001179 boards = set()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001180 output_file = 'moveconfig.suspicious'
1181
1182 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001183 boards |= slot.get_suspicious_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001184
1185 if boards:
1186 boards = '\n'.join(boards) + '\n'
Simon Glassdaa694d2021-12-18 14:54:30 -07001187 msg = 'The following boards might have been converted incorrectly.\n'
1188 msg += 'It is highly recommended to check them manually:\n'
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001189 msg += boards
Simon Glassdaa694d2021-12-18 14:54:30 -07001190 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassb2e83c62021-12-18 14:54:31 -07001191 print(color_text(self.args.color, COLOR_YELLOW,
Simon Glass793dca32019-10-31 07:42:57 -06001192 msg), file=sys.stderr)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001193
Simon Glass2fd85bd2021-12-18 14:54:33 -07001194 write_file(output_file, boards)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001195
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001196class ReferenceSource:
1197
1198 """Reference source against which original configs should be parsed."""
1199
1200 def __init__(self, commit):
1201 """Create a reference source directory based on a specified commit.
1202
Simon Glass91197aa2021-12-18 14:54:35 -07001203 Args:
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001204 commit: commit to git-clone
1205 """
1206 self.src_dir = tempfile.mkdtemp()
Simon Glassdaa694d2021-12-18 14:54:30 -07001207 print('Cloning git repo to a separate work directory...')
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001208 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1209 cwd=self.src_dir)
Simon Glass793dca32019-10-31 07:42:57 -06001210 print("Checkout '%s' to build the original autoconf.mk." % \
1211 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip())
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001212 subprocess.check_output(['git', 'checkout', commit],
1213 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001214
1215 def __del__(self):
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001216 """Delete the reference source directory
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001217
1218 This function makes sure the temporary directory is cleaned away
1219 even if Python suddenly dies due to error. It should be done in here
1220 because it is guaranteed the destructor is always invoked when the
1221 instance of the class gets unreferenced.
1222 """
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001223 shutil.rmtree(self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001224
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001225 def get_dir(self):
1226 """Return the absolute path to the reference source directory."""
1227
1228 return self.src_dir
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001229
Simon Glassb2e83c62021-12-18 14:54:31 -07001230def move_config(toolchains, configs, args, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001231 """Move config options to defconfig files.
1232
Simon Glass91197aa2021-12-18 14:54:35 -07001233 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001234 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -07001235 args: Program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001236 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001237 if len(configs) == 0:
Simon Glassb2e83c62021-12-18 14:54:31 -07001238 if args.force_sync:
Simon Glass793dca32019-10-31 07:42:57 -06001239 print('No CONFIG is specified. You are probably syncing defconfigs.', end=' ')
Simon Glassb2e83c62021-12-18 14:54:31 -07001240 elif args.build_db:
Simon Glass793dca32019-10-31 07:42:57 -06001241 print('Building %s database' % CONFIG_DATABASE)
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001242 else:
Simon Glass793dca32019-10-31 07:42:57 -06001243 print('Neither CONFIG nor --force-sync is specified. Nothing will happen.', end=' ')
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001244 else:
Simon Glass793dca32019-10-31 07:42:57 -06001245 print('Move ' + ', '.join(configs), end=' ')
Simon Glassb2e83c62021-12-18 14:54:31 -07001246 print('(jobs: %d)\n' % args.jobs)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001247
Simon Glassb2e83c62021-12-18 14:54:31 -07001248 if args.git_ref:
1249 reference_src = ReferenceSource(args.git_ref)
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001250 reference_src_dir = reference_src.get_dir()
1251 else:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001252 reference_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001253
Simon Glassb2e83c62021-12-18 14:54:31 -07001254 if args.defconfigs:
1255 defconfigs = get_matched_defconfigs(args.defconfigs)
Joe Hershberger91040e82015-05-19 13:21:19 -05001256 else:
Masahiro Yamada684c3062016-07-25 19:15:28 +09001257 defconfigs = get_all_defconfigs()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001258
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001259 progress = Progress(len(defconfigs))
Simon Glassb2e83c62021-12-18 14:54:31 -07001260 slots = Slots(toolchains, configs, args, progress, reference_src_dir,
Simon Glass91197aa2021-12-18 14:54:35 -07001261 db_queue)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001262
1263 # Main loop to process defconfig files:
1264 # Add a new subprocess into a vacant slot.
1265 # Sleep if there is no available slot.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001266 for defconfig in defconfigs:
1267 while not slots.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001268 while not slots.available():
1269 # No available slot: sleep for a while
1270 time.sleep(SLEEP_TIME)
1271
1272 # wait until all the subprocesses finish
1273 while not slots.empty():
1274 time.sleep(SLEEP_TIME)
1275
Simon Glass793dca32019-10-31 07:42:57 -06001276 print('')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001277 slots.show_failed_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001278 slots.show_suspicious_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001279
Simon Glasscb008832017-06-15 21:39:33 -06001280def find_kconfig_rules(kconf, config, imply_config):
1281 """Check whether a config has a 'select' or 'imply' keyword
1282
1283 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -04001284 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -06001285 config: Name of config to check (without CONFIG_ prefix)
1286 imply_config: Implying config (without CONFIG_ prefix) which may or
1287 may not have an 'imply' for 'config')
1288
1289 Returns:
1290 Symbol object for 'config' if found, else None
1291 """
Tom Rini65e05dd2019-09-20 17:42:09 -04001292 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -06001293 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001294 for sel, cond in (sym.selects + sym.implies):
Simon Glassa3627082021-12-18 08:09:42 -07001295 if sel.name == config:
Simon Glasscb008832017-06-15 21:39:33 -06001296 return sym
1297 return None
1298
1299def check_imply_rule(kconf, config, imply_config):
1300 """Check if we can add an 'imply' option
1301
1302 This finds imply_config in the Kconfig and looks to see if it is possible
1303 to add an 'imply' for 'config' to that part of the Kconfig.
1304
1305 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -04001306 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -06001307 config: Name of config to check (without CONFIG_ prefix)
1308 imply_config: Implying config (without CONFIG_ prefix) which may or
1309 may not have an 'imply' for 'config')
1310
1311 Returns:
1312 tuple:
1313 filename of Kconfig file containing imply_config, or None if none
1314 line number within the Kconfig file, or 0 if none
1315 message indicating the result
1316 """
Tom Rini65e05dd2019-09-20 17:42:09 -04001317 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -06001318 if not sym:
1319 return 'cannot find sym'
Simon Glassea40b202021-07-21 21:35:53 -06001320 nodes = sym.nodes
1321 if len(nodes) != 1:
1322 return '%d locations' % len(nodes)
Simon Glassa3627082021-12-18 08:09:42 -07001323 node = nodes[0]
1324 fname, linenum = node.filename, node.linenr
Simon Glasscb008832017-06-15 21:39:33 -06001325 cwd = os.getcwd()
1326 if cwd and fname.startswith(cwd):
1327 fname = fname[len(cwd) + 1:]
1328 file_line = ' at %s:%d' % (fname, linenum)
Simon Glass37f815c2021-12-18 14:54:34 -07001329 data = read_file(fname)
Simon Glasscb008832017-06-15 21:39:33 -06001330 if data[linenum - 1] != 'config %s' % imply_config:
1331 return None, 0, 'bad sym format %s%s' % (data[linenum], file_line)
1332 return fname, linenum, 'adding%s' % file_line
1333
1334def add_imply_rule(config, fname, linenum):
1335 """Add a new 'imply' option to a Kconfig
1336
1337 Args:
1338 config: config option to add an imply for (without CONFIG_ prefix)
1339 fname: Kconfig filename to update
1340 linenum: Line number to place the 'imply' before
1341
1342 Returns:
1343 Message indicating the result
1344 """
1345 file_line = ' at %s:%d' % (fname, linenum)
Simon Glass37f815c2021-12-18 14:54:34 -07001346 data = read_file(fname)
Simon Glasscb008832017-06-15 21:39:33 -06001347 linenum -= 1
1348
1349 for offset, line in enumerate(data[linenum:]):
1350 if line.strip().startswith('help') or not line:
1351 data.insert(linenum + offset, '\timply %s' % config)
Simon Glass2fd85bd2021-12-18 14:54:33 -07001352 write_file(fname, data)
Simon Glasscb008832017-06-15 21:39:33 -06001353 return 'added%s' % file_line
1354
1355 return 'could not insert%s'
1356
1357(IMPLY_MIN_2, IMPLY_TARGET, IMPLY_CMD, IMPLY_NON_ARCH_BOARD) = (
1358 1, 2, 4, 8)
Simon Glass9b2a2e82017-06-15 21:39:32 -06001359
1360IMPLY_FLAGS = {
1361 'min2': [IMPLY_MIN_2, 'Show options which imply >2 boards (normally >5)'],
1362 'target': [IMPLY_TARGET, 'Allow CONFIG_TARGET_... options to imply'],
1363 'cmd': [IMPLY_CMD, 'Allow CONFIG_CMD_... to imply'],
Simon Glasscb008832017-06-15 21:39:33 -06001364 'non-arch-board': [
1365 IMPLY_NON_ARCH_BOARD,
1366 'Allow Kconfig options outside arch/ and /board/ to imply'],
Simon Glass91197aa2021-12-18 14:54:35 -07001367}
Simon Glass9b2a2e82017-06-15 21:39:32 -06001368
Simon Glass9d603392021-12-18 08:09:43 -07001369
1370def read_database():
1371 """Read in the config database
1372
1373 Returns:
1374 tuple:
1375 set of all config options seen (each a str)
1376 set of all defconfigs seen (each a str)
1377 dict of configs for each defconfig:
1378 key: defconfig name, e.g. "MPC8548CDS_legacy_defconfig"
1379 value: dict:
1380 key: CONFIG option
1381 value: Value of option
1382 dict of defconfigs for each config:
1383 key: CONFIG option
1384 value: set of boards using that option
1385
1386 """
1387 configs = {}
1388
1389 # key is defconfig name, value is dict of (CONFIG_xxx, value)
1390 config_db = {}
1391
1392 # Set of all config options we have seen
1393 all_configs = set()
1394
1395 # Set of all defconfigs we have seen
1396 all_defconfigs = set()
1397
1398 defconfig_db = collections.defaultdict(set)
Simon Glass37f815c2021-12-18 14:54:34 -07001399 for line in read_file(CONFIG_DATABASE):
1400 line = line.rstrip()
1401 if not line: # Separator between defconfigs
1402 config_db[defconfig] = configs
1403 all_defconfigs.add(defconfig)
1404 configs = {}
1405 elif line[0] == ' ': # CONFIG line
1406 config, value = line.strip().split('=', 1)
1407 configs[config] = value
1408 defconfig_db[config].add(defconfig)
1409 all_configs.add(config)
1410 else: # New defconfig
1411 defconfig = line
Simon Glass9d603392021-12-18 08:09:43 -07001412
1413 return all_configs, all_defconfigs, config_db, defconfig_db
1414
1415
Simon Glasscb008832017-06-15 21:39:33 -06001416def do_imply_config(config_list, add_imply, imply_flags, skip_added,
1417 check_kconfig=True, find_superset=False):
Simon Glass99b66602017-06-01 19:39:03 -06001418 """Find CONFIG options which imply those in the list
1419
1420 Some CONFIG options can be implied by others and this can help to reduce
1421 the size of the defconfig files. For example, CONFIG_X86 implies
1422 CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
1423 all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
1424 each of the x86 defconfig files.
1425
1426 This function uses the moveconfig database to find such options. It
1427 displays a list of things that could possibly imply those in the list.
1428 The algorithm ignores any that start with CONFIG_TARGET since these
1429 typically refer to only a few defconfigs (often one). It also does not
1430 display a config with less than 5 defconfigs.
1431
1432 The algorithm works using sets. For each target config in config_list:
1433 - Get the set 'defconfigs' which use that target config
1434 - For each config (from a list of all configs):
1435 - Get the set 'imply_defconfig' of defconfigs which use that config
1436 -
1437 - If imply_defconfigs contains anything not in defconfigs then
1438 this config does not imply the target config
1439
1440 Params:
1441 config_list: List of CONFIG options to check (each a string)
Simon Glasscb008832017-06-15 21:39:33 -06001442 add_imply: Automatically add an 'imply' for each config.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001443 imply_flags: Flags which control which implying configs are allowed
1444 (IMPLY_...)
Simon Glasscb008832017-06-15 21:39:33 -06001445 skip_added: Don't show options which already have an imply added.
1446 check_kconfig: Check if implied symbols already have an 'imply' or
1447 'select' for the target config, and show this information if so.
Simon Glass99b66602017-06-01 19:39:03 -06001448 find_superset: True to look for configs which are a superset of those
1449 already found. So for example if CONFIG_EXYNOS5 implies an option,
1450 but CONFIG_EXYNOS covers a larger set of defconfigs and also
1451 implies that option, this will drop the former in favour of the
1452 latter. In practice this option has not proved very used.
1453
1454 Note the terminoloy:
1455 config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
1456 defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
1457 """
Simon Glasscb008832017-06-15 21:39:33 -06001458 kconf = KconfigScanner().conf if check_kconfig else None
1459 if add_imply and add_imply != 'all':
Simon Glassa3627082021-12-18 08:09:42 -07001460 add_imply = add_imply.split(',')
Simon Glasscb008832017-06-15 21:39:33 -06001461
Simon Glass9d603392021-12-18 08:09:43 -07001462 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
Simon Glass99b66602017-06-01 19:39:03 -06001463
Simon Glassa3627082021-12-18 08:09:42 -07001464 # Work through each target config option in turn, independently
Simon Glass99b66602017-06-01 19:39:03 -06001465 for config in config_list:
1466 defconfigs = defconfig_db.get(config)
1467 if not defconfigs:
Simon Glass793dca32019-10-31 07:42:57 -06001468 print('%s not found in any defconfig' % config)
Simon Glass99b66602017-06-01 19:39:03 -06001469 continue
1470
1471 # Get the set of defconfigs without this one (since a config cannot
1472 # imply itself)
1473 non_defconfigs = all_defconfigs - defconfigs
1474 num_defconfigs = len(defconfigs)
Simon Glass793dca32019-10-31 07:42:57 -06001475 print('%s found in %d/%d defconfigs' % (config, num_defconfigs,
1476 len(all_configs)))
Simon Glass99b66602017-06-01 19:39:03 -06001477
1478 # This will hold the results: key=config, value=defconfigs containing it
1479 imply_configs = {}
1480 rest_configs = all_configs - set([config])
1481
1482 # Look at every possible config, except the target one
1483 for imply_config in rest_configs:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001484 if 'ERRATUM' in imply_config:
Simon Glass99b66602017-06-01 19:39:03 -06001485 continue
Simon Glass91197aa2021-12-18 14:54:35 -07001486 if not imply_flags & IMPLY_CMD:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001487 if 'CONFIG_CMD' in imply_config:
1488 continue
Simon Glass91197aa2021-12-18 14:54:35 -07001489 if not imply_flags & IMPLY_TARGET:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001490 if 'CONFIG_TARGET' in imply_config:
1491 continue
Simon Glass99b66602017-06-01 19:39:03 -06001492
1493 # Find set of defconfigs that have this config
1494 imply_defconfig = defconfig_db[imply_config]
1495
1496 # Get the intersection of this with defconfigs containing the
1497 # target config
1498 common_defconfigs = imply_defconfig & defconfigs
1499
1500 # Get the set of defconfigs containing this config which DO NOT
1501 # also contain the taret config. If this set is non-empty it means
1502 # that this config affects other defconfigs as well as (possibly)
1503 # the ones affected by the target config. This means it implies
1504 # things we don't want to imply.
1505 not_common_defconfigs = imply_defconfig & non_defconfigs
1506 if not_common_defconfigs:
1507 continue
1508
1509 # If there are common defconfigs, imply_config may be useful
1510 if common_defconfigs:
1511 skip = False
1512 if find_superset:
Simon Glass793dca32019-10-31 07:42:57 -06001513 for prev in list(imply_configs.keys()):
Simon Glass99b66602017-06-01 19:39:03 -06001514 prev_count = len(imply_configs[prev])
1515 count = len(common_defconfigs)
1516 if (prev_count > count and
1517 (imply_configs[prev] & common_defconfigs ==
1518 common_defconfigs)):
1519 # skip imply_config because prev is a superset
1520 skip = True
1521 break
1522 elif count > prev_count:
1523 # delete prev because imply_config is a superset
1524 del imply_configs[prev]
1525 if not skip:
1526 imply_configs[imply_config] = common_defconfigs
1527
1528 # Now we have a dict imply_configs of configs which imply each config
1529 # The value of each dict item is the set of defconfigs containing that
1530 # config. Rank them so that we print the configs that imply the largest
1531 # number of defconfigs first.
Simon Glasscb008832017-06-15 21:39:33 -06001532 ranked_iconfigs = sorted(imply_configs,
Simon Glass99b66602017-06-01 19:39:03 -06001533 key=lambda k: len(imply_configs[k]), reverse=True)
Simon Glasscb008832017-06-15 21:39:33 -06001534 kconfig_info = ''
1535 cwd = os.getcwd()
1536 add_list = collections.defaultdict(list)
1537 for iconfig in ranked_iconfigs:
1538 num_common = len(imply_configs[iconfig])
Simon Glass99b66602017-06-01 19:39:03 -06001539
1540 # Don't bother if there are less than 5 defconfigs affected.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001541 if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
Simon Glass99b66602017-06-01 19:39:03 -06001542 continue
Simon Glasscb008832017-06-15 21:39:33 -06001543 missing = defconfigs - imply_configs[iconfig]
Simon Glass99b66602017-06-01 19:39:03 -06001544 missing_str = ', '.join(missing) if missing else 'all'
1545 missing_str = ''
Simon Glasscb008832017-06-15 21:39:33 -06001546 show = True
1547 if kconf:
1548 sym = find_kconfig_rules(kconf, config[CONFIG_LEN:],
1549 iconfig[CONFIG_LEN:])
1550 kconfig_info = ''
1551 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001552 nodes = sym.nodes
1553 if len(nodes) == 1:
1554 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001555 if cwd and fname.startswith(cwd):
1556 fname = fname[len(cwd) + 1:]
1557 kconfig_info = '%s:%d' % (fname, linenum)
1558 if skip_added:
1559 show = False
1560 else:
Tom Rini65e05dd2019-09-20 17:42:09 -04001561 sym = kconf.syms.get(iconfig[CONFIG_LEN:])
Simon Glasscb008832017-06-15 21:39:33 -06001562 fname = ''
1563 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001564 nodes = sym.nodes
1565 if len(nodes) == 1:
1566 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001567 if cwd and fname.startswith(cwd):
1568 fname = fname[len(cwd) + 1:]
1569 in_arch_board = not sym or (fname.startswith('arch') or
1570 fname.startswith('board'))
1571 if (not in_arch_board and
Simon Glass91197aa2021-12-18 14:54:35 -07001572 not imply_flags & IMPLY_NON_ARCH_BOARD):
Simon Glasscb008832017-06-15 21:39:33 -06001573 continue
1574
1575 if add_imply and (add_imply == 'all' or
1576 iconfig in add_imply):
1577 fname, linenum, kconfig_info = (check_imply_rule(kconf,
1578 config[CONFIG_LEN:], iconfig[CONFIG_LEN:]))
1579 if fname:
1580 add_list[fname].append(linenum)
1581
1582 if show and kconfig_info != 'skip':
Simon Glass793dca32019-10-31 07:42:57 -06001583 print('%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
1584 kconfig_info, missing_str))
Simon Glasscb008832017-06-15 21:39:33 -06001585
1586 # Having collected a list of things to add, now we add them. We process
1587 # each file from the largest line number to the smallest so that
1588 # earlier additions do not affect our line numbers. E.g. if we added an
1589 # imply at line 20 it would change the position of each line after
1590 # that.
Simon Glass793dca32019-10-31 07:42:57 -06001591 for fname, linenums in add_list.items():
Simon Glasscb008832017-06-15 21:39:33 -06001592 for linenum in sorted(linenums, reverse=True):
1593 add_imply_rule(config[CONFIG_LEN:], fname, linenum)
Simon Glass99b66602017-06-01 19:39:03 -06001594
Simon Glass941671a2022-02-08 11:49:46 -07001595def defconfig_matches(configs, re_match):
1596 """Check if any CONFIG option matches a regex
1597
1598 The match must be complete, i.e. from the start to end of the CONFIG option.
1599
1600 Args:
1601 configs (dict): Dict of CONFIG options:
1602 key: CONFIG option
1603 value: Value of option
1604 re_match (re.Pattern): Match to check
1605
1606 Returns:
1607 bool: True if any CONFIG matches the regex
1608 """
1609 for cfg in configs:
Simon Glassd9c958f2022-03-05 20:18:54 -07001610 if re_match.fullmatch(cfg):
Simon Glass941671a2022-02-08 11:49:46 -07001611 return True
1612 return False
Simon Glass99b66602017-06-01 19:39:03 -06001613
Simon Glass65d7fce2021-12-18 08:09:46 -07001614def do_find_config(config_list):
1615 """Find boards with a given combination of CONFIGs
1616
1617 Params:
Simon Glass941671a2022-02-08 11:49:46 -07001618 config_list: List of CONFIG options to check (each a regex consisting
Simon Glass65d7fce2021-12-18 08:09:46 -07001619 of a config option, with or without a CONFIG_ prefix. If an option
1620 is preceded by a tilde (~) then it must be false, otherwise it must
1621 be true)
1622 """
1623 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
1624
1625 # Get the whitelist
Simon Glass37f815c2021-12-18 14:54:34 -07001626 adhoc_configs = set(read_file('scripts/config_whitelist.txt'))
Simon Glass65d7fce2021-12-18 08:09:46 -07001627
1628 # Start with all defconfigs
1629 out = all_defconfigs
1630
1631 # Work through each config in turn
1632 adhoc = []
1633 for item in config_list:
1634 # Get the real config name and whether we want this config or not
1635 cfg = item
1636 want = True
1637 if cfg[0] == '~':
1638 want = False
1639 cfg = cfg[1:]
1640
1641 if cfg in adhoc_configs:
1642 adhoc.append(cfg)
1643 continue
1644
1645 # Search everything that is still in the running. If it has a config
1646 # that we want, or doesn't have one that we don't, add it into the
1647 # running for the next stage
1648 in_list = out
1649 out = set()
Simon Glass941671a2022-02-08 11:49:46 -07001650 re_match = re.compile(cfg)
Simon Glass65d7fce2021-12-18 08:09:46 -07001651 for defc in in_list:
Simon Glass941671a2022-02-08 11:49:46 -07001652 has_cfg = defconfig_matches(config_db[defc], re_match)
Simon Glass65d7fce2021-12-18 08:09:46 -07001653 if has_cfg == want:
1654 out.add(defc)
1655 if adhoc:
1656 print(f"Error: Not in Kconfig: %s" % ' '.join(adhoc))
1657 else:
1658 print(f'{len(out)} matches')
Simon Glass78f12e52022-03-05 20:18:53 -07001659 print(' '.join(item.split('_defconfig')[0] for item in out))
Simon Glass65d7fce2021-12-18 08:09:46 -07001660
1661
1662def prefix_config(cfg):
1663 """Prefix a config with CONFIG_ if needed
1664
1665 This handles ~ operator, which indicates that the CONFIG should be disabled
1666
1667 >>> prefix_config('FRED')
1668 'CONFIG_FRED'
1669 >>> prefix_config('CONFIG_FRED')
1670 'CONFIG_FRED'
1671 >>> prefix_config('~FRED')
1672 '~CONFIG_FRED'
1673 >>> prefix_config('~CONFIG_FRED')
1674 '~CONFIG_FRED'
1675 >>> prefix_config('A123')
1676 'CONFIG_A123'
1677 """
1678 op = ''
1679 if cfg[0] == '~':
1680 op = cfg[0]
1681 cfg = cfg[1:]
1682 if not cfg.startswith('CONFIG_'):
1683 cfg = 'CONFIG_' + cfg
1684 return op + cfg
1685
1686
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001687def main():
1688 try:
1689 cpu_count = multiprocessing.cpu_count()
1690 except NotImplementedError:
1691 cpu_count = 1
1692
Simon Glassb2e83c62021-12-18 14:54:31 -07001693 epilog = '''Move config options from headers to defconfig files. See
1694doc/develop/moveconfig.rst for documentation.'''
1695
1696 parser = ArgumentParser(epilog=epilog)
1697 # Add arguments here
1698 parser.add_argument('-a', '--add-imply', type=str, default='',
Simon Glasscb008832017-06-15 21:39:33 -06001699 help='comma-separated list of CONFIG options to add '
1700 "an 'imply' statement to for the CONFIG in -i")
Simon Glassb2e83c62021-12-18 14:54:31 -07001701 parser.add_argument('-A', '--skip-added', action='store_true', default=False,
Simon Glasscb008832017-06-15 21:39:33 -06001702 help="don't show options which are already marked as "
1703 'implying others')
Simon Glassb2e83c62021-12-18 14:54:31 -07001704 parser.add_argument('-b', '--build-db', action='store_true', default=False,
Simon Glassd73fcb12017-06-01 19:39:02 -06001705 help='build a CONFIG database')
Simon Glassb2e83c62021-12-18 14:54:31 -07001706 parser.add_argument('-c', '--color', action='store_true', default=False,
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001707 help='display the log in color')
Simon Glassb2e83c62021-12-18 14:54:31 -07001708 parser.add_argument('-C', '--commit', action='store_true', default=False,
Simon Glass9ede2122016-09-12 23:18:21 -06001709 help='Create a git commit for the operation')
Simon Glassb2e83c62021-12-18 14:54:31 -07001710 parser.add_argument('-d', '--defconfigs', type=str,
Simon Glassee4e61b2017-06-01 19:38:59 -06001711 help='a file containing a list of defconfigs to move, '
1712 "one per line (for example 'snow_defconfig') "
1713 "or '-' to read from stdin")
Simon Glassb2e83c62021-12-18 14:54:31 -07001714 parser.add_argument('-e', '--exit-on-error', action='store_true',
Simon Glasse1ae5632021-12-18 08:09:44 -07001715 default=False,
1716 help='exit immediately on any error')
Simon Glassb2e83c62021-12-18 14:54:31 -07001717 parser.add_argument('-f', '--find', action='store_true', default=False,
Simon Glass65d7fce2021-12-18 08:09:46 -07001718 help='Find boards with a given config combination')
Simon Glassb2e83c62021-12-18 14:54:31 -07001719 parser.add_argument('-H', '--headers-only', dest='cleanup_headers_only',
Simon Glasse1ae5632021-12-18 08:09:44 -07001720 action='store_true', default=False,
1721 help='only cleanup the headers')
Simon Glassb2e83c62021-12-18 14:54:31 -07001722 parser.add_argument('-i', '--imply', action='store_true', default=False,
Simon Glass99b66602017-06-01 19:39:03 -06001723 help='find options which imply others')
Simon Glassb2e83c62021-12-18 14:54:31 -07001724 parser.add_argument('-I', '--imply-flags', type=str, default='',
Simon Glass9b2a2e82017-06-15 21:39:32 -06001725 help="control the -i option ('help' for help")
Simon Glassb2e83c62021-12-18 14:54:31 -07001726 parser.add_argument('-j', '--jobs', type=int, default=cpu_count,
Simon Glasse1ae5632021-12-18 08:09:44 -07001727 help='the number of jobs to run simultaneously')
Simon Glassb2e83c62021-12-18 14:54:31 -07001728 parser.add_argument('-n', '--dry-run', action='store_true', default=False,
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001729 help='perform a trial run (show log with no changes)')
Simon Glassb2e83c62021-12-18 14:54:31 -07001730 parser.add_argument('-r', '--git-ref', type=str,
Simon Glasse1ae5632021-12-18 08:09:44 -07001731 help='the git ref to clone for building the autoconf.mk')
Simon Glassb2e83c62021-12-18 14:54:31 -07001732 parser.add_argument('-s', '--force-sync', action='store_true', default=False,
Masahiro Yamada8513dc02016-05-19 15:52:08 +09001733 help='force sync by savedefconfig')
Simon Glassb2e83c62021-12-18 14:54:31 -07001734 parser.add_argument('-S', '--spl', action='store_true', default=False,
Masahiro Yamada07913d12016-08-22 22:18:22 +09001735 help='parse config options defined for SPL build')
Simon Glassb2e83c62021-12-18 14:54:31 -07001736 parser.add_argument('-t', '--test', action='store_true', default=False,
Simon Glasse1ae5632021-12-18 08:09:44 -07001737 help='run unit tests')
Simon Glassb2e83c62021-12-18 14:54:31 -07001738 parser.add_argument('-y', '--yes', action='store_true', default=False,
Simon Glass6b403df2016-09-12 23:18:20 -06001739 help="respond 'yes' to any prompts")
Simon Glassb2e83c62021-12-18 14:54:31 -07001740 parser.add_argument('-v', '--verbose', action='store_true', default=False,
Joe Hershberger95bf9c72015-05-19 13:21:24 -05001741 help='show any build errors as boards are built')
Simon Glassb2e83c62021-12-18 14:54:31 -07001742 parser.add_argument('configs', nargs='*')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001743
Simon Glassb2e83c62021-12-18 14:54:31 -07001744 args = parser.parse_args()
1745 configs = args.configs
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001746
Simon Glassb2e83c62021-12-18 14:54:31 -07001747 if args.test:
Simon Glass84067a52021-12-18 08:09:45 -07001748 sys.argv = [sys.argv[0]]
1749 fail, count = doctest.testmod()
1750 if fail:
1751 return 1
1752 unittest.main()
1753
Simon Glassb2e83c62021-12-18 14:54:31 -07001754 if not any((len(configs), args.force_sync, args.build_db, args.imply,
1755 args.find)):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001756 parser.print_usage()
1757 sys.exit(1)
1758
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001759 # prefix the option name with CONFIG_ if missing
Simon Glass65d7fce2021-12-18 08:09:46 -07001760 configs = [prefix_config(cfg) for cfg in configs]
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001761
Joe Hershberger2144f882015-05-19 13:21:20 -05001762 check_top_directory()
1763
Simon Glassb2e83c62021-12-18 14:54:31 -07001764 if args.imply:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001765 imply_flags = 0
Simon Glassb2e83c62021-12-18 14:54:31 -07001766 if args.imply_flags == 'all':
Simon Glassdee36c72017-07-10 14:47:46 -06001767 imply_flags = -1
1768
Simon Glassb2e83c62021-12-18 14:54:31 -07001769 elif args.imply_flags:
1770 for flag in args.imply_flags.split(','):
Simon Glassdee36c72017-07-10 14:47:46 -06001771 bad = flag not in IMPLY_FLAGS
1772 if bad:
Simon Glass793dca32019-10-31 07:42:57 -06001773 print("Invalid flag '%s'" % flag)
Simon Glassdee36c72017-07-10 14:47:46 -06001774 if flag == 'help' or bad:
Simon Glass793dca32019-10-31 07:42:57 -06001775 print("Imply flags: (separate with ',')")
1776 for name, info in IMPLY_FLAGS.items():
1777 print(' %-15s: %s' % (name, info[1]))
Simon Glassdee36c72017-07-10 14:47:46 -06001778 parser.print_usage()
1779 sys.exit(1)
1780 imply_flags |= IMPLY_FLAGS[flag][0]
Simon Glass9b2a2e82017-06-15 21:39:32 -06001781
Simon Glassb2e83c62021-12-18 14:54:31 -07001782 do_imply_config(configs, args.add_imply, imply_flags, args.skip_added)
Simon Glass99b66602017-06-01 19:39:03 -06001783 return
1784
Simon Glassb2e83c62021-12-18 14:54:31 -07001785 if args.find:
Simon Glass65d7fce2021-12-18 08:09:46 -07001786 do_find_config(configs)
1787 return
1788
Simon Glassd73fcb12017-06-01 19:39:02 -06001789 config_db = {}
Simon Glass793dca32019-10-31 07:42:57 -06001790 db_queue = queue.Queue()
Simon Glassd73fcb12017-06-01 19:39:02 -06001791 t = DatabaseThread(config_db, db_queue)
1792 t.setDaemon(True)
1793 t.start()
1794
Simon Glassb2e83c62021-12-18 14:54:31 -07001795 if not args.cleanup_headers_only:
Masahiro Yamadaf7536f72016-07-25 19:15:23 +09001796 check_clean_directory()
Simon Glass793dca32019-10-31 07:42:57 -06001797 bsettings.Setup('')
Simon Glass6821a742017-07-10 14:47:47 -06001798 toolchains = toolchain.Toolchains()
1799 toolchains.GetSettings()
1800 toolchains.Scan(verbose=False)
Simon Glassb2e83c62021-12-18 14:54:31 -07001801 move_config(toolchains, configs, args, db_queue)
Simon Glassd73fcb12017-06-01 19:39:02 -06001802 db_queue.join()
Joe Hershberger2144f882015-05-19 13:21:20 -05001803
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001804 if configs:
Simon Glassb2e83c62021-12-18 14:54:31 -07001805 cleanup_headers(configs, args)
Tom Rini25b8ace2022-04-02 18:18:57 -04001806 cleanup_extra_options(configs, args)
Simon Glassb2e83c62021-12-18 14:54:31 -07001807 cleanup_whitelist(configs, args)
1808 cleanup_readme(configs, args)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001809
Simon Glassb2e83c62021-12-18 14:54:31 -07001810 if args.commit:
Simon Glass9ede2122016-09-12 23:18:21 -06001811 subprocess.call(['git', 'add', '-u'])
1812 if configs:
1813 msg = 'Convert %s %sto Kconfig' % (configs[0],
1814 'et al ' if len(configs) > 1 else '')
1815 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1816 '\n '.join(configs))
1817 else:
1818 msg = 'configs: Resync with savedefconfig'
1819 msg += '\n\nRsync all defconfig files using moveconfig.py'
1820 subprocess.call(['git', 'commit', '-s', '-m', msg])
1821
Simon Glassb2e83c62021-12-18 14:54:31 -07001822 if args.build_db:
Simon Glass91197aa2021-12-18 14:54:35 -07001823 with open(CONFIG_DATABASE, 'w', encoding='utf-8') as fd:
Simon Glass793dca32019-10-31 07:42:57 -06001824 for defconfig, configs in config_db.items():
Simon Glassc79d18c2017-08-13 16:02:54 -06001825 fd.write('%s\n' % defconfig)
Simon Glassd73fcb12017-06-01 19:39:02 -06001826 for config in sorted(configs.keys()):
Simon Glassc79d18c2017-08-13 16:02:54 -06001827 fd.write(' %s=%s\n' % (config, configs[config]))
1828 fd.write('\n')
Simon Glassd73fcb12017-06-01 19:39:02 -06001829
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001830if __name__ == '__main__':
Simon Glass65d7fce2021-12-18 08:09:46 -07001831 sys.exit(main())