blob: d4a96ef45d2ccc4cc49d489f40b1eff79625c387 [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 ###
Simon Glassb8d11da2022-02-08 11:49:45 -070097def remove_defconfig(defc):
98 """Drop the _defconfig suffix on a string
99
100 Args:
101 defc (str): String to convert
102
103 Returns:
104 str: string with the '_defconfig' suffix removed
105 """
106 return RE_REMOVE_DEFCONFIG.match(defc)[1]
107
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900108def check_top_directory():
109 """Exit if we are not at the top of source directory."""
Simon Glass91197aa2021-12-18 14:54:35 -0700110 for fname in 'README', 'Licenses':
111 if not os.path.exists(fname):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900112 sys.exit('Please run at the top of source directory.')
113
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900114def check_clean_directory():
115 """Exit if the source tree is not clean."""
Simon Glass91197aa2021-12-18 14:54:35 -0700116 for fname in '.config', 'include/config':
117 if os.path.exists(fname):
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900118 sys.exit("source tree is not clean, please run 'make mrproper'")
119
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900120def get_make_cmd():
121 """Get the command name of GNU Make.
122
123 U-Boot needs GNU Make for building, but the command name is not
124 necessarily "make". (for example, "gmake" on FreeBSD).
125 Returns the most appropriate command name on your system.
126 """
Simon Glass91197aa2021-12-18 14:54:35 -0700127 with subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE) as proc:
128 ret = proc.communicate()
129 if proc.returncode:
130 sys.exit('GNU Make not found')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900131 return ret[0].rstrip()
132
Simon Glass25f978c2017-06-01 19:38:58 -0600133def get_matched_defconfig(line):
134 """Get the defconfig files that match a pattern
135
136 Args:
Simon Glass91197aa2021-12-18 14:54:35 -0700137 line (str): Path or filename to match, e.g. 'configs/snow_defconfig' or
Simon Glass25f978c2017-06-01 19:38:58 -0600138 'k2*_defconfig'. If no directory is provided, 'configs/' is
139 prepended
140
141 Returns:
Simon Glass91197aa2021-12-18 14:54:35 -0700142 list of str: a list of matching defconfig files
Simon Glass25f978c2017-06-01 19:38:58 -0600143 """
144 dirname = os.path.dirname(line)
145 if dirname:
146 pattern = line
147 else:
148 pattern = os.path.join('configs', line)
149 return glob.glob(pattern) + glob.glob(pattern + '_defconfig')
150
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900151def get_matched_defconfigs(defconfigs_file):
Simon Glassee4e61b2017-06-01 19:38:59 -0600152 """Get all the defconfig files that match the patterns in a file.
153
154 Args:
Simon Glass91197aa2021-12-18 14:54:35 -0700155 defconfigs_file (str): File containing a list of defconfigs to process,
156 or '-' to read the list from stdin
Simon Glassee4e61b2017-06-01 19:38:59 -0600157
158 Returns:
Simon Glass91197aa2021-12-18 14:54:35 -0700159 list of str: A list of paths to defconfig files, with no duplicates
Simon Glassee4e61b2017-06-01 19:38:59 -0600160 """
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900161 defconfigs = []
Simon Glass91197aa2021-12-18 14:54:35 -0700162 with ExitStack() as stack:
163 if defconfigs_file == '-':
164 inf = sys.stdin
165 defconfigs_file = 'stdin'
166 else:
167 inf = stack.enter_context(open(defconfigs_file, encoding='utf-8'))
168 for i, line in enumerate(inf):
169 line = line.strip()
170 if not line:
171 continue # skip blank lines silently
172 if ' ' in line:
173 line = line.split(' ')[0] # handle 'git log' input
174 matched = get_matched_defconfig(line)
175 if not matched:
176 print(f"warning: {defconfigs_file}:{i + 1}: no defconfig matched '{line}'",
177 file=sys.stderr)
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900178
Simon Glass91197aa2021-12-18 14:54:35 -0700179 defconfigs += matched
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900180
181 # use set() to drop multiple matching
Simon Glass91197aa2021-12-18 14:54:35 -0700182 return [defconfig[len('configs') + 1:] for defconfig in set(defconfigs)]
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900183
Masahiro Yamada684c3062016-07-25 19:15:28 +0900184def get_all_defconfigs():
Simon Glass91197aa2021-12-18 14:54:35 -0700185 """Get all the defconfig files under the configs/ directory.
186
187 Returns:
188 list of str: List of paths to defconfig files
189 """
Masahiro Yamada684c3062016-07-25 19:15:28 +0900190 defconfigs = []
Simon Glass91197aa2021-12-18 14:54:35 -0700191 for (dirpath, _, filenames) in os.walk('configs'):
Masahiro Yamada684c3062016-07-25 19:15:28 +0900192 dirpath = dirpath[len('configs') + 1:]
193 for filename in fnmatch.filter(filenames, '*_defconfig'):
194 defconfigs.append(os.path.join(dirpath, filename))
195
196 return defconfigs
197
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900198def color_text(color_enabled, color, string):
199 """Return colored string."""
200 if color_enabled:
Masahiro Yamada1d085562016-05-19 15:52:02 +0900201 # LF should not be surrounded by the escape sequence.
202 # Otherwise, additional whitespace or line-feed might be printed.
203 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
204 for s in string.split('\n') ])
Simon Glass91197aa2021-12-18 14:54:35 -0700205 return string
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900206
Simon Glass91197aa2021-12-18 14:54:35 -0700207def show_diff(alines, blines, file_path, color_enabled):
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900208 """Show unidified diff.
209
Simon Glass91197aa2021-12-18 14:54:35 -0700210 Args:
211 alines (list of str): A list of lines (before)
212 blines (list of str): A list of lines (after)
213 file_path (str): Path to the file
214 color_enabled (bool): Display the diff in color
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900215 """
Simon Glass91197aa2021-12-18 14:54:35 -0700216 diff = difflib.unified_diff(alines, blines,
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900217 fromfile=os.path.join('a', file_path),
218 tofile=os.path.join('b', file_path))
219
220 for line in diff:
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300221 if line.startswith('-') and not line.startswith('--'):
222 print(color_text(color_enabled, COLOR_RED, line))
223 elif line.startswith('+') and not line.startswith('++'):
224 print(color_text(color_enabled, COLOR_GREEN, line))
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900225 else:
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300226 print(line)
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900227
Simon Glass91197aa2021-12-18 14:54:35 -0700228def extend_matched_lines(lines, matched, pre_patterns, post_patterns,
229 extend_pre, extend_post):
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900230 """Extend matched lines if desired patterns are found before/after already
231 matched lines.
232
Simon Glass91197aa2021-12-18 14:54:35 -0700233 Args:
234 lines (list of str): list of lines handled.
235 matched (list of int): list of line numbers that have been already
236 matched (will be updated by this function)
237 pre_patterns (list of re.Pattern): list of regular expression that should
238 be matched as preamble
239 post_patterns (list of re.Pattern): list of regular expression that should
240 be matched as postamble
241 extend_pre (bool): Add the line number of matched preamble to the matched
242 list
243 extend_post (bool): Add the line number of matched postamble to the
244 matched list
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900245 """
246 extended_matched = []
247
248 j = matched[0]
249
250 for i in matched:
251 if i == 0 or i < j:
252 continue
253 j = i
254 while j in matched:
255 j += 1
256 if j >= len(lines):
257 break
258
Simon Glass91197aa2021-12-18 14:54:35 -0700259 for pat in pre_patterns:
260 if pat.search(lines[i - 1]):
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900261 break
262 else:
263 # not matched
264 continue
265
Simon Glass91197aa2021-12-18 14:54:35 -0700266 for pat in post_patterns:
267 if pat.search(lines[j]):
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900268 break
269 else:
270 # not matched
271 continue
272
273 if extend_pre:
274 extended_matched.append(i - 1)
275 if extend_post:
276 extended_matched.append(j)
277
278 matched += extended_matched
279 matched.sort()
280
Simon Glassb2e83c62021-12-18 14:54:31 -0700281def confirm(args, prompt):
Simon Glass91197aa2021-12-18 14:54:35 -0700282 """Ask the user to confirm something
283
284 Args:
285 args (Namespace ): program arguments
286
287 Returns:
288 bool: True to confirm, False to cancel/stop
289 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700290 if not args.yes:
Chris Packham85edfc12017-05-02 21:30:46 +1200291 while True:
Simon Glass91197aa2021-12-18 14:54:35 -0700292 choice = input(f'{prompt} [y/n]: ')
Chris Packham85edfc12017-05-02 21:30:46 +1200293 choice = choice.lower()
Simon Glass793dca32019-10-31 07:42:57 -0600294 print(choice)
Simon Glass91197aa2021-12-18 14:54:35 -0700295 if choice in ('y', 'n'):
Chris Packham85edfc12017-05-02 21:30:46 +1200296 break
297
298 if choice == 'n':
299 return False
300
301 return True
302
Simon Glass2fd85bd2021-12-18 14:54:33 -0700303def write_file(fname, data):
304 """Write data to a file
305
306 Args:
307 fname (str): Filename to write to
308 data (list of str): Lines to write (with or without trailing newline);
309 or str to write
310 """
311 with open(fname, 'w', encoding='utf-8') as out:
312 if isinstance(data, list):
313 for line in data:
314 print(line.rstrip('\n'), file=out)
315 else:
316 out.write(data)
317
Simon Glass37f815c2021-12-18 14:54:34 -0700318def read_file(fname, as_lines=True, skip_unicode=False):
319 """Read a file and return the contents
320
321 Args:
322 fname (str): Filename to read from
323 as_lines: Return file contents as a list of lines
324 skip_unicode (bool): True to report unicode errors and continue
325
326 Returns:
327 iter of str: List of ;ines from the file with newline removed; str if
328 as_lines is False with newlines intact; or None if a unicode error
329 occurred
330
331 Raises:
332 UnicodeDecodeError: Unicode error occurred when reading
333 """
334 with open(fname, encoding='utf-8') as inf:
335 try:
336 if as_lines:
337 return [line.rstrip('\n') for line in inf.readlines()]
338 else:
339 return inf.read()
340 except UnicodeDecodeError as e:
341 if not skip_unicode:
Simon Glass68a0b712022-02-11 13:23:22 -0700342 raise
Simon Glass37f815c2021-12-18 14:54:34 -0700343 print("Failed on file %s': %s" % (fname, e))
344 return None
345
Simon Glassb2e83c62021-12-18 14:54:31 -0700346def cleanup_empty_blocks(header_path, args):
Chris Packham4d9dbb12019-01-30 20:23:16 +1300347 """Clean up empty conditional blocks
348
Simon Glass91197aa2021-12-18 14:54:35 -0700349 Args:
350 header_path (str): path to the cleaned file.
351 args (Namespace): program arguments
Chris Packham4d9dbb12019-01-30 20:23:16 +1300352 """
353 pattern = re.compile(r'^\s*#\s*if.*$\n^\s*#\s*endif.*$\n*', flags=re.M)
Simon Glass37f815c2021-12-18 14:54:34 -0700354 data = read_file(header_path, as_lines=False, skip_unicode=True)
355 if data is None:
356 return
Chris Packham4d9dbb12019-01-30 20:23:16 +1300357
358 new_data = pattern.sub('\n', data)
359
360 show_diff(data.splitlines(True), new_data.splitlines(True), header_path,
Simon Glassb2e83c62021-12-18 14:54:31 -0700361 args.color)
Chris Packham4d9dbb12019-01-30 20:23:16 +1300362
Simon Glassb2e83c62021-12-18 14:54:31 -0700363 if args.dry_run:
Chris Packham4d9dbb12019-01-30 20:23:16 +1300364 return
365
Simon Glass37f815c2021-12-18 14:54:34 -0700366 if new_data != data:
367 write_file(header_path, new_data)
Chris Packham4d9dbb12019-01-30 20:23:16 +1300368
Simon Glassb2e83c62021-12-18 14:54:31 -0700369def cleanup_one_header(header_path, patterns, args):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900370 """Clean regex-matched lines away from a file.
371
Simon Glass91197aa2021-12-18 14:54:35 -0700372 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900373 header_path: path to the cleaned file.
374 patterns: list of regex patterns. Any lines matching to these
375 patterns are deleted.
Simon Glass91197aa2021-12-18 14:54:35 -0700376 args (Namespace): program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900377 """
Simon Glass37f815c2021-12-18 14:54:34 -0700378 lines = read_file(header_path, skip_unicode=True)
379 if lines is None:
380 return
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900381
382 matched = []
383 for i, line in enumerate(lines):
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300384 if i - 1 in matched and lines[i - 1].endswith('\\'):
Masahiro Yamadaa3a779f2016-07-25 19:15:27 +0900385 matched.append(i)
386 continue
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900387 for pattern in patterns:
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900388 if pattern.search(line):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900389 matched.append(i)
390 break
391
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900392 if not matched:
393 return
394
395 # remove empty #ifdef ... #endif, successive blank lines
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300396 pattern_if = re.compile(r'#\s*if(def|ndef)?\b') # #if, #ifdef, #ifndef
397 pattern_elif = re.compile(r'#\s*el(if|se)\b') # #elif, #else
398 pattern_endif = re.compile(r'#\s*endif\b') # #endif
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900399 pattern_blank = re.compile(r'^\s*$') # empty line
400
401 while True:
402 old_matched = copy.copy(matched)
403 extend_matched_lines(lines, matched, [pattern_if],
404 [pattern_endif], True, True)
405 extend_matched_lines(lines, matched, [pattern_elif],
406 [pattern_elif, pattern_endif], True, False)
407 extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
408 [pattern_blank], False, True)
409 extend_matched_lines(lines, matched, [pattern_blank],
410 [pattern_elif, pattern_endif], True, False)
411 extend_matched_lines(lines, matched, [pattern_blank],
412 [pattern_blank], True, False)
413 if matched == old_matched:
414 break
415
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900416 tolines = copy.copy(lines)
417
418 for i in reversed(matched):
419 tolines.pop(i)
420
Simon Glassb2e83c62021-12-18 14:54:31 -0700421 show_diff(lines, tolines, header_path, args.color)
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900422
Simon Glassb2e83c62021-12-18 14:54:31 -0700423 if args.dry_run:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900424 return
425
Simon Glass2fd85bd2021-12-18 14:54:33 -0700426 write_file(header_path, tolines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900427
Simon Glassb2e83c62021-12-18 14:54:31 -0700428def cleanup_headers(configs, args):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900429 """Delete config defines from board headers.
430
Simon Glass91197aa2021-12-18 14:54:35 -0700431 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900432 configs: A list of CONFIGs to remove.
Simon Glass91197aa2021-12-18 14:54:35 -0700433 args (Namespace): program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900434 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700435 if not confirm(args, 'Clean up headers?'):
Chris Packham85edfc12017-05-02 21:30:46 +1200436 return
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900437
438 patterns = []
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900439 for config in configs:
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300440 patterns.append(re.compile(r'#\s*define\s+%s\b' % config))
441 patterns.append(re.compile(r'#\s*undef\s+%s\b' % config))
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900442
Joe Hershberger60727f52015-05-19 13:21:21 -0500443 for dir in 'include', 'arch', 'board':
444 for (dirpath, dirnames, filenames) in os.walk(dir):
Masahiro Yamadadc6de502016-07-25 19:15:22 +0900445 if dirpath == os.path.join('include', 'generated'):
446 continue
Joe Hershberger60727f52015-05-19 13:21:21 -0500447 for filename in filenames:
Simon Glassa38cc172020-08-11 11:23:34 -0600448 if not filename.endswith(('~', '.dts', '.dtsi', '.bin',
Trevor Woernerdc514d72021-03-15 12:01:33 -0400449 '.elf','.aml','.dat')):
Chris Packham4d9dbb12019-01-30 20:23:16 +1300450 header_path = os.path.join(dirpath, filename)
Tom Rini02b56702019-11-10 21:19:37 -0500451 # This file contains UTF-16 data and no CONFIG symbols
452 if header_path == 'include/video_font_data.h':
453 continue
Simon Glassb2e83c62021-12-18 14:54:31 -0700454 cleanup_one_header(header_path, patterns, args)
455 cleanup_empty_blocks(header_path, args)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900456
Simon Glassb2e83c62021-12-18 14:54:31 -0700457def cleanup_one_extra_option(defconfig_path, configs, args):
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900458 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file.
459
Simon Glass91197aa2021-12-18 14:54:35 -0700460 Args:
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900461 defconfig_path: path to the cleaned defconfig file.
462 configs: A list of CONFIGs to remove.
Simon Glass91197aa2021-12-18 14:54:35 -0700463 args (Namespace): program arguments
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900464 """
465
466 start = 'CONFIG_SYS_EXTRA_OPTIONS="'
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300467 end = '"'
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900468
Simon Glass37f815c2021-12-18 14:54:34 -0700469 lines = read_file(defconfig_path)
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900470
471 for i, line in enumerate(lines):
472 if line.startswith(start) and line.endswith(end):
473 break
474 else:
475 # CONFIG_SYS_EXTRA_OPTIONS was not found in this defconfig
476 return
477
478 old_tokens = line[len(start):-len(end)].split(',')
479 new_tokens = []
480
481 for token in old_tokens:
482 pos = token.find('=')
483 if not (token[:pos] if pos >= 0 else token) in configs:
484 new_tokens.append(token)
485
486 if new_tokens == old_tokens:
487 return
488
489 tolines = copy.copy(lines)
490
491 if new_tokens:
492 tolines[i] = start + ','.join(new_tokens) + end
493 else:
494 tolines.pop(i)
495
Simon Glassb2e83c62021-12-18 14:54:31 -0700496 show_diff(lines, tolines, defconfig_path, args.color)
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900497
Simon Glassb2e83c62021-12-18 14:54:31 -0700498 if args.dry_run:
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900499 return
500
Simon Glass2fd85bd2021-12-18 14:54:33 -0700501 write_file(defconfig_path, tolines)
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900502
Simon Glassb2e83c62021-12-18 14:54:31 -0700503def cleanup_extra_options(configs, args):
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900504 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files.
505
Simon Glass91197aa2021-12-18 14:54:35 -0700506 Args:
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900507 configs: A list of CONFIGs to remove.
Simon Glass91197aa2021-12-18 14:54:35 -0700508 args (Namespace): program arguments
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900509 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700510 if not confirm(args, 'Clean up CONFIG_SYS_EXTRA_OPTIONS?'):
Chris Packham85edfc12017-05-02 21:30:46 +1200511 return
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900512
513 configs = [ config[len('CONFIG_'):] for config in configs ]
514
515 defconfigs = get_all_defconfigs()
516
517 for defconfig in defconfigs:
518 cleanup_one_extra_option(os.path.join('configs', defconfig), configs,
Simon Glassb2e83c62021-12-18 14:54:31 -0700519 args)
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900520
Simon Glassb2e83c62021-12-18 14:54:31 -0700521def cleanup_whitelist(configs, args):
Chris Packhamca438342017-05-02 21:30:47 +1200522 """Delete config whitelist entries
523
Simon Glass91197aa2021-12-18 14:54:35 -0700524 Args:
Chris Packhamca438342017-05-02 21:30:47 +1200525 configs: A list of CONFIGs to remove.
Simon Glass91197aa2021-12-18 14:54:35 -0700526 args (Namespace): program arguments
Chris Packhamca438342017-05-02 21:30:47 +1200527 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700528 if not confirm(args, 'Clean up whitelist entries?'):
Chris Packhamca438342017-05-02 21:30:47 +1200529 return
530
Simon Glass37f815c2021-12-18 14:54:34 -0700531 lines = read_file(os.path.join('scripts', 'config_whitelist.txt'))
Chris Packhamca438342017-05-02 21:30:47 +1200532
533 lines = [x for x in lines if x.strip() not in configs]
534
Simon Glass2fd85bd2021-12-18 14:54:33 -0700535 write_file(os.path.join('scripts', 'config_whitelist.txt'), lines)
Chris Packhamca438342017-05-02 21:30:47 +1200536
Chris Packhamf90df592017-05-02 21:30:48 +1200537def find_matching(patterns, line):
538 for pat in patterns:
539 if pat.search(line):
540 return True
541 return False
542
Simon Glassb2e83c62021-12-18 14:54:31 -0700543def cleanup_readme(configs, args):
Chris Packhamf90df592017-05-02 21:30:48 +1200544 """Delete config description in README
545
Simon Glass91197aa2021-12-18 14:54:35 -0700546 Args:
Chris Packhamf90df592017-05-02 21:30:48 +1200547 configs: A list of CONFIGs to remove.
Simon Glass91197aa2021-12-18 14:54:35 -0700548 args (Namespace): program arguments
Chris Packhamf90df592017-05-02 21:30:48 +1200549 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700550 if not confirm(args, 'Clean up README?'):
Chris Packhamf90df592017-05-02 21:30:48 +1200551 return
552
553 patterns = []
554 for config in configs:
555 patterns.append(re.compile(r'^\s+%s' % config))
556
Simon Glass37f815c2021-12-18 14:54:34 -0700557 lines = read_file('README')
Chris Packhamf90df592017-05-02 21:30:48 +1200558
559 found = False
560 newlines = []
561 for line in lines:
562 if not found:
563 found = find_matching(patterns, line)
564 if found:
565 continue
566
567 if found and re.search(r'^\s+CONFIG', line):
568 found = False
569
570 if not found:
571 newlines.append(line)
572
Simon Glass2fd85bd2021-12-18 14:54:33 -0700573 write_file('README', newlines)
Chris Packhamf90df592017-05-02 21:30:48 +1200574
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200575def try_expand(line):
576 """If value looks like an expression, try expanding it
577 Otherwise just return the existing value
578 """
579 if line.find('=') == -1:
580 return line
581
582 try:
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +0100583 aeval = asteval.Interpreter( usersyms=SIZES, minimal=True )
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200584 cfg, val = re.split("=", line)
585 val= val.strip('\"')
Simon Glassdaa694d2021-12-18 14:54:30 -0700586 if re.search(r'[*+-/]|<<|SZ_+|\(([^\)]+)\)', val):
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +0100587 newval = hex(aeval(val))
Simon Glassdaa694d2021-12-18 14:54:30 -0700588 print('\tExpanded expression %s to %s' % (val, newval))
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200589 return cfg+'='+newval
590 except:
Simon Glassdaa694d2021-12-18 14:54:30 -0700591 print('\tFailed to expand expression in %s' % line)
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200592
593 return line
594
Chris Packhamca438342017-05-02 21:30:47 +1200595
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900596### classes ###
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900597class Progress:
598
599 """Progress Indicator"""
600
601 def __init__(self, total):
602 """Create a new progress indicator.
603
Simon Glass91197aa2021-12-18 14:54:35 -0700604 Args:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900605 total: A number of defconfig files to process.
606 """
607 self.current = 0
608 self.total = total
609
610 def inc(self):
611 """Increment the number of processed defconfig files."""
612
613 self.current += 1
614
615 def show(self):
616 """Display the progress."""
Simon Glass793dca32019-10-31 07:42:57 -0600617 print(' %d defconfigs out of %d\r' % (self.current, self.total), end=' ')
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900618 sys.stdout.flush()
619
Simon Glasscb008832017-06-15 21:39:33 -0600620
621class KconfigScanner:
622 """Kconfig scanner."""
623
624 def __init__(self):
625 """Scan all the Kconfig files and create a Config object."""
626 # Define environment variables referenced from Kconfig
627 os.environ['srctree'] = os.getcwd()
628 os.environ['UBOOTVERSION'] = 'dummy'
629 os.environ['KCONFIG_OBJDIR'] = ''
Tom Rini65e05dd2019-09-20 17:42:09 -0400630 self.conf = kconfiglib.Kconfig()
Simon Glasscb008832017-06-15 21:39:33 -0600631
632
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900633class KconfigParser:
634
635 """A parser of .config and include/autoconf.mk."""
636
637 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
638 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
639
Simon Glassb2e83c62021-12-18 14:54:31 -0700640 def __init__(self, configs, args, build_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900641 """Create a new parser.
642
Simon Glass91197aa2021-12-18 14:54:35 -0700643 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900644 configs: A list of CONFIGs to move.
Simon Glass91197aa2021-12-18 14:54:35 -0700645 args (Namespace): program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900646 build_dir: Build directory.
647 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900648 self.configs = configs
Simon Glassb2e83c62021-12-18 14:54:31 -0700649 self.args = args
Masahiro Yamada1f169922016-05-19 15:52:00 +0900650 self.dotconfig = os.path.join(build_dir, '.config')
651 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
Masahiro Yamada07913d12016-08-22 22:18:22 +0900652 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
653 'autoconf.mk')
Simon Glassf3b8e642017-06-01 19:39:01 -0600654 self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900655 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900656
Simon Glass6821a742017-07-10 14:47:47 -0600657 def get_arch(self):
658 """Parse .config file and return the architecture.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900659
660 Returns:
Simon Glass6821a742017-07-10 14:47:47 -0600661 Architecture name (e.g. 'arm').
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900662 """
663 arch = ''
664 cpu = ''
Simon Glass37f815c2021-12-18 14:54:34 -0700665 for line in read_file(self.dotconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900666 m = self.re_arch.match(line)
667 if m:
668 arch = m.group(1)
669 continue
670 m = self.re_cpu.match(line)
671 if m:
672 cpu = m.group(1)
673
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900674 if not arch:
675 return None
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900676
677 # fix-up for aarch64
678 if arch == 'arm' and cpu == 'armv8':
679 arch = 'aarch64'
680
Simon Glass6821a742017-07-10 14:47:47 -0600681 return arch
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900682
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900683 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900684 """Parse .config, defconfig, include/autoconf.mk for one config.
685
686 This function looks for the config options in the lines from
687 defconfig, .config, and include/autoconf.mk in order to decide
688 which action should be taken for this defconfig.
689
Simon Glass91197aa2021-12-18 14:54:35 -0700690 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900691 config: CONFIG name to parse.
Masahiro Yamadacc008292016-05-19 15:51:56 +0900692 dotconfig_lines: lines from the .config file.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900693 autoconf_lines: lines from the include/autoconf.mk file.
694
695 Returns:
696 A tupple of the action for this defconfig and the line
697 matched for the config.
698 """
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900699 not_set = '# %s is not set' % config
700
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900701 for line in autoconf_lines:
702 line = line.rstrip()
703 if line.startswith(config + '='):
Masahiro Yamadacc008292016-05-19 15:51:56 +0900704 new_val = line
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900705 break
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900706 else:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900707 new_val = not_set
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900708
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200709 new_val = try_expand(new_val)
710
Masahiro Yamada916224c2016-08-22 22:18:21 +0900711 for line in dotconfig_lines:
712 line = line.rstrip()
713 if line.startswith(config + '=') or line == not_set:
714 old_val = line
715 break
716 else:
717 if new_val == not_set:
718 return (ACTION_NO_ENTRY, config)
719 else:
720 return (ACTION_NO_ENTRY_WARN, config)
721
Masahiro Yamadacc008292016-05-19 15:51:56 +0900722 # If this CONFIG is neither bool nor trisate
723 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
724 # tools/scripts/define2mk.sed changes '1' to 'y'.
725 # This is a problem if the CONFIG is int type.
726 # Check the type in Kconfig and handle it correctly.
727 if new_val[-2:] == '=y':
728 new_val = new_val[:-1] + '1'
729
Masahiro Yamada50301592016-06-15 14:33:50 +0900730 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
731 new_val)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900732
Masahiro Yamada1d085562016-05-19 15:52:02 +0900733 def update_dotconfig(self):
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900734 """Parse files for the config options and update the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900735
Masahiro Yamadacc008292016-05-19 15:51:56 +0900736 This function parses the generated .config and include/autoconf.mk
737 searching the target options.
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900738 Move the config option(s) to the .config as needed.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900739
Simon Glass91197aa2021-12-18 14:54:35 -0700740 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900741 defconfig: defconfig name.
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900742
743 Returns:
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900744 Return a tuple of (updated flag, log string).
745 The "updated flag" is True if the .config was updated, False
746 otherwise. The "log string" shows what happend to the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900747 """
748
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900749 results = []
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900750 updated = False
Masahiro Yamada916224c2016-08-22 22:18:21 +0900751 suspicious = False
Masahiro Yamada07913d12016-08-22 22:18:22 +0900752 rm_files = [self.config_autoconf, self.autoconf]
753
Simon Glassb2e83c62021-12-18 14:54:31 -0700754 if self.args.spl:
Masahiro Yamada07913d12016-08-22 22:18:22 +0900755 if os.path.exists(self.spl_autoconf):
756 autoconf_path = self.spl_autoconf
757 rm_files.append(self.spl_autoconf)
758 else:
759 for f in rm_files:
760 os.remove(f)
761 return (updated, suspicious,
Simon Glassb2e83c62021-12-18 14:54:31 -0700762 color_text(self.args.color, COLOR_BROWN,
Masahiro Yamada07913d12016-08-22 22:18:22 +0900763 "SPL is not enabled. Skipped.") + '\n')
764 else:
765 autoconf_path = self.autoconf
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900766
Simon Glass37f815c2021-12-18 14:54:34 -0700767 dotconfig_lines = read_file(self.dotconfig)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900768
Simon Glass37f815c2021-12-18 14:54:34 -0700769 autoconf_lines = read_file(autoconf_path)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900770
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900771 for config in self.configs:
772 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger96464ba2015-05-19 13:21:17 -0500773 autoconf_lines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900774 results.append(result)
775
776 log = ''
777
778 for (action, value) in results:
779 if action == ACTION_MOVE:
780 actlog = "Move '%s'" % value
781 log_color = COLOR_LIGHT_GREEN
Masahiro Yamadacc008292016-05-19 15:51:56 +0900782 elif action == ACTION_NO_ENTRY:
Simon Glassdaa694d2021-12-18 14:54:30 -0700783 actlog = '%s is not defined in Kconfig. Do nothing.' % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900784 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada916224c2016-08-22 22:18:21 +0900785 elif action == ACTION_NO_ENTRY_WARN:
Simon Glassdaa694d2021-12-18 14:54:30 -0700786 actlog = '%s is not defined in Kconfig (suspicious). Do nothing.' % value
Masahiro Yamada916224c2016-08-22 22:18:21 +0900787 log_color = COLOR_YELLOW
788 suspicious = True
Masahiro Yamadacc008292016-05-19 15:51:56 +0900789 elif action == ACTION_NO_CHANGE:
790 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
791 % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900792 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900793 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700794 sys.exit('Internal Error. This should not happen.')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900795
Simon Glassb2e83c62021-12-18 14:54:31 -0700796 log += color_text(self.args.color, log_color, actlog) + '\n'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900797
Simon Glass91197aa2021-12-18 14:54:35 -0700798 with open(self.dotconfig, 'a', encoding='utf-8') as out:
Masahiro Yamadae423d172016-05-19 15:51:49 +0900799 for (action, value) in results:
800 if action == ACTION_MOVE:
Simon Glass91197aa2021-12-18 14:54:35 -0700801 out.write(value + '\n')
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900802 updated = True
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900803
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900804 self.results = results
Masahiro Yamada07913d12016-08-22 22:18:22 +0900805 for f in rm_files:
806 os.remove(f)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900807
Masahiro Yamada916224c2016-08-22 22:18:21 +0900808 return (updated, suspicious, log)
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900809
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900810 def check_defconfig(self):
811 """Check the defconfig after savedefconfig
812
813 Returns:
814 Return additional log if moved CONFIGs were removed again by
815 'make savedefconfig'.
816 """
817
818 log = ''
819
Simon Glass37f815c2021-12-18 14:54:34 -0700820 defconfig_lines = read_file(self.defconfig)
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900821
822 for (action, value) in self.results:
823 if action != ACTION_MOVE:
824 continue
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300825 if not value in defconfig_lines:
Simon Glassb2e83c62021-12-18 14:54:31 -0700826 log += color_text(self.args.color, COLOR_YELLOW,
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900827 "'%s' was removed by savedefconfig.\n" %
828 value)
829
830 return log
831
Simon Glassd73fcb12017-06-01 19:39:02 -0600832
833class DatabaseThread(threading.Thread):
834 """This thread processes results from Slot threads.
835
836 It collects the data in the master config directary. There is only one
837 result thread, and this helps to serialise the build output.
838 """
839 def __init__(self, config_db, db_queue):
840 """Set up a new result thread
841
842 Args:
843 builder: Builder which will be sent each result
844 """
845 threading.Thread.__init__(self)
846 self.config_db = config_db
847 self.db_queue= db_queue
848
849 def run(self):
850 """Called to start up the result thread.
851
852 We collect the next result job and pass it on to the build.
853 """
854 while True:
855 defconfig, configs = self.db_queue.get()
856 self.config_db[defconfig] = configs
857 self.db_queue.task_done()
858
859
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900860class Slot:
861
862 """A slot to store a subprocess.
863
864 Each instance of this class handles one subprocess.
865 This class is useful to control multiple threads
866 for faster processing.
867 """
868
Simon Glassb2e83c62021-12-18 14:54:31 -0700869 def __init__(self, toolchains, configs, args, progress, devnull,
Simon Glass6821a742017-07-10 14:47:47 -0600870 make_cmd, reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900871 """Create a new process slot.
872
Simon Glass91197aa2021-12-18 14:54:35 -0700873 Args:
Simon Glass6821a742017-07-10 14:47:47 -0600874 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900875 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -0700876 args: Program arguments
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900877 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900878 devnull: A file object of '/dev/null'.
879 make_cmd: command name of GNU Make.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500880 reference_src_dir: Determine the true starting config state from this
881 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -0600882 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900883 """
Simon Glass6821a742017-07-10 14:47:47 -0600884 self.toolchains = toolchains
Simon Glassb2e83c62021-12-18 14:54:31 -0700885 self.args = args
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900886 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900887 self.build_dir = tempfile.mkdtemp()
888 self.devnull = devnull
889 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500890 self.reference_src_dir = reference_src_dir
Simon Glassd73fcb12017-06-01 19:39:02 -0600891 self.db_queue = db_queue
Simon Glassb2e83c62021-12-18 14:54:31 -0700892 self.parser = KconfigParser(configs, args, self.build_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900893 self.state = STATE_IDLE
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900894 self.failed_boards = set()
895 self.suspicious_boards = set()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900896
897 def __del__(self):
898 """Delete the working directory
899
900 This function makes sure the temporary directory is cleaned away
901 even if Python suddenly dies due to error. It should be done in here
Joe Hershbergerf2dae752016-06-10 14:53:29 -0500902 because it is guaranteed the destructor is always invoked when the
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900903 instance of the class gets unreferenced.
904
905 If the subprocess is still running, wait until it finishes.
906 """
907 if self.state != STATE_IDLE:
908 while self.ps.poll() == None:
909 pass
910 shutil.rmtree(self.build_dir)
911
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900912 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900913 """Assign a new subprocess for defconfig and add it to the slot.
914
915 If the slot is vacant, create a new subprocess for processing the
916 given defconfig and add it to the slot. Just returns False if
917 the slot is occupied (i.e. the current subprocess is still running).
918
Simon Glass91197aa2021-12-18 14:54:35 -0700919 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900920 defconfig: defconfig name.
921
922 Returns:
923 Return True on success or False on failure
924 """
925 if self.state != STATE_IDLE:
926 return False
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900927
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900928 self.defconfig = defconfig
Masahiro Yamada1d085562016-05-19 15:52:02 +0900929 self.log = ''
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900930 self.current_src_dir = self.reference_src_dir
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900931 self.do_defconfig()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900932 return True
933
934 def poll(self):
935 """Check the status of the subprocess and handle it as needed.
936
937 Returns True if the slot is vacant (i.e. in idle state).
938 If the configuration is successfully finished, assign a new
939 subprocess to build include/autoconf.mk.
940 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900941 parse the .config and the include/autoconf.mk, moving
942 config options to the .config as needed.
943 If the .config was updated, run "make savedefconfig" to sync
944 it, update the original defconfig, and then set the slot back
945 to the idle state.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900946
947 Returns:
948 Return True if the subprocess is terminated, False otherwise
949 """
950 if self.state == STATE_IDLE:
951 return True
952
953 if self.ps.poll() == None:
954 return False
955
956 if self.ps.poll() != 0:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900957 self.handle_error()
958 elif self.state == STATE_DEFCONFIG:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900959 if self.reference_src_dir and not self.current_src_dir:
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500960 self.do_savedefconfig()
961 else:
962 self.do_autoconf()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900963 elif self.state == STATE_AUTOCONF:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900964 if self.current_src_dir:
965 self.current_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500966 self.do_defconfig()
Simon Glassb2e83c62021-12-18 14:54:31 -0700967 elif self.args.build_db:
Simon Glassd73fcb12017-06-01 19:39:02 -0600968 self.do_build_db()
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500969 else:
970 self.do_savedefconfig()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900971 elif self.state == STATE_SAVEDEFCONFIG:
972 self.update_defconfig()
973 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700974 sys.exit('Internal Error. This should not happen.')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900975
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900976 return True if self.state == STATE_IDLE else False
Joe Hershberger96464ba2015-05-19 13:21:17 -0500977
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900978 def handle_error(self):
979 """Handle error cases."""
Masahiro Yamada8513dc02016-05-19 15:52:08 +0900980
Simon Glassb2e83c62021-12-18 14:54:31 -0700981 self.log += color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glassdaa694d2021-12-18 14:54:30 -0700982 'Failed to process.\n')
Simon Glassb2e83c62021-12-18 14:54:31 -0700983 if self.args.verbose:
984 self.log += color_text(self.args.color, COLOR_LIGHT_CYAN,
Markus Klotzbuecher4f5c5e92020-02-12 20:46:45 +0100985 self.ps.stderr.read().decode())
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900986 self.finish(False)
Joe Hershberger96464ba2015-05-19 13:21:17 -0500987
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900988 def do_defconfig(self):
989 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900990
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900991 cmd = list(self.make_cmd)
992 cmd.append(self.defconfig)
993 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900994 stderr=subprocess.PIPE,
995 cwd=self.current_src_dir)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900996 self.state = STATE_DEFCONFIG
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900997
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900998 def do_autoconf(self):
Simon Glassf3b8e642017-06-01 19:39:01 -0600999 """Run 'make AUTO_CONF_PATH'."""
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001000
Simon Glass6821a742017-07-10 14:47:47 -06001001 arch = self.parser.get_arch()
1002 try:
1003 toolchain = self.toolchains.Select(arch)
1004 except ValueError:
Simon Glassb2e83c62021-12-18 14:54:31 -07001005 self.log += color_text(self.args.color, COLOR_YELLOW,
Chris Packhamce3ba452017-08-27 20:00:51 +12001006 "Tool chain for '%s' is missing. Do nothing.\n" % arch)
Masahiro Yamada4efef992016-05-19 15:52:03 +09001007 self.finish(False)
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001008 return
Simon Glass793dca32019-10-31 07:42:57 -06001009 env = toolchain.MakeEnvironment(False)
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +09001010
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001011 cmd = list(self.make_cmd)
Joe Hershberger7740f652015-05-19 13:21:18 -05001012 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Simon Glassf3b8e642017-06-01 19:39:01 -06001013 cmd.append(AUTO_CONF_PATH)
Simon Glass6821a742017-07-10 14:47:47 -06001014 self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001015 stderr=subprocess.PIPE,
1016 cwd=self.current_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001017 self.state = STATE_AUTOCONF
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001018
Simon Glassd73fcb12017-06-01 19:39:02 -06001019 def do_build_db(self):
1020 """Add the board to the database"""
1021 configs = {}
Simon Glass37f815c2021-12-18 14:54:34 -07001022 for line in read_file(os.path.join(self.build_dir, AUTO_CONF_PATH)):
1023 if line.startswith('CONFIG'):
1024 config, value = line.split('=', 1)
1025 configs[config] = value.rstrip()
Simon Glassd73fcb12017-06-01 19:39:02 -06001026 self.db_queue.put([self.defconfig, configs])
1027 self.finish(True)
1028
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001029 def do_savedefconfig(self):
1030 """Update the .config and run 'make savedefconfig'."""
1031
Masahiro Yamada916224c2016-08-22 22:18:21 +09001032 (updated, suspicious, log) = self.parser.update_dotconfig()
1033 if suspicious:
1034 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001035 self.log += log
1036
Simon Glassb2e83c62021-12-18 14:54:31 -07001037 if not self.args.force_sync and not updated:
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001038 self.finish(True)
1039 return
1040 if updated:
Simon Glassb2e83c62021-12-18 14:54:31 -07001041 self.log += color_text(self.args.color, COLOR_LIGHT_GREEN,
Simon Glassdaa694d2021-12-18 14:54:30 -07001042 'Syncing by savedefconfig...\n')
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001043 else:
Simon Glassdaa694d2021-12-18 14:54:30 -07001044 self.log += 'Syncing by savedefconfig (forced by option)...\n'
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001045
1046 cmd = list(self.make_cmd)
1047 cmd.append('savedefconfig')
1048 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
1049 stderr=subprocess.PIPE)
1050 self.state = STATE_SAVEDEFCONFIG
1051
1052 def update_defconfig(self):
1053 """Update the input defconfig and go back to the idle state."""
1054
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001055 log = self.parser.check_defconfig()
1056 if log:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001057 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001058 self.log += log
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001059 orig_defconfig = os.path.join('configs', self.defconfig)
1060 new_defconfig = os.path.join(self.build_dir, 'defconfig')
1061 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
1062
1063 if updated:
Simon Glassb2e83c62021-12-18 14:54:31 -07001064 self.log += color_text(self.args.color, COLOR_LIGHT_BLUE,
Simon Glassdaa694d2021-12-18 14:54:30 -07001065 'defconfig was updated.\n')
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001066
Simon Glassb2e83c62021-12-18 14:54:31 -07001067 if not self.args.dry_run and updated:
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001068 shutil.move(new_defconfig, orig_defconfig)
1069 self.finish(True)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001070
Masahiro Yamada4efef992016-05-19 15:52:03 +09001071 def finish(self, success):
1072 """Display log along with progress and go to the idle state.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001073
Simon Glass91197aa2021-12-18 14:54:35 -07001074 Args:
Masahiro Yamada4efef992016-05-19 15:52:03 +09001075 success: Should be True when the defconfig was processed
1076 successfully, or False when it fails.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001077 """
1078 # output at least 30 characters to hide the "* defconfigs out of *".
1079 log = self.defconfig.ljust(30) + '\n'
1080
1081 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
1082 # Some threads are running in parallel.
1083 # Print log atomically to not mix up logs from different threads.
Simon Glass793dca32019-10-31 07:42:57 -06001084 print(log, file=(sys.stdout if success else sys.stderr))
Masahiro Yamada4efef992016-05-19 15:52:03 +09001085
1086 if not success:
Simon Glassb2e83c62021-12-18 14:54:31 -07001087 if self.args.exit_on_error:
Simon Glassdaa694d2021-12-18 14:54:30 -07001088 sys.exit('Exit on error.')
Masahiro Yamada4efef992016-05-19 15:52:03 +09001089 # If --exit-on-error flag is not set, skip this board and continue.
1090 # Record the failed board.
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001091 self.failed_boards.add(self.defconfig)
Masahiro Yamada4efef992016-05-19 15:52:03 +09001092
Masahiro Yamada1d085562016-05-19 15:52:02 +09001093 self.progress.inc()
1094 self.progress.show()
Masahiro Yamada4efef992016-05-19 15:52:03 +09001095 self.state = STATE_IDLE
Masahiro Yamada1d085562016-05-19 15:52:02 +09001096
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001097 def get_failed_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001098 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001099 """
1100 return self.failed_boards
1101
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001102 def get_suspicious_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001103 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001104 """
Masahiro Yamada916224c2016-08-22 22:18:21 +09001105 return self.suspicious_boards - self.failed_boards
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001106
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001107class Slots:
1108
1109 """Controller of the array of subprocess slots."""
1110
Simon Glassb2e83c62021-12-18 14:54:31 -07001111 def __init__(self, toolchains, configs, args, progress,
Simon Glass6821a742017-07-10 14:47:47 -06001112 reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001113 """Create a new slots controller.
1114
Simon Glass91197aa2021-12-18 14:54:35 -07001115 Args:
Simon Glass6821a742017-07-10 14:47:47 -06001116 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001117 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -07001118 args: Program arguments
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001119 progress: A progress indicator.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001120 reference_src_dir: Determine the true starting config state from this
1121 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -06001122 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001123 """
Simon Glassb2e83c62021-12-18 14:54:31 -07001124 self.args = args
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001125 self.slots = []
Simon Glass478920d2021-12-18 14:54:32 -07001126 devnull = subprocess.DEVNULL
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001127 make_cmd = get_make_cmd()
Simon Glassb2e83c62021-12-18 14:54:31 -07001128 for i in range(args.jobs):
1129 self.slots.append(Slot(toolchains, configs, args, progress,
Simon Glass6821a742017-07-10 14:47:47 -06001130 devnull, make_cmd, reference_src_dir,
1131 db_queue))
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001132
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001133 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001134 """Add a new subprocess if a vacant slot is found.
1135
Simon Glass91197aa2021-12-18 14:54:35 -07001136 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001137 defconfig: defconfig name to be put into.
1138
1139 Returns:
1140 Return True on success or False on failure
1141 """
1142 for slot in self.slots:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001143 if slot.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001144 return True
1145 return False
1146
1147 def available(self):
1148 """Check if there is a vacant slot.
1149
1150 Returns:
1151 Return True if at lease one vacant slot is found, False otherwise.
1152 """
1153 for slot in self.slots:
1154 if slot.poll():
1155 return True
1156 return False
1157
1158 def empty(self):
1159 """Check if all slots are vacant.
1160
1161 Returns:
1162 Return True if all the slots are vacant, False otherwise.
1163 """
1164 ret = True
1165 for slot in self.slots:
1166 if not slot.poll():
1167 ret = False
1168 return ret
1169
1170 def show_failed_boards(self):
1171 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001172 boards = set()
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001173 output_file = 'moveconfig.failed'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001174
1175 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001176 boards |= slot.get_failed_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001177
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001178 if boards:
1179 boards = '\n'.join(boards) + '\n'
Simon Glassdaa694d2021-12-18 14:54:30 -07001180 msg = 'The following boards were not processed due to error:\n'
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001181 msg += boards
Simon Glassdaa694d2021-12-18 14:54:30 -07001182 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassb2e83c62021-12-18 14:54:31 -07001183 print(color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glass793dca32019-10-31 07:42:57 -06001184 msg), file=sys.stderr)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001185
Simon Glass2fd85bd2021-12-18 14:54:33 -07001186 write_file(output_file, boards)
Joe Hershberger2559cd82015-05-19 13:21:22 -05001187
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001188 def show_suspicious_boards(self):
1189 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001190 boards = set()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001191 output_file = 'moveconfig.suspicious'
1192
1193 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001194 boards |= slot.get_suspicious_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001195
1196 if boards:
1197 boards = '\n'.join(boards) + '\n'
Simon Glassdaa694d2021-12-18 14:54:30 -07001198 msg = 'The following boards might have been converted incorrectly.\n'
1199 msg += 'It is highly recommended to check them manually:\n'
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001200 msg += boards
Simon Glassdaa694d2021-12-18 14:54:30 -07001201 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassb2e83c62021-12-18 14:54:31 -07001202 print(color_text(self.args.color, COLOR_YELLOW,
Simon Glass793dca32019-10-31 07:42:57 -06001203 msg), file=sys.stderr)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001204
Simon Glass2fd85bd2021-12-18 14:54:33 -07001205 write_file(output_file, boards)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001206
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001207class ReferenceSource:
1208
1209 """Reference source against which original configs should be parsed."""
1210
1211 def __init__(self, commit):
1212 """Create a reference source directory based on a specified commit.
1213
Simon Glass91197aa2021-12-18 14:54:35 -07001214 Args:
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001215 commit: commit to git-clone
1216 """
1217 self.src_dir = tempfile.mkdtemp()
Simon Glassdaa694d2021-12-18 14:54:30 -07001218 print('Cloning git repo to a separate work directory...')
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001219 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1220 cwd=self.src_dir)
Simon Glass793dca32019-10-31 07:42:57 -06001221 print("Checkout '%s' to build the original autoconf.mk." % \
1222 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip())
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001223 subprocess.check_output(['git', 'checkout', commit],
1224 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001225
1226 def __del__(self):
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001227 """Delete the reference source directory
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001228
1229 This function makes sure the temporary directory is cleaned away
1230 even if Python suddenly dies due to error. It should be done in here
1231 because it is guaranteed the destructor is always invoked when the
1232 instance of the class gets unreferenced.
1233 """
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001234 shutil.rmtree(self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001235
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001236 def get_dir(self):
1237 """Return the absolute path to the reference source directory."""
1238
1239 return self.src_dir
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001240
Simon Glassb2e83c62021-12-18 14:54:31 -07001241def move_config(toolchains, configs, args, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001242 """Move config options to defconfig files.
1243
Simon Glass91197aa2021-12-18 14:54:35 -07001244 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001245 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -07001246 args: Program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001247 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001248 if len(configs) == 0:
Simon Glassb2e83c62021-12-18 14:54:31 -07001249 if args.force_sync:
Simon Glass793dca32019-10-31 07:42:57 -06001250 print('No CONFIG is specified. You are probably syncing defconfigs.', end=' ')
Simon Glassb2e83c62021-12-18 14:54:31 -07001251 elif args.build_db:
Simon Glass793dca32019-10-31 07:42:57 -06001252 print('Building %s database' % CONFIG_DATABASE)
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001253 else:
Simon Glass793dca32019-10-31 07:42:57 -06001254 print('Neither CONFIG nor --force-sync is specified. Nothing will happen.', end=' ')
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001255 else:
Simon Glass793dca32019-10-31 07:42:57 -06001256 print('Move ' + ', '.join(configs), end=' ')
Simon Glassb2e83c62021-12-18 14:54:31 -07001257 print('(jobs: %d)\n' % args.jobs)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001258
Simon Glassb2e83c62021-12-18 14:54:31 -07001259 if args.git_ref:
1260 reference_src = ReferenceSource(args.git_ref)
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001261 reference_src_dir = reference_src.get_dir()
1262 else:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001263 reference_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001264
Simon Glassb2e83c62021-12-18 14:54:31 -07001265 if args.defconfigs:
1266 defconfigs = get_matched_defconfigs(args.defconfigs)
Joe Hershberger91040e82015-05-19 13:21:19 -05001267 else:
Masahiro Yamada684c3062016-07-25 19:15:28 +09001268 defconfigs = get_all_defconfigs()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001269
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001270 progress = Progress(len(defconfigs))
Simon Glassb2e83c62021-12-18 14:54:31 -07001271 slots = Slots(toolchains, configs, args, progress, reference_src_dir,
Simon Glass91197aa2021-12-18 14:54:35 -07001272 db_queue)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001273
1274 # Main loop to process defconfig files:
1275 # Add a new subprocess into a vacant slot.
1276 # Sleep if there is no available slot.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001277 for defconfig in defconfigs:
1278 while not slots.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001279 while not slots.available():
1280 # No available slot: sleep for a while
1281 time.sleep(SLEEP_TIME)
1282
1283 # wait until all the subprocesses finish
1284 while not slots.empty():
1285 time.sleep(SLEEP_TIME)
1286
Simon Glass793dca32019-10-31 07:42:57 -06001287 print('')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001288 slots.show_failed_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001289 slots.show_suspicious_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001290
Simon Glasscb008832017-06-15 21:39:33 -06001291def find_kconfig_rules(kconf, config, imply_config):
1292 """Check whether a config has a 'select' or 'imply' keyword
1293
1294 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -04001295 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -06001296 config: Name of config to check (without CONFIG_ prefix)
1297 imply_config: Implying config (without CONFIG_ prefix) which may or
1298 may not have an 'imply' for 'config')
1299
1300 Returns:
1301 Symbol object for 'config' if found, else None
1302 """
Tom Rini65e05dd2019-09-20 17:42:09 -04001303 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -06001304 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001305 for sel, cond in (sym.selects + sym.implies):
Simon Glassa3627082021-12-18 08:09:42 -07001306 if sel.name == config:
Simon Glasscb008832017-06-15 21:39:33 -06001307 return sym
1308 return None
1309
1310def check_imply_rule(kconf, config, imply_config):
1311 """Check if we can add an 'imply' option
1312
1313 This finds imply_config in the Kconfig and looks to see if it is possible
1314 to add an 'imply' for 'config' to that part of the Kconfig.
1315
1316 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -04001317 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -06001318 config: Name of config to check (without CONFIG_ prefix)
1319 imply_config: Implying config (without CONFIG_ prefix) which may or
1320 may not have an 'imply' for 'config')
1321
1322 Returns:
1323 tuple:
1324 filename of Kconfig file containing imply_config, or None if none
1325 line number within the Kconfig file, or 0 if none
1326 message indicating the result
1327 """
Tom Rini65e05dd2019-09-20 17:42:09 -04001328 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -06001329 if not sym:
1330 return 'cannot find sym'
Simon Glassea40b202021-07-21 21:35:53 -06001331 nodes = sym.nodes
1332 if len(nodes) != 1:
1333 return '%d locations' % len(nodes)
Simon Glassa3627082021-12-18 08:09:42 -07001334 node = nodes[0]
1335 fname, linenum = node.filename, node.linenr
Simon Glasscb008832017-06-15 21:39:33 -06001336 cwd = os.getcwd()
1337 if cwd and fname.startswith(cwd):
1338 fname = fname[len(cwd) + 1:]
1339 file_line = ' at %s:%d' % (fname, linenum)
Simon Glass37f815c2021-12-18 14:54:34 -07001340 data = read_file(fname)
Simon Glasscb008832017-06-15 21:39:33 -06001341 if data[linenum - 1] != 'config %s' % imply_config:
1342 return None, 0, 'bad sym format %s%s' % (data[linenum], file_line)
1343 return fname, linenum, 'adding%s' % file_line
1344
1345def add_imply_rule(config, fname, linenum):
1346 """Add a new 'imply' option to a Kconfig
1347
1348 Args:
1349 config: config option to add an imply for (without CONFIG_ prefix)
1350 fname: Kconfig filename to update
1351 linenum: Line number to place the 'imply' before
1352
1353 Returns:
1354 Message indicating the result
1355 """
1356 file_line = ' at %s:%d' % (fname, linenum)
Simon Glass37f815c2021-12-18 14:54:34 -07001357 data = read_file(fname)
Simon Glasscb008832017-06-15 21:39:33 -06001358 linenum -= 1
1359
1360 for offset, line in enumerate(data[linenum:]):
1361 if line.strip().startswith('help') or not line:
1362 data.insert(linenum + offset, '\timply %s' % config)
Simon Glass2fd85bd2021-12-18 14:54:33 -07001363 write_file(fname, data)
Simon Glasscb008832017-06-15 21:39:33 -06001364 return 'added%s' % file_line
1365
1366 return 'could not insert%s'
1367
1368(IMPLY_MIN_2, IMPLY_TARGET, IMPLY_CMD, IMPLY_NON_ARCH_BOARD) = (
1369 1, 2, 4, 8)
Simon Glass9b2a2e82017-06-15 21:39:32 -06001370
1371IMPLY_FLAGS = {
1372 'min2': [IMPLY_MIN_2, 'Show options which imply >2 boards (normally >5)'],
1373 'target': [IMPLY_TARGET, 'Allow CONFIG_TARGET_... options to imply'],
1374 'cmd': [IMPLY_CMD, 'Allow CONFIG_CMD_... to imply'],
Simon Glasscb008832017-06-15 21:39:33 -06001375 'non-arch-board': [
1376 IMPLY_NON_ARCH_BOARD,
1377 'Allow Kconfig options outside arch/ and /board/ to imply'],
Simon Glass91197aa2021-12-18 14:54:35 -07001378}
Simon Glass9b2a2e82017-06-15 21:39:32 -06001379
Simon Glass9d603392021-12-18 08:09:43 -07001380
1381def read_database():
1382 """Read in the config database
1383
1384 Returns:
1385 tuple:
1386 set of all config options seen (each a str)
1387 set of all defconfigs seen (each a str)
1388 dict of configs for each defconfig:
1389 key: defconfig name, e.g. "MPC8548CDS_legacy_defconfig"
1390 value: dict:
1391 key: CONFIG option
1392 value: Value of option
1393 dict of defconfigs for each config:
1394 key: CONFIG option
1395 value: set of boards using that option
1396
1397 """
1398 configs = {}
1399
1400 # key is defconfig name, value is dict of (CONFIG_xxx, value)
1401 config_db = {}
1402
1403 # Set of all config options we have seen
1404 all_configs = set()
1405
1406 # Set of all defconfigs we have seen
1407 all_defconfigs = set()
1408
1409 defconfig_db = collections.defaultdict(set)
Simon Glass37f815c2021-12-18 14:54:34 -07001410 for line in read_file(CONFIG_DATABASE):
1411 line = line.rstrip()
1412 if not line: # Separator between defconfigs
1413 config_db[defconfig] = configs
1414 all_defconfigs.add(defconfig)
1415 configs = {}
1416 elif line[0] == ' ': # CONFIG line
1417 config, value = line.strip().split('=', 1)
1418 configs[config] = value
1419 defconfig_db[config].add(defconfig)
1420 all_configs.add(config)
1421 else: # New defconfig
1422 defconfig = line
Simon Glass9d603392021-12-18 08:09:43 -07001423
1424 return all_configs, all_defconfigs, config_db, defconfig_db
1425
1426
Simon Glasscb008832017-06-15 21:39:33 -06001427def do_imply_config(config_list, add_imply, imply_flags, skip_added,
1428 check_kconfig=True, find_superset=False):
Simon Glass99b66602017-06-01 19:39:03 -06001429 """Find CONFIG options which imply those in the list
1430
1431 Some CONFIG options can be implied by others and this can help to reduce
1432 the size of the defconfig files. For example, CONFIG_X86 implies
1433 CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
1434 all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
1435 each of the x86 defconfig files.
1436
1437 This function uses the moveconfig database to find such options. It
1438 displays a list of things that could possibly imply those in the list.
1439 The algorithm ignores any that start with CONFIG_TARGET since these
1440 typically refer to only a few defconfigs (often one). It also does not
1441 display a config with less than 5 defconfigs.
1442
1443 The algorithm works using sets. For each target config in config_list:
1444 - Get the set 'defconfigs' which use that target config
1445 - For each config (from a list of all configs):
1446 - Get the set 'imply_defconfig' of defconfigs which use that config
1447 -
1448 - If imply_defconfigs contains anything not in defconfigs then
1449 this config does not imply the target config
1450
1451 Params:
1452 config_list: List of CONFIG options to check (each a string)
Simon Glasscb008832017-06-15 21:39:33 -06001453 add_imply: Automatically add an 'imply' for each config.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001454 imply_flags: Flags which control which implying configs are allowed
1455 (IMPLY_...)
Simon Glasscb008832017-06-15 21:39:33 -06001456 skip_added: Don't show options which already have an imply added.
1457 check_kconfig: Check if implied symbols already have an 'imply' or
1458 'select' for the target config, and show this information if so.
Simon Glass99b66602017-06-01 19:39:03 -06001459 find_superset: True to look for configs which are a superset of those
1460 already found. So for example if CONFIG_EXYNOS5 implies an option,
1461 but CONFIG_EXYNOS covers a larger set of defconfigs and also
1462 implies that option, this will drop the former in favour of the
1463 latter. In practice this option has not proved very used.
1464
1465 Note the terminoloy:
1466 config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
1467 defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
1468 """
Simon Glasscb008832017-06-15 21:39:33 -06001469 kconf = KconfigScanner().conf if check_kconfig else None
1470 if add_imply and add_imply != 'all':
Simon Glassa3627082021-12-18 08:09:42 -07001471 add_imply = add_imply.split(',')
Simon Glasscb008832017-06-15 21:39:33 -06001472
Simon Glass9d603392021-12-18 08:09:43 -07001473 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
Simon Glass99b66602017-06-01 19:39:03 -06001474
Simon Glassa3627082021-12-18 08:09:42 -07001475 # Work through each target config option in turn, independently
Simon Glass99b66602017-06-01 19:39:03 -06001476 for config in config_list:
1477 defconfigs = defconfig_db.get(config)
1478 if not defconfigs:
Simon Glass793dca32019-10-31 07:42:57 -06001479 print('%s not found in any defconfig' % config)
Simon Glass99b66602017-06-01 19:39:03 -06001480 continue
1481
1482 # Get the set of defconfigs without this one (since a config cannot
1483 # imply itself)
1484 non_defconfigs = all_defconfigs - defconfigs
1485 num_defconfigs = len(defconfigs)
Simon Glass793dca32019-10-31 07:42:57 -06001486 print('%s found in %d/%d defconfigs' % (config, num_defconfigs,
1487 len(all_configs)))
Simon Glass99b66602017-06-01 19:39:03 -06001488
1489 # This will hold the results: key=config, value=defconfigs containing it
1490 imply_configs = {}
1491 rest_configs = all_configs - set([config])
1492
1493 # Look at every possible config, except the target one
1494 for imply_config in rest_configs:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001495 if 'ERRATUM' in imply_config:
Simon Glass99b66602017-06-01 19:39:03 -06001496 continue
Simon Glass91197aa2021-12-18 14:54:35 -07001497 if not imply_flags & IMPLY_CMD:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001498 if 'CONFIG_CMD' in imply_config:
1499 continue
Simon Glass91197aa2021-12-18 14:54:35 -07001500 if not imply_flags & IMPLY_TARGET:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001501 if 'CONFIG_TARGET' in imply_config:
1502 continue
Simon Glass99b66602017-06-01 19:39:03 -06001503
1504 # Find set of defconfigs that have this config
1505 imply_defconfig = defconfig_db[imply_config]
1506
1507 # Get the intersection of this with defconfigs containing the
1508 # target config
1509 common_defconfigs = imply_defconfig & defconfigs
1510
1511 # Get the set of defconfigs containing this config which DO NOT
1512 # also contain the taret config. If this set is non-empty it means
1513 # that this config affects other defconfigs as well as (possibly)
1514 # the ones affected by the target config. This means it implies
1515 # things we don't want to imply.
1516 not_common_defconfigs = imply_defconfig & non_defconfigs
1517 if not_common_defconfigs:
1518 continue
1519
1520 # If there are common defconfigs, imply_config may be useful
1521 if common_defconfigs:
1522 skip = False
1523 if find_superset:
Simon Glass793dca32019-10-31 07:42:57 -06001524 for prev in list(imply_configs.keys()):
Simon Glass99b66602017-06-01 19:39:03 -06001525 prev_count = len(imply_configs[prev])
1526 count = len(common_defconfigs)
1527 if (prev_count > count and
1528 (imply_configs[prev] & common_defconfigs ==
1529 common_defconfigs)):
1530 # skip imply_config because prev is a superset
1531 skip = True
1532 break
1533 elif count > prev_count:
1534 # delete prev because imply_config is a superset
1535 del imply_configs[prev]
1536 if not skip:
1537 imply_configs[imply_config] = common_defconfigs
1538
1539 # Now we have a dict imply_configs of configs which imply each config
1540 # The value of each dict item is the set of defconfigs containing that
1541 # config. Rank them so that we print the configs that imply the largest
1542 # number of defconfigs first.
Simon Glasscb008832017-06-15 21:39:33 -06001543 ranked_iconfigs = sorted(imply_configs,
Simon Glass99b66602017-06-01 19:39:03 -06001544 key=lambda k: len(imply_configs[k]), reverse=True)
Simon Glasscb008832017-06-15 21:39:33 -06001545 kconfig_info = ''
1546 cwd = os.getcwd()
1547 add_list = collections.defaultdict(list)
1548 for iconfig in ranked_iconfigs:
1549 num_common = len(imply_configs[iconfig])
Simon Glass99b66602017-06-01 19:39:03 -06001550
1551 # Don't bother if there are less than 5 defconfigs affected.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001552 if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
Simon Glass99b66602017-06-01 19:39:03 -06001553 continue
Simon Glasscb008832017-06-15 21:39:33 -06001554 missing = defconfigs - imply_configs[iconfig]
Simon Glass99b66602017-06-01 19:39:03 -06001555 missing_str = ', '.join(missing) if missing else 'all'
1556 missing_str = ''
Simon Glasscb008832017-06-15 21:39:33 -06001557 show = True
1558 if kconf:
1559 sym = find_kconfig_rules(kconf, config[CONFIG_LEN:],
1560 iconfig[CONFIG_LEN:])
1561 kconfig_info = ''
1562 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001563 nodes = sym.nodes
1564 if len(nodes) == 1:
1565 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001566 if cwd and fname.startswith(cwd):
1567 fname = fname[len(cwd) + 1:]
1568 kconfig_info = '%s:%d' % (fname, linenum)
1569 if skip_added:
1570 show = False
1571 else:
Tom Rini65e05dd2019-09-20 17:42:09 -04001572 sym = kconf.syms.get(iconfig[CONFIG_LEN:])
Simon Glasscb008832017-06-15 21:39:33 -06001573 fname = ''
1574 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001575 nodes = sym.nodes
1576 if len(nodes) == 1:
1577 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001578 if cwd and fname.startswith(cwd):
1579 fname = fname[len(cwd) + 1:]
1580 in_arch_board = not sym or (fname.startswith('arch') or
1581 fname.startswith('board'))
1582 if (not in_arch_board and
Simon Glass91197aa2021-12-18 14:54:35 -07001583 not imply_flags & IMPLY_NON_ARCH_BOARD):
Simon Glasscb008832017-06-15 21:39:33 -06001584 continue
1585
1586 if add_imply and (add_imply == 'all' or
1587 iconfig in add_imply):
1588 fname, linenum, kconfig_info = (check_imply_rule(kconf,
1589 config[CONFIG_LEN:], iconfig[CONFIG_LEN:]))
1590 if fname:
1591 add_list[fname].append(linenum)
1592
1593 if show and kconfig_info != 'skip':
Simon Glass793dca32019-10-31 07:42:57 -06001594 print('%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
1595 kconfig_info, missing_str))
Simon Glasscb008832017-06-15 21:39:33 -06001596
1597 # Having collected a list of things to add, now we add them. We process
1598 # each file from the largest line number to the smallest so that
1599 # earlier additions do not affect our line numbers. E.g. if we added an
1600 # imply at line 20 it would change the position of each line after
1601 # that.
Simon Glass793dca32019-10-31 07:42:57 -06001602 for fname, linenums in add_list.items():
Simon Glasscb008832017-06-15 21:39:33 -06001603 for linenum in sorted(linenums, reverse=True):
1604 add_imply_rule(config[CONFIG_LEN:], fname, linenum)
Simon Glass99b66602017-06-01 19:39:03 -06001605
Simon Glass941671a2022-02-08 11:49:46 -07001606def defconfig_matches(configs, re_match):
1607 """Check if any CONFIG option matches a regex
1608
1609 The match must be complete, i.e. from the start to end of the CONFIG option.
1610
1611 Args:
1612 configs (dict): Dict of CONFIG options:
1613 key: CONFIG option
1614 value: Value of option
1615 re_match (re.Pattern): Match to check
1616
1617 Returns:
1618 bool: True if any CONFIG matches the regex
1619 """
1620 for cfg in configs:
1621 m_cfg = re_match.match(cfg)
1622 if m_cfg and m_cfg.span()[1] == len(cfg):
1623 return True
1624 return False
Simon Glass99b66602017-06-01 19:39:03 -06001625
Simon Glass65d7fce2021-12-18 08:09:46 -07001626def do_find_config(config_list):
1627 """Find boards with a given combination of CONFIGs
1628
1629 Params:
Simon Glass941671a2022-02-08 11:49:46 -07001630 config_list: List of CONFIG options to check (each a regex consisting
Simon Glass65d7fce2021-12-18 08:09:46 -07001631 of a config option, with or without a CONFIG_ prefix. If an option
1632 is preceded by a tilde (~) then it must be false, otherwise it must
1633 be true)
1634 """
1635 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
1636
1637 # Get the whitelist
Simon Glass37f815c2021-12-18 14:54:34 -07001638 adhoc_configs = set(read_file('scripts/config_whitelist.txt'))
Simon Glass65d7fce2021-12-18 08:09:46 -07001639
1640 # Start with all defconfigs
1641 out = all_defconfigs
1642
1643 # Work through each config in turn
1644 adhoc = []
1645 for item in config_list:
1646 # Get the real config name and whether we want this config or not
1647 cfg = item
1648 want = True
1649 if cfg[0] == '~':
1650 want = False
1651 cfg = cfg[1:]
1652
1653 if cfg in adhoc_configs:
1654 adhoc.append(cfg)
1655 continue
1656
1657 # Search everything that is still in the running. If it has a config
1658 # that we want, or doesn't have one that we don't, add it into the
1659 # running for the next stage
1660 in_list = out
1661 out = set()
Simon Glass941671a2022-02-08 11:49:46 -07001662 re_match = re.compile(cfg)
Simon Glass65d7fce2021-12-18 08:09:46 -07001663 for defc in in_list:
Simon Glass941671a2022-02-08 11:49:46 -07001664 has_cfg = defconfig_matches(config_db[defc], re_match)
Simon Glass65d7fce2021-12-18 08:09:46 -07001665 if has_cfg == want:
1666 out.add(defc)
1667 if adhoc:
1668 print(f"Error: Not in Kconfig: %s" % ' '.join(adhoc))
1669 else:
1670 print(f'{len(out)} matches')
Simon Glassb8d11da2022-02-08 11:49:45 -07001671 print(' '.join([remove_defconfig(item) for item in out]))
Simon Glass65d7fce2021-12-18 08:09:46 -07001672
1673
1674def prefix_config(cfg):
1675 """Prefix a config with CONFIG_ if needed
1676
1677 This handles ~ operator, which indicates that the CONFIG should be disabled
1678
1679 >>> prefix_config('FRED')
1680 'CONFIG_FRED'
1681 >>> prefix_config('CONFIG_FRED')
1682 'CONFIG_FRED'
1683 >>> prefix_config('~FRED')
1684 '~CONFIG_FRED'
1685 >>> prefix_config('~CONFIG_FRED')
1686 '~CONFIG_FRED'
1687 >>> prefix_config('A123')
1688 'CONFIG_A123'
1689 """
1690 op = ''
1691 if cfg[0] == '~':
1692 op = cfg[0]
1693 cfg = cfg[1:]
1694 if not cfg.startswith('CONFIG_'):
1695 cfg = 'CONFIG_' + cfg
1696 return op + cfg
1697
1698
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001699def main():
1700 try:
1701 cpu_count = multiprocessing.cpu_count()
1702 except NotImplementedError:
1703 cpu_count = 1
1704
Simon Glassb2e83c62021-12-18 14:54:31 -07001705 epilog = '''Move config options from headers to defconfig files. See
1706doc/develop/moveconfig.rst for documentation.'''
1707
1708 parser = ArgumentParser(epilog=epilog)
1709 # Add arguments here
1710 parser.add_argument('-a', '--add-imply', type=str, default='',
Simon Glasscb008832017-06-15 21:39:33 -06001711 help='comma-separated list of CONFIG options to add '
1712 "an 'imply' statement to for the CONFIG in -i")
Simon Glassb2e83c62021-12-18 14:54:31 -07001713 parser.add_argument('-A', '--skip-added', action='store_true', default=False,
Simon Glasscb008832017-06-15 21:39:33 -06001714 help="don't show options which are already marked as "
1715 'implying others')
Simon Glassb2e83c62021-12-18 14:54:31 -07001716 parser.add_argument('-b', '--build-db', action='store_true', default=False,
Simon Glassd73fcb12017-06-01 19:39:02 -06001717 help='build a CONFIG database')
Simon Glassb2e83c62021-12-18 14:54:31 -07001718 parser.add_argument('-c', '--color', action='store_true', default=False,
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001719 help='display the log in color')
Simon Glassb2e83c62021-12-18 14:54:31 -07001720 parser.add_argument('-C', '--commit', action='store_true', default=False,
Simon Glass9ede2122016-09-12 23:18:21 -06001721 help='Create a git commit for the operation')
Simon Glassb2e83c62021-12-18 14:54:31 -07001722 parser.add_argument('-d', '--defconfigs', type=str,
Simon Glassee4e61b2017-06-01 19:38:59 -06001723 help='a file containing a list of defconfigs to move, '
1724 "one per line (for example 'snow_defconfig') "
1725 "or '-' to read from stdin")
Simon Glassb2e83c62021-12-18 14:54:31 -07001726 parser.add_argument('-e', '--exit-on-error', action='store_true',
Simon Glasse1ae5632021-12-18 08:09:44 -07001727 default=False,
1728 help='exit immediately on any error')
Simon Glassb2e83c62021-12-18 14:54:31 -07001729 parser.add_argument('-f', '--find', action='store_true', default=False,
Simon Glass65d7fce2021-12-18 08:09:46 -07001730 help='Find boards with a given config combination')
Simon Glassb2e83c62021-12-18 14:54:31 -07001731 parser.add_argument('-H', '--headers-only', dest='cleanup_headers_only',
Simon Glasse1ae5632021-12-18 08:09:44 -07001732 action='store_true', default=False,
1733 help='only cleanup the headers')
Simon Glassb2e83c62021-12-18 14:54:31 -07001734 parser.add_argument('-i', '--imply', action='store_true', default=False,
Simon Glass99b66602017-06-01 19:39:03 -06001735 help='find options which imply others')
Simon Glassb2e83c62021-12-18 14:54:31 -07001736 parser.add_argument('-I', '--imply-flags', type=str, default='',
Simon Glass9b2a2e82017-06-15 21:39:32 -06001737 help="control the -i option ('help' for help")
Simon Glassb2e83c62021-12-18 14:54:31 -07001738 parser.add_argument('-j', '--jobs', type=int, default=cpu_count,
Simon Glasse1ae5632021-12-18 08:09:44 -07001739 help='the number of jobs to run simultaneously')
Simon Glassb2e83c62021-12-18 14:54:31 -07001740 parser.add_argument('-n', '--dry-run', action='store_true', default=False,
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001741 help='perform a trial run (show log with no changes)')
Simon Glassb2e83c62021-12-18 14:54:31 -07001742 parser.add_argument('-r', '--git-ref', type=str,
Simon Glasse1ae5632021-12-18 08:09:44 -07001743 help='the git ref to clone for building the autoconf.mk')
Simon Glassb2e83c62021-12-18 14:54:31 -07001744 parser.add_argument('-s', '--force-sync', action='store_true', default=False,
Masahiro Yamada8513dc02016-05-19 15:52:08 +09001745 help='force sync by savedefconfig')
Simon Glassb2e83c62021-12-18 14:54:31 -07001746 parser.add_argument('-S', '--spl', action='store_true', default=False,
Masahiro Yamada07913d12016-08-22 22:18:22 +09001747 help='parse config options defined for SPL build')
Simon Glassb2e83c62021-12-18 14:54:31 -07001748 parser.add_argument('-t', '--test', action='store_true', default=False,
Simon Glasse1ae5632021-12-18 08:09:44 -07001749 help='run unit tests')
Simon Glassb2e83c62021-12-18 14:54:31 -07001750 parser.add_argument('-y', '--yes', action='store_true', default=False,
Simon Glass6b403df2016-09-12 23:18:20 -06001751 help="respond 'yes' to any prompts")
Simon Glassb2e83c62021-12-18 14:54:31 -07001752 parser.add_argument('-v', '--verbose', action='store_true', default=False,
Joe Hershberger95bf9c72015-05-19 13:21:24 -05001753 help='show any build errors as boards are built')
Simon Glassb2e83c62021-12-18 14:54:31 -07001754 parser.add_argument('configs', nargs='*')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001755
Simon Glassb2e83c62021-12-18 14:54:31 -07001756 args = parser.parse_args()
1757 configs = args.configs
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001758
Simon Glassb2e83c62021-12-18 14:54:31 -07001759 if args.test:
Simon Glass84067a52021-12-18 08:09:45 -07001760 sys.argv = [sys.argv[0]]
1761 fail, count = doctest.testmod()
1762 if fail:
1763 return 1
1764 unittest.main()
1765
Simon Glassb2e83c62021-12-18 14:54:31 -07001766 if not any((len(configs), args.force_sync, args.build_db, args.imply,
1767 args.find)):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001768 parser.print_usage()
1769 sys.exit(1)
1770
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001771 # prefix the option name with CONFIG_ if missing
Simon Glass65d7fce2021-12-18 08:09:46 -07001772 configs = [prefix_config(cfg) for cfg in configs]
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001773
Joe Hershberger2144f882015-05-19 13:21:20 -05001774 check_top_directory()
1775
Simon Glassb2e83c62021-12-18 14:54:31 -07001776 if args.imply:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001777 imply_flags = 0
Simon Glassb2e83c62021-12-18 14:54:31 -07001778 if args.imply_flags == 'all':
Simon Glassdee36c72017-07-10 14:47:46 -06001779 imply_flags = -1
1780
Simon Glassb2e83c62021-12-18 14:54:31 -07001781 elif args.imply_flags:
1782 for flag in args.imply_flags.split(','):
Simon Glassdee36c72017-07-10 14:47:46 -06001783 bad = flag not in IMPLY_FLAGS
1784 if bad:
Simon Glass793dca32019-10-31 07:42:57 -06001785 print("Invalid flag '%s'" % flag)
Simon Glassdee36c72017-07-10 14:47:46 -06001786 if flag == 'help' or bad:
Simon Glass793dca32019-10-31 07:42:57 -06001787 print("Imply flags: (separate with ',')")
1788 for name, info in IMPLY_FLAGS.items():
1789 print(' %-15s: %s' % (name, info[1]))
Simon Glassdee36c72017-07-10 14:47:46 -06001790 parser.print_usage()
1791 sys.exit(1)
1792 imply_flags |= IMPLY_FLAGS[flag][0]
Simon Glass9b2a2e82017-06-15 21:39:32 -06001793
Simon Glassb2e83c62021-12-18 14:54:31 -07001794 do_imply_config(configs, args.add_imply, imply_flags, args.skip_added)
Simon Glass99b66602017-06-01 19:39:03 -06001795 return
1796
Simon Glassb2e83c62021-12-18 14:54:31 -07001797 if args.find:
Simon Glass65d7fce2021-12-18 08:09:46 -07001798 do_find_config(configs)
1799 return
1800
Simon Glassd73fcb12017-06-01 19:39:02 -06001801 config_db = {}
Simon Glass793dca32019-10-31 07:42:57 -06001802 db_queue = queue.Queue()
Simon Glassd73fcb12017-06-01 19:39:02 -06001803 t = DatabaseThread(config_db, db_queue)
1804 t.setDaemon(True)
1805 t.start()
1806
Simon Glassb2e83c62021-12-18 14:54:31 -07001807 if not args.cleanup_headers_only:
Masahiro Yamadaf7536f72016-07-25 19:15:23 +09001808 check_clean_directory()
Simon Glass793dca32019-10-31 07:42:57 -06001809 bsettings.Setup('')
Simon Glass6821a742017-07-10 14:47:47 -06001810 toolchains = toolchain.Toolchains()
1811 toolchains.GetSettings()
1812 toolchains.Scan(verbose=False)
Simon Glassb2e83c62021-12-18 14:54:31 -07001813 move_config(toolchains, configs, args, db_queue)
Simon Glassd73fcb12017-06-01 19:39:02 -06001814 db_queue.join()
Joe Hershberger2144f882015-05-19 13:21:20 -05001815
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001816 if configs:
Simon Glassb2e83c62021-12-18 14:54:31 -07001817 cleanup_headers(configs, args)
1818 cleanup_extra_options(configs, args)
1819 cleanup_whitelist(configs, args)
1820 cleanup_readme(configs, args)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001821
Simon Glassb2e83c62021-12-18 14:54:31 -07001822 if args.commit:
Simon Glass9ede2122016-09-12 23:18:21 -06001823 subprocess.call(['git', 'add', '-u'])
1824 if configs:
1825 msg = 'Convert %s %sto Kconfig' % (configs[0],
1826 'et al ' if len(configs) > 1 else '')
1827 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1828 '\n '.join(configs))
1829 else:
1830 msg = 'configs: Resync with savedefconfig'
1831 msg += '\n\nRsync all defconfig files using moveconfig.py'
1832 subprocess.call(['git', 'commit', '-s', '-m', msg])
1833
Simon Glassb2e83c62021-12-18 14:54:31 -07001834 if args.build_db:
Simon Glass91197aa2021-12-18 14:54:35 -07001835 with open(CONFIG_DATABASE, 'w', encoding='utf-8') as fd:
Simon Glass793dca32019-10-31 07:42:57 -06001836 for defconfig, configs in config_db.items():
Simon Glassc79d18c2017-08-13 16:02:54 -06001837 fd.write('%s\n' % defconfig)
Simon Glassd73fcb12017-06-01 19:39:02 -06001838 for config in sorted(configs.keys()):
Simon Glassc79d18c2017-08-13 16:02:54 -06001839 fd.write(' %s=%s\n' % (config, configs[config]))
1840 fd.write('\n')
Simon Glassd73fcb12017-06-01 19:39:02 -06001841
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001842if __name__ == '__main__':
Simon Glass65d7fce2021-12-18 08:09:46 -07001843 sys.exit(main())