blob: 35fe6710d70a0cb3ef0e295aa4d397462e5c8928 [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
Masahiro Yamada5a27c732015-05-20 11:36:07 +090094### helper functions ###
Masahiro Yamada5a27c732015-05-20 11:36:07 +090095def check_top_directory():
96 """Exit if we are not at the top of source directory."""
Simon Glass91197aa2021-12-18 14:54:35 -070097 for fname in 'README', 'Licenses':
98 if not os.path.exists(fname):
Masahiro Yamada5a27c732015-05-20 11:36:07 +090099 sys.exit('Please run at the top of source directory.')
100
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900101def check_clean_directory():
102 """Exit if the source tree is not clean."""
Simon Glass91197aa2021-12-18 14:54:35 -0700103 for fname in '.config', 'include/config':
104 if os.path.exists(fname):
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900105 sys.exit("source tree is not clean, please run 'make mrproper'")
106
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900107def get_make_cmd():
108 """Get the command name of GNU Make.
109
110 U-Boot needs GNU Make for building, but the command name is not
111 necessarily "make". (for example, "gmake" on FreeBSD).
112 Returns the most appropriate command name on your system.
113 """
Simon Glass91197aa2021-12-18 14:54:35 -0700114 with subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE) as proc:
115 ret = proc.communicate()
116 if proc.returncode:
117 sys.exit('GNU Make not found')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900118 return ret[0].rstrip()
119
Simon Glass25f978c2017-06-01 19:38:58 -0600120def get_matched_defconfig(line):
121 """Get the defconfig files that match a pattern
122
123 Args:
Simon Glass91197aa2021-12-18 14:54:35 -0700124 line (str): Path or filename to match, e.g. 'configs/snow_defconfig' or
Simon Glass25f978c2017-06-01 19:38:58 -0600125 'k2*_defconfig'. If no directory is provided, 'configs/' is
126 prepended
127
128 Returns:
Simon Glass91197aa2021-12-18 14:54:35 -0700129 list of str: a list of matching defconfig files
Simon Glass25f978c2017-06-01 19:38:58 -0600130 """
131 dirname = os.path.dirname(line)
132 if dirname:
133 pattern = line
134 else:
135 pattern = os.path.join('configs', line)
136 return glob.glob(pattern) + glob.glob(pattern + '_defconfig')
137
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900138def get_matched_defconfigs(defconfigs_file):
Simon Glassee4e61b2017-06-01 19:38:59 -0600139 """Get all the defconfig files that match the patterns in a file.
140
141 Args:
Simon Glass91197aa2021-12-18 14:54:35 -0700142 defconfigs_file (str): File containing a list of defconfigs to process,
143 or '-' to read the list from stdin
Simon Glassee4e61b2017-06-01 19:38:59 -0600144
145 Returns:
Simon Glass91197aa2021-12-18 14:54:35 -0700146 list of str: A list of paths to defconfig files, with no duplicates
Simon Glassee4e61b2017-06-01 19:38:59 -0600147 """
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900148 defconfigs = []
Simon Glass91197aa2021-12-18 14:54:35 -0700149 with ExitStack() as stack:
150 if defconfigs_file == '-':
151 inf = sys.stdin
152 defconfigs_file = 'stdin'
153 else:
154 inf = stack.enter_context(open(defconfigs_file, encoding='utf-8'))
155 for i, line in enumerate(inf):
156 line = line.strip()
157 if not line:
158 continue # skip blank lines silently
159 if ' ' in line:
160 line = line.split(' ')[0] # handle 'git log' input
161 matched = get_matched_defconfig(line)
162 if not matched:
163 print(f"warning: {defconfigs_file}:{i + 1}: no defconfig matched '{line}'",
164 file=sys.stderr)
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900165
Simon Glass91197aa2021-12-18 14:54:35 -0700166 defconfigs += matched
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900167
168 # use set() to drop multiple matching
Simon Glass91197aa2021-12-18 14:54:35 -0700169 return [defconfig[len('configs') + 1:] for defconfig in set(defconfigs)]
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900170
Masahiro Yamada684c3062016-07-25 19:15:28 +0900171def get_all_defconfigs():
Simon Glass91197aa2021-12-18 14:54:35 -0700172 """Get all the defconfig files under the configs/ directory.
173
174 Returns:
175 list of str: List of paths to defconfig files
176 """
Masahiro Yamada684c3062016-07-25 19:15:28 +0900177 defconfigs = []
Simon Glass91197aa2021-12-18 14:54:35 -0700178 for (dirpath, _, filenames) in os.walk('configs'):
Masahiro Yamada684c3062016-07-25 19:15:28 +0900179 dirpath = dirpath[len('configs') + 1:]
180 for filename in fnmatch.filter(filenames, '*_defconfig'):
181 defconfigs.append(os.path.join(dirpath, filename))
182
183 return defconfigs
184
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900185def color_text(color_enabled, color, string):
186 """Return colored string."""
187 if color_enabled:
Masahiro Yamada1d085562016-05-19 15:52:02 +0900188 # LF should not be surrounded by the escape sequence.
189 # Otherwise, additional whitespace or line-feed might be printed.
190 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
191 for s in string.split('\n') ])
Simon Glass91197aa2021-12-18 14:54:35 -0700192 return string
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900193
Simon Glass91197aa2021-12-18 14:54:35 -0700194def show_diff(alines, blines, file_path, color_enabled):
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900195 """Show unidified diff.
196
Simon Glass91197aa2021-12-18 14:54:35 -0700197 Args:
198 alines (list of str): A list of lines (before)
199 blines (list of str): A list of lines (after)
200 file_path (str): Path to the file
201 color_enabled (bool): Display the diff in color
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900202 """
Simon Glass91197aa2021-12-18 14:54:35 -0700203 diff = difflib.unified_diff(alines, blines,
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900204 fromfile=os.path.join('a', file_path),
205 tofile=os.path.join('b', file_path))
206
207 for line in diff:
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900208 if line[0] == '-' and line[1] != '-':
Simon Glass793dca32019-10-31 07:42:57 -0600209 print(color_text(color_enabled, COLOR_RED, line), end=' ')
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900210 elif line[0] == '+' and line[1] != '+':
Simon Glass793dca32019-10-31 07:42:57 -0600211 print(color_text(color_enabled, COLOR_GREEN, line), end=' ')
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900212 else:
Simon Glass793dca32019-10-31 07:42:57 -0600213 print(line, end=' ')
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900214
Simon Glass91197aa2021-12-18 14:54:35 -0700215def extend_matched_lines(lines, matched, pre_patterns, post_patterns,
216 extend_pre, extend_post):
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900217 """Extend matched lines if desired patterns are found before/after already
218 matched lines.
219
Simon Glass91197aa2021-12-18 14:54:35 -0700220 Args:
221 lines (list of str): list of lines handled.
222 matched (list of int): list of line numbers that have been already
223 matched (will be updated by this function)
224 pre_patterns (list of re.Pattern): list of regular expression that should
225 be matched as preamble
226 post_patterns (list of re.Pattern): list of regular expression that should
227 be matched as postamble
228 extend_pre (bool): Add the line number of matched preamble to the matched
229 list
230 extend_post (bool): Add the line number of matched postamble to the
231 matched list
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900232 """
233 extended_matched = []
234
235 j = matched[0]
236
237 for i in matched:
238 if i == 0 or i < j:
239 continue
240 j = i
241 while j in matched:
242 j += 1
243 if j >= len(lines):
244 break
245
Simon Glass91197aa2021-12-18 14:54:35 -0700246 for pat in pre_patterns:
247 if pat.search(lines[i - 1]):
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900248 break
249 else:
250 # not matched
251 continue
252
Simon Glass91197aa2021-12-18 14:54:35 -0700253 for pat in post_patterns:
254 if pat.search(lines[j]):
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900255 break
256 else:
257 # not matched
258 continue
259
260 if extend_pre:
261 extended_matched.append(i - 1)
262 if extend_post:
263 extended_matched.append(j)
264
265 matched += extended_matched
266 matched.sort()
267
Simon Glassb2e83c62021-12-18 14:54:31 -0700268def confirm(args, prompt):
Simon Glass91197aa2021-12-18 14:54:35 -0700269 """Ask the user to confirm something
270
271 Args:
272 args (Namespace ): program arguments
273
274 Returns:
275 bool: True to confirm, False to cancel/stop
276 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700277 if not args.yes:
Chris Packham85edfc12017-05-02 21:30:46 +1200278 while True:
Simon Glass91197aa2021-12-18 14:54:35 -0700279 choice = input(f'{prompt} [y/n]: ')
Chris Packham85edfc12017-05-02 21:30:46 +1200280 choice = choice.lower()
Simon Glass793dca32019-10-31 07:42:57 -0600281 print(choice)
Simon Glass91197aa2021-12-18 14:54:35 -0700282 if choice in ('y', 'n'):
Chris Packham85edfc12017-05-02 21:30:46 +1200283 break
284
285 if choice == 'n':
286 return False
287
288 return True
289
Simon Glass2fd85bd2021-12-18 14:54:33 -0700290def write_file(fname, data):
291 """Write data to a file
292
293 Args:
294 fname (str): Filename to write to
295 data (list of str): Lines to write (with or without trailing newline);
296 or str to write
297 """
298 with open(fname, 'w', encoding='utf-8') as out:
299 if isinstance(data, list):
300 for line in data:
301 print(line.rstrip('\n'), file=out)
302 else:
303 out.write(data)
304
Simon Glass37f815c2021-12-18 14:54:34 -0700305def read_file(fname, as_lines=True, skip_unicode=False):
306 """Read a file and return the contents
307
308 Args:
309 fname (str): Filename to read from
310 as_lines: Return file contents as a list of lines
311 skip_unicode (bool): True to report unicode errors and continue
312
313 Returns:
314 iter of str: List of ;ines from the file with newline removed; str if
315 as_lines is False with newlines intact; or None if a unicode error
316 occurred
317
318 Raises:
319 UnicodeDecodeError: Unicode error occurred when reading
320 """
321 with open(fname, encoding='utf-8') as inf:
322 try:
323 if as_lines:
324 return [line.rstrip('\n') for line in inf.readlines()]
325 else:
326 return inf.read()
327 except UnicodeDecodeError as e:
328 if not skip_unicode:
329 raises
330 print("Failed on file %s': %s" % (fname, e))
331 return None
332
Simon Glassb2e83c62021-12-18 14:54:31 -0700333def cleanup_empty_blocks(header_path, args):
Chris Packham4d9dbb12019-01-30 20:23:16 +1300334 """Clean up empty conditional blocks
335
Simon Glass91197aa2021-12-18 14:54:35 -0700336 Args:
337 header_path (str): path to the cleaned file.
338 args (Namespace): program arguments
Chris Packham4d9dbb12019-01-30 20:23:16 +1300339 """
340 pattern = re.compile(r'^\s*#\s*if.*$\n^\s*#\s*endif.*$\n*', flags=re.M)
Simon Glass37f815c2021-12-18 14:54:34 -0700341 data = read_file(header_path, as_lines=False, skip_unicode=True)
342 if data is None:
343 return
Chris Packham4d9dbb12019-01-30 20:23:16 +1300344
345 new_data = pattern.sub('\n', data)
346
347 show_diff(data.splitlines(True), new_data.splitlines(True), header_path,
Simon Glassb2e83c62021-12-18 14:54:31 -0700348 args.color)
Chris Packham4d9dbb12019-01-30 20:23:16 +1300349
Simon Glassb2e83c62021-12-18 14:54:31 -0700350 if args.dry_run:
Chris Packham4d9dbb12019-01-30 20:23:16 +1300351 return
352
Simon Glass37f815c2021-12-18 14:54:34 -0700353 if new_data != data:
354 write_file(header_path, new_data)
Chris Packham4d9dbb12019-01-30 20:23:16 +1300355
Simon Glassb2e83c62021-12-18 14:54:31 -0700356def cleanup_one_header(header_path, patterns, args):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900357 """Clean regex-matched lines away from a file.
358
Simon Glass91197aa2021-12-18 14:54:35 -0700359 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900360 header_path: path to the cleaned file.
361 patterns: list of regex patterns. Any lines matching to these
362 patterns are deleted.
Simon Glass91197aa2021-12-18 14:54:35 -0700363 args (Namespace): program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900364 """
Simon Glass37f815c2021-12-18 14:54:34 -0700365 lines = read_file(header_path, skip_unicode=True)
366 if lines is None:
367 return
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900368
369 matched = []
370 for i, line in enumerate(lines):
Masahiro Yamadaa3a779f2016-07-25 19:15:27 +0900371 if i - 1 in matched and lines[i - 1][-2:] == '\\\n':
372 matched.append(i)
373 continue
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900374 for pattern in patterns:
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900375 if pattern.search(line):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900376 matched.append(i)
377 break
378
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900379 if not matched:
380 return
381
382 # remove empty #ifdef ... #endif, successive blank lines
383 pattern_if = re.compile(r'#\s*if(def|ndef)?\W') # #if, #ifdef, #ifndef
384 pattern_elif = re.compile(r'#\s*el(if|se)\W') # #elif, #else
385 pattern_endif = re.compile(r'#\s*endif\W') # #endif
386 pattern_blank = re.compile(r'^\s*$') # empty line
387
388 while True:
389 old_matched = copy.copy(matched)
390 extend_matched_lines(lines, matched, [pattern_if],
391 [pattern_endif], True, True)
392 extend_matched_lines(lines, matched, [pattern_elif],
393 [pattern_elif, pattern_endif], True, False)
394 extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
395 [pattern_blank], False, True)
396 extend_matched_lines(lines, matched, [pattern_blank],
397 [pattern_elif, pattern_endif], True, False)
398 extend_matched_lines(lines, matched, [pattern_blank],
399 [pattern_blank], True, False)
400 if matched == old_matched:
401 break
402
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900403 tolines = copy.copy(lines)
404
405 for i in reversed(matched):
406 tolines.pop(i)
407
Simon Glassb2e83c62021-12-18 14:54:31 -0700408 show_diff(lines, tolines, header_path, args.color)
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900409
Simon Glassb2e83c62021-12-18 14:54:31 -0700410 if args.dry_run:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900411 return
412
Simon Glass2fd85bd2021-12-18 14:54:33 -0700413 write_file(header_path, tolines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900414
Simon Glassb2e83c62021-12-18 14:54:31 -0700415def cleanup_headers(configs, args):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900416 """Delete config defines from board headers.
417
Simon Glass91197aa2021-12-18 14:54:35 -0700418 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900419 configs: A list of CONFIGs to remove.
Simon Glass91197aa2021-12-18 14:54:35 -0700420 args (Namespace): program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900421 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700422 if not confirm(args, 'Clean up headers?'):
Chris Packham85edfc12017-05-02 21:30:46 +1200423 return
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900424
425 patterns = []
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900426 for config in configs:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900427 patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
428 patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
429
Joe Hershberger60727f52015-05-19 13:21:21 -0500430 for dir in 'include', 'arch', 'board':
431 for (dirpath, dirnames, filenames) in os.walk(dir):
Masahiro Yamadadc6de502016-07-25 19:15:22 +0900432 if dirpath == os.path.join('include', 'generated'):
433 continue
Joe Hershberger60727f52015-05-19 13:21:21 -0500434 for filename in filenames:
Simon Glassa38cc172020-08-11 11:23:34 -0600435 if not filename.endswith(('~', '.dts', '.dtsi', '.bin',
Trevor Woernerdc514d72021-03-15 12:01:33 -0400436 '.elf','.aml','.dat')):
Chris Packham4d9dbb12019-01-30 20:23:16 +1300437 header_path = os.path.join(dirpath, filename)
Tom Rini02b56702019-11-10 21:19:37 -0500438 # This file contains UTF-16 data and no CONFIG symbols
439 if header_path == 'include/video_font_data.h':
440 continue
Simon Glassb2e83c62021-12-18 14:54:31 -0700441 cleanup_one_header(header_path, patterns, args)
442 cleanup_empty_blocks(header_path, args)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900443
Simon Glassb2e83c62021-12-18 14:54:31 -0700444def cleanup_one_extra_option(defconfig_path, configs, args):
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900445 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file.
446
Simon Glass91197aa2021-12-18 14:54:35 -0700447 Args:
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900448 defconfig_path: path to the cleaned defconfig file.
449 configs: A list of CONFIGs to remove.
Simon Glass91197aa2021-12-18 14:54:35 -0700450 args (Namespace): program arguments
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900451 """
452
453 start = 'CONFIG_SYS_EXTRA_OPTIONS="'
454 end = '"\n'
455
Simon Glass37f815c2021-12-18 14:54:34 -0700456 lines = read_file(defconfig_path)
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900457
458 for i, line in enumerate(lines):
459 if line.startswith(start) and line.endswith(end):
460 break
461 else:
462 # CONFIG_SYS_EXTRA_OPTIONS was not found in this defconfig
463 return
464
465 old_tokens = line[len(start):-len(end)].split(',')
466 new_tokens = []
467
468 for token in old_tokens:
469 pos = token.find('=')
470 if not (token[:pos] if pos >= 0 else token) in configs:
471 new_tokens.append(token)
472
473 if new_tokens == old_tokens:
474 return
475
476 tolines = copy.copy(lines)
477
478 if new_tokens:
479 tolines[i] = start + ','.join(new_tokens) + end
480 else:
481 tolines.pop(i)
482
Simon Glassb2e83c62021-12-18 14:54:31 -0700483 show_diff(lines, tolines, defconfig_path, args.color)
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900484
Simon Glassb2e83c62021-12-18 14:54:31 -0700485 if args.dry_run:
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900486 return
487
Simon Glass2fd85bd2021-12-18 14:54:33 -0700488 write_file(defconfig_path, tolines)
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900489
Simon Glassb2e83c62021-12-18 14:54:31 -0700490def cleanup_extra_options(configs, args):
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900491 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files.
492
Simon Glass91197aa2021-12-18 14:54:35 -0700493 Args:
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900494 configs: A list of CONFIGs to remove.
Simon Glass91197aa2021-12-18 14:54:35 -0700495 args (Namespace): program arguments
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900496 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700497 if not confirm(args, 'Clean up CONFIG_SYS_EXTRA_OPTIONS?'):
Chris Packham85edfc12017-05-02 21:30:46 +1200498 return
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900499
500 configs = [ config[len('CONFIG_'):] for config in configs ]
501
502 defconfigs = get_all_defconfigs()
503
504 for defconfig in defconfigs:
505 cleanup_one_extra_option(os.path.join('configs', defconfig), configs,
Simon Glassb2e83c62021-12-18 14:54:31 -0700506 args)
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900507
Simon Glassb2e83c62021-12-18 14:54:31 -0700508def cleanup_whitelist(configs, args):
Chris Packhamca438342017-05-02 21:30:47 +1200509 """Delete config whitelist entries
510
Simon Glass91197aa2021-12-18 14:54:35 -0700511 Args:
Chris Packhamca438342017-05-02 21:30:47 +1200512 configs: A list of CONFIGs to remove.
Simon Glass91197aa2021-12-18 14:54:35 -0700513 args (Namespace): program arguments
Chris Packhamca438342017-05-02 21:30:47 +1200514 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700515 if not confirm(args, 'Clean up whitelist entries?'):
Chris Packhamca438342017-05-02 21:30:47 +1200516 return
517
Simon Glass37f815c2021-12-18 14:54:34 -0700518 lines = read_file(os.path.join('scripts', 'config_whitelist.txt'))
Chris Packhamca438342017-05-02 21:30:47 +1200519
520 lines = [x for x in lines if x.strip() not in configs]
521
Simon Glass2fd85bd2021-12-18 14:54:33 -0700522 write_file(os.path.join('scripts', 'config_whitelist.txt'), lines)
Chris Packhamca438342017-05-02 21:30:47 +1200523
Chris Packhamf90df592017-05-02 21:30:48 +1200524def find_matching(patterns, line):
525 for pat in patterns:
526 if pat.search(line):
527 return True
528 return False
529
Simon Glassb2e83c62021-12-18 14:54:31 -0700530def cleanup_readme(configs, args):
Chris Packhamf90df592017-05-02 21:30:48 +1200531 """Delete config description in README
532
Simon Glass91197aa2021-12-18 14:54:35 -0700533 Args:
Chris Packhamf90df592017-05-02 21:30:48 +1200534 configs: A list of CONFIGs to remove.
Simon Glass91197aa2021-12-18 14:54:35 -0700535 args (Namespace): program arguments
Chris Packhamf90df592017-05-02 21:30:48 +1200536 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700537 if not confirm(args, 'Clean up README?'):
Chris Packhamf90df592017-05-02 21:30:48 +1200538 return
539
540 patterns = []
541 for config in configs:
542 patterns.append(re.compile(r'^\s+%s' % config))
543
Simon Glass37f815c2021-12-18 14:54:34 -0700544 lines = read_file('README')
Chris Packhamf90df592017-05-02 21:30:48 +1200545
546 found = False
547 newlines = []
548 for line in lines:
549 if not found:
550 found = find_matching(patterns, line)
551 if found:
552 continue
553
554 if found and re.search(r'^\s+CONFIG', line):
555 found = False
556
557 if not found:
558 newlines.append(line)
559
Simon Glass2fd85bd2021-12-18 14:54:33 -0700560 write_file('README', newlines)
Chris Packhamf90df592017-05-02 21:30:48 +1200561
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200562def try_expand(line):
563 """If value looks like an expression, try expanding it
564 Otherwise just return the existing value
565 """
566 if line.find('=') == -1:
567 return line
568
569 try:
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +0100570 aeval = asteval.Interpreter( usersyms=SIZES, minimal=True )
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200571 cfg, val = re.split("=", line)
572 val= val.strip('\"')
Simon Glassdaa694d2021-12-18 14:54:30 -0700573 if re.search(r'[*+-/]|<<|SZ_+|\(([^\)]+)\)', val):
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +0100574 newval = hex(aeval(val))
Simon Glassdaa694d2021-12-18 14:54:30 -0700575 print('\tExpanded expression %s to %s' % (val, newval))
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200576 return cfg+'='+newval
577 except:
Simon Glassdaa694d2021-12-18 14:54:30 -0700578 print('\tFailed to expand expression in %s' % line)
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200579
580 return line
581
Chris Packhamca438342017-05-02 21:30:47 +1200582
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900583### classes ###
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900584class Progress:
585
586 """Progress Indicator"""
587
588 def __init__(self, total):
589 """Create a new progress indicator.
590
Simon Glass91197aa2021-12-18 14:54:35 -0700591 Args:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900592 total: A number of defconfig files to process.
593 """
594 self.current = 0
595 self.total = total
596
597 def inc(self):
598 """Increment the number of processed defconfig files."""
599
600 self.current += 1
601
602 def show(self):
603 """Display the progress."""
Simon Glass793dca32019-10-31 07:42:57 -0600604 print(' %d defconfigs out of %d\r' % (self.current, self.total), end=' ')
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900605 sys.stdout.flush()
606
Simon Glasscb008832017-06-15 21:39:33 -0600607
608class KconfigScanner:
609 """Kconfig scanner."""
610
611 def __init__(self):
612 """Scan all the Kconfig files and create a Config object."""
613 # Define environment variables referenced from Kconfig
614 os.environ['srctree'] = os.getcwd()
615 os.environ['UBOOTVERSION'] = 'dummy'
616 os.environ['KCONFIG_OBJDIR'] = ''
Tom Rini65e05dd2019-09-20 17:42:09 -0400617 self.conf = kconfiglib.Kconfig()
Simon Glasscb008832017-06-15 21:39:33 -0600618
619
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900620class KconfigParser:
621
622 """A parser of .config and include/autoconf.mk."""
623
624 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
625 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
626
Simon Glassb2e83c62021-12-18 14:54:31 -0700627 def __init__(self, configs, args, build_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900628 """Create a new parser.
629
Simon Glass91197aa2021-12-18 14:54:35 -0700630 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900631 configs: A list of CONFIGs to move.
Simon Glass91197aa2021-12-18 14:54:35 -0700632 args (Namespace): program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900633 build_dir: Build directory.
634 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900635 self.configs = configs
Simon Glassb2e83c62021-12-18 14:54:31 -0700636 self.args = args
Masahiro Yamada1f169922016-05-19 15:52:00 +0900637 self.dotconfig = os.path.join(build_dir, '.config')
638 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
Masahiro Yamada07913d12016-08-22 22:18:22 +0900639 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
640 'autoconf.mk')
Simon Glassf3b8e642017-06-01 19:39:01 -0600641 self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900642 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900643
Simon Glass6821a742017-07-10 14:47:47 -0600644 def get_arch(self):
645 """Parse .config file and return the architecture.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900646
647 Returns:
Simon Glass6821a742017-07-10 14:47:47 -0600648 Architecture name (e.g. 'arm').
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900649 """
650 arch = ''
651 cpu = ''
Simon Glass37f815c2021-12-18 14:54:34 -0700652 for line in read_file(self.dotconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900653 m = self.re_arch.match(line)
654 if m:
655 arch = m.group(1)
656 continue
657 m = self.re_cpu.match(line)
658 if m:
659 cpu = m.group(1)
660
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900661 if not arch:
662 return None
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900663
664 # fix-up for aarch64
665 if arch == 'arm' and cpu == 'armv8':
666 arch = 'aarch64'
667
Simon Glass6821a742017-07-10 14:47:47 -0600668 return arch
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900669
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900670 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900671 """Parse .config, defconfig, include/autoconf.mk for one config.
672
673 This function looks for the config options in the lines from
674 defconfig, .config, and include/autoconf.mk in order to decide
675 which action should be taken for this defconfig.
676
Simon Glass91197aa2021-12-18 14:54:35 -0700677 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900678 config: CONFIG name to parse.
Masahiro Yamadacc008292016-05-19 15:51:56 +0900679 dotconfig_lines: lines from the .config file.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900680 autoconf_lines: lines from the include/autoconf.mk file.
681
682 Returns:
683 A tupple of the action for this defconfig and the line
684 matched for the config.
685 """
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900686 not_set = '# %s is not set' % config
687
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900688 for line in autoconf_lines:
689 line = line.rstrip()
690 if line.startswith(config + '='):
Masahiro Yamadacc008292016-05-19 15:51:56 +0900691 new_val = line
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900692 break
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900693 else:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900694 new_val = not_set
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900695
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200696 new_val = try_expand(new_val)
697
Masahiro Yamada916224c2016-08-22 22:18:21 +0900698 for line in dotconfig_lines:
699 line = line.rstrip()
700 if line.startswith(config + '=') or line == not_set:
701 old_val = line
702 break
703 else:
704 if new_val == not_set:
705 return (ACTION_NO_ENTRY, config)
706 else:
707 return (ACTION_NO_ENTRY_WARN, config)
708
Masahiro Yamadacc008292016-05-19 15:51:56 +0900709 # If this CONFIG is neither bool nor trisate
710 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
711 # tools/scripts/define2mk.sed changes '1' to 'y'.
712 # This is a problem if the CONFIG is int type.
713 # Check the type in Kconfig and handle it correctly.
714 if new_val[-2:] == '=y':
715 new_val = new_val[:-1] + '1'
716
Masahiro Yamada50301592016-06-15 14:33:50 +0900717 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
718 new_val)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900719
Masahiro Yamada1d085562016-05-19 15:52:02 +0900720 def update_dotconfig(self):
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900721 """Parse files for the config options and update the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900722
Masahiro Yamadacc008292016-05-19 15:51:56 +0900723 This function parses the generated .config and include/autoconf.mk
724 searching the target options.
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900725 Move the config option(s) to the .config as needed.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900726
Simon Glass91197aa2021-12-18 14:54:35 -0700727 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900728 defconfig: defconfig name.
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900729
730 Returns:
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900731 Return a tuple of (updated flag, log string).
732 The "updated flag" is True if the .config was updated, False
733 otherwise. The "log string" shows what happend to the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900734 """
735
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900736 results = []
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900737 updated = False
Masahiro Yamada916224c2016-08-22 22:18:21 +0900738 suspicious = False
Masahiro Yamada07913d12016-08-22 22:18:22 +0900739 rm_files = [self.config_autoconf, self.autoconf]
740
Simon Glassb2e83c62021-12-18 14:54:31 -0700741 if self.args.spl:
Masahiro Yamada07913d12016-08-22 22:18:22 +0900742 if os.path.exists(self.spl_autoconf):
743 autoconf_path = self.spl_autoconf
744 rm_files.append(self.spl_autoconf)
745 else:
746 for f in rm_files:
747 os.remove(f)
748 return (updated, suspicious,
Simon Glassb2e83c62021-12-18 14:54:31 -0700749 color_text(self.args.color, COLOR_BROWN,
Masahiro Yamada07913d12016-08-22 22:18:22 +0900750 "SPL is not enabled. Skipped.") + '\n')
751 else:
752 autoconf_path = self.autoconf
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900753
Simon Glass37f815c2021-12-18 14:54:34 -0700754 dotconfig_lines = read_file(self.dotconfig)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900755
Simon Glass37f815c2021-12-18 14:54:34 -0700756 autoconf_lines = read_file(autoconf_path)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900757
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900758 for config in self.configs:
759 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger96464ba2015-05-19 13:21:17 -0500760 autoconf_lines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900761 results.append(result)
762
763 log = ''
764
765 for (action, value) in results:
766 if action == ACTION_MOVE:
767 actlog = "Move '%s'" % value
768 log_color = COLOR_LIGHT_GREEN
Masahiro Yamadacc008292016-05-19 15:51:56 +0900769 elif action == ACTION_NO_ENTRY:
Simon Glassdaa694d2021-12-18 14:54:30 -0700770 actlog = '%s is not defined in Kconfig. Do nothing.' % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900771 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada916224c2016-08-22 22:18:21 +0900772 elif action == ACTION_NO_ENTRY_WARN:
Simon Glassdaa694d2021-12-18 14:54:30 -0700773 actlog = '%s is not defined in Kconfig (suspicious). Do nothing.' % value
Masahiro Yamada916224c2016-08-22 22:18:21 +0900774 log_color = COLOR_YELLOW
775 suspicious = True
Masahiro Yamadacc008292016-05-19 15:51:56 +0900776 elif action == ACTION_NO_CHANGE:
777 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
778 % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900779 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada07913d12016-08-22 22:18:22 +0900780 elif action == ACTION_SPL_NOT_EXIST:
Simon Glassdaa694d2021-12-18 14:54:30 -0700781 actlog = 'SPL is not enabled for this defconfig. Skip.'
Masahiro Yamada07913d12016-08-22 22:18:22 +0900782 log_color = COLOR_PURPLE
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900783 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700784 sys.exit('Internal Error. This should not happen.')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900785
Simon Glassb2e83c62021-12-18 14:54:31 -0700786 log += color_text(self.args.color, log_color, actlog) + '\n'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900787
Simon Glass91197aa2021-12-18 14:54:35 -0700788 with open(self.dotconfig, 'a', encoding='utf-8') as out:
Masahiro Yamadae423d172016-05-19 15:51:49 +0900789 for (action, value) in results:
790 if action == ACTION_MOVE:
Simon Glass91197aa2021-12-18 14:54:35 -0700791 out.write(value + '\n')
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900792 updated = True
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900793
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900794 self.results = results
Masahiro Yamada07913d12016-08-22 22:18:22 +0900795 for f in rm_files:
796 os.remove(f)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900797
Masahiro Yamada916224c2016-08-22 22:18:21 +0900798 return (updated, suspicious, log)
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900799
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900800 def check_defconfig(self):
801 """Check the defconfig after savedefconfig
802
803 Returns:
804 Return additional log if moved CONFIGs were removed again by
805 'make savedefconfig'.
806 """
807
808 log = ''
809
Simon Glass37f815c2021-12-18 14:54:34 -0700810 defconfig_lines = read_file(self.defconfig)
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900811
812 for (action, value) in self.results:
813 if action != ACTION_MOVE:
814 continue
815 if not value + '\n' in defconfig_lines:
Simon Glassb2e83c62021-12-18 14:54:31 -0700816 log += color_text(self.args.color, COLOR_YELLOW,
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900817 "'%s' was removed by savedefconfig.\n" %
818 value)
819
820 return log
821
Simon Glassd73fcb12017-06-01 19:39:02 -0600822
823class DatabaseThread(threading.Thread):
824 """This thread processes results from Slot threads.
825
826 It collects the data in the master config directary. There is only one
827 result thread, and this helps to serialise the build output.
828 """
829 def __init__(self, config_db, db_queue):
830 """Set up a new result thread
831
832 Args:
833 builder: Builder which will be sent each result
834 """
835 threading.Thread.__init__(self)
836 self.config_db = config_db
837 self.db_queue= db_queue
838
839 def run(self):
840 """Called to start up the result thread.
841
842 We collect the next result job and pass it on to the build.
843 """
844 while True:
845 defconfig, configs = self.db_queue.get()
846 self.config_db[defconfig] = configs
847 self.db_queue.task_done()
848
849
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900850class Slot:
851
852 """A slot to store a subprocess.
853
854 Each instance of this class handles one subprocess.
855 This class is useful to control multiple threads
856 for faster processing.
857 """
858
Simon Glassb2e83c62021-12-18 14:54:31 -0700859 def __init__(self, toolchains, configs, args, progress, devnull,
Simon Glass6821a742017-07-10 14:47:47 -0600860 make_cmd, reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900861 """Create a new process slot.
862
Simon Glass91197aa2021-12-18 14:54:35 -0700863 Args:
Simon Glass6821a742017-07-10 14:47:47 -0600864 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900865 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -0700866 args: Program arguments
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900867 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900868 devnull: A file object of '/dev/null'.
869 make_cmd: command name of GNU Make.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500870 reference_src_dir: Determine the true starting config state from this
871 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -0600872 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900873 """
Simon Glass6821a742017-07-10 14:47:47 -0600874 self.toolchains = toolchains
Simon Glassb2e83c62021-12-18 14:54:31 -0700875 self.args = args
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900876 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900877 self.build_dir = tempfile.mkdtemp()
878 self.devnull = devnull
879 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500880 self.reference_src_dir = reference_src_dir
Simon Glassd73fcb12017-06-01 19:39:02 -0600881 self.db_queue = db_queue
Simon Glassb2e83c62021-12-18 14:54:31 -0700882 self.parser = KconfigParser(configs, args, self.build_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900883 self.state = STATE_IDLE
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900884 self.failed_boards = set()
885 self.suspicious_boards = set()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900886
887 def __del__(self):
888 """Delete the working directory
889
890 This function makes sure the temporary directory is cleaned away
891 even if Python suddenly dies due to error. It should be done in here
Joe Hershbergerf2dae752016-06-10 14:53:29 -0500892 because it is guaranteed the destructor is always invoked when the
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900893 instance of the class gets unreferenced.
894
895 If the subprocess is still running, wait until it finishes.
896 """
897 if self.state != STATE_IDLE:
898 while self.ps.poll() == None:
899 pass
900 shutil.rmtree(self.build_dir)
901
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900902 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900903 """Assign a new subprocess for defconfig and add it to the slot.
904
905 If the slot is vacant, create a new subprocess for processing the
906 given defconfig and add it to the slot. Just returns False if
907 the slot is occupied (i.e. the current subprocess is still running).
908
Simon Glass91197aa2021-12-18 14:54:35 -0700909 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900910 defconfig: defconfig name.
911
912 Returns:
913 Return True on success or False on failure
914 """
915 if self.state != STATE_IDLE:
916 return False
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900917
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900918 self.defconfig = defconfig
Masahiro Yamada1d085562016-05-19 15:52:02 +0900919 self.log = ''
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900920 self.current_src_dir = self.reference_src_dir
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900921 self.do_defconfig()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900922 return True
923
924 def poll(self):
925 """Check the status of the subprocess and handle it as needed.
926
927 Returns True if the slot is vacant (i.e. in idle state).
928 If the configuration is successfully finished, assign a new
929 subprocess to build include/autoconf.mk.
930 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900931 parse the .config and the include/autoconf.mk, moving
932 config options to the .config as needed.
933 If the .config was updated, run "make savedefconfig" to sync
934 it, update the original defconfig, and then set the slot back
935 to the idle state.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900936
937 Returns:
938 Return True if the subprocess is terminated, False otherwise
939 """
940 if self.state == STATE_IDLE:
941 return True
942
943 if self.ps.poll() == None:
944 return False
945
946 if self.ps.poll() != 0:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900947 self.handle_error()
948 elif self.state == STATE_DEFCONFIG:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900949 if self.reference_src_dir and not self.current_src_dir:
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500950 self.do_savedefconfig()
951 else:
952 self.do_autoconf()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900953 elif self.state == STATE_AUTOCONF:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900954 if self.current_src_dir:
955 self.current_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500956 self.do_defconfig()
Simon Glassb2e83c62021-12-18 14:54:31 -0700957 elif self.args.build_db:
Simon Glassd73fcb12017-06-01 19:39:02 -0600958 self.do_build_db()
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500959 else:
960 self.do_savedefconfig()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900961 elif self.state == STATE_SAVEDEFCONFIG:
962 self.update_defconfig()
963 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700964 sys.exit('Internal Error. This should not happen.')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900965
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900966 return True if self.state == STATE_IDLE else False
Joe Hershberger96464ba2015-05-19 13:21:17 -0500967
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900968 def handle_error(self):
969 """Handle error cases."""
Masahiro Yamada8513dc02016-05-19 15:52:08 +0900970
Simon Glassb2e83c62021-12-18 14:54:31 -0700971 self.log += color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glassdaa694d2021-12-18 14:54:30 -0700972 'Failed to process.\n')
Simon Glassb2e83c62021-12-18 14:54:31 -0700973 if self.args.verbose:
974 self.log += color_text(self.args.color, COLOR_LIGHT_CYAN,
Markus Klotzbuecher4f5c5e92020-02-12 20:46:45 +0100975 self.ps.stderr.read().decode())
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900976 self.finish(False)
Joe Hershberger96464ba2015-05-19 13:21:17 -0500977
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900978 def do_defconfig(self):
979 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900980
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900981 cmd = list(self.make_cmd)
982 cmd.append(self.defconfig)
983 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900984 stderr=subprocess.PIPE,
985 cwd=self.current_src_dir)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900986 self.state = STATE_DEFCONFIG
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900987
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900988 def do_autoconf(self):
Simon Glassf3b8e642017-06-01 19:39:01 -0600989 """Run 'make AUTO_CONF_PATH'."""
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900990
Simon Glass6821a742017-07-10 14:47:47 -0600991 arch = self.parser.get_arch()
992 try:
993 toolchain = self.toolchains.Select(arch)
994 except ValueError:
Simon Glassb2e83c62021-12-18 14:54:31 -0700995 self.log += color_text(self.args.color, COLOR_YELLOW,
Chris Packhamce3ba452017-08-27 20:00:51 +1200996 "Tool chain for '%s' is missing. Do nothing.\n" % arch)
Masahiro Yamada4efef992016-05-19 15:52:03 +0900997 self.finish(False)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900998 return
Simon Glass793dca32019-10-31 07:42:57 -0600999 env = toolchain.MakeEnvironment(False)
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +09001000
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001001 cmd = list(self.make_cmd)
Joe Hershberger7740f652015-05-19 13:21:18 -05001002 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Simon Glassf3b8e642017-06-01 19:39:01 -06001003 cmd.append(AUTO_CONF_PATH)
Simon Glass6821a742017-07-10 14:47:47 -06001004 self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001005 stderr=subprocess.PIPE,
1006 cwd=self.current_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001007 self.state = STATE_AUTOCONF
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001008
Simon Glassd73fcb12017-06-01 19:39:02 -06001009 def do_build_db(self):
1010 """Add the board to the database"""
1011 configs = {}
Simon Glass37f815c2021-12-18 14:54:34 -07001012 for line in read_file(os.path.join(self.build_dir, AUTO_CONF_PATH)):
1013 if line.startswith('CONFIG'):
1014 config, value = line.split('=', 1)
1015 configs[config] = value.rstrip()
Simon Glassd73fcb12017-06-01 19:39:02 -06001016 self.db_queue.put([self.defconfig, configs])
1017 self.finish(True)
1018
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001019 def do_savedefconfig(self):
1020 """Update the .config and run 'make savedefconfig'."""
1021
Masahiro Yamada916224c2016-08-22 22:18:21 +09001022 (updated, suspicious, log) = self.parser.update_dotconfig()
1023 if suspicious:
1024 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001025 self.log += log
1026
Simon Glassb2e83c62021-12-18 14:54:31 -07001027 if not self.args.force_sync and not updated:
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001028 self.finish(True)
1029 return
1030 if updated:
Simon Glassb2e83c62021-12-18 14:54:31 -07001031 self.log += color_text(self.args.color, COLOR_LIGHT_GREEN,
Simon Glassdaa694d2021-12-18 14:54:30 -07001032 'Syncing by savedefconfig...\n')
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001033 else:
Simon Glassdaa694d2021-12-18 14:54:30 -07001034 self.log += 'Syncing by savedefconfig (forced by option)...\n'
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001035
1036 cmd = list(self.make_cmd)
1037 cmd.append('savedefconfig')
1038 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
1039 stderr=subprocess.PIPE)
1040 self.state = STATE_SAVEDEFCONFIG
1041
1042 def update_defconfig(self):
1043 """Update the input defconfig and go back to the idle state."""
1044
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001045 log = self.parser.check_defconfig()
1046 if log:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001047 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001048 self.log += log
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001049 orig_defconfig = os.path.join('configs', self.defconfig)
1050 new_defconfig = os.path.join(self.build_dir, 'defconfig')
1051 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
1052
1053 if updated:
Simon Glassb2e83c62021-12-18 14:54:31 -07001054 self.log += color_text(self.args.color, COLOR_LIGHT_BLUE,
Simon Glassdaa694d2021-12-18 14:54:30 -07001055 'defconfig was updated.\n')
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001056
Simon Glassb2e83c62021-12-18 14:54:31 -07001057 if not self.args.dry_run and updated:
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001058 shutil.move(new_defconfig, orig_defconfig)
1059 self.finish(True)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001060
Masahiro Yamada4efef992016-05-19 15:52:03 +09001061 def finish(self, success):
1062 """Display log along with progress and go to the idle state.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001063
Simon Glass91197aa2021-12-18 14:54:35 -07001064 Args:
Masahiro Yamada4efef992016-05-19 15:52:03 +09001065 success: Should be True when the defconfig was processed
1066 successfully, or False when it fails.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001067 """
1068 # output at least 30 characters to hide the "* defconfigs out of *".
1069 log = self.defconfig.ljust(30) + '\n'
1070
1071 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
1072 # Some threads are running in parallel.
1073 # Print log atomically to not mix up logs from different threads.
Simon Glass793dca32019-10-31 07:42:57 -06001074 print(log, file=(sys.stdout if success else sys.stderr))
Masahiro Yamada4efef992016-05-19 15:52:03 +09001075
1076 if not success:
Simon Glassb2e83c62021-12-18 14:54:31 -07001077 if self.args.exit_on_error:
Simon Glassdaa694d2021-12-18 14:54:30 -07001078 sys.exit('Exit on error.')
Masahiro Yamada4efef992016-05-19 15:52:03 +09001079 # If --exit-on-error flag is not set, skip this board and continue.
1080 # Record the failed board.
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001081 self.failed_boards.add(self.defconfig)
Masahiro Yamada4efef992016-05-19 15:52:03 +09001082
Masahiro Yamada1d085562016-05-19 15:52:02 +09001083 self.progress.inc()
1084 self.progress.show()
Masahiro Yamada4efef992016-05-19 15:52:03 +09001085 self.state = STATE_IDLE
Masahiro Yamada1d085562016-05-19 15:52:02 +09001086
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001087 def get_failed_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001088 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001089 """
1090 return self.failed_boards
1091
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001092 def get_suspicious_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001093 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001094 """
Masahiro Yamada916224c2016-08-22 22:18:21 +09001095 return self.suspicious_boards - self.failed_boards
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001096
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001097class Slots:
1098
1099 """Controller of the array of subprocess slots."""
1100
Simon Glassb2e83c62021-12-18 14:54:31 -07001101 def __init__(self, toolchains, configs, args, progress,
Simon Glass6821a742017-07-10 14:47:47 -06001102 reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001103 """Create a new slots controller.
1104
Simon Glass91197aa2021-12-18 14:54:35 -07001105 Args:
Simon Glass6821a742017-07-10 14:47:47 -06001106 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001107 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -07001108 args: Program arguments
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001109 progress: A progress indicator.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001110 reference_src_dir: Determine the true starting config state from this
1111 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -06001112 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001113 """
Simon Glassb2e83c62021-12-18 14:54:31 -07001114 self.args = args
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001115 self.slots = []
Simon Glass478920d2021-12-18 14:54:32 -07001116 devnull = subprocess.DEVNULL
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001117 make_cmd = get_make_cmd()
Simon Glassb2e83c62021-12-18 14:54:31 -07001118 for i in range(args.jobs):
1119 self.slots.append(Slot(toolchains, configs, args, progress,
Simon Glass6821a742017-07-10 14:47:47 -06001120 devnull, make_cmd, reference_src_dir,
1121 db_queue))
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001122
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001123 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001124 """Add a new subprocess if a vacant slot is found.
1125
Simon Glass91197aa2021-12-18 14:54:35 -07001126 Args:
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001127 defconfig: defconfig name to be put into.
1128
1129 Returns:
1130 Return True on success or False on failure
1131 """
1132 for slot in self.slots:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001133 if slot.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001134 return True
1135 return False
1136
1137 def available(self):
1138 """Check if there is a vacant slot.
1139
1140 Returns:
1141 Return True if at lease one vacant slot is found, False otherwise.
1142 """
1143 for slot in self.slots:
1144 if slot.poll():
1145 return True
1146 return False
1147
1148 def empty(self):
1149 """Check if all slots are vacant.
1150
1151 Returns:
1152 Return True if all the slots are vacant, False otherwise.
1153 """
1154 ret = True
1155 for slot in self.slots:
1156 if not slot.poll():
1157 ret = False
1158 return ret
1159
1160 def show_failed_boards(self):
1161 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001162 boards = set()
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001163 output_file = 'moveconfig.failed'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001164
1165 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001166 boards |= slot.get_failed_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001167
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001168 if boards:
1169 boards = '\n'.join(boards) + '\n'
Simon Glassdaa694d2021-12-18 14:54:30 -07001170 msg = 'The following boards were not processed due to error:\n'
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001171 msg += boards
Simon Glassdaa694d2021-12-18 14:54:30 -07001172 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassb2e83c62021-12-18 14:54:31 -07001173 print(color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glass793dca32019-10-31 07:42:57 -06001174 msg), file=sys.stderr)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001175
Simon Glass2fd85bd2021-12-18 14:54:33 -07001176 write_file(output_file, boards)
Joe Hershberger2559cd82015-05-19 13:21:22 -05001177
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001178 def show_suspicious_boards(self):
1179 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001180 boards = set()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001181 output_file = 'moveconfig.suspicious'
1182
1183 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001184 boards |= slot.get_suspicious_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001185
1186 if boards:
1187 boards = '\n'.join(boards) + '\n'
Simon Glassdaa694d2021-12-18 14:54:30 -07001188 msg = 'The following boards might have been converted incorrectly.\n'
1189 msg += 'It is highly recommended to check them manually:\n'
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001190 msg += boards
Simon Glassdaa694d2021-12-18 14:54:30 -07001191 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassb2e83c62021-12-18 14:54:31 -07001192 print(color_text(self.args.color, COLOR_YELLOW,
Simon Glass793dca32019-10-31 07:42:57 -06001193 msg), file=sys.stderr)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001194
Simon Glass2fd85bd2021-12-18 14:54:33 -07001195 write_file(output_file, boards)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001196
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001197class ReferenceSource:
1198
1199 """Reference source against which original configs should be parsed."""
1200
1201 def __init__(self, commit):
1202 """Create a reference source directory based on a specified commit.
1203
Simon Glass91197aa2021-12-18 14:54:35 -07001204 Args:
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001205 commit: commit to git-clone
1206 """
1207 self.src_dir = tempfile.mkdtemp()
Simon Glassdaa694d2021-12-18 14:54:30 -07001208 print('Cloning git repo to a separate work directory...')
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001209 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1210 cwd=self.src_dir)
Simon Glass793dca32019-10-31 07:42:57 -06001211 print("Checkout '%s' to build the original autoconf.mk." % \
1212 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip())
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001213 subprocess.check_output(['git', 'checkout', commit],
1214 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001215
1216 def __del__(self):
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001217 """Delete the reference source directory
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001218
1219 This function makes sure the temporary directory is cleaned away
1220 even if Python suddenly dies due to error. It should be done in here
1221 because it is guaranteed the destructor is always invoked when the
1222 instance of the class gets unreferenced.
1223 """
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001224 shutil.rmtree(self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001225
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001226 def get_dir(self):
1227 """Return the absolute path to the reference source directory."""
1228
1229 return self.src_dir
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001230
Simon Glassb2e83c62021-12-18 14:54:31 -07001231def move_config(toolchains, configs, args, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001232 """Move config options to defconfig files.
1233
Simon Glass91197aa2021-12-18 14:54:35 -07001234 Args:
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001235 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -07001236 args: Program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001237 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001238 if len(configs) == 0:
Simon Glassb2e83c62021-12-18 14:54:31 -07001239 if args.force_sync:
Simon Glass793dca32019-10-31 07:42:57 -06001240 print('No CONFIG is specified. You are probably syncing defconfigs.', end=' ')
Simon Glassb2e83c62021-12-18 14:54:31 -07001241 elif args.build_db:
Simon Glass793dca32019-10-31 07:42:57 -06001242 print('Building %s database' % CONFIG_DATABASE)
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001243 else:
Simon Glass793dca32019-10-31 07:42:57 -06001244 print('Neither CONFIG nor --force-sync is specified. Nothing will happen.', end=' ')
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001245 else:
Simon Glass793dca32019-10-31 07:42:57 -06001246 print('Move ' + ', '.join(configs), end=' ')
Simon Glassb2e83c62021-12-18 14:54:31 -07001247 print('(jobs: %d)\n' % args.jobs)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001248
Simon Glassb2e83c62021-12-18 14:54:31 -07001249 if args.git_ref:
1250 reference_src = ReferenceSource(args.git_ref)
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001251 reference_src_dir = reference_src.get_dir()
1252 else:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001253 reference_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001254
Simon Glassb2e83c62021-12-18 14:54:31 -07001255 if args.defconfigs:
1256 defconfigs = get_matched_defconfigs(args.defconfigs)
Joe Hershberger91040e82015-05-19 13:21:19 -05001257 else:
Masahiro Yamada684c3062016-07-25 19:15:28 +09001258 defconfigs = get_all_defconfigs()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001259
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001260 progress = Progress(len(defconfigs))
Simon Glassb2e83c62021-12-18 14:54:31 -07001261 slots = Slots(toolchains, configs, args, progress, reference_src_dir,
Simon Glass91197aa2021-12-18 14:54:35 -07001262 db_queue)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001263
1264 # Main loop to process defconfig files:
1265 # Add a new subprocess into a vacant slot.
1266 # Sleep if there is no available slot.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001267 for defconfig in defconfigs:
1268 while not slots.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001269 while not slots.available():
1270 # No available slot: sleep for a while
1271 time.sleep(SLEEP_TIME)
1272
1273 # wait until all the subprocesses finish
1274 while not slots.empty():
1275 time.sleep(SLEEP_TIME)
1276
Simon Glass793dca32019-10-31 07:42:57 -06001277 print('')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001278 slots.show_failed_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001279 slots.show_suspicious_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001280
Simon Glasscb008832017-06-15 21:39:33 -06001281def find_kconfig_rules(kconf, config, imply_config):
1282 """Check whether a config has a 'select' or 'imply' keyword
1283
1284 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -04001285 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -06001286 config: Name of config to check (without CONFIG_ prefix)
1287 imply_config: Implying config (without CONFIG_ prefix) which may or
1288 may not have an 'imply' for 'config')
1289
1290 Returns:
1291 Symbol object for 'config' if found, else None
1292 """
Tom Rini65e05dd2019-09-20 17:42:09 -04001293 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -06001294 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001295 for sel, cond in (sym.selects + sym.implies):
Simon Glassa3627082021-12-18 08:09:42 -07001296 if sel.name == config:
Simon Glasscb008832017-06-15 21:39:33 -06001297 return sym
1298 return None
1299
1300def check_imply_rule(kconf, config, imply_config):
1301 """Check if we can add an 'imply' option
1302
1303 This finds imply_config in the Kconfig and looks to see if it is possible
1304 to add an 'imply' for 'config' to that part of the Kconfig.
1305
1306 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -04001307 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -06001308 config: Name of config to check (without CONFIG_ prefix)
1309 imply_config: Implying config (without CONFIG_ prefix) which may or
1310 may not have an 'imply' for 'config')
1311
1312 Returns:
1313 tuple:
1314 filename of Kconfig file containing imply_config, or None if none
1315 line number within the Kconfig file, or 0 if none
1316 message indicating the result
1317 """
Tom Rini65e05dd2019-09-20 17:42:09 -04001318 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -06001319 if not sym:
1320 return 'cannot find sym'
Simon Glassea40b202021-07-21 21:35:53 -06001321 nodes = sym.nodes
1322 if len(nodes) != 1:
1323 return '%d locations' % len(nodes)
Simon Glassa3627082021-12-18 08:09:42 -07001324 node = nodes[0]
1325 fname, linenum = node.filename, node.linenr
Simon Glasscb008832017-06-15 21:39:33 -06001326 cwd = os.getcwd()
1327 if cwd and fname.startswith(cwd):
1328 fname = fname[len(cwd) + 1:]
1329 file_line = ' at %s:%d' % (fname, linenum)
Simon Glass37f815c2021-12-18 14:54:34 -07001330 data = read_file(fname)
Simon Glasscb008832017-06-15 21:39:33 -06001331 if data[linenum - 1] != 'config %s' % imply_config:
1332 return None, 0, 'bad sym format %s%s' % (data[linenum], file_line)
1333 return fname, linenum, 'adding%s' % file_line
1334
1335def add_imply_rule(config, fname, linenum):
1336 """Add a new 'imply' option to a Kconfig
1337
1338 Args:
1339 config: config option to add an imply for (without CONFIG_ prefix)
1340 fname: Kconfig filename to update
1341 linenum: Line number to place the 'imply' before
1342
1343 Returns:
1344 Message indicating the result
1345 """
1346 file_line = ' at %s:%d' % (fname, linenum)
Simon Glass37f815c2021-12-18 14:54:34 -07001347 data = read_file(fname)
Simon Glasscb008832017-06-15 21:39:33 -06001348 linenum -= 1
1349
1350 for offset, line in enumerate(data[linenum:]):
1351 if line.strip().startswith('help') or not line:
1352 data.insert(linenum + offset, '\timply %s' % config)
Simon Glass2fd85bd2021-12-18 14:54:33 -07001353 write_file(fname, data)
Simon Glasscb008832017-06-15 21:39:33 -06001354 return 'added%s' % file_line
1355
1356 return 'could not insert%s'
1357
1358(IMPLY_MIN_2, IMPLY_TARGET, IMPLY_CMD, IMPLY_NON_ARCH_BOARD) = (
1359 1, 2, 4, 8)
Simon Glass9b2a2e82017-06-15 21:39:32 -06001360
1361IMPLY_FLAGS = {
1362 'min2': [IMPLY_MIN_2, 'Show options which imply >2 boards (normally >5)'],
1363 'target': [IMPLY_TARGET, 'Allow CONFIG_TARGET_... options to imply'],
1364 'cmd': [IMPLY_CMD, 'Allow CONFIG_CMD_... to imply'],
Simon Glasscb008832017-06-15 21:39:33 -06001365 'non-arch-board': [
1366 IMPLY_NON_ARCH_BOARD,
1367 'Allow Kconfig options outside arch/ and /board/ to imply'],
Simon Glass91197aa2021-12-18 14:54:35 -07001368}
Simon Glass9b2a2e82017-06-15 21:39:32 -06001369
Simon Glass9d603392021-12-18 08:09:43 -07001370
1371def read_database():
1372 """Read in the config database
1373
1374 Returns:
1375 tuple:
1376 set of all config options seen (each a str)
1377 set of all defconfigs seen (each a str)
1378 dict of configs for each defconfig:
1379 key: defconfig name, e.g. "MPC8548CDS_legacy_defconfig"
1380 value: dict:
1381 key: CONFIG option
1382 value: Value of option
1383 dict of defconfigs for each config:
1384 key: CONFIG option
1385 value: set of boards using that option
1386
1387 """
1388 configs = {}
1389
1390 # key is defconfig name, value is dict of (CONFIG_xxx, value)
1391 config_db = {}
1392
1393 # Set of all config options we have seen
1394 all_configs = set()
1395
1396 # Set of all defconfigs we have seen
1397 all_defconfigs = set()
1398
1399 defconfig_db = collections.defaultdict(set)
Simon Glass37f815c2021-12-18 14:54:34 -07001400 for line in read_file(CONFIG_DATABASE):
1401 line = line.rstrip()
1402 if not line: # Separator between defconfigs
1403 config_db[defconfig] = configs
1404 all_defconfigs.add(defconfig)
1405 configs = {}
1406 elif line[0] == ' ': # CONFIG line
1407 config, value = line.strip().split('=', 1)
1408 configs[config] = value
1409 defconfig_db[config].add(defconfig)
1410 all_configs.add(config)
1411 else: # New defconfig
1412 defconfig = line
Simon Glass9d603392021-12-18 08:09:43 -07001413
1414 return all_configs, all_defconfigs, config_db, defconfig_db
1415
1416
Simon Glasscb008832017-06-15 21:39:33 -06001417def do_imply_config(config_list, add_imply, imply_flags, skip_added,
1418 check_kconfig=True, find_superset=False):
Simon Glass99b66602017-06-01 19:39:03 -06001419 """Find CONFIG options which imply those in the list
1420
1421 Some CONFIG options can be implied by others and this can help to reduce
1422 the size of the defconfig files. For example, CONFIG_X86 implies
1423 CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
1424 all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
1425 each of the x86 defconfig files.
1426
1427 This function uses the moveconfig database to find such options. It
1428 displays a list of things that could possibly imply those in the list.
1429 The algorithm ignores any that start with CONFIG_TARGET since these
1430 typically refer to only a few defconfigs (often one). It also does not
1431 display a config with less than 5 defconfigs.
1432
1433 The algorithm works using sets. For each target config in config_list:
1434 - Get the set 'defconfigs' which use that target config
1435 - For each config (from a list of all configs):
1436 - Get the set 'imply_defconfig' of defconfigs which use that config
1437 -
1438 - If imply_defconfigs contains anything not in defconfigs then
1439 this config does not imply the target config
1440
1441 Params:
1442 config_list: List of CONFIG options to check (each a string)
Simon Glasscb008832017-06-15 21:39:33 -06001443 add_imply: Automatically add an 'imply' for each config.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001444 imply_flags: Flags which control which implying configs are allowed
1445 (IMPLY_...)
Simon Glasscb008832017-06-15 21:39:33 -06001446 skip_added: Don't show options which already have an imply added.
1447 check_kconfig: Check if implied symbols already have an 'imply' or
1448 'select' for the target config, and show this information if so.
Simon Glass99b66602017-06-01 19:39:03 -06001449 find_superset: True to look for configs which are a superset of those
1450 already found. So for example if CONFIG_EXYNOS5 implies an option,
1451 but CONFIG_EXYNOS covers a larger set of defconfigs and also
1452 implies that option, this will drop the former in favour of the
1453 latter. In practice this option has not proved very used.
1454
1455 Note the terminoloy:
1456 config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
1457 defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
1458 """
Simon Glasscb008832017-06-15 21:39:33 -06001459 kconf = KconfigScanner().conf if check_kconfig else None
1460 if add_imply and add_imply != 'all':
Simon Glassa3627082021-12-18 08:09:42 -07001461 add_imply = add_imply.split(',')
Simon Glasscb008832017-06-15 21:39:33 -06001462
Simon Glass9d603392021-12-18 08:09:43 -07001463 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
Simon Glass99b66602017-06-01 19:39:03 -06001464
Simon Glassa3627082021-12-18 08:09:42 -07001465 # Work through each target config option in turn, independently
Simon Glass99b66602017-06-01 19:39:03 -06001466 for config in config_list:
1467 defconfigs = defconfig_db.get(config)
1468 if not defconfigs:
Simon Glass793dca32019-10-31 07:42:57 -06001469 print('%s not found in any defconfig' % config)
Simon Glass99b66602017-06-01 19:39:03 -06001470 continue
1471
1472 # Get the set of defconfigs without this one (since a config cannot
1473 # imply itself)
1474 non_defconfigs = all_defconfigs - defconfigs
1475 num_defconfigs = len(defconfigs)
Simon Glass793dca32019-10-31 07:42:57 -06001476 print('%s found in %d/%d defconfigs' % (config, num_defconfigs,
1477 len(all_configs)))
Simon Glass99b66602017-06-01 19:39:03 -06001478
1479 # This will hold the results: key=config, value=defconfigs containing it
1480 imply_configs = {}
1481 rest_configs = all_configs - set([config])
1482
1483 # Look at every possible config, except the target one
1484 for imply_config in rest_configs:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001485 if 'ERRATUM' in imply_config:
Simon Glass99b66602017-06-01 19:39:03 -06001486 continue
Simon Glass91197aa2021-12-18 14:54:35 -07001487 if not imply_flags & IMPLY_CMD:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001488 if 'CONFIG_CMD' in imply_config:
1489 continue
Simon Glass91197aa2021-12-18 14:54:35 -07001490 if not imply_flags & IMPLY_TARGET:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001491 if 'CONFIG_TARGET' in imply_config:
1492 continue
Simon Glass99b66602017-06-01 19:39:03 -06001493
1494 # Find set of defconfigs that have this config
1495 imply_defconfig = defconfig_db[imply_config]
1496
1497 # Get the intersection of this with defconfigs containing the
1498 # target config
1499 common_defconfigs = imply_defconfig & defconfigs
1500
1501 # Get the set of defconfigs containing this config which DO NOT
1502 # also contain the taret config. If this set is non-empty it means
1503 # that this config affects other defconfigs as well as (possibly)
1504 # the ones affected by the target config. This means it implies
1505 # things we don't want to imply.
1506 not_common_defconfigs = imply_defconfig & non_defconfigs
1507 if not_common_defconfigs:
1508 continue
1509
1510 # If there are common defconfigs, imply_config may be useful
1511 if common_defconfigs:
1512 skip = False
1513 if find_superset:
Simon Glass793dca32019-10-31 07:42:57 -06001514 for prev in list(imply_configs.keys()):
Simon Glass99b66602017-06-01 19:39:03 -06001515 prev_count = len(imply_configs[prev])
1516 count = len(common_defconfigs)
1517 if (prev_count > count and
1518 (imply_configs[prev] & common_defconfigs ==
1519 common_defconfigs)):
1520 # skip imply_config because prev is a superset
1521 skip = True
1522 break
1523 elif count > prev_count:
1524 # delete prev because imply_config is a superset
1525 del imply_configs[prev]
1526 if not skip:
1527 imply_configs[imply_config] = common_defconfigs
1528
1529 # Now we have a dict imply_configs of configs which imply each config
1530 # The value of each dict item is the set of defconfigs containing that
1531 # config. Rank them so that we print the configs that imply the largest
1532 # number of defconfigs first.
Simon Glasscb008832017-06-15 21:39:33 -06001533 ranked_iconfigs = sorted(imply_configs,
Simon Glass99b66602017-06-01 19:39:03 -06001534 key=lambda k: len(imply_configs[k]), reverse=True)
Simon Glasscb008832017-06-15 21:39:33 -06001535 kconfig_info = ''
1536 cwd = os.getcwd()
1537 add_list = collections.defaultdict(list)
1538 for iconfig in ranked_iconfigs:
1539 num_common = len(imply_configs[iconfig])
Simon Glass99b66602017-06-01 19:39:03 -06001540
1541 # Don't bother if there are less than 5 defconfigs affected.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001542 if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
Simon Glass99b66602017-06-01 19:39:03 -06001543 continue
Simon Glasscb008832017-06-15 21:39:33 -06001544 missing = defconfigs - imply_configs[iconfig]
Simon Glass99b66602017-06-01 19:39:03 -06001545 missing_str = ', '.join(missing) if missing else 'all'
1546 missing_str = ''
Simon Glasscb008832017-06-15 21:39:33 -06001547 show = True
1548 if kconf:
1549 sym = find_kconfig_rules(kconf, config[CONFIG_LEN:],
1550 iconfig[CONFIG_LEN:])
1551 kconfig_info = ''
1552 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001553 nodes = sym.nodes
1554 if len(nodes) == 1:
1555 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001556 if cwd and fname.startswith(cwd):
1557 fname = fname[len(cwd) + 1:]
1558 kconfig_info = '%s:%d' % (fname, linenum)
1559 if skip_added:
1560 show = False
1561 else:
Tom Rini65e05dd2019-09-20 17:42:09 -04001562 sym = kconf.syms.get(iconfig[CONFIG_LEN:])
Simon Glasscb008832017-06-15 21:39:33 -06001563 fname = ''
1564 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001565 nodes = sym.nodes
1566 if len(nodes) == 1:
1567 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001568 if cwd and fname.startswith(cwd):
1569 fname = fname[len(cwd) + 1:]
1570 in_arch_board = not sym or (fname.startswith('arch') or
1571 fname.startswith('board'))
1572 if (not in_arch_board and
Simon Glass91197aa2021-12-18 14:54:35 -07001573 not imply_flags & IMPLY_NON_ARCH_BOARD):
Simon Glasscb008832017-06-15 21:39:33 -06001574 continue
1575
1576 if add_imply and (add_imply == 'all' or
1577 iconfig in add_imply):
1578 fname, linenum, kconfig_info = (check_imply_rule(kconf,
1579 config[CONFIG_LEN:], iconfig[CONFIG_LEN:]))
1580 if fname:
1581 add_list[fname].append(linenum)
1582
1583 if show and kconfig_info != 'skip':
Simon Glass793dca32019-10-31 07:42:57 -06001584 print('%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
1585 kconfig_info, missing_str))
Simon Glasscb008832017-06-15 21:39:33 -06001586
1587 # Having collected a list of things to add, now we add them. We process
1588 # each file from the largest line number to the smallest so that
1589 # earlier additions do not affect our line numbers. E.g. if we added an
1590 # imply at line 20 it would change the position of each line after
1591 # that.
Simon Glass793dca32019-10-31 07:42:57 -06001592 for fname, linenums in add_list.items():
Simon Glasscb008832017-06-15 21:39:33 -06001593 for linenum in sorted(linenums, reverse=True):
1594 add_imply_rule(config[CONFIG_LEN:], fname, linenum)
Simon Glass99b66602017-06-01 19:39:03 -06001595
1596
Simon Glass65d7fce2021-12-18 08:09:46 -07001597def do_find_config(config_list):
1598 """Find boards with a given combination of CONFIGs
1599
1600 Params:
1601 config_list: List of CONFIG options to check (each a string consisting
1602 of a config option, with or without a CONFIG_ prefix. If an option
1603 is preceded by a tilde (~) then it must be false, otherwise it must
1604 be true)
1605 """
1606 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
1607
1608 # Get the whitelist
Simon Glass37f815c2021-12-18 14:54:34 -07001609 adhoc_configs = set(read_file('scripts/config_whitelist.txt'))
Simon Glass65d7fce2021-12-18 08:09:46 -07001610
1611 # Start with all defconfigs
1612 out = all_defconfigs
1613
1614 # Work through each config in turn
1615 adhoc = []
1616 for item in config_list:
1617 # Get the real config name and whether we want this config or not
1618 cfg = item
1619 want = True
1620 if cfg[0] == '~':
1621 want = False
1622 cfg = cfg[1:]
1623
1624 if cfg in adhoc_configs:
1625 adhoc.append(cfg)
1626 continue
1627
1628 # Search everything that is still in the running. If it has a config
1629 # that we want, or doesn't have one that we don't, add it into the
1630 # running for the next stage
1631 in_list = out
1632 out = set()
1633 for defc in in_list:
1634 has_cfg = cfg in config_db[defc]
1635 if has_cfg == want:
1636 out.add(defc)
1637 if adhoc:
1638 print(f"Error: Not in Kconfig: %s" % ' '.join(adhoc))
1639 else:
1640 print(f'{len(out)} matches')
1641 print(' '.join(out))
1642
1643
1644def prefix_config(cfg):
1645 """Prefix a config with CONFIG_ if needed
1646
1647 This handles ~ operator, which indicates that the CONFIG should be disabled
1648
1649 >>> prefix_config('FRED')
1650 'CONFIG_FRED'
1651 >>> prefix_config('CONFIG_FRED')
1652 'CONFIG_FRED'
1653 >>> prefix_config('~FRED')
1654 '~CONFIG_FRED'
1655 >>> prefix_config('~CONFIG_FRED')
1656 '~CONFIG_FRED'
1657 >>> prefix_config('A123')
1658 'CONFIG_A123'
1659 """
1660 op = ''
1661 if cfg[0] == '~':
1662 op = cfg[0]
1663 cfg = cfg[1:]
1664 if not cfg.startswith('CONFIG_'):
1665 cfg = 'CONFIG_' + cfg
1666 return op + cfg
1667
1668
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001669def main():
1670 try:
1671 cpu_count = multiprocessing.cpu_count()
1672 except NotImplementedError:
1673 cpu_count = 1
1674
Simon Glassb2e83c62021-12-18 14:54:31 -07001675 epilog = '''Move config options from headers to defconfig files. See
1676doc/develop/moveconfig.rst for documentation.'''
1677
1678 parser = ArgumentParser(epilog=epilog)
1679 # Add arguments here
1680 parser.add_argument('-a', '--add-imply', type=str, default='',
Simon Glasscb008832017-06-15 21:39:33 -06001681 help='comma-separated list of CONFIG options to add '
1682 "an 'imply' statement to for the CONFIG in -i")
Simon Glassb2e83c62021-12-18 14:54:31 -07001683 parser.add_argument('-A', '--skip-added', action='store_true', default=False,
Simon Glasscb008832017-06-15 21:39:33 -06001684 help="don't show options which are already marked as "
1685 'implying others')
Simon Glassb2e83c62021-12-18 14:54:31 -07001686 parser.add_argument('-b', '--build-db', action='store_true', default=False,
Simon Glassd73fcb12017-06-01 19:39:02 -06001687 help='build a CONFIG database')
Simon Glassb2e83c62021-12-18 14:54:31 -07001688 parser.add_argument('-c', '--color', action='store_true', default=False,
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001689 help='display the log in color')
Simon Glassb2e83c62021-12-18 14:54:31 -07001690 parser.add_argument('-C', '--commit', action='store_true', default=False,
Simon Glass9ede2122016-09-12 23:18:21 -06001691 help='Create a git commit for the operation')
Simon Glassb2e83c62021-12-18 14:54:31 -07001692 parser.add_argument('-d', '--defconfigs', type=str,
Simon Glassee4e61b2017-06-01 19:38:59 -06001693 help='a file containing a list of defconfigs to move, '
1694 "one per line (for example 'snow_defconfig') "
1695 "or '-' to read from stdin")
Simon Glassb2e83c62021-12-18 14:54:31 -07001696 parser.add_argument('-e', '--exit-on-error', action='store_true',
Simon Glasse1ae5632021-12-18 08:09:44 -07001697 default=False,
1698 help='exit immediately on any error')
Simon Glassb2e83c62021-12-18 14:54:31 -07001699 parser.add_argument('-f', '--find', action='store_true', default=False,
Simon Glass65d7fce2021-12-18 08:09:46 -07001700 help='Find boards with a given config combination')
Simon Glassb2e83c62021-12-18 14:54:31 -07001701 parser.add_argument('-H', '--headers-only', dest='cleanup_headers_only',
Simon Glasse1ae5632021-12-18 08:09:44 -07001702 action='store_true', default=False,
1703 help='only cleanup the headers')
Simon Glassb2e83c62021-12-18 14:54:31 -07001704 parser.add_argument('-i', '--imply', action='store_true', default=False,
Simon Glass99b66602017-06-01 19:39:03 -06001705 help='find options which imply others')
Simon Glassb2e83c62021-12-18 14:54:31 -07001706 parser.add_argument('-I', '--imply-flags', type=str, default='',
Simon Glass9b2a2e82017-06-15 21:39:32 -06001707 help="control the -i option ('help' for help")
Simon Glassb2e83c62021-12-18 14:54:31 -07001708 parser.add_argument('-j', '--jobs', type=int, default=cpu_count,
Simon Glasse1ae5632021-12-18 08:09:44 -07001709 help='the number of jobs to run simultaneously')
Simon Glassb2e83c62021-12-18 14:54:31 -07001710 parser.add_argument('-n', '--dry-run', action='store_true', default=False,
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001711 help='perform a trial run (show log with no changes)')
Simon Glassb2e83c62021-12-18 14:54:31 -07001712 parser.add_argument('-r', '--git-ref', type=str,
Simon Glasse1ae5632021-12-18 08:09:44 -07001713 help='the git ref to clone for building the autoconf.mk')
Simon Glassb2e83c62021-12-18 14:54:31 -07001714 parser.add_argument('-s', '--force-sync', action='store_true', default=False,
Masahiro Yamada8513dc02016-05-19 15:52:08 +09001715 help='force sync by savedefconfig')
Simon Glassb2e83c62021-12-18 14:54:31 -07001716 parser.add_argument('-S', '--spl', action='store_true', default=False,
Masahiro Yamada07913d12016-08-22 22:18:22 +09001717 help='parse config options defined for SPL build')
Simon Glassb2e83c62021-12-18 14:54:31 -07001718 parser.add_argument('-t', '--test', action='store_true', default=False,
Simon Glasse1ae5632021-12-18 08:09:44 -07001719 help='run unit tests')
Simon Glassb2e83c62021-12-18 14:54:31 -07001720 parser.add_argument('-y', '--yes', action='store_true', default=False,
Simon Glass6b403df2016-09-12 23:18:20 -06001721 help="respond 'yes' to any prompts")
Simon Glassb2e83c62021-12-18 14:54:31 -07001722 parser.add_argument('-v', '--verbose', action='store_true', default=False,
Joe Hershberger95bf9c72015-05-19 13:21:24 -05001723 help='show any build errors as boards are built')
Simon Glassb2e83c62021-12-18 14:54:31 -07001724 parser.add_argument('configs', nargs='*')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001725
Simon Glassb2e83c62021-12-18 14:54:31 -07001726 args = parser.parse_args()
1727 configs = args.configs
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001728
Simon Glassb2e83c62021-12-18 14:54:31 -07001729 if args.test:
Simon Glass84067a52021-12-18 08:09:45 -07001730 sys.argv = [sys.argv[0]]
1731 fail, count = doctest.testmod()
1732 if fail:
1733 return 1
1734 unittest.main()
1735
Simon Glassb2e83c62021-12-18 14:54:31 -07001736 if not any((len(configs), args.force_sync, args.build_db, args.imply,
1737 args.find)):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001738 parser.print_usage()
1739 sys.exit(1)
1740
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001741 # prefix the option name with CONFIG_ if missing
Simon Glass65d7fce2021-12-18 08:09:46 -07001742 configs = [prefix_config(cfg) for cfg in configs]
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001743
Joe Hershberger2144f882015-05-19 13:21:20 -05001744 check_top_directory()
1745
Simon Glassb2e83c62021-12-18 14:54:31 -07001746 if args.imply:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001747 imply_flags = 0
Simon Glassb2e83c62021-12-18 14:54:31 -07001748 if args.imply_flags == 'all':
Simon Glassdee36c72017-07-10 14:47:46 -06001749 imply_flags = -1
1750
Simon Glassb2e83c62021-12-18 14:54:31 -07001751 elif args.imply_flags:
1752 for flag in args.imply_flags.split(','):
Simon Glassdee36c72017-07-10 14:47:46 -06001753 bad = flag not in IMPLY_FLAGS
1754 if bad:
Simon Glass793dca32019-10-31 07:42:57 -06001755 print("Invalid flag '%s'" % flag)
Simon Glassdee36c72017-07-10 14:47:46 -06001756 if flag == 'help' or bad:
Simon Glass793dca32019-10-31 07:42:57 -06001757 print("Imply flags: (separate with ',')")
1758 for name, info in IMPLY_FLAGS.items():
1759 print(' %-15s: %s' % (name, info[1]))
Simon Glassdee36c72017-07-10 14:47:46 -06001760 parser.print_usage()
1761 sys.exit(1)
1762 imply_flags |= IMPLY_FLAGS[flag][0]
Simon Glass9b2a2e82017-06-15 21:39:32 -06001763
Simon Glassb2e83c62021-12-18 14:54:31 -07001764 do_imply_config(configs, args.add_imply, imply_flags, args.skip_added)
Simon Glass99b66602017-06-01 19:39:03 -06001765 return
1766
Simon Glassb2e83c62021-12-18 14:54:31 -07001767 if args.find:
Simon Glass65d7fce2021-12-18 08:09:46 -07001768 do_find_config(configs)
1769 return
1770
Simon Glassd73fcb12017-06-01 19:39:02 -06001771 config_db = {}
Simon Glass793dca32019-10-31 07:42:57 -06001772 db_queue = queue.Queue()
Simon Glassd73fcb12017-06-01 19:39:02 -06001773 t = DatabaseThread(config_db, db_queue)
1774 t.setDaemon(True)
1775 t.start()
1776
Simon Glassb2e83c62021-12-18 14:54:31 -07001777 if not args.cleanup_headers_only:
Masahiro Yamadaf7536f72016-07-25 19:15:23 +09001778 check_clean_directory()
Simon Glass793dca32019-10-31 07:42:57 -06001779 bsettings.Setup('')
Simon Glass6821a742017-07-10 14:47:47 -06001780 toolchains = toolchain.Toolchains()
1781 toolchains.GetSettings()
1782 toolchains.Scan(verbose=False)
Simon Glassb2e83c62021-12-18 14:54:31 -07001783 move_config(toolchains, configs, args, db_queue)
Simon Glassd73fcb12017-06-01 19:39:02 -06001784 db_queue.join()
Joe Hershberger2144f882015-05-19 13:21:20 -05001785
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001786 if configs:
Simon Glassb2e83c62021-12-18 14:54:31 -07001787 cleanup_headers(configs, args)
1788 cleanup_extra_options(configs, args)
1789 cleanup_whitelist(configs, args)
1790 cleanup_readme(configs, args)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001791
Simon Glassb2e83c62021-12-18 14:54:31 -07001792 if args.commit:
Simon Glass9ede2122016-09-12 23:18:21 -06001793 subprocess.call(['git', 'add', '-u'])
1794 if configs:
1795 msg = 'Convert %s %sto Kconfig' % (configs[0],
1796 'et al ' if len(configs) > 1 else '')
1797 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1798 '\n '.join(configs))
1799 else:
1800 msg = 'configs: Resync with savedefconfig'
1801 msg += '\n\nRsync all defconfig files using moveconfig.py'
1802 subprocess.call(['git', 'commit', '-s', '-m', msg])
1803
Simon Glassb2e83c62021-12-18 14:54:31 -07001804 if args.build_db:
Simon Glass91197aa2021-12-18 14:54:35 -07001805 with open(CONFIG_DATABASE, 'w', encoding='utf-8') as fd:
Simon Glass793dca32019-10-31 07:42:57 -06001806 for defconfig, configs in config_db.items():
Simon Glassc79d18c2017-08-13 16:02:54 -06001807 fd.write('%s\n' % defconfig)
Simon Glassd73fcb12017-06-01 19:39:02 -06001808 for config in sorted(configs.keys()):
Simon Glassc79d18c2017-08-13 16:02:54 -06001809 fd.write(' %s=%s\n' % (config, configs[config]))
1810 fd.write('\n')
Simon Glassd73fcb12017-06-01 19:39:02 -06001811
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001812if __name__ == '__main__':
Simon Glass65d7fce2021-12-18 08:09:46 -07001813 sys.exit(main())