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