blob: 373b395fda4eca37acab4c11cb7ca6b6fbf882e5 [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
Markus Klotzbuecherbac0bb52020-02-12 20:46:44 +010013import asteval
Simon Glassc6e73cf2017-06-01 19:39:03 -060014import collections
Masahiro Yamadaea8f5342016-07-25 19:15:24 +090015import copy
Masahiro Yamada573b3902016-07-25 19:15:25 +090016import difflib
Masahiro Yamada0f6beda2016-05-19 15:52:07 +090017import filecmp
Masahiro Yamadab6160812015-05-20 11:36:07 +090018import fnmatch
Masahiro Yamada3984d6e2016-10-19 14:39:54 +090019import glob
Masahiro Yamadab6160812015-05-20 11:36:07 +090020import multiprocessing
21import optparse
22import os
Simon Glass1f701862019-10-31 07:42:57 -060023import queue
Masahiro Yamadab6160812015-05-20 11:36:07 +090024import re
25import shutil
26import subprocess
27import sys
28import tempfile
Simon Glass43cf08f2017-06-01 19:39:02 -060029import threading
Masahiro Yamadab6160812015-05-20 11:36:07 +090030import time
31
Simon Glassf0d9c102020-04-17 18:09:02 -060032from buildman import bsettings
33from buildman import kconfiglib
34from buildman import toolchain
Simon Glass44116332017-06-15 21:39:33 -060035
Masahiro Yamadab6160812015-05-20 11:36:07 +090036SHOW_GNU_MAKE = 'scripts/show-gnu-make'
37SLEEP_TIME=0.03
38
Masahiro Yamadab6160812015-05-20 11:36:07 +090039STATE_IDLE = 0
40STATE_DEFCONFIG = 1
41STATE_AUTOCONF = 2
Joe Hershberger166edec2015-05-19 13:21:17 -050042STATE_SAVEDEFCONFIG = 3
Masahiro Yamadab6160812015-05-20 11:36:07 +090043
44ACTION_MOVE = 0
Masahiro Yamada5643d6e2016-05-19 15:51:56 +090045ACTION_NO_ENTRY = 1
Masahiro Yamada35204d92016-08-22 22:18:21 +090046ACTION_NO_ENTRY_WARN = 2
47ACTION_NO_CHANGE = 3
Masahiro Yamadab6160812015-05-20 11:36:07 +090048
49COLOR_BLACK = '0;30'
50COLOR_RED = '0;31'
51COLOR_GREEN = '0;32'
52COLOR_BROWN = '0;33'
53COLOR_BLUE = '0;34'
54COLOR_PURPLE = '0;35'
55COLOR_CYAN = '0;36'
56COLOR_LIGHT_GRAY = '0;37'
57COLOR_DARK_GRAY = '1;30'
58COLOR_LIGHT_RED = '1;31'
59COLOR_LIGHT_GREEN = '1;32'
60COLOR_YELLOW = '1;33'
61COLOR_LIGHT_BLUE = '1;34'
62COLOR_LIGHT_PURPLE = '1;35'
63COLOR_LIGHT_CYAN = '1;36'
64COLOR_WHITE = '1;37'
65
Simon Glass8fb5bd02017-06-01 19:39:01 -060066AUTO_CONF_PATH = 'include/config/auto.conf'
Simon Glass43cf08f2017-06-01 19:39:02 -060067CONFIG_DATABASE = 'moveconfig.db'
Simon Glass8fb5bd02017-06-01 19:39:01 -060068
Simon Glass44116332017-06-15 21:39:33 -060069CONFIG_LEN = len('CONFIG_')
Simon Glass8fb5bd02017-06-01 19:39:01 -060070
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +020071SIZES = {
72 "SZ_1": 0x00000001, "SZ_2": 0x00000002,
73 "SZ_4": 0x00000004, "SZ_8": 0x00000008,
74 "SZ_16": 0x00000010, "SZ_32": 0x00000020,
75 "SZ_64": 0x00000040, "SZ_128": 0x00000080,
76 "SZ_256": 0x00000100, "SZ_512": 0x00000200,
77 "SZ_1K": 0x00000400, "SZ_2K": 0x00000800,
78 "SZ_4K": 0x00001000, "SZ_8K": 0x00002000,
79 "SZ_16K": 0x00004000, "SZ_32K": 0x00008000,
80 "SZ_64K": 0x00010000, "SZ_128K": 0x00020000,
81 "SZ_256K": 0x00040000, "SZ_512K": 0x00080000,
82 "SZ_1M": 0x00100000, "SZ_2M": 0x00200000,
83 "SZ_4M": 0x00400000, "SZ_8M": 0x00800000,
84 "SZ_16M": 0x01000000, "SZ_32M": 0x02000000,
85 "SZ_64M": 0x04000000, "SZ_128M": 0x08000000,
86 "SZ_256M": 0x10000000, "SZ_512M": 0x20000000,
87 "SZ_1G": 0x40000000, "SZ_2G": 0x80000000,
88 "SZ_4G": 0x100000000
89}
90
Masahiro Yamadab6160812015-05-20 11:36:07 +090091### helper functions ###
92def get_devnull():
93 """Get the file object of '/dev/null' device."""
94 try:
95 devnull = subprocess.DEVNULL # py3k
96 except AttributeError:
97 devnull = open(os.devnull, 'wb')
98 return devnull
99
100def check_top_directory():
101 """Exit if we are not at the top of source directory."""
102 for f in ('README', 'Licenses'):
103 if not os.path.exists(f):
104 sys.exit('Please run at the top of source directory.')
105
Masahiro Yamada990e6772016-05-19 15:51:54 +0900106def check_clean_directory():
107 """Exit if the source tree is not clean."""
108 for f in ('.config', 'include/config'):
109 if os.path.exists(f):
110 sys.exit("source tree is not clean, please run 'make mrproper'")
111
Masahiro Yamadab6160812015-05-20 11:36:07 +0900112def get_make_cmd():
113 """Get the command name of GNU Make.
114
115 U-Boot needs GNU Make for building, but the command name is not
116 necessarily "make". (for example, "gmake" on FreeBSD).
117 Returns the most appropriate command name on your system.
118 """
119 process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE)
120 ret = process.communicate()
121 if process.returncode:
122 sys.exit('GNU Make not found')
123 return ret[0].rstrip()
124
Simon Glass18774bc2017-06-01 19:38:58 -0600125def get_matched_defconfig(line):
126 """Get the defconfig files that match a pattern
127
128 Args:
129 line: Path or filename to match, e.g. 'configs/snow_defconfig' or
130 'k2*_defconfig'. If no directory is provided, 'configs/' is
131 prepended
132
133 Returns:
134 a list of matching defconfig files
135 """
136 dirname = os.path.dirname(line)
137 if dirname:
138 pattern = line
139 else:
140 pattern = os.path.join('configs', line)
141 return glob.glob(pattern) + glob.glob(pattern + '_defconfig')
142
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900143def get_matched_defconfigs(defconfigs_file):
Simon Glass8f3cf312017-06-01 19:38:59 -0600144 """Get all the defconfig files that match the patterns in a file.
145
146 Args:
147 defconfigs_file: File containing a list of defconfigs to process, or
148 '-' to read the list from stdin
149
150 Returns:
151 A list of paths to defconfig files, with no duplicates
152 """
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900153 defconfigs = []
Simon Glass8f3cf312017-06-01 19:38:59 -0600154 if defconfigs_file == '-':
155 fd = sys.stdin
156 defconfigs_file = 'stdin'
157 else:
158 fd = open(defconfigs_file)
159 for i, line in enumerate(fd):
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900160 line = line.strip()
161 if not line:
162 continue # skip blank lines silently
Simon Glass452fa8e2017-06-15 21:39:31 -0600163 if ' ' in line:
164 line = line.split(' ')[0] # handle 'git log' input
Simon Glass18774bc2017-06-01 19:38:58 -0600165 matched = get_matched_defconfig(line)
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900166 if not matched:
Simon Glass1f701862019-10-31 07:42:57 -0600167 print("warning: %s:%d: no defconfig matched '%s'" % \
168 (defconfigs_file, i + 1, line), file=sys.stderr)
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900169
170 defconfigs += matched
171
172 # use set() to drop multiple matching
173 return [ defconfig[len('configs') + 1:] for defconfig in set(defconfigs) ]
174
Masahiro Yamada58175e32016-07-25 19:15:28 +0900175def get_all_defconfigs():
176 """Get all the defconfig files under the configs/ directory."""
177 defconfigs = []
178 for (dirpath, dirnames, filenames) in os.walk('configs'):
179 dirpath = dirpath[len('configs') + 1:]
180 for filename in fnmatch.filter(filenames, '*_defconfig'):
181 defconfigs.append(os.path.join(dirpath, filename))
182
183 return defconfigs
184
Masahiro Yamadab6160812015-05-20 11:36:07 +0900185def color_text(color_enabled, color, string):
186 """Return colored string."""
187 if color_enabled:
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900188 # LF should not be surrounded by the escape sequence.
189 # Otherwise, additional whitespace or line-feed might be printed.
190 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
191 for s in string.split('\n') ])
Masahiro Yamadab6160812015-05-20 11:36:07 +0900192 else:
193 return string
194
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900195def show_diff(a, b, file_path, color_enabled):
Masahiro Yamada573b3902016-07-25 19:15:25 +0900196 """Show unidified diff.
197
198 Arguments:
199 a: A list of lines (before)
200 b: A list of lines (after)
201 file_path: Path to the file
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900202 color_enabled: Display the diff in color
Masahiro Yamada573b3902016-07-25 19:15:25 +0900203 """
204
205 diff = difflib.unified_diff(a, b,
206 fromfile=os.path.join('a', file_path),
207 tofile=os.path.join('b', file_path))
208
209 for line in diff:
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900210 if line[0] == '-' and line[1] != '-':
Simon Glass1f701862019-10-31 07:42:57 -0600211 print(color_text(color_enabled, COLOR_RED, line), end=' ')
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900212 elif line[0] == '+' and line[1] != '+':
Simon Glass1f701862019-10-31 07:42:57 -0600213 print(color_text(color_enabled, COLOR_GREEN, line), end=' ')
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900214 else:
Simon Glass1f701862019-10-31 07:42:57 -0600215 print(line, end=' ')
Masahiro Yamada573b3902016-07-25 19:15:25 +0900216
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900217def extend_matched_lines(lines, matched, pre_patterns, post_patterns, extend_pre,
218 extend_post):
219 """Extend matched lines if desired patterns are found before/after already
220 matched lines.
221
222 Arguments:
223 lines: A list of lines handled.
224 matched: A list of line numbers that have been already matched.
225 (will be updated by this function)
226 pre_patterns: A list of regular expression that should be matched as
227 preamble.
228 post_patterns: A list of regular expression that should be matched as
229 postamble.
230 extend_pre: Add the line number of matched preamble to the matched list.
231 extend_post: Add the line number of matched postamble to the matched list.
232 """
233 extended_matched = []
234
235 j = matched[0]
236
237 for i in matched:
238 if i == 0 or i < j:
239 continue
240 j = i
241 while j in matched:
242 j += 1
243 if j >= len(lines):
244 break
245
246 for p in pre_patterns:
247 if p.search(lines[i - 1]):
248 break
249 else:
250 # not matched
251 continue
252
253 for p in post_patterns:
254 if p.search(lines[j]):
255 break
256 else:
257 # not matched
258 continue
259
260 if extend_pre:
261 extended_matched.append(i - 1)
262 if extend_post:
263 extended_matched.append(j)
264
265 matched += extended_matched
266 matched.sort()
267
Chris Packham85e15c52017-05-02 21:30:46 +1200268def confirm(options, prompt):
269 if not options.yes:
270 while True:
Simon Glass1f701862019-10-31 07:42:57 -0600271 choice = input('{} [y/n]: '.format(prompt))
Chris Packham85e15c52017-05-02 21:30:46 +1200272 choice = choice.lower()
Simon Glass1f701862019-10-31 07:42:57 -0600273 print(choice)
Chris Packham85e15c52017-05-02 21:30:46 +1200274 if choice == 'y' or choice == 'n':
275 break
276
277 if choice == 'n':
278 return False
279
280 return True
281
Chris Packham73f6c7c2019-01-30 20:23:16 +1300282def cleanup_empty_blocks(header_path, options):
283 """Clean up empty conditional blocks
284
285 Arguments:
286 header_path: path to the cleaned file.
287 options: option flags.
288 """
289 pattern = re.compile(r'^\s*#\s*if.*$\n^\s*#\s*endif.*$\n*', flags=re.M)
290 with open(header_path) as f:
Simon Glassea52c2d2021-03-26 16:17:29 +1300291 try:
292 data = f.read()
293 except UnicodeDecodeError as e:
294 print("Failed on file %s': %s" % (header_path, e))
295 return
Chris Packham73f6c7c2019-01-30 20:23:16 +1300296
297 new_data = pattern.sub('\n', data)
298
299 show_diff(data.splitlines(True), new_data.splitlines(True), header_path,
300 options.color)
301
302 if options.dry_run:
303 return
304
305 with open(header_path, 'w') as f:
306 f.write(new_data)
307
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900308def cleanup_one_header(header_path, patterns, options):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900309 """Clean regex-matched lines away from a file.
310
311 Arguments:
312 header_path: path to the cleaned file.
313 patterns: list of regex patterns. Any lines matching to these
314 patterns are deleted.
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900315 options: option flags.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900316 """
317 with open(header_path) as f:
Simon Glassea52c2d2021-03-26 16:17:29 +1300318 try:
319 lines = f.readlines()
320 except UnicodeDecodeError as e:
321 print("Failed on file %s': %s" % (header_path, e))
322 return
Masahiro Yamadab6160812015-05-20 11:36:07 +0900323
324 matched = []
325 for i, line in enumerate(lines):
Masahiro Yamada6d798ba2016-07-25 19:15:27 +0900326 if i - 1 in matched and lines[i - 1][-2:] == '\\\n':
327 matched.append(i)
328 continue
Masahiro Yamadab6160812015-05-20 11:36:07 +0900329 for pattern in patterns:
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900330 if pattern.search(line):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900331 matched.append(i)
332 break
333
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900334 if not matched:
335 return
336
337 # remove empty #ifdef ... #endif, successive blank lines
338 pattern_if = re.compile(r'#\s*if(def|ndef)?\W') # #if, #ifdef, #ifndef
339 pattern_elif = re.compile(r'#\s*el(if|se)\W') # #elif, #else
340 pattern_endif = re.compile(r'#\s*endif\W') # #endif
341 pattern_blank = re.compile(r'^\s*$') # empty line
342
343 while True:
344 old_matched = copy.copy(matched)
345 extend_matched_lines(lines, matched, [pattern_if],
346 [pattern_endif], True, True)
347 extend_matched_lines(lines, matched, [pattern_elif],
348 [pattern_elif, pattern_endif], True, False)
349 extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
350 [pattern_blank], False, True)
351 extend_matched_lines(lines, matched, [pattern_blank],
352 [pattern_elif, pattern_endif], True, False)
353 extend_matched_lines(lines, matched, [pattern_blank],
354 [pattern_blank], True, False)
355 if matched == old_matched:
356 break
357
Masahiro Yamada573b3902016-07-25 19:15:25 +0900358 tolines = copy.copy(lines)
359
360 for i in reversed(matched):
361 tolines.pop(i)
362
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900363 show_diff(lines, tolines, header_path, options.color)
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900364
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900365 if options.dry_run:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900366 return
367
368 with open(header_path, 'w') as f:
Masahiro Yamada573b3902016-07-25 19:15:25 +0900369 for line in tolines:
370 f.write(line)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900371
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900372def cleanup_headers(configs, options):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900373 """Delete config defines from board headers.
374
375 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900376 configs: A list of CONFIGs to remove.
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900377 options: option flags.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900378 """
Chris Packham85e15c52017-05-02 21:30:46 +1200379 if not confirm(options, 'Clean up headers?'):
380 return
Masahiro Yamadab6160812015-05-20 11:36:07 +0900381
382 patterns = []
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900383 for config in configs:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900384 patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
385 patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
386
Joe Hershbergerb78ad422015-05-19 13:21:21 -0500387 for dir in 'include', 'arch', 'board':
388 for (dirpath, dirnames, filenames) in os.walk(dir):
Masahiro Yamada28a6d352016-07-25 19:15:22 +0900389 if dirpath == os.path.join('include', 'generated'):
390 continue
Joe Hershbergerb78ad422015-05-19 13:21:21 -0500391 for filename in filenames:
Simon Glassce44bf52020-08-11 11:23:34 -0600392 if not filename.endswith(('~', '.dts', '.dtsi', '.bin',
Trevor Woerneraaad0c22021-03-15 12:01:33 -0400393 '.elf','.aml','.dat')):
Chris Packham73f6c7c2019-01-30 20:23:16 +1300394 header_path = os.path.join(dirpath, filename)
Tom Rinie90c0072019-11-10 21:19:37 -0500395 # This file contains UTF-16 data and no CONFIG symbols
396 if header_path == 'include/video_font_data.h':
397 continue
Chris Packham73f6c7c2019-01-30 20:23:16 +1300398 cleanup_one_header(header_path, patterns, options)
399 cleanup_empty_blocks(header_path, options)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900400
Masahiro Yamadadce28de2016-07-25 19:15:29 +0900401def cleanup_one_extra_option(defconfig_path, configs, options):
402 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file.
403
404 Arguments:
405 defconfig_path: path to the cleaned defconfig file.
406 configs: A list of CONFIGs to remove.
407 options: option flags.
408 """
409
410 start = 'CONFIG_SYS_EXTRA_OPTIONS="'
411 end = '"\n'
412
413 with open(defconfig_path) as f:
414 lines = f.readlines()
415
416 for i, line in enumerate(lines):
417 if line.startswith(start) and line.endswith(end):
418 break
419 else:
420 # CONFIG_SYS_EXTRA_OPTIONS was not found in this defconfig
421 return
422
423 old_tokens = line[len(start):-len(end)].split(',')
424 new_tokens = []
425
426 for token in old_tokens:
427 pos = token.find('=')
428 if not (token[:pos] if pos >= 0 else token) in configs:
429 new_tokens.append(token)
430
431 if new_tokens == old_tokens:
432 return
433
434 tolines = copy.copy(lines)
435
436 if new_tokens:
437 tolines[i] = start + ','.join(new_tokens) + end
438 else:
439 tolines.pop(i)
440
441 show_diff(lines, tolines, defconfig_path, options.color)
442
443 if options.dry_run:
444 return
445
446 with open(defconfig_path, 'w') as f:
447 for line in tolines:
448 f.write(line)
449
450def cleanup_extra_options(configs, options):
451 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files.
452
453 Arguments:
454 configs: A list of CONFIGs to remove.
455 options: option flags.
456 """
Chris Packham85e15c52017-05-02 21:30:46 +1200457 if not confirm(options, 'Clean up CONFIG_SYS_EXTRA_OPTIONS?'):
458 return
Masahiro Yamadadce28de2016-07-25 19:15:29 +0900459
460 configs = [ config[len('CONFIG_'):] for config in configs ]
461
462 defconfigs = get_all_defconfigs()
463
464 for defconfig in defconfigs:
465 cleanup_one_extra_option(os.path.join('configs', defconfig), configs,
466 options)
467
Chris Packham9d5274f2017-05-02 21:30:47 +1200468def cleanup_whitelist(configs, options):
469 """Delete config whitelist entries
470
471 Arguments:
472 configs: A list of CONFIGs to remove.
473 options: option flags.
474 """
475 if not confirm(options, 'Clean up whitelist entries?'):
476 return
477
478 with open(os.path.join('scripts', 'config_whitelist.txt')) as f:
479 lines = f.readlines()
480
481 lines = [x for x in lines if x.strip() not in configs]
482
483 with open(os.path.join('scripts', 'config_whitelist.txt'), 'w') as f:
484 f.write(''.join(lines))
485
Chris Packham0e6deff2017-05-02 21:30:48 +1200486def find_matching(patterns, line):
487 for pat in patterns:
488 if pat.search(line):
489 return True
490 return False
491
492def cleanup_readme(configs, options):
493 """Delete config description in README
494
495 Arguments:
496 configs: A list of CONFIGs to remove.
497 options: option flags.
498 """
499 if not confirm(options, 'Clean up README?'):
500 return
501
502 patterns = []
503 for config in configs:
504 patterns.append(re.compile(r'^\s+%s' % config))
505
506 with open('README') as f:
507 lines = f.readlines()
508
509 found = False
510 newlines = []
511 for line in lines:
512 if not found:
513 found = find_matching(patterns, line)
514 if found:
515 continue
516
517 if found and re.search(r'^\s+CONFIG', line):
518 found = False
519
520 if not found:
521 newlines.append(line)
522
523 with open('README', 'w') as f:
524 f.write(''.join(newlines))
525
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200526def try_expand(line):
527 """If value looks like an expression, try expanding it
528 Otherwise just return the existing value
529 """
530 if line.find('=') == -1:
531 return line
532
533 try:
Markus Klotzbuecherbac0bb52020-02-12 20:46:44 +0100534 aeval = asteval.Interpreter( usersyms=SIZES, minimal=True )
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200535 cfg, val = re.split("=", line)
536 val= val.strip('\"')
537 if re.search("[*+-/]|<<|SZ_+|\(([^\)]+)\)", val):
Markus Klotzbuecherbac0bb52020-02-12 20:46:44 +0100538 newval = hex(aeval(val))
Simon Glass1f701862019-10-31 07:42:57 -0600539 print("\tExpanded expression %s to %s" % (val, newval))
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200540 return cfg+'='+newval
541 except:
Simon Glass1f701862019-10-31 07:42:57 -0600542 print("\tFailed to expand expression in %s" % line)
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200543
544 return line
545
Chris Packham9d5274f2017-05-02 21:30:47 +1200546
Masahiro Yamadab6160812015-05-20 11:36:07 +0900547### classes ###
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900548class Progress:
549
550 """Progress Indicator"""
551
552 def __init__(self, total):
553 """Create a new progress indicator.
554
555 Arguments:
556 total: A number of defconfig files to process.
557 """
558 self.current = 0
559 self.total = total
560
561 def inc(self):
562 """Increment the number of processed defconfig files."""
563
564 self.current += 1
565
566 def show(self):
567 """Display the progress."""
Simon Glass1f701862019-10-31 07:42:57 -0600568 print(' %d defconfigs out of %d\r' % (self.current, self.total), end=' ')
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900569 sys.stdout.flush()
570
Simon Glass44116332017-06-15 21:39:33 -0600571
572class KconfigScanner:
573 """Kconfig scanner."""
574
575 def __init__(self):
576 """Scan all the Kconfig files and create a Config object."""
577 # Define environment variables referenced from Kconfig
578 os.environ['srctree'] = os.getcwd()
579 os.environ['UBOOTVERSION'] = 'dummy'
580 os.environ['KCONFIG_OBJDIR'] = ''
Tom Rini3c5f4152019-09-20 17:42:09 -0400581 self.conf = kconfiglib.Kconfig()
Simon Glass44116332017-06-15 21:39:33 -0600582
583
Masahiro Yamadab6160812015-05-20 11:36:07 +0900584class KconfigParser:
585
586 """A parser of .config and include/autoconf.mk."""
587
588 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
589 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
590
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900591 def __init__(self, configs, options, build_dir):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900592 """Create a new parser.
593
594 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900595 configs: A list of CONFIGs to move.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900596 options: option flags.
597 build_dir: Build directory.
598 """
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900599 self.configs = configs
Masahiro Yamadab6160812015-05-20 11:36:07 +0900600 self.options = options
Masahiro Yamada5393b612016-05-19 15:52:00 +0900601 self.dotconfig = os.path.join(build_dir, '.config')
602 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
Masahiro Yamada6d139172016-08-22 22:18:22 +0900603 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
604 'autoconf.mk')
Simon Glass8fb5bd02017-06-01 19:39:01 -0600605 self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
Masahiro Yamada07f98522016-05-19 15:52:06 +0900606 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamadab6160812015-05-20 11:36:07 +0900607
Simon Glass257f5232017-07-10 14:47:47 -0600608 def get_arch(self):
609 """Parse .config file and return the architecture.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900610
611 Returns:
Simon Glass257f5232017-07-10 14:47:47 -0600612 Architecture name (e.g. 'arm').
Masahiro Yamadab6160812015-05-20 11:36:07 +0900613 """
614 arch = ''
615 cpu = ''
Masahiro Yamada5393b612016-05-19 15:52:00 +0900616 for line in open(self.dotconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900617 m = self.re_arch.match(line)
618 if m:
619 arch = m.group(1)
620 continue
621 m = self.re_cpu.match(line)
622 if m:
623 cpu = m.group(1)
624
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900625 if not arch:
626 return None
Masahiro Yamadab6160812015-05-20 11:36:07 +0900627
628 # fix-up for aarch64
629 if arch == 'arm' and cpu == 'armv8':
630 arch = 'aarch64'
631
Simon Glass257f5232017-07-10 14:47:47 -0600632 return arch
Masahiro Yamadab6160812015-05-20 11:36:07 +0900633
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900634 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900635 """Parse .config, defconfig, include/autoconf.mk for one config.
636
637 This function looks for the config options in the lines from
638 defconfig, .config, and include/autoconf.mk in order to decide
639 which action should be taken for this defconfig.
640
641 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900642 config: CONFIG name to parse.
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900643 dotconfig_lines: lines from the .config file.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900644 autoconf_lines: lines from the include/autoconf.mk file.
645
646 Returns:
647 A tupple of the action for this defconfig and the line
648 matched for the config.
649 """
Masahiro Yamadab6160812015-05-20 11:36:07 +0900650 not_set = '# %s is not set' % config
651
Masahiro Yamadab6160812015-05-20 11:36:07 +0900652 for line in autoconf_lines:
653 line = line.rstrip()
654 if line.startswith(config + '='):
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900655 new_val = line
Masahiro Yamadab6160812015-05-20 11:36:07 +0900656 break
Masahiro Yamadab6160812015-05-20 11:36:07 +0900657 else:
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900658 new_val = not_set
659
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200660 new_val = try_expand(new_val)
661
Masahiro Yamada35204d92016-08-22 22:18:21 +0900662 for line in dotconfig_lines:
663 line = line.rstrip()
664 if line.startswith(config + '=') or line == not_set:
665 old_val = line
666 break
667 else:
668 if new_val == not_set:
669 return (ACTION_NO_ENTRY, config)
670 else:
671 return (ACTION_NO_ENTRY_WARN, config)
672
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900673 # If this CONFIG is neither bool nor trisate
674 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
675 # tools/scripts/define2mk.sed changes '1' to 'y'.
676 # This is a problem if the CONFIG is int type.
677 # Check the type in Kconfig and handle it correctly.
678 if new_val[-2:] == '=y':
679 new_val = new_val[:-1] + '1'
680
Masahiro Yamadab48387f2016-06-15 14:33:50 +0900681 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
682 new_val)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900683
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900684 def update_dotconfig(self):
Masahiro Yamada7c0d9d22016-05-19 15:51:50 +0900685 """Parse files for the config options and update the .config.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900686
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900687 This function parses the generated .config and include/autoconf.mk
688 searching the target options.
Masahiro Yamada7c0d9d22016-05-19 15:51:50 +0900689 Move the config option(s) to the .config as needed.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900690
691 Arguments:
692 defconfig: defconfig name.
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900693
694 Returns:
Masahiro Yamada263d1372016-05-19 15:52:04 +0900695 Return a tuple of (updated flag, log string).
696 The "updated flag" is True if the .config was updated, False
697 otherwise. The "log string" shows what happend to the .config.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900698 """
699
Masahiro Yamadab6160812015-05-20 11:36:07 +0900700 results = []
Masahiro Yamada263d1372016-05-19 15:52:04 +0900701 updated = False
Masahiro Yamada35204d92016-08-22 22:18:21 +0900702 suspicious = False
Masahiro Yamada6d139172016-08-22 22:18:22 +0900703 rm_files = [self.config_autoconf, self.autoconf]
704
705 if self.options.spl:
706 if os.path.exists(self.spl_autoconf):
707 autoconf_path = self.spl_autoconf
708 rm_files.append(self.spl_autoconf)
709 else:
710 for f in rm_files:
711 os.remove(f)
712 return (updated, suspicious,
713 color_text(self.options.color, COLOR_BROWN,
714 "SPL is not enabled. Skipped.") + '\n')
715 else:
716 autoconf_path = self.autoconf
Masahiro Yamadab6160812015-05-20 11:36:07 +0900717
Masahiro Yamada5393b612016-05-19 15:52:00 +0900718 with open(self.dotconfig) as f:
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900719 dotconfig_lines = f.readlines()
Masahiro Yamadab6160812015-05-20 11:36:07 +0900720
Masahiro Yamada6d139172016-08-22 22:18:22 +0900721 with open(autoconf_path) as f:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900722 autoconf_lines = f.readlines()
723
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900724 for config in self.configs:
725 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger166edec2015-05-19 13:21:17 -0500726 autoconf_lines)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900727 results.append(result)
728
729 log = ''
730
731 for (action, value) in results:
732 if action == ACTION_MOVE:
733 actlog = "Move '%s'" % value
734 log_color = COLOR_LIGHT_GREEN
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900735 elif action == ACTION_NO_ENTRY:
736 actlog = "%s is not defined in Kconfig. Do nothing." % value
Masahiro Yamadab6160812015-05-20 11:36:07 +0900737 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada35204d92016-08-22 22:18:21 +0900738 elif action == ACTION_NO_ENTRY_WARN:
739 actlog = "%s is not defined in Kconfig (suspicious). Do nothing." % value
740 log_color = COLOR_YELLOW
741 suspicious = True
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900742 elif action == ACTION_NO_CHANGE:
743 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
744 % value
Masahiro Yamadab6160812015-05-20 11:36:07 +0900745 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada6d139172016-08-22 22:18:22 +0900746 elif action == ACTION_SPL_NOT_EXIST:
747 actlog = "SPL is not enabled for this defconfig. Skip."
748 log_color = COLOR_PURPLE
Masahiro Yamadab6160812015-05-20 11:36:07 +0900749 else:
750 sys.exit("Internal Error. This should not happen.")
751
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900752 log += color_text(self.options.color, log_color, actlog) + '\n'
Masahiro Yamadab6160812015-05-20 11:36:07 +0900753
Masahiro Yamada5393b612016-05-19 15:52:00 +0900754 with open(self.dotconfig, 'a') as f:
Masahiro Yamada953d93b2016-05-19 15:51:49 +0900755 for (action, value) in results:
756 if action == ACTION_MOVE:
757 f.write(value + '\n')
Masahiro Yamada263d1372016-05-19 15:52:04 +0900758 updated = True
Masahiro Yamadab6160812015-05-20 11:36:07 +0900759
Masahiro Yamada07f98522016-05-19 15:52:06 +0900760 self.results = results
Masahiro Yamada6d139172016-08-22 22:18:22 +0900761 for f in rm_files:
762 os.remove(f)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900763
Masahiro Yamada35204d92016-08-22 22:18:21 +0900764 return (updated, suspicious, log)
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900765
Masahiro Yamada07f98522016-05-19 15:52:06 +0900766 def check_defconfig(self):
767 """Check the defconfig after savedefconfig
768
769 Returns:
770 Return additional log if moved CONFIGs were removed again by
771 'make savedefconfig'.
772 """
773
774 log = ''
775
776 with open(self.defconfig) as f:
777 defconfig_lines = f.readlines()
778
779 for (action, value) in self.results:
780 if action != ACTION_MOVE:
781 continue
782 if not value + '\n' in defconfig_lines:
783 log += color_text(self.options.color, COLOR_YELLOW,
784 "'%s' was removed by savedefconfig.\n" %
785 value)
786
787 return log
788
Simon Glass43cf08f2017-06-01 19:39:02 -0600789
790class DatabaseThread(threading.Thread):
791 """This thread processes results from Slot threads.
792
793 It collects the data in the master config directary. There is only one
794 result thread, and this helps to serialise the build output.
795 """
796 def __init__(self, config_db, db_queue):
797 """Set up a new result thread
798
799 Args:
800 builder: Builder which will be sent each result
801 """
802 threading.Thread.__init__(self)
803 self.config_db = config_db
804 self.db_queue= db_queue
805
806 def run(self):
807 """Called to start up the result thread.
808
809 We collect the next result job and pass it on to the build.
810 """
811 while True:
812 defconfig, configs = self.db_queue.get()
813 self.config_db[defconfig] = configs
814 self.db_queue.task_done()
815
816
Masahiro Yamadab6160812015-05-20 11:36:07 +0900817class Slot:
818
819 """A slot to store a subprocess.
820
821 Each instance of this class handles one subprocess.
822 This class is useful to control multiple threads
823 for faster processing.
824 """
825
Simon Glass257f5232017-07-10 14:47:47 -0600826 def __init__(self, toolchains, configs, options, progress, devnull,
827 make_cmd, reference_src_dir, db_queue):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900828 """Create a new process slot.
829
830 Arguments:
Simon Glass257f5232017-07-10 14:47:47 -0600831 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900832 configs: A list of CONFIGs to move.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900833 options: option flags.
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900834 progress: A progress indicator.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900835 devnull: A file object of '/dev/null'.
836 make_cmd: command name of GNU Make.
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500837 reference_src_dir: Determine the true starting config state from this
838 source tree.
Simon Glass43cf08f2017-06-01 19:39:02 -0600839 db_queue: output queue to write config info for the database
Masahiro Yamadab6160812015-05-20 11:36:07 +0900840 """
Simon Glass257f5232017-07-10 14:47:47 -0600841 self.toolchains = toolchains
Masahiro Yamadab6160812015-05-20 11:36:07 +0900842 self.options = options
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900843 self.progress = progress
Masahiro Yamadab6160812015-05-20 11:36:07 +0900844 self.build_dir = tempfile.mkdtemp()
845 self.devnull = devnull
846 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500847 self.reference_src_dir = reference_src_dir
Simon Glass43cf08f2017-06-01 19:39:02 -0600848 self.db_queue = db_queue
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900849 self.parser = KconfigParser(configs, options, self.build_dir)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900850 self.state = STATE_IDLE
Masahiro Yamada1271b672016-08-22 22:18:20 +0900851 self.failed_boards = set()
852 self.suspicious_boards = set()
Masahiro Yamadab6160812015-05-20 11:36:07 +0900853
854 def __del__(self):
855 """Delete the working directory
856
857 This function makes sure the temporary directory is cleaned away
858 even if Python suddenly dies due to error. It should be done in here
Joe Hershberger640de872016-06-10 14:53:29 -0500859 because it is guaranteed the destructor is always invoked when the
Masahiro Yamadab6160812015-05-20 11:36:07 +0900860 instance of the class gets unreferenced.
861
862 If the subprocess is still running, wait until it finishes.
863 """
864 if self.state != STATE_IDLE:
865 while self.ps.poll() == None:
866 pass
867 shutil.rmtree(self.build_dir)
868
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900869 def add(self, defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900870 """Assign a new subprocess for defconfig and add it to the slot.
871
872 If the slot is vacant, create a new subprocess for processing the
873 given defconfig and add it to the slot. Just returns False if
874 the slot is occupied (i.e. the current subprocess is still running).
875
876 Arguments:
877 defconfig: defconfig name.
878
879 Returns:
880 Return True on success or False on failure
881 """
882 if self.state != STATE_IDLE:
883 return False
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900884
Masahiro Yamadab6160812015-05-20 11:36:07 +0900885 self.defconfig = defconfig
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900886 self.log = ''
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900887 self.current_src_dir = self.reference_src_dir
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900888 self.do_defconfig()
Masahiro Yamadab6160812015-05-20 11:36:07 +0900889 return True
890
891 def poll(self):
892 """Check the status of the subprocess and handle it as needed.
893
894 Returns True if the slot is vacant (i.e. in idle state).
895 If the configuration is successfully finished, assign a new
896 subprocess to build include/autoconf.mk.
897 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada263d1372016-05-19 15:52:04 +0900898 parse the .config and the include/autoconf.mk, moving
899 config options to the .config as needed.
900 If the .config was updated, run "make savedefconfig" to sync
901 it, update the original defconfig, and then set the slot back
902 to the idle state.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900903
904 Returns:
905 Return True if the subprocess is terminated, False otherwise
906 """
907 if self.state == STATE_IDLE:
908 return True
909
910 if self.ps.poll() == None:
911 return False
912
913 if self.ps.poll() != 0:
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900914 self.handle_error()
915 elif self.state == STATE_DEFCONFIG:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900916 if self.reference_src_dir and not self.current_src_dir:
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500917 self.do_savedefconfig()
918 else:
919 self.do_autoconf()
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900920 elif self.state == STATE_AUTOCONF:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900921 if self.current_src_dir:
922 self.current_src_dir = None
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500923 self.do_defconfig()
Simon Glass43cf08f2017-06-01 19:39:02 -0600924 elif self.options.build_db:
925 self.do_build_db()
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500926 else:
927 self.do_savedefconfig()
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900928 elif self.state == STATE_SAVEDEFCONFIG:
929 self.update_defconfig()
930 else:
931 sys.exit("Internal Error. This should not happen.")
Masahiro Yamadab6160812015-05-20 11:36:07 +0900932
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900933 return True if self.state == STATE_IDLE else False
Joe Hershberger166edec2015-05-19 13:21:17 -0500934
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900935 def handle_error(self):
936 """Handle error cases."""
Masahiro Yamada83c17672016-05-19 15:52:08 +0900937
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900938 self.log += color_text(self.options.color, COLOR_LIGHT_RED,
939 "Failed to process.\n")
940 if self.options.verbose:
941 self.log += color_text(self.options.color, COLOR_LIGHT_CYAN,
Markus Klotzbuecher8773f352020-02-12 20:46:45 +0100942 self.ps.stderr.read().decode())
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900943 self.finish(False)
Joe Hershberger166edec2015-05-19 13:21:17 -0500944
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900945 def do_defconfig(self):
946 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamada0f6beda2016-05-19 15:52:07 +0900947
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900948 cmd = list(self.make_cmd)
949 cmd.append(self.defconfig)
950 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900951 stderr=subprocess.PIPE,
952 cwd=self.current_src_dir)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900953 self.state = STATE_DEFCONFIG
Masahiro Yamada0f6beda2016-05-19 15:52:07 +0900954
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900955 def do_autoconf(self):
Simon Glass8fb5bd02017-06-01 19:39:01 -0600956 """Run 'make AUTO_CONF_PATH'."""
Masahiro Yamadab6160812015-05-20 11:36:07 +0900957
Simon Glass257f5232017-07-10 14:47:47 -0600958 arch = self.parser.get_arch()
959 try:
960 toolchain = self.toolchains.Select(arch)
961 except ValueError:
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900962 self.log += color_text(self.options.color, COLOR_YELLOW,
Chris Packham1ebcbd12017-08-27 20:00:51 +1200963 "Tool chain for '%s' is missing. Do nothing.\n" % arch)
Masahiro Yamada274a5ee2016-05-19 15:52:03 +0900964 self.finish(False)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900965 return
Simon Glass1f701862019-10-31 07:42:57 -0600966 env = toolchain.MakeEnvironment(False)
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900967
Masahiro Yamadab6160812015-05-20 11:36:07 +0900968 cmd = list(self.make_cmd)
Joe Hershberger765442b2015-05-19 13:21:18 -0500969 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Simon Glass8fb5bd02017-06-01 19:39:01 -0600970 cmd.append(AUTO_CONF_PATH)
Simon Glass257f5232017-07-10 14:47:47 -0600971 self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900972 stderr=subprocess.PIPE,
973 cwd=self.current_src_dir)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900974 self.state = STATE_AUTOCONF
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900975
Simon Glass43cf08f2017-06-01 19:39:02 -0600976 def do_build_db(self):
977 """Add the board to the database"""
978 configs = {}
979 with open(os.path.join(self.build_dir, AUTO_CONF_PATH)) as fd:
980 for line in fd.readlines():
981 if line.startswith('CONFIG'):
982 config, value = line.split('=', 1)
983 configs[config] = value.rstrip()
984 self.db_queue.put([self.defconfig, configs])
985 self.finish(True)
986
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900987 def do_savedefconfig(self):
988 """Update the .config and run 'make savedefconfig'."""
989
Masahiro Yamada35204d92016-08-22 22:18:21 +0900990 (updated, suspicious, log) = self.parser.update_dotconfig()
991 if suspicious:
992 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900993 self.log += log
994
995 if not self.options.force_sync and not updated:
996 self.finish(True)
997 return
998 if updated:
999 self.log += color_text(self.options.color, COLOR_LIGHT_GREEN,
1000 "Syncing by savedefconfig...\n")
1001 else:
1002 self.log += "Syncing by savedefconfig (forced by option)...\n"
1003
1004 cmd = list(self.make_cmd)
1005 cmd.append('savedefconfig')
1006 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
1007 stderr=subprocess.PIPE)
1008 self.state = STATE_SAVEDEFCONFIG
1009
1010 def update_defconfig(self):
1011 """Update the input defconfig and go back to the idle state."""
1012
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001013 log = self.parser.check_defconfig()
1014 if log:
Masahiro Yamada1271b672016-08-22 22:18:20 +09001015 self.suspicious_boards.add(self.defconfig)
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001016 self.log += log
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001017 orig_defconfig = os.path.join('configs', self.defconfig)
1018 new_defconfig = os.path.join(self.build_dir, 'defconfig')
1019 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
1020
1021 if updated:
Joe Hershberger93f1c2d2016-06-10 14:53:30 -05001022 self.log += color_text(self.options.color, COLOR_LIGHT_BLUE,
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001023 "defconfig was updated.\n")
1024
1025 if not self.options.dry_run and updated:
1026 shutil.move(new_defconfig, orig_defconfig)
1027 self.finish(True)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001028
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001029 def finish(self, success):
1030 """Display log along with progress and go to the idle state.
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001031
1032 Arguments:
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001033 success: Should be True when the defconfig was processed
1034 successfully, or False when it fails.
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001035 """
1036 # output at least 30 characters to hide the "* defconfigs out of *".
1037 log = self.defconfig.ljust(30) + '\n'
1038
1039 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
1040 # Some threads are running in parallel.
1041 # Print log atomically to not mix up logs from different threads.
Simon Glass1f701862019-10-31 07:42:57 -06001042 print(log, file=(sys.stdout if success else sys.stderr))
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001043
1044 if not success:
1045 if self.options.exit_on_error:
1046 sys.exit("Exit on error.")
1047 # If --exit-on-error flag is not set, skip this board and continue.
1048 # Record the failed board.
Masahiro Yamada1271b672016-08-22 22:18:20 +09001049 self.failed_boards.add(self.defconfig)
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001050
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001051 self.progress.inc()
1052 self.progress.show()
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001053 self.state = STATE_IDLE
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001054
Masahiro Yamadab6160812015-05-20 11:36:07 +09001055 def get_failed_boards(self):
Masahiro Yamada1271b672016-08-22 22:18:20 +09001056 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamadab6160812015-05-20 11:36:07 +09001057 """
1058 return self.failed_boards
1059
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001060 def get_suspicious_boards(self):
Masahiro Yamada1271b672016-08-22 22:18:20 +09001061 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001062 """
Masahiro Yamada35204d92016-08-22 22:18:21 +09001063 return self.suspicious_boards - self.failed_boards
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001064
Masahiro Yamadab6160812015-05-20 11:36:07 +09001065class Slots:
1066
1067 """Controller of the array of subprocess slots."""
1068
Simon Glass257f5232017-07-10 14:47:47 -06001069 def __init__(self, toolchains, configs, options, progress,
1070 reference_src_dir, db_queue):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001071 """Create a new slots controller.
1072
1073 Arguments:
Simon Glass257f5232017-07-10 14:47:47 -06001074 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001075 configs: A list of CONFIGs to move.
Masahiro Yamadab6160812015-05-20 11:36:07 +09001076 options: option flags.
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001077 progress: A progress indicator.
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001078 reference_src_dir: Determine the true starting config state from this
1079 source tree.
Simon Glass43cf08f2017-06-01 19:39:02 -06001080 db_queue: output queue to write config info for the database
Masahiro Yamadab6160812015-05-20 11:36:07 +09001081 """
1082 self.options = options
1083 self.slots = []
1084 devnull = get_devnull()
1085 make_cmd = get_make_cmd()
1086 for i in range(options.jobs):
Simon Glass257f5232017-07-10 14:47:47 -06001087 self.slots.append(Slot(toolchains, configs, options, progress,
1088 devnull, make_cmd, reference_src_dir,
1089 db_queue))
Masahiro Yamadab6160812015-05-20 11:36:07 +09001090
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001091 def add(self, defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001092 """Add a new subprocess if a vacant slot is found.
1093
1094 Arguments:
1095 defconfig: defconfig name to be put into.
1096
1097 Returns:
1098 Return True on success or False on failure
1099 """
1100 for slot in self.slots:
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001101 if slot.add(defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001102 return True
1103 return False
1104
1105 def available(self):
1106 """Check if there is a vacant slot.
1107
1108 Returns:
1109 Return True if at lease one vacant slot is found, False otherwise.
1110 """
1111 for slot in self.slots:
1112 if slot.poll():
1113 return True
1114 return False
1115
1116 def empty(self):
1117 """Check if all slots are vacant.
1118
1119 Returns:
1120 Return True if all the slots are vacant, False otherwise.
1121 """
1122 ret = True
1123 for slot in self.slots:
1124 if not slot.poll():
1125 ret = False
1126 return ret
1127
1128 def show_failed_boards(self):
1129 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada1271b672016-08-22 22:18:20 +09001130 boards = set()
Masahiro Yamada0153f032016-06-15 14:33:53 +09001131 output_file = 'moveconfig.failed'
Masahiro Yamadab6160812015-05-20 11:36:07 +09001132
1133 for slot in self.slots:
Masahiro Yamada1271b672016-08-22 22:18:20 +09001134 boards |= slot.get_failed_boards()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001135
Masahiro Yamada0153f032016-06-15 14:33:53 +09001136 if boards:
1137 boards = '\n'.join(boards) + '\n'
1138 msg = "The following boards were not processed due to error:\n"
1139 msg += boards
1140 msg += "(the list has been saved in %s)\n" % output_file
Simon Glass1f701862019-10-31 07:42:57 -06001141 print(color_text(self.options.color, COLOR_LIGHT_RED,
1142 msg), file=sys.stderr)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001143
Masahiro Yamada0153f032016-06-15 14:33:53 +09001144 with open(output_file, 'w') as f:
1145 f.write(boards)
Joe Hershbergerdade12e2015-05-19 13:21:22 -05001146
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001147 def show_suspicious_boards(self):
1148 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada1271b672016-08-22 22:18:20 +09001149 boards = set()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001150 output_file = 'moveconfig.suspicious'
1151
1152 for slot in self.slots:
Masahiro Yamada1271b672016-08-22 22:18:20 +09001153 boards |= slot.get_suspicious_boards()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001154
1155 if boards:
1156 boards = '\n'.join(boards) + '\n'
1157 msg = "The following boards might have been converted incorrectly.\n"
1158 msg += "It is highly recommended to check them manually:\n"
1159 msg += boards
1160 msg += "(the list has been saved in %s)\n" % output_file
Simon Glass1f701862019-10-31 07:42:57 -06001161 print(color_text(self.options.color, COLOR_YELLOW,
1162 msg), file=sys.stderr)
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001163
1164 with open(output_file, 'w') as f:
1165 f.write(boards)
1166
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001167class ReferenceSource:
1168
1169 """Reference source against which original configs should be parsed."""
1170
1171 def __init__(self, commit):
1172 """Create a reference source directory based on a specified commit.
1173
1174 Arguments:
1175 commit: commit to git-clone
1176 """
1177 self.src_dir = tempfile.mkdtemp()
Simon Glass1f701862019-10-31 07:42:57 -06001178 print("Cloning git repo to a separate work directory...")
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001179 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1180 cwd=self.src_dir)
Simon Glass1f701862019-10-31 07:42:57 -06001181 print("Checkout '%s' to build the original autoconf.mk." % \
1182 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip())
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001183 subprocess.check_output(['git', 'checkout', commit],
1184 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001185
1186 def __del__(self):
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001187 """Delete the reference source directory
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001188
1189 This function makes sure the temporary directory is cleaned away
1190 even if Python suddenly dies due to error. It should be done in here
1191 because it is guaranteed the destructor is always invoked when the
1192 instance of the class gets unreferenced.
1193 """
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001194 shutil.rmtree(self.src_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001195
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001196 def get_dir(self):
1197 """Return the absolute path to the reference source directory."""
1198
1199 return self.src_dir
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001200
Simon Glass257f5232017-07-10 14:47:47 -06001201def move_config(toolchains, configs, options, db_queue):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001202 """Move config options to defconfig files.
1203
1204 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001205 configs: A list of CONFIGs to move.
Masahiro Yamadab6160812015-05-20 11:36:07 +09001206 options: option flags
1207 """
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001208 if len(configs) == 0:
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001209 if options.force_sync:
Simon Glass1f701862019-10-31 07:42:57 -06001210 print('No CONFIG is specified. You are probably syncing defconfigs.', end=' ')
Simon Glass43cf08f2017-06-01 19:39:02 -06001211 elif options.build_db:
Simon Glass1f701862019-10-31 07:42:57 -06001212 print('Building %s database' % CONFIG_DATABASE)
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001213 else:
Simon Glass1f701862019-10-31 07:42:57 -06001214 print('Neither CONFIG nor --force-sync is specified. Nothing will happen.', end=' ')
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001215 else:
Simon Glass1f701862019-10-31 07:42:57 -06001216 print('Move ' + ', '.join(configs), end=' ')
1217 print('(jobs: %d)\n' % options.jobs)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001218
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001219 if options.git_ref:
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001220 reference_src = ReferenceSource(options.git_ref)
1221 reference_src_dir = reference_src.get_dir()
1222 else:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +09001223 reference_src_dir = None
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001224
Joe Hershbergerc6e043a2015-05-19 13:21:19 -05001225 if options.defconfigs:
Masahiro Yamada3984d6e2016-10-19 14:39:54 +09001226 defconfigs = get_matched_defconfigs(options.defconfigs)
Joe Hershbergerc6e043a2015-05-19 13:21:19 -05001227 else:
Masahiro Yamada58175e32016-07-25 19:15:28 +09001228 defconfigs = get_all_defconfigs()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001229
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001230 progress = Progress(len(defconfigs))
Simon Glass257f5232017-07-10 14:47:47 -06001231 slots = Slots(toolchains, configs, options, progress, reference_src_dir,
1232 db_queue)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001233
1234 # Main loop to process defconfig files:
1235 # Add a new subprocess into a vacant slot.
1236 # Sleep if there is no available slot.
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001237 for defconfig in defconfigs:
1238 while not slots.add(defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001239 while not slots.available():
1240 # No available slot: sleep for a while
1241 time.sleep(SLEEP_TIME)
1242
1243 # wait until all the subprocesses finish
1244 while not slots.empty():
1245 time.sleep(SLEEP_TIME)
1246
Simon Glass1f701862019-10-31 07:42:57 -06001247 print('')
Masahiro Yamadab6160812015-05-20 11:36:07 +09001248 slots.show_failed_boards()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001249 slots.show_suspicious_boards()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001250
Simon Glass44116332017-06-15 21:39:33 -06001251def find_kconfig_rules(kconf, config, imply_config):
1252 """Check whether a config has a 'select' or 'imply' keyword
1253
1254 Args:
Tom Rini3c5f4152019-09-20 17:42:09 -04001255 kconf: Kconfiglib.Kconfig object
Simon Glass44116332017-06-15 21:39:33 -06001256 config: Name of config to check (without CONFIG_ prefix)
1257 imply_config: Implying config (without CONFIG_ prefix) which may or
1258 may not have an 'imply' for 'config')
1259
1260 Returns:
1261 Symbol object for 'config' if found, else None
1262 """
Tom Rini3c5f4152019-09-20 17:42:09 -04001263 sym = kconf.syms.get(imply_config)
Simon Glass44116332017-06-15 21:39:33 -06001264 if sym:
Simon Glass520b47a2021-07-21 21:35:53 -06001265 for sel, cond in (sym.selects + sym.implies):
1266 if sel == config:
Simon Glass44116332017-06-15 21:39:33 -06001267 return sym
1268 return None
1269
1270def check_imply_rule(kconf, config, imply_config):
1271 """Check if we can add an 'imply' option
1272
1273 This finds imply_config in the Kconfig and looks to see if it is possible
1274 to add an 'imply' for 'config' to that part of the Kconfig.
1275
1276 Args:
Tom Rini3c5f4152019-09-20 17:42:09 -04001277 kconf: Kconfiglib.Kconfig object
Simon Glass44116332017-06-15 21:39:33 -06001278 config: Name of config to check (without CONFIG_ prefix)
1279 imply_config: Implying config (without CONFIG_ prefix) which may or
1280 may not have an 'imply' for 'config')
1281
1282 Returns:
1283 tuple:
1284 filename of Kconfig file containing imply_config, or None if none
1285 line number within the Kconfig file, or 0 if none
1286 message indicating the result
1287 """
Tom Rini3c5f4152019-09-20 17:42:09 -04001288 sym = kconf.syms.get(imply_config)
Simon Glass44116332017-06-15 21:39:33 -06001289 if not sym:
1290 return 'cannot find sym'
Simon Glass520b47a2021-07-21 21:35:53 -06001291 nodes = sym.nodes
1292 if len(nodes) != 1:
1293 return '%d locations' % len(nodes)
1294 fname, linenum = nodes[0].filename, nodes[0].linern
Simon Glass44116332017-06-15 21:39:33 -06001295 cwd = os.getcwd()
1296 if cwd and fname.startswith(cwd):
1297 fname = fname[len(cwd) + 1:]
1298 file_line = ' at %s:%d' % (fname, linenum)
1299 with open(fname) as fd:
1300 data = fd.read().splitlines()
1301 if data[linenum - 1] != 'config %s' % imply_config:
1302 return None, 0, 'bad sym format %s%s' % (data[linenum], file_line)
1303 return fname, linenum, 'adding%s' % file_line
1304
1305def add_imply_rule(config, fname, linenum):
1306 """Add a new 'imply' option to a Kconfig
1307
1308 Args:
1309 config: config option to add an imply for (without CONFIG_ prefix)
1310 fname: Kconfig filename to update
1311 linenum: Line number to place the 'imply' before
1312
1313 Returns:
1314 Message indicating the result
1315 """
1316 file_line = ' at %s:%d' % (fname, linenum)
1317 data = open(fname).read().splitlines()
1318 linenum -= 1
1319
1320 for offset, line in enumerate(data[linenum:]):
1321 if line.strip().startswith('help') or not line:
1322 data.insert(linenum + offset, '\timply %s' % config)
1323 with open(fname, 'w') as fd:
1324 fd.write('\n'.join(data) + '\n')
1325 return 'added%s' % file_line
1326
1327 return 'could not insert%s'
1328
1329(IMPLY_MIN_2, IMPLY_TARGET, IMPLY_CMD, IMPLY_NON_ARCH_BOARD) = (
1330 1, 2, 4, 8)
Simon Glass92e55582017-06-15 21:39:32 -06001331
1332IMPLY_FLAGS = {
1333 'min2': [IMPLY_MIN_2, 'Show options which imply >2 boards (normally >5)'],
1334 'target': [IMPLY_TARGET, 'Allow CONFIG_TARGET_... options to imply'],
1335 'cmd': [IMPLY_CMD, 'Allow CONFIG_CMD_... to imply'],
Simon Glass44116332017-06-15 21:39:33 -06001336 'non-arch-board': [
1337 IMPLY_NON_ARCH_BOARD,
1338 'Allow Kconfig options outside arch/ and /board/ to imply'],
Simon Glass92e55582017-06-15 21:39:32 -06001339};
1340
Simon Glass44116332017-06-15 21:39:33 -06001341def do_imply_config(config_list, add_imply, imply_flags, skip_added,
1342 check_kconfig=True, find_superset=False):
Simon Glassc6e73cf2017-06-01 19:39:03 -06001343 """Find CONFIG options which imply those in the list
1344
1345 Some CONFIG options can be implied by others and this can help to reduce
1346 the size of the defconfig files. For example, CONFIG_X86 implies
1347 CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
1348 all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
1349 each of the x86 defconfig files.
1350
1351 This function uses the moveconfig database to find such options. It
1352 displays a list of things that could possibly imply those in the list.
1353 The algorithm ignores any that start with CONFIG_TARGET since these
1354 typically refer to only a few defconfigs (often one). It also does not
1355 display a config with less than 5 defconfigs.
1356
1357 The algorithm works using sets. For each target config in config_list:
1358 - Get the set 'defconfigs' which use that target config
1359 - For each config (from a list of all configs):
1360 - Get the set 'imply_defconfig' of defconfigs which use that config
1361 -
1362 - If imply_defconfigs contains anything not in defconfigs then
1363 this config does not imply the target config
1364
1365 Params:
1366 config_list: List of CONFIG options to check (each a string)
Simon Glass44116332017-06-15 21:39:33 -06001367 add_imply: Automatically add an 'imply' for each config.
Simon Glass92e55582017-06-15 21:39:32 -06001368 imply_flags: Flags which control which implying configs are allowed
1369 (IMPLY_...)
Simon Glass44116332017-06-15 21:39:33 -06001370 skip_added: Don't show options which already have an imply added.
1371 check_kconfig: Check if implied symbols already have an 'imply' or
1372 'select' for the target config, and show this information if so.
Simon Glassc6e73cf2017-06-01 19:39:03 -06001373 find_superset: True to look for configs which are a superset of those
1374 already found. So for example if CONFIG_EXYNOS5 implies an option,
1375 but CONFIG_EXYNOS covers a larger set of defconfigs and also
1376 implies that option, this will drop the former in favour of the
1377 latter. In practice this option has not proved very used.
1378
1379 Note the terminoloy:
1380 config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
1381 defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
1382 """
Simon Glass44116332017-06-15 21:39:33 -06001383 kconf = KconfigScanner().conf if check_kconfig else None
1384 if add_imply and add_imply != 'all':
1385 add_imply = add_imply.split()
1386
Simon Glassc6e73cf2017-06-01 19:39:03 -06001387 # key is defconfig name, value is dict of (CONFIG_xxx, value)
1388 config_db = {}
1389
1390 # Holds a dict containing the set of defconfigs that contain each config
1391 # key is config, value is set of defconfigs using that config
1392 defconfig_db = collections.defaultdict(set)
1393
1394 # Set of all config options we have seen
1395 all_configs = set()
1396
1397 # Set of all defconfigs we have seen
1398 all_defconfigs = set()
1399
1400 # Read in the database
1401 configs = {}
1402 with open(CONFIG_DATABASE) as fd:
1403 for line in fd.readlines():
1404 line = line.rstrip()
1405 if not line: # Separator between defconfigs
1406 config_db[defconfig] = configs
1407 all_defconfigs.add(defconfig)
1408 configs = {}
1409 elif line[0] == ' ': # CONFIG line
1410 config, value = line.strip().split('=', 1)
1411 configs[config] = value
1412 defconfig_db[config].add(defconfig)
1413 all_configs.add(config)
1414 else: # New defconfig
1415 defconfig = line
1416
1417 # Work through each target config option in tern, independently
1418 for config in config_list:
1419 defconfigs = defconfig_db.get(config)
1420 if not defconfigs:
Simon Glass1f701862019-10-31 07:42:57 -06001421 print('%s not found in any defconfig' % config)
Simon Glassc6e73cf2017-06-01 19:39:03 -06001422 continue
1423
1424 # Get the set of defconfigs without this one (since a config cannot
1425 # imply itself)
1426 non_defconfigs = all_defconfigs - defconfigs
1427 num_defconfigs = len(defconfigs)
Simon Glass1f701862019-10-31 07:42:57 -06001428 print('%s found in %d/%d defconfigs' % (config, num_defconfigs,
1429 len(all_configs)))
Simon Glassc6e73cf2017-06-01 19:39:03 -06001430
1431 # This will hold the results: key=config, value=defconfigs containing it
1432 imply_configs = {}
1433 rest_configs = all_configs - set([config])
1434
1435 # Look at every possible config, except the target one
1436 for imply_config in rest_configs:
Simon Glass92e55582017-06-15 21:39:32 -06001437 if 'ERRATUM' in imply_config:
Simon Glassc6e73cf2017-06-01 19:39:03 -06001438 continue
Simon Glass92e55582017-06-15 21:39:32 -06001439 if not (imply_flags & IMPLY_CMD):
1440 if 'CONFIG_CMD' in imply_config:
1441 continue
1442 if not (imply_flags & IMPLY_TARGET):
1443 if 'CONFIG_TARGET' in imply_config:
1444 continue
Simon Glassc6e73cf2017-06-01 19:39:03 -06001445
1446 # Find set of defconfigs that have this config
1447 imply_defconfig = defconfig_db[imply_config]
1448
1449 # Get the intersection of this with defconfigs containing the
1450 # target config
1451 common_defconfigs = imply_defconfig & defconfigs
1452
1453 # Get the set of defconfigs containing this config which DO NOT
1454 # also contain the taret config. If this set is non-empty it means
1455 # that this config affects other defconfigs as well as (possibly)
1456 # the ones affected by the target config. This means it implies
1457 # things we don't want to imply.
1458 not_common_defconfigs = imply_defconfig & non_defconfigs
1459 if not_common_defconfigs:
1460 continue
1461
1462 # If there are common defconfigs, imply_config may be useful
1463 if common_defconfigs:
1464 skip = False
1465 if find_superset:
Simon Glass1f701862019-10-31 07:42:57 -06001466 for prev in list(imply_configs.keys()):
Simon Glassc6e73cf2017-06-01 19:39:03 -06001467 prev_count = len(imply_configs[prev])
1468 count = len(common_defconfigs)
1469 if (prev_count > count and
1470 (imply_configs[prev] & common_defconfigs ==
1471 common_defconfigs)):
1472 # skip imply_config because prev is a superset
1473 skip = True
1474 break
1475 elif count > prev_count:
1476 # delete prev because imply_config is a superset
1477 del imply_configs[prev]
1478 if not skip:
1479 imply_configs[imply_config] = common_defconfigs
1480
1481 # Now we have a dict imply_configs of configs which imply each config
1482 # The value of each dict item is the set of defconfigs containing that
1483 # config. Rank them so that we print the configs that imply the largest
1484 # number of defconfigs first.
Simon Glass44116332017-06-15 21:39:33 -06001485 ranked_iconfigs = sorted(imply_configs,
Simon Glassc6e73cf2017-06-01 19:39:03 -06001486 key=lambda k: len(imply_configs[k]), reverse=True)
Simon Glass44116332017-06-15 21:39:33 -06001487 kconfig_info = ''
1488 cwd = os.getcwd()
1489 add_list = collections.defaultdict(list)
1490 for iconfig in ranked_iconfigs:
1491 num_common = len(imply_configs[iconfig])
Simon Glassc6e73cf2017-06-01 19:39:03 -06001492
1493 # Don't bother if there are less than 5 defconfigs affected.
Simon Glass92e55582017-06-15 21:39:32 -06001494 if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
Simon Glassc6e73cf2017-06-01 19:39:03 -06001495 continue
Simon Glass44116332017-06-15 21:39:33 -06001496 missing = defconfigs - imply_configs[iconfig]
Simon Glassc6e73cf2017-06-01 19:39:03 -06001497 missing_str = ', '.join(missing) if missing else 'all'
1498 missing_str = ''
Simon Glass44116332017-06-15 21:39:33 -06001499 show = True
1500 if kconf:
1501 sym = find_kconfig_rules(kconf, config[CONFIG_LEN:],
1502 iconfig[CONFIG_LEN:])
1503 kconfig_info = ''
1504 if sym:
Simon Glass520b47a2021-07-21 21:35:53 -06001505 nodes = sym.nodes
1506 if len(nodes) == 1:
1507 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glass44116332017-06-15 21:39:33 -06001508 if cwd and fname.startswith(cwd):
1509 fname = fname[len(cwd) + 1:]
1510 kconfig_info = '%s:%d' % (fname, linenum)
1511 if skip_added:
1512 show = False
1513 else:
Tom Rini3c5f4152019-09-20 17:42:09 -04001514 sym = kconf.syms.get(iconfig[CONFIG_LEN:])
Simon Glass44116332017-06-15 21:39:33 -06001515 fname = ''
1516 if sym:
Simon Glass520b47a2021-07-21 21:35:53 -06001517 nodes = sym.nodes
1518 if len(nodes) == 1:
1519 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glass44116332017-06-15 21:39:33 -06001520 if cwd and fname.startswith(cwd):
1521 fname = fname[len(cwd) + 1:]
1522 in_arch_board = not sym or (fname.startswith('arch') or
1523 fname.startswith('board'))
1524 if (not in_arch_board and
1525 not (imply_flags & IMPLY_NON_ARCH_BOARD)):
1526 continue
1527
1528 if add_imply and (add_imply == 'all' or
1529 iconfig in add_imply):
1530 fname, linenum, kconfig_info = (check_imply_rule(kconf,
1531 config[CONFIG_LEN:], iconfig[CONFIG_LEN:]))
1532 if fname:
1533 add_list[fname].append(linenum)
Simon Glassc6e73cf2017-06-01 19:39:03 -06001534
Simon Glass44116332017-06-15 21:39:33 -06001535 if show and kconfig_info != 'skip':
Simon Glass1f701862019-10-31 07:42:57 -06001536 print('%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
1537 kconfig_info, missing_str))
Simon Glass44116332017-06-15 21:39:33 -06001538
1539 # Having collected a list of things to add, now we add them. We process
1540 # each file from the largest line number to the smallest so that
1541 # earlier additions do not affect our line numbers. E.g. if we added an
1542 # imply at line 20 it would change the position of each line after
1543 # that.
Simon Glass1f701862019-10-31 07:42:57 -06001544 for fname, linenums in add_list.items():
Simon Glass44116332017-06-15 21:39:33 -06001545 for linenum in sorted(linenums, reverse=True):
1546 add_imply_rule(config[CONFIG_LEN:], fname, linenum)
1547
Simon Glassc6e73cf2017-06-01 19:39:03 -06001548
Masahiro Yamadab6160812015-05-20 11:36:07 +09001549def main():
1550 try:
1551 cpu_count = multiprocessing.cpu_count()
1552 except NotImplementedError:
1553 cpu_count = 1
1554
1555 parser = optparse.OptionParser()
1556 # Add options here
Simon Glass44116332017-06-15 21:39:33 -06001557 parser.add_option('-a', '--add-imply', type='string', default='',
1558 help='comma-separated list of CONFIG options to add '
1559 "an 'imply' statement to for the CONFIG in -i")
1560 parser.add_option('-A', '--skip-added', action='store_true', default=False,
1561 help="don't show options which are already marked as "
1562 'implying others')
Simon Glass43cf08f2017-06-01 19:39:02 -06001563 parser.add_option('-b', '--build-db', action='store_true', default=False,
1564 help='build a CONFIG database')
Masahiro Yamadab6160812015-05-20 11:36:07 +09001565 parser.add_option('-c', '--color', action='store_true', default=False,
1566 help='display the log in color')
Simon Glass8bf41c22016-09-12 23:18:21 -06001567 parser.add_option('-C', '--commit', action='store_true', default=False,
1568 help='Create a git commit for the operation')
Joe Hershbergerc6e043a2015-05-19 13:21:19 -05001569 parser.add_option('-d', '--defconfigs', type='string',
Simon Glass8f3cf312017-06-01 19:38:59 -06001570 help='a file containing a list of defconfigs to move, '
1571 "one per line (for example 'snow_defconfig') "
1572 "or '-' to read from stdin")
Simon Glassc6e73cf2017-06-01 19:39:03 -06001573 parser.add_option('-i', '--imply', action='store_true', default=False,
1574 help='find options which imply others')
Simon Glass92e55582017-06-15 21:39:32 -06001575 parser.add_option('-I', '--imply-flags', type='string', default='',
1576 help="control the -i option ('help' for help")
Masahiro Yamadab6160812015-05-20 11:36:07 +09001577 parser.add_option('-n', '--dry-run', action='store_true', default=False,
1578 help='perform a trial run (show log with no changes)')
1579 parser.add_option('-e', '--exit-on-error', action='store_true',
1580 default=False,
1581 help='exit immediately on any error')
Masahiro Yamada83c17672016-05-19 15:52:08 +09001582 parser.add_option('-s', '--force-sync', action='store_true', default=False,
1583 help='force sync by savedefconfig')
Masahiro Yamada6d139172016-08-22 22:18:22 +09001584 parser.add_option('-S', '--spl', action='store_true', default=False,
1585 help='parse config options defined for SPL build')
Joe Hershberger23475932015-05-19 13:21:20 -05001586 parser.add_option('-H', '--headers-only', dest='cleanup_headers_only',
1587 action='store_true', default=False,
1588 help='only cleanup the headers')
Masahiro Yamadab6160812015-05-20 11:36:07 +09001589 parser.add_option('-j', '--jobs', type='int', default=cpu_count,
1590 help='the number of jobs to run simultaneously')
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001591 parser.add_option('-r', '--git-ref', type='string',
1592 help='the git ref to clone for building the autoconf.mk')
Simon Glass13e05a02016-09-12 23:18:20 -06001593 parser.add_option('-y', '--yes', action='store_true', default=False,
1594 help="respond 'yes' to any prompts")
Joe Hershberger808b63f2015-05-19 13:21:24 -05001595 parser.add_option('-v', '--verbose', action='store_true', default=False,
1596 help='show any build errors as boards are built')
Masahiro Yamadab903c4e2016-05-19 15:51:58 +09001597 parser.usage += ' CONFIG ...'
Masahiro Yamadab6160812015-05-20 11:36:07 +09001598
Masahiro Yamadab903c4e2016-05-19 15:51:58 +09001599 (options, configs) = parser.parse_args()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001600
Simon Glassc6e73cf2017-06-01 19:39:03 -06001601 if len(configs) == 0 and not any((options.force_sync, options.build_db,
1602 options.imply)):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001603 parser.print_usage()
1604 sys.exit(1)
1605
Masahiro Yamadab903c4e2016-05-19 15:51:58 +09001606 # prefix the option name with CONFIG_ if missing
1607 configs = [ config if config.startswith('CONFIG_') else 'CONFIG_' + config
1608 for config in configs ]
Masahiro Yamadab6160812015-05-20 11:36:07 +09001609
Joe Hershberger23475932015-05-19 13:21:20 -05001610 check_top_directory()
1611
Simon Glassc6e73cf2017-06-01 19:39:03 -06001612 if options.imply:
Simon Glass92e55582017-06-15 21:39:32 -06001613 imply_flags = 0
Simon Glass5f096922017-07-10 14:47:46 -06001614 if options.imply_flags == 'all':
1615 imply_flags = -1
1616
1617 elif options.imply_flags:
1618 for flag in options.imply_flags.split(','):
1619 bad = flag not in IMPLY_FLAGS
1620 if bad:
Simon Glass1f701862019-10-31 07:42:57 -06001621 print("Invalid flag '%s'" % flag)
Simon Glass5f096922017-07-10 14:47:46 -06001622 if flag == 'help' or bad:
Simon Glass1f701862019-10-31 07:42:57 -06001623 print("Imply flags: (separate with ',')")
1624 for name, info in IMPLY_FLAGS.items():
1625 print(' %-15s: %s' % (name, info[1]))
Simon Glass5f096922017-07-10 14:47:46 -06001626 parser.print_usage()
1627 sys.exit(1)
1628 imply_flags |= IMPLY_FLAGS[flag][0]
Simon Glass92e55582017-06-15 21:39:32 -06001629
Simon Glass44116332017-06-15 21:39:33 -06001630 do_imply_config(configs, options.add_imply, imply_flags,
1631 options.skip_added)
Simon Glassc6e73cf2017-06-01 19:39:03 -06001632 return
1633
Simon Glass43cf08f2017-06-01 19:39:02 -06001634 config_db = {}
Simon Glass1f701862019-10-31 07:42:57 -06001635 db_queue = queue.Queue()
Simon Glass43cf08f2017-06-01 19:39:02 -06001636 t = DatabaseThread(config_db, db_queue)
1637 t.setDaemon(True)
1638 t.start()
1639
Joe Hershberger23475932015-05-19 13:21:20 -05001640 if not options.cleanup_headers_only:
Masahiro Yamadad0a9d2a2016-07-25 19:15:23 +09001641 check_clean_directory()
Simon Glass1f701862019-10-31 07:42:57 -06001642 bsettings.Setup('')
Simon Glass257f5232017-07-10 14:47:47 -06001643 toolchains = toolchain.Toolchains()
1644 toolchains.GetSettings()
1645 toolchains.Scan(verbose=False)
1646 move_config(toolchains, configs, options, db_queue)
Simon Glass43cf08f2017-06-01 19:39:02 -06001647 db_queue.join()
Joe Hershberger23475932015-05-19 13:21:20 -05001648
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001649 if configs:
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +09001650 cleanup_headers(configs, options)
Masahiro Yamadadce28de2016-07-25 19:15:29 +09001651 cleanup_extra_options(configs, options)
Chris Packham9d5274f2017-05-02 21:30:47 +12001652 cleanup_whitelist(configs, options)
Chris Packham0e6deff2017-05-02 21:30:48 +12001653 cleanup_readme(configs, options)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001654
Simon Glass8bf41c22016-09-12 23:18:21 -06001655 if options.commit:
1656 subprocess.call(['git', 'add', '-u'])
1657 if configs:
1658 msg = 'Convert %s %sto Kconfig' % (configs[0],
1659 'et al ' if len(configs) > 1 else '')
1660 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1661 '\n '.join(configs))
1662 else:
1663 msg = 'configs: Resync with savedefconfig'
1664 msg += '\n\nRsync all defconfig files using moveconfig.py'
1665 subprocess.call(['git', 'commit', '-s', '-m', msg])
1666
Simon Glass43cf08f2017-06-01 19:39:02 -06001667 if options.build_db:
1668 with open(CONFIG_DATABASE, 'w') as fd:
Simon Glass1f701862019-10-31 07:42:57 -06001669 for defconfig, configs in config_db.items():
Simon Glass1c879312017-08-13 16:02:54 -06001670 fd.write('%s\n' % defconfig)
Simon Glass43cf08f2017-06-01 19:39:02 -06001671 for config in sorted(configs.keys()):
Simon Glass1c879312017-08-13 16:02:54 -06001672 fd.write(' %s=%s\n' % (config, configs[config]))
1673 fd.write('\n')
Simon Glass43cf08f2017-06-01 19:39:02 -06001674
Masahiro Yamadab6160812015-05-20 11:36:07 +09001675if __name__ == '__main__':
1676 main()