blob: 09617a07f91bc4e8bcec7b6d99e5601cf8104f30 [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
Simon Glassb2e83c62021-12-18 14:54:31 -0700446def cleanup_whitelist(configs, args):
Chris Packhamca438342017-05-02 21:30:47 +1200447 """Delete config whitelist entries
448
Simon Glass91197aa2021-12-18 14:54:35 -0700449 Args:
Chris Packhamca438342017-05-02 21:30:47 +1200450 configs: A list of CONFIGs to remove.
Simon Glass91197aa2021-12-18 14:54:35 -0700451 args (Namespace): program arguments
Chris Packhamca438342017-05-02 21:30:47 +1200452 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700453 if not confirm(args, 'Clean up whitelist entries?'):
Chris Packhamca438342017-05-02 21:30:47 +1200454 return
455
Simon Glass37f815c2021-12-18 14:54:34 -0700456 lines = read_file(os.path.join('scripts', 'config_whitelist.txt'))
Chris Packhamca438342017-05-02 21:30:47 +1200457
458 lines = [x for x in lines if x.strip() not in configs]
459
Simon Glass2fd85bd2021-12-18 14:54:33 -0700460 write_file(os.path.join('scripts', 'config_whitelist.txt'), lines)
Chris Packhamca438342017-05-02 21:30:47 +1200461
Chris Packhamf90df592017-05-02 21:30:48 +1200462def find_matching(patterns, line):
463 for pat in patterns:
464 if pat.search(line):
465 return True
466 return False
467
Simon Glassb2e83c62021-12-18 14:54:31 -0700468def cleanup_readme(configs, args):
Chris Packhamf90df592017-05-02 21:30:48 +1200469 """Delete config description in README
470
Simon Glass91197aa2021-12-18 14:54:35 -0700471 Args:
Chris Packhamf90df592017-05-02 21:30:48 +1200472 configs: A list of CONFIGs to remove.
Simon Glass91197aa2021-12-18 14:54:35 -0700473 args (Namespace): program arguments
Chris Packhamf90df592017-05-02 21:30:48 +1200474 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700475 if not confirm(args, 'Clean up README?'):
Chris Packhamf90df592017-05-02 21:30:48 +1200476 return
477
478 patterns = []
479 for config in configs:
480 patterns.append(re.compile(r'^\s+%s' % config))
481
Simon Glass37f815c2021-12-18 14:54:34 -0700482 lines = read_file('README')
Chris Packhamf90df592017-05-02 21:30:48 +1200483
484 found = False
485 newlines = []
486 for line in lines:
487 if not found:
488 found = find_matching(patterns, line)
489 if found:
490 continue
491
492 if found and re.search(r'^\s+CONFIG', line):
493 found = False
494
495 if not found:
496 newlines.append(line)
497
Simon Glass2fd85bd2021-12-18 14:54:33 -0700498 write_file('README', newlines)
Chris Packhamf90df592017-05-02 21:30:48 +1200499
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200500def try_expand(line):
501 """If value looks like an expression, try expanding it
502 Otherwise just return the existing value
503 """
504 if line.find('=') == -1:
505 return line
506
507 try:
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +0100508 aeval = asteval.Interpreter( usersyms=SIZES, minimal=True )
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200509 cfg, val = re.split("=", line)
510 val= val.strip('\"')
Simon Glassdaa694d2021-12-18 14:54:30 -0700511 if re.search(r'[*+-/]|<<|SZ_+|\(([^\)]+)\)', val):
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +0100512 newval = hex(aeval(val))
Simon Glassdaa694d2021-12-18 14:54:30 -0700513 print('\tExpanded expression %s to %s' % (val, newval))
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200514 return cfg+'='+newval
515 except:
Simon Glassdaa694d2021-12-18 14:54:30 -0700516 print('\tFailed to expand expression in %s' % line)
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200517
518 return line
519
Chris Packhamca438342017-05-02 21:30:47 +1200520
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900521### classes ###
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900522class Progress:
523
524 """Progress Indicator"""
525
526 def __init__(self, total):
527 """Create a new progress indicator.
528
Simon Glass91197aa2021-12-18 14:54:35 -0700529 Args:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900530 total: A number of defconfig files to process.
531 """
532 self.current = 0
533 self.total = total
534
535 def inc(self):
536 """Increment the number of processed defconfig files."""
537
538 self.current += 1
539
540 def show(self):
541 """Display the progress."""
Simon Glass793dca32019-10-31 07:42:57 -0600542 print(' %d defconfigs out of %d\r' % (self.current, self.total), end=' ')
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900543 sys.stdout.flush()
544
Simon Glasscb008832017-06-15 21:39:33 -0600545
546class KconfigScanner:
547 """Kconfig scanner."""
548
549 def __init__(self):
550 """Scan all the Kconfig files and create a Config object."""
551 # Define environment variables referenced from Kconfig
552 os.environ['srctree'] = os.getcwd()
553 os.environ['UBOOTVERSION'] = 'dummy'
554 os.environ['KCONFIG_OBJDIR'] = ''
Tom Rini65e05dd2019-09-20 17:42:09 -0400555 self.conf = kconfiglib.Kconfig()
Simon Glasscb008832017-06-15 21:39:33 -0600556
557
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900558class KconfigParser:
559
560 """A parser of .config and include/autoconf.mk."""
561
562 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
563 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
564
Simon Glassb2e83c62021-12-18 14:54:31 -0700565 def __init__(self, configs, args, build_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900566 """Create a new parser.
567
Simon Glass91197aa2021-12-18 14:54:35 -0700568 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900569 configs: A list of CONFIGs to move.
Simon Glass91197aa2021-12-18 14:54:35 -0700570 args (Namespace): program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900571 build_dir: Build directory.
572 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900573 self.configs = configs
Simon Glassb2e83c62021-12-18 14:54:31 -0700574 self.args = args
Masahiro Yamada1f169922016-05-19 15:52:00 +0900575 self.dotconfig = os.path.join(build_dir, '.config')
576 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
Masahiro Yamada07913d12016-08-22 22:18:22 +0900577 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
578 'autoconf.mk')
Simon Glassf3b8e642017-06-01 19:39:01 -0600579 self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900580 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900581
Simon Glass6821a742017-07-10 14:47:47 -0600582 def get_arch(self):
583 """Parse .config file and return the architecture.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900584
585 Returns:
Simon Glass6821a742017-07-10 14:47:47 -0600586 Architecture name (e.g. 'arm').
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900587 """
588 arch = ''
589 cpu = ''
Simon Glass37f815c2021-12-18 14:54:34 -0700590 for line in read_file(self.dotconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900591 m = self.re_arch.match(line)
592 if m:
593 arch = m.group(1)
594 continue
595 m = self.re_cpu.match(line)
596 if m:
597 cpu = m.group(1)
598
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900599 if not arch:
600 return None
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900601
602 # fix-up for aarch64
603 if arch == 'arm' and cpu == 'armv8':
604 arch = 'aarch64'
605
Simon Glass6821a742017-07-10 14:47:47 -0600606 return arch
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900607
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900608 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900609 """Parse .config, defconfig, include/autoconf.mk for one config.
610
611 This function looks for the config options in the lines from
612 defconfig, .config, and include/autoconf.mk in order to decide
613 which action should be taken for this defconfig.
614
Simon Glass91197aa2021-12-18 14:54:35 -0700615 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900616 config: CONFIG name to parse.
Masahiro Yamadacc008292016-05-19 15:51:56 +0900617 dotconfig_lines: lines from the .config file.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900618 autoconf_lines: lines from the include/autoconf.mk file.
619
620 Returns:
621 A tupple of the action for this defconfig and the line
622 matched for the config.
623 """
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900624 not_set = '# %s is not set' % config
625
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900626 for line in autoconf_lines:
627 line = line.rstrip()
628 if line.startswith(config + '='):
Masahiro Yamadacc008292016-05-19 15:51:56 +0900629 new_val = line
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900630 break
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900631 else:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900632 new_val = not_set
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900633
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200634 new_val = try_expand(new_val)
635
Masahiro Yamada916224c2016-08-22 22:18:21 +0900636 for line in dotconfig_lines:
637 line = line.rstrip()
638 if line.startswith(config + '=') or line == not_set:
639 old_val = line
640 break
641 else:
642 if new_val == not_set:
643 return (ACTION_NO_ENTRY, config)
644 else:
645 return (ACTION_NO_ENTRY_WARN, config)
646
Masahiro Yamadacc008292016-05-19 15:51:56 +0900647 # If this CONFIG is neither bool nor trisate
648 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
649 # tools/scripts/define2mk.sed changes '1' to 'y'.
650 # This is a problem if the CONFIG is int type.
651 # Check the type in Kconfig and handle it correctly.
652 if new_val[-2:] == '=y':
653 new_val = new_val[:-1] + '1'
654
Masahiro Yamada50301592016-06-15 14:33:50 +0900655 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
656 new_val)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900657
Masahiro Yamada1d085562016-05-19 15:52:02 +0900658 def update_dotconfig(self):
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900659 """Parse files for the config options and update the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900660
Masahiro Yamadacc008292016-05-19 15:51:56 +0900661 This function parses the generated .config and include/autoconf.mk
662 searching the target options.
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900663 Move the config option(s) to the .config as needed.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900664
Simon Glass91197aa2021-12-18 14:54:35 -0700665 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900666 defconfig: defconfig name.
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900667
668 Returns:
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900669 Return a tuple of (updated flag, log string).
670 The "updated flag" is True if the .config was updated, False
671 otherwise. The "log string" shows what happend to the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900672 """
673
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900674 results = []
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900675 updated = False
Masahiro Yamada916224c2016-08-22 22:18:21 +0900676 suspicious = False
Masahiro Yamada07913d12016-08-22 22:18:22 +0900677 rm_files = [self.config_autoconf, self.autoconf]
678
Simon Glassb2e83c62021-12-18 14:54:31 -0700679 if self.args.spl:
Masahiro Yamada07913d12016-08-22 22:18:22 +0900680 if os.path.exists(self.spl_autoconf):
681 autoconf_path = self.spl_autoconf
682 rm_files.append(self.spl_autoconf)
683 else:
684 for f in rm_files:
685 os.remove(f)
686 return (updated, suspicious,
Simon Glassb2e83c62021-12-18 14:54:31 -0700687 color_text(self.args.color, COLOR_BROWN,
Masahiro Yamada07913d12016-08-22 22:18:22 +0900688 "SPL is not enabled. Skipped.") + '\n')
689 else:
690 autoconf_path = self.autoconf
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900691
Simon Glass37f815c2021-12-18 14:54:34 -0700692 dotconfig_lines = read_file(self.dotconfig)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900693
Simon Glass37f815c2021-12-18 14:54:34 -0700694 autoconf_lines = read_file(autoconf_path)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900695
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900696 for config in self.configs:
697 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger96464ba2015-05-19 13:21:17 -0500698 autoconf_lines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900699 results.append(result)
700
701 log = ''
702
703 for (action, value) in results:
704 if action == ACTION_MOVE:
705 actlog = "Move '%s'" % value
706 log_color = COLOR_LIGHT_GREEN
Masahiro Yamadacc008292016-05-19 15:51:56 +0900707 elif action == ACTION_NO_ENTRY:
Simon Glassdaa694d2021-12-18 14:54:30 -0700708 actlog = '%s is not defined in Kconfig. Do nothing.' % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900709 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada916224c2016-08-22 22:18:21 +0900710 elif action == ACTION_NO_ENTRY_WARN:
Simon Glassdaa694d2021-12-18 14:54:30 -0700711 actlog = '%s is not defined in Kconfig (suspicious). Do nothing.' % value
Masahiro Yamada916224c2016-08-22 22:18:21 +0900712 log_color = COLOR_YELLOW
713 suspicious = True
Masahiro Yamadacc008292016-05-19 15:51:56 +0900714 elif action == ACTION_NO_CHANGE:
715 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
716 % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900717 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900718 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700719 sys.exit('Internal Error. This should not happen.')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900720
Simon Glassb2e83c62021-12-18 14:54:31 -0700721 log += color_text(self.args.color, log_color, actlog) + '\n'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900722
Simon Glass91197aa2021-12-18 14:54:35 -0700723 with open(self.dotconfig, 'a', encoding='utf-8') as out:
Masahiro Yamadae423d172016-05-19 15:51:49 +0900724 for (action, value) in results:
725 if action == ACTION_MOVE:
Simon Glass91197aa2021-12-18 14:54:35 -0700726 out.write(value + '\n')
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900727 updated = True
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900728
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900729 self.results = results
Masahiro Yamada07913d12016-08-22 22:18:22 +0900730 for f in rm_files:
731 os.remove(f)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900732
Masahiro Yamada916224c2016-08-22 22:18:21 +0900733 return (updated, suspicious, log)
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900734
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900735 def check_defconfig(self):
736 """Check the defconfig after savedefconfig
737
738 Returns:
739 Return additional log if moved CONFIGs were removed again by
740 'make savedefconfig'.
741 """
742
743 log = ''
744
Simon Glass37f815c2021-12-18 14:54:34 -0700745 defconfig_lines = read_file(self.defconfig)
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900746
747 for (action, value) in self.results:
748 if action != ACTION_MOVE:
749 continue
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300750 if not value in defconfig_lines:
Simon Glassb2e83c62021-12-18 14:54:31 -0700751 log += color_text(self.args.color, COLOR_YELLOW,
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900752 "'%s' was removed by savedefconfig.\n" %
753 value)
754
755 return log
756
Simon Glassd73fcb12017-06-01 19:39:02 -0600757
758class DatabaseThread(threading.Thread):
759 """This thread processes results from Slot threads.
760
761 It collects the data in the master config directary. There is only one
762 result thread, and this helps to serialise the build output.
763 """
764 def __init__(self, config_db, db_queue):
765 """Set up a new result thread
766
767 Args:
768 builder: Builder which will be sent each result
769 """
770 threading.Thread.__init__(self)
771 self.config_db = config_db
772 self.db_queue= db_queue
773
774 def run(self):
775 """Called to start up the result thread.
776
777 We collect the next result job and pass it on to the build.
778 """
779 while True:
780 defconfig, configs = self.db_queue.get()
781 self.config_db[defconfig] = configs
782 self.db_queue.task_done()
783
784
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900785class Slot:
786
787 """A slot to store a subprocess.
788
789 Each instance of this class handles one subprocess.
790 This class is useful to control multiple threads
791 for faster processing.
792 """
793
Simon Glassb2e83c62021-12-18 14:54:31 -0700794 def __init__(self, toolchains, configs, args, progress, devnull,
Simon Glass6821a742017-07-10 14:47:47 -0600795 make_cmd, reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900796 """Create a new process slot.
797
Simon Glass91197aa2021-12-18 14:54:35 -0700798 Args:
Simon Glass6821a742017-07-10 14:47:47 -0600799 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900800 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -0700801 args: Program arguments
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900802 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900803 devnull: A file object of '/dev/null'.
804 make_cmd: command name of GNU Make.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500805 reference_src_dir: Determine the true starting config state from this
806 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -0600807 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900808 """
Simon Glass6821a742017-07-10 14:47:47 -0600809 self.toolchains = toolchains
Simon Glassb2e83c62021-12-18 14:54:31 -0700810 self.args = args
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900811 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900812 self.build_dir = tempfile.mkdtemp()
813 self.devnull = devnull
814 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500815 self.reference_src_dir = reference_src_dir
Simon Glassd73fcb12017-06-01 19:39:02 -0600816 self.db_queue = db_queue
Simon Glassb2e83c62021-12-18 14:54:31 -0700817 self.parser = KconfigParser(configs, args, self.build_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900818 self.state = STATE_IDLE
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900819 self.failed_boards = set()
820 self.suspicious_boards = set()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900821
822 def __del__(self):
823 """Delete the working directory
824
825 This function makes sure the temporary directory is cleaned away
826 even if Python suddenly dies due to error. It should be done in here
Joe Hershbergerf2dae752016-06-10 14:53:29 -0500827 because it is guaranteed the destructor is always invoked when the
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900828 instance of the class gets unreferenced.
829
830 If the subprocess is still running, wait until it finishes.
831 """
832 if self.state != STATE_IDLE:
833 while self.ps.poll() == None:
834 pass
835 shutil.rmtree(self.build_dir)
836
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900837 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900838 """Assign a new subprocess for defconfig and add it to the slot.
839
840 If the slot is vacant, create a new subprocess for processing the
841 given defconfig and add it to the slot. Just returns False if
842 the slot is occupied (i.e. the current subprocess is still running).
843
Simon Glass91197aa2021-12-18 14:54:35 -0700844 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900845 defconfig: defconfig name.
846
847 Returns:
848 Return True on success or False on failure
849 """
850 if self.state != STATE_IDLE:
851 return False
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900852
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900853 self.defconfig = defconfig
Masahiro Yamada1d085562016-05-19 15:52:02 +0900854 self.log = ''
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900855 self.current_src_dir = self.reference_src_dir
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900856 self.do_defconfig()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900857 return True
858
859 def poll(self):
860 """Check the status of the subprocess and handle it as needed.
861
862 Returns True if the slot is vacant (i.e. in idle state).
863 If the configuration is successfully finished, assign a new
864 subprocess to build include/autoconf.mk.
865 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900866 parse the .config and the include/autoconf.mk, moving
867 config options to the .config as needed.
868 If the .config was updated, run "make savedefconfig" to sync
869 it, update the original defconfig, and then set the slot back
870 to the idle state.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900871
872 Returns:
873 Return True if the subprocess is terminated, False otherwise
874 """
875 if self.state == STATE_IDLE:
876 return True
877
878 if self.ps.poll() == None:
879 return False
880
881 if self.ps.poll() != 0:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900882 self.handle_error()
883 elif self.state == STATE_DEFCONFIG:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900884 if self.reference_src_dir and not self.current_src_dir:
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500885 self.do_savedefconfig()
886 else:
887 self.do_autoconf()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900888 elif self.state == STATE_AUTOCONF:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900889 if self.current_src_dir:
890 self.current_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500891 self.do_defconfig()
Simon Glassb2e83c62021-12-18 14:54:31 -0700892 elif self.args.build_db:
Simon Glassd73fcb12017-06-01 19:39:02 -0600893 self.do_build_db()
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500894 else:
895 self.do_savedefconfig()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900896 elif self.state == STATE_SAVEDEFCONFIG:
897 self.update_defconfig()
898 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700899 sys.exit('Internal Error. This should not happen.')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900900
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900901 return True if self.state == STATE_IDLE else False
Joe Hershberger96464ba2015-05-19 13:21:17 -0500902
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900903 def handle_error(self):
904 """Handle error cases."""
Masahiro Yamada8513dc02016-05-19 15:52:08 +0900905
Simon Glassb2e83c62021-12-18 14:54:31 -0700906 self.log += color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glassdaa694d2021-12-18 14:54:30 -0700907 'Failed to process.\n')
Simon Glassb2e83c62021-12-18 14:54:31 -0700908 if self.args.verbose:
909 self.log += color_text(self.args.color, COLOR_LIGHT_CYAN,
Markus Klotzbuecher4f5c5e92020-02-12 20:46:45 +0100910 self.ps.stderr.read().decode())
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900911 self.finish(False)
Joe Hershberger96464ba2015-05-19 13:21:17 -0500912
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900913 def do_defconfig(self):
914 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900915
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900916 cmd = list(self.make_cmd)
917 cmd.append(self.defconfig)
918 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900919 stderr=subprocess.PIPE,
920 cwd=self.current_src_dir)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900921 self.state = STATE_DEFCONFIG
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900922
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900923 def do_autoconf(self):
Simon Glassf3b8e642017-06-01 19:39:01 -0600924 """Run 'make AUTO_CONF_PATH'."""
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900925
Simon Glass6821a742017-07-10 14:47:47 -0600926 arch = self.parser.get_arch()
927 try:
928 toolchain = self.toolchains.Select(arch)
929 except ValueError:
Simon Glassb2e83c62021-12-18 14:54:31 -0700930 self.log += color_text(self.args.color, COLOR_YELLOW,
Chris Packhamce3ba452017-08-27 20:00:51 +1200931 "Tool chain for '%s' is missing. Do nothing.\n" % arch)
Masahiro Yamada4efef992016-05-19 15:52:03 +0900932 self.finish(False)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900933 return
Simon Glass793dca32019-10-31 07:42:57 -0600934 env = toolchain.MakeEnvironment(False)
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900935
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900936 cmd = list(self.make_cmd)
Joe Hershberger7740f652015-05-19 13:21:18 -0500937 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Simon Glassf3b8e642017-06-01 19:39:01 -0600938 cmd.append(AUTO_CONF_PATH)
Simon Glass6821a742017-07-10 14:47:47 -0600939 self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900940 stderr=subprocess.PIPE,
941 cwd=self.current_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900942 self.state = STATE_AUTOCONF
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900943
Simon Glassd73fcb12017-06-01 19:39:02 -0600944 def do_build_db(self):
945 """Add the board to the database"""
946 configs = {}
Simon Glass37f815c2021-12-18 14:54:34 -0700947 for line in read_file(os.path.join(self.build_dir, AUTO_CONF_PATH)):
948 if line.startswith('CONFIG'):
949 config, value = line.split('=', 1)
950 configs[config] = value.rstrip()
Simon Glassd73fcb12017-06-01 19:39:02 -0600951 self.db_queue.put([self.defconfig, configs])
952 self.finish(True)
953
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900954 def do_savedefconfig(self):
955 """Update the .config and run 'make savedefconfig'."""
956
Masahiro Yamada916224c2016-08-22 22:18:21 +0900957 (updated, suspicious, log) = self.parser.update_dotconfig()
958 if suspicious:
959 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900960 self.log += log
961
Simon Glassb2e83c62021-12-18 14:54:31 -0700962 if not self.args.force_sync and not updated:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900963 self.finish(True)
964 return
965 if updated:
Simon Glassb2e83c62021-12-18 14:54:31 -0700966 self.log += color_text(self.args.color, COLOR_LIGHT_GREEN,
Simon Glassdaa694d2021-12-18 14:54:30 -0700967 'Syncing by savedefconfig...\n')
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900968 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700969 self.log += 'Syncing by savedefconfig (forced by option)...\n'
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900970
971 cmd = list(self.make_cmd)
972 cmd.append('savedefconfig')
973 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
974 stderr=subprocess.PIPE)
975 self.state = STATE_SAVEDEFCONFIG
976
977 def update_defconfig(self):
978 """Update the input defconfig and go back to the idle state."""
979
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900980 log = self.parser.check_defconfig()
981 if log:
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900982 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900983 self.log += log
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900984 orig_defconfig = os.path.join('configs', self.defconfig)
985 new_defconfig = os.path.join(self.build_dir, 'defconfig')
986 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
987
988 if updated:
Simon Glassb2e83c62021-12-18 14:54:31 -0700989 self.log += color_text(self.args.color, COLOR_LIGHT_BLUE,
Simon Glassdaa694d2021-12-18 14:54:30 -0700990 'defconfig was updated.\n')
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900991
Simon Glassb2e83c62021-12-18 14:54:31 -0700992 if not self.args.dry_run and updated:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900993 shutil.move(new_defconfig, orig_defconfig)
994 self.finish(True)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900995
Masahiro Yamada4efef992016-05-19 15:52:03 +0900996 def finish(self, success):
997 """Display log along with progress and go to the idle state.
Masahiro Yamada1d085562016-05-19 15:52:02 +0900998
Simon Glass91197aa2021-12-18 14:54:35 -0700999 Args:
Masahiro Yamada4efef992016-05-19 15:52:03 +09001000 success: Should be True when the defconfig was processed
1001 successfully, or False when it fails.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001002 """
1003 # output at least 30 characters to hide the "* defconfigs out of *".
1004 log = self.defconfig.ljust(30) + '\n'
1005
1006 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
1007 # Some threads are running in parallel.
1008 # Print log atomically to not mix up logs from different threads.
Simon Glass793dca32019-10-31 07:42:57 -06001009 print(log, file=(sys.stdout if success else sys.stderr))
Masahiro Yamada4efef992016-05-19 15:52:03 +09001010
1011 if not success:
Simon Glassb2e83c62021-12-18 14:54:31 -07001012 if self.args.exit_on_error:
Simon Glassdaa694d2021-12-18 14:54:30 -07001013 sys.exit('Exit on error.')
Masahiro Yamada4efef992016-05-19 15:52:03 +09001014 # If --exit-on-error flag is not set, skip this board and continue.
1015 # Record the failed board.
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001016 self.failed_boards.add(self.defconfig)
Masahiro Yamada4efef992016-05-19 15:52:03 +09001017
Masahiro Yamada1d085562016-05-19 15:52:02 +09001018 self.progress.inc()
1019 self.progress.show()
Masahiro Yamada4efef992016-05-19 15:52:03 +09001020 self.state = STATE_IDLE
Masahiro Yamada1d085562016-05-19 15:52:02 +09001021
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001022 def get_failed_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001023 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001024 """
1025 return self.failed_boards
1026
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001027 def get_suspicious_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001028 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001029 """
Masahiro Yamada916224c2016-08-22 22:18:21 +09001030 return self.suspicious_boards - self.failed_boards
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001031
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001032class Slots:
1033
1034 """Controller of the array of subprocess slots."""
1035
Simon Glassb2e83c62021-12-18 14:54:31 -07001036 def __init__(self, toolchains, configs, args, progress,
Simon Glass6821a742017-07-10 14:47:47 -06001037 reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001038 """Create a new slots controller.
1039
Simon Glass91197aa2021-12-18 14:54:35 -07001040 Args:
Simon Glass6821a742017-07-10 14:47:47 -06001041 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001042 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -07001043 args: Program arguments
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001044 progress: A progress indicator.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001045 reference_src_dir: Determine the true starting config state from this
1046 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -06001047 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001048 """
Simon Glassb2e83c62021-12-18 14:54:31 -07001049 self.args = args
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001050 self.slots = []
Simon Glass478920d2021-12-18 14:54:32 -07001051 devnull = subprocess.DEVNULL
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001052 make_cmd = get_make_cmd()
Simon Glassb2e83c62021-12-18 14:54:31 -07001053 for i in range(args.jobs):
1054 self.slots.append(Slot(toolchains, configs, args, progress,
Simon Glass6821a742017-07-10 14:47:47 -06001055 devnull, make_cmd, reference_src_dir,
1056 db_queue))
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001057
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001058 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001059 """Add a new subprocess if a vacant slot is found.
1060
Simon Glass91197aa2021-12-18 14:54:35 -07001061 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001062 defconfig: defconfig name to be put into.
1063
1064 Returns:
1065 Return True on success or False on failure
1066 """
1067 for slot in self.slots:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001068 if slot.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001069 return True
1070 return False
1071
1072 def available(self):
1073 """Check if there is a vacant slot.
1074
1075 Returns:
1076 Return True if at lease one vacant slot is found, False otherwise.
1077 """
1078 for slot in self.slots:
1079 if slot.poll():
1080 return True
1081 return False
1082
1083 def empty(self):
1084 """Check if all slots are vacant.
1085
1086 Returns:
1087 Return True if all the slots are vacant, False otherwise.
1088 """
1089 ret = True
1090 for slot in self.slots:
1091 if not slot.poll():
1092 ret = False
1093 return ret
1094
1095 def show_failed_boards(self):
1096 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001097 boards = set()
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001098 output_file = 'moveconfig.failed'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001099
1100 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001101 boards |= slot.get_failed_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001102
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001103 if boards:
1104 boards = '\n'.join(boards) + '\n'
Simon Glassdaa694d2021-12-18 14:54:30 -07001105 msg = 'The following boards were not processed due to error:\n'
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001106 msg += boards
Simon Glassdaa694d2021-12-18 14:54:30 -07001107 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassb2e83c62021-12-18 14:54:31 -07001108 print(color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glass793dca32019-10-31 07:42:57 -06001109 msg), file=sys.stderr)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001110
Simon Glass2fd85bd2021-12-18 14:54:33 -07001111 write_file(output_file, boards)
Joe Hershberger2559cd82015-05-19 13:21:22 -05001112
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001113 def show_suspicious_boards(self):
1114 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001115 boards = set()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001116 output_file = 'moveconfig.suspicious'
1117
1118 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001119 boards |= slot.get_suspicious_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001120
1121 if boards:
1122 boards = '\n'.join(boards) + '\n'
Simon Glassdaa694d2021-12-18 14:54:30 -07001123 msg = 'The following boards might have been converted incorrectly.\n'
1124 msg += 'It is highly recommended to check them manually:\n'
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001125 msg += boards
Simon Glassdaa694d2021-12-18 14:54:30 -07001126 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassb2e83c62021-12-18 14:54:31 -07001127 print(color_text(self.args.color, COLOR_YELLOW,
Simon Glass793dca32019-10-31 07:42:57 -06001128 msg), file=sys.stderr)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001129
Simon Glass2fd85bd2021-12-18 14:54:33 -07001130 write_file(output_file, boards)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001131
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001132class ReferenceSource:
1133
1134 """Reference source against which original configs should be parsed."""
1135
1136 def __init__(self, commit):
1137 """Create a reference source directory based on a specified commit.
1138
Simon Glass91197aa2021-12-18 14:54:35 -07001139 Args:
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001140 commit: commit to git-clone
1141 """
1142 self.src_dir = tempfile.mkdtemp()
Simon Glassdaa694d2021-12-18 14:54:30 -07001143 print('Cloning git repo to a separate work directory...')
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001144 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1145 cwd=self.src_dir)
Simon Glass793dca32019-10-31 07:42:57 -06001146 print("Checkout '%s' to build the original autoconf.mk." % \
1147 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip())
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001148 subprocess.check_output(['git', 'checkout', commit],
1149 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001150
1151 def __del__(self):
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001152 """Delete the reference source directory
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001153
1154 This function makes sure the temporary directory is cleaned away
1155 even if Python suddenly dies due to error. It should be done in here
1156 because it is guaranteed the destructor is always invoked when the
1157 instance of the class gets unreferenced.
1158 """
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001159 shutil.rmtree(self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001160
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001161 def get_dir(self):
1162 """Return the absolute path to the reference source directory."""
1163
1164 return self.src_dir
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001165
Simon Glassb2e83c62021-12-18 14:54:31 -07001166def move_config(toolchains, configs, args, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001167 """Move config options to defconfig files.
1168
Simon Glass91197aa2021-12-18 14:54:35 -07001169 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001170 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -07001171 args: Program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001172 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001173 if len(configs) == 0:
Simon Glassb2e83c62021-12-18 14:54:31 -07001174 if args.force_sync:
Simon Glass793dca32019-10-31 07:42:57 -06001175 print('No CONFIG is specified. You are probably syncing defconfigs.', end=' ')
Simon Glassb2e83c62021-12-18 14:54:31 -07001176 elif args.build_db:
Simon Glass793dca32019-10-31 07:42:57 -06001177 print('Building %s database' % CONFIG_DATABASE)
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001178 else:
Simon Glass793dca32019-10-31 07:42:57 -06001179 print('Neither CONFIG nor --force-sync is specified. Nothing will happen.', end=' ')
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001180 else:
Simon Glass793dca32019-10-31 07:42:57 -06001181 print('Move ' + ', '.join(configs), end=' ')
Simon Glassb2e83c62021-12-18 14:54:31 -07001182 print('(jobs: %d)\n' % args.jobs)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001183
Simon Glassb2e83c62021-12-18 14:54:31 -07001184 if args.git_ref:
1185 reference_src = ReferenceSource(args.git_ref)
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001186 reference_src_dir = reference_src.get_dir()
1187 else:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001188 reference_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001189
Simon Glassb2e83c62021-12-18 14:54:31 -07001190 if args.defconfigs:
1191 defconfigs = get_matched_defconfigs(args.defconfigs)
Joe Hershberger91040e82015-05-19 13:21:19 -05001192 else:
Masahiro Yamada684c3062016-07-25 19:15:28 +09001193 defconfigs = get_all_defconfigs()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001194
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001195 progress = Progress(len(defconfigs))
Simon Glassb2e83c62021-12-18 14:54:31 -07001196 slots = Slots(toolchains, configs, args, progress, reference_src_dir,
Simon Glass91197aa2021-12-18 14:54:35 -07001197 db_queue)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001198
1199 # Main loop to process defconfig files:
1200 # Add a new subprocess into a vacant slot.
1201 # Sleep if there is no available slot.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001202 for defconfig in defconfigs:
1203 while not slots.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001204 while not slots.available():
1205 # No available slot: sleep for a while
1206 time.sleep(SLEEP_TIME)
1207
1208 # wait until all the subprocesses finish
1209 while not slots.empty():
1210 time.sleep(SLEEP_TIME)
1211
Simon Glass793dca32019-10-31 07:42:57 -06001212 print('')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001213 slots.show_failed_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001214 slots.show_suspicious_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001215
Simon Glasscb008832017-06-15 21:39:33 -06001216def find_kconfig_rules(kconf, config, imply_config):
1217 """Check whether a config has a 'select' or 'imply' keyword
1218
1219 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -04001220 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -06001221 config: Name of config to check (without CONFIG_ prefix)
1222 imply_config: Implying config (without CONFIG_ prefix) which may or
1223 may not have an 'imply' for 'config')
1224
1225 Returns:
1226 Symbol object for 'config' if found, else None
1227 """
Tom Rini65e05dd2019-09-20 17:42:09 -04001228 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -06001229 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001230 for sel, cond in (sym.selects + sym.implies):
Simon Glassa3627082021-12-18 08:09:42 -07001231 if sel.name == config:
Simon Glasscb008832017-06-15 21:39:33 -06001232 return sym
1233 return None
1234
1235def check_imply_rule(kconf, config, imply_config):
1236 """Check if we can add an 'imply' option
1237
1238 This finds imply_config in the Kconfig and looks to see if it is possible
1239 to add an 'imply' for 'config' to that part of the Kconfig.
1240
1241 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -04001242 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -06001243 config: Name of config to check (without CONFIG_ prefix)
1244 imply_config: Implying config (without CONFIG_ prefix) which may or
1245 may not have an 'imply' for 'config')
1246
1247 Returns:
1248 tuple:
1249 filename of Kconfig file containing imply_config, or None if none
1250 line number within the Kconfig file, or 0 if none
1251 message indicating the result
1252 """
Tom Rini65e05dd2019-09-20 17:42:09 -04001253 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -06001254 if not sym:
1255 return 'cannot find sym'
Simon Glassea40b202021-07-21 21:35:53 -06001256 nodes = sym.nodes
1257 if len(nodes) != 1:
1258 return '%d locations' % len(nodes)
Simon Glassa3627082021-12-18 08:09:42 -07001259 node = nodes[0]
1260 fname, linenum = node.filename, node.linenr
Simon Glasscb008832017-06-15 21:39:33 -06001261 cwd = os.getcwd()
1262 if cwd and fname.startswith(cwd):
1263 fname = fname[len(cwd) + 1:]
1264 file_line = ' at %s:%d' % (fname, linenum)
Simon Glass37f815c2021-12-18 14:54:34 -07001265 data = read_file(fname)
Simon Glasscb008832017-06-15 21:39:33 -06001266 if data[linenum - 1] != 'config %s' % imply_config:
1267 return None, 0, 'bad sym format %s%s' % (data[linenum], file_line)
1268 return fname, linenum, 'adding%s' % file_line
1269
1270def add_imply_rule(config, fname, linenum):
1271 """Add a new 'imply' option to a Kconfig
1272
1273 Args:
1274 config: config option to add an imply for (without CONFIG_ prefix)
1275 fname: Kconfig filename to update
1276 linenum: Line number to place the 'imply' before
1277
1278 Returns:
1279 Message indicating the result
1280 """
1281 file_line = ' at %s:%d' % (fname, linenum)
Simon Glass37f815c2021-12-18 14:54:34 -07001282 data = read_file(fname)
Simon Glasscb008832017-06-15 21:39:33 -06001283 linenum -= 1
1284
1285 for offset, line in enumerate(data[linenum:]):
1286 if line.strip().startswith('help') or not line:
1287 data.insert(linenum + offset, '\timply %s' % config)
Simon Glass2fd85bd2021-12-18 14:54:33 -07001288 write_file(fname, data)
Simon Glasscb008832017-06-15 21:39:33 -06001289 return 'added%s' % file_line
1290
1291 return 'could not insert%s'
1292
1293(IMPLY_MIN_2, IMPLY_TARGET, IMPLY_CMD, IMPLY_NON_ARCH_BOARD) = (
1294 1, 2, 4, 8)
Simon Glass9b2a2e82017-06-15 21:39:32 -06001295
1296IMPLY_FLAGS = {
1297 'min2': [IMPLY_MIN_2, 'Show options which imply >2 boards (normally >5)'],
1298 'target': [IMPLY_TARGET, 'Allow CONFIG_TARGET_... options to imply'],
1299 'cmd': [IMPLY_CMD, 'Allow CONFIG_CMD_... to imply'],
Simon Glasscb008832017-06-15 21:39:33 -06001300 'non-arch-board': [
1301 IMPLY_NON_ARCH_BOARD,
1302 'Allow Kconfig options outside arch/ and /board/ to imply'],
Simon Glass91197aa2021-12-18 14:54:35 -07001303}
Simon Glass9b2a2e82017-06-15 21:39:32 -06001304
Simon Glass9d603392021-12-18 08:09:43 -07001305
1306def read_database():
1307 """Read in the config database
1308
1309 Returns:
1310 tuple:
1311 set of all config options seen (each a str)
1312 set of all defconfigs seen (each a str)
1313 dict of configs for each defconfig:
1314 key: defconfig name, e.g. "MPC8548CDS_legacy_defconfig"
1315 value: dict:
1316 key: CONFIG option
1317 value: Value of option
1318 dict of defconfigs for each config:
1319 key: CONFIG option
1320 value: set of boards using that option
1321
1322 """
1323 configs = {}
1324
1325 # key is defconfig name, value is dict of (CONFIG_xxx, value)
1326 config_db = {}
1327
1328 # Set of all config options we have seen
1329 all_configs = set()
1330
1331 # Set of all defconfigs we have seen
1332 all_defconfigs = set()
1333
1334 defconfig_db = collections.defaultdict(set)
Simon Glass37f815c2021-12-18 14:54:34 -07001335 for line in read_file(CONFIG_DATABASE):
1336 line = line.rstrip()
1337 if not line: # Separator between defconfigs
1338 config_db[defconfig] = configs
1339 all_defconfigs.add(defconfig)
1340 configs = {}
1341 elif line[0] == ' ': # CONFIG line
1342 config, value = line.strip().split('=', 1)
1343 configs[config] = value
1344 defconfig_db[config].add(defconfig)
1345 all_configs.add(config)
1346 else: # New defconfig
1347 defconfig = line
Simon Glass9d603392021-12-18 08:09:43 -07001348
1349 return all_configs, all_defconfigs, config_db, defconfig_db
1350
1351
Simon Glasscb008832017-06-15 21:39:33 -06001352def do_imply_config(config_list, add_imply, imply_flags, skip_added,
1353 check_kconfig=True, find_superset=False):
Simon Glass99b66602017-06-01 19:39:03 -06001354 """Find CONFIG options which imply those in the list
1355
1356 Some CONFIG options can be implied by others and this can help to reduce
1357 the size of the defconfig files. For example, CONFIG_X86 implies
1358 CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
1359 all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
1360 each of the x86 defconfig files.
1361
1362 This function uses the moveconfig database to find such options. It
1363 displays a list of things that could possibly imply those in the list.
1364 The algorithm ignores any that start with CONFIG_TARGET since these
1365 typically refer to only a few defconfigs (often one). It also does not
1366 display a config with less than 5 defconfigs.
1367
1368 The algorithm works using sets. For each target config in config_list:
1369 - Get the set 'defconfigs' which use that target config
1370 - For each config (from a list of all configs):
1371 - Get the set 'imply_defconfig' of defconfigs which use that config
1372 -
1373 - If imply_defconfigs contains anything not in defconfigs then
1374 this config does not imply the target config
1375
1376 Params:
1377 config_list: List of CONFIG options to check (each a string)
Simon Glasscb008832017-06-15 21:39:33 -06001378 add_imply: Automatically add an 'imply' for each config.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001379 imply_flags: Flags which control which implying configs are allowed
1380 (IMPLY_...)
Simon Glasscb008832017-06-15 21:39:33 -06001381 skip_added: Don't show options which already have an imply added.
1382 check_kconfig: Check if implied symbols already have an 'imply' or
1383 'select' for the target config, and show this information if so.
Simon Glass99b66602017-06-01 19:39:03 -06001384 find_superset: True to look for configs which are a superset of those
1385 already found. So for example if CONFIG_EXYNOS5 implies an option,
1386 but CONFIG_EXYNOS covers a larger set of defconfigs and also
1387 implies that option, this will drop the former in favour of the
1388 latter. In practice this option has not proved very used.
1389
1390 Note the terminoloy:
1391 config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
1392 defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
1393 """
Simon Glasscb008832017-06-15 21:39:33 -06001394 kconf = KconfigScanner().conf if check_kconfig else None
1395 if add_imply and add_imply != 'all':
Simon Glassa3627082021-12-18 08:09:42 -07001396 add_imply = add_imply.split(',')
Simon Glasscb008832017-06-15 21:39:33 -06001397
Simon Glass9d603392021-12-18 08:09:43 -07001398 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
Simon Glass99b66602017-06-01 19:39:03 -06001399
Simon Glassa3627082021-12-18 08:09:42 -07001400 # Work through each target config option in turn, independently
Simon Glass99b66602017-06-01 19:39:03 -06001401 for config in config_list:
1402 defconfigs = defconfig_db.get(config)
1403 if not defconfigs:
Simon Glass793dca32019-10-31 07:42:57 -06001404 print('%s not found in any defconfig' % config)
Simon Glass99b66602017-06-01 19:39:03 -06001405 continue
1406
1407 # Get the set of defconfigs without this one (since a config cannot
1408 # imply itself)
1409 non_defconfigs = all_defconfigs - defconfigs
1410 num_defconfigs = len(defconfigs)
Simon Glass793dca32019-10-31 07:42:57 -06001411 print('%s found in %d/%d defconfigs' % (config, num_defconfigs,
1412 len(all_configs)))
Simon Glass99b66602017-06-01 19:39:03 -06001413
1414 # This will hold the results: key=config, value=defconfigs containing it
1415 imply_configs = {}
1416 rest_configs = all_configs - set([config])
1417
1418 # Look at every possible config, except the target one
1419 for imply_config in rest_configs:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001420 if 'ERRATUM' in imply_config:
Simon Glass99b66602017-06-01 19:39:03 -06001421 continue
Simon Glass91197aa2021-12-18 14:54:35 -07001422 if not imply_flags & IMPLY_CMD:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001423 if 'CONFIG_CMD' in imply_config:
1424 continue
Simon Glass91197aa2021-12-18 14:54:35 -07001425 if not imply_flags & IMPLY_TARGET:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001426 if 'CONFIG_TARGET' in imply_config:
1427 continue
Simon Glass99b66602017-06-01 19:39:03 -06001428
1429 # Find set of defconfigs that have this config
1430 imply_defconfig = defconfig_db[imply_config]
1431
1432 # Get the intersection of this with defconfigs containing the
1433 # target config
1434 common_defconfigs = imply_defconfig & defconfigs
1435
1436 # Get the set of defconfigs containing this config which DO NOT
1437 # also contain the taret config. If this set is non-empty it means
1438 # that this config affects other defconfigs as well as (possibly)
1439 # the ones affected by the target config. This means it implies
1440 # things we don't want to imply.
1441 not_common_defconfigs = imply_defconfig & non_defconfigs
1442 if not_common_defconfigs:
1443 continue
1444
1445 # If there are common defconfigs, imply_config may be useful
1446 if common_defconfigs:
1447 skip = False
1448 if find_superset:
Simon Glass793dca32019-10-31 07:42:57 -06001449 for prev in list(imply_configs.keys()):
Simon Glass99b66602017-06-01 19:39:03 -06001450 prev_count = len(imply_configs[prev])
1451 count = len(common_defconfigs)
1452 if (prev_count > count and
1453 (imply_configs[prev] & common_defconfigs ==
1454 common_defconfigs)):
1455 # skip imply_config because prev is a superset
1456 skip = True
1457 break
1458 elif count > prev_count:
1459 # delete prev because imply_config is a superset
1460 del imply_configs[prev]
1461 if not skip:
1462 imply_configs[imply_config] = common_defconfigs
1463
1464 # Now we have a dict imply_configs of configs which imply each config
1465 # The value of each dict item is the set of defconfigs containing that
1466 # config. Rank them so that we print the configs that imply the largest
1467 # number of defconfigs first.
Simon Glasscb008832017-06-15 21:39:33 -06001468 ranked_iconfigs = sorted(imply_configs,
Simon Glass99b66602017-06-01 19:39:03 -06001469 key=lambda k: len(imply_configs[k]), reverse=True)
Simon Glasscb008832017-06-15 21:39:33 -06001470 kconfig_info = ''
1471 cwd = os.getcwd()
1472 add_list = collections.defaultdict(list)
1473 for iconfig in ranked_iconfigs:
1474 num_common = len(imply_configs[iconfig])
Simon Glass99b66602017-06-01 19:39:03 -06001475
1476 # Don't bother if there are less than 5 defconfigs affected.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001477 if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
Simon Glass99b66602017-06-01 19:39:03 -06001478 continue
Simon Glasscb008832017-06-15 21:39:33 -06001479 missing = defconfigs - imply_configs[iconfig]
Simon Glass99b66602017-06-01 19:39:03 -06001480 missing_str = ', '.join(missing) if missing else 'all'
1481 missing_str = ''
Simon Glasscb008832017-06-15 21:39:33 -06001482 show = True
1483 if kconf:
1484 sym = find_kconfig_rules(kconf, config[CONFIG_LEN:],
1485 iconfig[CONFIG_LEN:])
1486 kconfig_info = ''
1487 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001488 nodes = sym.nodes
1489 if len(nodes) == 1:
1490 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001491 if cwd and fname.startswith(cwd):
1492 fname = fname[len(cwd) + 1:]
1493 kconfig_info = '%s:%d' % (fname, linenum)
1494 if skip_added:
1495 show = False
1496 else:
Tom Rini65e05dd2019-09-20 17:42:09 -04001497 sym = kconf.syms.get(iconfig[CONFIG_LEN:])
Simon Glasscb008832017-06-15 21:39:33 -06001498 fname = ''
1499 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001500 nodes = sym.nodes
1501 if len(nodes) == 1:
1502 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001503 if cwd and fname.startswith(cwd):
1504 fname = fname[len(cwd) + 1:]
1505 in_arch_board = not sym or (fname.startswith('arch') or
1506 fname.startswith('board'))
1507 if (not in_arch_board and
Simon Glass91197aa2021-12-18 14:54:35 -07001508 not imply_flags & IMPLY_NON_ARCH_BOARD):
Simon Glasscb008832017-06-15 21:39:33 -06001509 continue
1510
1511 if add_imply and (add_imply == 'all' or
1512 iconfig in add_imply):
1513 fname, linenum, kconfig_info = (check_imply_rule(kconf,
1514 config[CONFIG_LEN:], iconfig[CONFIG_LEN:]))
1515 if fname:
1516 add_list[fname].append(linenum)
1517
1518 if show and kconfig_info != 'skip':
Simon Glass793dca32019-10-31 07:42:57 -06001519 print('%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
1520 kconfig_info, missing_str))
Simon Glasscb008832017-06-15 21:39:33 -06001521
1522 # Having collected a list of things to add, now we add them. We process
1523 # each file from the largest line number to the smallest so that
1524 # earlier additions do not affect our line numbers. E.g. if we added an
1525 # imply at line 20 it would change the position of each line after
1526 # that.
Simon Glass793dca32019-10-31 07:42:57 -06001527 for fname, linenums in add_list.items():
Simon Glasscb008832017-06-15 21:39:33 -06001528 for linenum in sorted(linenums, reverse=True):
1529 add_imply_rule(config[CONFIG_LEN:], fname, linenum)
Simon Glass99b66602017-06-01 19:39:03 -06001530
Simon Glass941671a2022-02-08 11:49:46 -07001531def defconfig_matches(configs, re_match):
1532 """Check if any CONFIG option matches a regex
1533
1534 The match must be complete, i.e. from the start to end of the CONFIG option.
1535
1536 Args:
1537 configs (dict): Dict of CONFIG options:
1538 key: CONFIG option
1539 value: Value of option
1540 re_match (re.Pattern): Match to check
1541
1542 Returns:
1543 bool: True if any CONFIG matches the regex
1544 """
1545 for cfg in configs:
Simon Glassd9c958f2022-03-05 20:18:54 -07001546 if re_match.fullmatch(cfg):
Simon Glass941671a2022-02-08 11:49:46 -07001547 return True
1548 return False
Simon Glass99b66602017-06-01 19:39:03 -06001549
Simon Glass65d7fce2021-12-18 08:09:46 -07001550def do_find_config(config_list):
1551 """Find boards with a given combination of CONFIGs
1552
1553 Params:
Simon Glass941671a2022-02-08 11:49:46 -07001554 config_list: List of CONFIG options to check (each a regex consisting
Simon Glass65d7fce2021-12-18 08:09:46 -07001555 of a config option, with or without a CONFIG_ prefix. If an option
1556 is preceded by a tilde (~) then it must be false, otherwise it must
1557 be true)
1558 """
1559 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
1560
1561 # Get the whitelist
Simon Glass37f815c2021-12-18 14:54:34 -07001562 adhoc_configs = set(read_file('scripts/config_whitelist.txt'))
Simon Glass65d7fce2021-12-18 08:09:46 -07001563
1564 # Start with all defconfigs
1565 out = all_defconfigs
1566
1567 # Work through each config in turn
1568 adhoc = []
1569 for item in config_list:
1570 # Get the real config name and whether we want this config or not
1571 cfg = item
1572 want = True
1573 if cfg[0] == '~':
1574 want = False
1575 cfg = cfg[1:]
1576
1577 if cfg in adhoc_configs:
1578 adhoc.append(cfg)
1579 continue
1580
1581 # Search everything that is still in the running. If it has a config
1582 # that we want, or doesn't have one that we don't, add it into the
1583 # running for the next stage
1584 in_list = out
1585 out = set()
Simon Glass941671a2022-02-08 11:49:46 -07001586 re_match = re.compile(cfg)
Simon Glass65d7fce2021-12-18 08:09:46 -07001587 for defc in in_list:
Simon Glass941671a2022-02-08 11:49:46 -07001588 has_cfg = defconfig_matches(config_db[defc], re_match)
Simon Glass65d7fce2021-12-18 08:09:46 -07001589 if has_cfg == want:
1590 out.add(defc)
1591 if adhoc:
1592 print(f"Error: Not in Kconfig: %s" % ' '.join(adhoc))
1593 else:
1594 print(f'{len(out)} matches')
Simon Glass78f12e52022-03-05 20:18:53 -07001595 print(' '.join(item.split('_defconfig')[0] for item in out))
Simon Glass65d7fce2021-12-18 08:09:46 -07001596
1597
1598def prefix_config(cfg):
1599 """Prefix a config with CONFIG_ if needed
1600
1601 This handles ~ operator, which indicates that the CONFIG should be disabled
1602
1603 >>> prefix_config('FRED')
1604 'CONFIG_FRED'
1605 >>> prefix_config('CONFIG_FRED')
1606 'CONFIG_FRED'
1607 >>> prefix_config('~FRED')
1608 '~CONFIG_FRED'
1609 >>> prefix_config('~CONFIG_FRED')
1610 '~CONFIG_FRED'
1611 >>> prefix_config('A123')
1612 'CONFIG_A123'
1613 """
1614 op = ''
1615 if cfg[0] == '~':
1616 op = cfg[0]
1617 cfg = cfg[1:]
1618 if not cfg.startswith('CONFIG_'):
1619 cfg = 'CONFIG_' + cfg
1620 return op + cfg
1621
1622
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001623def main():
1624 try:
1625 cpu_count = multiprocessing.cpu_count()
1626 except NotImplementedError:
1627 cpu_count = 1
1628
Simon Glassb2e83c62021-12-18 14:54:31 -07001629 epilog = '''Move config options from headers to defconfig files. See
1630doc/develop/moveconfig.rst for documentation.'''
1631
1632 parser = ArgumentParser(epilog=epilog)
1633 # Add arguments here
1634 parser.add_argument('-a', '--add-imply', type=str, default='',
Simon Glasscb008832017-06-15 21:39:33 -06001635 help='comma-separated list of CONFIG options to add '
1636 "an 'imply' statement to for the CONFIG in -i")
Simon Glassb2e83c62021-12-18 14:54:31 -07001637 parser.add_argument('-A', '--skip-added', action='store_true', default=False,
Simon Glasscb008832017-06-15 21:39:33 -06001638 help="don't show options which are already marked as "
1639 'implying others')
Simon Glassb2e83c62021-12-18 14:54:31 -07001640 parser.add_argument('-b', '--build-db', action='store_true', default=False,
Simon Glassd73fcb12017-06-01 19:39:02 -06001641 help='build a CONFIG database')
Simon Glassb2e83c62021-12-18 14:54:31 -07001642 parser.add_argument('-c', '--color', action='store_true', default=False,
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001643 help='display the log in color')
Simon Glassb2e83c62021-12-18 14:54:31 -07001644 parser.add_argument('-C', '--commit', action='store_true', default=False,
Simon Glass9ede2122016-09-12 23:18:21 -06001645 help='Create a git commit for the operation')
Simon Glassb2e83c62021-12-18 14:54:31 -07001646 parser.add_argument('-d', '--defconfigs', type=str,
Simon Glassee4e61b2017-06-01 19:38:59 -06001647 help='a file containing a list of defconfigs to move, '
1648 "one per line (for example 'snow_defconfig') "
1649 "or '-' to read from stdin")
Simon Glassb2e83c62021-12-18 14:54:31 -07001650 parser.add_argument('-e', '--exit-on-error', action='store_true',
Simon Glasse1ae5632021-12-18 08:09:44 -07001651 default=False,
1652 help='exit immediately on any error')
Simon Glassb2e83c62021-12-18 14:54:31 -07001653 parser.add_argument('-f', '--find', action='store_true', default=False,
Simon Glass65d7fce2021-12-18 08:09:46 -07001654 help='Find boards with a given config combination')
Simon Glassb2e83c62021-12-18 14:54:31 -07001655 parser.add_argument('-H', '--headers-only', dest='cleanup_headers_only',
Simon Glasse1ae5632021-12-18 08:09:44 -07001656 action='store_true', default=False,
1657 help='only cleanup the headers')
Simon Glassb2e83c62021-12-18 14:54:31 -07001658 parser.add_argument('-i', '--imply', action='store_true', default=False,
Simon Glass99b66602017-06-01 19:39:03 -06001659 help='find options which imply others')
Simon Glassb2e83c62021-12-18 14:54:31 -07001660 parser.add_argument('-I', '--imply-flags', type=str, default='',
Simon Glass9b2a2e82017-06-15 21:39:32 -06001661 help="control the -i option ('help' for help")
Simon Glassb2e83c62021-12-18 14:54:31 -07001662 parser.add_argument('-j', '--jobs', type=int, default=cpu_count,
Simon Glasse1ae5632021-12-18 08:09:44 -07001663 help='the number of jobs to run simultaneously')
Simon Glassb2e83c62021-12-18 14:54:31 -07001664 parser.add_argument('-n', '--dry-run', action='store_true', default=False,
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001665 help='perform a trial run (show log with no changes)')
Simon Glassb2e83c62021-12-18 14:54:31 -07001666 parser.add_argument('-r', '--git-ref', type=str,
Simon Glasse1ae5632021-12-18 08:09:44 -07001667 help='the git ref to clone for building the autoconf.mk')
Simon Glassb2e83c62021-12-18 14:54:31 -07001668 parser.add_argument('-s', '--force-sync', action='store_true', default=False,
Masahiro Yamada8513dc02016-05-19 15:52:08 +09001669 help='force sync by savedefconfig')
Simon Glassb2e83c62021-12-18 14:54:31 -07001670 parser.add_argument('-S', '--spl', action='store_true', default=False,
Masahiro Yamada07913d12016-08-22 22:18:22 +09001671 help='parse config options defined for SPL build')
Simon Glassb2e83c62021-12-18 14:54:31 -07001672 parser.add_argument('-t', '--test', action='store_true', default=False,
Simon Glasse1ae5632021-12-18 08:09:44 -07001673 help='run unit tests')
Simon Glassb2e83c62021-12-18 14:54:31 -07001674 parser.add_argument('-y', '--yes', action='store_true', default=False,
Simon Glass6b403df2016-09-12 23:18:20 -06001675 help="respond 'yes' to any prompts")
Simon Glassb2e83c62021-12-18 14:54:31 -07001676 parser.add_argument('-v', '--verbose', action='store_true', default=False,
Joe Hershberger95bf9c72015-05-19 13:21:24 -05001677 help='show any build errors as boards are built')
Simon Glassb2e83c62021-12-18 14:54:31 -07001678 parser.add_argument('configs', nargs='*')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001679
Simon Glassb2e83c62021-12-18 14:54:31 -07001680 args = parser.parse_args()
1681 configs = args.configs
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001682
Simon Glassb2e83c62021-12-18 14:54:31 -07001683 if args.test:
Simon Glass84067a52021-12-18 08:09:45 -07001684 sys.argv = [sys.argv[0]]
1685 fail, count = doctest.testmod()
1686 if fail:
1687 return 1
1688 unittest.main()
1689
Simon Glassb2e83c62021-12-18 14:54:31 -07001690 if not any((len(configs), args.force_sync, args.build_db, args.imply,
1691 args.find)):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001692 parser.print_usage()
1693 sys.exit(1)
1694
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001695 # prefix the option name with CONFIG_ if missing
Simon Glass65d7fce2021-12-18 08:09:46 -07001696 configs = [prefix_config(cfg) for cfg in configs]
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001697
Joe Hershberger2144f882015-05-19 13:21:20 -05001698 check_top_directory()
1699
Simon Glassb2e83c62021-12-18 14:54:31 -07001700 if args.imply:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001701 imply_flags = 0
Simon Glassb2e83c62021-12-18 14:54:31 -07001702 if args.imply_flags == 'all':
Simon Glassdee36c72017-07-10 14:47:46 -06001703 imply_flags = -1
1704
Simon Glassb2e83c62021-12-18 14:54:31 -07001705 elif args.imply_flags:
1706 for flag in args.imply_flags.split(','):
Simon Glassdee36c72017-07-10 14:47:46 -06001707 bad = flag not in IMPLY_FLAGS
1708 if bad:
Simon Glass793dca32019-10-31 07:42:57 -06001709 print("Invalid flag '%s'" % flag)
Simon Glassdee36c72017-07-10 14:47:46 -06001710 if flag == 'help' or bad:
Simon Glass793dca32019-10-31 07:42:57 -06001711 print("Imply flags: (separate with ',')")
1712 for name, info in IMPLY_FLAGS.items():
1713 print(' %-15s: %s' % (name, info[1]))
Simon Glassdee36c72017-07-10 14:47:46 -06001714 parser.print_usage()
1715 sys.exit(1)
1716 imply_flags |= IMPLY_FLAGS[flag][0]
Simon Glass9b2a2e82017-06-15 21:39:32 -06001717
Simon Glassb2e83c62021-12-18 14:54:31 -07001718 do_imply_config(configs, args.add_imply, imply_flags, args.skip_added)
Simon Glass99b66602017-06-01 19:39:03 -06001719 return
1720
Simon Glassb2e83c62021-12-18 14:54:31 -07001721 if args.find:
Simon Glass65d7fce2021-12-18 08:09:46 -07001722 do_find_config(configs)
1723 return
1724
Simon Glassd73fcb12017-06-01 19:39:02 -06001725 config_db = {}
Simon Glass793dca32019-10-31 07:42:57 -06001726 db_queue = queue.Queue()
Simon Glassd73fcb12017-06-01 19:39:02 -06001727 t = DatabaseThread(config_db, db_queue)
1728 t.setDaemon(True)
1729 t.start()
1730
Simon Glassb2e83c62021-12-18 14:54:31 -07001731 if not args.cleanup_headers_only:
Masahiro Yamadaf7536f72016-07-25 19:15:23 +09001732 check_clean_directory()
Simon Glass793dca32019-10-31 07:42:57 -06001733 bsettings.Setup('')
Simon Glass6821a742017-07-10 14:47:47 -06001734 toolchains = toolchain.Toolchains()
1735 toolchains.GetSettings()
1736 toolchains.Scan(verbose=False)
Simon Glassb2e83c62021-12-18 14:54:31 -07001737 move_config(toolchains, configs, args, db_queue)
Simon Glassd73fcb12017-06-01 19:39:02 -06001738 db_queue.join()
Joe Hershberger2144f882015-05-19 13:21:20 -05001739
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001740 if configs:
Simon Glassb2e83c62021-12-18 14:54:31 -07001741 cleanup_headers(configs, args)
Simon Glassb2e83c62021-12-18 14:54:31 -07001742 cleanup_whitelist(configs, args)
1743 cleanup_readme(configs, args)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001744
Simon Glassb2e83c62021-12-18 14:54:31 -07001745 if args.commit:
Simon Glass9ede2122016-09-12 23:18:21 -06001746 subprocess.call(['git', 'add', '-u'])
1747 if configs:
1748 msg = 'Convert %s %sto Kconfig' % (configs[0],
1749 'et al ' if len(configs) > 1 else '')
1750 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1751 '\n '.join(configs))
1752 else:
1753 msg = 'configs: Resync with savedefconfig'
1754 msg += '\n\nRsync all defconfig files using moveconfig.py'
1755 subprocess.call(['git', 'commit', '-s', '-m', msg])
1756
Simon Glassb2e83c62021-12-18 14:54:31 -07001757 if args.build_db:
Simon Glass91197aa2021-12-18 14:54:35 -07001758 with open(CONFIG_DATABASE, 'w', encoding='utf-8') as fd:
Simon Glass793dca32019-10-31 07:42:57 -06001759 for defconfig, configs in config_db.items():
Simon Glassc79d18c2017-08-13 16:02:54 -06001760 fd.write('%s\n' % defconfig)
Simon Glassd73fcb12017-06-01 19:39:02 -06001761 for config in sorted(configs.keys()):
Simon Glassc79d18c2017-08-13 16:02:54 -06001762 fd.write(' %s=%s\n' % (config, configs[config]))
1763 fd.write('\n')
Simon Glassd73fcb12017-06-01 19:39:02 -06001764
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001765if __name__ == '__main__':
Simon Glass65d7fce2021-12-18 08:09:46 -07001766 sys.exit(main())