blob: 84bc875fff8efcf15d7acadce3a0944bf3c1c65d [file] [log] [blame]
Simon Glass1f701862019-10-31 07:42:57 -06001#!/usr/bin/env python3
Tom Rini10e47792018-05-06 17:58:06 -04002# SPDX-License-Identifier: GPL-2.0+
Masahiro Yamadab6160812015-05-20 11:36:07 +09003#
4# Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5#
Masahiro Yamadab6160812015-05-20 11:36:07 +09006
7"""
8Move config options from headers to defconfig files.
9
Simon Glass83cc72e2021-07-21 21:35:51 -060010See doc/develop/moveconfig.rst for documentation.
Masahiro Yamadab6160812015-05-20 11:36:07 +090011"""
12
Simon Glassd9c1da22021-12-18 14:54:31 -070013from argparse import ArgumentParser
Markus Klotzbuecherbac0bb52020-02-12 20:46:44 +010014import asteval
Simon Glassc6e73cf2017-06-01 19:39:03 -060015import collections
Simon Glassb3464eb2021-12-18 14:54:35 -070016from contextlib import ExitStack
Masahiro Yamadaea8f5342016-07-25 19:15:24 +090017import copy
Masahiro Yamada573b3902016-07-25 19:15:25 +090018import difflib
Simon Glassbb57be72021-12-18 08:09:45 -070019import doctest
Masahiro Yamada0f6beda2016-05-19 15:52:07 +090020import filecmp
Masahiro Yamadab6160812015-05-20 11:36:07 +090021import fnmatch
Masahiro Yamada3984d6e2016-10-19 14:39:54 +090022import glob
Masahiro Yamadab6160812015-05-20 11:36:07 +090023import multiprocessing
Masahiro Yamadab6160812015-05-20 11:36:07 +090024import os
Simon Glass1f701862019-10-31 07:42:57 -060025import queue
Masahiro Yamadab6160812015-05-20 11:36:07 +090026import re
27import shutil
28import subprocess
29import sys
30import tempfile
Simon Glass43cf08f2017-06-01 19:39:02 -060031import threading
Masahiro Yamadab6160812015-05-20 11:36:07 +090032import time
Simon Glassbb57be72021-12-18 08:09:45 -070033import unittest
Masahiro Yamadab6160812015-05-20 11:36:07 +090034
Simon Glassf0d9c102020-04-17 18:09:02 -060035from buildman import bsettings
36from buildman import kconfiglib
37from buildman import toolchain
Simon Glass44116332017-06-15 21:39:33 -060038
Masahiro Yamadab6160812015-05-20 11:36:07 +090039SHOW_GNU_MAKE = 'scripts/show-gnu-make'
40SLEEP_TIME=0.03
41
Masahiro Yamadab6160812015-05-20 11:36:07 +090042STATE_IDLE = 0
43STATE_DEFCONFIG = 1
44STATE_AUTOCONF = 2
Joe Hershberger166edec2015-05-19 13:21:17 -050045STATE_SAVEDEFCONFIG = 3
Masahiro Yamadab6160812015-05-20 11:36:07 +090046
47ACTION_MOVE = 0
Masahiro Yamada5643d6e2016-05-19 15:51:56 +090048ACTION_NO_ENTRY = 1
Masahiro Yamada35204d92016-08-22 22:18:21 +090049ACTION_NO_ENTRY_WARN = 2
50ACTION_NO_CHANGE = 3
Masahiro Yamadab6160812015-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 Glass8fb5bd02017-06-01 19:39:01 -060069AUTO_CONF_PATH = 'include/config/auto.conf'
Simon Glass43cf08f2017-06-01 19:39:02 -060070CONFIG_DATABASE = 'moveconfig.db'
Simon Glass8fb5bd02017-06-01 19:39:01 -060071
Simon Glass44116332017-06-15 21:39:33 -060072CONFIG_LEN = len('CONFIG_')
Simon Glass8fb5bd02017-06-01 19:39:01 -060073
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +020074SIZES = {
Simon Glassdc634d92021-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 Klotzbuecher3d5d4182019-05-15 15:15:52 +020092}
93
Simon Glasse8037552022-02-08 11:49:45 -070094RE_REMOVE_DEFCONFIG = re.compile(r'(.*)_defconfig')
95
Masahiro Yamadab6160812015-05-20 11:36:07 +090096### helper functions ###
Masahiro Yamadab6160812015-05-20 11:36:07 +090097def check_top_directory():
98 """Exit if we are not at the top of source directory."""
Simon Glassb3464eb2021-12-18 14:54:35 -070099 for fname in 'README', 'Licenses':
100 if not os.path.exists(fname):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900101 sys.exit('Please run at the top of source directory.')
102
Masahiro Yamada990e6772016-05-19 15:51:54 +0900103def check_clean_directory():
104 """Exit if the source tree is not clean."""
Simon Glassb3464eb2021-12-18 14:54:35 -0700105 for fname in '.config', 'include/config':
106 if os.path.exists(fname):
Masahiro Yamada990e6772016-05-19 15:51:54 +0900107 sys.exit("source tree is not clean, please run 'make mrproper'")
108
Masahiro Yamadab6160812015-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 Glassb3464eb2021-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 Yamadab6160812015-05-20 11:36:07 +0900120 return ret[0].rstrip()
121
Simon Glass18774bc2017-06-01 19:38:58 -0600122def get_matched_defconfig(line):
123 """Get the defconfig files that match a pattern
124
125 Args:
Simon Glassb3464eb2021-12-18 14:54:35 -0700126 line (str): Path or filename to match, e.g. 'configs/snow_defconfig' or
Simon Glass18774bc2017-06-01 19:38:58 -0600127 'k2*_defconfig'. If no directory is provided, 'configs/' is
128 prepended
129
130 Returns:
Simon Glassb3464eb2021-12-18 14:54:35 -0700131 list of str: a list of matching defconfig files
Simon Glass18774bc2017-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 Yamada3984d6e2016-10-19 14:39:54 +0900140def get_matched_defconfigs(defconfigs_file):
Simon Glass8f3cf312017-06-01 19:38:59 -0600141 """Get all the defconfig files that match the patterns in a file.
142
143 Args:
Simon Glassb3464eb2021-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 Glass8f3cf312017-06-01 19:38:59 -0600146
147 Returns:
Simon Glassb3464eb2021-12-18 14:54:35 -0700148 list of str: A list of paths to defconfig files, with no duplicates
Simon Glass8f3cf312017-06-01 19:38:59 -0600149 """
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900150 defconfigs = []
Simon Glassb3464eb2021-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 Yamada3984d6e2016-10-19 14:39:54 +0900167
Simon Glassb3464eb2021-12-18 14:54:35 -0700168 defconfigs += matched
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900169
170 # use set() to drop multiple matching
Simon Glassb3464eb2021-12-18 14:54:35 -0700171 return [defconfig[len('configs') + 1:] for defconfig in set(defconfigs)]
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900172
Masahiro Yamada58175e32016-07-25 19:15:28 +0900173def get_all_defconfigs():
Simon Glassb3464eb2021-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 Yamada58175e32016-07-25 19:15:28 +0900179 defconfigs = []
Simon Glassb3464eb2021-12-18 14:54:35 -0700180 for (dirpath, _, filenames) in os.walk('configs'):
Masahiro Yamada58175e32016-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 Yamadab6160812015-05-20 11:36:07 +0900187def color_text(color_enabled, color, string):
188 """Return colored string."""
189 if color_enabled:
Masahiro Yamada465b7c02016-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 Glassb3464eb2021-12-18 14:54:35 -0700194 return string
Masahiro Yamadab6160812015-05-20 11:36:07 +0900195
Simon Glassb3464eb2021-12-18 14:54:35 -0700196def show_diff(alines, blines, file_path, color_enabled):
Masahiro Yamada573b3902016-07-25 19:15:25 +0900197 """Show unidified diff.
198
Simon Glassb3464eb2021-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 Yamada573b3902016-07-25 19:15:25 +0900204 """
Simon Glassb3464eb2021-12-18 14:54:35 -0700205 diff = difflib.unified_diff(alines, blines,
Masahiro Yamada573b3902016-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 Yasakcab84072022-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 Yamadaa1a4b092016-07-25 19:15:26 +0900214 else:
Alper Nebi Yasakcab84072022-01-29 18:22:08 +0300215 print(line)
Masahiro Yamada573b3902016-07-25 19:15:25 +0900216
Simon Glassb3464eb2021-12-18 14:54:35 -0700217def extend_matched_lines(lines, matched, pre_patterns, post_patterns,
218 extend_pre, extend_post):
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900219 """Extend matched lines if desired patterns are found before/after already
220 matched lines.
221
Simon Glassb3464eb2021-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 Yamadaea8f5342016-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 Glassb3464eb2021-12-18 14:54:35 -0700248 for pat in pre_patterns:
249 if pat.search(lines[i - 1]):
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900250 break
251 else:
252 # not matched
253 continue
254
Simon Glassb3464eb2021-12-18 14:54:35 -0700255 for pat in post_patterns:
256 if pat.search(lines[j]):
Masahiro Yamadaea8f5342016-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 Glassd9c1da22021-12-18 14:54:31 -0700270def confirm(args, prompt):
Simon Glassb3464eb2021-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 Glassd9c1da22021-12-18 14:54:31 -0700279 if not args.yes:
Chris Packham85e15c52017-05-02 21:30:46 +1200280 while True:
Simon Glassb3464eb2021-12-18 14:54:35 -0700281 choice = input(f'{prompt} [y/n]: ')
Chris Packham85e15c52017-05-02 21:30:46 +1200282 choice = choice.lower()
Simon Glass1f701862019-10-31 07:42:57 -0600283 print(choice)
Simon Glassb3464eb2021-12-18 14:54:35 -0700284 if choice in ('y', 'n'):
Chris Packham85e15c52017-05-02 21:30:46 +1200285 break
286
287 if choice == 'n':
288 return False
289
290 return True
291
Simon Glassb09ae452021-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 Glassaba238f2021-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 Glassafaddc72022-02-11 13:23:22 -0700331 raise
Simon Glassaba238f2021-12-18 14:54:34 -0700332 print("Failed on file %s': %s" % (fname, e))
333 return None
334
Simon Glassd9c1da22021-12-18 14:54:31 -0700335def cleanup_empty_blocks(header_path, args):
Chris Packham73f6c7c2019-01-30 20:23:16 +1300336 """Clean up empty conditional blocks
337
Simon Glassb3464eb2021-12-18 14:54:35 -0700338 Args:
339 header_path (str): path to the cleaned file.
340 args (Namespace): program arguments
Chris Packham73f6c7c2019-01-30 20:23:16 +1300341 """
342 pattern = re.compile(r'^\s*#\s*if.*$\n^\s*#\s*endif.*$\n*', flags=re.M)
Simon Glassaba238f2021-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 Packham73f6c7c2019-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 Glassd9c1da22021-12-18 14:54:31 -0700350 args.color)
Chris Packham73f6c7c2019-01-30 20:23:16 +1300351
Simon Glassd9c1da22021-12-18 14:54:31 -0700352 if args.dry_run:
Chris Packham73f6c7c2019-01-30 20:23:16 +1300353 return
354
Simon Glassaba238f2021-12-18 14:54:34 -0700355 if new_data != data:
356 write_file(header_path, new_data)
Chris Packham73f6c7c2019-01-30 20:23:16 +1300357
Simon Glassd9c1da22021-12-18 14:54:31 -0700358def cleanup_one_header(header_path, patterns, args):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900359 """Clean regex-matched lines away from a file.
360
Simon Glassb3464eb2021-12-18 14:54:35 -0700361 Args:
Masahiro Yamadab6160812015-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 Glassb3464eb2021-12-18 14:54:35 -0700365 args (Namespace): program arguments
Masahiro Yamadab6160812015-05-20 11:36:07 +0900366 """
Simon Glassaba238f2021-12-18 14:54:34 -0700367 lines = read_file(header_path, skip_unicode=True)
368 if lines is None:
369 return
Masahiro Yamadab6160812015-05-20 11:36:07 +0900370
371 matched = []
372 for i, line in enumerate(lines):
Alper Nebi Yasakcab84072022-01-29 18:22:08 +0300373 if i - 1 in matched and lines[i - 1].endswith('\\'):
Masahiro Yamada6d798ba2016-07-25 19:15:27 +0900374 matched.append(i)
375 continue
Masahiro Yamadab6160812015-05-20 11:36:07 +0900376 for pattern in patterns:
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900377 if pattern.search(line):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900378 matched.append(i)
379 break
380
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900381 if not matched:
382 return
383
384 # remove empty #ifdef ... #endif, successive blank lines
Alper Nebi Yasakcab84072022-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 Yamadaea8f5342016-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 Yamada573b3902016-07-25 19:15:25 +0900405 tolines = copy.copy(lines)
406
407 for i in reversed(matched):
408 tolines.pop(i)
409
Simon Glassd9c1da22021-12-18 14:54:31 -0700410 show_diff(lines, tolines, header_path, args.color)
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900411
Simon Glassd9c1da22021-12-18 14:54:31 -0700412 if args.dry_run:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900413 return
414
Simon Glassb09ae452021-12-18 14:54:33 -0700415 write_file(header_path, tolines)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900416
Simon Glassd9c1da22021-12-18 14:54:31 -0700417def cleanup_headers(configs, args):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900418 """Delete config defines from board headers.
419
Simon Glassb3464eb2021-12-18 14:54:35 -0700420 Args:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900421 configs: A list of CONFIGs to remove.
Simon Glassb3464eb2021-12-18 14:54:35 -0700422 args (Namespace): program arguments
Masahiro Yamadab6160812015-05-20 11:36:07 +0900423 """
Simon Glassd9c1da22021-12-18 14:54:31 -0700424 if not confirm(args, 'Clean up headers?'):
Chris Packham85e15c52017-05-02 21:30:46 +1200425 return
Masahiro Yamadab6160812015-05-20 11:36:07 +0900426
427 patterns = []
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900428 for config in configs:
Alper Nebi Yasakcab84072022-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 Yamadab6160812015-05-20 11:36:07 +0900431
Joe Hershbergerb78ad422015-05-19 13:21:21 -0500432 for dir in 'include', 'arch', 'board':
433 for (dirpath, dirnames, filenames) in os.walk(dir):
Masahiro Yamada28a6d352016-07-25 19:15:22 +0900434 if dirpath == os.path.join('include', 'generated'):
435 continue
Joe Hershbergerb78ad422015-05-19 13:21:21 -0500436 for filename in filenames:
Simon Glassce44bf52020-08-11 11:23:34 -0600437 if not filename.endswith(('~', '.dts', '.dtsi', '.bin',
Trevor Woerneraaad0c22021-03-15 12:01:33 -0400438 '.elf','.aml','.dat')):
Chris Packham73f6c7c2019-01-30 20:23:16 +1300439 header_path = os.path.join(dirpath, filename)
Tom Rinie90c0072019-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 Glassd9c1da22021-12-18 14:54:31 -0700443 cleanup_one_header(header_path, patterns, args)
444 cleanup_empty_blocks(header_path, args)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900445
Tom Rinia23179e2022-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 Glassd9c1da22021-12-18 14:54:31 -0700510def cleanup_whitelist(configs, args):
Chris Packham9d5274f2017-05-02 21:30:47 +1200511 """Delete config whitelist entries
512
Simon Glassb3464eb2021-12-18 14:54:35 -0700513 Args:
Chris Packham9d5274f2017-05-02 21:30:47 +1200514 configs: A list of CONFIGs to remove.
Simon Glassb3464eb2021-12-18 14:54:35 -0700515 args (Namespace): program arguments
Chris Packham9d5274f2017-05-02 21:30:47 +1200516 """
Simon Glassd9c1da22021-12-18 14:54:31 -0700517 if not confirm(args, 'Clean up whitelist entries?'):
Chris Packham9d5274f2017-05-02 21:30:47 +1200518 return
519
Simon Glassaba238f2021-12-18 14:54:34 -0700520 lines = read_file(os.path.join('scripts', 'config_whitelist.txt'))
Chris Packham9d5274f2017-05-02 21:30:47 +1200521
522 lines = [x for x in lines if x.strip() not in configs]
523
Simon Glassb09ae452021-12-18 14:54:33 -0700524 write_file(os.path.join('scripts', 'config_whitelist.txt'), lines)
Chris Packham9d5274f2017-05-02 21:30:47 +1200525
Chris Packham0e6deff2017-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 Glassd9c1da22021-12-18 14:54:31 -0700532def cleanup_readme(configs, args):
Chris Packham0e6deff2017-05-02 21:30:48 +1200533 """Delete config description in README
534
Simon Glassb3464eb2021-12-18 14:54:35 -0700535 Args:
Chris Packham0e6deff2017-05-02 21:30:48 +1200536 configs: A list of CONFIGs to remove.
Simon Glassb3464eb2021-12-18 14:54:35 -0700537 args (Namespace): program arguments
Chris Packham0e6deff2017-05-02 21:30:48 +1200538 """
Simon Glassd9c1da22021-12-18 14:54:31 -0700539 if not confirm(args, 'Clean up README?'):
Chris Packham0e6deff2017-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 Glassaba238f2021-12-18 14:54:34 -0700546 lines = read_file('README')
Chris Packham0e6deff2017-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 Glassb09ae452021-12-18 14:54:33 -0700562 write_file('README', newlines)
Chris Packham0e6deff2017-05-02 21:30:48 +1200563
Markus Klotzbuecher3d5d4182019-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 Klotzbuecherbac0bb52020-02-12 20:46:44 +0100572 aeval = asteval.Interpreter( usersyms=SIZES, minimal=True )
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200573 cfg, val = re.split("=", line)
574 val= val.strip('\"')
Simon Glassdc634d92021-12-18 14:54:30 -0700575 if re.search(r'[*+-/]|<<|SZ_+|\(([^\)]+)\)', val):
Markus Klotzbuecherbac0bb52020-02-12 20:46:44 +0100576 newval = hex(aeval(val))
Simon Glassdc634d92021-12-18 14:54:30 -0700577 print('\tExpanded expression %s to %s' % (val, newval))
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200578 return cfg+'='+newval
579 except:
Simon Glassdc634d92021-12-18 14:54:30 -0700580 print('\tFailed to expand expression in %s' % line)
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200581
582 return line
583
Chris Packham9d5274f2017-05-02 21:30:47 +1200584
Masahiro Yamadab6160812015-05-20 11:36:07 +0900585### classes ###
Masahiro Yamadacefaa582016-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 Glassb3464eb2021-12-18 14:54:35 -0700593 Args:
Masahiro Yamadacefaa582016-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 Glass1f701862019-10-31 07:42:57 -0600606 print(' %d defconfigs out of %d\r' % (self.current, self.total), end=' ')
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900607 sys.stdout.flush()
608
Simon Glass44116332017-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 Rini3c5f4152019-09-20 17:42:09 -0400619 self.conf = kconfiglib.Kconfig()
Simon Glass44116332017-06-15 21:39:33 -0600620
621
Masahiro Yamadab6160812015-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 Glassd9c1da22021-12-18 14:54:31 -0700629 def __init__(self, configs, args, build_dir):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900630 """Create a new parser.
631
Simon Glassb3464eb2021-12-18 14:54:35 -0700632 Args:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900633 configs: A list of CONFIGs to move.
Simon Glassb3464eb2021-12-18 14:54:35 -0700634 args (Namespace): program arguments
Masahiro Yamadab6160812015-05-20 11:36:07 +0900635 build_dir: Build directory.
636 """
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900637 self.configs = configs
Simon Glassd9c1da22021-12-18 14:54:31 -0700638 self.args = args
Masahiro Yamada5393b612016-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 Yamada6d139172016-08-22 22:18:22 +0900641 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
642 'autoconf.mk')
Simon Glass8fb5bd02017-06-01 19:39:01 -0600643 self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
Masahiro Yamada07f98522016-05-19 15:52:06 +0900644 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamadab6160812015-05-20 11:36:07 +0900645
Simon Glass257f5232017-07-10 14:47:47 -0600646 def get_arch(self):
647 """Parse .config file and return the architecture.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900648
649 Returns:
Simon Glass257f5232017-07-10 14:47:47 -0600650 Architecture name (e.g. 'arm').
Masahiro Yamadab6160812015-05-20 11:36:07 +0900651 """
652 arch = ''
653 cpu = ''
Simon Glassaba238f2021-12-18 14:54:34 -0700654 for line in read_file(self.dotconfig):
Masahiro Yamadab6160812015-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 Yamadac4d76eb2016-05-19 15:51:53 +0900663 if not arch:
664 return None
Masahiro Yamadab6160812015-05-20 11:36:07 +0900665
666 # fix-up for aarch64
667 if arch == 'arm' and cpu == 'armv8':
668 arch = 'aarch64'
669
Simon Glass257f5232017-07-10 14:47:47 -0600670 return arch
Masahiro Yamadab6160812015-05-20 11:36:07 +0900671
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900672 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamadab6160812015-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 Glassb3464eb2021-12-18 14:54:35 -0700679 Args:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900680 config: CONFIG name to parse.
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900681 dotconfig_lines: lines from the .config file.
Masahiro Yamadab6160812015-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 Yamadab6160812015-05-20 11:36:07 +0900688 not_set = '# %s is not set' % config
689
Masahiro Yamadab6160812015-05-20 11:36:07 +0900690 for line in autoconf_lines:
691 line = line.rstrip()
692 if line.startswith(config + '='):
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900693 new_val = line
Masahiro Yamadab6160812015-05-20 11:36:07 +0900694 break
Masahiro Yamadab6160812015-05-20 11:36:07 +0900695 else:
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900696 new_val = not_set
697
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200698 new_val = try_expand(new_val)
699
Masahiro Yamada35204d92016-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 Yamada5643d6e2016-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 Yamadab48387f2016-06-15 14:33:50 +0900719 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
720 new_val)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900721
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900722 def update_dotconfig(self):
Masahiro Yamada7c0d9d22016-05-19 15:51:50 +0900723 """Parse files for the config options and update the .config.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900724
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900725 This function parses the generated .config and include/autoconf.mk
726 searching the target options.
Masahiro Yamada7c0d9d22016-05-19 15:51:50 +0900727 Move the config option(s) to the .config as needed.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900728
Simon Glassb3464eb2021-12-18 14:54:35 -0700729 Args:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900730 defconfig: defconfig name.
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900731
732 Returns:
Masahiro Yamada263d1372016-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 Yamadab6160812015-05-20 11:36:07 +0900736 """
737
Masahiro Yamadab6160812015-05-20 11:36:07 +0900738 results = []
Masahiro Yamada263d1372016-05-19 15:52:04 +0900739 updated = False
Masahiro Yamada35204d92016-08-22 22:18:21 +0900740 suspicious = False
Masahiro Yamada6d139172016-08-22 22:18:22 +0900741 rm_files = [self.config_autoconf, self.autoconf]
742
Simon Glassd9c1da22021-12-18 14:54:31 -0700743 if self.args.spl:
Masahiro Yamada6d139172016-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 Glassd9c1da22021-12-18 14:54:31 -0700751 color_text(self.args.color, COLOR_BROWN,
Masahiro Yamada6d139172016-08-22 22:18:22 +0900752 "SPL is not enabled. Skipped.") + '\n')
753 else:
754 autoconf_path = self.autoconf
Masahiro Yamadab6160812015-05-20 11:36:07 +0900755
Simon Glassaba238f2021-12-18 14:54:34 -0700756 dotconfig_lines = read_file(self.dotconfig)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900757
Simon Glassaba238f2021-12-18 14:54:34 -0700758 autoconf_lines = read_file(autoconf_path)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900759
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900760 for config in self.configs:
761 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger166edec2015-05-19 13:21:17 -0500762 autoconf_lines)
Masahiro Yamadab6160812015-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 Yamada5643d6e2016-05-19 15:51:56 +0900771 elif action == ACTION_NO_ENTRY:
Simon Glassdc634d92021-12-18 14:54:30 -0700772 actlog = '%s is not defined in Kconfig. Do nothing.' % value
Masahiro Yamadab6160812015-05-20 11:36:07 +0900773 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada35204d92016-08-22 22:18:21 +0900774 elif action == ACTION_NO_ENTRY_WARN:
Simon Glassdc634d92021-12-18 14:54:30 -0700775 actlog = '%s is not defined in Kconfig (suspicious). Do nothing.' % value
Masahiro Yamada35204d92016-08-22 22:18:21 +0900776 log_color = COLOR_YELLOW
777 suspicious = True
Masahiro Yamada5643d6e2016-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 Yamadab6160812015-05-20 11:36:07 +0900781 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamadab6160812015-05-20 11:36:07 +0900782 else:
Simon Glassdc634d92021-12-18 14:54:30 -0700783 sys.exit('Internal Error. This should not happen.')
Masahiro Yamadab6160812015-05-20 11:36:07 +0900784
Simon Glassd9c1da22021-12-18 14:54:31 -0700785 log += color_text(self.args.color, log_color, actlog) + '\n'
Masahiro Yamadab6160812015-05-20 11:36:07 +0900786
Simon Glassb3464eb2021-12-18 14:54:35 -0700787 with open(self.dotconfig, 'a', encoding='utf-8') as out:
Masahiro Yamada953d93b2016-05-19 15:51:49 +0900788 for (action, value) in results:
789 if action == ACTION_MOVE:
Simon Glassb3464eb2021-12-18 14:54:35 -0700790 out.write(value + '\n')
Masahiro Yamada263d1372016-05-19 15:52:04 +0900791 updated = True
Masahiro Yamadab6160812015-05-20 11:36:07 +0900792
Masahiro Yamada07f98522016-05-19 15:52:06 +0900793 self.results = results
Masahiro Yamada6d139172016-08-22 22:18:22 +0900794 for f in rm_files:
795 os.remove(f)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900796
Masahiro Yamada35204d92016-08-22 22:18:21 +0900797 return (updated, suspicious, log)
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900798
Masahiro Yamada07f98522016-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 Glassaba238f2021-12-18 14:54:34 -0700809 defconfig_lines = read_file(self.defconfig)
Masahiro Yamada07f98522016-05-19 15:52:06 +0900810
811 for (action, value) in self.results:
812 if action != ACTION_MOVE:
813 continue
Alper Nebi Yasakcab84072022-01-29 18:22:08 +0300814 if not value in defconfig_lines:
Simon Glassd9c1da22021-12-18 14:54:31 -0700815 log += color_text(self.args.color, COLOR_YELLOW,
Masahiro Yamada07f98522016-05-19 15:52:06 +0900816 "'%s' was removed by savedefconfig.\n" %
817 value)
818
819 return log
820
Simon Glass43cf08f2017-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 Yamadab6160812015-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 Glassd9c1da22021-12-18 14:54:31 -0700858 def __init__(self, toolchains, configs, args, progress, devnull,
Simon Glass257f5232017-07-10 14:47:47 -0600859 make_cmd, reference_src_dir, db_queue):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900860 """Create a new process slot.
861
Simon Glassb3464eb2021-12-18 14:54:35 -0700862 Args:
Simon Glass257f5232017-07-10 14:47:47 -0600863 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900864 configs: A list of CONFIGs to move.
Simon Glassd9c1da22021-12-18 14:54:31 -0700865 args: Program arguments
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900866 progress: A progress indicator.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900867 devnull: A file object of '/dev/null'.
868 make_cmd: command name of GNU Make.
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500869 reference_src_dir: Determine the true starting config state from this
870 source tree.
Simon Glass43cf08f2017-06-01 19:39:02 -0600871 db_queue: output queue to write config info for the database
Masahiro Yamadab6160812015-05-20 11:36:07 +0900872 """
Simon Glass257f5232017-07-10 14:47:47 -0600873 self.toolchains = toolchains
Simon Glassd9c1da22021-12-18 14:54:31 -0700874 self.args = args
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900875 self.progress = progress
Masahiro Yamadab6160812015-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 Hershbergerb1a570f2016-06-10 14:53:32 -0500879 self.reference_src_dir = reference_src_dir
Simon Glass43cf08f2017-06-01 19:39:02 -0600880 self.db_queue = db_queue
Simon Glassd9c1da22021-12-18 14:54:31 -0700881 self.parser = KconfigParser(configs, args, self.build_dir)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900882 self.state = STATE_IDLE
Masahiro Yamada1271b672016-08-22 22:18:20 +0900883 self.failed_boards = set()
884 self.suspicious_boards = set()
Masahiro Yamadab6160812015-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 Hershberger640de872016-06-10 14:53:29 -0500891 because it is guaranteed the destructor is always invoked when the
Masahiro Yamadab6160812015-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 Yamadacefaa582016-05-19 15:51:55 +0900901 def add(self, defconfig):
Masahiro Yamadab6160812015-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 Glassb3464eb2021-12-18 14:54:35 -0700908 Args:
Masahiro Yamadab6160812015-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 Yamadacb256cb2016-06-08 11:47:37 +0900916
Masahiro Yamadab6160812015-05-20 11:36:07 +0900917 self.defconfig = defconfig
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900918 self.log = ''
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900919 self.current_src_dir = self.reference_src_dir
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900920 self.do_defconfig()
Masahiro Yamadab6160812015-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 Yamada263d1372016-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 Yamadab6160812015-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 Yamadacb256cb2016-06-08 11:47:37 +0900946 self.handle_error()
947 elif self.state == STATE_DEFCONFIG:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900948 if self.reference_src_dir and not self.current_src_dir:
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500949 self.do_savedefconfig()
950 else:
951 self.do_autoconf()
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900952 elif self.state == STATE_AUTOCONF:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900953 if self.current_src_dir:
954 self.current_src_dir = None
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500955 self.do_defconfig()
Simon Glassd9c1da22021-12-18 14:54:31 -0700956 elif self.args.build_db:
Simon Glass43cf08f2017-06-01 19:39:02 -0600957 self.do_build_db()
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500958 else:
959 self.do_savedefconfig()
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900960 elif self.state == STATE_SAVEDEFCONFIG:
961 self.update_defconfig()
962 else:
Simon Glassdc634d92021-12-18 14:54:30 -0700963 sys.exit('Internal Error. This should not happen.')
Masahiro Yamadab6160812015-05-20 11:36:07 +0900964
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900965 return True if self.state == STATE_IDLE else False
Joe Hershberger166edec2015-05-19 13:21:17 -0500966
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900967 def handle_error(self):
968 """Handle error cases."""
Masahiro Yamada83c17672016-05-19 15:52:08 +0900969
Simon Glassd9c1da22021-12-18 14:54:31 -0700970 self.log += color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glassdc634d92021-12-18 14:54:30 -0700971 'Failed to process.\n')
Simon Glassd9c1da22021-12-18 14:54:31 -0700972 if self.args.verbose:
973 self.log += color_text(self.args.color, COLOR_LIGHT_CYAN,
Markus Klotzbuecher8773f352020-02-12 20:46:45 +0100974 self.ps.stderr.read().decode())
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900975 self.finish(False)
Joe Hershberger166edec2015-05-19 13:21:17 -0500976
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900977 def do_defconfig(self):
978 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamada0f6beda2016-05-19 15:52:07 +0900979
Masahiro Yamadacb256cb2016-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 Yamada8f5256a2016-06-15 14:33:52 +0900983 stderr=subprocess.PIPE,
984 cwd=self.current_src_dir)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900985 self.state = STATE_DEFCONFIG
Masahiro Yamada0f6beda2016-05-19 15:52:07 +0900986
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900987 def do_autoconf(self):
Simon Glass8fb5bd02017-06-01 19:39:01 -0600988 """Run 'make AUTO_CONF_PATH'."""
Masahiro Yamadab6160812015-05-20 11:36:07 +0900989
Simon Glass257f5232017-07-10 14:47:47 -0600990 arch = self.parser.get_arch()
991 try:
992 toolchain = self.toolchains.Select(arch)
993 except ValueError:
Simon Glassd9c1da22021-12-18 14:54:31 -0700994 self.log += color_text(self.args.color, COLOR_YELLOW,
Chris Packham1ebcbd12017-08-27 20:00:51 +1200995 "Tool chain for '%s' is missing. Do nothing.\n" % arch)
Masahiro Yamada274a5ee2016-05-19 15:52:03 +0900996 self.finish(False)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900997 return
Simon Glass1f701862019-10-31 07:42:57 -0600998 env = toolchain.MakeEnvironment(False)
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900999
Masahiro Yamadab6160812015-05-20 11:36:07 +09001000 cmd = list(self.make_cmd)
Joe Hershberger765442b2015-05-19 13:21:18 -05001001 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Simon Glass8fb5bd02017-06-01 19:39:01 -06001002 cmd.append(AUTO_CONF_PATH)
Simon Glass257f5232017-07-10 14:47:47 -06001003 self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
Masahiro Yamada8f5256a2016-06-15 14:33:52 +09001004 stderr=subprocess.PIPE,
1005 cwd=self.current_src_dir)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001006 self.state = STATE_AUTOCONF
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001007
Simon Glass43cf08f2017-06-01 19:39:02 -06001008 def do_build_db(self):
1009 """Add the board to the database"""
1010 configs = {}
Simon Glassaba238f2021-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 Glass43cf08f2017-06-01 19:39:02 -06001015 self.db_queue.put([self.defconfig, configs])
1016 self.finish(True)
1017
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001018 def do_savedefconfig(self):
1019 """Update the .config and run 'make savedefconfig'."""
1020
Masahiro Yamada35204d92016-08-22 22:18:21 +09001021 (updated, suspicious, log) = self.parser.update_dotconfig()
1022 if suspicious:
1023 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001024 self.log += log
1025
Simon Glassd9c1da22021-12-18 14:54:31 -07001026 if not self.args.force_sync and not updated:
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001027 self.finish(True)
1028 return
1029 if updated:
Simon Glassd9c1da22021-12-18 14:54:31 -07001030 self.log += color_text(self.args.color, COLOR_LIGHT_GREEN,
Simon Glassdc634d92021-12-18 14:54:30 -07001031 'Syncing by savedefconfig...\n')
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001032 else:
Simon Glassdc634d92021-12-18 14:54:30 -07001033 self.log += 'Syncing by savedefconfig (forced by option)...\n'
Masahiro Yamadacb256cb2016-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 Yamada3c9bfea2016-06-15 14:33:54 +09001044 log = self.parser.check_defconfig()
1045 if log:
Masahiro Yamada1271b672016-08-22 22:18:20 +09001046 self.suspicious_boards.add(self.defconfig)
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001047 self.log += log
Masahiro Yamadacb256cb2016-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 Glassd9c1da22021-12-18 14:54:31 -07001053 self.log += color_text(self.args.color, COLOR_LIGHT_BLUE,
Simon Glassdc634d92021-12-18 14:54:30 -07001054 'defconfig was updated.\n')
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001055
Simon Glassd9c1da22021-12-18 14:54:31 -07001056 if not self.args.dry_run and updated:
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001057 shutil.move(new_defconfig, orig_defconfig)
1058 self.finish(True)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001059
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001060 def finish(self, success):
1061 """Display log along with progress and go to the idle state.
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001062
Simon Glassb3464eb2021-12-18 14:54:35 -07001063 Args:
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001064 success: Should be True when the defconfig was processed
1065 successfully, or False when it fails.
Masahiro Yamada465b7c02016-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 Glass1f701862019-10-31 07:42:57 -06001073 print(log, file=(sys.stdout if success else sys.stderr))
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001074
1075 if not success:
Simon Glassd9c1da22021-12-18 14:54:31 -07001076 if self.args.exit_on_error:
Simon Glassdc634d92021-12-18 14:54:30 -07001077 sys.exit('Exit on error.')
Masahiro Yamada274a5ee2016-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 Yamada1271b672016-08-22 22:18:20 +09001080 self.failed_boards.add(self.defconfig)
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001081
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001082 self.progress.inc()
1083 self.progress.show()
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001084 self.state = STATE_IDLE
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001085
Masahiro Yamadab6160812015-05-20 11:36:07 +09001086 def get_failed_boards(self):
Masahiro Yamada1271b672016-08-22 22:18:20 +09001087 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamadab6160812015-05-20 11:36:07 +09001088 """
1089 return self.failed_boards
1090
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001091 def get_suspicious_boards(self):
Masahiro Yamada1271b672016-08-22 22:18:20 +09001092 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001093 """
Masahiro Yamada35204d92016-08-22 22:18:21 +09001094 return self.suspicious_boards - self.failed_boards
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001095
Masahiro Yamadab6160812015-05-20 11:36:07 +09001096class Slots:
1097
1098 """Controller of the array of subprocess slots."""
1099
Simon Glassd9c1da22021-12-18 14:54:31 -07001100 def __init__(self, toolchains, configs, args, progress,
Simon Glass257f5232017-07-10 14:47:47 -06001101 reference_src_dir, db_queue):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001102 """Create a new slots controller.
1103
Simon Glassb3464eb2021-12-18 14:54:35 -07001104 Args:
Simon Glass257f5232017-07-10 14:47:47 -06001105 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001106 configs: A list of CONFIGs to move.
Simon Glassd9c1da22021-12-18 14:54:31 -07001107 args: Program arguments
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001108 progress: A progress indicator.
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001109 reference_src_dir: Determine the true starting config state from this
1110 source tree.
Simon Glass43cf08f2017-06-01 19:39:02 -06001111 db_queue: output queue to write config info for the database
Masahiro Yamadab6160812015-05-20 11:36:07 +09001112 """
Simon Glassd9c1da22021-12-18 14:54:31 -07001113 self.args = args
Masahiro Yamadab6160812015-05-20 11:36:07 +09001114 self.slots = []
Simon Glass34c505f2021-12-18 14:54:32 -07001115 devnull = subprocess.DEVNULL
Masahiro Yamadab6160812015-05-20 11:36:07 +09001116 make_cmd = get_make_cmd()
Simon Glassd9c1da22021-12-18 14:54:31 -07001117 for i in range(args.jobs):
1118 self.slots.append(Slot(toolchains, configs, args, progress,
Simon Glass257f5232017-07-10 14:47:47 -06001119 devnull, make_cmd, reference_src_dir,
1120 db_queue))
Masahiro Yamadab6160812015-05-20 11:36:07 +09001121
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001122 def add(self, defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001123 """Add a new subprocess if a vacant slot is found.
1124
Simon Glassb3464eb2021-12-18 14:54:35 -07001125 Args:
Masahiro Yamadab6160812015-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 Yamadacefaa582016-05-19 15:51:55 +09001132 if slot.add(defconfig):
Masahiro Yamadab6160812015-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 Yamada1271b672016-08-22 22:18:20 +09001161 boards = set()
Masahiro Yamada0153f032016-06-15 14:33:53 +09001162 output_file = 'moveconfig.failed'
Masahiro Yamadab6160812015-05-20 11:36:07 +09001163
1164 for slot in self.slots:
Masahiro Yamada1271b672016-08-22 22:18:20 +09001165 boards |= slot.get_failed_boards()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001166
Masahiro Yamada0153f032016-06-15 14:33:53 +09001167 if boards:
1168 boards = '\n'.join(boards) + '\n'
Simon Glassdc634d92021-12-18 14:54:30 -07001169 msg = 'The following boards were not processed due to error:\n'
Masahiro Yamada0153f032016-06-15 14:33:53 +09001170 msg += boards
Simon Glassdc634d92021-12-18 14:54:30 -07001171 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassd9c1da22021-12-18 14:54:31 -07001172 print(color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glass1f701862019-10-31 07:42:57 -06001173 msg), file=sys.stderr)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001174
Simon Glassb09ae452021-12-18 14:54:33 -07001175 write_file(output_file, boards)
Joe Hershbergerdade12e2015-05-19 13:21:22 -05001176
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001177 def show_suspicious_boards(self):
1178 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada1271b672016-08-22 22:18:20 +09001179 boards = set()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001180 output_file = 'moveconfig.suspicious'
1181
1182 for slot in self.slots:
Masahiro Yamada1271b672016-08-22 22:18:20 +09001183 boards |= slot.get_suspicious_boards()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001184
1185 if boards:
1186 boards = '\n'.join(boards) + '\n'
Simon Glassdc634d92021-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 Yamada3c9bfea2016-06-15 14:33:54 +09001189 msg += boards
Simon Glassdc634d92021-12-18 14:54:30 -07001190 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassd9c1da22021-12-18 14:54:31 -07001191 print(color_text(self.args.color, COLOR_YELLOW,
Simon Glass1f701862019-10-31 07:42:57 -06001192 msg), file=sys.stderr)
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001193
Simon Glassb09ae452021-12-18 14:54:33 -07001194 write_file(output_file, boards)
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001195
Masahiro Yamada2e74fee2016-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 Glassb3464eb2021-12-18 14:54:35 -07001203 Args:
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001204 commit: commit to git-clone
1205 """
1206 self.src_dir = tempfile.mkdtemp()
Simon Glassdc634d92021-12-18 14:54:30 -07001207 print('Cloning git repo to a separate work directory...')
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001208 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1209 cwd=self.src_dir)
Simon Glass1f701862019-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 Yamada2e74fee2016-06-15 14:33:51 +09001212 subprocess.check_output(['git', 'checkout', commit],
1213 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001214
1215 def __del__(self):
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001216 """Delete the reference source directory
Joe Hershbergerb1a570f2016-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 Yamada2e74fee2016-06-15 14:33:51 +09001223 shutil.rmtree(self.src_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001224
Masahiro Yamada2e74fee2016-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 Hershbergerb1a570f2016-06-10 14:53:32 -05001229
Simon Glassd9c1da22021-12-18 14:54:31 -07001230def move_config(toolchains, configs, args, db_queue):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001231 """Move config options to defconfig files.
1232
Simon Glassb3464eb2021-12-18 14:54:35 -07001233 Args:
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001234 configs: A list of CONFIGs to move.
Simon Glassd9c1da22021-12-18 14:54:31 -07001235 args: Program arguments
Masahiro Yamadab6160812015-05-20 11:36:07 +09001236 """
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001237 if len(configs) == 0:
Simon Glassd9c1da22021-12-18 14:54:31 -07001238 if args.force_sync:
Simon Glass1f701862019-10-31 07:42:57 -06001239 print('No CONFIG is specified. You are probably syncing defconfigs.', end=' ')
Simon Glassd9c1da22021-12-18 14:54:31 -07001240 elif args.build_db:
Simon Glass1f701862019-10-31 07:42:57 -06001241 print('Building %s database' % CONFIG_DATABASE)
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001242 else:
Simon Glass1f701862019-10-31 07:42:57 -06001243 print('Neither CONFIG nor --force-sync is specified. Nothing will happen.', end=' ')
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001244 else:
Simon Glass1f701862019-10-31 07:42:57 -06001245 print('Move ' + ', '.join(configs), end=' ')
Simon Glassd9c1da22021-12-18 14:54:31 -07001246 print('(jobs: %d)\n' % args.jobs)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001247
Simon Glassd9c1da22021-12-18 14:54:31 -07001248 if args.git_ref:
1249 reference_src = ReferenceSource(args.git_ref)
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001250 reference_src_dir = reference_src.get_dir()
1251 else:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +09001252 reference_src_dir = None
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001253
Simon Glassd9c1da22021-12-18 14:54:31 -07001254 if args.defconfigs:
1255 defconfigs = get_matched_defconfigs(args.defconfigs)
Joe Hershbergerc6e043a2015-05-19 13:21:19 -05001256 else:
Masahiro Yamada58175e32016-07-25 19:15:28 +09001257 defconfigs = get_all_defconfigs()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001258
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001259 progress = Progress(len(defconfigs))
Simon Glassd9c1da22021-12-18 14:54:31 -07001260 slots = Slots(toolchains, configs, args, progress, reference_src_dir,
Simon Glassb3464eb2021-12-18 14:54:35 -07001261 db_queue)
Masahiro Yamadab6160812015-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 Yamadacefaa582016-05-19 15:51:55 +09001266 for defconfig in defconfigs:
1267 while not slots.add(defconfig):
Masahiro Yamadab6160812015-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 Glass1f701862019-10-31 07:42:57 -06001276 print('')
Masahiro Yamadab6160812015-05-20 11:36:07 +09001277 slots.show_failed_boards()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001278 slots.show_suspicious_boards()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001279
Simon Glass44116332017-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 Rini3c5f4152019-09-20 17:42:09 -04001284 kconf: Kconfiglib.Kconfig object
Simon Glass44116332017-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 Rini3c5f4152019-09-20 17:42:09 -04001292 sym = kconf.syms.get(imply_config)
Simon Glass44116332017-06-15 21:39:33 -06001293 if sym:
Simon Glass520b47a2021-07-21 21:35:53 -06001294 for sel, cond in (sym.selects + sym.implies):
Simon Glass93c0a9e2021-12-18 08:09:42 -07001295 if sel.name == config:
Simon Glass44116332017-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 Rini3c5f4152019-09-20 17:42:09 -04001306 kconf: Kconfiglib.Kconfig object
Simon Glass44116332017-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 Rini3c5f4152019-09-20 17:42:09 -04001317 sym = kconf.syms.get(imply_config)
Simon Glass44116332017-06-15 21:39:33 -06001318 if not sym:
1319 return 'cannot find sym'
Simon Glass520b47a2021-07-21 21:35:53 -06001320 nodes = sym.nodes
1321 if len(nodes) != 1:
1322 return '%d locations' % len(nodes)
Simon Glass93c0a9e2021-12-18 08:09:42 -07001323 node = nodes[0]
1324 fname, linenum = node.filename, node.linenr
Simon Glass44116332017-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 Glassaba238f2021-12-18 14:54:34 -07001329 data = read_file(fname)
Simon Glass44116332017-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 Glassaba238f2021-12-18 14:54:34 -07001346 data = read_file(fname)
Simon Glass44116332017-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 Glassb09ae452021-12-18 14:54:33 -07001352 write_file(fname, data)
Simon Glass44116332017-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 Glass92e55582017-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 Glass44116332017-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 Glassb3464eb2021-12-18 14:54:35 -07001367}
Simon Glass92e55582017-06-15 21:39:32 -06001368
Simon Glassf931c2f2021-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 Glassaba238f2021-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 Glassf931c2f2021-12-18 08:09:43 -07001412
1413 return all_configs, all_defconfigs, config_db, defconfig_db
1414
1415
Simon Glass44116332017-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 Glassc6e73cf2017-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 Glass44116332017-06-15 21:39:33 -06001442 add_imply: Automatically add an 'imply' for each config.
Simon Glass92e55582017-06-15 21:39:32 -06001443 imply_flags: Flags which control which implying configs are allowed
1444 (IMPLY_...)
Simon Glass44116332017-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 Glassc6e73cf2017-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 Glass44116332017-06-15 21:39:33 -06001458 kconf = KconfigScanner().conf if check_kconfig else None
1459 if add_imply and add_imply != 'all':
Simon Glass93c0a9e2021-12-18 08:09:42 -07001460 add_imply = add_imply.split(',')
Simon Glass44116332017-06-15 21:39:33 -06001461
Simon Glassf931c2f2021-12-18 08:09:43 -07001462 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
Simon Glassc6e73cf2017-06-01 19:39:03 -06001463
Simon Glass93c0a9e2021-12-18 08:09:42 -07001464 # Work through each target config option in turn, independently
Simon Glassc6e73cf2017-06-01 19:39:03 -06001465 for config in config_list:
1466 defconfigs = defconfig_db.get(config)
1467 if not defconfigs:
Simon Glass1f701862019-10-31 07:42:57 -06001468 print('%s not found in any defconfig' % config)
Simon Glassc6e73cf2017-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 Glass1f701862019-10-31 07:42:57 -06001475 print('%s found in %d/%d defconfigs' % (config, num_defconfigs,
1476 len(all_configs)))
Simon Glassc6e73cf2017-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 Glass92e55582017-06-15 21:39:32 -06001484 if 'ERRATUM' in imply_config:
Simon Glassc6e73cf2017-06-01 19:39:03 -06001485 continue
Simon Glassb3464eb2021-12-18 14:54:35 -07001486 if not imply_flags & IMPLY_CMD:
Simon Glass92e55582017-06-15 21:39:32 -06001487 if 'CONFIG_CMD' in imply_config:
1488 continue
Simon Glassb3464eb2021-12-18 14:54:35 -07001489 if not imply_flags & IMPLY_TARGET:
Simon Glass92e55582017-06-15 21:39:32 -06001490 if 'CONFIG_TARGET' in imply_config:
1491 continue
Simon Glassc6e73cf2017-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 Glass1f701862019-10-31 07:42:57 -06001513 for prev in list(imply_configs.keys()):
Simon Glassc6e73cf2017-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 Glass44116332017-06-15 21:39:33 -06001532 ranked_iconfigs = sorted(imply_configs,
Simon Glassc6e73cf2017-06-01 19:39:03 -06001533 key=lambda k: len(imply_configs[k]), reverse=True)
Simon Glass44116332017-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 Glassc6e73cf2017-06-01 19:39:03 -06001539
1540 # Don't bother if there are less than 5 defconfigs affected.
Simon Glass92e55582017-06-15 21:39:32 -06001541 if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
Simon Glassc6e73cf2017-06-01 19:39:03 -06001542 continue
Simon Glass44116332017-06-15 21:39:33 -06001543 missing = defconfigs - imply_configs[iconfig]
Simon Glassc6e73cf2017-06-01 19:39:03 -06001544 missing_str = ', '.join(missing) if missing else 'all'
1545 missing_str = ''
Simon Glass44116332017-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 Glass520b47a2021-07-21 21:35:53 -06001552 nodes = sym.nodes
1553 if len(nodes) == 1:
1554 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glass44116332017-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 Rini3c5f4152019-09-20 17:42:09 -04001561 sym = kconf.syms.get(iconfig[CONFIG_LEN:])
Simon Glass44116332017-06-15 21:39:33 -06001562 fname = ''
1563 if sym:
Simon Glass520b47a2021-07-21 21:35:53 -06001564 nodes = sym.nodes
1565 if len(nodes) == 1:
1566 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glass44116332017-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 Glassb3464eb2021-12-18 14:54:35 -07001572 not imply_flags & IMPLY_NON_ARCH_BOARD):
Simon Glass44116332017-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)
Simon Glassc6e73cf2017-06-01 19:39:03 -06001581
Simon Glass44116332017-06-15 21:39:33 -06001582 if show and kconfig_info != 'skip':
Simon Glass1f701862019-10-31 07:42:57 -06001583 print('%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
1584 kconfig_info, missing_str))
Simon Glass44116332017-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 Glass1f701862019-10-31 07:42:57 -06001591 for fname, linenums in add_list.items():
Simon Glass44116332017-06-15 21:39:33 -06001592 for linenum in sorted(linenums, reverse=True):
1593 add_imply_rule(config[CONFIG_LEN:], fname, linenum)
1594
Simon Glass99f79422022-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 Glassfea71c92022-03-05 20:18:54 -07001610 if re_match.fullmatch(cfg):
Simon Glass99f79422022-02-08 11:49:46 -07001611 return True
1612 return False
Simon Glassc6e73cf2017-06-01 19:39:03 -06001613
Simon Glass0082b2e2021-12-18 08:09:46 -07001614def do_find_config(config_list):
1615 """Find boards with a given combination of CONFIGs
1616
1617 Params:
Simon Glass99f79422022-02-08 11:49:46 -07001618 config_list: List of CONFIG options to check (each a regex consisting
Simon Glass0082b2e2021-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 Glassaba238f2021-12-18 14:54:34 -07001626 adhoc_configs = set(read_file('scripts/config_whitelist.txt'))
Simon Glass0082b2e2021-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 Glass99f79422022-02-08 11:49:46 -07001650 re_match = re.compile(cfg)
Simon Glass0082b2e2021-12-18 08:09:46 -07001651 for defc in in_list:
Simon Glass99f79422022-02-08 11:49:46 -07001652 has_cfg = defconfig_matches(config_db[defc], re_match)
Simon Glass0082b2e2021-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 Glass51aa73b2022-03-05 20:18:53 -07001659 print(' '.join(item.split('_defconfig')[0] for item in out))
Simon Glass0082b2e2021-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 Yamadab6160812015-05-20 11:36:07 +09001687def main():
1688 try:
1689 cpu_count = multiprocessing.cpu_count()
1690 except NotImplementedError:
1691 cpu_count = 1
1692
Simon Glassd9c1da22021-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 Glass44116332017-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 Glassd9c1da22021-12-18 14:54:31 -07001701 parser.add_argument('-A', '--skip-added', action='store_true', default=False,
Simon Glass44116332017-06-15 21:39:33 -06001702 help="don't show options which are already marked as "
1703 'implying others')
Simon Glassd9c1da22021-12-18 14:54:31 -07001704 parser.add_argument('-b', '--build-db', action='store_true', default=False,
Simon Glass43cf08f2017-06-01 19:39:02 -06001705 help='build a CONFIG database')
Simon Glassd9c1da22021-12-18 14:54:31 -07001706 parser.add_argument('-c', '--color', action='store_true', default=False,
Masahiro Yamadab6160812015-05-20 11:36:07 +09001707 help='display the log in color')
Simon Glassd9c1da22021-12-18 14:54:31 -07001708 parser.add_argument('-C', '--commit', action='store_true', default=False,
Simon Glass8bf41c22016-09-12 23:18:21 -06001709 help='Create a git commit for the operation')
Simon Glassd9c1da22021-12-18 14:54:31 -07001710 parser.add_argument('-d', '--defconfigs', type=str,
Simon Glass8f3cf312017-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 Glassd9c1da22021-12-18 14:54:31 -07001714 parser.add_argument('-e', '--exit-on-error', action='store_true',
Masahiro Yamadab6160812015-05-20 11:36:07 +09001715 default=False,
1716 help='exit immediately on any error')
Simon Glassd9c1da22021-12-18 14:54:31 -07001717 parser.add_argument('-f', '--find', action='store_true', default=False,
Simon Glass0082b2e2021-12-18 08:09:46 -07001718 help='Find boards with a given config combination')
Simon Glassd9c1da22021-12-18 14:54:31 -07001719 parser.add_argument('-H', '--headers-only', dest='cleanup_headers_only',
Joe Hershberger23475932015-05-19 13:21:20 -05001720 action='store_true', default=False,
1721 help='only cleanup the headers')
Simon Glassd9c1da22021-12-18 14:54:31 -07001722 parser.add_argument('-i', '--imply', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001723 help='find options which imply others')
Simon Glassd9c1da22021-12-18 14:54:31 -07001724 parser.add_argument('-I', '--imply-flags', type=str, default='',
Simon Glass0559a742021-12-18 08:09:44 -07001725 help="control the -i option ('help' for help")
Simon Glassd9c1da22021-12-18 14:54:31 -07001726 parser.add_argument('-j', '--jobs', type=int, default=cpu_count,
Masahiro Yamadab6160812015-05-20 11:36:07 +09001727 help='the number of jobs to run simultaneously')
Simon Glassd9c1da22021-12-18 14:54:31 -07001728 parser.add_argument('-n', '--dry-run', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001729 help='perform a trial run (show log with no changes)')
Simon Glassd9c1da22021-12-18 14:54:31 -07001730 parser.add_argument('-r', '--git-ref', type=str,
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001731 help='the git ref to clone for building the autoconf.mk')
Simon Glassd9c1da22021-12-18 14:54:31 -07001732 parser.add_argument('-s', '--force-sync', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001733 help='force sync by savedefconfig')
Simon Glassd9c1da22021-12-18 14:54:31 -07001734 parser.add_argument('-S', '--spl', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001735 help='parse config options defined for SPL build')
Simon Glassd9c1da22021-12-18 14:54:31 -07001736 parser.add_argument('-t', '--test', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001737 help='run unit tests')
Simon Glassd9c1da22021-12-18 14:54:31 -07001738 parser.add_argument('-y', '--yes', action='store_true', default=False,
Simon Glass13e05a02016-09-12 23:18:20 -06001739 help="respond 'yes' to any prompts")
Simon Glassd9c1da22021-12-18 14:54:31 -07001740 parser.add_argument('-v', '--verbose', action='store_true', default=False,
Joe Hershberger808b63f2015-05-19 13:21:24 -05001741 help='show any build errors as boards are built')
Simon Glassd9c1da22021-12-18 14:54:31 -07001742 parser.add_argument('configs', nargs='*')
Masahiro Yamadab6160812015-05-20 11:36:07 +09001743
Simon Glassd9c1da22021-12-18 14:54:31 -07001744 args = parser.parse_args()
1745 configs = args.configs
Masahiro Yamadab6160812015-05-20 11:36:07 +09001746
Simon Glassd9c1da22021-12-18 14:54:31 -07001747 if args.test:
Simon Glassbb57be72021-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 Glassd9c1da22021-12-18 14:54:31 -07001754 if not any((len(configs), args.force_sync, args.build_db, args.imply,
1755 args.find)):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001756 parser.print_usage()
1757 sys.exit(1)
1758
Masahiro Yamadab903c4e2016-05-19 15:51:58 +09001759 # prefix the option name with CONFIG_ if missing
Simon Glass0082b2e2021-12-18 08:09:46 -07001760 configs = [prefix_config(cfg) for cfg in configs]
Masahiro Yamadab6160812015-05-20 11:36:07 +09001761
Joe Hershberger23475932015-05-19 13:21:20 -05001762 check_top_directory()
1763
Simon Glassd9c1da22021-12-18 14:54:31 -07001764 if args.imply:
Simon Glass92e55582017-06-15 21:39:32 -06001765 imply_flags = 0
Simon Glassd9c1da22021-12-18 14:54:31 -07001766 if args.imply_flags == 'all':
Simon Glass5f096922017-07-10 14:47:46 -06001767 imply_flags = -1
1768
Simon Glassd9c1da22021-12-18 14:54:31 -07001769 elif args.imply_flags:
1770 for flag in args.imply_flags.split(','):
Simon Glass5f096922017-07-10 14:47:46 -06001771 bad = flag not in IMPLY_FLAGS
1772 if bad:
Simon Glass1f701862019-10-31 07:42:57 -06001773 print("Invalid flag '%s'" % flag)
Simon Glass5f096922017-07-10 14:47:46 -06001774 if flag == 'help' or bad:
Simon Glass1f701862019-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 Glass5f096922017-07-10 14:47:46 -06001778 parser.print_usage()
1779 sys.exit(1)
1780 imply_flags |= IMPLY_FLAGS[flag][0]
Simon Glass92e55582017-06-15 21:39:32 -06001781
Simon Glassd9c1da22021-12-18 14:54:31 -07001782 do_imply_config(configs, args.add_imply, imply_flags, args.skip_added)
Simon Glassc6e73cf2017-06-01 19:39:03 -06001783 return
1784
Simon Glassd9c1da22021-12-18 14:54:31 -07001785 if args.find:
Simon Glass0082b2e2021-12-18 08:09:46 -07001786 do_find_config(configs)
1787 return
1788
Simon Glass43cf08f2017-06-01 19:39:02 -06001789 config_db = {}
Simon Glass1f701862019-10-31 07:42:57 -06001790 db_queue = queue.Queue()
Simon Glass43cf08f2017-06-01 19:39:02 -06001791 t = DatabaseThread(config_db, db_queue)
1792 t.setDaemon(True)
1793 t.start()
1794
Simon Glassd9c1da22021-12-18 14:54:31 -07001795 if not args.cleanup_headers_only:
Masahiro Yamadad0a9d2a2016-07-25 19:15:23 +09001796 check_clean_directory()
Simon Glass1f701862019-10-31 07:42:57 -06001797 bsettings.Setup('')
Simon Glass257f5232017-07-10 14:47:47 -06001798 toolchains = toolchain.Toolchains()
1799 toolchains.GetSettings()
1800 toolchains.Scan(verbose=False)
Simon Glassd9c1da22021-12-18 14:54:31 -07001801 move_config(toolchains, configs, args, db_queue)
Simon Glass43cf08f2017-06-01 19:39:02 -06001802 db_queue.join()
Joe Hershberger23475932015-05-19 13:21:20 -05001803
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001804 if configs:
Simon Glassd9c1da22021-12-18 14:54:31 -07001805 cleanup_headers(configs, args)
Tom Rinia23179e2022-04-02 18:18:57 -04001806 cleanup_extra_options(configs, args)
Simon Glassd9c1da22021-12-18 14:54:31 -07001807 cleanup_whitelist(configs, args)
1808 cleanup_readme(configs, args)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001809
Simon Glassd9c1da22021-12-18 14:54:31 -07001810 if args.commit:
Simon Glass8bf41c22016-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 Glassd9c1da22021-12-18 14:54:31 -07001822 if args.build_db:
Simon Glassb3464eb2021-12-18 14:54:35 -07001823 with open(CONFIG_DATABASE, 'w', encoding='utf-8') as fd:
Simon Glass1f701862019-10-31 07:42:57 -06001824 for defconfig, configs in config_db.items():
Simon Glass1c879312017-08-13 16:02:54 -06001825 fd.write('%s\n' % defconfig)
Simon Glass43cf08f2017-06-01 19:39:02 -06001826 for config in sorted(configs.keys()):
Simon Glass1c879312017-08-13 16:02:54 -06001827 fd.write(' %s=%s\n' % (config, configs[config]))
1828 fd.write('\n')
Simon Glass43cf08f2017-06-01 19:39:02 -06001829
Masahiro Yamadab6160812015-05-20 11:36:07 +09001830if __name__ == '__main__':
Simon Glass0082b2e2021-12-18 08:09:46 -07001831 sys.exit(main())