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