blob: c41aa4ee28392775bcd807cc7c67952fe4004088 [file] [log] [blame]
Simon Glass793dca32019-10-31 07:42:57 -06001#!/usr/bin/env python3
Tom Rini83d290c2018-05-06 17:58:06 -04002# SPDX-License-Identifier: GPL-2.0+
Masahiro Yamada5a27c732015-05-20 11:36:07 +09003#
4# Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5#
Masahiro Yamada5a27c732015-05-20 11:36:07 +09006
7"""
8Move config options from headers to defconfig files.
9
Simon Glass5c72c0e2021-07-21 21:35:51 -060010See doc/develop/moveconfig.rst for documentation.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090011"""
12
Simon Glassb2e83c62021-12-18 14:54:31 -070013from argparse import ArgumentParser
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +010014import asteval
Simon Glass99b66602017-06-01 19:39:03 -060015import collections
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +090016import copy
Masahiro Yamadaf2f69812016-07-25 19:15:25 +090017import difflib
Simon Glass84067a52021-12-18 08:09:45 -070018import doctest
Masahiro Yamadac8e1b102016-05-19 15:52:07 +090019import filecmp
Masahiro Yamada5a27c732015-05-20 11:36:07 +090020import fnmatch
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +090021import glob
Masahiro Yamada5a27c732015-05-20 11:36:07 +090022import multiprocessing
Masahiro Yamada5a27c732015-05-20 11:36:07 +090023import os
Simon Glass793dca32019-10-31 07:42:57 -060024import queue
Masahiro Yamada5a27c732015-05-20 11:36:07 +090025import re
26import shutil
27import subprocess
28import sys
29import tempfile
Simon Glassd73fcb12017-06-01 19:39:02 -060030import threading
Masahiro Yamada5a27c732015-05-20 11:36:07 +090031import time
Simon Glass84067a52021-12-18 08:09:45 -070032import unittest
Masahiro Yamada5a27c732015-05-20 11:36:07 +090033
Simon Glass0ede00f2020-04-17 18:09:02 -060034from buildman import bsettings
35from buildman import kconfiglib
36from buildman import toolchain
Simon Glasscb008832017-06-15 21:39:33 -060037
Masahiro Yamada5a27c732015-05-20 11:36:07 +090038SHOW_GNU_MAKE = 'scripts/show-gnu-make'
39SLEEP_TIME=0.03
40
Masahiro Yamada5a27c732015-05-20 11:36:07 +090041STATE_IDLE = 0
42STATE_DEFCONFIG = 1
43STATE_AUTOCONF = 2
Joe Hershberger96464ba2015-05-19 13:21:17 -050044STATE_SAVEDEFCONFIG = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +090045
46ACTION_MOVE = 0
Masahiro Yamadacc008292016-05-19 15:51:56 +090047ACTION_NO_ENTRY = 1
Masahiro Yamada916224c2016-08-22 22:18:21 +090048ACTION_NO_ENTRY_WARN = 2
49ACTION_NO_CHANGE = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +090050
51COLOR_BLACK = '0;30'
52COLOR_RED = '0;31'
53COLOR_GREEN = '0;32'
54COLOR_BROWN = '0;33'
55COLOR_BLUE = '0;34'
56COLOR_PURPLE = '0;35'
57COLOR_CYAN = '0;36'
58COLOR_LIGHT_GRAY = '0;37'
59COLOR_DARK_GRAY = '1;30'
60COLOR_LIGHT_RED = '1;31'
61COLOR_LIGHT_GREEN = '1;32'
62COLOR_YELLOW = '1;33'
63COLOR_LIGHT_BLUE = '1;34'
64COLOR_LIGHT_PURPLE = '1;35'
65COLOR_LIGHT_CYAN = '1;36'
66COLOR_WHITE = '1;37'
67
Simon Glassf3b8e642017-06-01 19:39:01 -060068AUTO_CONF_PATH = 'include/config/auto.conf'
Simon Glassd73fcb12017-06-01 19:39:02 -060069CONFIG_DATABASE = 'moveconfig.db'
Simon Glassf3b8e642017-06-01 19:39:01 -060070
Simon Glasscb008832017-06-15 21:39:33 -060071CONFIG_LEN = len('CONFIG_')
Simon Glassf3b8e642017-06-01 19:39:01 -060072
Markus Klotzbuecherb237d352019-05-15 15:15:52 +020073SIZES = {
Simon Glassdaa694d2021-12-18 14:54:30 -070074 'SZ_1': 0x00000001, 'SZ_2': 0x00000002,
75 'SZ_4': 0x00000004, 'SZ_8': 0x00000008,
76 'SZ_16': 0x00000010, 'SZ_32': 0x00000020,
77 'SZ_64': 0x00000040, 'SZ_128': 0x00000080,
78 'SZ_256': 0x00000100, 'SZ_512': 0x00000200,
79 'SZ_1K': 0x00000400, 'SZ_2K': 0x00000800,
80 'SZ_4K': 0x00001000, 'SZ_8K': 0x00002000,
81 'SZ_16K': 0x00004000, 'SZ_32K': 0x00008000,
82 'SZ_64K': 0x00010000, 'SZ_128K': 0x00020000,
83 'SZ_256K': 0x00040000, 'SZ_512K': 0x00080000,
84 'SZ_1M': 0x00100000, 'SZ_2M': 0x00200000,
85 'SZ_4M': 0x00400000, 'SZ_8M': 0x00800000,
86 'SZ_16M': 0x01000000, 'SZ_32M': 0x02000000,
87 'SZ_64M': 0x04000000, 'SZ_128M': 0x08000000,
88 'SZ_256M': 0x10000000, 'SZ_512M': 0x20000000,
89 'SZ_1G': 0x40000000, 'SZ_2G': 0x80000000,
90 'SZ_4G': 0x100000000
Markus Klotzbuecherb237d352019-05-15 15:15:52 +020091}
92
Masahiro Yamada5a27c732015-05-20 11:36:07 +090093### helper functions ###
Masahiro Yamada5a27c732015-05-20 11:36:07 +090094def check_top_directory():
95 """Exit if we are not at the top of source directory."""
96 for f in ('README', 'Licenses'):
97 if not os.path.exists(f):
98 sys.exit('Please run at the top of source directory.')
99
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900100def check_clean_directory():
101 """Exit if the source tree is not clean."""
102 for f in ('.config', 'include/config'):
103 if os.path.exists(f):
104 sys.exit("source tree is not clean, please run 'make mrproper'")
105
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900106def get_make_cmd():
107 """Get the command name of GNU Make.
108
109 U-Boot needs GNU Make for building, but the command name is not
110 necessarily "make". (for example, "gmake" on FreeBSD).
111 Returns the most appropriate command name on your system.
112 """
113 process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE)
114 ret = process.communicate()
115 if process.returncode:
116 sys.exit('GNU Make not found')
117 return ret[0].rstrip()
118
Simon Glass25f978c2017-06-01 19:38:58 -0600119def get_matched_defconfig(line):
120 """Get the defconfig files that match a pattern
121
122 Args:
123 line: Path or filename to match, e.g. 'configs/snow_defconfig' or
124 'k2*_defconfig'. If no directory is provided, 'configs/' is
125 prepended
126
127 Returns:
128 a list of matching defconfig files
129 """
130 dirname = os.path.dirname(line)
131 if dirname:
132 pattern = line
133 else:
134 pattern = os.path.join('configs', line)
135 return glob.glob(pattern) + glob.glob(pattern + '_defconfig')
136
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900137def get_matched_defconfigs(defconfigs_file):
Simon Glassee4e61b2017-06-01 19:38:59 -0600138 """Get all the defconfig files that match the patterns in a file.
139
140 Args:
141 defconfigs_file: File containing a list of defconfigs to process, or
142 '-' to read the list from stdin
143
144 Returns:
145 A list of paths to defconfig files, with no duplicates
146 """
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900147 defconfigs = []
Simon Glassee4e61b2017-06-01 19:38:59 -0600148 if defconfigs_file == '-':
149 fd = sys.stdin
150 defconfigs_file = 'stdin'
151 else:
152 fd = open(defconfigs_file)
153 for i, line in enumerate(fd):
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900154 line = line.strip()
155 if not line:
156 continue # skip blank lines silently
Simon Glass2ddd85d2017-06-15 21:39:31 -0600157 if ' ' in line:
158 line = line.split(' ')[0] # handle 'git log' input
Simon Glass25f978c2017-06-01 19:38:58 -0600159 matched = get_matched_defconfig(line)
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900160 if not matched:
Simon Glass793dca32019-10-31 07:42:57 -0600161 print("warning: %s:%d: no defconfig matched '%s'" % \
162 (defconfigs_file, i + 1, line), file=sys.stderr)
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900163
164 defconfigs += matched
165
166 # use set() to drop multiple matching
167 return [ defconfig[len('configs') + 1:] for defconfig in set(defconfigs) ]
168
Masahiro Yamada684c3062016-07-25 19:15:28 +0900169def get_all_defconfigs():
170 """Get all the defconfig files under the configs/ directory."""
171 defconfigs = []
172 for (dirpath, dirnames, filenames) in os.walk('configs'):
173 dirpath = dirpath[len('configs') + 1:]
174 for filename in fnmatch.filter(filenames, '*_defconfig'):
175 defconfigs.append(os.path.join(dirpath, filename))
176
177 return defconfigs
178
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900179def color_text(color_enabled, color, string):
180 """Return colored string."""
181 if color_enabled:
Masahiro Yamada1d085562016-05-19 15:52:02 +0900182 # LF should not be surrounded by the escape sequence.
183 # Otherwise, additional whitespace or line-feed might be printed.
184 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
185 for s in string.split('\n') ])
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900186 else:
187 return string
188
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900189def show_diff(a, b, file_path, color_enabled):
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900190 """Show unidified diff.
191
192 Arguments:
193 a: A list of lines (before)
194 b: A list of lines (after)
195 file_path: Path to the file
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900196 color_enabled: Display the diff in color
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900197 """
198
199 diff = difflib.unified_diff(a, b,
200 fromfile=os.path.join('a', file_path),
201 tofile=os.path.join('b', file_path))
202
203 for line in diff:
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900204 if line[0] == '-' and line[1] != '-':
Simon Glass793dca32019-10-31 07:42:57 -0600205 print(color_text(color_enabled, COLOR_RED, line), end=' ')
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900206 elif line[0] == '+' and line[1] != '+':
Simon Glass793dca32019-10-31 07:42:57 -0600207 print(color_text(color_enabled, COLOR_GREEN, line), end=' ')
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900208 else:
Simon Glass793dca32019-10-31 07:42:57 -0600209 print(line, end=' ')
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900210
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900211def extend_matched_lines(lines, matched, pre_patterns, post_patterns, extend_pre,
212 extend_post):
213 """Extend matched lines if desired patterns are found before/after already
214 matched lines.
215
216 Arguments:
217 lines: A list of lines handled.
218 matched: A list of line numbers that have been already matched.
219 (will be updated by this function)
220 pre_patterns: A list of regular expression that should be matched as
221 preamble.
222 post_patterns: A list of regular expression that should be matched as
223 postamble.
224 extend_pre: Add the line number of matched preamble to the matched list.
225 extend_post: Add the line number of matched postamble to the matched list.
226 """
227 extended_matched = []
228
229 j = matched[0]
230
231 for i in matched:
232 if i == 0 or i < j:
233 continue
234 j = i
235 while j in matched:
236 j += 1
237 if j >= len(lines):
238 break
239
240 for p in pre_patterns:
241 if p.search(lines[i - 1]):
242 break
243 else:
244 # not matched
245 continue
246
247 for p in post_patterns:
248 if p.search(lines[j]):
249 break
250 else:
251 # not matched
252 continue
253
254 if extend_pre:
255 extended_matched.append(i - 1)
256 if extend_post:
257 extended_matched.append(j)
258
259 matched += extended_matched
260 matched.sort()
261
Simon Glassb2e83c62021-12-18 14:54:31 -0700262def confirm(args, prompt):
263 if not args.yes:
Chris Packham85edfc12017-05-02 21:30:46 +1200264 while True:
Simon Glass793dca32019-10-31 07:42:57 -0600265 choice = input('{} [y/n]: '.format(prompt))
Chris Packham85edfc12017-05-02 21:30:46 +1200266 choice = choice.lower()
Simon Glass793dca32019-10-31 07:42:57 -0600267 print(choice)
Chris Packham85edfc12017-05-02 21:30:46 +1200268 if choice == 'y' or choice == 'n':
269 break
270
271 if choice == 'n':
272 return False
273
274 return True
275
Simon Glass2fd85bd2021-12-18 14:54:33 -0700276def write_file(fname, data):
277 """Write data to a file
278
279 Args:
280 fname (str): Filename to write to
281 data (list of str): Lines to write (with or without trailing newline);
282 or str to write
283 """
284 with open(fname, 'w', encoding='utf-8') as out:
285 if isinstance(data, list):
286 for line in data:
287 print(line.rstrip('\n'), file=out)
288 else:
289 out.write(data)
290
Simon Glass37f815c2021-12-18 14:54:34 -0700291def read_file(fname, as_lines=True, skip_unicode=False):
292 """Read a file and return the contents
293
294 Args:
295 fname (str): Filename to read from
296 as_lines: Return file contents as a list of lines
297 skip_unicode (bool): True to report unicode errors and continue
298
299 Returns:
300 iter of str: List of ;ines from the file with newline removed; str if
301 as_lines is False with newlines intact; or None if a unicode error
302 occurred
303
304 Raises:
305 UnicodeDecodeError: Unicode error occurred when reading
306 """
307 with open(fname, encoding='utf-8') as inf:
308 try:
309 if as_lines:
310 return [line.rstrip('\n') for line in inf.readlines()]
311 else:
312 return inf.read()
313 except UnicodeDecodeError as e:
314 if not skip_unicode:
315 raises
316 print("Failed on file %s': %s" % (fname, e))
317 return None
318
Simon Glassb2e83c62021-12-18 14:54:31 -0700319def cleanup_empty_blocks(header_path, args):
Chris Packham4d9dbb12019-01-30 20:23:16 +1300320 """Clean up empty conditional blocks
321
322 Arguments:
323 header_path: path to the cleaned file.
Simon Glassb2e83c62021-12-18 14:54:31 -0700324 args: program arguments
Chris Packham4d9dbb12019-01-30 20:23:16 +1300325 """
326 pattern = re.compile(r'^\s*#\s*if.*$\n^\s*#\s*endif.*$\n*', flags=re.M)
Simon Glass37f815c2021-12-18 14:54:34 -0700327 data = read_file(header_path, as_lines=False, skip_unicode=True)
328 if data is None:
329 return
Chris Packham4d9dbb12019-01-30 20:23:16 +1300330
331 new_data = pattern.sub('\n', data)
332
333 show_diff(data.splitlines(True), new_data.splitlines(True), header_path,
Simon Glassb2e83c62021-12-18 14:54:31 -0700334 args.color)
Chris Packham4d9dbb12019-01-30 20:23:16 +1300335
Simon Glassb2e83c62021-12-18 14:54:31 -0700336 if args.dry_run:
Chris Packham4d9dbb12019-01-30 20:23:16 +1300337 return
338
Simon Glass37f815c2021-12-18 14:54:34 -0700339 if new_data != data:
340 write_file(header_path, new_data)
Chris Packham4d9dbb12019-01-30 20:23:16 +1300341
Simon Glassb2e83c62021-12-18 14:54:31 -0700342def cleanup_one_header(header_path, patterns, args):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900343 """Clean regex-matched lines away from a file.
344
345 Arguments:
346 header_path: path to the cleaned file.
347 patterns: list of regex patterns. Any lines matching to these
348 patterns are deleted.
Simon Glassb2e83c62021-12-18 14:54:31 -0700349 args: program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900350 """
Simon Glass37f815c2021-12-18 14:54:34 -0700351 lines = read_file(header_path, skip_unicode=True)
352 if lines is None:
353 return
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900354
355 matched = []
356 for i, line in enumerate(lines):
Masahiro Yamadaa3a779f2016-07-25 19:15:27 +0900357 if i - 1 in matched and lines[i - 1][-2:] == '\\\n':
358 matched.append(i)
359 continue
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900360 for pattern in patterns:
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900361 if pattern.search(line):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900362 matched.append(i)
363 break
364
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900365 if not matched:
366 return
367
368 # remove empty #ifdef ... #endif, successive blank lines
369 pattern_if = re.compile(r'#\s*if(def|ndef)?\W') # #if, #ifdef, #ifndef
370 pattern_elif = re.compile(r'#\s*el(if|se)\W') # #elif, #else
371 pattern_endif = re.compile(r'#\s*endif\W') # #endif
372 pattern_blank = re.compile(r'^\s*$') # empty line
373
374 while True:
375 old_matched = copy.copy(matched)
376 extend_matched_lines(lines, matched, [pattern_if],
377 [pattern_endif], True, True)
378 extend_matched_lines(lines, matched, [pattern_elif],
379 [pattern_elif, pattern_endif], True, False)
380 extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
381 [pattern_blank], False, True)
382 extend_matched_lines(lines, matched, [pattern_blank],
383 [pattern_elif, pattern_endif], True, False)
384 extend_matched_lines(lines, matched, [pattern_blank],
385 [pattern_blank], True, False)
386 if matched == old_matched:
387 break
388
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900389 tolines = copy.copy(lines)
390
391 for i in reversed(matched):
392 tolines.pop(i)
393
Simon Glassb2e83c62021-12-18 14:54:31 -0700394 show_diff(lines, tolines, header_path, args.color)
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900395
Simon Glassb2e83c62021-12-18 14:54:31 -0700396 if args.dry_run:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900397 return
398
Simon Glass2fd85bd2021-12-18 14:54:33 -0700399 write_file(header_path, tolines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900400
Simon Glassb2e83c62021-12-18 14:54:31 -0700401def cleanup_headers(configs, args):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900402 """Delete config defines from board headers.
403
404 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900405 configs: A list of CONFIGs to remove.
Simon Glassb2e83c62021-12-18 14:54:31 -0700406 args: program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900407 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700408 if not confirm(args, 'Clean up headers?'):
Chris Packham85edfc12017-05-02 21:30:46 +1200409 return
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900410
411 patterns = []
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900412 for config in configs:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900413 patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
414 patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
415
Joe Hershberger60727f52015-05-19 13:21:21 -0500416 for dir in 'include', 'arch', 'board':
417 for (dirpath, dirnames, filenames) in os.walk(dir):
Masahiro Yamadadc6de502016-07-25 19:15:22 +0900418 if dirpath == os.path.join('include', 'generated'):
419 continue
Joe Hershberger60727f52015-05-19 13:21:21 -0500420 for filename in filenames:
Simon Glassa38cc172020-08-11 11:23:34 -0600421 if not filename.endswith(('~', '.dts', '.dtsi', '.bin',
Trevor Woernerdc514d72021-03-15 12:01:33 -0400422 '.elf','.aml','.dat')):
Chris Packham4d9dbb12019-01-30 20:23:16 +1300423 header_path = os.path.join(dirpath, filename)
Tom Rini02b56702019-11-10 21:19:37 -0500424 # This file contains UTF-16 data and no CONFIG symbols
425 if header_path == 'include/video_font_data.h':
426 continue
Simon Glassb2e83c62021-12-18 14:54:31 -0700427 cleanup_one_header(header_path, patterns, args)
428 cleanup_empty_blocks(header_path, args)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900429
Simon Glassb2e83c62021-12-18 14:54:31 -0700430def cleanup_one_extra_option(defconfig_path, configs, args):
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900431 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file.
432
433 Arguments:
434 defconfig_path: path to the cleaned defconfig file.
435 configs: A list of CONFIGs to remove.
Simon Glassb2e83c62021-12-18 14:54:31 -0700436 args: program arguments
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900437 """
438
439 start = 'CONFIG_SYS_EXTRA_OPTIONS="'
440 end = '"\n'
441
Simon Glass37f815c2021-12-18 14:54:34 -0700442 lines = read_file(defconfig_path)
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900443
444 for i, line in enumerate(lines):
445 if line.startswith(start) and line.endswith(end):
446 break
447 else:
448 # CONFIG_SYS_EXTRA_OPTIONS was not found in this defconfig
449 return
450
451 old_tokens = line[len(start):-len(end)].split(',')
452 new_tokens = []
453
454 for token in old_tokens:
455 pos = token.find('=')
456 if not (token[:pos] if pos >= 0 else token) in configs:
457 new_tokens.append(token)
458
459 if new_tokens == old_tokens:
460 return
461
462 tolines = copy.copy(lines)
463
464 if new_tokens:
465 tolines[i] = start + ','.join(new_tokens) + end
466 else:
467 tolines.pop(i)
468
Simon Glassb2e83c62021-12-18 14:54:31 -0700469 show_diff(lines, tolines, defconfig_path, args.color)
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900470
Simon Glassb2e83c62021-12-18 14:54:31 -0700471 if args.dry_run:
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900472 return
473
Simon Glass2fd85bd2021-12-18 14:54:33 -0700474 write_file(defconfig_path, tolines)
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900475
Simon Glassb2e83c62021-12-18 14:54:31 -0700476def cleanup_extra_options(configs, args):
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900477 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files.
478
479 Arguments:
480 configs: A list of CONFIGs to remove.
Simon Glassb2e83c62021-12-18 14:54:31 -0700481 args: program arguments
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900482 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700483 if not confirm(args, 'Clean up CONFIG_SYS_EXTRA_OPTIONS?'):
Chris Packham85edfc12017-05-02 21:30:46 +1200484 return
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900485
486 configs = [ config[len('CONFIG_'):] for config in configs ]
487
488 defconfigs = get_all_defconfigs()
489
490 for defconfig in defconfigs:
491 cleanup_one_extra_option(os.path.join('configs', defconfig), configs,
Simon Glassb2e83c62021-12-18 14:54:31 -0700492 args)
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900493
Simon Glassb2e83c62021-12-18 14:54:31 -0700494def cleanup_whitelist(configs, args):
Chris Packhamca438342017-05-02 21:30:47 +1200495 """Delete config whitelist entries
496
497 Arguments:
498 configs: A list of CONFIGs to remove.
Simon Glassb2e83c62021-12-18 14:54:31 -0700499 args: program arguments
Chris Packhamca438342017-05-02 21:30:47 +1200500 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700501 if not confirm(args, 'Clean up whitelist entries?'):
Chris Packhamca438342017-05-02 21:30:47 +1200502 return
503
Simon Glass37f815c2021-12-18 14:54:34 -0700504 lines = read_file(os.path.join('scripts', 'config_whitelist.txt'))
Chris Packhamca438342017-05-02 21:30:47 +1200505
506 lines = [x for x in lines if x.strip() not in configs]
507
Simon Glass2fd85bd2021-12-18 14:54:33 -0700508 write_file(os.path.join('scripts', 'config_whitelist.txt'), lines)
Chris Packhamca438342017-05-02 21:30:47 +1200509
Chris Packhamf90df592017-05-02 21:30:48 +1200510def find_matching(patterns, line):
511 for pat in patterns:
512 if pat.search(line):
513 return True
514 return False
515
Simon Glassb2e83c62021-12-18 14:54:31 -0700516def cleanup_readme(configs, args):
Chris Packhamf90df592017-05-02 21:30:48 +1200517 """Delete config description in README
518
519 Arguments:
520 configs: A list of CONFIGs to remove.
Simon Glassb2e83c62021-12-18 14:54:31 -0700521 args: program arguments
Chris Packhamf90df592017-05-02 21:30:48 +1200522 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700523 if not confirm(args, 'Clean up README?'):
Chris Packhamf90df592017-05-02 21:30:48 +1200524 return
525
526 patterns = []
527 for config in configs:
528 patterns.append(re.compile(r'^\s+%s' % config))
529
Simon Glass37f815c2021-12-18 14:54:34 -0700530 lines = read_file('README')
Chris Packhamf90df592017-05-02 21:30:48 +1200531
532 found = False
533 newlines = []
534 for line in lines:
535 if not found:
536 found = find_matching(patterns, line)
537 if found:
538 continue
539
540 if found and re.search(r'^\s+CONFIG', line):
541 found = False
542
543 if not found:
544 newlines.append(line)
545
Simon Glass2fd85bd2021-12-18 14:54:33 -0700546 write_file('README', newlines)
Chris Packhamf90df592017-05-02 21:30:48 +1200547
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200548def try_expand(line):
549 """If value looks like an expression, try expanding it
550 Otherwise just return the existing value
551 """
552 if line.find('=') == -1:
553 return line
554
555 try:
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +0100556 aeval = asteval.Interpreter( usersyms=SIZES, minimal=True )
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200557 cfg, val = re.split("=", line)
558 val= val.strip('\"')
Simon Glassdaa694d2021-12-18 14:54:30 -0700559 if re.search(r'[*+-/]|<<|SZ_+|\(([^\)]+)\)', val):
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +0100560 newval = hex(aeval(val))
Simon Glassdaa694d2021-12-18 14:54:30 -0700561 print('\tExpanded expression %s to %s' % (val, newval))
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200562 return cfg+'='+newval
563 except:
Simon Glassdaa694d2021-12-18 14:54:30 -0700564 print('\tFailed to expand expression in %s' % line)
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200565
566 return line
567
Chris Packhamca438342017-05-02 21:30:47 +1200568
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900569### classes ###
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900570class Progress:
571
572 """Progress Indicator"""
573
574 def __init__(self, total):
575 """Create a new progress indicator.
576
577 Arguments:
578 total: A number of defconfig files to process.
579 """
580 self.current = 0
581 self.total = total
582
583 def inc(self):
584 """Increment the number of processed defconfig files."""
585
586 self.current += 1
587
588 def show(self):
589 """Display the progress."""
Simon Glass793dca32019-10-31 07:42:57 -0600590 print(' %d defconfigs out of %d\r' % (self.current, self.total), end=' ')
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900591 sys.stdout.flush()
592
Simon Glasscb008832017-06-15 21:39:33 -0600593
594class KconfigScanner:
595 """Kconfig scanner."""
596
597 def __init__(self):
598 """Scan all the Kconfig files and create a Config object."""
599 # Define environment variables referenced from Kconfig
600 os.environ['srctree'] = os.getcwd()
601 os.environ['UBOOTVERSION'] = 'dummy'
602 os.environ['KCONFIG_OBJDIR'] = ''
Tom Rini65e05dd2019-09-20 17:42:09 -0400603 self.conf = kconfiglib.Kconfig()
Simon Glasscb008832017-06-15 21:39:33 -0600604
605
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900606class KconfigParser:
607
608 """A parser of .config and include/autoconf.mk."""
609
610 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
611 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
612
Simon Glassb2e83c62021-12-18 14:54:31 -0700613 def __init__(self, configs, args, build_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900614 """Create a new parser.
615
616 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900617 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -0700618 args: program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900619 build_dir: Build directory.
620 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900621 self.configs = configs
Simon Glassb2e83c62021-12-18 14:54:31 -0700622 self.args = args
Masahiro Yamada1f169922016-05-19 15:52:00 +0900623 self.dotconfig = os.path.join(build_dir, '.config')
624 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
Masahiro Yamada07913d12016-08-22 22:18:22 +0900625 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
626 'autoconf.mk')
Simon Glassf3b8e642017-06-01 19:39:01 -0600627 self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900628 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900629
Simon Glass6821a742017-07-10 14:47:47 -0600630 def get_arch(self):
631 """Parse .config file and return the architecture.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900632
633 Returns:
Simon Glass6821a742017-07-10 14:47:47 -0600634 Architecture name (e.g. 'arm').
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900635 """
636 arch = ''
637 cpu = ''
Simon Glass37f815c2021-12-18 14:54:34 -0700638 for line in read_file(self.dotconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900639 m = self.re_arch.match(line)
640 if m:
641 arch = m.group(1)
642 continue
643 m = self.re_cpu.match(line)
644 if m:
645 cpu = m.group(1)
646
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900647 if not arch:
648 return None
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900649
650 # fix-up for aarch64
651 if arch == 'arm' and cpu == 'armv8':
652 arch = 'aarch64'
653
Simon Glass6821a742017-07-10 14:47:47 -0600654 return arch
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900655
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900656 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900657 """Parse .config, defconfig, include/autoconf.mk for one config.
658
659 This function looks for the config options in the lines from
660 defconfig, .config, and include/autoconf.mk in order to decide
661 which action should be taken for this defconfig.
662
663 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900664 config: CONFIG name to parse.
Masahiro Yamadacc008292016-05-19 15:51:56 +0900665 dotconfig_lines: lines from the .config file.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900666 autoconf_lines: lines from the include/autoconf.mk file.
667
668 Returns:
669 A tupple of the action for this defconfig and the line
670 matched for the config.
671 """
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900672 not_set = '# %s is not set' % config
673
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900674 for line in autoconf_lines:
675 line = line.rstrip()
676 if line.startswith(config + '='):
Masahiro Yamadacc008292016-05-19 15:51:56 +0900677 new_val = line
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900678 break
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900679 else:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900680 new_val = not_set
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900681
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200682 new_val = try_expand(new_val)
683
Masahiro Yamada916224c2016-08-22 22:18:21 +0900684 for line in dotconfig_lines:
685 line = line.rstrip()
686 if line.startswith(config + '=') or line == not_set:
687 old_val = line
688 break
689 else:
690 if new_val == not_set:
691 return (ACTION_NO_ENTRY, config)
692 else:
693 return (ACTION_NO_ENTRY_WARN, config)
694
Masahiro Yamadacc008292016-05-19 15:51:56 +0900695 # If this CONFIG is neither bool nor trisate
696 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
697 # tools/scripts/define2mk.sed changes '1' to 'y'.
698 # This is a problem if the CONFIG is int type.
699 # Check the type in Kconfig and handle it correctly.
700 if new_val[-2:] == '=y':
701 new_val = new_val[:-1] + '1'
702
Masahiro Yamada50301592016-06-15 14:33:50 +0900703 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
704 new_val)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900705
Masahiro Yamada1d085562016-05-19 15:52:02 +0900706 def update_dotconfig(self):
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900707 """Parse files for the config options and update the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900708
Masahiro Yamadacc008292016-05-19 15:51:56 +0900709 This function parses the generated .config and include/autoconf.mk
710 searching the target options.
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900711 Move the config option(s) to the .config as needed.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900712
713 Arguments:
714 defconfig: defconfig name.
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900715
716 Returns:
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900717 Return a tuple of (updated flag, log string).
718 The "updated flag" is True if the .config was updated, False
719 otherwise. The "log string" shows what happend to the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900720 """
721
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900722 results = []
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900723 updated = False
Masahiro Yamada916224c2016-08-22 22:18:21 +0900724 suspicious = False
Masahiro Yamada07913d12016-08-22 22:18:22 +0900725 rm_files = [self.config_autoconf, self.autoconf]
726
Simon Glassb2e83c62021-12-18 14:54:31 -0700727 if self.args.spl:
Masahiro Yamada07913d12016-08-22 22:18:22 +0900728 if os.path.exists(self.spl_autoconf):
729 autoconf_path = self.spl_autoconf
730 rm_files.append(self.spl_autoconf)
731 else:
732 for f in rm_files:
733 os.remove(f)
734 return (updated, suspicious,
Simon Glassb2e83c62021-12-18 14:54:31 -0700735 color_text(self.args.color, COLOR_BROWN,
Masahiro Yamada07913d12016-08-22 22:18:22 +0900736 "SPL is not enabled. Skipped.") + '\n')
737 else:
738 autoconf_path = self.autoconf
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900739
Simon Glass37f815c2021-12-18 14:54:34 -0700740 dotconfig_lines = read_file(self.dotconfig)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900741
Simon Glass37f815c2021-12-18 14:54:34 -0700742 autoconf_lines = read_file(autoconf_path)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900743
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900744 for config in self.configs:
745 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger96464ba2015-05-19 13:21:17 -0500746 autoconf_lines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900747 results.append(result)
748
749 log = ''
750
751 for (action, value) in results:
752 if action == ACTION_MOVE:
753 actlog = "Move '%s'" % value
754 log_color = COLOR_LIGHT_GREEN
Masahiro Yamadacc008292016-05-19 15:51:56 +0900755 elif action == ACTION_NO_ENTRY:
Simon Glassdaa694d2021-12-18 14:54:30 -0700756 actlog = '%s is not defined in Kconfig. Do nothing.' % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900757 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada916224c2016-08-22 22:18:21 +0900758 elif action == ACTION_NO_ENTRY_WARN:
Simon Glassdaa694d2021-12-18 14:54:30 -0700759 actlog = '%s is not defined in Kconfig (suspicious). Do nothing.' % value
Masahiro Yamada916224c2016-08-22 22:18:21 +0900760 log_color = COLOR_YELLOW
761 suspicious = True
Masahiro Yamadacc008292016-05-19 15:51:56 +0900762 elif action == ACTION_NO_CHANGE:
763 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
764 % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900765 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada07913d12016-08-22 22:18:22 +0900766 elif action == ACTION_SPL_NOT_EXIST:
Simon Glassdaa694d2021-12-18 14:54:30 -0700767 actlog = 'SPL is not enabled for this defconfig. Skip.'
Masahiro Yamada07913d12016-08-22 22:18:22 +0900768 log_color = COLOR_PURPLE
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900769 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700770 sys.exit('Internal Error. This should not happen.')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900771
Simon Glassb2e83c62021-12-18 14:54:31 -0700772 log += color_text(self.args.color, log_color, actlog) + '\n'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900773
Masahiro Yamada1f169922016-05-19 15:52:00 +0900774 with open(self.dotconfig, 'a') as f:
Masahiro Yamadae423d172016-05-19 15:51:49 +0900775 for (action, value) in results:
776 if action == ACTION_MOVE:
777 f.write(value + '\n')
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900778 updated = True
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900779
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900780 self.results = results
Masahiro Yamada07913d12016-08-22 22:18:22 +0900781 for f in rm_files:
782 os.remove(f)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900783
Masahiro Yamada916224c2016-08-22 22:18:21 +0900784 return (updated, suspicious, log)
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900785
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900786 def check_defconfig(self):
787 """Check the defconfig after savedefconfig
788
789 Returns:
790 Return additional log if moved CONFIGs were removed again by
791 'make savedefconfig'.
792 """
793
794 log = ''
795
Simon Glass37f815c2021-12-18 14:54:34 -0700796 defconfig_lines = read_file(self.defconfig)
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900797
798 for (action, value) in self.results:
799 if action != ACTION_MOVE:
800 continue
801 if not value + '\n' in defconfig_lines:
Simon Glassb2e83c62021-12-18 14:54:31 -0700802 log += color_text(self.args.color, COLOR_YELLOW,
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900803 "'%s' was removed by savedefconfig.\n" %
804 value)
805
806 return log
807
Simon Glassd73fcb12017-06-01 19:39:02 -0600808
809class DatabaseThread(threading.Thread):
810 """This thread processes results from Slot threads.
811
812 It collects the data in the master config directary. There is only one
813 result thread, and this helps to serialise the build output.
814 """
815 def __init__(self, config_db, db_queue):
816 """Set up a new result thread
817
818 Args:
819 builder: Builder which will be sent each result
820 """
821 threading.Thread.__init__(self)
822 self.config_db = config_db
823 self.db_queue= db_queue
824
825 def run(self):
826 """Called to start up the result thread.
827
828 We collect the next result job and pass it on to the build.
829 """
830 while True:
831 defconfig, configs = self.db_queue.get()
832 self.config_db[defconfig] = configs
833 self.db_queue.task_done()
834
835
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900836class Slot:
837
838 """A slot to store a subprocess.
839
840 Each instance of this class handles one subprocess.
841 This class is useful to control multiple threads
842 for faster processing.
843 """
844
Simon Glassb2e83c62021-12-18 14:54:31 -0700845 def __init__(self, toolchains, configs, args, progress, devnull,
Simon Glass6821a742017-07-10 14:47:47 -0600846 make_cmd, reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900847 """Create a new process slot.
848
849 Arguments:
Simon Glass6821a742017-07-10 14:47:47 -0600850 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900851 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -0700852 args: Program arguments
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900853 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900854 devnull: A file object of '/dev/null'.
855 make_cmd: command name of GNU Make.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500856 reference_src_dir: Determine the true starting config state from this
857 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -0600858 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900859 """
Simon Glass6821a742017-07-10 14:47:47 -0600860 self.toolchains = toolchains
Simon Glassb2e83c62021-12-18 14:54:31 -0700861 self.args = args
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900862 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900863 self.build_dir = tempfile.mkdtemp()
864 self.devnull = devnull
865 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500866 self.reference_src_dir = reference_src_dir
Simon Glassd73fcb12017-06-01 19:39:02 -0600867 self.db_queue = db_queue
Simon Glassb2e83c62021-12-18 14:54:31 -0700868 self.parser = KconfigParser(configs, args, self.build_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900869 self.state = STATE_IDLE
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900870 self.failed_boards = set()
871 self.suspicious_boards = set()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900872
873 def __del__(self):
874 """Delete the working directory
875
876 This function makes sure the temporary directory is cleaned away
877 even if Python suddenly dies due to error. It should be done in here
Joe Hershbergerf2dae752016-06-10 14:53:29 -0500878 because it is guaranteed the destructor is always invoked when the
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900879 instance of the class gets unreferenced.
880
881 If the subprocess is still running, wait until it finishes.
882 """
883 if self.state != STATE_IDLE:
884 while self.ps.poll() == None:
885 pass
886 shutil.rmtree(self.build_dir)
887
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900888 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900889 """Assign a new subprocess for defconfig and add it to the slot.
890
891 If the slot is vacant, create a new subprocess for processing the
892 given defconfig and add it to the slot. Just returns False if
893 the slot is occupied (i.e. the current subprocess is still running).
894
895 Arguments:
896 defconfig: defconfig name.
897
898 Returns:
899 Return True on success or False on failure
900 """
901 if self.state != STATE_IDLE:
902 return False
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900903
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900904 self.defconfig = defconfig
Masahiro Yamada1d085562016-05-19 15:52:02 +0900905 self.log = ''
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900906 self.current_src_dir = self.reference_src_dir
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900907 self.do_defconfig()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900908 return True
909
910 def poll(self):
911 """Check the status of the subprocess and handle it as needed.
912
913 Returns True if the slot is vacant (i.e. in idle state).
914 If the configuration is successfully finished, assign a new
915 subprocess to build include/autoconf.mk.
916 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900917 parse the .config and the include/autoconf.mk, moving
918 config options to the .config as needed.
919 If the .config was updated, run "make savedefconfig" to sync
920 it, update the original defconfig, and then set the slot back
921 to the idle state.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900922
923 Returns:
924 Return True if the subprocess is terminated, False otherwise
925 """
926 if self.state == STATE_IDLE:
927 return True
928
929 if self.ps.poll() == None:
930 return False
931
932 if self.ps.poll() != 0:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900933 self.handle_error()
934 elif self.state == STATE_DEFCONFIG:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900935 if self.reference_src_dir and not self.current_src_dir:
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500936 self.do_savedefconfig()
937 else:
938 self.do_autoconf()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900939 elif self.state == STATE_AUTOCONF:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900940 if self.current_src_dir:
941 self.current_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500942 self.do_defconfig()
Simon Glassb2e83c62021-12-18 14:54:31 -0700943 elif self.args.build_db:
Simon Glassd73fcb12017-06-01 19:39:02 -0600944 self.do_build_db()
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500945 else:
946 self.do_savedefconfig()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900947 elif self.state == STATE_SAVEDEFCONFIG:
948 self.update_defconfig()
949 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700950 sys.exit('Internal Error. This should not happen.')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900951
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900952 return True if self.state == STATE_IDLE else False
Joe Hershberger96464ba2015-05-19 13:21:17 -0500953
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900954 def handle_error(self):
955 """Handle error cases."""
Masahiro Yamada8513dc02016-05-19 15:52:08 +0900956
Simon Glassb2e83c62021-12-18 14:54:31 -0700957 self.log += color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glassdaa694d2021-12-18 14:54:30 -0700958 'Failed to process.\n')
Simon Glassb2e83c62021-12-18 14:54:31 -0700959 if self.args.verbose:
960 self.log += color_text(self.args.color, COLOR_LIGHT_CYAN,
Markus Klotzbuecher4f5c5e92020-02-12 20:46:45 +0100961 self.ps.stderr.read().decode())
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900962 self.finish(False)
Joe Hershberger96464ba2015-05-19 13:21:17 -0500963
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900964 def do_defconfig(self):
965 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900966
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900967 cmd = list(self.make_cmd)
968 cmd.append(self.defconfig)
969 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900970 stderr=subprocess.PIPE,
971 cwd=self.current_src_dir)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900972 self.state = STATE_DEFCONFIG
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900973
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900974 def do_autoconf(self):
Simon Glassf3b8e642017-06-01 19:39:01 -0600975 """Run 'make AUTO_CONF_PATH'."""
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900976
Simon Glass6821a742017-07-10 14:47:47 -0600977 arch = self.parser.get_arch()
978 try:
979 toolchain = self.toolchains.Select(arch)
980 except ValueError:
Simon Glassb2e83c62021-12-18 14:54:31 -0700981 self.log += color_text(self.args.color, COLOR_YELLOW,
Chris Packhamce3ba452017-08-27 20:00:51 +1200982 "Tool chain for '%s' is missing. Do nothing.\n" % arch)
Masahiro Yamada4efef992016-05-19 15:52:03 +0900983 self.finish(False)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900984 return
Simon Glass793dca32019-10-31 07:42:57 -0600985 env = toolchain.MakeEnvironment(False)
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900986
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900987 cmd = list(self.make_cmd)
Joe Hershberger7740f652015-05-19 13:21:18 -0500988 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Simon Glassf3b8e642017-06-01 19:39:01 -0600989 cmd.append(AUTO_CONF_PATH)
Simon Glass6821a742017-07-10 14:47:47 -0600990 self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900991 stderr=subprocess.PIPE,
992 cwd=self.current_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900993 self.state = STATE_AUTOCONF
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900994
Simon Glassd73fcb12017-06-01 19:39:02 -0600995 def do_build_db(self):
996 """Add the board to the database"""
997 configs = {}
Simon Glass37f815c2021-12-18 14:54:34 -0700998 for line in read_file(os.path.join(self.build_dir, AUTO_CONF_PATH)):
999 if line.startswith('CONFIG'):
1000 config, value = line.split('=', 1)
1001 configs[config] = value.rstrip()
Simon Glassd73fcb12017-06-01 19:39:02 -06001002 self.db_queue.put([self.defconfig, configs])
1003 self.finish(True)
1004
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001005 def do_savedefconfig(self):
1006 """Update the .config and run 'make savedefconfig'."""
1007
Masahiro Yamada916224c2016-08-22 22:18:21 +09001008 (updated, suspicious, log) = self.parser.update_dotconfig()
1009 if suspicious:
1010 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001011 self.log += log
1012
Simon Glassb2e83c62021-12-18 14:54:31 -07001013 if not self.args.force_sync and not updated:
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001014 self.finish(True)
1015 return
1016 if updated:
Simon Glassb2e83c62021-12-18 14:54:31 -07001017 self.log += color_text(self.args.color, COLOR_LIGHT_GREEN,
Simon Glassdaa694d2021-12-18 14:54:30 -07001018 'Syncing by savedefconfig...\n')
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001019 else:
Simon Glassdaa694d2021-12-18 14:54:30 -07001020 self.log += 'Syncing by savedefconfig (forced by option)...\n'
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001021
1022 cmd = list(self.make_cmd)
1023 cmd.append('savedefconfig')
1024 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
1025 stderr=subprocess.PIPE)
1026 self.state = STATE_SAVEDEFCONFIG
1027
1028 def update_defconfig(self):
1029 """Update the input defconfig and go back to the idle state."""
1030
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001031 log = self.parser.check_defconfig()
1032 if log:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001033 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001034 self.log += log
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001035 orig_defconfig = os.path.join('configs', self.defconfig)
1036 new_defconfig = os.path.join(self.build_dir, 'defconfig')
1037 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
1038
1039 if updated:
Simon Glassb2e83c62021-12-18 14:54:31 -07001040 self.log += color_text(self.args.color, COLOR_LIGHT_BLUE,
Simon Glassdaa694d2021-12-18 14:54:30 -07001041 'defconfig was updated.\n')
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001042
Simon Glassb2e83c62021-12-18 14:54:31 -07001043 if not self.args.dry_run and updated:
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001044 shutil.move(new_defconfig, orig_defconfig)
1045 self.finish(True)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001046
Masahiro Yamada4efef992016-05-19 15:52:03 +09001047 def finish(self, success):
1048 """Display log along with progress and go to the idle state.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001049
1050 Arguments:
Masahiro Yamada4efef992016-05-19 15:52:03 +09001051 success: Should be True when the defconfig was processed
1052 successfully, or False when it fails.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001053 """
1054 # output at least 30 characters to hide the "* defconfigs out of *".
1055 log = self.defconfig.ljust(30) + '\n'
1056
1057 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
1058 # Some threads are running in parallel.
1059 # Print log atomically to not mix up logs from different threads.
Simon Glass793dca32019-10-31 07:42:57 -06001060 print(log, file=(sys.stdout if success else sys.stderr))
Masahiro Yamada4efef992016-05-19 15:52:03 +09001061
1062 if not success:
Simon Glassb2e83c62021-12-18 14:54:31 -07001063 if self.args.exit_on_error:
Simon Glassdaa694d2021-12-18 14:54:30 -07001064 sys.exit('Exit on error.')
Masahiro Yamada4efef992016-05-19 15:52:03 +09001065 # If --exit-on-error flag is not set, skip this board and continue.
1066 # Record the failed board.
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001067 self.failed_boards.add(self.defconfig)
Masahiro Yamada4efef992016-05-19 15:52:03 +09001068
Masahiro Yamada1d085562016-05-19 15:52:02 +09001069 self.progress.inc()
1070 self.progress.show()
Masahiro Yamada4efef992016-05-19 15:52:03 +09001071 self.state = STATE_IDLE
Masahiro Yamada1d085562016-05-19 15:52:02 +09001072
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001073 def get_failed_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001074 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001075 """
1076 return self.failed_boards
1077
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001078 def get_suspicious_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001079 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001080 """
Masahiro Yamada916224c2016-08-22 22:18:21 +09001081 return self.suspicious_boards - self.failed_boards
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001082
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001083class Slots:
1084
1085 """Controller of the array of subprocess slots."""
1086
Simon Glassb2e83c62021-12-18 14:54:31 -07001087 def __init__(self, toolchains, configs, args, progress,
Simon Glass6821a742017-07-10 14:47:47 -06001088 reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001089 """Create a new slots controller.
1090
1091 Arguments:
Simon Glass6821a742017-07-10 14:47:47 -06001092 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001093 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -07001094 args: Program arguments
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001095 progress: A progress indicator.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001096 reference_src_dir: Determine the true starting config state from this
1097 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -06001098 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001099 """
Simon Glassb2e83c62021-12-18 14:54:31 -07001100 self.args = args
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001101 self.slots = []
Simon Glass478920d2021-12-18 14:54:32 -07001102 devnull = subprocess.DEVNULL
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001103 make_cmd = get_make_cmd()
Simon Glassb2e83c62021-12-18 14:54:31 -07001104 for i in range(args.jobs):
1105 self.slots.append(Slot(toolchains, configs, args, progress,
Simon Glass6821a742017-07-10 14:47:47 -06001106 devnull, make_cmd, reference_src_dir,
1107 db_queue))
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001108
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001109 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001110 """Add a new subprocess if a vacant slot is found.
1111
1112 Arguments:
1113 defconfig: defconfig name to be put into.
1114
1115 Returns:
1116 Return True on success or False on failure
1117 """
1118 for slot in self.slots:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001119 if slot.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001120 return True
1121 return False
1122
1123 def available(self):
1124 """Check if there is a vacant slot.
1125
1126 Returns:
1127 Return True if at lease one vacant slot is found, False otherwise.
1128 """
1129 for slot in self.slots:
1130 if slot.poll():
1131 return True
1132 return False
1133
1134 def empty(self):
1135 """Check if all slots are vacant.
1136
1137 Returns:
1138 Return True if all the slots are vacant, False otherwise.
1139 """
1140 ret = True
1141 for slot in self.slots:
1142 if not slot.poll():
1143 ret = False
1144 return ret
1145
1146 def show_failed_boards(self):
1147 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001148 boards = set()
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001149 output_file = 'moveconfig.failed'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001150
1151 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001152 boards |= slot.get_failed_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001153
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001154 if boards:
1155 boards = '\n'.join(boards) + '\n'
Simon Glassdaa694d2021-12-18 14:54:30 -07001156 msg = 'The following boards were not processed due to error:\n'
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001157 msg += boards
Simon Glassdaa694d2021-12-18 14:54:30 -07001158 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassb2e83c62021-12-18 14:54:31 -07001159 print(color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glass793dca32019-10-31 07:42:57 -06001160 msg), file=sys.stderr)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001161
Simon Glass2fd85bd2021-12-18 14:54:33 -07001162 write_file(output_file, boards)
Joe Hershberger2559cd82015-05-19 13:21:22 -05001163
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001164 def show_suspicious_boards(self):
1165 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001166 boards = set()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001167 output_file = 'moveconfig.suspicious'
1168
1169 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001170 boards |= slot.get_suspicious_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001171
1172 if boards:
1173 boards = '\n'.join(boards) + '\n'
Simon Glassdaa694d2021-12-18 14:54:30 -07001174 msg = 'The following boards might have been converted incorrectly.\n'
1175 msg += 'It is highly recommended to check them manually:\n'
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001176 msg += boards
Simon Glassdaa694d2021-12-18 14:54:30 -07001177 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassb2e83c62021-12-18 14:54:31 -07001178 print(color_text(self.args.color, COLOR_YELLOW,
Simon Glass793dca32019-10-31 07:42:57 -06001179 msg), file=sys.stderr)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001180
Simon Glass2fd85bd2021-12-18 14:54:33 -07001181 write_file(output_file, boards)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001182
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001183class ReferenceSource:
1184
1185 """Reference source against which original configs should be parsed."""
1186
1187 def __init__(self, commit):
1188 """Create a reference source directory based on a specified commit.
1189
1190 Arguments:
1191 commit: commit to git-clone
1192 """
1193 self.src_dir = tempfile.mkdtemp()
Simon Glassdaa694d2021-12-18 14:54:30 -07001194 print('Cloning git repo to a separate work directory...')
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001195 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1196 cwd=self.src_dir)
Simon Glass793dca32019-10-31 07:42:57 -06001197 print("Checkout '%s' to build the original autoconf.mk." % \
1198 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip())
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001199 subprocess.check_output(['git', 'checkout', commit],
1200 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001201
1202 def __del__(self):
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001203 """Delete the reference source directory
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001204
1205 This function makes sure the temporary directory is cleaned away
1206 even if Python suddenly dies due to error. It should be done in here
1207 because it is guaranteed the destructor is always invoked when the
1208 instance of the class gets unreferenced.
1209 """
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001210 shutil.rmtree(self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001211
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001212 def get_dir(self):
1213 """Return the absolute path to the reference source directory."""
1214
1215 return self.src_dir
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001216
Simon Glassb2e83c62021-12-18 14:54:31 -07001217def move_config(toolchains, configs, args, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001218 """Move config options to defconfig files.
1219
1220 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001221 configs: A list of CONFIGs to move.
Simon Glassb2e83c62021-12-18 14:54:31 -07001222 args: Program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001223 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001224 if len(configs) == 0:
Simon Glassb2e83c62021-12-18 14:54:31 -07001225 if args.force_sync:
Simon Glass793dca32019-10-31 07:42:57 -06001226 print('No CONFIG is specified. You are probably syncing defconfigs.', end=' ')
Simon Glassb2e83c62021-12-18 14:54:31 -07001227 elif args.build_db:
Simon Glass793dca32019-10-31 07:42:57 -06001228 print('Building %s database' % CONFIG_DATABASE)
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001229 else:
Simon Glass793dca32019-10-31 07:42:57 -06001230 print('Neither CONFIG nor --force-sync is specified. Nothing will happen.', end=' ')
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001231 else:
Simon Glass793dca32019-10-31 07:42:57 -06001232 print('Move ' + ', '.join(configs), end=' ')
Simon Glassb2e83c62021-12-18 14:54:31 -07001233 print('(jobs: %d)\n' % args.jobs)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001234
Simon Glassb2e83c62021-12-18 14:54:31 -07001235 if args.git_ref:
1236 reference_src = ReferenceSource(args.git_ref)
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001237 reference_src_dir = reference_src.get_dir()
1238 else:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001239 reference_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001240
Simon Glassb2e83c62021-12-18 14:54:31 -07001241 if args.defconfigs:
1242 defconfigs = get_matched_defconfigs(args.defconfigs)
Joe Hershberger91040e82015-05-19 13:21:19 -05001243 else:
Masahiro Yamada684c3062016-07-25 19:15:28 +09001244 defconfigs = get_all_defconfigs()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001245
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001246 progress = Progress(len(defconfigs))
Simon Glassb2e83c62021-12-18 14:54:31 -07001247 slots = Slots(toolchains, configs, args, progress, reference_src_dir,
Simon Glass6821a742017-07-10 14:47:47 -06001248 db_queue)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001249
1250 # Main loop to process defconfig files:
1251 # Add a new subprocess into a vacant slot.
1252 # Sleep if there is no available slot.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001253 for defconfig in defconfigs:
1254 while not slots.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001255 while not slots.available():
1256 # No available slot: sleep for a while
1257 time.sleep(SLEEP_TIME)
1258
1259 # wait until all the subprocesses finish
1260 while not slots.empty():
1261 time.sleep(SLEEP_TIME)
1262
Simon Glass793dca32019-10-31 07:42:57 -06001263 print('')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001264 slots.show_failed_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001265 slots.show_suspicious_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001266
Simon Glasscb008832017-06-15 21:39:33 -06001267def find_kconfig_rules(kconf, config, imply_config):
1268 """Check whether a config has a 'select' or 'imply' keyword
1269
1270 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -04001271 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -06001272 config: Name of config to check (without CONFIG_ prefix)
1273 imply_config: Implying config (without CONFIG_ prefix) which may or
1274 may not have an 'imply' for 'config')
1275
1276 Returns:
1277 Symbol object for 'config' if found, else None
1278 """
Tom Rini65e05dd2019-09-20 17:42:09 -04001279 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -06001280 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001281 for sel, cond in (sym.selects + sym.implies):
Simon Glassa3627082021-12-18 08:09:42 -07001282 if sel.name == config:
Simon Glasscb008832017-06-15 21:39:33 -06001283 return sym
1284 return None
1285
1286def check_imply_rule(kconf, config, imply_config):
1287 """Check if we can add an 'imply' option
1288
1289 This finds imply_config in the Kconfig and looks to see if it is possible
1290 to add an 'imply' for 'config' to that part of the Kconfig.
1291
1292 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -04001293 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -06001294 config: Name of config to check (without CONFIG_ prefix)
1295 imply_config: Implying config (without CONFIG_ prefix) which may or
1296 may not have an 'imply' for 'config')
1297
1298 Returns:
1299 tuple:
1300 filename of Kconfig file containing imply_config, or None if none
1301 line number within the Kconfig file, or 0 if none
1302 message indicating the result
1303 """
Tom Rini65e05dd2019-09-20 17:42:09 -04001304 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -06001305 if not sym:
1306 return 'cannot find sym'
Simon Glassea40b202021-07-21 21:35:53 -06001307 nodes = sym.nodes
1308 if len(nodes) != 1:
1309 return '%d locations' % len(nodes)
Simon Glassa3627082021-12-18 08:09:42 -07001310 node = nodes[0]
1311 fname, linenum = node.filename, node.linenr
Simon Glasscb008832017-06-15 21:39:33 -06001312 cwd = os.getcwd()
1313 if cwd and fname.startswith(cwd):
1314 fname = fname[len(cwd) + 1:]
1315 file_line = ' at %s:%d' % (fname, linenum)
Simon Glass37f815c2021-12-18 14:54:34 -07001316 data = read_file(fname)
Simon Glasscb008832017-06-15 21:39:33 -06001317 if data[linenum - 1] != 'config %s' % imply_config:
1318 return None, 0, 'bad sym format %s%s' % (data[linenum], file_line)
1319 return fname, linenum, 'adding%s' % file_line
1320
1321def add_imply_rule(config, fname, linenum):
1322 """Add a new 'imply' option to a Kconfig
1323
1324 Args:
1325 config: config option to add an imply for (without CONFIG_ prefix)
1326 fname: Kconfig filename to update
1327 linenum: Line number to place the 'imply' before
1328
1329 Returns:
1330 Message indicating the result
1331 """
1332 file_line = ' at %s:%d' % (fname, linenum)
Simon Glass37f815c2021-12-18 14:54:34 -07001333 data = read_file(fname)
Simon Glasscb008832017-06-15 21:39:33 -06001334 linenum -= 1
1335
1336 for offset, line in enumerate(data[linenum:]):
1337 if line.strip().startswith('help') or not line:
1338 data.insert(linenum + offset, '\timply %s' % config)
Simon Glass2fd85bd2021-12-18 14:54:33 -07001339 write_file(fname, data)
Simon Glasscb008832017-06-15 21:39:33 -06001340 return 'added%s' % file_line
1341
1342 return 'could not insert%s'
1343
1344(IMPLY_MIN_2, IMPLY_TARGET, IMPLY_CMD, IMPLY_NON_ARCH_BOARD) = (
1345 1, 2, 4, 8)
Simon Glass9b2a2e82017-06-15 21:39:32 -06001346
1347IMPLY_FLAGS = {
1348 'min2': [IMPLY_MIN_2, 'Show options which imply >2 boards (normally >5)'],
1349 'target': [IMPLY_TARGET, 'Allow CONFIG_TARGET_... options to imply'],
1350 'cmd': [IMPLY_CMD, 'Allow CONFIG_CMD_... to imply'],
Simon Glasscb008832017-06-15 21:39:33 -06001351 'non-arch-board': [
1352 IMPLY_NON_ARCH_BOARD,
1353 'Allow Kconfig options outside arch/ and /board/ to imply'],
Simon Glass9b2a2e82017-06-15 21:39:32 -06001354};
1355
Simon Glass9d603392021-12-18 08:09:43 -07001356
1357def read_database():
1358 """Read in the config database
1359
1360 Returns:
1361 tuple:
1362 set of all config options seen (each a str)
1363 set of all defconfigs seen (each a str)
1364 dict of configs for each defconfig:
1365 key: defconfig name, e.g. "MPC8548CDS_legacy_defconfig"
1366 value: dict:
1367 key: CONFIG option
1368 value: Value of option
1369 dict of defconfigs for each config:
1370 key: CONFIG option
1371 value: set of boards using that option
1372
1373 """
1374 configs = {}
1375
1376 # key is defconfig name, value is dict of (CONFIG_xxx, value)
1377 config_db = {}
1378
1379 # Set of all config options we have seen
1380 all_configs = set()
1381
1382 # Set of all defconfigs we have seen
1383 all_defconfigs = set()
1384
1385 defconfig_db = collections.defaultdict(set)
Simon Glass37f815c2021-12-18 14:54:34 -07001386 for line in read_file(CONFIG_DATABASE):
1387 line = line.rstrip()
1388 if not line: # Separator between defconfigs
1389 config_db[defconfig] = configs
1390 all_defconfigs.add(defconfig)
1391 configs = {}
1392 elif line[0] == ' ': # CONFIG line
1393 config, value = line.strip().split('=', 1)
1394 configs[config] = value
1395 defconfig_db[config].add(defconfig)
1396 all_configs.add(config)
1397 else: # New defconfig
1398 defconfig = line
Simon Glass9d603392021-12-18 08:09:43 -07001399
1400 return all_configs, all_defconfigs, config_db, defconfig_db
1401
1402
Simon Glasscb008832017-06-15 21:39:33 -06001403def do_imply_config(config_list, add_imply, imply_flags, skip_added,
1404 check_kconfig=True, find_superset=False):
Simon Glass99b66602017-06-01 19:39:03 -06001405 """Find CONFIG options which imply those in the list
1406
1407 Some CONFIG options can be implied by others and this can help to reduce
1408 the size of the defconfig files. For example, CONFIG_X86 implies
1409 CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
1410 all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
1411 each of the x86 defconfig files.
1412
1413 This function uses the moveconfig database to find such options. It
1414 displays a list of things that could possibly imply those in the list.
1415 The algorithm ignores any that start with CONFIG_TARGET since these
1416 typically refer to only a few defconfigs (often one). It also does not
1417 display a config with less than 5 defconfigs.
1418
1419 The algorithm works using sets. For each target config in config_list:
1420 - Get the set 'defconfigs' which use that target config
1421 - For each config (from a list of all configs):
1422 - Get the set 'imply_defconfig' of defconfigs which use that config
1423 -
1424 - If imply_defconfigs contains anything not in defconfigs then
1425 this config does not imply the target config
1426
1427 Params:
1428 config_list: List of CONFIG options to check (each a string)
Simon Glasscb008832017-06-15 21:39:33 -06001429 add_imply: Automatically add an 'imply' for each config.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001430 imply_flags: Flags which control which implying configs are allowed
1431 (IMPLY_...)
Simon Glasscb008832017-06-15 21:39:33 -06001432 skip_added: Don't show options which already have an imply added.
1433 check_kconfig: Check if implied symbols already have an 'imply' or
1434 'select' for the target config, and show this information if so.
Simon Glass99b66602017-06-01 19:39:03 -06001435 find_superset: True to look for configs which are a superset of those
1436 already found. So for example if CONFIG_EXYNOS5 implies an option,
1437 but CONFIG_EXYNOS covers a larger set of defconfigs and also
1438 implies that option, this will drop the former in favour of the
1439 latter. In practice this option has not proved very used.
1440
1441 Note the terminoloy:
1442 config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
1443 defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
1444 """
Simon Glasscb008832017-06-15 21:39:33 -06001445 kconf = KconfigScanner().conf if check_kconfig else None
1446 if add_imply and add_imply != 'all':
Simon Glassa3627082021-12-18 08:09:42 -07001447 add_imply = add_imply.split(',')
Simon Glasscb008832017-06-15 21:39:33 -06001448
Simon Glass9d603392021-12-18 08:09:43 -07001449 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
Simon Glass99b66602017-06-01 19:39:03 -06001450
Simon Glassa3627082021-12-18 08:09:42 -07001451 # Work through each target config option in turn, independently
Simon Glass99b66602017-06-01 19:39:03 -06001452 for config in config_list:
1453 defconfigs = defconfig_db.get(config)
1454 if not defconfigs:
Simon Glass793dca32019-10-31 07:42:57 -06001455 print('%s not found in any defconfig' % config)
Simon Glass99b66602017-06-01 19:39:03 -06001456 continue
1457
1458 # Get the set of defconfigs without this one (since a config cannot
1459 # imply itself)
1460 non_defconfigs = all_defconfigs - defconfigs
1461 num_defconfigs = len(defconfigs)
Simon Glass793dca32019-10-31 07:42:57 -06001462 print('%s found in %d/%d defconfigs' % (config, num_defconfigs,
1463 len(all_configs)))
Simon Glass99b66602017-06-01 19:39:03 -06001464
1465 # This will hold the results: key=config, value=defconfigs containing it
1466 imply_configs = {}
1467 rest_configs = all_configs - set([config])
1468
1469 # Look at every possible config, except the target one
1470 for imply_config in rest_configs:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001471 if 'ERRATUM' in imply_config:
Simon Glass99b66602017-06-01 19:39:03 -06001472 continue
Simon Glass9b2a2e82017-06-15 21:39:32 -06001473 if not (imply_flags & IMPLY_CMD):
1474 if 'CONFIG_CMD' in imply_config:
1475 continue
1476 if not (imply_flags & IMPLY_TARGET):
1477 if 'CONFIG_TARGET' in imply_config:
1478 continue
Simon Glass99b66602017-06-01 19:39:03 -06001479
1480 # Find set of defconfigs that have this config
1481 imply_defconfig = defconfig_db[imply_config]
1482
1483 # Get the intersection of this with defconfigs containing the
1484 # target config
1485 common_defconfigs = imply_defconfig & defconfigs
1486
1487 # Get the set of defconfigs containing this config which DO NOT
1488 # also contain the taret config. If this set is non-empty it means
1489 # that this config affects other defconfigs as well as (possibly)
1490 # the ones affected by the target config. This means it implies
1491 # things we don't want to imply.
1492 not_common_defconfigs = imply_defconfig & non_defconfigs
1493 if not_common_defconfigs:
1494 continue
1495
1496 # If there are common defconfigs, imply_config may be useful
1497 if common_defconfigs:
1498 skip = False
1499 if find_superset:
Simon Glass793dca32019-10-31 07:42:57 -06001500 for prev in list(imply_configs.keys()):
Simon Glass99b66602017-06-01 19:39:03 -06001501 prev_count = len(imply_configs[prev])
1502 count = len(common_defconfigs)
1503 if (prev_count > count and
1504 (imply_configs[prev] & common_defconfigs ==
1505 common_defconfigs)):
1506 # skip imply_config because prev is a superset
1507 skip = True
1508 break
1509 elif count > prev_count:
1510 # delete prev because imply_config is a superset
1511 del imply_configs[prev]
1512 if not skip:
1513 imply_configs[imply_config] = common_defconfigs
1514
1515 # Now we have a dict imply_configs of configs which imply each config
1516 # The value of each dict item is the set of defconfigs containing that
1517 # config. Rank them so that we print the configs that imply the largest
1518 # number of defconfigs first.
Simon Glasscb008832017-06-15 21:39:33 -06001519 ranked_iconfigs = sorted(imply_configs,
Simon Glass99b66602017-06-01 19:39:03 -06001520 key=lambda k: len(imply_configs[k]), reverse=True)
Simon Glasscb008832017-06-15 21:39:33 -06001521 kconfig_info = ''
1522 cwd = os.getcwd()
1523 add_list = collections.defaultdict(list)
1524 for iconfig in ranked_iconfigs:
1525 num_common = len(imply_configs[iconfig])
Simon Glass99b66602017-06-01 19:39:03 -06001526
1527 # Don't bother if there are less than 5 defconfigs affected.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001528 if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
Simon Glass99b66602017-06-01 19:39:03 -06001529 continue
Simon Glasscb008832017-06-15 21:39:33 -06001530 missing = defconfigs - imply_configs[iconfig]
Simon Glass99b66602017-06-01 19:39:03 -06001531 missing_str = ', '.join(missing) if missing else 'all'
1532 missing_str = ''
Simon Glasscb008832017-06-15 21:39:33 -06001533 show = True
1534 if kconf:
1535 sym = find_kconfig_rules(kconf, config[CONFIG_LEN:],
1536 iconfig[CONFIG_LEN:])
1537 kconfig_info = ''
1538 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001539 nodes = sym.nodes
1540 if len(nodes) == 1:
1541 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001542 if cwd and fname.startswith(cwd):
1543 fname = fname[len(cwd) + 1:]
1544 kconfig_info = '%s:%d' % (fname, linenum)
1545 if skip_added:
1546 show = False
1547 else:
Tom Rini65e05dd2019-09-20 17:42:09 -04001548 sym = kconf.syms.get(iconfig[CONFIG_LEN:])
Simon Glasscb008832017-06-15 21:39:33 -06001549 fname = ''
1550 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001551 nodes = sym.nodes
1552 if len(nodes) == 1:
1553 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001554 if cwd and fname.startswith(cwd):
1555 fname = fname[len(cwd) + 1:]
1556 in_arch_board = not sym or (fname.startswith('arch') or
1557 fname.startswith('board'))
1558 if (not in_arch_board and
1559 not (imply_flags & IMPLY_NON_ARCH_BOARD)):
1560 continue
1561
1562 if add_imply and (add_imply == 'all' or
1563 iconfig in add_imply):
1564 fname, linenum, kconfig_info = (check_imply_rule(kconf,
1565 config[CONFIG_LEN:], iconfig[CONFIG_LEN:]))
1566 if fname:
1567 add_list[fname].append(linenum)
1568
1569 if show and kconfig_info != 'skip':
Simon Glass793dca32019-10-31 07:42:57 -06001570 print('%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
1571 kconfig_info, missing_str))
Simon Glasscb008832017-06-15 21:39:33 -06001572
1573 # Having collected a list of things to add, now we add them. We process
1574 # each file from the largest line number to the smallest so that
1575 # earlier additions do not affect our line numbers. E.g. if we added an
1576 # imply at line 20 it would change the position of each line after
1577 # that.
Simon Glass793dca32019-10-31 07:42:57 -06001578 for fname, linenums in add_list.items():
Simon Glasscb008832017-06-15 21:39:33 -06001579 for linenum in sorted(linenums, reverse=True):
1580 add_imply_rule(config[CONFIG_LEN:], fname, linenum)
Simon Glass99b66602017-06-01 19:39:03 -06001581
1582
Simon Glass65d7fce2021-12-18 08:09:46 -07001583def do_find_config(config_list):
1584 """Find boards with a given combination of CONFIGs
1585
1586 Params:
1587 config_list: List of CONFIG options to check (each a string consisting
1588 of a config option, with or without a CONFIG_ prefix. If an option
1589 is preceded by a tilde (~) then it must be false, otherwise it must
1590 be true)
1591 """
1592 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
1593
1594 # Get the whitelist
Simon Glass37f815c2021-12-18 14:54:34 -07001595 adhoc_configs = set(read_file('scripts/config_whitelist.txt'))
Simon Glass65d7fce2021-12-18 08:09:46 -07001596
1597 # Start with all defconfigs
1598 out = all_defconfigs
1599
1600 # Work through each config in turn
1601 adhoc = []
1602 for item in config_list:
1603 # Get the real config name and whether we want this config or not
1604 cfg = item
1605 want = True
1606 if cfg[0] == '~':
1607 want = False
1608 cfg = cfg[1:]
1609
1610 if cfg in adhoc_configs:
1611 adhoc.append(cfg)
1612 continue
1613
1614 # Search everything that is still in the running. If it has a config
1615 # that we want, or doesn't have one that we don't, add it into the
1616 # running for the next stage
1617 in_list = out
1618 out = set()
1619 for defc in in_list:
1620 has_cfg = cfg in config_db[defc]
1621 if has_cfg == want:
1622 out.add(defc)
1623 if adhoc:
1624 print(f"Error: Not in Kconfig: %s" % ' '.join(adhoc))
1625 else:
1626 print(f'{len(out)} matches')
1627 print(' '.join(out))
1628
1629
1630def prefix_config(cfg):
1631 """Prefix a config with CONFIG_ if needed
1632
1633 This handles ~ operator, which indicates that the CONFIG should be disabled
1634
1635 >>> prefix_config('FRED')
1636 'CONFIG_FRED'
1637 >>> prefix_config('CONFIG_FRED')
1638 'CONFIG_FRED'
1639 >>> prefix_config('~FRED')
1640 '~CONFIG_FRED'
1641 >>> prefix_config('~CONFIG_FRED')
1642 '~CONFIG_FRED'
1643 >>> prefix_config('A123')
1644 'CONFIG_A123'
1645 """
1646 op = ''
1647 if cfg[0] == '~':
1648 op = cfg[0]
1649 cfg = cfg[1:]
1650 if not cfg.startswith('CONFIG_'):
1651 cfg = 'CONFIG_' + cfg
1652 return op + cfg
1653
1654
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001655def main():
1656 try:
1657 cpu_count = multiprocessing.cpu_count()
1658 except NotImplementedError:
1659 cpu_count = 1
1660
Simon Glassb2e83c62021-12-18 14:54:31 -07001661 epilog = '''Move config options from headers to defconfig files. See
1662doc/develop/moveconfig.rst for documentation.'''
1663
1664 parser = ArgumentParser(epilog=epilog)
1665 # Add arguments here
1666 parser.add_argument('-a', '--add-imply', type=str, default='',
Simon Glasscb008832017-06-15 21:39:33 -06001667 help='comma-separated list of CONFIG options to add '
1668 "an 'imply' statement to for the CONFIG in -i")
Simon Glassb2e83c62021-12-18 14:54:31 -07001669 parser.add_argument('-A', '--skip-added', action='store_true', default=False,
Simon Glasscb008832017-06-15 21:39:33 -06001670 help="don't show options which are already marked as "
1671 'implying others')
Simon Glassb2e83c62021-12-18 14:54:31 -07001672 parser.add_argument('-b', '--build-db', action='store_true', default=False,
Simon Glassd73fcb12017-06-01 19:39:02 -06001673 help='build a CONFIG database')
Simon Glassb2e83c62021-12-18 14:54:31 -07001674 parser.add_argument('-c', '--color', action='store_true', default=False,
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001675 help='display the log in color')
Simon Glassb2e83c62021-12-18 14:54:31 -07001676 parser.add_argument('-C', '--commit', action='store_true', default=False,
Simon Glass9ede2122016-09-12 23:18:21 -06001677 help='Create a git commit for the operation')
Simon Glassb2e83c62021-12-18 14:54:31 -07001678 parser.add_argument('-d', '--defconfigs', type=str,
Simon Glassee4e61b2017-06-01 19:38:59 -06001679 help='a file containing a list of defconfigs to move, '
1680 "one per line (for example 'snow_defconfig') "
1681 "or '-' to read from stdin")
Simon Glassb2e83c62021-12-18 14:54:31 -07001682 parser.add_argument('-e', '--exit-on-error', action='store_true',
Simon Glasse1ae5632021-12-18 08:09:44 -07001683 default=False,
1684 help='exit immediately on any error')
Simon Glassb2e83c62021-12-18 14:54:31 -07001685 parser.add_argument('-f', '--find', action='store_true', default=False,
Simon Glass65d7fce2021-12-18 08:09:46 -07001686 help='Find boards with a given config combination')
Simon Glassb2e83c62021-12-18 14:54:31 -07001687 parser.add_argument('-H', '--headers-only', dest='cleanup_headers_only',
Simon Glasse1ae5632021-12-18 08:09:44 -07001688 action='store_true', default=False,
1689 help='only cleanup the headers')
Simon Glassb2e83c62021-12-18 14:54:31 -07001690 parser.add_argument('-i', '--imply', action='store_true', default=False,
Simon Glass99b66602017-06-01 19:39:03 -06001691 help='find options which imply others')
Simon Glassb2e83c62021-12-18 14:54:31 -07001692 parser.add_argument('-I', '--imply-flags', type=str, default='',
Simon Glass9b2a2e82017-06-15 21:39:32 -06001693 help="control the -i option ('help' for help")
Simon Glassb2e83c62021-12-18 14:54:31 -07001694 parser.add_argument('-j', '--jobs', type=int, default=cpu_count,
Simon Glasse1ae5632021-12-18 08:09:44 -07001695 help='the number of jobs to run simultaneously')
Simon Glassb2e83c62021-12-18 14:54:31 -07001696 parser.add_argument('-n', '--dry-run', action='store_true', default=False,
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001697 help='perform a trial run (show log with no changes)')
Simon Glassb2e83c62021-12-18 14:54:31 -07001698 parser.add_argument('-r', '--git-ref', type=str,
Simon Glasse1ae5632021-12-18 08:09:44 -07001699 help='the git ref to clone for building the autoconf.mk')
Simon Glassb2e83c62021-12-18 14:54:31 -07001700 parser.add_argument('-s', '--force-sync', action='store_true', default=False,
Masahiro Yamada8513dc02016-05-19 15:52:08 +09001701 help='force sync by savedefconfig')
Simon Glassb2e83c62021-12-18 14:54:31 -07001702 parser.add_argument('-S', '--spl', action='store_true', default=False,
Masahiro Yamada07913d12016-08-22 22:18:22 +09001703 help='parse config options defined for SPL build')
Simon Glassb2e83c62021-12-18 14:54:31 -07001704 parser.add_argument('-t', '--test', action='store_true', default=False,
Simon Glasse1ae5632021-12-18 08:09:44 -07001705 help='run unit tests')
Simon Glassb2e83c62021-12-18 14:54:31 -07001706 parser.add_argument('-y', '--yes', action='store_true', default=False,
Simon Glass6b403df2016-09-12 23:18:20 -06001707 help="respond 'yes' to any prompts")
Simon Glassb2e83c62021-12-18 14:54:31 -07001708 parser.add_argument('-v', '--verbose', action='store_true', default=False,
Joe Hershberger95bf9c72015-05-19 13:21:24 -05001709 help='show any build errors as boards are built')
Simon Glassb2e83c62021-12-18 14:54:31 -07001710 parser.add_argument('configs', nargs='*')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001711
Simon Glassb2e83c62021-12-18 14:54:31 -07001712 args = parser.parse_args()
1713 configs = args.configs
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001714
Simon Glassb2e83c62021-12-18 14:54:31 -07001715 if args.test:
Simon Glass84067a52021-12-18 08:09:45 -07001716 sys.argv = [sys.argv[0]]
1717 fail, count = doctest.testmod()
1718 if fail:
1719 return 1
1720 unittest.main()
1721
Simon Glassb2e83c62021-12-18 14:54:31 -07001722 if not any((len(configs), args.force_sync, args.build_db, args.imply,
1723 args.find)):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001724 parser.print_usage()
1725 sys.exit(1)
1726
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001727 # prefix the option name with CONFIG_ if missing
Simon Glass65d7fce2021-12-18 08:09:46 -07001728 configs = [prefix_config(cfg) for cfg in configs]
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001729
Joe Hershberger2144f882015-05-19 13:21:20 -05001730 check_top_directory()
1731
Simon Glassb2e83c62021-12-18 14:54:31 -07001732 if args.imply:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001733 imply_flags = 0
Simon Glassb2e83c62021-12-18 14:54:31 -07001734 if args.imply_flags == 'all':
Simon Glassdee36c72017-07-10 14:47:46 -06001735 imply_flags = -1
1736
Simon Glassb2e83c62021-12-18 14:54:31 -07001737 elif args.imply_flags:
1738 for flag in args.imply_flags.split(','):
Simon Glassdee36c72017-07-10 14:47:46 -06001739 bad = flag not in IMPLY_FLAGS
1740 if bad:
Simon Glass793dca32019-10-31 07:42:57 -06001741 print("Invalid flag '%s'" % flag)
Simon Glassdee36c72017-07-10 14:47:46 -06001742 if flag == 'help' or bad:
Simon Glass793dca32019-10-31 07:42:57 -06001743 print("Imply flags: (separate with ',')")
1744 for name, info in IMPLY_FLAGS.items():
1745 print(' %-15s: %s' % (name, info[1]))
Simon Glassdee36c72017-07-10 14:47:46 -06001746 parser.print_usage()
1747 sys.exit(1)
1748 imply_flags |= IMPLY_FLAGS[flag][0]
Simon Glass9b2a2e82017-06-15 21:39:32 -06001749
Simon Glassb2e83c62021-12-18 14:54:31 -07001750 do_imply_config(configs, args.add_imply, imply_flags, args.skip_added)
Simon Glass99b66602017-06-01 19:39:03 -06001751 return
1752
Simon Glassb2e83c62021-12-18 14:54:31 -07001753 if args.find:
Simon Glass65d7fce2021-12-18 08:09:46 -07001754 do_find_config(configs)
1755 return
1756
Simon Glassd73fcb12017-06-01 19:39:02 -06001757 config_db = {}
Simon Glass793dca32019-10-31 07:42:57 -06001758 db_queue = queue.Queue()
Simon Glassd73fcb12017-06-01 19:39:02 -06001759 t = DatabaseThread(config_db, db_queue)
1760 t.setDaemon(True)
1761 t.start()
1762
Simon Glassb2e83c62021-12-18 14:54:31 -07001763 if not args.cleanup_headers_only:
Masahiro Yamadaf7536f72016-07-25 19:15:23 +09001764 check_clean_directory()
Simon Glass793dca32019-10-31 07:42:57 -06001765 bsettings.Setup('')
Simon Glass6821a742017-07-10 14:47:47 -06001766 toolchains = toolchain.Toolchains()
1767 toolchains.GetSettings()
1768 toolchains.Scan(verbose=False)
Simon Glassb2e83c62021-12-18 14:54:31 -07001769 move_config(toolchains, configs, args, db_queue)
Simon Glassd73fcb12017-06-01 19:39:02 -06001770 db_queue.join()
Joe Hershberger2144f882015-05-19 13:21:20 -05001771
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001772 if configs:
Simon Glassb2e83c62021-12-18 14:54:31 -07001773 cleanup_headers(configs, args)
1774 cleanup_extra_options(configs, args)
1775 cleanup_whitelist(configs, args)
1776 cleanup_readme(configs, args)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001777
Simon Glassb2e83c62021-12-18 14:54:31 -07001778 if args.commit:
Simon Glass9ede2122016-09-12 23:18:21 -06001779 subprocess.call(['git', 'add', '-u'])
1780 if configs:
1781 msg = 'Convert %s %sto Kconfig' % (configs[0],
1782 'et al ' if len(configs) > 1 else '')
1783 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1784 '\n '.join(configs))
1785 else:
1786 msg = 'configs: Resync with savedefconfig'
1787 msg += '\n\nRsync all defconfig files using moveconfig.py'
1788 subprocess.call(['git', 'commit', '-s', '-m', msg])
1789
Simon Glassb2e83c62021-12-18 14:54:31 -07001790 if args.build_db:
Simon Glassd73fcb12017-06-01 19:39:02 -06001791 with open(CONFIG_DATABASE, 'w') as fd:
Simon Glass793dca32019-10-31 07:42:57 -06001792 for defconfig, configs in config_db.items():
Simon Glassc79d18c2017-08-13 16:02:54 -06001793 fd.write('%s\n' % defconfig)
Simon Glassd73fcb12017-06-01 19:39:02 -06001794 for config in sorted(configs.keys()):
Simon Glassc79d18c2017-08-13 16:02:54 -06001795 fd.write(' %s=%s\n' % (config, configs[config]))
1796 fd.write('\n')
Simon Glassd73fcb12017-06-01 19:39:02 -06001797
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001798if __name__ == '__main__':
Simon Glass65d7fce2021-12-18 08:09:46 -07001799 sys.exit(main())