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