blob: cff1e30658100fc0907dd5d5481d79073f37815c [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:
342 raises
343 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 Yamada07913d12016-08-22 22:18:22 +0900793 elif action == ACTION_SPL_NOT_EXIST:
Simon Glassdaa694d2021-12-18 14:54:30 -0700794 actlog = 'SPL is not enabled for this defconfig. Skip.'
Masahiro Yamada07913d12016-08-22 22:18:22 +0900795 log_color = COLOR_PURPLE
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900796 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700797 sys.exit('Internal Error. This should not happen.')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900798
Simon Glassb2e83c62021-12-18 14:54:31 -0700799 log += color_text(self.args.color, log_color, actlog) + '\n'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900800
Simon Glass91197aa2021-12-18 14:54:35 -0700801 with open(self.dotconfig, 'a', encoding='utf-8') as out:
Masahiro Yamadae423d172016-05-19 15:51:49 +0900802 for (action, value) in results:
803 if action == ACTION_MOVE:
Simon Glass91197aa2021-12-18 14:54:35 -0700804 out.write(value + '\n')
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900805 updated = True
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900806
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900807 self.results = results
Masahiro Yamada07913d12016-08-22 22:18:22 +0900808 for f in rm_files:
809 os.remove(f)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900810
Masahiro Yamada916224c2016-08-22 22:18:21 +0900811 return (updated, suspicious, log)
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900812
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900813 def check_defconfig(self):
814 """Check the defconfig after savedefconfig
815
816 Returns:
817 Return additional log if moved CONFIGs were removed again by
818 'make savedefconfig'.
819 """
820
821 log = ''
822
Simon Glass37f815c2021-12-18 14:54:34 -0700823 defconfig_lines = read_file(self.defconfig)
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900824
825 for (action, value) in self.results:
826 if action != ACTION_MOVE:
827 continue
Alper Nebi Yasak6c928c62022-01-29 18:22:08 +0300828 if not value in defconfig_lines:
Simon Glassb2e83c62021-12-18 14:54:31 -0700829 log += color_text(self.args.color, COLOR_YELLOW,
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900830 "'%s' was removed by savedefconfig.\n" %
831 value)
832
833 return log
834
Simon Glassd73fcb12017-06-01 19:39:02 -0600835
836class DatabaseThread(threading.Thread):
837 """This thread processes results from Slot threads.
838
839 It collects the data in the master config directary. There is only one
840 result thread, and this helps to serialise the build output.
841 """
842 def __init__(self, config_db, db_queue):
843 """Set up a new result thread
844
845 Args:
846 builder: Builder which will be sent each result
847 """
848 threading.Thread.__init__(self)
849 self.config_db = config_db
850 self.db_queue= db_queue
851
852 def run(self):
853 """Called to start up the result thread.
854
855 We collect the next result job and pass it on to the build.
856 """
857 while True:
858 defconfig, configs = self.db_queue.get()
859 self.config_db[defconfig] = configs
860 self.db_queue.task_done()
861
862
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900863class Slot:
864
865 """A slot to store a subprocess.
866
867 Each instance of this class handles one subprocess.
868 This class is useful to control multiple threads
869 for faster processing.
870 """
871
Simon Glassb2e83c62021-12-18 14:54:31 -0700872 def __init__(self, toolchains, configs, args, progress, devnull,
Simon Glass6821a742017-07-10 14:47:47 -0600873 make_cmd, reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900874 """Create a new process slot.
875
Simon Glass91197aa2021-12-18 14:54:35 -0700876 Args:
Simon Glass6821a742017-07-10 14:47:47 -0600877 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900878 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -0700879 args: Program arguments
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900880 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900881 devnull: A file object of '/dev/null'.
882 make_cmd: command name of GNU Make.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500883 reference_src_dir: Determine the true starting config state from this
884 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -0600885 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900886 """
Simon Glass6821a742017-07-10 14:47:47 -0600887 self.toolchains = toolchains
Simon Glassb2e83c62021-12-18 14:54:31 -0700888 self.args = args
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900889 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900890 self.build_dir = tempfile.mkdtemp()
891 self.devnull = devnull
892 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500893 self.reference_src_dir = reference_src_dir
Simon Glassd73fcb12017-06-01 19:39:02 -0600894 self.db_queue = db_queue
Simon Glassb2e83c62021-12-18 14:54:31 -0700895 self.parser = KconfigParser(configs, args, self.build_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900896 self.state = STATE_IDLE
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900897 self.failed_boards = set()
898 self.suspicious_boards = set()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900899
900 def __del__(self):
901 """Delete the working directory
902
903 This function makes sure the temporary directory is cleaned away
904 even if Python suddenly dies due to error. It should be done in here
Joe Hershbergerf2dae752016-06-10 14:53:29 -0500905 because it is guaranteed the destructor is always invoked when the
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900906 instance of the class gets unreferenced.
907
908 If the subprocess is still running, wait until it finishes.
909 """
910 if self.state != STATE_IDLE:
911 while self.ps.poll() == None:
912 pass
913 shutil.rmtree(self.build_dir)
914
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900915 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900916 """Assign a new subprocess for defconfig and add it to the slot.
917
918 If the slot is vacant, create a new subprocess for processing the
919 given defconfig and add it to the slot. Just returns False if
920 the slot is occupied (i.e. the current subprocess is still running).
921
Simon Glass91197aa2021-12-18 14:54:35 -0700922 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900923 defconfig: defconfig name.
924
925 Returns:
926 Return True on success or False on failure
927 """
928 if self.state != STATE_IDLE:
929 return False
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900930
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900931 self.defconfig = defconfig
Masahiro Yamada1d085562016-05-19 15:52:02 +0900932 self.log = ''
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900933 self.current_src_dir = self.reference_src_dir
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900934 self.do_defconfig()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900935 return True
936
937 def poll(self):
938 """Check the status of the subprocess and handle it as needed.
939
940 Returns True if the slot is vacant (i.e. in idle state).
941 If the configuration is successfully finished, assign a new
942 subprocess to build include/autoconf.mk.
943 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900944 parse the .config and the include/autoconf.mk, moving
945 config options to the .config as needed.
946 If the .config was updated, run "make savedefconfig" to sync
947 it, update the original defconfig, and then set the slot back
948 to the idle state.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900949
950 Returns:
951 Return True if the subprocess is terminated, False otherwise
952 """
953 if self.state == STATE_IDLE:
954 return True
955
956 if self.ps.poll() == None:
957 return False
958
959 if self.ps.poll() != 0:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900960 self.handle_error()
961 elif self.state == STATE_DEFCONFIG:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900962 if self.reference_src_dir and not self.current_src_dir:
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500963 self.do_savedefconfig()
964 else:
965 self.do_autoconf()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900966 elif self.state == STATE_AUTOCONF:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900967 if self.current_src_dir:
968 self.current_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500969 self.do_defconfig()
Simon Glassb2e83c62021-12-18 14:54:31 -0700970 elif self.args.build_db:
Simon Glassd73fcb12017-06-01 19:39:02 -0600971 self.do_build_db()
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500972 else:
973 self.do_savedefconfig()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900974 elif self.state == STATE_SAVEDEFCONFIG:
975 self.update_defconfig()
976 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700977 sys.exit('Internal Error. This should not happen.')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900978
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900979 return True if self.state == STATE_IDLE else False
Joe Hershberger96464ba2015-05-19 13:21:17 -0500980
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900981 def handle_error(self):
982 """Handle error cases."""
Masahiro Yamada8513dc02016-05-19 15:52:08 +0900983
Simon Glassb2e83c62021-12-18 14:54:31 -0700984 self.log += color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glassdaa694d2021-12-18 14:54:30 -0700985 'Failed to process.\n')
Simon Glassb2e83c62021-12-18 14:54:31 -0700986 if self.args.verbose:
987 self.log += color_text(self.args.color, COLOR_LIGHT_CYAN,
Markus Klotzbuecher4f5c5e92020-02-12 20:46:45 +0100988 self.ps.stderr.read().decode())
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900989 self.finish(False)
Joe Hershberger96464ba2015-05-19 13:21:17 -0500990
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900991 def do_defconfig(self):
992 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900993
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900994 cmd = list(self.make_cmd)
995 cmd.append(self.defconfig)
996 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900997 stderr=subprocess.PIPE,
998 cwd=self.current_src_dir)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900999 self.state = STATE_DEFCONFIG
Masahiro Yamadac8e1b102016-05-19 15:52:07 +09001000
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001001 def do_autoconf(self):
Simon Glassf3b8e642017-06-01 19:39:01 -06001002 """Run 'make AUTO_CONF_PATH'."""
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001003
Simon Glass6821a742017-07-10 14:47:47 -06001004 arch = self.parser.get_arch()
1005 try:
1006 toolchain = self.toolchains.Select(arch)
1007 except ValueError:
Simon Glassb2e83c62021-12-18 14:54:31 -07001008 self.log += color_text(self.args.color, COLOR_YELLOW,
Chris Packhamce3ba452017-08-27 20:00:51 +12001009 "Tool chain for '%s' is missing. Do nothing.\n" % arch)
Masahiro Yamada4efef992016-05-19 15:52:03 +09001010 self.finish(False)
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001011 return
Simon Glass793dca32019-10-31 07:42:57 -06001012 env = toolchain.MakeEnvironment(False)
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +09001013
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001014 cmd = list(self.make_cmd)
Joe Hershberger7740f652015-05-19 13:21:18 -05001015 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Simon Glassf3b8e642017-06-01 19:39:01 -06001016 cmd.append(AUTO_CONF_PATH)
Simon Glass6821a742017-07-10 14:47:47 -06001017 self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001018 stderr=subprocess.PIPE,
1019 cwd=self.current_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001020 self.state = STATE_AUTOCONF
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001021
Simon Glassd73fcb12017-06-01 19:39:02 -06001022 def do_build_db(self):
1023 """Add the board to the database"""
1024 configs = {}
Simon Glass37f815c2021-12-18 14:54:34 -07001025 for line in read_file(os.path.join(self.build_dir, AUTO_CONF_PATH)):
1026 if line.startswith('CONFIG'):
1027 config, value = line.split('=', 1)
1028 configs[config] = value.rstrip()
Simon Glassd73fcb12017-06-01 19:39:02 -06001029 self.db_queue.put([self.defconfig, configs])
1030 self.finish(True)
1031
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001032 def do_savedefconfig(self):
1033 """Update the .config and run 'make savedefconfig'."""
1034
Masahiro Yamada916224c2016-08-22 22:18:21 +09001035 (updated, suspicious, log) = self.parser.update_dotconfig()
1036 if suspicious:
1037 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001038 self.log += log
1039
Simon Glassb2e83c62021-12-18 14:54:31 -07001040 if not self.args.force_sync and not updated:
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001041 self.finish(True)
1042 return
1043 if updated:
Simon Glassb2e83c62021-12-18 14:54:31 -07001044 self.log += color_text(self.args.color, COLOR_LIGHT_GREEN,
Simon Glassdaa694d2021-12-18 14:54:30 -07001045 'Syncing by savedefconfig...\n')
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001046 else:
Simon Glassdaa694d2021-12-18 14:54:30 -07001047 self.log += 'Syncing by savedefconfig (forced by option)...\n'
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001048
1049 cmd = list(self.make_cmd)
1050 cmd.append('savedefconfig')
1051 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
1052 stderr=subprocess.PIPE)
1053 self.state = STATE_SAVEDEFCONFIG
1054
1055 def update_defconfig(self):
1056 """Update the input defconfig and go back to the idle state."""
1057
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001058 log = self.parser.check_defconfig()
1059 if log:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001060 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001061 self.log += log
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001062 orig_defconfig = os.path.join('configs', self.defconfig)
1063 new_defconfig = os.path.join(self.build_dir, 'defconfig')
1064 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
1065
1066 if updated:
Simon Glassb2e83c62021-12-18 14:54:31 -07001067 self.log += color_text(self.args.color, COLOR_LIGHT_BLUE,
Simon Glassdaa694d2021-12-18 14:54:30 -07001068 'defconfig was updated.\n')
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001069
Simon Glassb2e83c62021-12-18 14:54:31 -07001070 if not self.args.dry_run and updated:
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001071 shutil.move(new_defconfig, orig_defconfig)
1072 self.finish(True)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001073
Masahiro Yamada4efef992016-05-19 15:52:03 +09001074 def finish(self, success):
1075 """Display log along with progress and go to the idle state.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001076
Simon Glass91197aa2021-12-18 14:54:35 -07001077 Args:
Masahiro Yamada4efef992016-05-19 15:52:03 +09001078 success: Should be True when the defconfig was processed
1079 successfully, or False when it fails.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001080 """
1081 # output at least 30 characters to hide the "* defconfigs out of *".
1082 log = self.defconfig.ljust(30) + '\n'
1083
1084 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
1085 # Some threads are running in parallel.
1086 # Print log atomically to not mix up logs from different threads.
Simon Glass793dca32019-10-31 07:42:57 -06001087 print(log, file=(sys.stdout if success else sys.stderr))
Masahiro Yamada4efef992016-05-19 15:52:03 +09001088
1089 if not success:
Simon Glassb2e83c62021-12-18 14:54:31 -07001090 if self.args.exit_on_error:
Simon Glassdaa694d2021-12-18 14:54:30 -07001091 sys.exit('Exit on error.')
Masahiro Yamada4efef992016-05-19 15:52:03 +09001092 # If --exit-on-error flag is not set, skip this board and continue.
1093 # Record the failed board.
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001094 self.failed_boards.add(self.defconfig)
Masahiro Yamada4efef992016-05-19 15:52:03 +09001095
Masahiro Yamada1d085562016-05-19 15:52:02 +09001096 self.progress.inc()
1097 self.progress.show()
Masahiro Yamada4efef992016-05-19 15:52:03 +09001098 self.state = STATE_IDLE
Masahiro Yamada1d085562016-05-19 15:52:02 +09001099
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001100 def get_failed_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001101 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001102 """
1103 return self.failed_boards
1104
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001105 def get_suspicious_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001106 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001107 """
Masahiro Yamada916224c2016-08-22 22:18:21 +09001108 return self.suspicious_boards - self.failed_boards
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001109
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001110class Slots:
1111
1112 """Controller of the array of subprocess slots."""
1113
Simon Glassb2e83c62021-12-18 14:54:31 -07001114 def __init__(self, toolchains, configs, args, progress,
Simon Glass6821a742017-07-10 14:47:47 -06001115 reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001116 """Create a new slots controller.
1117
Simon Glass91197aa2021-12-18 14:54:35 -07001118 Args:
Simon Glass6821a742017-07-10 14:47:47 -06001119 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001120 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -07001121 args: Program arguments
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001122 progress: A progress indicator.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001123 reference_src_dir: Determine the true starting config state from this
1124 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -06001125 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001126 """
Simon Glassb2e83c62021-12-18 14:54:31 -07001127 self.args = args
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001128 self.slots = []
Simon Glass478920d2021-12-18 14:54:32 -07001129 devnull = subprocess.DEVNULL
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001130 make_cmd = get_make_cmd()
Simon Glassb2e83c62021-12-18 14:54:31 -07001131 for i in range(args.jobs):
1132 self.slots.append(Slot(toolchains, configs, args, progress,
Simon Glass6821a742017-07-10 14:47:47 -06001133 devnull, make_cmd, reference_src_dir,
1134 db_queue))
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001135
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001136 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001137 """Add a new subprocess if a vacant slot is found.
1138
Simon Glass91197aa2021-12-18 14:54:35 -07001139 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001140 defconfig: defconfig name to be put into.
1141
1142 Returns:
1143 Return True on success or False on failure
1144 """
1145 for slot in self.slots:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001146 if slot.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001147 return True
1148 return False
1149
1150 def available(self):
1151 """Check if there is a vacant slot.
1152
1153 Returns:
1154 Return True if at lease one vacant slot is found, False otherwise.
1155 """
1156 for slot in self.slots:
1157 if slot.poll():
1158 return True
1159 return False
1160
1161 def empty(self):
1162 """Check if all slots are vacant.
1163
1164 Returns:
1165 Return True if all the slots are vacant, False otherwise.
1166 """
1167 ret = True
1168 for slot in self.slots:
1169 if not slot.poll():
1170 ret = False
1171 return ret
1172
1173 def show_failed_boards(self):
1174 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001175 boards = set()
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001176 output_file = 'moveconfig.failed'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001177
1178 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001179 boards |= slot.get_failed_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001180
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001181 if boards:
1182 boards = '\n'.join(boards) + '\n'
Simon Glassdaa694d2021-12-18 14:54:30 -07001183 msg = 'The following boards were not processed due to error:\n'
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001184 msg += boards
Simon Glassdaa694d2021-12-18 14:54:30 -07001185 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassb2e83c62021-12-18 14:54:31 -07001186 print(color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glass793dca32019-10-31 07:42:57 -06001187 msg), file=sys.stderr)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001188
Simon Glass2fd85bd2021-12-18 14:54:33 -07001189 write_file(output_file, boards)
Joe Hershberger2559cd82015-05-19 13:21:22 -05001190
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001191 def show_suspicious_boards(self):
1192 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001193 boards = set()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001194 output_file = 'moveconfig.suspicious'
1195
1196 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001197 boards |= slot.get_suspicious_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001198
1199 if boards:
1200 boards = '\n'.join(boards) + '\n'
Simon Glassdaa694d2021-12-18 14:54:30 -07001201 msg = 'The following boards might have been converted incorrectly.\n'
1202 msg += 'It is highly recommended to check them manually:\n'
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001203 msg += boards
Simon Glassdaa694d2021-12-18 14:54:30 -07001204 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassb2e83c62021-12-18 14:54:31 -07001205 print(color_text(self.args.color, COLOR_YELLOW,
Simon Glass793dca32019-10-31 07:42:57 -06001206 msg), file=sys.stderr)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001207
Simon Glass2fd85bd2021-12-18 14:54:33 -07001208 write_file(output_file, boards)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001209
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001210class ReferenceSource:
1211
1212 """Reference source against which original configs should be parsed."""
1213
1214 def __init__(self, commit):
1215 """Create a reference source directory based on a specified commit.
1216
Simon Glass91197aa2021-12-18 14:54:35 -07001217 Args:
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001218 commit: commit to git-clone
1219 """
1220 self.src_dir = tempfile.mkdtemp()
Simon Glassdaa694d2021-12-18 14:54:30 -07001221 print('Cloning git repo to a separate work directory...')
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001222 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1223 cwd=self.src_dir)
Simon Glass793dca32019-10-31 07:42:57 -06001224 print("Checkout '%s' to build the original autoconf.mk." % \
1225 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip())
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001226 subprocess.check_output(['git', 'checkout', commit],
1227 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001228
1229 def __del__(self):
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001230 """Delete the reference source directory
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001231
1232 This function makes sure the temporary directory is cleaned away
1233 even if Python suddenly dies due to error. It should be done in here
1234 because it is guaranteed the destructor is always invoked when the
1235 instance of the class gets unreferenced.
1236 """
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001237 shutil.rmtree(self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001238
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001239 def get_dir(self):
1240 """Return the absolute path to the reference source directory."""
1241
1242 return self.src_dir
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001243
Simon Glassb2e83c62021-12-18 14:54:31 -07001244def move_config(toolchains, configs, args, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001245 """Move config options to defconfig files.
1246
Simon Glass91197aa2021-12-18 14:54:35 -07001247 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001248 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -07001249 args: Program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001250 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001251 if len(configs) == 0:
Simon Glassb2e83c62021-12-18 14:54:31 -07001252 if args.force_sync:
Simon Glass793dca32019-10-31 07:42:57 -06001253 print('No CONFIG is specified. You are probably syncing defconfigs.', end=' ')
Simon Glassb2e83c62021-12-18 14:54:31 -07001254 elif args.build_db:
Simon Glass793dca32019-10-31 07:42:57 -06001255 print('Building %s database' % CONFIG_DATABASE)
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001256 else:
Simon Glass793dca32019-10-31 07:42:57 -06001257 print('Neither CONFIG nor --force-sync is specified. Nothing will happen.', end=' ')
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001258 else:
Simon Glass793dca32019-10-31 07:42:57 -06001259 print('Move ' + ', '.join(configs), end=' ')
Simon Glassb2e83c62021-12-18 14:54:31 -07001260 print('(jobs: %d)\n' % args.jobs)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001261
Simon Glassb2e83c62021-12-18 14:54:31 -07001262 if args.git_ref:
1263 reference_src = ReferenceSource(args.git_ref)
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001264 reference_src_dir = reference_src.get_dir()
1265 else:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001266 reference_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001267
Simon Glassb2e83c62021-12-18 14:54:31 -07001268 if args.defconfigs:
1269 defconfigs = get_matched_defconfigs(args.defconfigs)
Joe Hershberger91040e82015-05-19 13:21:19 -05001270 else:
Masahiro Yamada684c3062016-07-25 19:15:28 +09001271 defconfigs = get_all_defconfigs()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001272
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001273 progress = Progress(len(defconfigs))
Simon Glassb2e83c62021-12-18 14:54:31 -07001274 slots = Slots(toolchains, configs, args, progress, reference_src_dir,
Simon Glass91197aa2021-12-18 14:54:35 -07001275 db_queue)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001276
1277 # Main loop to process defconfig files:
1278 # Add a new subprocess into a vacant slot.
1279 # Sleep if there is no available slot.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001280 for defconfig in defconfigs:
1281 while not slots.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001282 while not slots.available():
1283 # No available slot: sleep for a while
1284 time.sleep(SLEEP_TIME)
1285
1286 # wait until all the subprocesses finish
1287 while not slots.empty():
1288 time.sleep(SLEEP_TIME)
1289
Simon Glass793dca32019-10-31 07:42:57 -06001290 print('')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001291 slots.show_failed_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001292 slots.show_suspicious_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001293
Simon Glasscb008832017-06-15 21:39:33 -06001294def find_kconfig_rules(kconf, config, imply_config):
1295 """Check whether a config has a 'select' or 'imply' keyword
1296
1297 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -04001298 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -06001299 config: Name of config to check (without CONFIG_ prefix)
1300 imply_config: Implying config (without CONFIG_ prefix) which may or
1301 may not have an 'imply' for 'config')
1302
1303 Returns:
1304 Symbol object for 'config' if found, else None
1305 """
Tom Rini65e05dd2019-09-20 17:42:09 -04001306 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -06001307 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001308 for sel, cond in (sym.selects + sym.implies):
Simon Glassa3627082021-12-18 08:09:42 -07001309 if sel.name == config:
Simon Glasscb008832017-06-15 21:39:33 -06001310 return sym
1311 return None
1312
1313def check_imply_rule(kconf, config, imply_config):
1314 """Check if we can add an 'imply' option
1315
1316 This finds imply_config in the Kconfig and looks to see if it is possible
1317 to add an 'imply' for 'config' to that part of the Kconfig.
1318
1319 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -04001320 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -06001321 config: Name of config to check (without CONFIG_ prefix)
1322 imply_config: Implying config (without CONFIG_ prefix) which may or
1323 may not have an 'imply' for 'config')
1324
1325 Returns:
1326 tuple:
1327 filename of Kconfig file containing imply_config, or None if none
1328 line number within the Kconfig file, or 0 if none
1329 message indicating the result
1330 """
Tom Rini65e05dd2019-09-20 17:42:09 -04001331 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -06001332 if not sym:
1333 return 'cannot find sym'
Simon Glassea40b202021-07-21 21:35:53 -06001334 nodes = sym.nodes
1335 if len(nodes) != 1:
1336 return '%d locations' % len(nodes)
Simon Glassa3627082021-12-18 08:09:42 -07001337 node = nodes[0]
1338 fname, linenum = node.filename, node.linenr
Simon Glasscb008832017-06-15 21:39:33 -06001339 cwd = os.getcwd()
1340 if cwd and fname.startswith(cwd):
1341 fname = fname[len(cwd) + 1:]
1342 file_line = ' at %s:%d' % (fname, linenum)
Simon Glass37f815c2021-12-18 14:54:34 -07001343 data = read_file(fname)
Simon Glasscb008832017-06-15 21:39:33 -06001344 if data[linenum - 1] != 'config %s' % imply_config:
1345 return None, 0, 'bad sym format %s%s' % (data[linenum], file_line)
1346 return fname, linenum, 'adding%s' % file_line
1347
1348def add_imply_rule(config, fname, linenum):
1349 """Add a new 'imply' option to a Kconfig
1350
1351 Args:
1352 config: config option to add an imply for (without CONFIG_ prefix)
1353 fname: Kconfig filename to update
1354 linenum: Line number to place the 'imply' before
1355
1356 Returns:
1357 Message indicating the result
1358 """
1359 file_line = ' at %s:%d' % (fname, linenum)
Simon Glass37f815c2021-12-18 14:54:34 -07001360 data = read_file(fname)
Simon Glasscb008832017-06-15 21:39:33 -06001361 linenum -= 1
1362
1363 for offset, line in enumerate(data[linenum:]):
1364 if line.strip().startswith('help') or not line:
1365 data.insert(linenum + offset, '\timply %s' % config)
Simon Glass2fd85bd2021-12-18 14:54:33 -07001366 write_file(fname, data)
Simon Glasscb008832017-06-15 21:39:33 -06001367 return 'added%s' % file_line
1368
1369 return 'could not insert%s'
1370
1371(IMPLY_MIN_2, IMPLY_TARGET, IMPLY_CMD, IMPLY_NON_ARCH_BOARD) = (
1372 1, 2, 4, 8)
Simon Glass9b2a2e82017-06-15 21:39:32 -06001373
1374IMPLY_FLAGS = {
1375 'min2': [IMPLY_MIN_2, 'Show options which imply >2 boards (normally >5)'],
1376 'target': [IMPLY_TARGET, 'Allow CONFIG_TARGET_... options to imply'],
1377 'cmd': [IMPLY_CMD, 'Allow CONFIG_CMD_... to imply'],
Simon Glasscb008832017-06-15 21:39:33 -06001378 'non-arch-board': [
1379 IMPLY_NON_ARCH_BOARD,
1380 'Allow Kconfig options outside arch/ and /board/ to imply'],
Simon Glass91197aa2021-12-18 14:54:35 -07001381}
Simon Glass9b2a2e82017-06-15 21:39:32 -06001382
Simon Glass9d603392021-12-18 08:09:43 -07001383
1384def read_database():
1385 """Read in the config database
1386
1387 Returns:
1388 tuple:
1389 set of all config options seen (each a str)
1390 set of all defconfigs seen (each a str)
1391 dict of configs for each defconfig:
1392 key: defconfig name, e.g. "MPC8548CDS_legacy_defconfig"
1393 value: dict:
1394 key: CONFIG option
1395 value: Value of option
1396 dict of defconfigs for each config:
1397 key: CONFIG option
1398 value: set of boards using that option
1399
1400 """
1401 configs = {}
1402
1403 # key is defconfig name, value is dict of (CONFIG_xxx, value)
1404 config_db = {}
1405
1406 # Set of all config options we have seen
1407 all_configs = set()
1408
1409 # Set of all defconfigs we have seen
1410 all_defconfigs = set()
1411
1412 defconfig_db = collections.defaultdict(set)
Simon Glass37f815c2021-12-18 14:54:34 -07001413 for line in read_file(CONFIG_DATABASE):
1414 line = line.rstrip()
1415 if not line: # Separator between defconfigs
1416 config_db[defconfig] = configs
1417 all_defconfigs.add(defconfig)
1418 configs = {}
1419 elif line[0] == ' ': # CONFIG line
1420 config, value = line.strip().split('=', 1)
1421 configs[config] = value
1422 defconfig_db[config].add(defconfig)
1423 all_configs.add(config)
1424 else: # New defconfig
1425 defconfig = line
Simon Glass9d603392021-12-18 08:09:43 -07001426
1427 return all_configs, all_defconfigs, config_db, defconfig_db
1428
1429
Simon Glasscb008832017-06-15 21:39:33 -06001430def do_imply_config(config_list, add_imply, imply_flags, skip_added,
1431 check_kconfig=True, find_superset=False):
Simon Glass99b66602017-06-01 19:39:03 -06001432 """Find CONFIG options which imply those in the list
1433
1434 Some CONFIG options can be implied by others and this can help to reduce
1435 the size of the defconfig files. For example, CONFIG_X86 implies
1436 CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
1437 all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
1438 each of the x86 defconfig files.
1439
1440 This function uses the moveconfig database to find such options. It
1441 displays a list of things that could possibly imply those in the list.
1442 The algorithm ignores any that start with CONFIG_TARGET since these
1443 typically refer to only a few defconfigs (often one). It also does not
1444 display a config with less than 5 defconfigs.
1445
1446 The algorithm works using sets. For each target config in config_list:
1447 - Get the set 'defconfigs' which use that target config
1448 - For each config (from a list of all configs):
1449 - Get the set 'imply_defconfig' of defconfigs which use that config
1450 -
1451 - If imply_defconfigs contains anything not in defconfigs then
1452 this config does not imply the target config
1453
1454 Params:
1455 config_list: List of CONFIG options to check (each a string)
Simon Glasscb008832017-06-15 21:39:33 -06001456 add_imply: Automatically add an 'imply' for each config.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001457 imply_flags: Flags which control which implying configs are allowed
1458 (IMPLY_...)
Simon Glasscb008832017-06-15 21:39:33 -06001459 skip_added: Don't show options which already have an imply added.
1460 check_kconfig: Check if implied symbols already have an 'imply' or
1461 'select' for the target config, and show this information if so.
Simon Glass99b66602017-06-01 19:39:03 -06001462 find_superset: True to look for configs which are a superset of those
1463 already found. So for example if CONFIG_EXYNOS5 implies an option,
1464 but CONFIG_EXYNOS covers a larger set of defconfigs and also
1465 implies that option, this will drop the former in favour of the
1466 latter. In practice this option has not proved very used.
1467
1468 Note the terminoloy:
1469 config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
1470 defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
1471 """
Simon Glasscb008832017-06-15 21:39:33 -06001472 kconf = KconfigScanner().conf if check_kconfig else None
1473 if add_imply and add_imply != 'all':
Simon Glassa3627082021-12-18 08:09:42 -07001474 add_imply = add_imply.split(',')
Simon Glasscb008832017-06-15 21:39:33 -06001475
Simon Glass9d603392021-12-18 08:09:43 -07001476 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
Simon Glass99b66602017-06-01 19:39:03 -06001477
Simon Glassa3627082021-12-18 08:09:42 -07001478 # Work through each target config option in turn, independently
Simon Glass99b66602017-06-01 19:39:03 -06001479 for config in config_list:
1480 defconfigs = defconfig_db.get(config)
1481 if not defconfigs:
Simon Glass793dca32019-10-31 07:42:57 -06001482 print('%s not found in any defconfig' % config)
Simon Glass99b66602017-06-01 19:39:03 -06001483 continue
1484
1485 # Get the set of defconfigs without this one (since a config cannot
1486 # imply itself)
1487 non_defconfigs = all_defconfigs - defconfigs
1488 num_defconfigs = len(defconfigs)
Simon Glass793dca32019-10-31 07:42:57 -06001489 print('%s found in %d/%d defconfigs' % (config, num_defconfigs,
1490 len(all_configs)))
Simon Glass99b66602017-06-01 19:39:03 -06001491
1492 # This will hold the results: key=config, value=defconfigs containing it
1493 imply_configs = {}
1494 rest_configs = all_configs - set([config])
1495
1496 # Look at every possible config, except the target one
1497 for imply_config in rest_configs:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001498 if 'ERRATUM' in imply_config:
Simon Glass99b66602017-06-01 19:39:03 -06001499 continue
Simon Glass91197aa2021-12-18 14:54:35 -07001500 if not imply_flags & IMPLY_CMD:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001501 if 'CONFIG_CMD' in imply_config:
1502 continue
Simon Glass91197aa2021-12-18 14:54:35 -07001503 if not imply_flags & IMPLY_TARGET:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001504 if 'CONFIG_TARGET' in imply_config:
1505 continue
Simon Glass99b66602017-06-01 19:39:03 -06001506
1507 # Find set of defconfigs that have this config
1508 imply_defconfig = defconfig_db[imply_config]
1509
1510 # Get the intersection of this with defconfigs containing the
1511 # target config
1512 common_defconfigs = imply_defconfig & defconfigs
1513
1514 # Get the set of defconfigs containing this config which DO NOT
1515 # also contain the taret config. If this set is non-empty it means
1516 # that this config affects other defconfigs as well as (possibly)
1517 # the ones affected by the target config. This means it implies
1518 # things we don't want to imply.
1519 not_common_defconfigs = imply_defconfig & non_defconfigs
1520 if not_common_defconfigs:
1521 continue
1522
1523 # If there are common defconfigs, imply_config may be useful
1524 if common_defconfigs:
1525 skip = False
1526 if find_superset:
Simon Glass793dca32019-10-31 07:42:57 -06001527 for prev in list(imply_configs.keys()):
Simon Glass99b66602017-06-01 19:39:03 -06001528 prev_count = len(imply_configs[prev])
1529 count = len(common_defconfigs)
1530 if (prev_count > count and
1531 (imply_configs[prev] & common_defconfigs ==
1532 common_defconfigs)):
1533 # skip imply_config because prev is a superset
1534 skip = True
1535 break
1536 elif count > prev_count:
1537 # delete prev because imply_config is a superset
1538 del imply_configs[prev]
1539 if not skip:
1540 imply_configs[imply_config] = common_defconfigs
1541
1542 # Now we have a dict imply_configs of configs which imply each config
1543 # The value of each dict item is the set of defconfigs containing that
1544 # config. Rank them so that we print the configs that imply the largest
1545 # number of defconfigs first.
Simon Glasscb008832017-06-15 21:39:33 -06001546 ranked_iconfigs = sorted(imply_configs,
Simon Glass99b66602017-06-01 19:39:03 -06001547 key=lambda k: len(imply_configs[k]), reverse=True)
Simon Glasscb008832017-06-15 21:39:33 -06001548 kconfig_info = ''
1549 cwd = os.getcwd()
1550 add_list = collections.defaultdict(list)
1551 for iconfig in ranked_iconfigs:
1552 num_common = len(imply_configs[iconfig])
Simon Glass99b66602017-06-01 19:39:03 -06001553
1554 # Don't bother if there are less than 5 defconfigs affected.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001555 if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
Simon Glass99b66602017-06-01 19:39:03 -06001556 continue
Simon Glasscb008832017-06-15 21:39:33 -06001557 missing = defconfigs - imply_configs[iconfig]
Simon Glass99b66602017-06-01 19:39:03 -06001558 missing_str = ', '.join(missing) if missing else 'all'
1559 missing_str = ''
Simon Glasscb008832017-06-15 21:39:33 -06001560 show = True
1561 if kconf:
1562 sym = find_kconfig_rules(kconf, config[CONFIG_LEN:],
1563 iconfig[CONFIG_LEN:])
1564 kconfig_info = ''
1565 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001566 nodes = sym.nodes
1567 if len(nodes) == 1:
1568 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001569 if cwd and fname.startswith(cwd):
1570 fname = fname[len(cwd) + 1:]
1571 kconfig_info = '%s:%d' % (fname, linenum)
1572 if skip_added:
1573 show = False
1574 else:
Tom Rini65e05dd2019-09-20 17:42:09 -04001575 sym = kconf.syms.get(iconfig[CONFIG_LEN:])
Simon Glasscb008832017-06-15 21:39:33 -06001576 fname = ''
1577 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001578 nodes = sym.nodes
1579 if len(nodes) == 1:
1580 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001581 if cwd and fname.startswith(cwd):
1582 fname = fname[len(cwd) + 1:]
1583 in_arch_board = not sym or (fname.startswith('arch') or
1584 fname.startswith('board'))
1585 if (not in_arch_board and
Simon Glass91197aa2021-12-18 14:54:35 -07001586 not imply_flags & IMPLY_NON_ARCH_BOARD):
Simon Glasscb008832017-06-15 21:39:33 -06001587 continue
1588
1589 if add_imply and (add_imply == 'all' or
1590 iconfig in add_imply):
1591 fname, linenum, kconfig_info = (check_imply_rule(kconf,
1592 config[CONFIG_LEN:], iconfig[CONFIG_LEN:]))
1593 if fname:
1594 add_list[fname].append(linenum)
1595
1596 if show and kconfig_info != 'skip':
Simon Glass793dca32019-10-31 07:42:57 -06001597 print('%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
1598 kconfig_info, missing_str))
Simon Glasscb008832017-06-15 21:39:33 -06001599
1600 # Having collected a list of things to add, now we add them. We process
1601 # each file from the largest line number to the smallest so that
1602 # earlier additions do not affect our line numbers. E.g. if we added an
1603 # imply at line 20 it would change the position of each line after
1604 # that.
Simon Glass793dca32019-10-31 07:42:57 -06001605 for fname, linenums in add_list.items():
Simon Glasscb008832017-06-15 21:39:33 -06001606 for linenum in sorted(linenums, reverse=True):
1607 add_imply_rule(config[CONFIG_LEN:], fname, linenum)
Simon Glass99b66602017-06-01 19:39:03 -06001608
Simon Glass941671a2022-02-08 11:49:46 -07001609def defconfig_matches(configs, re_match):
1610 """Check if any CONFIG option matches a regex
1611
1612 The match must be complete, i.e. from the start to end of the CONFIG option.
1613
1614 Args:
1615 configs (dict): Dict of CONFIG options:
1616 key: CONFIG option
1617 value: Value of option
1618 re_match (re.Pattern): Match to check
1619
1620 Returns:
1621 bool: True if any CONFIG matches the regex
1622 """
1623 for cfg in configs:
1624 m_cfg = re_match.match(cfg)
1625 if m_cfg and m_cfg.span()[1] == len(cfg):
1626 return True
1627 return False
Simon Glass99b66602017-06-01 19:39:03 -06001628
Simon Glass65d7fce2021-12-18 08:09:46 -07001629def do_find_config(config_list):
1630 """Find boards with a given combination of CONFIGs
1631
1632 Params:
Simon Glass941671a2022-02-08 11:49:46 -07001633 config_list: List of CONFIG options to check (each a regex consisting
Simon Glass65d7fce2021-12-18 08:09:46 -07001634 of a config option, with or without a CONFIG_ prefix. If an option
1635 is preceded by a tilde (~) then it must be false, otherwise it must
1636 be true)
1637 """
1638 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
1639
1640 # Get the whitelist
Simon Glass37f815c2021-12-18 14:54:34 -07001641 adhoc_configs = set(read_file('scripts/config_whitelist.txt'))
Simon Glass65d7fce2021-12-18 08:09:46 -07001642
1643 # Start with all defconfigs
1644 out = all_defconfigs
1645
1646 # Work through each config in turn
1647 adhoc = []
1648 for item in config_list:
1649 # Get the real config name and whether we want this config or not
1650 cfg = item
1651 want = True
1652 if cfg[0] == '~':
1653 want = False
1654 cfg = cfg[1:]
1655
1656 if cfg in adhoc_configs:
1657 adhoc.append(cfg)
1658 continue
1659
1660 # Search everything that is still in the running. If it has a config
1661 # that we want, or doesn't have one that we don't, add it into the
1662 # running for the next stage
1663 in_list = out
1664 out = set()
Simon Glass941671a2022-02-08 11:49:46 -07001665 re_match = re.compile(cfg)
Simon Glass65d7fce2021-12-18 08:09:46 -07001666 for defc in in_list:
Simon Glass941671a2022-02-08 11:49:46 -07001667 has_cfg = defconfig_matches(config_db[defc], re_match)
Simon Glass65d7fce2021-12-18 08:09:46 -07001668 if has_cfg == want:
1669 out.add(defc)
1670 if adhoc:
1671 print(f"Error: Not in Kconfig: %s" % ' '.join(adhoc))
1672 else:
1673 print(f'{len(out)} matches')
Simon Glassb8d11da2022-02-08 11:49:45 -07001674 print(' '.join([remove_defconfig(item) for item in out]))
Simon Glass65d7fce2021-12-18 08:09:46 -07001675
1676
1677def prefix_config(cfg):
1678 """Prefix a config with CONFIG_ if needed
1679
1680 This handles ~ operator, which indicates that the CONFIG should be disabled
1681
1682 >>> prefix_config('FRED')
1683 'CONFIG_FRED'
1684 >>> prefix_config('CONFIG_FRED')
1685 'CONFIG_FRED'
1686 >>> prefix_config('~FRED')
1687 '~CONFIG_FRED'
1688 >>> prefix_config('~CONFIG_FRED')
1689 '~CONFIG_FRED'
1690 >>> prefix_config('A123')
1691 'CONFIG_A123'
1692 """
1693 op = ''
1694 if cfg[0] == '~':
1695 op = cfg[0]
1696 cfg = cfg[1:]
1697 if not cfg.startswith('CONFIG_'):
1698 cfg = 'CONFIG_' + cfg
1699 return op + cfg
1700
1701
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001702def main():
1703 try:
1704 cpu_count = multiprocessing.cpu_count()
1705 except NotImplementedError:
1706 cpu_count = 1
1707
Simon Glassb2e83c62021-12-18 14:54:31 -07001708 epilog = '''Move config options from headers to defconfig files. See
1709doc/develop/moveconfig.rst for documentation.'''
1710
1711 parser = ArgumentParser(epilog=epilog)
1712 # Add arguments here
1713 parser.add_argument('-a', '--add-imply', type=str, default='',
Simon Glasscb008832017-06-15 21:39:33 -06001714 help='comma-separated list of CONFIG options to add '
1715 "an 'imply' statement to for the CONFIG in -i")
Simon Glassb2e83c62021-12-18 14:54:31 -07001716 parser.add_argument('-A', '--skip-added', action='store_true', default=False,
Simon Glasscb008832017-06-15 21:39:33 -06001717 help="don't show options which are already marked as "
1718 'implying others')
Simon Glassb2e83c62021-12-18 14:54:31 -07001719 parser.add_argument('-b', '--build-db', action='store_true', default=False,
Simon Glassd73fcb12017-06-01 19:39:02 -06001720 help='build a CONFIG database')
Simon Glassb2e83c62021-12-18 14:54:31 -07001721 parser.add_argument('-c', '--color', action='store_true', default=False,
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001722 help='display the log in color')
Simon Glassb2e83c62021-12-18 14:54:31 -07001723 parser.add_argument('-C', '--commit', action='store_true', default=False,
Simon Glass9ede2122016-09-12 23:18:21 -06001724 help='Create a git commit for the operation')
Simon Glassb2e83c62021-12-18 14:54:31 -07001725 parser.add_argument('-d', '--defconfigs', type=str,
Simon Glassee4e61b2017-06-01 19:38:59 -06001726 help='a file containing a list of defconfigs to move, '
1727 "one per line (for example 'snow_defconfig') "
1728 "or '-' to read from stdin")
Simon Glassb2e83c62021-12-18 14:54:31 -07001729 parser.add_argument('-e', '--exit-on-error', action='store_true',
Simon Glasse1ae5632021-12-18 08:09:44 -07001730 default=False,
1731 help='exit immediately on any error')
Simon Glassb2e83c62021-12-18 14:54:31 -07001732 parser.add_argument('-f', '--find', action='store_true', default=False,
Simon Glass65d7fce2021-12-18 08:09:46 -07001733 help='Find boards with a given config combination')
Simon Glassb2e83c62021-12-18 14:54:31 -07001734 parser.add_argument('-H', '--headers-only', dest='cleanup_headers_only',
Simon Glasse1ae5632021-12-18 08:09:44 -07001735 action='store_true', default=False,
1736 help='only cleanup the headers')
Simon Glassb2e83c62021-12-18 14:54:31 -07001737 parser.add_argument('-i', '--imply', action='store_true', default=False,
Simon Glass99b66602017-06-01 19:39:03 -06001738 help='find options which imply others')
Simon Glassb2e83c62021-12-18 14:54:31 -07001739 parser.add_argument('-I', '--imply-flags', type=str, default='',
Simon Glass9b2a2e82017-06-15 21:39:32 -06001740 help="control the -i option ('help' for help")
Simon Glassb2e83c62021-12-18 14:54:31 -07001741 parser.add_argument('-j', '--jobs', type=int, default=cpu_count,
Simon Glasse1ae5632021-12-18 08:09:44 -07001742 help='the number of jobs to run simultaneously')
Simon Glassb2e83c62021-12-18 14:54:31 -07001743 parser.add_argument('-n', '--dry-run', action='store_true', default=False,
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001744 help='perform a trial run (show log with no changes)')
Simon Glassb2e83c62021-12-18 14:54:31 -07001745 parser.add_argument('-r', '--git-ref', type=str,
Simon Glasse1ae5632021-12-18 08:09:44 -07001746 help='the git ref to clone for building the autoconf.mk')
Simon Glassb2e83c62021-12-18 14:54:31 -07001747 parser.add_argument('-s', '--force-sync', action='store_true', default=False,
Masahiro Yamada8513dc02016-05-19 15:52:08 +09001748 help='force sync by savedefconfig')
Simon Glassb2e83c62021-12-18 14:54:31 -07001749 parser.add_argument('-S', '--spl', action='store_true', default=False,
Masahiro Yamada07913d12016-08-22 22:18:22 +09001750 help='parse config options defined for SPL build')
Simon Glassb2e83c62021-12-18 14:54:31 -07001751 parser.add_argument('-t', '--test', action='store_true', default=False,
Simon Glasse1ae5632021-12-18 08:09:44 -07001752 help='run unit tests')
Simon Glassb2e83c62021-12-18 14:54:31 -07001753 parser.add_argument('-y', '--yes', action='store_true', default=False,
Simon Glass6b403df2016-09-12 23:18:20 -06001754 help="respond 'yes' to any prompts")
Simon Glassb2e83c62021-12-18 14:54:31 -07001755 parser.add_argument('-v', '--verbose', action='store_true', default=False,
Joe Hershberger95bf9c72015-05-19 13:21:24 -05001756 help='show any build errors as boards are built')
Simon Glassb2e83c62021-12-18 14:54:31 -07001757 parser.add_argument('configs', nargs='*')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001758
Simon Glassb2e83c62021-12-18 14:54:31 -07001759 args = parser.parse_args()
1760 configs = args.configs
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001761
Simon Glassb2e83c62021-12-18 14:54:31 -07001762 if args.test:
Simon Glass84067a52021-12-18 08:09:45 -07001763 sys.argv = [sys.argv[0]]
1764 fail, count = doctest.testmod()
1765 if fail:
1766 return 1
1767 unittest.main()
1768
Simon Glassb2e83c62021-12-18 14:54:31 -07001769 if not any((len(configs), args.force_sync, args.build_db, args.imply,
1770 args.find)):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001771 parser.print_usage()
1772 sys.exit(1)
1773
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001774 # prefix the option name with CONFIG_ if missing
Simon Glass65d7fce2021-12-18 08:09:46 -07001775 configs = [prefix_config(cfg) for cfg in configs]
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001776
Joe Hershberger2144f882015-05-19 13:21:20 -05001777 check_top_directory()
1778
Simon Glassb2e83c62021-12-18 14:54:31 -07001779 if args.imply:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001780 imply_flags = 0
Simon Glassb2e83c62021-12-18 14:54:31 -07001781 if args.imply_flags == 'all':
Simon Glassdee36c72017-07-10 14:47:46 -06001782 imply_flags = -1
1783
Simon Glassb2e83c62021-12-18 14:54:31 -07001784 elif args.imply_flags:
1785 for flag in args.imply_flags.split(','):
Simon Glassdee36c72017-07-10 14:47:46 -06001786 bad = flag not in IMPLY_FLAGS
1787 if bad:
Simon Glass793dca32019-10-31 07:42:57 -06001788 print("Invalid flag '%s'" % flag)
Simon Glassdee36c72017-07-10 14:47:46 -06001789 if flag == 'help' or bad:
Simon Glass793dca32019-10-31 07:42:57 -06001790 print("Imply flags: (separate with ',')")
1791 for name, info in IMPLY_FLAGS.items():
1792 print(' %-15s: %s' % (name, info[1]))
Simon Glassdee36c72017-07-10 14:47:46 -06001793 parser.print_usage()
1794 sys.exit(1)
1795 imply_flags |= IMPLY_FLAGS[flag][0]
Simon Glass9b2a2e82017-06-15 21:39:32 -06001796
Simon Glassb2e83c62021-12-18 14:54:31 -07001797 do_imply_config(configs, args.add_imply, imply_flags, args.skip_added)
Simon Glass99b66602017-06-01 19:39:03 -06001798 return
1799
Simon Glassb2e83c62021-12-18 14:54:31 -07001800 if args.find:
Simon Glass65d7fce2021-12-18 08:09:46 -07001801 do_find_config(configs)
1802 return
1803
Simon Glassd73fcb12017-06-01 19:39:02 -06001804 config_db = {}
Simon Glass793dca32019-10-31 07:42:57 -06001805 db_queue = queue.Queue()
Simon Glassd73fcb12017-06-01 19:39:02 -06001806 t = DatabaseThread(config_db, db_queue)
1807 t.setDaemon(True)
1808 t.start()
1809
Simon Glassb2e83c62021-12-18 14:54:31 -07001810 if not args.cleanup_headers_only:
Masahiro Yamadaf7536f72016-07-25 19:15:23 +09001811 check_clean_directory()
Simon Glass793dca32019-10-31 07:42:57 -06001812 bsettings.Setup('')
Simon Glass6821a742017-07-10 14:47:47 -06001813 toolchains = toolchain.Toolchains()
1814 toolchains.GetSettings()
1815 toolchains.Scan(verbose=False)
Simon Glassb2e83c62021-12-18 14:54:31 -07001816 move_config(toolchains, configs, args, db_queue)
Simon Glassd73fcb12017-06-01 19:39:02 -06001817 db_queue.join()
Joe Hershberger2144f882015-05-19 13:21:20 -05001818
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001819 if configs:
Simon Glassb2e83c62021-12-18 14:54:31 -07001820 cleanup_headers(configs, args)
1821 cleanup_extra_options(configs, args)
1822 cleanup_whitelist(configs, args)
1823 cleanup_readme(configs, args)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001824
Simon Glassb2e83c62021-12-18 14:54:31 -07001825 if args.commit:
Simon Glass9ede2122016-09-12 23:18:21 -06001826 subprocess.call(['git', 'add', '-u'])
1827 if configs:
1828 msg = 'Convert %s %sto Kconfig' % (configs[0],
1829 'et al ' if len(configs) > 1 else '')
1830 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1831 '\n '.join(configs))
1832 else:
1833 msg = 'configs: Resync with savedefconfig'
1834 msg += '\n\nRsync all defconfig files using moveconfig.py'
1835 subprocess.call(['git', 'commit', '-s', '-m', msg])
1836
Simon Glassb2e83c62021-12-18 14:54:31 -07001837 if args.build_db:
Simon Glass91197aa2021-12-18 14:54:35 -07001838 with open(CONFIG_DATABASE, 'w', encoding='utf-8') as fd:
Simon Glass793dca32019-10-31 07:42:57 -06001839 for defconfig, configs in config_db.items():
Simon Glassc79d18c2017-08-13 16:02:54 -06001840 fd.write('%s\n' % defconfig)
Simon Glassd73fcb12017-06-01 19:39:02 -06001841 for config in sorted(configs.keys()):
Simon Glassc79d18c2017-08-13 16:02:54 -06001842 fd.write(' %s=%s\n' % (config, configs[config]))
1843 fd.write('\n')
Simon Glassd73fcb12017-06-01 19:39:02 -06001844
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001845if __name__ == '__main__':
Simon Glass65d7fce2021-12-18 08:09:46 -07001846 sys.exit(main())