blob: 35fe6710d70a0cb3ef0e295aa4d397462e5c8928 [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
Masahiro Yamadab6160812015-05-20 11:36:07 +090094### helper functions ###
Masahiro Yamadab6160812015-05-20 11:36:07 +090095def check_top_directory():
96 """Exit if we are not at the top of source directory."""
Simon Glassb3464eb2021-12-18 14:54:35 -070097 for fname in 'README', 'Licenses':
98 if not os.path.exists(fname):
Masahiro Yamadab6160812015-05-20 11:36:07 +090099 sys.exit('Please run at the top of source directory.')
100
Masahiro Yamada990e6772016-05-19 15:51:54 +0900101def check_clean_directory():
102 """Exit if the source tree is not clean."""
Simon Glassb3464eb2021-12-18 14:54:35 -0700103 for fname in '.config', 'include/config':
104 if os.path.exists(fname):
Masahiro Yamada990e6772016-05-19 15:51:54 +0900105 sys.exit("source tree is not clean, please run 'make mrproper'")
106
Masahiro Yamadab6160812015-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 Glassb3464eb2021-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 Yamadab6160812015-05-20 11:36:07 +0900118 return ret[0].rstrip()
119
Simon Glass18774bc2017-06-01 19:38:58 -0600120def get_matched_defconfig(line):
121 """Get the defconfig files that match a pattern
122
123 Args:
Simon Glassb3464eb2021-12-18 14:54:35 -0700124 line (str): Path or filename to match, e.g. 'configs/snow_defconfig' or
Simon Glass18774bc2017-06-01 19:38:58 -0600125 'k2*_defconfig'. If no directory is provided, 'configs/' is
126 prepended
127
128 Returns:
Simon Glassb3464eb2021-12-18 14:54:35 -0700129 list of str: a list of matching defconfig files
Simon Glass18774bc2017-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 Yamada3984d6e2016-10-19 14:39:54 +0900138def get_matched_defconfigs(defconfigs_file):
Simon Glass8f3cf312017-06-01 19:38:59 -0600139 """Get all the defconfig files that match the patterns in a file.
140
141 Args:
Simon Glassb3464eb2021-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 Glass8f3cf312017-06-01 19:38:59 -0600144
145 Returns:
Simon Glassb3464eb2021-12-18 14:54:35 -0700146 list of str: A list of paths to defconfig files, with no duplicates
Simon Glass8f3cf312017-06-01 19:38:59 -0600147 """
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900148 defconfigs = []
Simon Glassb3464eb2021-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 Yamada3984d6e2016-10-19 14:39:54 +0900165
Simon Glassb3464eb2021-12-18 14:54:35 -0700166 defconfigs += matched
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900167
168 # use set() to drop multiple matching
Simon Glassb3464eb2021-12-18 14:54:35 -0700169 return [defconfig[len('configs') + 1:] for defconfig in set(defconfigs)]
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900170
Masahiro Yamada58175e32016-07-25 19:15:28 +0900171def get_all_defconfigs():
Simon Glassb3464eb2021-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 Yamada58175e32016-07-25 19:15:28 +0900177 defconfigs = []
Simon Glassb3464eb2021-12-18 14:54:35 -0700178 for (dirpath, _, filenames) in os.walk('configs'):
Masahiro Yamada58175e32016-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 Yamadab6160812015-05-20 11:36:07 +0900185def color_text(color_enabled, color, string):
186 """Return colored string."""
187 if color_enabled:
Masahiro Yamada465b7c02016-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 Glassb3464eb2021-12-18 14:54:35 -0700192 return string
Masahiro Yamadab6160812015-05-20 11:36:07 +0900193
Simon Glassb3464eb2021-12-18 14:54:35 -0700194def show_diff(alines, blines, file_path, color_enabled):
Masahiro Yamada573b3902016-07-25 19:15:25 +0900195 """Show unidified diff.
196
Simon Glassb3464eb2021-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 Yamada573b3902016-07-25 19:15:25 +0900202 """
Simon Glassb3464eb2021-12-18 14:54:35 -0700203 diff = difflib.unified_diff(alines, blines,
Masahiro Yamada573b3902016-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 Yamadaa1a4b092016-07-25 19:15:26 +0900208 if line[0] == '-' and line[1] != '-':
Simon Glass1f701862019-10-31 07:42:57 -0600209 print(color_text(color_enabled, COLOR_RED, line), end=' ')
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900210 elif line[0] == '+' and line[1] != '+':
Simon Glass1f701862019-10-31 07:42:57 -0600211 print(color_text(color_enabled, COLOR_GREEN, line), end=' ')
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900212 else:
Simon Glass1f701862019-10-31 07:42:57 -0600213 print(line, end=' ')
Masahiro Yamada573b3902016-07-25 19:15:25 +0900214
Simon Glassb3464eb2021-12-18 14:54:35 -0700215def extend_matched_lines(lines, matched, pre_patterns, post_patterns,
216 extend_pre, extend_post):
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900217 """Extend matched lines if desired patterns are found before/after already
218 matched lines.
219
Simon Glassb3464eb2021-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 Yamadaea8f5342016-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 Glassb3464eb2021-12-18 14:54:35 -0700246 for pat in pre_patterns:
247 if pat.search(lines[i - 1]):
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900248 break
249 else:
250 # not matched
251 continue
252
Simon Glassb3464eb2021-12-18 14:54:35 -0700253 for pat in post_patterns:
254 if pat.search(lines[j]):
Masahiro Yamadaea8f5342016-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 Glassd9c1da22021-12-18 14:54:31 -0700268def confirm(args, prompt):
Simon Glassb3464eb2021-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 Glassd9c1da22021-12-18 14:54:31 -0700277 if not args.yes:
Chris Packham85e15c52017-05-02 21:30:46 +1200278 while True:
Simon Glassb3464eb2021-12-18 14:54:35 -0700279 choice = input(f'{prompt} [y/n]: ')
Chris Packham85e15c52017-05-02 21:30:46 +1200280 choice = choice.lower()
Simon Glass1f701862019-10-31 07:42:57 -0600281 print(choice)
Simon Glassb3464eb2021-12-18 14:54:35 -0700282 if choice in ('y', 'n'):
Chris Packham85e15c52017-05-02 21:30:46 +1200283 break
284
285 if choice == 'n':
286 return False
287
288 return True
289
Simon Glassb09ae452021-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 Glassaba238f2021-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 Glassd9c1da22021-12-18 14:54:31 -0700333def cleanup_empty_blocks(header_path, args):
Chris Packham73f6c7c2019-01-30 20:23:16 +1300334 """Clean up empty conditional blocks
335
Simon Glassb3464eb2021-12-18 14:54:35 -0700336 Args:
337 header_path (str): path to the cleaned file.
338 args (Namespace): program arguments
Chris Packham73f6c7c2019-01-30 20:23:16 +1300339 """
340 pattern = re.compile(r'^\s*#\s*if.*$\n^\s*#\s*endif.*$\n*', flags=re.M)
Simon Glassaba238f2021-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 Packham73f6c7c2019-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 Glassd9c1da22021-12-18 14:54:31 -0700348 args.color)
Chris Packham73f6c7c2019-01-30 20:23:16 +1300349
Simon Glassd9c1da22021-12-18 14:54:31 -0700350 if args.dry_run:
Chris Packham73f6c7c2019-01-30 20:23:16 +1300351 return
352
Simon Glassaba238f2021-12-18 14:54:34 -0700353 if new_data != data:
354 write_file(header_path, new_data)
Chris Packham73f6c7c2019-01-30 20:23:16 +1300355
Simon Glassd9c1da22021-12-18 14:54:31 -0700356def cleanup_one_header(header_path, patterns, args):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900357 """Clean regex-matched lines away from a file.
358
Simon Glassb3464eb2021-12-18 14:54:35 -0700359 Args:
Masahiro Yamadab6160812015-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 Glassb3464eb2021-12-18 14:54:35 -0700363 args (Namespace): program arguments
Masahiro Yamadab6160812015-05-20 11:36:07 +0900364 """
Simon Glassaba238f2021-12-18 14:54:34 -0700365 lines = read_file(header_path, skip_unicode=True)
366 if lines is None:
367 return
Masahiro Yamadab6160812015-05-20 11:36:07 +0900368
369 matched = []
370 for i, line in enumerate(lines):
Masahiro Yamada6d798ba2016-07-25 19:15:27 +0900371 if i - 1 in matched and lines[i - 1][-2:] == '\\\n':
372 matched.append(i)
373 continue
Masahiro Yamadab6160812015-05-20 11:36:07 +0900374 for pattern in patterns:
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900375 if pattern.search(line):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900376 matched.append(i)
377 break
378
Masahiro Yamadaea8f5342016-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 Yamada573b3902016-07-25 19:15:25 +0900403 tolines = copy.copy(lines)
404
405 for i in reversed(matched):
406 tolines.pop(i)
407
Simon Glassd9c1da22021-12-18 14:54:31 -0700408 show_diff(lines, tolines, header_path, args.color)
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900409
Simon Glassd9c1da22021-12-18 14:54:31 -0700410 if args.dry_run:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900411 return
412
Simon Glassb09ae452021-12-18 14:54:33 -0700413 write_file(header_path, tolines)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900414
Simon Glassd9c1da22021-12-18 14:54:31 -0700415def cleanup_headers(configs, args):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900416 """Delete config defines from board headers.
417
Simon Glassb3464eb2021-12-18 14:54:35 -0700418 Args:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900419 configs: A list of CONFIGs to remove.
Simon Glassb3464eb2021-12-18 14:54:35 -0700420 args (Namespace): program arguments
Masahiro Yamadab6160812015-05-20 11:36:07 +0900421 """
Simon Glassd9c1da22021-12-18 14:54:31 -0700422 if not confirm(args, 'Clean up headers?'):
Chris Packham85e15c52017-05-02 21:30:46 +1200423 return
Masahiro Yamadab6160812015-05-20 11:36:07 +0900424
425 patterns = []
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900426 for config in configs:
Masahiro Yamadab6160812015-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 Hershbergerb78ad422015-05-19 13:21:21 -0500430 for dir in 'include', 'arch', 'board':
431 for (dirpath, dirnames, filenames) in os.walk(dir):
Masahiro Yamada28a6d352016-07-25 19:15:22 +0900432 if dirpath == os.path.join('include', 'generated'):
433 continue
Joe Hershbergerb78ad422015-05-19 13:21:21 -0500434 for filename in filenames:
Simon Glassce44bf52020-08-11 11:23:34 -0600435 if not filename.endswith(('~', '.dts', '.dtsi', '.bin',
Trevor Woerneraaad0c22021-03-15 12:01:33 -0400436 '.elf','.aml','.dat')):
Chris Packham73f6c7c2019-01-30 20:23:16 +1300437 header_path = os.path.join(dirpath, filename)
Tom Rinie90c0072019-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 Glassd9c1da22021-12-18 14:54:31 -0700441 cleanup_one_header(header_path, patterns, args)
442 cleanup_empty_blocks(header_path, args)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900443
Simon Glassd9c1da22021-12-18 14:54:31 -0700444def cleanup_one_extra_option(defconfig_path, configs, args):
Masahiro Yamadadce28de2016-07-25 19:15:29 +0900445 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file.
446
Simon Glassb3464eb2021-12-18 14:54:35 -0700447 Args:
Masahiro Yamadadce28de2016-07-25 19:15:29 +0900448 defconfig_path: path to the cleaned defconfig file.
449 configs: A list of CONFIGs to remove.
Simon Glassb3464eb2021-12-18 14:54:35 -0700450 args (Namespace): program arguments
Masahiro Yamadadce28de2016-07-25 19:15:29 +0900451 """
452
453 start = 'CONFIG_SYS_EXTRA_OPTIONS="'
454 end = '"\n'
455
Simon Glassaba238f2021-12-18 14:54:34 -0700456 lines = read_file(defconfig_path)
Masahiro Yamadadce28de2016-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 Glassd9c1da22021-12-18 14:54:31 -0700483 show_diff(lines, tolines, defconfig_path, args.color)
Masahiro Yamadadce28de2016-07-25 19:15:29 +0900484
Simon Glassd9c1da22021-12-18 14:54:31 -0700485 if args.dry_run:
Masahiro Yamadadce28de2016-07-25 19:15:29 +0900486 return
487
Simon Glassb09ae452021-12-18 14:54:33 -0700488 write_file(defconfig_path, tolines)
Masahiro Yamadadce28de2016-07-25 19:15:29 +0900489
Simon Glassd9c1da22021-12-18 14:54:31 -0700490def cleanup_extra_options(configs, args):
Masahiro Yamadadce28de2016-07-25 19:15:29 +0900491 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files.
492
Simon Glassb3464eb2021-12-18 14:54:35 -0700493 Args:
Masahiro Yamadadce28de2016-07-25 19:15:29 +0900494 configs: A list of CONFIGs to remove.
Simon Glassb3464eb2021-12-18 14:54:35 -0700495 args (Namespace): program arguments
Masahiro Yamadadce28de2016-07-25 19:15:29 +0900496 """
Simon Glassd9c1da22021-12-18 14:54:31 -0700497 if not confirm(args, 'Clean up CONFIG_SYS_EXTRA_OPTIONS?'):
Chris Packham85e15c52017-05-02 21:30:46 +1200498 return
Masahiro Yamadadce28de2016-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 Glassd9c1da22021-12-18 14:54:31 -0700506 args)
Masahiro Yamadadce28de2016-07-25 19:15:29 +0900507
Simon Glassd9c1da22021-12-18 14:54:31 -0700508def cleanup_whitelist(configs, args):
Chris Packham9d5274f2017-05-02 21:30:47 +1200509 """Delete config whitelist entries
510
Simon Glassb3464eb2021-12-18 14:54:35 -0700511 Args:
Chris Packham9d5274f2017-05-02 21:30:47 +1200512 configs: A list of CONFIGs to remove.
Simon Glassb3464eb2021-12-18 14:54:35 -0700513 args (Namespace): program arguments
Chris Packham9d5274f2017-05-02 21:30:47 +1200514 """
Simon Glassd9c1da22021-12-18 14:54:31 -0700515 if not confirm(args, 'Clean up whitelist entries?'):
Chris Packham9d5274f2017-05-02 21:30:47 +1200516 return
517
Simon Glassaba238f2021-12-18 14:54:34 -0700518 lines = read_file(os.path.join('scripts', 'config_whitelist.txt'))
Chris Packham9d5274f2017-05-02 21:30:47 +1200519
520 lines = [x for x in lines if x.strip() not in configs]
521
Simon Glassb09ae452021-12-18 14:54:33 -0700522 write_file(os.path.join('scripts', 'config_whitelist.txt'), lines)
Chris Packham9d5274f2017-05-02 21:30:47 +1200523
Chris Packham0e6deff2017-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 Glassd9c1da22021-12-18 14:54:31 -0700530def cleanup_readme(configs, args):
Chris Packham0e6deff2017-05-02 21:30:48 +1200531 """Delete config description in README
532
Simon Glassb3464eb2021-12-18 14:54:35 -0700533 Args:
Chris Packham0e6deff2017-05-02 21:30:48 +1200534 configs: A list of CONFIGs to remove.
Simon Glassb3464eb2021-12-18 14:54:35 -0700535 args (Namespace): program arguments
Chris Packham0e6deff2017-05-02 21:30:48 +1200536 """
Simon Glassd9c1da22021-12-18 14:54:31 -0700537 if not confirm(args, 'Clean up README?'):
Chris Packham0e6deff2017-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 Glassaba238f2021-12-18 14:54:34 -0700544 lines = read_file('README')
Chris Packham0e6deff2017-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 Glassb09ae452021-12-18 14:54:33 -0700560 write_file('README', newlines)
Chris Packham0e6deff2017-05-02 21:30:48 +1200561
Markus Klotzbuecher3d5d4182019-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 Klotzbuecherbac0bb52020-02-12 20:46:44 +0100570 aeval = asteval.Interpreter( usersyms=SIZES, minimal=True )
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200571 cfg, val = re.split("=", line)
572 val= val.strip('\"')
Simon Glassdc634d92021-12-18 14:54:30 -0700573 if re.search(r'[*+-/]|<<|SZ_+|\(([^\)]+)\)', val):
Markus Klotzbuecherbac0bb52020-02-12 20:46:44 +0100574 newval = hex(aeval(val))
Simon Glassdc634d92021-12-18 14:54:30 -0700575 print('\tExpanded expression %s to %s' % (val, newval))
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200576 return cfg+'='+newval
577 except:
Simon Glassdc634d92021-12-18 14:54:30 -0700578 print('\tFailed to expand expression in %s' % line)
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200579
580 return line
581
Chris Packham9d5274f2017-05-02 21:30:47 +1200582
Masahiro Yamadab6160812015-05-20 11:36:07 +0900583### classes ###
Masahiro Yamadacefaa582016-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 Glassb3464eb2021-12-18 14:54:35 -0700591 Args:
Masahiro Yamadacefaa582016-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 Glass1f701862019-10-31 07:42:57 -0600604 print(' %d defconfigs out of %d\r' % (self.current, self.total), end=' ')
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900605 sys.stdout.flush()
606
Simon Glass44116332017-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 Rini3c5f4152019-09-20 17:42:09 -0400617 self.conf = kconfiglib.Kconfig()
Simon Glass44116332017-06-15 21:39:33 -0600618
619
Masahiro Yamadab6160812015-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 Glassd9c1da22021-12-18 14:54:31 -0700627 def __init__(self, configs, args, build_dir):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900628 """Create a new parser.
629
Simon Glassb3464eb2021-12-18 14:54:35 -0700630 Args:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900631 configs: A list of CONFIGs to move.
Simon Glassb3464eb2021-12-18 14:54:35 -0700632 args (Namespace): program arguments
Masahiro Yamadab6160812015-05-20 11:36:07 +0900633 build_dir: Build directory.
634 """
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900635 self.configs = configs
Simon Glassd9c1da22021-12-18 14:54:31 -0700636 self.args = args
Masahiro Yamada5393b612016-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 Yamada6d139172016-08-22 22:18:22 +0900639 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
640 'autoconf.mk')
Simon Glass8fb5bd02017-06-01 19:39:01 -0600641 self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
Masahiro Yamada07f98522016-05-19 15:52:06 +0900642 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamadab6160812015-05-20 11:36:07 +0900643
Simon Glass257f5232017-07-10 14:47:47 -0600644 def get_arch(self):
645 """Parse .config file and return the architecture.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900646
647 Returns:
Simon Glass257f5232017-07-10 14:47:47 -0600648 Architecture name (e.g. 'arm').
Masahiro Yamadab6160812015-05-20 11:36:07 +0900649 """
650 arch = ''
651 cpu = ''
Simon Glassaba238f2021-12-18 14:54:34 -0700652 for line in read_file(self.dotconfig):
Masahiro Yamadab6160812015-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 Yamadac4d76eb2016-05-19 15:51:53 +0900661 if not arch:
662 return None
Masahiro Yamadab6160812015-05-20 11:36:07 +0900663
664 # fix-up for aarch64
665 if arch == 'arm' and cpu == 'armv8':
666 arch = 'aarch64'
667
Simon Glass257f5232017-07-10 14:47:47 -0600668 return arch
Masahiro Yamadab6160812015-05-20 11:36:07 +0900669
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900670 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamadab6160812015-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 Glassb3464eb2021-12-18 14:54:35 -0700677 Args:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900678 config: CONFIG name to parse.
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900679 dotconfig_lines: lines from the .config file.
Masahiro Yamadab6160812015-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 Yamadab6160812015-05-20 11:36:07 +0900686 not_set = '# %s is not set' % config
687
Masahiro Yamadab6160812015-05-20 11:36:07 +0900688 for line in autoconf_lines:
689 line = line.rstrip()
690 if line.startswith(config + '='):
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900691 new_val = line
Masahiro Yamadab6160812015-05-20 11:36:07 +0900692 break
Masahiro Yamadab6160812015-05-20 11:36:07 +0900693 else:
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900694 new_val = not_set
695
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200696 new_val = try_expand(new_val)
697
Masahiro Yamada35204d92016-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 Yamada5643d6e2016-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 Yamadab48387f2016-06-15 14:33:50 +0900717 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
718 new_val)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900719
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900720 def update_dotconfig(self):
Masahiro Yamada7c0d9d22016-05-19 15:51:50 +0900721 """Parse files for the config options and update the .config.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900722
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900723 This function parses the generated .config and include/autoconf.mk
724 searching the target options.
Masahiro Yamada7c0d9d22016-05-19 15:51:50 +0900725 Move the config option(s) to the .config as needed.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900726
Simon Glassb3464eb2021-12-18 14:54:35 -0700727 Args:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900728 defconfig: defconfig name.
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900729
730 Returns:
Masahiro Yamada263d1372016-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 Yamadab6160812015-05-20 11:36:07 +0900734 """
735
Masahiro Yamadab6160812015-05-20 11:36:07 +0900736 results = []
Masahiro Yamada263d1372016-05-19 15:52:04 +0900737 updated = False
Masahiro Yamada35204d92016-08-22 22:18:21 +0900738 suspicious = False
Masahiro Yamada6d139172016-08-22 22:18:22 +0900739 rm_files = [self.config_autoconf, self.autoconf]
740
Simon Glassd9c1da22021-12-18 14:54:31 -0700741 if self.args.spl:
Masahiro Yamada6d139172016-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 Glassd9c1da22021-12-18 14:54:31 -0700749 color_text(self.args.color, COLOR_BROWN,
Masahiro Yamada6d139172016-08-22 22:18:22 +0900750 "SPL is not enabled. Skipped.") + '\n')
751 else:
752 autoconf_path = self.autoconf
Masahiro Yamadab6160812015-05-20 11:36:07 +0900753
Simon Glassaba238f2021-12-18 14:54:34 -0700754 dotconfig_lines = read_file(self.dotconfig)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900755
Simon Glassaba238f2021-12-18 14:54:34 -0700756 autoconf_lines = read_file(autoconf_path)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900757
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900758 for config in self.configs:
759 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger166edec2015-05-19 13:21:17 -0500760 autoconf_lines)
Masahiro Yamadab6160812015-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 Yamada5643d6e2016-05-19 15:51:56 +0900769 elif action == ACTION_NO_ENTRY:
Simon Glassdc634d92021-12-18 14:54:30 -0700770 actlog = '%s is not defined in Kconfig. Do nothing.' % value
Masahiro Yamadab6160812015-05-20 11:36:07 +0900771 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada35204d92016-08-22 22:18:21 +0900772 elif action == ACTION_NO_ENTRY_WARN:
Simon Glassdc634d92021-12-18 14:54:30 -0700773 actlog = '%s is not defined in Kconfig (suspicious). Do nothing.' % value
Masahiro Yamada35204d92016-08-22 22:18:21 +0900774 log_color = COLOR_YELLOW
775 suspicious = True
Masahiro Yamada5643d6e2016-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 Yamadab6160812015-05-20 11:36:07 +0900779 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada6d139172016-08-22 22:18:22 +0900780 elif action == ACTION_SPL_NOT_EXIST:
Simon Glassdc634d92021-12-18 14:54:30 -0700781 actlog = 'SPL is not enabled for this defconfig. Skip.'
Masahiro Yamada6d139172016-08-22 22:18:22 +0900782 log_color = COLOR_PURPLE
Masahiro Yamadab6160812015-05-20 11:36:07 +0900783 else:
Simon Glassdc634d92021-12-18 14:54:30 -0700784 sys.exit('Internal Error. This should not happen.')
Masahiro Yamadab6160812015-05-20 11:36:07 +0900785
Simon Glassd9c1da22021-12-18 14:54:31 -0700786 log += color_text(self.args.color, log_color, actlog) + '\n'
Masahiro Yamadab6160812015-05-20 11:36:07 +0900787
Simon Glassb3464eb2021-12-18 14:54:35 -0700788 with open(self.dotconfig, 'a', encoding='utf-8') as out:
Masahiro Yamada953d93b2016-05-19 15:51:49 +0900789 for (action, value) in results:
790 if action == ACTION_MOVE:
Simon Glassb3464eb2021-12-18 14:54:35 -0700791 out.write(value + '\n')
Masahiro Yamada263d1372016-05-19 15:52:04 +0900792 updated = True
Masahiro Yamadab6160812015-05-20 11:36:07 +0900793
Masahiro Yamada07f98522016-05-19 15:52:06 +0900794 self.results = results
Masahiro Yamada6d139172016-08-22 22:18:22 +0900795 for f in rm_files:
796 os.remove(f)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900797
Masahiro Yamada35204d92016-08-22 22:18:21 +0900798 return (updated, suspicious, log)
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900799
Masahiro Yamada07f98522016-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 Glassaba238f2021-12-18 14:54:34 -0700810 defconfig_lines = read_file(self.defconfig)
Masahiro Yamada07f98522016-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 Glassd9c1da22021-12-18 14:54:31 -0700816 log += color_text(self.args.color, COLOR_YELLOW,
Masahiro Yamada07f98522016-05-19 15:52:06 +0900817 "'%s' was removed by savedefconfig.\n" %
818 value)
819
820 return log
821
Simon Glass43cf08f2017-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 Yamadab6160812015-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 Glassd9c1da22021-12-18 14:54:31 -0700859 def __init__(self, toolchains, configs, args, progress, devnull,
Simon Glass257f5232017-07-10 14:47:47 -0600860 make_cmd, reference_src_dir, db_queue):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900861 """Create a new process slot.
862
Simon Glassb3464eb2021-12-18 14:54:35 -0700863 Args:
Simon Glass257f5232017-07-10 14:47:47 -0600864 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900865 configs: A list of CONFIGs to move.
Simon Glassd9c1da22021-12-18 14:54:31 -0700866 args: Program arguments
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900867 progress: A progress indicator.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900868 devnull: A file object of '/dev/null'.
869 make_cmd: command name of GNU Make.
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500870 reference_src_dir: Determine the true starting config state from this
871 source tree.
Simon Glass43cf08f2017-06-01 19:39:02 -0600872 db_queue: output queue to write config info for the database
Masahiro Yamadab6160812015-05-20 11:36:07 +0900873 """
Simon Glass257f5232017-07-10 14:47:47 -0600874 self.toolchains = toolchains
Simon Glassd9c1da22021-12-18 14:54:31 -0700875 self.args = args
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900876 self.progress = progress
Masahiro Yamadab6160812015-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 Hershbergerb1a570f2016-06-10 14:53:32 -0500880 self.reference_src_dir = reference_src_dir
Simon Glass43cf08f2017-06-01 19:39:02 -0600881 self.db_queue = db_queue
Simon Glassd9c1da22021-12-18 14:54:31 -0700882 self.parser = KconfigParser(configs, args, self.build_dir)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900883 self.state = STATE_IDLE
Masahiro Yamada1271b672016-08-22 22:18:20 +0900884 self.failed_boards = set()
885 self.suspicious_boards = set()
Masahiro Yamadab6160812015-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 Hershberger640de872016-06-10 14:53:29 -0500892 because it is guaranteed the destructor is always invoked when the
Masahiro Yamadab6160812015-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 Yamadacefaa582016-05-19 15:51:55 +0900902 def add(self, defconfig):
Masahiro Yamadab6160812015-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 Glassb3464eb2021-12-18 14:54:35 -0700909 Args:
Masahiro Yamadab6160812015-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 Yamadacb256cb2016-06-08 11:47:37 +0900917
Masahiro Yamadab6160812015-05-20 11:36:07 +0900918 self.defconfig = defconfig
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900919 self.log = ''
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900920 self.current_src_dir = self.reference_src_dir
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900921 self.do_defconfig()
Masahiro Yamadab6160812015-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 Yamada263d1372016-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 Yamadab6160812015-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 Yamadacb256cb2016-06-08 11:47:37 +0900947 self.handle_error()
948 elif self.state == STATE_DEFCONFIG:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900949 if self.reference_src_dir and not self.current_src_dir:
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500950 self.do_savedefconfig()
951 else:
952 self.do_autoconf()
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900953 elif self.state == STATE_AUTOCONF:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900954 if self.current_src_dir:
955 self.current_src_dir = None
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500956 self.do_defconfig()
Simon Glassd9c1da22021-12-18 14:54:31 -0700957 elif self.args.build_db:
Simon Glass43cf08f2017-06-01 19:39:02 -0600958 self.do_build_db()
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500959 else:
960 self.do_savedefconfig()
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900961 elif self.state == STATE_SAVEDEFCONFIG:
962 self.update_defconfig()
963 else:
Simon Glassdc634d92021-12-18 14:54:30 -0700964 sys.exit('Internal Error. This should not happen.')
Masahiro Yamadab6160812015-05-20 11:36:07 +0900965
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900966 return True if self.state == STATE_IDLE else False
Joe Hershberger166edec2015-05-19 13:21:17 -0500967
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900968 def handle_error(self):
969 """Handle error cases."""
Masahiro Yamada83c17672016-05-19 15:52:08 +0900970
Simon Glassd9c1da22021-12-18 14:54:31 -0700971 self.log += color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glassdc634d92021-12-18 14:54:30 -0700972 'Failed to process.\n')
Simon Glassd9c1da22021-12-18 14:54:31 -0700973 if self.args.verbose:
974 self.log += color_text(self.args.color, COLOR_LIGHT_CYAN,
Markus Klotzbuecher8773f352020-02-12 20:46:45 +0100975 self.ps.stderr.read().decode())
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900976 self.finish(False)
Joe Hershberger166edec2015-05-19 13:21:17 -0500977
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900978 def do_defconfig(self):
979 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamada0f6beda2016-05-19 15:52:07 +0900980
Masahiro Yamadacb256cb2016-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 Yamada8f5256a2016-06-15 14:33:52 +0900984 stderr=subprocess.PIPE,
985 cwd=self.current_src_dir)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900986 self.state = STATE_DEFCONFIG
Masahiro Yamada0f6beda2016-05-19 15:52:07 +0900987
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900988 def do_autoconf(self):
Simon Glass8fb5bd02017-06-01 19:39:01 -0600989 """Run 'make AUTO_CONF_PATH'."""
Masahiro Yamadab6160812015-05-20 11:36:07 +0900990
Simon Glass257f5232017-07-10 14:47:47 -0600991 arch = self.parser.get_arch()
992 try:
993 toolchain = self.toolchains.Select(arch)
994 except ValueError:
Simon Glassd9c1da22021-12-18 14:54:31 -0700995 self.log += color_text(self.args.color, COLOR_YELLOW,
Chris Packham1ebcbd12017-08-27 20:00:51 +1200996 "Tool chain for '%s' is missing. Do nothing.\n" % arch)
Masahiro Yamada274a5ee2016-05-19 15:52:03 +0900997 self.finish(False)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900998 return
Simon Glass1f701862019-10-31 07:42:57 -0600999 env = toolchain.MakeEnvironment(False)
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +09001000
Masahiro Yamadab6160812015-05-20 11:36:07 +09001001 cmd = list(self.make_cmd)
Joe Hershberger765442b2015-05-19 13:21:18 -05001002 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Simon Glass8fb5bd02017-06-01 19:39:01 -06001003 cmd.append(AUTO_CONF_PATH)
Simon Glass257f5232017-07-10 14:47:47 -06001004 self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
Masahiro Yamada8f5256a2016-06-15 14:33:52 +09001005 stderr=subprocess.PIPE,
1006 cwd=self.current_src_dir)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001007 self.state = STATE_AUTOCONF
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001008
Simon Glass43cf08f2017-06-01 19:39:02 -06001009 def do_build_db(self):
1010 """Add the board to the database"""
1011 configs = {}
Simon Glassaba238f2021-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 Glass43cf08f2017-06-01 19:39:02 -06001016 self.db_queue.put([self.defconfig, configs])
1017 self.finish(True)
1018
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001019 def do_savedefconfig(self):
1020 """Update the .config and run 'make savedefconfig'."""
1021
Masahiro Yamada35204d92016-08-22 22:18:21 +09001022 (updated, suspicious, log) = self.parser.update_dotconfig()
1023 if suspicious:
1024 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001025 self.log += log
1026
Simon Glassd9c1da22021-12-18 14:54:31 -07001027 if not self.args.force_sync and not updated:
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001028 self.finish(True)
1029 return
1030 if updated:
Simon Glassd9c1da22021-12-18 14:54:31 -07001031 self.log += color_text(self.args.color, COLOR_LIGHT_GREEN,
Simon Glassdc634d92021-12-18 14:54:30 -07001032 'Syncing by savedefconfig...\n')
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001033 else:
Simon Glassdc634d92021-12-18 14:54:30 -07001034 self.log += 'Syncing by savedefconfig (forced by option)...\n'
Masahiro Yamadacb256cb2016-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 Yamada3c9bfea2016-06-15 14:33:54 +09001045 log = self.parser.check_defconfig()
1046 if log:
Masahiro Yamada1271b672016-08-22 22:18:20 +09001047 self.suspicious_boards.add(self.defconfig)
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001048 self.log += log
Masahiro Yamadacb256cb2016-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 Glassd9c1da22021-12-18 14:54:31 -07001054 self.log += color_text(self.args.color, COLOR_LIGHT_BLUE,
Simon Glassdc634d92021-12-18 14:54:30 -07001055 'defconfig was updated.\n')
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001056
Simon Glassd9c1da22021-12-18 14:54:31 -07001057 if not self.args.dry_run and updated:
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001058 shutil.move(new_defconfig, orig_defconfig)
1059 self.finish(True)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001060
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001061 def finish(self, success):
1062 """Display log along with progress and go to the idle state.
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001063
Simon Glassb3464eb2021-12-18 14:54:35 -07001064 Args:
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001065 success: Should be True when the defconfig was processed
1066 successfully, or False when it fails.
Masahiro Yamada465b7c02016-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 Glass1f701862019-10-31 07:42:57 -06001074 print(log, file=(sys.stdout if success else sys.stderr))
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001075
1076 if not success:
Simon Glassd9c1da22021-12-18 14:54:31 -07001077 if self.args.exit_on_error:
Simon Glassdc634d92021-12-18 14:54:30 -07001078 sys.exit('Exit on error.')
Masahiro Yamada274a5ee2016-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 Yamada1271b672016-08-22 22:18:20 +09001081 self.failed_boards.add(self.defconfig)
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001082
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001083 self.progress.inc()
1084 self.progress.show()
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001085 self.state = STATE_IDLE
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001086
Masahiro Yamadab6160812015-05-20 11:36:07 +09001087 def get_failed_boards(self):
Masahiro Yamada1271b672016-08-22 22:18:20 +09001088 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamadab6160812015-05-20 11:36:07 +09001089 """
1090 return self.failed_boards
1091
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001092 def get_suspicious_boards(self):
Masahiro Yamada1271b672016-08-22 22:18:20 +09001093 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001094 """
Masahiro Yamada35204d92016-08-22 22:18:21 +09001095 return self.suspicious_boards - self.failed_boards
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001096
Masahiro Yamadab6160812015-05-20 11:36:07 +09001097class Slots:
1098
1099 """Controller of the array of subprocess slots."""
1100
Simon Glassd9c1da22021-12-18 14:54:31 -07001101 def __init__(self, toolchains, configs, args, progress,
Simon Glass257f5232017-07-10 14:47:47 -06001102 reference_src_dir, db_queue):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001103 """Create a new slots controller.
1104
Simon Glassb3464eb2021-12-18 14:54:35 -07001105 Args:
Simon Glass257f5232017-07-10 14:47:47 -06001106 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001107 configs: A list of CONFIGs to move.
Simon Glassd9c1da22021-12-18 14:54:31 -07001108 args: Program arguments
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001109 progress: A progress indicator.
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001110 reference_src_dir: Determine the true starting config state from this
1111 source tree.
Simon Glass43cf08f2017-06-01 19:39:02 -06001112 db_queue: output queue to write config info for the database
Masahiro Yamadab6160812015-05-20 11:36:07 +09001113 """
Simon Glassd9c1da22021-12-18 14:54:31 -07001114 self.args = args
Masahiro Yamadab6160812015-05-20 11:36:07 +09001115 self.slots = []
Simon Glass34c505f2021-12-18 14:54:32 -07001116 devnull = subprocess.DEVNULL
Masahiro Yamadab6160812015-05-20 11:36:07 +09001117 make_cmd = get_make_cmd()
Simon Glassd9c1da22021-12-18 14:54:31 -07001118 for i in range(args.jobs):
1119 self.slots.append(Slot(toolchains, configs, args, progress,
Simon Glass257f5232017-07-10 14:47:47 -06001120 devnull, make_cmd, reference_src_dir,
1121 db_queue))
Masahiro Yamadab6160812015-05-20 11:36:07 +09001122
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001123 def add(self, defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001124 """Add a new subprocess if a vacant slot is found.
1125
Simon Glassb3464eb2021-12-18 14:54:35 -07001126 Args:
Masahiro Yamadab6160812015-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 Yamadacefaa582016-05-19 15:51:55 +09001133 if slot.add(defconfig):
Masahiro Yamadab6160812015-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 Yamada1271b672016-08-22 22:18:20 +09001162 boards = set()
Masahiro Yamada0153f032016-06-15 14:33:53 +09001163 output_file = 'moveconfig.failed'
Masahiro Yamadab6160812015-05-20 11:36:07 +09001164
1165 for slot in self.slots:
Masahiro Yamada1271b672016-08-22 22:18:20 +09001166 boards |= slot.get_failed_boards()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001167
Masahiro Yamada0153f032016-06-15 14:33:53 +09001168 if boards:
1169 boards = '\n'.join(boards) + '\n'
Simon Glassdc634d92021-12-18 14:54:30 -07001170 msg = 'The following boards were not processed due to error:\n'
Masahiro Yamada0153f032016-06-15 14:33:53 +09001171 msg += boards
Simon Glassdc634d92021-12-18 14:54:30 -07001172 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassd9c1da22021-12-18 14:54:31 -07001173 print(color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glass1f701862019-10-31 07:42:57 -06001174 msg), file=sys.stderr)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001175
Simon Glassb09ae452021-12-18 14:54:33 -07001176 write_file(output_file, boards)
Joe Hershbergerdade12e2015-05-19 13:21:22 -05001177
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001178 def show_suspicious_boards(self):
1179 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada1271b672016-08-22 22:18:20 +09001180 boards = set()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001181 output_file = 'moveconfig.suspicious'
1182
1183 for slot in self.slots:
Masahiro Yamada1271b672016-08-22 22:18:20 +09001184 boards |= slot.get_suspicious_boards()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001185
1186 if boards:
1187 boards = '\n'.join(boards) + '\n'
Simon Glassdc634d92021-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 Yamada3c9bfea2016-06-15 14:33:54 +09001190 msg += boards
Simon Glassdc634d92021-12-18 14:54:30 -07001191 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassd9c1da22021-12-18 14:54:31 -07001192 print(color_text(self.args.color, COLOR_YELLOW,
Simon Glass1f701862019-10-31 07:42:57 -06001193 msg), file=sys.stderr)
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001194
Simon Glassb09ae452021-12-18 14:54:33 -07001195 write_file(output_file, boards)
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001196
Masahiro Yamada2e74fee2016-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 Glassb3464eb2021-12-18 14:54:35 -07001204 Args:
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001205 commit: commit to git-clone
1206 """
1207 self.src_dir = tempfile.mkdtemp()
Simon Glassdc634d92021-12-18 14:54:30 -07001208 print('Cloning git repo to a separate work directory...')
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001209 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1210 cwd=self.src_dir)
Simon Glass1f701862019-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 Yamada2e74fee2016-06-15 14:33:51 +09001213 subprocess.check_output(['git', 'checkout', commit],
1214 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001215
1216 def __del__(self):
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001217 """Delete the reference source directory
Joe Hershbergerb1a570f2016-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 Yamada2e74fee2016-06-15 14:33:51 +09001224 shutil.rmtree(self.src_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001225
Masahiro Yamada2e74fee2016-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 Hershbergerb1a570f2016-06-10 14:53:32 -05001230
Simon Glassd9c1da22021-12-18 14:54:31 -07001231def move_config(toolchains, configs, args, db_queue):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001232 """Move config options to defconfig files.
1233
Simon Glassb3464eb2021-12-18 14:54:35 -07001234 Args:
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001235 configs: A list of CONFIGs to move.
Simon Glassd9c1da22021-12-18 14:54:31 -07001236 args: Program arguments
Masahiro Yamadab6160812015-05-20 11:36:07 +09001237 """
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001238 if len(configs) == 0:
Simon Glassd9c1da22021-12-18 14:54:31 -07001239 if args.force_sync:
Simon Glass1f701862019-10-31 07:42:57 -06001240 print('No CONFIG is specified. You are probably syncing defconfigs.', end=' ')
Simon Glassd9c1da22021-12-18 14:54:31 -07001241 elif args.build_db:
Simon Glass1f701862019-10-31 07:42:57 -06001242 print('Building %s database' % CONFIG_DATABASE)
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001243 else:
Simon Glass1f701862019-10-31 07:42:57 -06001244 print('Neither CONFIG nor --force-sync is specified. Nothing will happen.', end=' ')
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001245 else:
Simon Glass1f701862019-10-31 07:42:57 -06001246 print('Move ' + ', '.join(configs), end=' ')
Simon Glassd9c1da22021-12-18 14:54:31 -07001247 print('(jobs: %d)\n' % args.jobs)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001248
Simon Glassd9c1da22021-12-18 14:54:31 -07001249 if args.git_ref:
1250 reference_src = ReferenceSource(args.git_ref)
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001251 reference_src_dir = reference_src.get_dir()
1252 else:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +09001253 reference_src_dir = None
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001254
Simon Glassd9c1da22021-12-18 14:54:31 -07001255 if args.defconfigs:
1256 defconfigs = get_matched_defconfigs(args.defconfigs)
Joe Hershbergerc6e043a2015-05-19 13:21:19 -05001257 else:
Masahiro Yamada58175e32016-07-25 19:15:28 +09001258 defconfigs = get_all_defconfigs()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001259
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001260 progress = Progress(len(defconfigs))
Simon Glassd9c1da22021-12-18 14:54:31 -07001261 slots = Slots(toolchains, configs, args, progress, reference_src_dir,
Simon Glassb3464eb2021-12-18 14:54:35 -07001262 db_queue)
Masahiro Yamadab6160812015-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 Yamadacefaa582016-05-19 15:51:55 +09001267 for defconfig in defconfigs:
1268 while not slots.add(defconfig):
Masahiro Yamadab6160812015-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 Glass1f701862019-10-31 07:42:57 -06001277 print('')
Masahiro Yamadab6160812015-05-20 11:36:07 +09001278 slots.show_failed_boards()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001279 slots.show_suspicious_boards()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001280
Simon Glass44116332017-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 Rini3c5f4152019-09-20 17:42:09 -04001285 kconf: Kconfiglib.Kconfig object
Simon Glass44116332017-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 Rini3c5f4152019-09-20 17:42:09 -04001293 sym = kconf.syms.get(imply_config)
Simon Glass44116332017-06-15 21:39:33 -06001294 if sym:
Simon Glass520b47a2021-07-21 21:35:53 -06001295 for sel, cond in (sym.selects + sym.implies):
Simon Glass93c0a9e2021-12-18 08:09:42 -07001296 if sel.name == config:
Simon Glass44116332017-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 Rini3c5f4152019-09-20 17:42:09 -04001307 kconf: Kconfiglib.Kconfig object
Simon Glass44116332017-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 Rini3c5f4152019-09-20 17:42:09 -04001318 sym = kconf.syms.get(imply_config)
Simon Glass44116332017-06-15 21:39:33 -06001319 if not sym:
1320 return 'cannot find sym'
Simon Glass520b47a2021-07-21 21:35:53 -06001321 nodes = sym.nodes
1322 if len(nodes) != 1:
1323 return '%d locations' % len(nodes)
Simon Glass93c0a9e2021-12-18 08:09:42 -07001324 node = nodes[0]
1325 fname, linenum = node.filename, node.linenr
Simon Glass44116332017-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 Glassaba238f2021-12-18 14:54:34 -07001330 data = read_file(fname)
Simon Glass44116332017-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 Glassaba238f2021-12-18 14:54:34 -07001347 data = read_file(fname)
Simon Glass44116332017-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 Glassb09ae452021-12-18 14:54:33 -07001353 write_file(fname, data)
Simon Glass44116332017-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 Glass92e55582017-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 Glass44116332017-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 Glassb3464eb2021-12-18 14:54:35 -07001368}
Simon Glass92e55582017-06-15 21:39:32 -06001369
Simon Glassf931c2f2021-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 Glassaba238f2021-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 Glassf931c2f2021-12-18 08:09:43 -07001413
1414 return all_configs, all_defconfigs, config_db, defconfig_db
1415
1416
Simon Glass44116332017-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 Glassc6e73cf2017-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 Glass44116332017-06-15 21:39:33 -06001443 add_imply: Automatically add an 'imply' for each config.
Simon Glass92e55582017-06-15 21:39:32 -06001444 imply_flags: Flags which control which implying configs are allowed
1445 (IMPLY_...)
Simon Glass44116332017-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 Glassc6e73cf2017-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 Glass44116332017-06-15 21:39:33 -06001459 kconf = KconfigScanner().conf if check_kconfig else None
1460 if add_imply and add_imply != 'all':
Simon Glass93c0a9e2021-12-18 08:09:42 -07001461 add_imply = add_imply.split(',')
Simon Glass44116332017-06-15 21:39:33 -06001462
Simon Glassf931c2f2021-12-18 08:09:43 -07001463 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
Simon Glassc6e73cf2017-06-01 19:39:03 -06001464
Simon Glass93c0a9e2021-12-18 08:09:42 -07001465 # Work through each target config option in turn, independently
Simon Glassc6e73cf2017-06-01 19:39:03 -06001466 for config in config_list:
1467 defconfigs = defconfig_db.get(config)
1468 if not defconfigs:
Simon Glass1f701862019-10-31 07:42:57 -06001469 print('%s not found in any defconfig' % config)
Simon Glassc6e73cf2017-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 Glass1f701862019-10-31 07:42:57 -06001476 print('%s found in %d/%d defconfigs' % (config, num_defconfigs,
1477 len(all_configs)))
Simon Glassc6e73cf2017-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 Glass92e55582017-06-15 21:39:32 -06001485 if 'ERRATUM' in imply_config:
Simon Glassc6e73cf2017-06-01 19:39:03 -06001486 continue
Simon Glassb3464eb2021-12-18 14:54:35 -07001487 if not imply_flags & IMPLY_CMD:
Simon Glass92e55582017-06-15 21:39:32 -06001488 if 'CONFIG_CMD' in imply_config:
1489 continue
Simon Glassb3464eb2021-12-18 14:54:35 -07001490 if not imply_flags & IMPLY_TARGET:
Simon Glass92e55582017-06-15 21:39:32 -06001491 if 'CONFIG_TARGET' in imply_config:
1492 continue
Simon Glassc6e73cf2017-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 Glass1f701862019-10-31 07:42:57 -06001514 for prev in list(imply_configs.keys()):
Simon Glassc6e73cf2017-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 Glass44116332017-06-15 21:39:33 -06001533 ranked_iconfigs = sorted(imply_configs,
Simon Glassc6e73cf2017-06-01 19:39:03 -06001534 key=lambda k: len(imply_configs[k]), reverse=True)
Simon Glass44116332017-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 Glassc6e73cf2017-06-01 19:39:03 -06001540
1541 # Don't bother if there are less than 5 defconfigs affected.
Simon Glass92e55582017-06-15 21:39:32 -06001542 if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
Simon Glassc6e73cf2017-06-01 19:39:03 -06001543 continue
Simon Glass44116332017-06-15 21:39:33 -06001544 missing = defconfigs - imply_configs[iconfig]
Simon Glassc6e73cf2017-06-01 19:39:03 -06001545 missing_str = ', '.join(missing) if missing else 'all'
1546 missing_str = ''
Simon Glass44116332017-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 Glass520b47a2021-07-21 21:35:53 -06001553 nodes = sym.nodes
1554 if len(nodes) == 1:
1555 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glass44116332017-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 Rini3c5f4152019-09-20 17:42:09 -04001562 sym = kconf.syms.get(iconfig[CONFIG_LEN:])
Simon Glass44116332017-06-15 21:39:33 -06001563 fname = ''
1564 if sym:
Simon Glass520b47a2021-07-21 21:35:53 -06001565 nodes = sym.nodes
1566 if len(nodes) == 1:
1567 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glass44116332017-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 Glassb3464eb2021-12-18 14:54:35 -07001573 not imply_flags & IMPLY_NON_ARCH_BOARD):
Simon Glass44116332017-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)
Simon Glassc6e73cf2017-06-01 19:39:03 -06001582
Simon Glass44116332017-06-15 21:39:33 -06001583 if show and kconfig_info != 'skip':
Simon Glass1f701862019-10-31 07:42:57 -06001584 print('%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
1585 kconfig_info, missing_str))
Simon Glass44116332017-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 Glass1f701862019-10-31 07:42:57 -06001592 for fname, linenums in add_list.items():
Simon Glass44116332017-06-15 21:39:33 -06001593 for linenum in sorted(linenums, reverse=True):
1594 add_imply_rule(config[CONFIG_LEN:], fname, linenum)
1595
Simon Glassc6e73cf2017-06-01 19:39:03 -06001596
Simon Glass0082b2e2021-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 Glassaba238f2021-12-18 14:54:34 -07001609 adhoc_configs = set(read_file('scripts/config_whitelist.txt'))
Simon Glass0082b2e2021-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 Yamadab6160812015-05-20 11:36:07 +09001669def main():
1670 try:
1671 cpu_count = multiprocessing.cpu_count()
1672 except NotImplementedError:
1673 cpu_count = 1
1674
Simon Glassd9c1da22021-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 Glass44116332017-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 Glassd9c1da22021-12-18 14:54:31 -07001683 parser.add_argument('-A', '--skip-added', action='store_true', default=False,
Simon Glass44116332017-06-15 21:39:33 -06001684 help="don't show options which are already marked as "
1685 'implying others')
Simon Glassd9c1da22021-12-18 14:54:31 -07001686 parser.add_argument('-b', '--build-db', action='store_true', default=False,
Simon Glass43cf08f2017-06-01 19:39:02 -06001687 help='build a CONFIG database')
Simon Glassd9c1da22021-12-18 14:54:31 -07001688 parser.add_argument('-c', '--color', action='store_true', default=False,
Masahiro Yamadab6160812015-05-20 11:36:07 +09001689 help='display the log in color')
Simon Glassd9c1da22021-12-18 14:54:31 -07001690 parser.add_argument('-C', '--commit', action='store_true', default=False,
Simon Glass8bf41c22016-09-12 23:18:21 -06001691 help='Create a git commit for the operation')
Simon Glassd9c1da22021-12-18 14:54:31 -07001692 parser.add_argument('-d', '--defconfigs', type=str,
Simon Glass8f3cf312017-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 Glassd9c1da22021-12-18 14:54:31 -07001696 parser.add_argument('-e', '--exit-on-error', action='store_true',
Masahiro Yamadab6160812015-05-20 11:36:07 +09001697 default=False,
1698 help='exit immediately on any error')
Simon Glassd9c1da22021-12-18 14:54:31 -07001699 parser.add_argument('-f', '--find', action='store_true', default=False,
Simon Glass0082b2e2021-12-18 08:09:46 -07001700 help='Find boards with a given config combination')
Simon Glassd9c1da22021-12-18 14:54:31 -07001701 parser.add_argument('-H', '--headers-only', dest='cleanup_headers_only',
Joe Hershberger23475932015-05-19 13:21:20 -05001702 action='store_true', default=False,
1703 help='only cleanup the headers')
Simon Glassd9c1da22021-12-18 14:54:31 -07001704 parser.add_argument('-i', '--imply', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001705 help='find options which imply others')
Simon Glassd9c1da22021-12-18 14:54:31 -07001706 parser.add_argument('-I', '--imply-flags', type=str, default='',
Simon Glass0559a742021-12-18 08:09:44 -07001707 help="control the -i option ('help' for help")
Simon Glassd9c1da22021-12-18 14:54:31 -07001708 parser.add_argument('-j', '--jobs', type=int, default=cpu_count,
Masahiro Yamadab6160812015-05-20 11:36:07 +09001709 help='the number of jobs to run simultaneously')
Simon Glassd9c1da22021-12-18 14:54:31 -07001710 parser.add_argument('-n', '--dry-run', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001711 help='perform a trial run (show log with no changes)')
Simon Glassd9c1da22021-12-18 14:54:31 -07001712 parser.add_argument('-r', '--git-ref', type=str,
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001713 help='the git ref to clone for building the autoconf.mk')
Simon Glassd9c1da22021-12-18 14:54:31 -07001714 parser.add_argument('-s', '--force-sync', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001715 help='force sync by savedefconfig')
Simon Glassd9c1da22021-12-18 14:54:31 -07001716 parser.add_argument('-S', '--spl', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001717 help='parse config options defined for SPL build')
Simon Glassd9c1da22021-12-18 14:54:31 -07001718 parser.add_argument('-t', '--test', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001719 help='run unit tests')
Simon Glassd9c1da22021-12-18 14:54:31 -07001720 parser.add_argument('-y', '--yes', action='store_true', default=False,
Simon Glass13e05a02016-09-12 23:18:20 -06001721 help="respond 'yes' to any prompts")
Simon Glassd9c1da22021-12-18 14:54:31 -07001722 parser.add_argument('-v', '--verbose', action='store_true', default=False,
Joe Hershberger808b63f2015-05-19 13:21:24 -05001723 help='show any build errors as boards are built')
Simon Glassd9c1da22021-12-18 14:54:31 -07001724 parser.add_argument('configs', nargs='*')
Masahiro Yamadab6160812015-05-20 11:36:07 +09001725
Simon Glassd9c1da22021-12-18 14:54:31 -07001726 args = parser.parse_args()
1727 configs = args.configs
Masahiro Yamadab6160812015-05-20 11:36:07 +09001728
Simon Glassd9c1da22021-12-18 14:54:31 -07001729 if args.test:
Simon Glassbb57be72021-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 Glassd9c1da22021-12-18 14:54:31 -07001736 if not any((len(configs), args.force_sync, args.build_db, args.imply,
1737 args.find)):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001738 parser.print_usage()
1739 sys.exit(1)
1740
Masahiro Yamadab903c4e2016-05-19 15:51:58 +09001741 # prefix the option name with CONFIG_ if missing
Simon Glass0082b2e2021-12-18 08:09:46 -07001742 configs = [prefix_config(cfg) for cfg in configs]
Masahiro Yamadab6160812015-05-20 11:36:07 +09001743
Joe Hershberger23475932015-05-19 13:21:20 -05001744 check_top_directory()
1745
Simon Glassd9c1da22021-12-18 14:54:31 -07001746 if args.imply:
Simon Glass92e55582017-06-15 21:39:32 -06001747 imply_flags = 0
Simon Glassd9c1da22021-12-18 14:54:31 -07001748 if args.imply_flags == 'all':
Simon Glass5f096922017-07-10 14:47:46 -06001749 imply_flags = -1
1750
Simon Glassd9c1da22021-12-18 14:54:31 -07001751 elif args.imply_flags:
1752 for flag in args.imply_flags.split(','):
Simon Glass5f096922017-07-10 14:47:46 -06001753 bad = flag not in IMPLY_FLAGS
1754 if bad:
Simon Glass1f701862019-10-31 07:42:57 -06001755 print("Invalid flag '%s'" % flag)
Simon Glass5f096922017-07-10 14:47:46 -06001756 if flag == 'help' or bad:
Simon Glass1f701862019-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 Glass5f096922017-07-10 14:47:46 -06001760 parser.print_usage()
1761 sys.exit(1)
1762 imply_flags |= IMPLY_FLAGS[flag][0]
Simon Glass92e55582017-06-15 21:39:32 -06001763
Simon Glassd9c1da22021-12-18 14:54:31 -07001764 do_imply_config(configs, args.add_imply, imply_flags, args.skip_added)
Simon Glassc6e73cf2017-06-01 19:39:03 -06001765 return
1766
Simon Glassd9c1da22021-12-18 14:54:31 -07001767 if args.find:
Simon Glass0082b2e2021-12-18 08:09:46 -07001768 do_find_config(configs)
1769 return
1770
Simon Glass43cf08f2017-06-01 19:39:02 -06001771 config_db = {}
Simon Glass1f701862019-10-31 07:42:57 -06001772 db_queue = queue.Queue()
Simon Glass43cf08f2017-06-01 19:39:02 -06001773 t = DatabaseThread(config_db, db_queue)
1774 t.setDaemon(True)
1775 t.start()
1776
Simon Glassd9c1da22021-12-18 14:54:31 -07001777 if not args.cleanup_headers_only:
Masahiro Yamadad0a9d2a2016-07-25 19:15:23 +09001778 check_clean_directory()
Simon Glass1f701862019-10-31 07:42:57 -06001779 bsettings.Setup('')
Simon Glass257f5232017-07-10 14:47:47 -06001780 toolchains = toolchain.Toolchains()
1781 toolchains.GetSettings()
1782 toolchains.Scan(verbose=False)
Simon Glassd9c1da22021-12-18 14:54:31 -07001783 move_config(toolchains, configs, args, db_queue)
Simon Glass43cf08f2017-06-01 19:39:02 -06001784 db_queue.join()
Joe Hershberger23475932015-05-19 13:21:20 -05001785
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001786 if configs:
Simon Glassd9c1da22021-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 Yamadab6160812015-05-20 11:36:07 +09001791
Simon Glassd9c1da22021-12-18 14:54:31 -07001792 if args.commit:
Simon Glass8bf41c22016-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 Glassd9c1da22021-12-18 14:54:31 -07001804 if args.build_db:
Simon Glassb3464eb2021-12-18 14:54:35 -07001805 with open(CONFIG_DATABASE, 'w', encoding='utf-8') as fd:
Simon Glass1f701862019-10-31 07:42:57 -06001806 for defconfig, configs in config_db.items():
Simon Glass1c879312017-08-13 16:02:54 -06001807 fd.write('%s\n' % defconfig)
Simon Glass43cf08f2017-06-01 19:39:02 -06001808 for config in sorted(configs.keys()):
Simon Glass1c879312017-08-13 16:02:54 -06001809 fd.write(' %s=%s\n' % (config, configs[config]))
1810 fd.write('\n')
Simon Glass43cf08f2017-06-01 19:39:02 -06001811
Masahiro Yamadab6160812015-05-20 11:36:07 +09001812if __name__ == '__main__':
Simon Glass0082b2e2021-12-18 08:09:46 -07001813 sys.exit(main())