blob: 8f084a6070aca895b43c71a572b3c3b3e5af587a [file] [log] [blame]
Simon Glass793dca32019-10-31 07:42:57 -06001#!/usr/bin/env python3
Tom Rini83d290c2018-05-06 17:58:06 -04002# SPDX-License-Identifier: GPL-2.0+
Masahiro Yamada5a27c732015-05-20 11:36:07 +09003#
4# Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5#
Masahiro Yamada5a27c732015-05-20 11:36:07 +09006
7"""
8Move config options from headers to defconfig files.
9
Simon Glass5c72c0e2021-07-21 21:35:51 -060010See doc/develop/moveconfig.rst for documentation.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090011"""
12
Simon Glassb2e83c62021-12-18 14:54:31 -070013from argparse import ArgumentParser
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +010014import asteval
Simon Glass99b66602017-06-01 19:39:03 -060015import collections
Simon Glass91197aa2021-12-18 14:54:35 -070016from contextlib import ExitStack
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +090017import copy
Masahiro Yamadaf2f69812016-07-25 19:15:25 +090018import difflib
Simon Glass84067a52021-12-18 08:09:45 -070019import doctest
Masahiro Yamadac8e1b102016-05-19 15:52:07 +090020import filecmp
Masahiro Yamada5a27c732015-05-20 11:36:07 +090021import fnmatch
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +090022import glob
Masahiro Yamada5a27c732015-05-20 11:36:07 +090023import multiprocessing
Masahiro Yamada5a27c732015-05-20 11:36:07 +090024import os
Simon Glass793dca32019-10-31 07:42:57 -060025import queue
Masahiro Yamada5a27c732015-05-20 11:36:07 +090026import re
27import shutil
28import subprocess
29import sys
30import tempfile
Simon Glassd73fcb12017-06-01 19:39:02 -060031import threading
Masahiro Yamada5a27c732015-05-20 11:36:07 +090032import time
Simon Glass84067a52021-12-18 08:09:45 -070033import unittest
Masahiro Yamada5a27c732015-05-20 11:36:07 +090034
Simon Glass0ede00f2020-04-17 18:09:02 -060035from buildman import bsettings
36from buildman import kconfiglib
37from buildman import toolchain
Simon Glasscb008832017-06-15 21:39:33 -060038
Masahiro Yamada5a27c732015-05-20 11:36:07 +090039SHOW_GNU_MAKE = 'scripts/show-gnu-make'
40SLEEP_TIME=0.03
41
Masahiro Yamada5a27c732015-05-20 11:36:07 +090042STATE_IDLE = 0
43STATE_DEFCONFIG = 1
44STATE_AUTOCONF = 2
Joe Hershberger96464ba2015-05-19 13:21:17 -050045STATE_SAVEDEFCONFIG = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +090046
47ACTION_MOVE = 0
Masahiro Yamadacc008292016-05-19 15:51:56 +090048ACTION_NO_ENTRY = 1
Masahiro Yamada916224c2016-08-22 22:18:21 +090049ACTION_NO_ENTRY_WARN = 2
50ACTION_NO_CHANGE = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +090051
52COLOR_BLACK = '0;30'
53COLOR_RED = '0;31'
54COLOR_GREEN = '0;32'
55COLOR_BROWN = '0;33'
56COLOR_BLUE = '0;34'
57COLOR_PURPLE = '0;35'
58COLOR_CYAN = '0;36'
59COLOR_LIGHT_GRAY = '0;37'
60COLOR_DARK_GRAY = '1;30'
61COLOR_LIGHT_RED = '1;31'
62COLOR_LIGHT_GREEN = '1;32'
63COLOR_YELLOW = '1;33'
64COLOR_LIGHT_BLUE = '1;34'
65COLOR_LIGHT_PURPLE = '1;35'
66COLOR_LIGHT_CYAN = '1;36'
67COLOR_WHITE = '1;37'
68
Simon Glassf3b8e642017-06-01 19:39:01 -060069AUTO_CONF_PATH = 'include/config/auto.conf'
Simon Glassd73fcb12017-06-01 19:39:02 -060070CONFIG_DATABASE = 'moveconfig.db'
Simon Glassf3b8e642017-06-01 19:39:01 -060071
Simon Glasscb008832017-06-15 21:39:33 -060072CONFIG_LEN = len('CONFIG_')
Simon Glassf3b8e642017-06-01 19:39:01 -060073
Markus Klotzbuecherb237d352019-05-15 15:15:52 +020074SIZES = {
Simon Glassdaa694d2021-12-18 14:54:30 -070075 'SZ_1': 0x00000001, 'SZ_2': 0x00000002,
76 'SZ_4': 0x00000004, 'SZ_8': 0x00000008,
77 'SZ_16': 0x00000010, 'SZ_32': 0x00000020,
78 'SZ_64': 0x00000040, 'SZ_128': 0x00000080,
79 'SZ_256': 0x00000100, 'SZ_512': 0x00000200,
80 'SZ_1K': 0x00000400, 'SZ_2K': 0x00000800,
81 'SZ_4K': 0x00001000, 'SZ_8K': 0x00002000,
82 'SZ_16K': 0x00004000, 'SZ_32K': 0x00008000,
83 'SZ_64K': 0x00010000, 'SZ_128K': 0x00020000,
84 'SZ_256K': 0x00040000, 'SZ_512K': 0x00080000,
85 'SZ_1M': 0x00100000, 'SZ_2M': 0x00200000,
86 'SZ_4M': 0x00400000, 'SZ_8M': 0x00800000,
87 'SZ_16M': 0x01000000, 'SZ_32M': 0x02000000,
88 'SZ_64M': 0x04000000, 'SZ_128M': 0x08000000,
89 'SZ_256M': 0x10000000, 'SZ_512M': 0x20000000,
90 'SZ_1G': 0x40000000, 'SZ_2G': 0x80000000,
91 'SZ_4G': 0x100000000
Markus Klotzbuecherb237d352019-05-15 15:15:52 +020092}
93
Simon Glassb8d11da2022-02-08 11:49:45 -070094RE_REMOVE_DEFCONFIG = re.compile(r'(.*)_defconfig')
95
Masahiro Yamada5a27c732015-05-20 11:36:07 +090096### helper functions ###
Masahiro Yamada5a27c732015-05-20 11:36:07 +090097def check_top_directory():
98 """Exit if we are not at the top of source directory."""
Simon Glass91197aa2021-12-18 14:54:35 -070099 for fname in 'README', 'Licenses':
100 if not os.path.exists(fname):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900101 sys.exit('Please run at the top of source directory.')
102
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900103def check_clean_directory():
104 """Exit if the source tree is not clean."""
Simon Glass91197aa2021-12-18 14:54:35 -0700105 for fname in '.config', 'include/config':
106 if os.path.exists(fname):
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900107 sys.exit("source tree is not clean, please run 'make mrproper'")
108
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900109def get_make_cmd():
110 """Get the command name of GNU Make.
111
112 U-Boot needs GNU Make for building, but the command name is not
113 necessarily "make". (for example, "gmake" on FreeBSD).
114 Returns the most appropriate command name on your system.
115 """
Simon Glass91197aa2021-12-18 14:54:35 -0700116 with subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE) as proc:
117 ret = proc.communicate()
118 if proc.returncode:
119 sys.exit('GNU Make not found')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900120 return ret[0].rstrip()
121
Simon Glass25f978c2017-06-01 19:38:58 -0600122def get_matched_defconfig(line):
123 """Get the defconfig files that match a pattern
124
125 Args:
Simon Glass91197aa2021-12-18 14:54:35 -0700126 line (str): Path or filename to match, e.g. 'configs/snow_defconfig' or
Simon Glass25f978c2017-06-01 19:38:58 -0600127 'k2*_defconfig'. If no directory is provided, 'configs/' is
128 prepended
129
130 Returns:
Simon Glass91197aa2021-12-18 14:54:35 -0700131 list of str: a list of matching defconfig files
Simon Glass25f978c2017-06-01 19:38:58 -0600132 """
133 dirname = os.path.dirname(line)
134 if dirname:
135 pattern = line
136 else:
137 pattern = os.path.join('configs', line)
138 return glob.glob(pattern) + glob.glob(pattern + '_defconfig')
139
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900140def get_matched_defconfigs(defconfigs_file):
Simon Glassee4e61b2017-06-01 19:38:59 -0600141 """Get all the defconfig files that match the patterns in a file.
142
143 Args:
Simon Glass91197aa2021-12-18 14:54:35 -0700144 defconfigs_file (str): File containing a list of defconfigs to process,
145 or '-' to read the list from stdin
Simon Glassee4e61b2017-06-01 19:38:59 -0600146
147 Returns:
Simon Glass91197aa2021-12-18 14:54:35 -0700148 list of str: A list of paths to defconfig files, with no duplicates
Simon Glassee4e61b2017-06-01 19:38:59 -0600149 """
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900150 defconfigs = []
Simon Glass91197aa2021-12-18 14:54:35 -0700151 with ExitStack() as stack:
152 if defconfigs_file == '-':
153 inf = sys.stdin
154 defconfigs_file = 'stdin'
155 else:
156 inf = stack.enter_context(open(defconfigs_file, encoding='utf-8'))
157 for i, line in enumerate(inf):
158 line = line.strip()
159 if not line:
160 continue # skip blank lines silently
161 if ' ' in line:
162 line = line.split(' ')[0] # handle 'git log' input
163 matched = get_matched_defconfig(line)
164 if not matched:
165 print(f"warning: {defconfigs_file}:{i + 1}: no defconfig matched '{line}'",
166 file=sys.stderr)
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900167
Simon Glass91197aa2021-12-18 14:54:35 -0700168 defconfigs += matched
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900169
170 # use set() to drop multiple matching
Simon Glass91197aa2021-12-18 14:54:35 -0700171 return [defconfig[len('configs') + 1:] for defconfig in set(defconfigs)]
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900172
Masahiro Yamada684c3062016-07-25 19:15:28 +0900173def get_all_defconfigs():
Simon Glass91197aa2021-12-18 14:54:35 -0700174 """Get all the defconfig files under the configs/ directory.
175
176 Returns:
177 list of str: List of paths to defconfig files
178 """
Masahiro Yamada684c3062016-07-25 19:15:28 +0900179 defconfigs = []
Simon Glass91197aa2021-12-18 14:54:35 -0700180 for (dirpath, _, filenames) in os.walk('configs'):
Masahiro Yamada684c3062016-07-25 19:15:28 +0900181 dirpath = dirpath[len('configs') + 1:]
182 for filename in fnmatch.filter(filenames, '*_defconfig'):
183 defconfigs.append(os.path.join(dirpath, filename))
184
185 return defconfigs
186
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900187def color_text(color_enabled, color, string):
188 """Return colored string."""
189 if color_enabled:
Masahiro Yamada1d085562016-05-19 15:52:02 +0900190 # LF should not be surrounded by the escape sequence.
191 # Otherwise, additional whitespace or line-feed might be printed.
192 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
193 for s in string.split('\n') ])
Simon Glass91197aa2021-12-18 14:54:35 -0700194 return string
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900195
Simon Glass91197aa2021-12-18 14:54:35 -0700196def show_diff(alines, blines, file_path, color_enabled):
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900197 """Show unidified diff.
198
Simon Glass91197aa2021-12-18 14:54:35 -0700199 Args:
200 alines (list of str): A list of lines (before)
201 blines (list of str): A list of lines (after)
202 file_path (str): Path to the file
203 color_enabled (bool): Display the diff in color
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900204 """
Simon Glass91197aa2021-12-18 14:54:35 -0700205 diff = difflib.unified_diff(alines, blines,
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900206 fromfile=os.path.join('a', file_path),
207 tofile=os.path.join('b', file_path))
208
209 for line in diff:
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300210 if line.startswith('-') and not line.startswith('--'):
211 print(color_text(color_enabled, COLOR_RED, line))
212 elif line.startswith('+') and not line.startswith('++'):
213 print(color_text(color_enabled, COLOR_GREEN, line))
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900214 else:
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300215 print(line)
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900216
Simon Glass91197aa2021-12-18 14:54:35 -0700217def extend_matched_lines(lines, matched, pre_patterns, post_patterns,
218 extend_pre, extend_post):
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900219 """Extend matched lines if desired patterns are found before/after already
220 matched lines.
221
Simon Glass91197aa2021-12-18 14:54:35 -0700222 Args:
223 lines (list of str): list of lines handled.
224 matched (list of int): list of line numbers that have been already
225 matched (will be updated by this function)
226 pre_patterns (list of re.Pattern): list of regular expression that should
227 be matched as preamble
228 post_patterns (list of re.Pattern): list of regular expression that should
229 be matched as postamble
230 extend_pre (bool): Add the line number of matched preamble to the matched
231 list
232 extend_post (bool): Add the line number of matched postamble to the
233 matched list
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900234 """
235 extended_matched = []
236
237 j = matched[0]
238
239 for i in matched:
240 if i == 0 or i < j:
241 continue
242 j = i
243 while j in matched:
244 j += 1
245 if j >= len(lines):
246 break
247
Simon Glass91197aa2021-12-18 14:54:35 -0700248 for pat in pre_patterns:
249 if pat.search(lines[i - 1]):
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900250 break
251 else:
252 # not matched
253 continue
254
Simon Glass91197aa2021-12-18 14:54:35 -0700255 for pat in post_patterns:
256 if pat.search(lines[j]):
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900257 break
258 else:
259 # not matched
260 continue
261
262 if extend_pre:
263 extended_matched.append(i - 1)
264 if extend_post:
265 extended_matched.append(j)
266
267 matched += extended_matched
268 matched.sort()
269
Simon Glassb2e83c62021-12-18 14:54:31 -0700270def confirm(args, prompt):
Simon Glass91197aa2021-12-18 14:54:35 -0700271 """Ask the user to confirm something
272
273 Args:
274 args (Namespace ): program arguments
275
276 Returns:
277 bool: True to confirm, False to cancel/stop
278 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700279 if not args.yes:
Chris Packham85edfc12017-05-02 21:30:46 +1200280 while True:
Simon Glass91197aa2021-12-18 14:54:35 -0700281 choice = input(f'{prompt} [y/n]: ')
Chris Packham85edfc12017-05-02 21:30:46 +1200282 choice = choice.lower()
Simon Glass793dca32019-10-31 07:42:57 -0600283 print(choice)
Simon Glass91197aa2021-12-18 14:54:35 -0700284 if choice in ('y', 'n'):
Chris Packham85edfc12017-05-02 21:30:46 +1200285 break
286
287 if choice == 'n':
288 return False
289
290 return True
291
Simon Glass2fd85bd2021-12-18 14:54:33 -0700292def write_file(fname, data):
293 """Write data to a file
294
295 Args:
296 fname (str): Filename to write to
297 data (list of str): Lines to write (with or without trailing newline);
298 or str to write
299 """
300 with open(fname, 'w', encoding='utf-8') as out:
301 if isinstance(data, list):
302 for line in data:
303 print(line.rstrip('\n'), file=out)
304 else:
305 out.write(data)
306
Simon Glass37f815c2021-12-18 14:54:34 -0700307def read_file(fname, as_lines=True, skip_unicode=False):
308 """Read a file and return the contents
309
310 Args:
311 fname (str): Filename to read from
312 as_lines: Return file contents as a list of lines
313 skip_unicode (bool): True to report unicode errors and continue
314
315 Returns:
316 iter of str: List of ;ines from the file with newline removed; str if
317 as_lines is False with newlines intact; or None if a unicode error
318 occurred
319
320 Raises:
321 UnicodeDecodeError: Unicode error occurred when reading
322 """
323 with open(fname, encoding='utf-8') as inf:
324 try:
325 if as_lines:
326 return [line.rstrip('\n') for line in inf.readlines()]
327 else:
328 return inf.read()
329 except UnicodeDecodeError as e:
330 if not skip_unicode:
Simon Glass68a0b712022-02-11 13:23:22 -0700331 raise
Simon Glass37f815c2021-12-18 14:54:34 -0700332 print("Failed on file %s': %s" % (fname, e))
333 return None
334
Simon Glassb2e83c62021-12-18 14:54:31 -0700335def cleanup_empty_blocks(header_path, args):
Chris Packham4d9dbb12019-01-30 20:23:16 +1300336 """Clean up empty conditional blocks
337
Simon Glass91197aa2021-12-18 14:54:35 -0700338 Args:
339 header_path (str): path to the cleaned file.
340 args (Namespace): program arguments
Chris Packham4d9dbb12019-01-30 20:23:16 +1300341 """
342 pattern = re.compile(r'^\s*#\s*if.*$\n^\s*#\s*endif.*$\n*', flags=re.M)
Simon Glass37f815c2021-12-18 14:54:34 -0700343 data = read_file(header_path, as_lines=False, skip_unicode=True)
344 if data is None:
345 return
Chris Packham4d9dbb12019-01-30 20:23:16 +1300346
347 new_data = pattern.sub('\n', data)
348
349 show_diff(data.splitlines(True), new_data.splitlines(True), header_path,
Simon Glassb2e83c62021-12-18 14:54:31 -0700350 args.color)
Chris Packham4d9dbb12019-01-30 20:23:16 +1300351
Simon Glassb2e83c62021-12-18 14:54:31 -0700352 if args.dry_run:
Chris Packham4d9dbb12019-01-30 20:23:16 +1300353 return
354
Simon Glass37f815c2021-12-18 14:54:34 -0700355 if new_data != data:
356 write_file(header_path, new_data)
Chris Packham4d9dbb12019-01-30 20:23:16 +1300357
Simon Glassb2e83c62021-12-18 14:54:31 -0700358def cleanup_one_header(header_path, patterns, args):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900359 """Clean regex-matched lines away from a file.
360
Simon Glass91197aa2021-12-18 14:54:35 -0700361 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900362 header_path: path to the cleaned file.
363 patterns: list of regex patterns. Any lines matching to these
364 patterns are deleted.
Simon Glass91197aa2021-12-18 14:54:35 -0700365 args (Namespace): program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900366 """
Simon Glass37f815c2021-12-18 14:54:34 -0700367 lines = read_file(header_path, skip_unicode=True)
368 if lines is None:
369 return
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900370
371 matched = []
372 for i, line in enumerate(lines):
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300373 if i - 1 in matched and lines[i - 1].endswith('\\'):
Masahiro Yamadaa3a779f2016-07-25 19:15:27 +0900374 matched.append(i)
375 continue
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900376 for pattern in patterns:
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900377 if pattern.search(line):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900378 matched.append(i)
379 break
380
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900381 if not matched:
382 return
383
384 # remove empty #ifdef ... #endif, successive blank lines
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300385 pattern_if = re.compile(r'#\s*if(def|ndef)?\b') # #if, #ifdef, #ifndef
386 pattern_elif = re.compile(r'#\s*el(if|se)\b') # #elif, #else
387 pattern_endif = re.compile(r'#\s*endif\b') # #endif
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900388 pattern_blank = re.compile(r'^\s*$') # empty line
389
390 while True:
391 old_matched = copy.copy(matched)
392 extend_matched_lines(lines, matched, [pattern_if],
393 [pattern_endif], True, True)
394 extend_matched_lines(lines, matched, [pattern_elif],
395 [pattern_elif, pattern_endif], True, False)
396 extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
397 [pattern_blank], False, True)
398 extend_matched_lines(lines, matched, [pattern_blank],
399 [pattern_elif, pattern_endif], True, False)
400 extend_matched_lines(lines, matched, [pattern_blank],
401 [pattern_blank], True, False)
402 if matched == old_matched:
403 break
404
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900405 tolines = copy.copy(lines)
406
407 for i in reversed(matched):
408 tolines.pop(i)
409
Simon Glassb2e83c62021-12-18 14:54:31 -0700410 show_diff(lines, tolines, header_path, args.color)
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900411
Simon Glassb2e83c62021-12-18 14:54:31 -0700412 if args.dry_run:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900413 return
414
Simon Glass2fd85bd2021-12-18 14:54:33 -0700415 write_file(header_path, tolines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900416
Simon Glassb2e83c62021-12-18 14:54:31 -0700417def cleanup_headers(configs, args):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900418 """Delete config defines from board headers.
419
Simon Glass91197aa2021-12-18 14:54:35 -0700420 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900421 configs: A list of CONFIGs to remove.
Simon Glass91197aa2021-12-18 14:54:35 -0700422 args (Namespace): program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900423 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700424 if not confirm(args, 'Clean up headers?'):
Chris Packham85edfc12017-05-02 21:30:46 +1200425 return
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900426
427 patterns = []
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900428 for config in configs:
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300429 patterns.append(re.compile(r'#\s*define\s+%s\b' % config))
430 patterns.append(re.compile(r'#\s*undef\s+%s\b' % config))
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900431
Joe Hershberger60727f52015-05-19 13:21:21 -0500432 for dir in 'include', 'arch', 'board':
433 for (dirpath, dirnames, filenames) in os.walk(dir):
Masahiro Yamadadc6de502016-07-25 19:15:22 +0900434 if dirpath == os.path.join('include', 'generated'):
435 continue
Joe Hershberger60727f52015-05-19 13:21:21 -0500436 for filename in filenames:
Simon Glassa38cc172020-08-11 11:23:34 -0600437 if not filename.endswith(('~', '.dts', '.dtsi', '.bin',
Trevor Woernerdc514d72021-03-15 12:01:33 -0400438 '.elf','.aml','.dat')):
Chris Packham4d9dbb12019-01-30 20:23:16 +1300439 header_path = os.path.join(dirpath, filename)
Tom Rini02b56702019-11-10 21:19:37 -0500440 # This file contains UTF-16 data and no CONFIG symbols
441 if header_path == 'include/video_font_data.h':
442 continue
Simon Glassb2e83c62021-12-18 14:54:31 -0700443 cleanup_one_header(header_path, patterns, args)
444 cleanup_empty_blocks(header_path, args)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900445
Chris Packhamf90df592017-05-02 21:30:48 +1200446def find_matching(patterns, line):
447 for pat in patterns:
448 if pat.search(line):
449 return True
450 return False
451
Simon Glassb2e83c62021-12-18 14:54:31 -0700452def cleanup_readme(configs, args):
Chris Packhamf90df592017-05-02 21:30:48 +1200453 """Delete config description in README
454
Simon Glass91197aa2021-12-18 14:54:35 -0700455 Args:
Chris Packhamf90df592017-05-02 21:30:48 +1200456 configs: A list of CONFIGs to remove.
Simon Glass91197aa2021-12-18 14:54:35 -0700457 args (Namespace): program arguments
Chris Packhamf90df592017-05-02 21:30:48 +1200458 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700459 if not confirm(args, 'Clean up README?'):
Chris Packhamf90df592017-05-02 21:30:48 +1200460 return
461
462 patterns = []
463 for config in configs:
464 patterns.append(re.compile(r'^\s+%s' % config))
465
Simon Glass37f815c2021-12-18 14:54:34 -0700466 lines = read_file('README')
Chris Packhamf90df592017-05-02 21:30:48 +1200467
468 found = False
469 newlines = []
470 for line in lines:
471 if not found:
472 found = find_matching(patterns, line)
473 if found:
474 continue
475
476 if found and re.search(r'^\s+CONFIG', line):
477 found = False
478
479 if not found:
480 newlines.append(line)
481
Simon Glass2fd85bd2021-12-18 14:54:33 -0700482 write_file('README', newlines)
Chris Packhamf90df592017-05-02 21:30:48 +1200483
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200484def try_expand(line):
485 """If value looks like an expression, try expanding it
486 Otherwise just return the existing value
487 """
488 if line.find('=') == -1:
489 return line
490
491 try:
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +0100492 aeval = asteval.Interpreter( usersyms=SIZES, minimal=True )
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200493 cfg, val = re.split("=", line)
494 val= val.strip('\"')
Simon Glassdaa694d2021-12-18 14:54:30 -0700495 if re.search(r'[*+-/]|<<|SZ_+|\(([^\)]+)\)', val):
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +0100496 newval = hex(aeval(val))
Simon Glassdaa694d2021-12-18 14:54:30 -0700497 print('\tExpanded expression %s to %s' % (val, newval))
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200498 return cfg+'='+newval
499 except:
Simon Glassdaa694d2021-12-18 14:54:30 -0700500 print('\tFailed to expand expression in %s' % line)
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200501
502 return line
503
Chris Packhamca438342017-05-02 21:30:47 +1200504
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900505### classes ###
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900506class Progress:
507
508 """Progress Indicator"""
509
510 def __init__(self, total):
511 """Create a new progress indicator.
512
Simon Glass91197aa2021-12-18 14:54:35 -0700513 Args:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900514 total: A number of defconfig files to process.
515 """
516 self.current = 0
517 self.total = total
518
519 def inc(self):
520 """Increment the number of processed defconfig files."""
521
522 self.current += 1
523
524 def show(self):
525 """Display the progress."""
Simon Glass793dca32019-10-31 07:42:57 -0600526 print(' %d defconfigs out of %d\r' % (self.current, self.total), end=' ')
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900527 sys.stdout.flush()
528
Simon Glasscb008832017-06-15 21:39:33 -0600529
530class KconfigScanner:
531 """Kconfig scanner."""
532
533 def __init__(self):
534 """Scan all the Kconfig files and create a Config object."""
535 # Define environment variables referenced from Kconfig
536 os.environ['srctree'] = os.getcwd()
537 os.environ['UBOOTVERSION'] = 'dummy'
538 os.environ['KCONFIG_OBJDIR'] = ''
Tom Rini65e05dd2019-09-20 17:42:09 -0400539 self.conf = kconfiglib.Kconfig()
Simon Glasscb008832017-06-15 21:39:33 -0600540
541
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900542class KconfigParser:
543
544 """A parser of .config and include/autoconf.mk."""
545
546 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
547 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
548
Simon Glassb2e83c62021-12-18 14:54:31 -0700549 def __init__(self, configs, args, build_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900550 """Create a new parser.
551
Simon Glass91197aa2021-12-18 14:54:35 -0700552 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900553 configs: A list of CONFIGs to move.
Simon Glass91197aa2021-12-18 14:54:35 -0700554 args (Namespace): program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900555 build_dir: Build directory.
556 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900557 self.configs = configs
Simon Glassb2e83c62021-12-18 14:54:31 -0700558 self.args = args
Masahiro Yamada1f169922016-05-19 15:52:00 +0900559 self.dotconfig = os.path.join(build_dir, '.config')
560 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
Masahiro Yamada07913d12016-08-22 22:18:22 +0900561 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
562 'autoconf.mk')
Simon Glassf3b8e642017-06-01 19:39:01 -0600563 self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900564 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900565
Simon Glass6821a742017-07-10 14:47:47 -0600566 def get_arch(self):
567 """Parse .config file and return the architecture.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900568
569 Returns:
Simon Glass6821a742017-07-10 14:47:47 -0600570 Architecture name (e.g. 'arm').
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900571 """
572 arch = ''
573 cpu = ''
Simon Glass37f815c2021-12-18 14:54:34 -0700574 for line in read_file(self.dotconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900575 m = self.re_arch.match(line)
576 if m:
577 arch = m.group(1)
578 continue
579 m = self.re_cpu.match(line)
580 if m:
581 cpu = m.group(1)
582
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900583 if not arch:
584 return None
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900585
586 # fix-up for aarch64
587 if arch == 'arm' and cpu == 'armv8':
588 arch = 'aarch64'
589
Simon Glass6821a742017-07-10 14:47:47 -0600590 return arch
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900591
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900592 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900593 """Parse .config, defconfig, include/autoconf.mk for one config.
594
595 This function looks for the config options in the lines from
596 defconfig, .config, and include/autoconf.mk in order to decide
597 which action should be taken for this defconfig.
598
Simon Glass91197aa2021-12-18 14:54:35 -0700599 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900600 config: CONFIG name to parse.
Masahiro Yamadacc008292016-05-19 15:51:56 +0900601 dotconfig_lines: lines from the .config file.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900602 autoconf_lines: lines from the include/autoconf.mk file.
603
604 Returns:
605 A tupple of the action for this defconfig and the line
606 matched for the config.
607 """
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900608 not_set = '# %s is not set' % config
609
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900610 for line in autoconf_lines:
611 line = line.rstrip()
612 if line.startswith(config + '='):
Masahiro Yamadacc008292016-05-19 15:51:56 +0900613 new_val = line
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900614 break
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900615 else:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900616 new_val = not_set
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900617
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200618 new_val = try_expand(new_val)
619
Masahiro Yamada916224c2016-08-22 22:18:21 +0900620 for line in dotconfig_lines:
621 line = line.rstrip()
622 if line.startswith(config + '=') or line == not_set:
623 old_val = line
624 break
625 else:
626 if new_val == not_set:
627 return (ACTION_NO_ENTRY, config)
628 else:
629 return (ACTION_NO_ENTRY_WARN, config)
630
Masahiro Yamadacc008292016-05-19 15:51:56 +0900631 # If this CONFIG is neither bool nor trisate
632 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
633 # tools/scripts/define2mk.sed changes '1' to 'y'.
634 # This is a problem if the CONFIG is int type.
635 # Check the type in Kconfig and handle it correctly.
636 if new_val[-2:] == '=y':
637 new_val = new_val[:-1] + '1'
638
Masahiro Yamada50301592016-06-15 14:33:50 +0900639 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
640 new_val)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900641
Masahiro Yamada1d085562016-05-19 15:52:02 +0900642 def update_dotconfig(self):
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900643 """Parse files for the config options and update the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900644
Masahiro Yamadacc008292016-05-19 15:51:56 +0900645 This function parses the generated .config and include/autoconf.mk
646 searching the target options.
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900647 Move the config option(s) to the .config as needed.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900648
Simon Glass91197aa2021-12-18 14:54:35 -0700649 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900650 defconfig: defconfig name.
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900651
652 Returns:
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900653 Return a tuple of (updated flag, log string).
654 The "updated flag" is True if the .config was updated, False
655 otherwise. The "log string" shows what happend to the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900656 """
657
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900658 results = []
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900659 updated = False
Masahiro Yamada916224c2016-08-22 22:18:21 +0900660 suspicious = False
Masahiro Yamada07913d12016-08-22 22:18:22 +0900661 rm_files = [self.config_autoconf, self.autoconf]
662
Simon Glassb2e83c62021-12-18 14:54:31 -0700663 if self.args.spl:
Masahiro Yamada07913d12016-08-22 22:18:22 +0900664 if os.path.exists(self.spl_autoconf):
665 autoconf_path = self.spl_autoconf
666 rm_files.append(self.spl_autoconf)
667 else:
668 for f in rm_files:
669 os.remove(f)
670 return (updated, suspicious,
Simon Glassb2e83c62021-12-18 14:54:31 -0700671 color_text(self.args.color, COLOR_BROWN,
Masahiro Yamada07913d12016-08-22 22:18:22 +0900672 "SPL is not enabled. Skipped.") + '\n')
673 else:
674 autoconf_path = self.autoconf
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900675
Simon Glass37f815c2021-12-18 14:54:34 -0700676 dotconfig_lines = read_file(self.dotconfig)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900677
Simon Glass37f815c2021-12-18 14:54:34 -0700678 autoconf_lines = read_file(autoconf_path)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900679
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900680 for config in self.configs:
681 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger96464ba2015-05-19 13:21:17 -0500682 autoconf_lines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900683 results.append(result)
684
685 log = ''
686
687 for (action, value) in results:
688 if action == ACTION_MOVE:
689 actlog = "Move '%s'" % value
690 log_color = COLOR_LIGHT_GREEN
Masahiro Yamadacc008292016-05-19 15:51:56 +0900691 elif action == ACTION_NO_ENTRY:
Simon Glassdaa694d2021-12-18 14:54:30 -0700692 actlog = '%s is not defined in Kconfig. Do nothing.' % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900693 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada916224c2016-08-22 22:18:21 +0900694 elif action == ACTION_NO_ENTRY_WARN:
Simon Glassdaa694d2021-12-18 14:54:30 -0700695 actlog = '%s is not defined in Kconfig (suspicious). Do nothing.' % value
Masahiro Yamada916224c2016-08-22 22:18:21 +0900696 log_color = COLOR_YELLOW
697 suspicious = True
Masahiro Yamadacc008292016-05-19 15:51:56 +0900698 elif action == ACTION_NO_CHANGE:
699 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
700 % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900701 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900702 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700703 sys.exit('Internal Error. This should not happen.')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900704
Simon Glassb2e83c62021-12-18 14:54:31 -0700705 log += color_text(self.args.color, log_color, actlog) + '\n'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900706
Simon Glass91197aa2021-12-18 14:54:35 -0700707 with open(self.dotconfig, 'a', encoding='utf-8') as out:
Masahiro Yamadae423d172016-05-19 15:51:49 +0900708 for (action, value) in results:
709 if action == ACTION_MOVE:
Simon Glass91197aa2021-12-18 14:54:35 -0700710 out.write(value + '\n')
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900711 updated = True
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900712
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900713 self.results = results
Masahiro Yamada07913d12016-08-22 22:18:22 +0900714 for f in rm_files:
715 os.remove(f)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900716
Masahiro Yamada916224c2016-08-22 22:18:21 +0900717 return (updated, suspicious, log)
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900718
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900719 def check_defconfig(self):
720 """Check the defconfig after savedefconfig
721
722 Returns:
723 Return additional log if moved CONFIGs were removed again by
724 'make savedefconfig'.
725 """
726
727 log = ''
728
Simon Glass37f815c2021-12-18 14:54:34 -0700729 defconfig_lines = read_file(self.defconfig)
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900730
731 for (action, value) in self.results:
732 if action != ACTION_MOVE:
733 continue
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300734 if not value in defconfig_lines:
Simon Glassb2e83c62021-12-18 14:54:31 -0700735 log += color_text(self.args.color, COLOR_YELLOW,
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900736 "'%s' was removed by savedefconfig.\n" %
737 value)
738
739 return log
740
Simon Glassd73fcb12017-06-01 19:39:02 -0600741
742class DatabaseThread(threading.Thread):
743 """This thread processes results from Slot threads.
744
745 It collects the data in the master config directary. There is only one
746 result thread, and this helps to serialise the build output.
747 """
748 def __init__(self, config_db, db_queue):
749 """Set up a new result thread
750
751 Args:
752 builder: Builder which will be sent each result
753 """
754 threading.Thread.__init__(self)
755 self.config_db = config_db
756 self.db_queue= db_queue
757
758 def run(self):
759 """Called to start up the result thread.
760
761 We collect the next result job and pass it on to the build.
762 """
763 while True:
764 defconfig, configs = self.db_queue.get()
765 self.config_db[defconfig] = configs
766 self.db_queue.task_done()
767
768
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900769class Slot:
770
771 """A slot to store a subprocess.
772
773 Each instance of this class handles one subprocess.
774 This class is useful to control multiple threads
775 for faster processing.
776 """
777
Simon Glassb2e83c62021-12-18 14:54:31 -0700778 def __init__(self, toolchains, configs, args, progress, devnull,
Simon Glass6821a742017-07-10 14:47:47 -0600779 make_cmd, reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900780 """Create a new process slot.
781
Simon Glass91197aa2021-12-18 14:54:35 -0700782 Args:
Simon Glass6821a742017-07-10 14:47:47 -0600783 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900784 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -0700785 args: Program arguments
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900786 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900787 devnull: A file object of '/dev/null'.
788 make_cmd: command name of GNU Make.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500789 reference_src_dir: Determine the true starting config state from this
790 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -0600791 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900792 """
Simon Glass6821a742017-07-10 14:47:47 -0600793 self.toolchains = toolchains
Simon Glassb2e83c62021-12-18 14:54:31 -0700794 self.args = args
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900795 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900796 self.build_dir = tempfile.mkdtemp()
797 self.devnull = devnull
798 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500799 self.reference_src_dir = reference_src_dir
Simon Glassd73fcb12017-06-01 19:39:02 -0600800 self.db_queue = db_queue
Simon Glassb2e83c62021-12-18 14:54:31 -0700801 self.parser = KconfigParser(configs, args, self.build_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900802 self.state = STATE_IDLE
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900803 self.failed_boards = set()
804 self.suspicious_boards = set()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900805
806 def __del__(self):
807 """Delete the working directory
808
809 This function makes sure the temporary directory is cleaned away
810 even if Python suddenly dies due to error. It should be done in here
Joe Hershbergerf2dae752016-06-10 14:53:29 -0500811 because it is guaranteed the destructor is always invoked when the
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900812 instance of the class gets unreferenced.
813
814 If the subprocess is still running, wait until it finishes.
815 """
816 if self.state != STATE_IDLE:
817 while self.ps.poll() == None:
818 pass
819 shutil.rmtree(self.build_dir)
820
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900821 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900822 """Assign a new subprocess for defconfig and add it to the slot.
823
824 If the slot is vacant, create a new subprocess for processing the
825 given defconfig and add it to the slot. Just returns False if
826 the slot is occupied (i.e. the current subprocess is still running).
827
Simon Glass91197aa2021-12-18 14:54:35 -0700828 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900829 defconfig: defconfig name.
830
831 Returns:
832 Return True on success or False on failure
833 """
834 if self.state != STATE_IDLE:
835 return False
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900836
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900837 self.defconfig = defconfig
Masahiro Yamada1d085562016-05-19 15:52:02 +0900838 self.log = ''
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900839 self.current_src_dir = self.reference_src_dir
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900840 self.do_defconfig()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900841 return True
842
843 def poll(self):
844 """Check the status of the subprocess and handle it as needed.
845
846 Returns True if the slot is vacant (i.e. in idle state).
847 If the configuration is successfully finished, assign a new
848 subprocess to build include/autoconf.mk.
849 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900850 parse the .config and the include/autoconf.mk, moving
851 config options to the .config as needed.
852 If the .config was updated, run "make savedefconfig" to sync
853 it, update the original defconfig, and then set the slot back
854 to the idle state.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900855
856 Returns:
857 Return True if the subprocess is terminated, False otherwise
858 """
859 if self.state == STATE_IDLE:
860 return True
861
862 if self.ps.poll() == None:
863 return False
864
865 if self.ps.poll() != 0:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900866 self.handle_error()
867 elif self.state == STATE_DEFCONFIG:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900868 if self.reference_src_dir and not self.current_src_dir:
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500869 self.do_savedefconfig()
870 else:
871 self.do_autoconf()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900872 elif self.state == STATE_AUTOCONF:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900873 if self.current_src_dir:
874 self.current_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500875 self.do_defconfig()
Simon Glassb2e83c62021-12-18 14:54:31 -0700876 elif self.args.build_db:
Simon Glassd73fcb12017-06-01 19:39:02 -0600877 self.do_build_db()
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500878 else:
879 self.do_savedefconfig()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900880 elif self.state == STATE_SAVEDEFCONFIG:
881 self.update_defconfig()
882 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700883 sys.exit('Internal Error. This should not happen.')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900884
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900885 return True if self.state == STATE_IDLE else False
Joe Hershberger96464ba2015-05-19 13:21:17 -0500886
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900887 def handle_error(self):
888 """Handle error cases."""
Masahiro Yamada8513dc02016-05-19 15:52:08 +0900889
Simon Glassb2e83c62021-12-18 14:54:31 -0700890 self.log += color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glassdaa694d2021-12-18 14:54:30 -0700891 'Failed to process.\n')
Simon Glassb2e83c62021-12-18 14:54:31 -0700892 if self.args.verbose:
893 self.log += color_text(self.args.color, COLOR_LIGHT_CYAN,
Markus Klotzbuecher4f5c5e92020-02-12 20:46:45 +0100894 self.ps.stderr.read().decode())
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900895 self.finish(False)
Joe Hershberger96464ba2015-05-19 13:21:17 -0500896
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900897 def do_defconfig(self):
898 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900899
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900900 cmd = list(self.make_cmd)
901 cmd.append(self.defconfig)
902 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900903 stderr=subprocess.PIPE,
904 cwd=self.current_src_dir)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900905 self.state = STATE_DEFCONFIG
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900906
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900907 def do_autoconf(self):
Simon Glassf3b8e642017-06-01 19:39:01 -0600908 """Run 'make AUTO_CONF_PATH'."""
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900909
Simon Glass6821a742017-07-10 14:47:47 -0600910 arch = self.parser.get_arch()
911 try:
912 toolchain = self.toolchains.Select(arch)
913 except ValueError:
Simon Glassb2e83c62021-12-18 14:54:31 -0700914 self.log += color_text(self.args.color, COLOR_YELLOW,
Chris Packhamce3ba452017-08-27 20:00:51 +1200915 "Tool chain for '%s' is missing. Do nothing.\n" % arch)
Masahiro Yamada4efef992016-05-19 15:52:03 +0900916 self.finish(False)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900917 return
Simon Glass793dca32019-10-31 07:42:57 -0600918 env = toolchain.MakeEnvironment(False)
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900919
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900920 cmd = list(self.make_cmd)
Joe Hershberger7740f652015-05-19 13:21:18 -0500921 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Simon Glassf3b8e642017-06-01 19:39:01 -0600922 cmd.append(AUTO_CONF_PATH)
Simon Glass6821a742017-07-10 14:47:47 -0600923 self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900924 stderr=subprocess.PIPE,
925 cwd=self.current_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900926 self.state = STATE_AUTOCONF
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900927
Simon Glassd73fcb12017-06-01 19:39:02 -0600928 def do_build_db(self):
929 """Add the board to the database"""
930 configs = {}
Simon Glass37f815c2021-12-18 14:54:34 -0700931 for line in read_file(os.path.join(self.build_dir, AUTO_CONF_PATH)):
932 if line.startswith('CONFIG'):
933 config, value = line.split('=', 1)
934 configs[config] = value.rstrip()
Simon Glassd73fcb12017-06-01 19:39:02 -0600935 self.db_queue.put([self.defconfig, configs])
936 self.finish(True)
937
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900938 def do_savedefconfig(self):
939 """Update the .config and run 'make savedefconfig'."""
940
Masahiro Yamada916224c2016-08-22 22:18:21 +0900941 (updated, suspicious, log) = self.parser.update_dotconfig()
942 if suspicious:
943 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900944 self.log += log
945
Simon Glassb2e83c62021-12-18 14:54:31 -0700946 if not self.args.force_sync and not updated:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900947 self.finish(True)
948 return
949 if updated:
Simon Glassb2e83c62021-12-18 14:54:31 -0700950 self.log += color_text(self.args.color, COLOR_LIGHT_GREEN,
Simon Glassdaa694d2021-12-18 14:54:30 -0700951 'Syncing by savedefconfig...\n')
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900952 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700953 self.log += 'Syncing by savedefconfig (forced by option)...\n'
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900954
955 cmd = list(self.make_cmd)
956 cmd.append('savedefconfig')
957 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
958 stderr=subprocess.PIPE)
959 self.state = STATE_SAVEDEFCONFIG
960
961 def update_defconfig(self):
962 """Update the input defconfig and go back to the idle state."""
963
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900964 log = self.parser.check_defconfig()
965 if log:
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900966 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900967 self.log += log
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900968 orig_defconfig = os.path.join('configs', self.defconfig)
969 new_defconfig = os.path.join(self.build_dir, 'defconfig')
970 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
971
972 if updated:
Simon Glassb2e83c62021-12-18 14:54:31 -0700973 self.log += color_text(self.args.color, COLOR_LIGHT_BLUE,
Simon Glassdaa694d2021-12-18 14:54:30 -0700974 'defconfig was updated.\n')
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900975
Simon Glassb2e83c62021-12-18 14:54:31 -0700976 if not self.args.dry_run and updated:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900977 shutil.move(new_defconfig, orig_defconfig)
978 self.finish(True)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900979
Masahiro Yamada4efef992016-05-19 15:52:03 +0900980 def finish(self, success):
981 """Display log along with progress and go to the idle state.
Masahiro Yamada1d085562016-05-19 15:52:02 +0900982
Simon Glass91197aa2021-12-18 14:54:35 -0700983 Args:
Masahiro Yamada4efef992016-05-19 15:52:03 +0900984 success: Should be True when the defconfig was processed
985 successfully, or False when it fails.
Masahiro Yamada1d085562016-05-19 15:52:02 +0900986 """
987 # output at least 30 characters to hide the "* defconfigs out of *".
988 log = self.defconfig.ljust(30) + '\n'
989
990 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
991 # Some threads are running in parallel.
992 # Print log atomically to not mix up logs from different threads.
Simon Glass793dca32019-10-31 07:42:57 -0600993 print(log, file=(sys.stdout if success else sys.stderr))
Masahiro Yamada4efef992016-05-19 15:52:03 +0900994
995 if not success:
Simon Glassb2e83c62021-12-18 14:54:31 -0700996 if self.args.exit_on_error:
Simon Glassdaa694d2021-12-18 14:54:30 -0700997 sys.exit('Exit on error.')
Masahiro Yamada4efef992016-05-19 15:52:03 +0900998 # If --exit-on-error flag is not set, skip this board and continue.
999 # Record the failed board.
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001000 self.failed_boards.add(self.defconfig)
Masahiro Yamada4efef992016-05-19 15:52:03 +09001001
Masahiro Yamada1d085562016-05-19 15:52:02 +09001002 self.progress.inc()
1003 self.progress.show()
Masahiro Yamada4efef992016-05-19 15:52:03 +09001004 self.state = STATE_IDLE
Masahiro Yamada1d085562016-05-19 15:52:02 +09001005
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001006 def get_failed_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001007 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001008 """
1009 return self.failed_boards
1010
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001011 def get_suspicious_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001012 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001013 """
Masahiro Yamada916224c2016-08-22 22:18:21 +09001014 return self.suspicious_boards - self.failed_boards
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001015
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001016class Slots:
1017
1018 """Controller of the array of subprocess slots."""
1019
Simon Glassb2e83c62021-12-18 14:54:31 -07001020 def __init__(self, toolchains, configs, args, progress,
Simon Glass6821a742017-07-10 14:47:47 -06001021 reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001022 """Create a new slots controller.
1023
Simon Glass91197aa2021-12-18 14:54:35 -07001024 Args:
Simon Glass6821a742017-07-10 14:47:47 -06001025 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001026 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -07001027 args: Program arguments
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001028 progress: A progress indicator.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001029 reference_src_dir: Determine the true starting config state from this
1030 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -06001031 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001032 """
Simon Glassb2e83c62021-12-18 14:54:31 -07001033 self.args = args
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001034 self.slots = []
Simon Glass478920d2021-12-18 14:54:32 -07001035 devnull = subprocess.DEVNULL
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001036 make_cmd = get_make_cmd()
Simon Glassb2e83c62021-12-18 14:54:31 -07001037 for i in range(args.jobs):
1038 self.slots.append(Slot(toolchains, configs, args, progress,
Simon Glass6821a742017-07-10 14:47:47 -06001039 devnull, make_cmd, reference_src_dir,
1040 db_queue))
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001041
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001042 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001043 """Add a new subprocess if a vacant slot is found.
1044
Simon Glass91197aa2021-12-18 14:54:35 -07001045 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001046 defconfig: defconfig name to be put into.
1047
1048 Returns:
1049 Return True on success or False on failure
1050 """
1051 for slot in self.slots:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001052 if slot.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001053 return True
1054 return False
1055
1056 def available(self):
1057 """Check if there is a vacant slot.
1058
1059 Returns:
1060 Return True if at lease one vacant slot is found, False otherwise.
1061 """
1062 for slot in self.slots:
1063 if slot.poll():
1064 return True
1065 return False
1066
1067 def empty(self):
1068 """Check if all slots are vacant.
1069
1070 Returns:
1071 Return True if all the slots are vacant, False otherwise.
1072 """
1073 ret = True
1074 for slot in self.slots:
1075 if not slot.poll():
1076 ret = False
1077 return ret
1078
1079 def show_failed_boards(self):
1080 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001081 boards = set()
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001082 output_file = 'moveconfig.failed'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001083
1084 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001085 boards |= slot.get_failed_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001086
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001087 if boards:
1088 boards = '\n'.join(boards) + '\n'
Simon Glassdaa694d2021-12-18 14:54:30 -07001089 msg = 'The following boards were not processed due to error:\n'
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001090 msg += boards
Simon Glassdaa694d2021-12-18 14:54:30 -07001091 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassb2e83c62021-12-18 14:54:31 -07001092 print(color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glass793dca32019-10-31 07:42:57 -06001093 msg), file=sys.stderr)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001094
Simon Glass2fd85bd2021-12-18 14:54:33 -07001095 write_file(output_file, boards)
Joe Hershberger2559cd82015-05-19 13:21:22 -05001096
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001097 def show_suspicious_boards(self):
1098 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001099 boards = set()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001100 output_file = 'moveconfig.suspicious'
1101
1102 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001103 boards |= slot.get_suspicious_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001104
1105 if boards:
1106 boards = '\n'.join(boards) + '\n'
Simon Glassdaa694d2021-12-18 14:54:30 -07001107 msg = 'The following boards might have been converted incorrectly.\n'
1108 msg += 'It is highly recommended to check them manually:\n'
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001109 msg += boards
Simon Glassdaa694d2021-12-18 14:54:30 -07001110 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassb2e83c62021-12-18 14:54:31 -07001111 print(color_text(self.args.color, COLOR_YELLOW,
Simon Glass793dca32019-10-31 07:42:57 -06001112 msg), file=sys.stderr)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001113
Simon Glass2fd85bd2021-12-18 14:54:33 -07001114 write_file(output_file, boards)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001115
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001116class ReferenceSource:
1117
1118 """Reference source against which original configs should be parsed."""
1119
1120 def __init__(self, commit):
1121 """Create a reference source directory based on a specified commit.
1122
Simon Glass91197aa2021-12-18 14:54:35 -07001123 Args:
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001124 commit: commit to git-clone
1125 """
1126 self.src_dir = tempfile.mkdtemp()
Simon Glassdaa694d2021-12-18 14:54:30 -07001127 print('Cloning git repo to a separate work directory...')
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001128 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1129 cwd=self.src_dir)
Simon Glass793dca32019-10-31 07:42:57 -06001130 print("Checkout '%s' to build the original autoconf.mk." % \
1131 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip())
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001132 subprocess.check_output(['git', 'checkout', commit],
1133 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001134
1135 def __del__(self):
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001136 """Delete the reference source directory
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001137
1138 This function makes sure the temporary directory is cleaned away
1139 even if Python suddenly dies due to error. It should be done in here
1140 because it is guaranteed the destructor is always invoked when the
1141 instance of the class gets unreferenced.
1142 """
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001143 shutil.rmtree(self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001144
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001145 def get_dir(self):
1146 """Return the absolute path to the reference source directory."""
1147
1148 return self.src_dir
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001149
Simon Glassb2e83c62021-12-18 14:54:31 -07001150def move_config(toolchains, configs, args, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001151 """Move config options to defconfig files.
1152
Simon Glass91197aa2021-12-18 14:54:35 -07001153 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001154 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -07001155 args: Program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001156 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001157 if len(configs) == 0:
Simon Glassb2e83c62021-12-18 14:54:31 -07001158 if args.force_sync:
Simon Glass793dca32019-10-31 07:42:57 -06001159 print('No CONFIG is specified. You are probably syncing defconfigs.', end=' ')
Simon Glassb2e83c62021-12-18 14:54:31 -07001160 elif args.build_db:
Simon Glass793dca32019-10-31 07:42:57 -06001161 print('Building %s database' % CONFIG_DATABASE)
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001162 else:
Simon Glass793dca32019-10-31 07:42:57 -06001163 print('Neither CONFIG nor --force-sync is specified. Nothing will happen.', end=' ')
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001164 else:
Simon Glass793dca32019-10-31 07:42:57 -06001165 print('Move ' + ', '.join(configs), end=' ')
Simon Glassb2e83c62021-12-18 14:54:31 -07001166 print('(jobs: %d)\n' % args.jobs)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001167
Simon Glassb2e83c62021-12-18 14:54:31 -07001168 if args.git_ref:
1169 reference_src = ReferenceSource(args.git_ref)
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001170 reference_src_dir = reference_src.get_dir()
1171 else:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001172 reference_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001173
Simon Glassb2e83c62021-12-18 14:54:31 -07001174 if args.defconfigs:
1175 defconfigs = get_matched_defconfigs(args.defconfigs)
Joe Hershberger91040e82015-05-19 13:21:19 -05001176 else:
Masahiro Yamada684c3062016-07-25 19:15:28 +09001177 defconfigs = get_all_defconfigs()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001178
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001179 progress = Progress(len(defconfigs))
Simon Glassb2e83c62021-12-18 14:54:31 -07001180 slots = Slots(toolchains, configs, args, progress, reference_src_dir,
Simon Glass91197aa2021-12-18 14:54:35 -07001181 db_queue)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001182
1183 # Main loop to process defconfig files:
1184 # Add a new subprocess into a vacant slot.
1185 # Sleep if there is no available slot.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001186 for defconfig in defconfigs:
1187 while not slots.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001188 while not slots.available():
1189 # No available slot: sleep for a while
1190 time.sleep(SLEEP_TIME)
1191
1192 # wait until all the subprocesses finish
1193 while not slots.empty():
1194 time.sleep(SLEEP_TIME)
1195
Simon Glass793dca32019-10-31 07:42:57 -06001196 print('')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001197 slots.show_failed_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001198 slots.show_suspicious_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001199
Simon Glasscb008832017-06-15 21:39:33 -06001200def find_kconfig_rules(kconf, config, imply_config):
1201 """Check whether a config has a 'select' or 'imply' keyword
1202
1203 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -04001204 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -06001205 config: Name of config to check (without CONFIG_ prefix)
1206 imply_config: Implying config (without CONFIG_ prefix) which may or
1207 may not have an 'imply' for 'config')
1208
1209 Returns:
1210 Symbol object for 'config' if found, else None
1211 """
Tom Rini65e05dd2019-09-20 17:42:09 -04001212 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -06001213 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001214 for sel, cond in (sym.selects + sym.implies):
Simon Glassa3627082021-12-18 08:09:42 -07001215 if sel.name == config:
Simon Glasscb008832017-06-15 21:39:33 -06001216 return sym
1217 return None
1218
1219def check_imply_rule(kconf, config, imply_config):
1220 """Check if we can add an 'imply' option
1221
1222 This finds imply_config in the Kconfig and looks to see if it is possible
1223 to add an 'imply' for 'config' to that part of the Kconfig.
1224
1225 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -04001226 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -06001227 config: Name of config to check (without CONFIG_ prefix)
1228 imply_config: Implying config (without CONFIG_ prefix) which may or
1229 may not have an 'imply' for 'config')
1230
1231 Returns:
1232 tuple:
1233 filename of Kconfig file containing imply_config, or None if none
1234 line number within the Kconfig file, or 0 if none
1235 message indicating the result
1236 """
Tom Rini65e05dd2019-09-20 17:42:09 -04001237 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -06001238 if not sym:
1239 return 'cannot find sym'
Simon Glassea40b202021-07-21 21:35:53 -06001240 nodes = sym.nodes
1241 if len(nodes) != 1:
1242 return '%d locations' % len(nodes)
Simon Glassa3627082021-12-18 08:09:42 -07001243 node = nodes[0]
1244 fname, linenum = node.filename, node.linenr
Simon Glasscb008832017-06-15 21:39:33 -06001245 cwd = os.getcwd()
1246 if cwd and fname.startswith(cwd):
1247 fname = fname[len(cwd) + 1:]
1248 file_line = ' at %s:%d' % (fname, linenum)
Simon Glass37f815c2021-12-18 14:54:34 -07001249 data = read_file(fname)
Simon Glasscb008832017-06-15 21:39:33 -06001250 if data[linenum - 1] != 'config %s' % imply_config:
1251 return None, 0, 'bad sym format %s%s' % (data[linenum], file_line)
1252 return fname, linenum, 'adding%s' % file_line
1253
1254def add_imply_rule(config, fname, linenum):
1255 """Add a new 'imply' option to a Kconfig
1256
1257 Args:
1258 config: config option to add an imply for (without CONFIG_ prefix)
1259 fname: Kconfig filename to update
1260 linenum: Line number to place the 'imply' before
1261
1262 Returns:
1263 Message indicating the result
1264 """
1265 file_line = ' at %s:%d' % (fname, linenum)
Simon Glass37f815c2021-12-18 14:54:34 -07001266 data = read_file(fname)
Simon Glasscb008832017-06-15 21:39:33 -06001267 linenum -= 1
1268
1269 for offset, line in enumerate(data[linenum:]):
1270 if line.strip().startswith('help') or not line:
1271 data.insert(linenum + offset, '\timply %s' % config)
Simon Glass2fd85bd2021-12-18 14:54:33 -07001272 write_file(fname, data)
Simon Glasscb008832017-06-15 21:39:33 -06001273 return 'added%s' % file_line
1274
1275 return 'could not insert%s'
1276
1277(IMPLY_MIN_2, IMPLY_TARGET, IMPLY_CMD, IMPLY_NON_ARCH_BOARD) = (
1278 1, 2, 4, 8)
Simon Glass9b2a2e82017-06-15 21:39:32 -06001279
1280IMPLY_FLAGS = {
1281 'min2': [IMPLY_MIN_2, 'Show options which imply >2 boards (normally >5)'],
1282 'target': [IMPLY_TARGET, 'Allow CONFIG_TARGET_... options to imply'],
1283 'cmd': [IMPLY_CMD, 'Allow CONFIG_CMD_... to imply'],
Simon Glasscb008832017-06-15 21:39:33 -06001284 'non-arch-board': [
1285 IMPLY_NON_ARCH_BOARD,
1286 'Allow Kconfig options outside arch/ and /board/ to imply'],
Simon Glass91197aa2021-12-18 14:54:35 -07001287}
Simon Glass9b2a2e82017-06-15 21:39:32 -06001288
Simon Glass9d603392021-12-18 08:09:43 -07001289
1290def read_database():
1291 """Read in the config database
1292
1293 Returns:
1294 tuple:
1295 set of all config options seen (each a str)
1296 set of all defconfigs seen (each a str)
1297 dict of configs for each defconfig:
1298 key: defconfig name, e.g. "MPC8548CDS_legacy_defconfig"
1299 value: dict:
1300 key: CONFIG option
1301 value: Value of option
1302 dict of defconfigs for each config:
1303 key: CONFIG option
1304 value: set of boards using that option
1305
1306 """
1307 configs = {}
1308
1309 # key is defconfig name, value is dict of (CONFIG_xxx, value)
1310 config_db = {}
1311
1312 # Set of all config options we have seen
1313 all_configs = set()
1314
1315 # Set of all defconfigs we have seen
1316 all_defconfigs = set()
1317
1318 defconfig_db = collections.defaultdict(set)
Simon Glass37f815c2021-12-18 14:54:34 -07001319 for line in read_file(CONFIG_DATABASE):
1320 line = line.rstrip()
1321 if not line: # Separator between defconfigs
1322 config_db[defconfig] = configs
1323 all_defconfigs.add(defconfig)
1324 configs = {}
1325 elif line[0] == ' ': # CONFIG line
1326 config, value = line.strip().split('=', 1)
1327 configs[config] = value
1328 defconfig_db[config].add(defconfig)
1329 all_configs.add(config)
1330 else: # New defconfig
1331 defconfig = line
Simon Glass9d603392021-12-18 08:09:43 -07001332
1333 return all_configs, all_defconfigs, config_db, defconfig_db
1334
1335
Simon Glasscb008832017-06-15 21:39:33 -06001336def do_imply_config(config_list, add_imply, imply_flags, skip_added,
1337 check_kconfig=True, find_superset=False):
Simon Glass99b66602017-06-01 19:39:03 -06001338 """Find CONFIG options which imply those in the list
1339
1340 Some CONFIG options can be implied by others and this can help to reduce
1341 the size of the defconfig files. For example, CONFIG_X86 implies
1342 CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
1343 all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
1344 each of the x86 defconfig files.
1345
1346 This function uses the moveconfig database to find such options. It
1347 displays a list of things that could possibly imply those in the list.
1348 The algorithm ignores any that start with CONFIG_TARGET since these
1349 typically refer to only a few defconfigs (often one). It also does not
1350 display a config with less than 5 defconfigs.
1351
1352 The algorithm works using sets. For each target config in config_list:
1353 - Get the set 'defconfigs' which use that target config
1354 - For each config (from a list of all configs):
1355 - Get the set 'imply_defconfig' of defconfigs which use that config
1356 -
1357 - If imply_defconfigs contains anything not in defconfigs then
1358 this config does not imply the target config
1359
1360 Params:
1361 config_list: List of CONFIG options to check (each a string)
Simon Glasscb008832017-06-15 21:39:33 -06001362 add_imply: Automatically add an 'imply' for each config.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001363 imply_flags: Flags which control which implying configs are allowed
1364 (IMPLY_...)
Simon Glasscb008832017-06-15 21:39:33 -06001365 skip_added: Don't show options which already have an imply added.
1366 check_kconfig: Check if implied symbols already have an 'imply' or
1367 'select' for the target config, and show this information if so.
Simon Glass99b66602017-06-01 19:39:03 -06001368 find_superset: True to look for configs which are a superset of those
1369 already found. So for example if CONFIG_EXYNOS5 implies an option,
1370 but CONFIG_EXYNOS covers a larger set of defconfigs and also
1371 implies that option, this will drop the former in favour of the
1372 latter. In practice this option has not proved very used.
1373
1374 Note the terminoloy:
1375 config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
1376 defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
1377 """
Simon Glasscb008832017-06-15 21:39:33 -06001378 kconf = KconfigScanner().conf if check_kconfig else None
1379 if add_imply and add_imply != 'all':
Simon Glassa3627082021-12-18 08:09:42 -07001380 add_imply = add_imply.split(',')
Simon Glasscb008832017-06-15 21:39:33 -06001381
Simon Glass9d603392021-12-18 08:09:43 -07001382 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
Simon Glass99b66602017-06-01 19:39:03 -06001383
Simon Glassa3627082021-12-18 08:09:42 -07001384 # Work through each target config option in turn, independently
Simon Glass99b66602017-06-01 19:39:03 -06001385 for config in config_list:
1386 defconfigs = defconfig_db.get(config)
1387 if not defconfigs:
Simon Glass793dca32019-10-31 07:42:57 -06001388 print('%s not found in any defconfig' % config)
Simon Glass99b66602017-06-01 19:39:03 -06001389 continue
1390
1391 # Get the set of defconfigs without this one (since a config cannot
1392 # imply itself)
1393 non_defconfigs = all_defconfigs - defconfigs
1394 num_defconfigs = len(defconfigs)
Simon Glass793dca32019-10-31 07:42:57 -06001395 print('%s found in %d/%d defconfigs' % (config, num_defconfigs,
1396 len(all_configs)))
Simon Glass99b66602017-06-01 19:39:03 -06001397
1398 # This will hold the results: key=config, value=defconfigs containing it
1399 imply_configs = {}
1400 rest_configs = all_configs - set([config])
1401
1402 # Look at every possible config, except the target one
1403 for imply_config in rest_configs:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001404 if 'ERRATUM' in imply_config:
Simon Glass99b66602017-06-01 19:39:03 -06001405 continue
Simon Glass91197aa2021-12-18 14:54:35 -07001406 if not imply_flags & IMPLY_CMD:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001407 if 'CONFIG_CMD' in imply_config:
1408 continue
Simon Glass91197aa2021-12-18 14:54:35 -07001409 if not imply_flags & IMPLY_TARGET:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001410 if 'CONFIG_TARGET' in imply_config:
1411 continue
Simon Glass99b66602017-06-01 19:39:03 -06001412
1413 # Find set of defconfigs that have this config
1414 imply_defconfig = defconfig_db[imply_config]
1415
1416 # Get the intersection of this with defconfigs containing the
1417 # target config
1418 common_defconfigs = imply_defconfig & defconfigs
1419
1420 # Get the set of defconfigs containing this config which DO NOT
1421 # also contain the taret config. If this set is non-empty it means
1422 # that this config affects other defconfigs as well as (possibly)
1423 # the ones affected by the target config. This means it implies
1424 # things we don't want to imply.
1425 not_common_defconfigs = imply_defconfig & non_defconfigs
1426 if not_common_defconfigs:
1427 continue
1428
1429 # If there are common defconfigs, imply_config may be useful
1430 if common_defconfigs:
1431 skip = False
1432 if find_superset:
Simon Glass793dca32019-10-31 07:42:57 -06001433 for prev in list(imply_configs.keys()):
Simon Glass99b66602017-06-01 19:39:03 -06001434 prev_count = len(imply_configs[prev])
1435 count = len(common_defconfigs)
1436 if (prev_count > count and
1437 (imply_configs[prev] & common_defconfigs ==
1438 common_defconfigs)):
1439 # skip imply_config because prev is a superset
1440 skip = True
1441 break
1442 elif count > prev_count:
1443 # delete prev because imply_config is a superset
1444 del imply_configs[prev]
1445 if not skip:
1446 imply_configs[imply_config] = common_defconfigs
1447
1448 # Now we have a dict imply_configs of configs which imply each config
1449 # The value of each dict item is the set of defconfigs containing that
1450 # config. Rank them so that we print the configs that imply the largest
1451 # number of defconfigs first.
Simon Glasscb008832017-06-15 21:39:33 -06001452 ranked_iconfigs = sorted(imply_configs,
Simon Glass99b66602017-06-01 19:39:03 -06001453 key=lambda k: len(imply_configs[k]), reverse=True)
Simon Glasscb008832017-06-15 21:39:33 -06001454 kconfig_info = ''
1455 cwd = os.getcwd()
1456 add_list = collections.defaultdict(list)
1457 for iconfig in ranked_iconfigs:
1458 num_common = len(imply_configs[iconfig])
Simon Glass99b66602017-06-01 19:39:03 -06001459
1460 # Don't bother if there are less than 5 defconfigs affected.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001461 if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
Simon Glass99b66602017-06-01 19:39:03 -06001462 continue
Simon Glasscb008832017-06-15 21:39:33 -06001463 missing = defconfigs - imply_configs[iconfig]
Simon Glass99b66602017-06-01 19:39:03 -06001464 missing_str = ', '.join(missing) if missing else 'all'
1465 missing_str = ''
Simon Glasscb008832017-06-15 21:39:33 -06001466 show = True
1467 if kconf:
1468 sym = find_kconfig_rules(kconf, config[CONFIG_LEN:],
1469 iconfig[CONFIG_LEN:])
1470 kconfig_info = ''
1471 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001472 nodes = sym.nodes
1473 if len(nodes) == 1:
1474 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001475 if cwd and fname.startswith(cwd):
1476 fname = fname[len(cwd) + 1:]
1477 kconfig_info = '%s:%d' % (fname, linenum)
1478 if skip_added:
1479 show = False
1480 else:
Tom Rini65e05dd2019-09-20 17:42:09 -04001481 sym = kconf.syms.get(iconfig[CONFIG_LEN:])
Simon Glasscb008832017-06-15 21:39:33 -06001482 fname = ''
1483 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001484 nodes = sym.nodes
1485 if len(nodes) == 1:
1486 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001487 if cwd and fname.startswith(cwd):
1488 fname = fname[len(cwd) + 1:]
1489 in_arch_board = not sym or (fname.startswith('arch') or
1490 fname.startswith('board'))
1491 if (not in_arch_board and
Simon Glass91197aa2021-12-18 14:54:35 -07001492 not imply_flags & IMPLY_NON_ARCH_BOARD):
Simon Glasscb008832017-06-15 21:39:33 -06001493 continue
1494
1495 if add_imply and (add_imply == 'all' or
1496 iconfig in add_imply):
1497 fname, linenum, kconfig_info = (check_imply_rule(kconf,
1498 config[CONFIG_LEN:], iconfig[CONFIG_LEN:]))
1499 if fname:
1500 add_list[fname].append(linenum)
1501
1502 if show and kconfig_info != 'skip':
Simon Glass793dca32019-10-31 07:42:57 -06001503 print('%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
1504 kconfig_info, missing_str))
Simon Glasscb008832017-06-15 21:39:33 -06001505
1506 # Having collected a list of things to add, now we add them. We process
1507 # each file from the largest line number to the smallest so that
1508 # earlier additions do not affect our line numbers. E.g. if we added an
1509 # imply at line 20 it would change the position of each line after
1510 # that.
Simon Glass793dca32019-10-31 07:42:57 -06001511 for fname, linenums in add_list.items():
Simon Glasscb008832017-06-15 21:39:33 -06001512 for linenum in sorted(linenums, reverse=True):
1513 add_imply_rule(config[CONFIG_LEN:], fname, linenum)
Simon Glass99b66602017-06-01 19:39:03 -06001514
Simon Glass941671a2022-02-08 11:49:46 -07001515def defconfig_matches(configs, re_match):
1516 """Check if any CONFIG option matches a regex
1517
1518 The match must be complete, i.e. from the start to end of the CONFIG option.
1519
1520 Args:
1521 configs (dict): Dict of CONFIG options:
1522 key: CONFIG option
1523 value: Value of option
1524 re_match (re.Pattern): Match to check
1525
1526 Returns:
1527 bool: True if any CONFIG matches the regex
1528 """
1529 for cfg in configs:
Simon Glassd9c958f2022-03-05 20:18:54 -07001530 if re_match.fullmatch(cfg):
Simon Glass941671a2022-02-08 11:49:46 -07001531 return True
1532 return False
Simon Glass99b66602017-06-01 19:39:03 -06001533
Simon Glass65d7fce2021-12-18 08:09:46 -07001534def do_find_config(config_list):
1535 """Find boards with a given combination of CONFIGs
1536
1537 Params:
Simon Glass941671a2022-02-08 11:49:46 -07001538 config_list: List of CONFIG options to check (each a regex consisting
Simon Glass65d7fce2021-12-18 08:09:46 -07001539 of a config option, with or without a CONFIG_ prefix. If an option
1540 is preceded by a tilde (~) then it must be false, otherwise it must
1541 be true)
1542 """
1543 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
1544
Simon Glass65d7fce2021-12-18 08:09:46 -07001545 # Start with all defconfigs
1546 out = all_defconfigs
1547
1548 # Work through each config in turn
Simon Glass65d7fce2021-12-18 08:09:46 -07001549 for item in config_list:
1550 # Get the real config name and whether we want this config or not
1551 cfg = item
1552 want = True
1553 if cfg[0] == '~':
1554 want = False
1555 cfg = cfg[1:]
1556
Simon Glass65d7fce2021-12-18 08:09:46 -07001557 # Search everything that is still in the running. If it has a config
1558 # that we want, or doesn't have one that we don't, add it into the
1559 # running for the next stage
1560 in_list = out
1561 out = set()
Simon Glass941671a2022-02-08 11:49:46 -07001562 re_match = re.compile(cfg)
Simon Glass65d7fce2021-12-18 08:09:46 -07001563 for defc in in_list:
Simon Glass941671a2022-02-08 11:49:46 -07001564 has_cfg = defconfig_matches(config_db[defc], re_match)
Simon Glass65d7fce2021-12-18 08:09:46 -07001565 if has_cfg == want:
1566 out.add(defc)
Tom Rini9ef3ba82022-12-04 10:14:16 -05001567 print(f'{len(out)} matches')
1568 print(' '.join(item.split('_defconfig')[0] for item in out))
Simon Glass65d7fce2021-12-18 08:09:46 -07001569
1570
1571def prefix_config(cfg):
1572 """Prefix a config with CONFIG_ if needed
1573
1574 This handles ~ operator, which indicates that the CONFIG should be disabled
1575
1576 >>> prefix_config('FRED')
1577 'CONFIG_FRED'
1578 >>> prefix_config('CONFIG_FRED')
1579 'CONFIG_FRED'
1580 >>> prefix_config('~FRED')
1581 '~CONFIG_FRED'
1582 >>> prefix_config('~CONFIG_FRED')
1583 '~CONFIG_FRED'
1584 >>> prefix_config('A123')
1585 'CONFIG_A123'
1586 """
1587 op = ''
1588 if cfg[0] == '~':
1589 op = cfg[0]
1590 cfg = cfg[1:]
1591 if not cfg.startswith('CONFIG_'):
1592 cfg = 'CONFIG_' + cfg
1593 return op + cfg
1594
1595
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001596def main():
1597 try:
1598 cpu_count = multiprocessing.cpu_count()
1599 except NotImplementedError:
1600 cpu_count = 1
1601
Simon Glassb2e83c62021-12-18 14:54:31 -07001602 epilog = '''Move config options from headers to defconfig files. See
1603doc/develop/moveconfig.rst for documentation.'''
1604
1605 parser = ArgumentParser(epilog=epilog)
1606 # Add arguments here
1607 parser.add_argument('-a', '--add-imply', type=str, default='',
Simon Glasscb008832017-06-15 21:39:33 -06001608 help='comma-separated list of CONFIG options to add '
1609 "an 'imply' statement to for the CONFIG in -i")
Simon Glassb2e83c62021-12-18 14:54:31 -07001610 parser.add_argument('-A', '--skip-added', action='store_true', default=False,
Simon Glasscb008832017-06-15 21:39:33 -06001611 help="don't show options which are already marked as "
1612 'implying others')
Simon Glassb2e83c62021-12-18 14:54:31 -07001613 parser.add_argument('-b', '--build-db', action='store_true', default=False,
Simon Glassd73fcb12017-06-01 19:39:02 -06001614 help='build a CONFIG database')
Simon Glassb2e83c62021-12-18 14:54:31 -07001615 parser.add_argument('-c', '--color', action='store_true', default=False,
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001616 help='display the log in color')
Simon Glassb2e83c62021-12-18 14:54:31 -07001617 parser.add_argument('-C', '--commit', action='store_true', default=False,
Simon Glass9ede2122016-09-12 23:18:21 -06001618 help='Create a git commit for the operation')
Simon Glassb2e83c62021-12-18 14:54:31 -07001619 parser.add_argument('-d', '--defconfigs', type=str,
Simon Glassee4e61b2017-06-01 19:38:59 -06001620 help='a file containing a list of defconfigs to move, '
1621 "one per line (for example 'snow_defconfig') "
1622 "or '-' to read from stdin")
Simon Glassb2e83c62021-12-18 14:54:31 -07001623 parser.add_argument('-e', '--exit-on-error', action='store_true',
Simon Glasse1ae5632021-12-18 08:09:44 -07001624 default=False,
1625 help='exit immediately on any error')
Simon Glassb2e83c62021-12-18 14:54:31 -07001626 parser.add_argument('-f', '--find', action='store_true', default=False,
Simon Glass65d7fce2021-12-18 08:09:46 -07001627 help='Find boards with a given config combination')
Simon Glassb2e83c62021-12-18 14:54:31 -07001628 parser.add_argument('-H', '--headers-only', dest='cleanup_headers_only',
Simon Glasse1ae5632021-12-18 08:09:44 -07001629 action='store_true', default=False,
1630 help='only cleanup the headers')
Simon Glassb2e83c62021-12-18 14:54:31 -07001631 parser.add_argument('-i', '--imply', action='store_true', default=False,
Simon Glass99b66602017-06-01 19:39:03 -06001632 help='find options which imply others')
Simon Glassb2e83c62021-12-18 14:54:31 -07001633 parser.add_argument('-I', '--imply-flags', type=str, default='',
Simon Glass9b2a2e82017-06-15 21:39:32 -06001634 help="control the -i option ('help' for help")
Simon Glassb2e83c62021-12-18 14:54:31 -07001635 parser.add_argument('-j', '--jobs', type=int, default=cpu_count,
Simon Glasse1ae5632021-12-18 08:09:44 -07001636 help='the number of jobs to run simultaneously')
Simon Glassb2e83c62021-12-18 14:54:31 -07001637 parser.add_argument('-n', '--dry-run', action='store_true', default=False,
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001638 help='perform a trial run (show log with no changes)')
Simon Glassb2e83c62021-12-18 14:54:31 -07001639 parser.add_argument('-r', '--git-ref', type=str,
Simon Glasse1ae5632021-12-18 08:09:44 -07001640 help='the git ref to clone for building the autoconf.mk')
Simon Glassb2e83c62021-12-18 14:54:31 -07001641 parser.add_argument('-s', '--force-sync', action='store_true', default=False,
Masahiro Yamada8513dc02016-05-19 15:52:08 +09001642 help='force sync by savedefconfig')
Simon Glassb2e83c62021-12-18 14:54:31 -07001643 parser.add_argument('-S', '--spl', action='store_true', default=False,
Masahiro Yamada07913d12016-08-22 22:18:22 +09001644 help='parse config options defined for SPL build')
Simon Glassb2e83c62021-12-18 14:54:31 -07001645 parser.add_argument('-t', '--test', action='store_true', default=False,
Simon Glasse1ae5632021-12-18 08:09:44 -07001646 help='run unit tests')
Simon Glassb2e83c62021-12-18 14:54:31 -07001647 parser.add_argument('-y', '--yes', action='store_true', default=False,
Simon Glass6b403df2016-09-12 23:18:20 -06001648 help="respond 'yes' to any prompts")
Simon Glassb2e83c62021-12-18 14:54:31 -07001649 parser.add_argument('-v', '--verbose', action='store_true', default=False,
Joe Hershberger95bf9c72015-05-19 13:21:24 -05001650 help='show any build errors as boards are built')
Simon Glassb2e83c62021-12-18 14:54:31 -07001651 parser.add_argument('configs', nargs='*')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001652
Simon Glassb2e83c62021-12-18 14:54:31 -07001653 args = parser.parse_args()
1654 configs = args.configs
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001655
Simon Glassb2e83c62021-12-18 14:54:31 -07001656 if args.test:
Simon Glass84067a52021-12-18 08:09:45 -07001657 sys.argv = [sys.argv[0]]
1658 fail, count = doctest.testmod()
1659 if fail:
1660 return 1
1661 unittest.main()
1662
Simon Glassb2e83c62021-12-18 14:54:31 -07001663 if not any((len(configs), args.force_sync, args.build_db, args.imply,
1664 args.find)):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001665 parser.print_usage()
1666 sys.exit(1)
1667
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001668 # prefix the option name with CONFIG_ if missing
Simon Glass65d7fce2021-12-18 08:09:46 -07001669 configs = [prefix_config(cfg) for cfg in configs]
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001670
Joe Hershberger2144f882015-05-19 13:21:20 -05001671 check_top_directory()
1672
Simon Glassb2e83c62021-12-18 14:54:31 -07001673 if args.imply:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001674 imply_flags = 0
Simon Glassb2e83c62021-12-18 14:54:31 -07001675 if args.imply_flags == 'all':
Simon Glassdee36c72017-07-10 14:47:46 -06001676 imply_flags = -1
1677
Simon Glassb2e83c62021-12-18 14:54:31 -07001678 elif args.imply_flags:
1679 for flag in args.imply_flags.split(','):
Simon Glassdee36c72017-07-10 14:47:46 -06001680 bad = flag not in IMPLY_FLAGS
1681 if bad:
Simon Glass793dca32019-10-31 07:42:57 -06001682 print("Invalid flag '%s'" % flag)
Simon Glassdee36c72017-07-10 14:47:46 -06001683 if flag == 'help' or bad:
Simon Glass793dca32019-10-31 07:42:57 -06001684 print("Imply flags: (separate with ',')")
1685 for name, info in IMPLY_FLAGS.items():
1686 print(' %-15s: %s' % (name, info[1]))
Simon Glassdee36c72017-07-10 14:47:46 -06001687 parser.print_usage()
1688 sys.exit(1)
1689 imply_flags |= IMPLY_FLAGS[flag][0]
Simon Glass9b2a2e82017-06-15 21:39:32 -06001690
Simon Glassb2e83c62021-12-18 14:54:31 -07001691 do_imply_config(configs, args.add_imply, imply_flags, args.skip_added)
Simon Glass99b66602017-06-01 19:39:03 -06001692 return
1693
Simon Glassb2e83c62021-12-18 14:54:31 -07001694 if args.find:
Simon Glass65d7fce2021-12-18 08:09:46 -07001695 do_find_config(configs)
1696 return
1697
Simon Glassd73fcb12017-06-01 19:39:02 -06001698 config_db = {}
Simon Glass793dca32019-10-31 07:42:57 -06001699 db_queue = queue.Queue()
Simon Glassd73fcb12017-06-01 19:39:02 -06001700 t = DatabaseThread(config_db, db_queue)
1701 t.setDaemon(True)
1702 t.start()
1703
Simon Glassb2e83c62021-12-18 14:54:31 -07001704 if not args.cleanup_headers_only:
Masahiro Yamadaf7536f72016-07-25 19:15:23 +09001705 check_clean_directory()
Simon Glass793dca32019-10-31 07:42:57 -06001706 bsettings.Setup('')
Simon Glass6821a742017-07-10 14:47:47 -06001707 toolchains = toolchain.Toolchains()
1708 toolchains.GetSettings()
1709 toolchains.Scan(verbose=False)
Simon Glassb2e83c62021-12-18 14:54:31 -07001710 move_config(toolchains, configs, args, db_queue)
Simon Glassd73fcb12017-06-01 19:39:02 -06001711 db_queue.join()
Joe Hershberger2144f882015-05-19 13:21:20 -05001712
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001713 if configs:
Simon Glassb2e83c62021-12-18 14:54:31 -07001714 cleanup_headers(configs, args)
Simon Glassb2e83c62021-12-18 14:54:31 -07001715 cleanup_readme(configs, args)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001716
Simon Glassb2e83c62021-12-18 14:54:31 -07001717 if args.commit:
Simon Glass9ede2122016-09-12 23:18:21 -06001718 subprocess.call(['git', 'add', '-u'])
1719 if configs:
1720 msg = 'Convert %s %sto Kconfig' % (configs[0],
1721 'et al ' if len(configs) > 1 else '')
1722 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1723 '\n '.join(configs))
1724 else:
1725 msg = 'configs: Resync with savedefconfig'
1726 msg += '\n\nRsync all defconfig files using moveconfig.py'
1727 subprocess.call(['git', 'commit', '-s', '-m', msg])
1728
Simon Glassb2e83c62021-12-18 14:54:31 -07001729 if args.build_db:
Simon Glass91197aa2021-12-18 14:54:35 -07001730 with open(CONFIG_DATABASE, 'w', encoding='utf-8') as fd:
Simon Glass793dca32019-10-31 07:42:57 -06001731 for defconfig, configs in config_db.items():
Simon Glassc79d18c2017-08-13 16:02:54 -06001732 fd.write('%s\n' % defconfig)
Simon Glassd73fcb12017-06-01 19:39:02 -06001733 for config in sorted(configs.keys()):
Simon Glassc79d18c2017-08-13 16:02:54 -06001734 fd.write(' %s=%s\n' % (config, configs[config]))
1735 fd.write('\n')
Simon Glassd73fcb12017-06-01 19:39:02 -06001736
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001737if __name__ == '__main__':
Simon Glass65d7fce2021-12-18 08:09:46 -07001738 sys.exit(main())