blob: 09617a07f91bc4e8bcec7b6d99e5601cf8104f30 [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
Simon Glassd9c1da22021-12-18 14:54:31 -0700446def cleanup_whitelist(configs, args):
Chris Packham9d5274f2017-05-02 21:30:47 +1200447 """Delete config whitelist entries
448
Simon Glassb3464eb2021-12-18 14:54:35 -0700449 Args:
Chris Packham9d5274f2017-05-02 21:30:47 +1200450 configs: A list of CONFIGs to remove.
Simon Glassb3464eb2021-12-18 14:54:35 -0700451 args (Namespace): program arguments
Chris Packham9d5274f2017-05-02 21:30:47 +1200452 """
Simon Glassd9c1da22021-12-18 14:54:31 -0700453 if not confirm(args, 'Clean up whitelist entries?'):
Chris Packham9d5274f2017-05-02 21:30:47 +1200454 return
455
Simon Glassaba238f2021-12-18 14:54:34 -0700456 lines = read_file(os.path.join('scripts', 'config_whitelist.txt'))
Chris Packham9d5274f2017-05-02 21:30:47 +1200457
458 lines = [x for x in lines if x.strip() not in configs]
459
Simon Glassb09ae452021-12-18 14:54:33 -0700460 write_file(os.path.join('scripts', 'config_whitelist.txt'), lines)
Chris Packham9d5274f2017-05-02 21:30:47 +1200461
Chris Packham0e6deff2017-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 Glassd9c1da22021-12-18 14:54:31 -0700468def cleanup_readme(configs, args):
Chris Packham0e6deff2017-05-02 21:30:48 +1200469 """Delete config description in README
470
Simon Glassb3464eb2021-12-18 14:54:35 -0700471 Args:
Chris Packham0e6deff2017-05-02 21:30:48 +1200472 configs: A list of CONFIGs to remove.
Simon Glassb3464eb2021-12-18 14:54:35 -0700473 args (Namespace): program arguments
Chris Packham0e6deff2017-05-02 21:30:48 +1200474 """
Simon Glassd9c1da22021-12-18 14:54:31 -0700475 if not confirm(args, 'Clean up README?'):
Chris Packham0e6deff2017-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 Glassaba238f2021-12-18 14:54:34 -0700482 lines = read_file('README')
Chris Packham0e6deff2017-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 Glassb09ae452021-12-18 14:54:33 -0700498 write_file('README', newlines)
Chris Packham0e6deff2017-05-02 21:30:48 +1200499
Markus Klotzbuecher3d5d4182019-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 Klotzbuecherbac0bb52020-02-12 20:46:44 +0100508 aeval = asteval.Interpreter( usersyms=SIZES, minimal=True )
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200509 cfg, val = re.split("=", line)
510 val= val.strip('\"')
Simon Glassdc634d92021-12-18 14:54:30 -0700511 if re.search(r'[*+-/]|<<|SZ_+|\(([^\)]+)\)', val):
Markus Klotzbuecherbac0bb52020-02-12 20:46:44 +0100512 newval = hex(aeval(val))
Simon Glassdc634d92021-12-18 14:54:30 -0700513 print('\tExpanded expression %s to %s' % (val, newval))
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200514 return cfg+'='+newval
515 except:
Simon Glassdc634d92021-12-18 14:54:30 -0700516 print('\tFailed to expand expression in %s' % line)
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200517
518 return line
519
Chris Packham9d5274f2017-05-02 21:30:47 +1200520
Masahiro Yamadab6160812015-05-20 11:36:07 +0900521### classes ###
Masahiro Yamadacefaa582016-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 Glassb3464eb2021-12-18 14:54:35 -0700529 Args:
Masahiro Yamadacefaa582016-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 Glass1f701862019-10-31 07:42:57 -0600542 print(' %d defconfigs out of %d\r' % (self.current, self.total), end=' ')
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900543 sys.stdout.flush()
544
Simon Glass44116332017-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 Rini3c5f4152019-09-20 17:42:09 -0400555 self.conf = kconfiglib.Kconfig()
Simon Glass44116332017-06-15 21:39:33 -0600556
557
Masahiro Yamadab6160812015-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 Glassd9c1da22021-12-18 14:54:31 -0700565 def __init__(self, configs, args, build_dir):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900566 """Create a new parser.
567
Simon Glassb3464eb2021-12-18 14:54:35 -0700568 Args:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900569 configs: A list of CONFIGs to move.
Simon Glassb3464eb2021-12-18 14:54:35 -0700570 args (Namespace): program arguments
Masahiro Yamadab6160812015-05-20 11:36:07 +0900571 build_dir: Build directory.
572 """
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900573 self.configs = configs
Simon Glassd9c1da22021-12-18 14:54:31 -0700574 self.args = args
Masahiro Yamada5393b612016-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 Yamada6d139172016-08-22 22:18:22 +0900577 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
578 'autoconf.mk')
Simon Glass8fb5bd02017-06-01 19:39:01 -0600579 self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
Masahiro Yamada07f98522016-05-19 15:52:06 +0900580 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamadab6160812015-05-20 11:36:07 +0900581
Simon Glass257f5232017-07-10 14:47:47 -0600582 def get_arch(self):
583 """Parse .config file and return the architecture.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900584
585 Returns:
Simon Glass257f5232017-07-10 14:47:47 -0600586 Architecture name (e.g. 'arm').
Masahiro Yamadab6160812015-05-20 11:36:07 +0900587 """
588 arch = ''
589 cpu = ''
Simon Glassaba238f2021-12-18 14:54:34 -0700590 for line in read_file(self.dotconfig):
Masahiro Yamadab6160812015-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 Yamadac4d76eb2016-05-19 15:51:53 +0900599 if not arch:
600 return None
Masahiro Yamadab6160812015-05-20 11:36:07 +0900601
602 # fix-up for aarch64
603 if arch == 'arm' and cpu == 'armv8':
604 arch = 'aarch64'
605
Simon Glass257f5232017-07-10 14:47:47 -0600606 return arch
Masahiro Yamadab6160812015-05-20 11:36:07 +0900607
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900608 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamadab6160812015-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 Glassb3464eb2021-12-18 14:54:35 -0700615 Args:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900616 config: CONFIG name to parse.
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900617 dotconfig_lines: lines from the .config file.
Masahiro Yamadab6160812015-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 Yamadab6160812015-05-20 11:36:07 +0900624 not_set = '# %s is not set' % config
625
Masahiro Yamadab6160812015-05-20 11:36:07 +0900626 for line in autoconf_lines:
627 line = line.rstrip()
628 if line.startswith(config + '='):
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900629 new_val = line
Masahiro Yamadab6160812015-05-20 11:36:07 +0900630 break
Masahiro Yamadab6160812015-05-20 11:36:07 +0900631 else:
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900632 new_val = not_set
633
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200634 new_val = try_expand(new_val)
635
Masahiro Yamada35204d92016-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 Yamada5643d6e2016-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 Yamadab48387f2016-06-15 14:33:50 +0900655 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
656 new_val)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900657
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900658 def update_dotconfig(self):
Masahiro Yamada7c0d9d22016-05-19 15:51:50 +0900659 """Parse files for the config options and update the .config.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900660
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900661 This function parses the generated .config and include/autoconf.mk
662 searching the target options.
Masahiro Yamada7c0d9d22016-05-19 15:51:50 +0900663 Move the config option(s) to the .config as needed.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900664
Simon Glassb3464eb2021-12-18 14:54:35 -0700665 Args:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900666 defconfig: defconfig name.
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900667
668 Returns:
Masahiro Yamada263d1372016-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 Yamadab6160812015-05-20 11:36:07 +0900672 """
673
Masahiro Yamadab6160812015-05-20 11:36:07 +0900674 results = []
Masahiro Yamada263d1372016-05-19 15:52:04 +0900675 updated = False
Masahiro Yamada35204d92016-08-22 22:18:21 +0900676 suspicious = False
Masahiro Yamada6d139172016-08-22 22:18:22 +0900677 rm_files = [self.config_autoconf, self.autoconf]
678
Simon Glassd9c1da22021-12-18 14:54:31 -0700679 if self.args.spl:
Masahiro Yamada6d139172016-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 Glassd9c1da22021-12-18 14:54:31 -0700687 color_text(self.args.color, COLOR_BROWN,
Masahiro Yamada6d139172016-08-22 22:18:22 +0900688 "SPL is not enabled. Skipped.") + '\n')
689 else:
690 autoconf_path = self.autoconf
Masahiro Yamadab6160812015-05-20 11:36:07 +0900691
Simon Glassaba238f2021-12-18 14:54:34 -0700692 dotconfig_lines = read_file(self.dotconfig)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900693
Simon Glassaba238f2021-12-18 14:54:34 -0700694 autoconf_lines = read_file(autoconf_path)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900695
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900696 for config in self.configs:
697 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger166edec2015-05-19 13:21:17 -0500698 autoconf_lines)
Masahiro Yamadab6160812015-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 Yamada5643d6e2016-05-19 15:51:56 +0900707 elif action == ACTION_NO_ENTRY:
Simon Glassdc634d92021-12-18 14:54:30 -0700708 actlog = '%s is not defined in Kconfig. Do nothing.' % value
Masahiro Yamadab6160812015-05-20 11:36:07 +0900709 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada35204d92016-08-22 22:18:21 +0900710 elif action == ACTION_NO_ENTRY_WARN:
Simon Glassdc634d92021-12-18 14:54:30 -0700711 actlog = '%s is not defined in Kconfig (suspicious). Do nothing.' % value
Masahiro Yamada35204d92016-08-22 22:18:21 +0900712 log_color = COLOR_YELLOW
713 suspicious = True
Masahiro Yamada5643d6e2016-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 Yamadab6160812015-05-20 11:36:07 +0900717 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamadab6160812015-05-20 11:36:07 +0900718 else:
Simon Glassdc634d92021-12-18 14:54:30 -0700719 sys.exit('Internal Error. This should not happen.')
Masahiro Yamadab6160812015-05-20 11:36:07 +0900720
Simon Glassd9c1da22021-12-18 14:54:31 -0700721 log += color_text(self.args.color, log_color, actlog) + '\n'
Masahiro Yamadab6160812015-05-20 11:36:07 +0900722
Simon Glassb3464eb2021-12-18 14:54:35 -0700723 with open(self.dotconfig, 'a', encoding='utf-8') as out:
Masahiro Yamada953d93b2016-05-19 15:51:49 +0900724 for (action, value) in results:
725 if action == ACTION_MOVE:
Simon Glassb3464eb2021-12-18 14:54:35 -0700726 out.write(value + '\n')
Masahiro Yamada263d1372016-05-19 15:52:04 +0900727 updated = True
Masahiro Yamadab6160812015-05-20 11:36:07 +0900728
Masahiro Yamada07f98522016-05-19 15:52:06 +0900729 self.results = results
Masahiro Yamada6d139172016-08-22 22:18:22 +0900730 for f in rm_files:
731 os.remove(f)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900732
Masahiro Yamada35204d92016-08-22 22:18:21 +0900733 return (updated, suspicious, log)
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900734
Masahiro Yamada07f98522016-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 Glassaba238f2021-12-18 14:54:34 -0700745 defconfig_lines = read_file(self.defconfig)
Masahiro Yamada07f98522016-05-19 15:52:06 +0900746
747 for (action, value) in self.results:
748 if action != ACTION_MOVE:
749 continue
Alper Nebi Yasakcab84072022-01-29 18:22:08 +0300750 if not value in defconfig_lines:
Simon Glassd9c1da22021-12-18 14:54:31 -0700751 log += color_text(self.args.color, COLOR_YELLOW,
Masahiro Yamada07f98522016-05-19 15:52:06 +0900752 "'%s' was removed by savedefconfig.\n" %
753 value)
754
755 return log
756
Simon Glass43cf08f2017-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 Yamadab6160812015-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 Glassd9c1da22021-12-18 14:54:31 -0700794 def __init__(self, toolchains, configs, args, progress, devnull,
Simon Glass257f5232017-07-10 14:47:47 -0600795 make_cmd, reference_src_dir, db_queue):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900796 """Create a new process slot.
797
Simon Glassb3464eb2021-12-18 14:54:35 -0700798 Args:
Simon Glass257f5232017-07-10 14:47:47 -0600799 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900800 configs: A list of CONFIGs to move.
Simon Glassd9c1da22021-12-18 14:54:31 -0700801 args: Program arguments
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900802 progress: A progress indicator.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900803 devnull: A file object of '/dev/null'.
804 make_cmd: command name of GNU Make.
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500805 reference_src_dir: Determine the true starting config state from this
806 source tree.
Simon Glass43cf08f2017-06-01 19:39:02 -0600807 db_queue: output queue to write config info for the database
Masahiro Yamadab6160812015-05-20 11:36:07 +0900808 """
Simon Glass257f5232017-07-10 14:47:47 -0600809 self.toolchains = toolchains
Simon Glassd9c1da22021-12-18 14:54:31 -0700810 self.args = args
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900811 self.progress = progress
Masahiro Yamadab6160812015-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 Hershbergerb1a570f2016-06-10 14:53:32 -0500815 self.reference_src_dir = reference_src_dir
Simon Glass43cf08f2017-06-01 19:39:02 -0600816 self.db_queue = db_queue
Simon Glassd9c1da22021-12-18 14:54:31 -0700817 self.parser = KconfigParser(configs, args, self.build_dir)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900818 self.state = STATE_IDLE
Masahiro Yamada1271b672016-08-22 22:18:20 +0900819 self.failed_boards = set()
820 self.suspicious_boards = set()
Masahiro Yamadab6160812015-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 Hershberger640de872016-06-10 14:53:29 -0500827 because it is guaranteed the destructor is always invoked when the
Masahiro Yamadab6160812015-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 Yamadacefaa582016-05-19 15:51:55 +0900837 def add(self, defconfig):
Masahiro Yamadab6160812015-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 Glassb3464eb2021-12-18 14:54:35 -0700844 Args:
Masahiro Yamadab6160812015-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 Yamadacb256cb2016-06-08 11:47:37 +0900852
Masahiro Yamadab6160812015-05-20 11:36:07 +0900853 self.defconfig = defconfig
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900854 self.log = ''
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900855 self.current_src_dir = self.reference_src_dir
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900856 self.do_defconfig()
Masahiro Yamadab6160812015-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 Yamada263d1372016-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 Yamadab6160812015-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 Yamadacb256cb2016-06-08 11:47:37 +0900882 self.handle_error()
883 elif self.state == STATE_DEFCONFIG:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900884 if self.reference_src_dir and not self.current_src_dir:
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500885 self.do_savedefconfig()
886 else:
887 self.do_autoconf()
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900888 elif self.state == STATE_AUTOCONF:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900889 if self.current_src_dir:
890 self.current_src_dir = None
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500891 self.do_defconfig()
Simon Glassd9c1da22021-12-18 14:54:31 -0700892 elif self.args.build_db:
Simon Glass43cf08f2017-06-01 19:39:02 -0600893 self.do_build_db()
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500894 else:
895 self.do_savedefconfig()
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900896 elif self.state == STATE_SAVEDEFCONFIG:
897 self.update_defconfig()
898 else:
Simon Glassdc634d92021-12-18 14:54:30 -0700899 sys.exit('Internal Error. This should not happen.')
Masahiro Yamadab6160812015-05-20 11:36:07 +0900900
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900901 return True if self.state == STATE_IDLE else False
Joe Hershberger166edec2015-05-19 13:21:17 -0500902
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900903 def handle_error(self):
904 """Handle error cases."""
Masahiro Yamada83c17672016-05-19 15:52:08 +0900905
Simon Glassd9c1da22021-12-18 14:54:31 -0700906 self.log += color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glassdc634d92021-12-18 14:54:30 -0700907 'Failed to process.\n')
Simon Glassd9c1da22021-12-18 14:54:31 -0700908 if self.args.verbose:
909 self.log += color_text(self.args.color, COLOR_LIGHT_CYAN,
Markus Klotzbuecher8773f352020-02-12 20:46:45 +0100910 self.ps.stderr.read().decode())
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900911 self.finish(False)
Joe Hershberger166edec2015-05-19 13:21:17 -0500912
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900913 def do_defconfig(self):
914 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamada0f6beda2016-05-19 15:52:07 +0900915
Masahiro Yamadacb256cb2016-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 Yamada8f5256a2016-06-15 14:33:52 +0900919 stderr=subprocess.PIPE,
920 cwd=self.current_src_dir)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900921 self.state = STATE_DEFCONFIG
Masahiro Yamada0f6beda2016-05-19 15:52:07 +0900922
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900923 def do_autoconf(self):
Simon Glass8fb5bd02017-06-01 19:39:01 -0600924 """Run 'make AUTO_CONF_PATH'."""
Masahiro Yamadab6160812015-05-20 11:36:07 +0900925
Simon Glass257f5232017-07-10 14:47:47 -0600926 arch = self.parser.get_arch()
927 try:
928 toolchain = self.toolchains.Select(arch)
929 except ValueError:
Simon Glassd9c1da22021-12-18 14:54:31 -0700930 self.log += color_text(self.args.color, COLOR_YELLOW,
Chris Packham1ebcbd12017-08-27 20:00:51 +1200931 "Tool chain for '%s' is missing. Do nothing.\n" % arch)
Masahiro Yamada274a5ee2016-05-19 15:52:03 +0900932 self.finish(False)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900933 return
Simon Glass1f701862019-10-31 07:42:57 -0600934 env = toolchain.MakeEnvironment(False)
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900935
Masahiro Yamadab6160812015-05-20 11:36:07 +0900936 cmd = list(self.make_cmd)
Joe Hershberger765442b2015-05-19 13:21:18 -0500937 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Simon Glass8fb5bd02017-06-01 19:39:01 -0600938 cmd.append(AUTO_CONF_PATH)
Simon Glass257f5232017-07-10 14:47:47 -0600939 self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900940 stderr=subprocess.PIPE,
941 cwd=self.current_src_dir)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900942 self.state = STATE_AUTOCONF
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900943
Simon Glass43cf08f2017-06-01 19:39:02 -0600944 def do_build_db(self):
945 """Add the board to the database"""
946 configs = {}
Simon Glassaba238f2021-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 Glass43cf08f2017-06-01 19:39:02 -0600951 self.db_queue.put([self.defconfig, configs])
952 self.finish(True)
953
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900954 def do_savedefconfig(self):
955 """Update the .config and run 'make savedefconfig'."""
956
Masahiro Yamada35204d92016-08-22 22:18:21 +0900957 (updated, suspicious, log) = self.parser.update_dotconfig()
958 if suspicious:
959 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900960 self.log += log
961
Simon Glassd9c1da22021-12-18 14:54:31 -0700962 if not self.args.force_sync and not updated:
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900963 self.finish(True)
964 return
965 if updated:
Simon Glassd9c1da22021-12-18 14:54:31 -0700966 self.log += color_text(self.args.color, COLOR_LIGHT_GREEN,
Simon Glassdc634d92021-12-18 14:54:30 -0700967 'Syncing by savedefconfig...\n')
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900968 else:
Simon Glassdc634d92021-12-18 14:54:30 -0700969 self.log += 'Syncing by savedefconfig (forced by option)...\n'
Masahiro Yamadacb256cb2016-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 Yamada3c9bfea2016-06-15 14:33:54 +0900980 log = self.parser.check_defconfig()
981 if log:
Masahiro Yamada1271b672016-08-22 22:18:20 +0900982 self.suspicious_boards.add(self.defconfig)
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +0900983 self.log += log
Masahiro Yamadacb256cb2016-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 Glassd9c1da22021-12-18 14:54:31 -0700989 self.log += color_text(self.args.color, COLOR_LIGHT_BLUE,
Simon Glassdc634d92021-12-18 14:54:30 -0700990 'defconfig was updated.\n')
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900991
Simon Glassd9c1da22021-12-18 14:54:31 -0700992 if not self.args.dry_run and updated:
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900993 shutil.move(new_defconfig, orig_defconfig)
994 self.finish(True)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900995
Masahiro Yamada274a5ee2016-05-19 15:52:03 +0900996 def finish(self, success):
997 """Display log along with progress and go to the idle state.
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900998
Simon Glassb3464eb2021-12-18 14:54:35 -0700999 Args:
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001000 success: Should be True when the defconfig was processed
1001 successfully, or False when it fails.
Masahiro Yamada465b7c02016-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 Glass1f701862019-10-31 07:42:57 -06001009 print(log, file=(sys.stdout if success else sys.stderr))
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001010
1011 if not success:
Simon Glassd9c1da22021-12-18 14:54:31 -07001012 if self.args.exit_on_error:
Simon Glassdc634d92021-12-18 14:54:30 -07001013 sys.exit('Exit on error.')
Masahiro Yamada274a5ee2016-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 Yamada1271b672016-08-22 22:18:20 +09001016 self.failed_boards.add(self.defconfig)
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001017
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001018 self.progress.inc()
1019 self.progress.show()
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001020 self.state = STATE_IDLE
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001021
Masahiro Yamadab6160812015-05-20 11:36:07 +09001022 def get_failed_boards(self):
Masahiro Yamada1271b672016-08-22 22:18:20 +09001023 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamadab6160812015-05-20 11:36:07 +09001024 """
1025 return self.failed_boards
1026
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001027 def get_suspicious_boards(self):
Masahiro Yamada1271b672016-08-22 22:18:20 +09001028 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001029 """
Masahiro Yamada35204d92016-08-22 22:18:21 +09001030 return self.suspicious_boards - self.failed_boards
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001031
Masahiro Yamadab6160812015-05-20 11:36:07 +09001032class Slots:
1033
1034 """Controller of the array of subprocess slots."""
1035
Simon Glassd9c1da22021-12-18 14:54:31 -07001036 def __init__(self, toolchains, configs, args, progress,
Simon Glass257f5232017-07-10 14:47:47 -06001037 reference_src_dir, db_queue):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001038 """Create a new slots controller.
1039
Simon Glassb3464eb2021-12-18 14:54:35 -07001040 Args:
Simon Glass257f5232017-07-10 14:47:47 -06001041 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001042 configs: A list of CONFIGs to move.
Simon Glassd9c1da22021-12-18 14:54:31 -07001043 args: Program arguments
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001044 progress: A progress indicator.
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001045 reference_src_dir: Determine the true starting config state from this
1046 source tree.
Simon Glass43cf08f2017-06-01 19:39:02 -06001047 db_queue: output queue to write config info for the database
Masahiro Yamadab6160812015-05-20 11:36:07 +09001048 """
Simon Glassd9c1da22021-12-18 14:54:31 -07001049 self.args = args
Masahiro Yamadab6160812015-05-20 11:36:07 +09001050 self.slots = []
Simon Glass34c505f2021-12-18 14:54:32 -07001051 devnull = subprocess.DEVNULL
Masahiro Yamadab6160812015-05-20 11:36:07 +09001052 make_cmd = get_make_cmd()
Simon Glassd9c1da22021-12-18 14:54:31 -07001053 for i in range(args.jobs):
1054 self.slots.append(Slot(toolchains, configs, args, progress,
Simon Glass257f5232017-07-10 14:47:47 -06001055 devnull, make_cmd, reference_src_dir,
1056 db_queue))
Masahiro Yamadab6160812015-05-20 11:36:07 +09001057
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001058 def add(self, defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001059 """Add a new subprocess if a vacant slot is found.
1060
Simon Glassb3464eb2021-12-18 14:54:35 -07001061 Args:
Masahiro Yamadab6160812015-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 Yamadacefaa582016-05-19 15:51:55 +09001068 if slot.add(defconfig):
Masahiro Yamadab6160812015-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 Yamada1271b672016-08-22 22:18:20 +09001097 boards = set()
Masahiro Yamada0153f032016-06-15 14:33:53 +09001098 output_file = 'moveconfig.failed'
Masahiro Yamadab6160812015-05-20 11:36:07 +09001099
1100 for slot in self.slots:
Masahiro Yamada1271b672016-08-22 22:18:20 +09001101 boards |= slot.get_failed_boards()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001102
Masahiro Yamada0153f032016-06-15 14:33:53 +09001103 if boards:
1104 boards = '\n'.join(boards) + '\n'
Simon Glassdc634d92021-12-18 14:54:30 -07001105 msg = 'The following boards were not processed due to error:\n'
Masahiro Yamada0153f032016-06-15 14:33:53 +09001106 msg += boards
Simon Glassdc634d92021-12-18 14:54:30 -07001107 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassd9c1da22021-12-18 14:54:31 -07001108 print(color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glass1f701862019-10-31 07:42:57 -06001109 msg), file=sys.stderr)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001110
Simon Glassb09ae452021-12-18 14:54:33 -07001111 write_file(output_file, boards)
Joe Hershbergerdade12e2015-05-19 13:21:22 -05001112
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001113 def show_suspicious_boards(self):
1114 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada1271b672016-08-22 22:18:20 +09001115 boards = set()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001116 output_file = 'moveconfig.suspicious'
1117
1118 for slot in self.slots:
Masahiro Yamada1271b672016-08-22 22:18:20 +09001119 boards |= slot.get_suspicious_boards()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001120
1121 if boards:
1122 boards = '\n'.join(boards) + '\n'
Simon Glassdc634d92021-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 Yamada3c9bfea2016-06-15 14:33:54 +09001125 msg += boards
Simon Glassdc634d92021-12-18 14:54:30 -07001126 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassd9c1da22021-12-18 14:54:31 -07001127 print(color_text(self.args.color, COLOR_YELLOW,
Simon Glass1f701862019-10-31 07:42:57 -06001128 msg), file=sys.stderr)
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001129
Simon Glassb09ae452021-12-18 14:54:33 -07001130 write_file(output_file, boards)
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001131
Masahiro Yamada2e74fee2016-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 Glassb3464eb2021-12-18 14:54:35 -07001139 Args:
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001140 commit: commit to git-clone
1141 """
1142 self.src_dir = tempfile.mkdtemp()
Simon Glassdc634d92021-12-18 14:54:30 -07001143 print('Cloning git repo to a separate work directory...')
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001144 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1145 cwd=self.src_dir)
Simon Glass1f701862019-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 Yamada2e74fee2016-06-15 14:33:51 +09001148 subprocess.check_output(['git', 'checkout', commit],
1149 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001150
1151 def __del__(self):
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001152 """Delete the reference source directory
Joe Hershbergerb1a570f2016-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 Yamada2e74fee2016-06-15 14:33:51 +09001159 shutil.rmtree(self.src_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001160
Masahiro Yamada2e74fee2016-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 Hershbergerb1a570f2016-06-10 14:53:32 -05001165
Simon Glassd9c1da22021-12-18 14:54:31 -07001166def move_config(toolchains, configs, args, db_queue):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001167 """Move config options to defconfig files.
1168
Simon Glassb3464eb2021-12-18 14:54:35 -07001169 Args:
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001170 configs: A list of CONFIGs to move.
Simon Glassd9c1da22021-12-18 14:54:31 -07001171 args: Program arguments
Masahiro Yamadab6160812015-05-20 11:36:07 +09001172 """
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001173 if len(configs) == 0:
Simon Glassd9c1da22021-12-18 14:54:31 -07001174 if args.force_sync:
Simon Glass1f701862019-10-31 07:42:57 -06001175 print('No CONFIG is specified. You are probably syncing defconfigs.', end=' ')
Simon Glassd9c1da22021-12-18 14:54:31 -07001176 elif args.build_db:
Simon Glass1f701862019-10-31 07:42:57 -06001177 print('Building %s database' % CONFIG_DATABASE)
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001178 else:
Simon Glass1f701862019-10-31 07:42:57 -06001179 print('Neither CONFIG nor --force-sync is specified. Nothing will happen.', end=' ')
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001180 else:
Simon Glass1f701862019-10-31 07:42:57 -06001181 print('Move ' + ', '.join(configs), end=' ')
Simon Glassd9c1da22021-12-18 14:54:31 -07001182 print('(jobs: %d)\n' % args.jobs)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001183
Simon Glassd9c1da22021-12-18 14:54:31 -07001184 if args.git_ref:
1185 reference_src = ReferenceSource(args.git_ref)
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001186 reference_src_dir = reference_src.get_dir()
1187 else:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +09001188 reference_src_dir = None
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001189
Simon Glassd9c1da22021-12-18 14:54:31 -07001190 if args.defconfigs:
1191 defconfigs = get_matched_defconfigs(args.defconfigs)
Joe Hershbergerc6e043a2015-05-19 13:21:19 -05001192 else:
Masahiro Yamada58175e32016-07-25 19:15:28 +09001193 defconfigs = get_all_defconfigs()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001194
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001195 progress = Progress(len(defconfigs))
Simon Glassd9c1da22021-12-18 14:54:31 -07001196 slots = Slots(toolchains, configs, args, progress, reference_src_dir,
Simon Glassb3464eb2021-12-18 14:54:35 -07001197 db_queue)
Masahiro Yamadab6160812015-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 Yamadacefaa582016-05-19 15:51:55 +09001202 for defconfig in defconfigs:
1203 while not slots.add(defconfig):
Masahiro Yamadab6160812015-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 Glass1f701862019-10-31 07:42:57 -06001212 print('')
Masahiro Yamadab6160812015-05-20 11:36:07 +09001213 slots.show_failed_boards()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001214 slots.show_suspicious_boards()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001215
Simon Glass44116332017-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 Rini3c5f4152019-09-20 17:42:09 -04001220 kconf: Kconfiglib.Kconfig object
Simon Glass44116332017-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 Rini3c5f4152019-09-20 17:42:09 -04001228 sym = kconf.syms.get(imply_config)
Simon Glass44116332017-06-15 21:39:33 -06001229 if sym:
Simon Glass520b47a2021-07-21 21:35:53 -06001230 for sel, cond in (sym.selects + sym.implies):
Simon Glass93c0a9e2021-12-18 08:09:42 -07001231 if sel.name == config:
Simon Glass44116332017-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 Rini3c5f4152019-09-20 17:42:09 -04001242 kconf: Kconfiglib.Kconfig object
Simon Glass44116332017-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 Rini3c5f4152019-09-20 17:42:09 -04001253 sym = kconf.syms.get(imply_config)
Simon Glass44116332017-06-15 21:39:33 -06001254 if not sym:
1255 return 'cannot find sym'
Simon Glass520b47a2021-07-21 21:35:53 -06001256 nodes = sym.nodes
1257 if len(nodes) != 1:
1258 return '%d locations' % len(nodes)
Simon Glass93c0a9e2021-12-18 08:09:42 -07001259 node = nodes[0]
1260 fname, linenum = node.filename, node.linenr
Simon Glass44116332017-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 Glassaba238f2021-12-18 14:54:34 -07001265 data = read_file(fname)
Simon Glass44116332017-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 Glassaba238f2021-12-18 14:54:34 -07001282 data = read_file(fname)
Simon Glass44116332017-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 Glassb09ae452021-12-18 14:54:33 -07001288 write_file(fname, data)
Simon Glass44116332017-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 Glass92e55582017-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 Glass44116332017-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 Glassb3464eb2021-12-18 14:54:35 -07001303}
Simon Glass92e55582017-06-15 21:39:32 -06001304
Simon Glassf931c2f2021-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 Glassaba238f2021-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 Glassf931c2f2021-12-18 08:09:43 -07001348
1349 return all_configs, all_defconfigs, config_db, defconfig_db
1350
1351
Simon Glass44116332017-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 Glassc6e73cf2017-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 Glass44116332017-06-15 21:39:33 -06001378 add_imply: Automatically add an 'imply' for each config.
Simon Glass92e55582017-06-15 21:39:32 -06001379 imply_flags: Flags which control which implying configs are allowed
1380 (IMPLY_...)
Simon Glass44116332017-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 Glassc6e73cf2017-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 Glass44116332017-06-15 21:39:33 -06001394 kconf = KconfigScanner().conf if check_kconfig else None
1395 if add_imply and add_imply != 'all':
Simon Glass93c0a9e2021-12-18 08:09:42 -07001396 add_imply = add_imply.split(',')
Simon Glass44116332017-06-15 21:39:33 -06001397
Simon Glassf931c2f2021-12-18 08:09:43 -07001398 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
Simon Glassc6e73cf2017-06-01 19:39:03 -06001399
Simon Glass93c0a9e2021-12-18 08:09:42 -07001400 # Work through each target config option in turn, independently
Simon Glassc6e73cf2017-06-01 19:39:03 -06001401 for config in config_list:
1402 defconfigs = defconfig_db.get(config)
1403 if not defconfigs:
Simon Glass1f701862019-10-31 07:42:57 -06001404 print('%s not found in any defconfig' % config)
Simon Glassc6e73cf2017-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 Glass1f701862019-10-31 07:42:57 -06001411 print('%s found in %d/%d defconfigs' % (config, num_defconfigs,
1412 len(all_configs)))
Simon Glassc6e73cf2017-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 Glass92e55582017-06-15 21:39:32 -06001420 if 'ERRATUM' in imply_config:
Simon Glassc6e73cf2017-06-01 19:39:03 -06001421 continue
Simon Glassb3464eb2021-12-18 14:54:35 -07001422 if not imply_flags & IMPLY_CMD:
Simon Glass92e55582017-06-15 21:39:32 -06001423 if 'CONFIG_CMD' in imply_config:
1424 continue
Simon Glassb3464eb2021-12-18 14:54:35 -07001425 if not imply_flags & IMPLY_TARGET:
Simon Glass92e55582017-06-15 21:39:32 -06001426 if 'CONFIG_TARGET' in imply_config:
1427 continue
Simon Glassc6e73cf2017-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 Glass1f701862019-10-31 07:42:57 -06001449 for prev in list(imply_configs.keys()):
Simon Glassc6e73cf2017-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 Glass44116332017-06-15 21:39:33 -06001468 ranked_iconfigs = sorted(imply_configs,
Simon Glassc6e73cf2017-06-01 19:39:03 -06001469 key=lambda k: len(imply_configs[k]), reverse=True)
Simon Glass44116332017-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 Glassc6e73cf2017-06-01 19:39:03 -06001475
1476 # Don't bother if there are less than 5 defconfigs affected.
Simon Glass92e55582017-06-15 21:39:32 -06001477 if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
Simon Glassc6e73cf2017-06-01 19:39:03 -06001478 continue
Simon Glass44116332017-06-15 21:39:33 -06001479 missing = defconfigs - imply_configs[iconfig]
Simon Glassc6e73cf2017-06-01 19:39:03 -06001480 missing_str = ', '.join(missing) if missing else 'all'
1481 missing_str = ''
Simon Glass44116332017-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 Glass520b47a2021-07-21 21:35:53 -06001488 nodes = sym.nodes
1489 if len(nodes) == 1:
1490 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glass44116332017-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 Rini3c5f4152019-09-20 17:42:09 -04001497 sym = kconf.syms.get(iconfig[CONFIG_LEN:])
Simon Glass44116332017-06-15 21:39:33 -06001498 fname = ''
1499 if sym:
Simon Glass520b47a2021-07-21 21:35:53 -06001500 nodes = sym.nodes
1501 if len(nodes) == 1:
1502 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glass44116332017-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 Glassb3464eb2021-12-18 14:54:35 -07001508 not imply_flags & IMPLY_NON_ARCH_BOARD):
Simon Glass44116332017-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)
Simon Glassc6e73cf2017-06-01 19:39:03 -06001517
Simon Glass44116332017-06-15 21:39:33 -06001518 if show and kconfig_info != 'skip':
Simon Glass1f701862019-10-31 07:42:57 -06001519 print('%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
1520 kconfig_info, missing_str))
Simon Glass44116332017-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 Glass1f701862019-10-31 07:42:57 -06001527 for fname, linenums in add_list.items():
Simon Glass44116332017-06-15 21:39:33 -06001528 for linenum in sorted(linenums, reverse=True):
1529 add_imply_rule(config[CONFIG_LEN:], fname, linenum)
1530
Simon Glass99f79422022-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 Glassfea71c92022-03-05 20:18:54 -07001546 if re_match.fullmatch(cfg):
Simon Glass99f79422022-02-08 11:49:46 -07001547 return True
1548 return False
Simon Glassc6e73cf2017-06-01 19:39:03 -06001549
Simon Glass0082b2e2021-12-18 08:09:46 -07001550def do_find_config(config_list):
1551 """Find boards with a given combination of CONFIGs
1552
1553 Params:
Simon Glass99f79422022-02-08 11:49:46 -07001554 config_list: List of CONFIG options to check (each a regex consisting
Simon Glass0082b2e2021-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 Glassaba238f2021-12-18 14:54:34 -07001562 adhoc_configs = set(read_file('scripts/config_whitelist.txt'))
Simon Glass0082b2e2021-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 Glass99f79422022-02-08 11:49:46 -07001586 re_match = re.compile(cfg)
Simon Glass0082b2e2021-12-18 08:09:46 -07001587 for defc in in_list:
Simon Glass99f79422022-02-08 11:49:46 -07001588 has_cfg = defconfig_matches(config_db[defc], re_match)
Simon Glass0082b2e2021-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 Glass51aa73b2022-03-05 20:18:53 -07001595 print(' '.join(item.split('_defconfig')[0] for item in out))
Simon Glass0082b2e2021-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 Yamadab6160812015-05-20 11:36:07 +09001623def main():
1624 try:
1625 cpu_count = multiprocessing.cpu_count()
1626 except NotImplementedError:
1627 cpu_count = 1
1628
Simon Glassd9c1da22021-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 Glass44116332017-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 Glassd9c1da22021-12-18 14:54:31 -07001637 parser.add_argument('-A', '--skip-added', action='store_true', default=False,
Simon Glass44116332017-06-15 21:39:33 -06001638 help="don't show options which are already marked as "
1639 'implying others')
Simon Glassd9c1da22021-12-18 14:54:31 -07001640 parser.add_argument('-b', '--build-db', action='store_true', default=False,
Simon Glass43cf08f2017-06-01 19:39:02 -06001641 help='build a CONFIG database')
Simon Glassd9c1da22021-12-18 14:54:31 -07001642 parser.add_argument('-c', '--color', action='store_true', default=False,
Masahiro Yamadab6160812015-05-20 11:36:07 +09001643 help='display the log in color')
Simon Glassd9c1da22021-12-18 14:54:31 -07001644 parser.add_argument('-C', '--commit', action='store_true', default=False,
Simon Glass8bf41c22016-09-12 23:18:21 -06001645 help='Create a git commit for the operation')
Simon Glassd9c1da22021-12-18 14:54:31 -07001646 parser.add_argument('-d', '--defconfigs', type=str,
Simon Glass8f3cf312017-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 Glassd9c1da22021-12-18 14:54:31 -07001650 parser.add_argument('-e', '--exit-on-error', action='store_true',
Masahiro Yamadab6160812015-05-20 11:36:07 +09001651 default=False,
1652 help='exit immediately on any error')
Simon Glassd9c1da22021-12-18 14:54:31 -07001653 parser.add_argument('-f', '--find', action='store_true', default=False,
Simon Glass0082b2e2021-12-18 08:09:46 -07001654 help='Find boards with a given config combination')
Simon Glassd9c1da22021-12-18 14:54:31 -07001655 parser.add_argument('-H', '--headers-only', dest='cleanup_headers_only',
Joe Hershberger23475932015-05-19 13:21:20 -05001656 action='store_true', default=False,
1657 help='only cleanup the headers')
Simon Glassd9c1da22021-12-18 14:54:31 -07001658 parser.add_argument('-i', '--imply', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001659 help='find options which imply others')
Simon Glassd9c1da22021-12-18 14:54:31 -07001660 parser.add_argument('-I', '--imply-flags', type=str, default='',
Simon Glass0559a742021-12-18 08:09:44 -07001661 help="control the -i option ('help' for help")
Simon Glassd9c1da22021-12-18 14:54:31 -07001662 parser.add_argument('-j', '--jobs', type=int, default=cpu_count,
Masahiro Yamadab6160812015-05-20 11:36:07 +09001663 help='the number of jobs to run simultaneously')
Simon Glassd9c1da22021-12-18 14:54:31 -07001664 parser.add_argument('-n', '--dry-run', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001665 help='perform a trial run (show log with no changes)')
Simon Glassd9c1da22021-12-18 14:54:31 -07001666 parser.add_argument('-r', '--git-ref', type=str,
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001667 help='the git ref to clone for building the autoconf.mk')
Simon Glassd9c1da22021-12-18 14:54:31 -07001668 parser.add_argument('-s', '--force-sync', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001669 help='force sync by savedefconfig')
Simon Glassd9c1da22021-12-18 14:54:31 -07001670 parser.add_argument('-S', '--spl', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001671 help='parse config options defined for SPL build')
Simon Glassd9c1da22021-12-18 14:54:31 -07001672 parser.add_argument('-t', '--test', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001673 help='run unit tests')
Simon Glassd9c1da22021-12-18 14:54:31 -07001674 parser.add_argument('-y', '--yes', action='store_true', default=False,
Simon Glass13e05a02016-09-12 23:18:20 -06001675 help="respond 'yes' to any prompts")
Simon Glassd9c1da22021-12-18 14:54:31 -07001676 parser.add_argument('-v', '--verbose', action='store_true', default=False,
Joe Hershberger808b63f2015-05-19 13:21:24 -05001677 help='show any build errors as boards are built')
Simon Glassd9c1da22021-12-18 14:54:31 -07001678 parser.add_argument('configs', nargs='*')
Masahiro Yamadab6160812015-05-20 11:36:07 +09001679
Simon Glassd9c1da22021-12-18 14:54:31 -07001680 args = parser.parse_args()
1681 configs = args.configs
Masahiro Yamadab6160812015-05-20 11:36:07 +09001682
Simon Glassd9c1da22021-12-18 14:54:31 -07001683 if args.test:
Simon Glassbb57be72021-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 Glassd9c1da22021-12-18 14:54:31 -07001690 if not any((len(configs), args.force_sync, args.build_db, args.imply,
1691 args.find)):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001692 parser.print_usage()
1693 sys.exit(1)
1694
Masahiro Yamadab903c4e2016-05-19 15:51:58 +09001695 # prefix the option name with CONFIG_ if missing
Simon Glass0082b2e2021-12-18 08:09:46 -07001696 configs = [prefix_config(cfg) for cfg in configs]
Masahiro Yamadab6160812015-05-20 11:36:07 +09001697
Joe Hershberger23475932015-05-19 13:21:20 -05001698 check_top_directory()
1699
Simon Glassd9c1da22021-12-18 14:54:31 -07001700 if args.imply:
Simon Glass92e55582017-06-15 21:39:32 -06001701 imply_flags = 0
Simon Glassd9c1da22021-12-18 14:54:31 -07001702 if args.imply_flags == 'all':
Simon Glass5f096922017-07-10 14:47:46 -06001703 imply_flags = -1
1704
Simon Glassd9c1da22021-12-18 14:54:31 -07001705 elif args.imply_flags:
1706 for flag in args.imply_flags.split(','):
Simon Glass5f096922017-07-10 14:47:46 -06001707 bad = flag not in IMPLY_FLAGS
1708 if bad:
Simon Glass1f701862019-10-31 07:42:57 -06001709 print("Invalid flag '%s'" % flag)
Simon Glass5f096922017-07-10 14:47:46 -06001710 if flag == 'help' or bad:
Simon Glass1f701862019-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 Glass5f096922017-07-10 14:47:46 -06001714 parser.print_usage()
1715 sys.exit(1)
1716 imply_flags |= IMPLY_FLAGS[flag][0]
Simon Glass92e55582017-06-15 21:39:32 -06001717
Simon Glassd9c1da22021-12-18 14:54:31 -07001718 do_imply_config(configs, args.add_imply, imply_flags, args.skip_added)
Simon Glassc6e73cf2017-06-01 19:39:03 -06001719 return
1720
Simon Glassd9c1da22021-12-18 14:54:31 -07001721 if args.find:
Simon Glass0082b2e2021-12-18 08:09:46 -07001722 do_find_config(configs)
1723 return
1724
Simon Glass43cf08f2017-06-01 19:39:02 -06001725 config_db = {}
Simon Glass1f701862019-10-31 07:42:57 -06001726 db_queue = queue.Queue()
Simon Glass43cf08f2017-06-01 19:39:02 -06001727 t = DatabaseThread(config_db, db_queue)
1728 t.setDaemon(True)
1729 t.start()
1730
Simon Glassd9c1da22021-12-18 14:54:31 -07001731 if not args.cleanup_headers_only:
Masahiro Yamadad0a9d2a2016-07-25 19:15:23 +09001732 check_clean_directory()
Simon Glass1f701862019-10-31 07:42:57 -06001733 bsettings.Setup('')
Simon Glass257f5232017-07-10 14:47:47 -06001734 toolchains = toolchain.Toolchains()
1735 toolchains.GetSettings()
1736 toolchains.Scan(verbose=False)
Simon Glassd9c1da22021-12-18 14:54:31 -07001737 move_config(toolchains, configs, args, db_queue)
Simon Glass43cf08f2017-06-01 19:39:02 -06001738 db_queue.join()
Joe Hershberger23475932015-05-19 13:21:20 -05001739
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001740 if configs:
Simon Glassd9c1da22021-12-18 14:54:31 -07001741 cleanup_headers(configs, args)
Simon Glassd9c1da22021-12-18 14:54:31 -07001742 cleanup_whitelist(configs, args)
1743 cleanup_readme(configs, args)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001744
Simon Glassd9c1da22021-12-18 14:54:31 -07001745 if args.commit:
Simon Glass8bf41c22016-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 Glassd9c1da22021-12-18 14:54:31 -07001757 if args.build_db:
Simon Glassb3464eb2021-12-18 14:54:35 -07001758 with open(CONFIG_DATABASE, 'w', encoding='utf-8') as fd:
Simon Glass1f701862019-10-31 07:42:57 -06001759 for defconfig, configs in config_db.items():
Simon Glass1c879312017-08-13 16:02:54 -06001760 fd.write('%s\n' % defconfig)
Simon Glass43cf08f2017-06-01 19:39:02 -06001761 for config in sorted(configs.keys()):
Simon Glass1c879312017-08-13 16:02:54 -06001762 fd.write(' %s=%s\n' % (config, configs[config]))
1763 fd.write('\n')
Simon Glass43cf08f2017-06-01 19:39:02 -06001764
Masahiro Yamadab6160812015-05-20 11:36:07 +09001765if __name__ == '__main__':
Simon Glass0082b2e2021-12-18 08:09:46 -07001766 sys.exit(main())