blob: ab2d4905db512a067ac9beca0088b344d911d6d9 [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
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +010013import asteval
Simon Glass99b66602017-06-01 19:39:03 -060014import collections
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +090015import copy
Masahiro Yamadaf2f69812016-07-25 19:15:25 +090016import difflib
Simon Glass84067a52021-12-18 08:09:45 -070017import doctest
Masahiro Yamadac8e1b102016-05-19 15:52:07 +090018import filecmp
Masahiro Yamada5a27c732015-05-20 11:36:07 +090019import fnmatch
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +090020import glob
Masahiro Yamada5a27c732015-05-20 11:36:07 +090021import multiprocessing
22import optparse
23import 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 ###
94def get_devnull():
95 """Get the file object of '/dev/null' device."""
96 try:
97 devnull = subprocess.DEVNULL # py3k
98 except AttributeError:
99 devnull = open(os.devnull, 'wb')
100 return devnull
101
102def check_top_directory():
103 """Exit if we are not at the top of source directory."""
104 for f in ('README', 'Licenses'):
105 if not os.path.exists(f):
106 sys.exit('Please run at the top of source directory.')
107
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900108def check_clean_directory():
109 """Exit if the source tree is not clean."""
110 for f in ('.config', 'include/config'):
111 if os.path.exists(f):
112 sys.exit("source tree is not clean, please run 'make mrproper'")
113
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900114def get_make_cmd():
115 """Get the command name of GNU Make.
116
117 U-Boot needs GNU Make for building, but the command name is not
118 necessarily "make". (for example, "gmake" on FreeBSD).
119 Returns the most appropriate command name on your system.
120 """
121 process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE)
122 ret = process.communicate()
123 if process.returncode:
124 sys.exit('GNU Make not found')
125 return ret[0].rstrip()
126
Simon Glass25f978c2017-06-01 19:38:58 -0600127def get_matched_defconfig(line):
128 """Get the defconfig files that match a pattern
129
130 Args:
131 line: Path or filename to match, e.g. 'configs/snow_defconfig' or
132 'k2*_defconfig'. If no directory is provided, 'configs/' is
133 prepended
134
135 Returns:
136 a list of matching defconfig files
137 """
138 dirname = os.path.dirname(line)
139 if dirname:
140 pattern = line
141 else:
142 pattern = os.path.join('configs', line)
143 return glob.glob(pattern) + glob.glob(pattern + '_defconfig')
144
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900145def get_matched_defconfigs(defconfigs_file):
Simon Glassee4e61b2017-06-01 19:38:59 -0600146 """Get all the defconfig files that match the patterns in a file.
147
148 Args:
149 defconfigs_file: File containing a list of defconfigs to process, or
150 '-' to read the list from stdin
151
152 Returns:
153 A list of paths to defconfig files, with no duplicates
154 """
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900155 defconfigs = []
Simon Glassee4e61b2017-06-01 19:38:59 -0600156 if defconfigs_file == '-':
157 fd = sys.stdin
158 defconfigs_file = 'stdin'
159 else:
160 fd = open(defconfigs_file)
161 for i, line in enumerate(fd):
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900162 line = line.strip()
163 if not line:
164 continue # skip blank lines silently
Simon Glass2ddd85d2017-06-15 21:39:31 -0600165 if ' ' in line:
166 line = line.split(' ')[0] # handle 'git log' input
Simon Glass25f978c2017-06-01 19:38:58 -0600167 matched = get_matched_defconfig(line)
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900168 if not matched:
Simon Glass793dca32019-10-31 07:42:57 -0600169 print("warning: %s:%d: no defconfig matched '%s'" % \
170 (defconfigs_file, i + 1, line), file=sys.stderr)
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900171
172 defconfigs += matched
173
174 # use set() to drop multiple matching
175 return [ defconfig[len('configs') + 1:] for defconfig in set(defconfigs) ]
176
Masahiro Yamada684c3062016-07-25 19:15:28 +0900177def get_all_defconfigs():
178 """Get all the defconfig files under the configs/ directory."""
179 defconfigs = []
180 for (dirpath, dirnames, filenames) in os.walk('configs'):
181 dirpath = dirpath[len('configs') + 1:]
182 for filename in fnmatch.filter(filenames, '*_defconfig'):
183 defconfigs.append(os.path.join(dirpath, filename))
184
185 return defconfigs
186
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900187def color_text(color_enabled, color, string):
188 """Return colored string."""
189 if color_enabled:
Masahiro Yamada1d085562016-05-19 15:52:02 +0900190 # LF should not be surrounded by the escape sequence.
191 # Otherwise, additional whitespace or line-feed might be printed.
192 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
193 for s in string.split('\n') ])
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900194 else:
195 return string
196
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900197def show_diff(a, b, file_path, color_enabled):
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900198 """Show unidified diff.
199
200 Arguments:
201 a: A list of lines (before)
202 b: A list of lines (after)
203 file_path: Path to the file
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900204 color_enabled: Display the diff in color
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900205 """
206
207 diff = difflib.unified_diff(a, b,
208 fromfile=os.path.join('a', file_path),
209 tofile=os.path.join('b', file_path))
210
211 for line in diff:
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900212 if line[0] == '-' and line[1] != '-':
Simon Glass793dca32019-10-31 07:42:57 -0600213 print(color_text(color_enabled, COLOR_RED, line), end=' ')
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900214 elif line[0] == '+' and line[1] != '+':
Simon Glass793dca32019-10-31 07:42:57 -0600215 print(color_text(color_enabled, COLOR_GREEN, line), end=' ')
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900216 else:
Simon Glass793dca32019-10-31 07:42:57 -0600217 print(line, end=' ')
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900218
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900219def extend_matched_lines(lines, matched, pre_patterns, post_patterns, extend_pre,
220 extend_post):
221 """Extend matched lines if desired patterns are found before/after already
222 matched lines.
223
224 Arguments:
225 lines: A list of lines handled.
226 matched: A list of line numbers that have been already matched.
227 (will be updated by this function)
228 pre_patterns: A list of regular expression that should be matched as
229 preamble.
230 post_patterns: A list of regular expression that should be matched as
231 postamble.
232 extend_pre: Add the line number of matched preamble to the matched list.
233 extend_post: Add the line number of matched postamble to the matched list.
234 """
235 extended_matched = []
236
237 j = matched[0]
238
239 for i in matched:
240 if i == 0 or i < j:
241 continue
242 j = i
243 while j in matched:
244 j += 1
245 if j >= len(lines):
246 break
247
248 for p in pre_patterns:
249 if p.search(lines[i - 1]):
250 break
251 else:
252 # not matched
253 continue
254
255 for p in post_patterns:
256 if p.search(lines[j]):
257 break
258 else:
259 # not matched
260 continue
261
262 if extend_pre:
263 extended_matched.append(i - 1)
264 if extend_post:
265 extended_matched.append(j)
266
267 matched += extended_matched
268 matched.sort()
269
Chris Packham85edfc12017-05-02 21:30:46 +1200270def confirm(options, prompt):
271 if not options.yes:
272 while True:
Simon Glass793dca32019-10-31 07:42:57 -0600273 choice = input('{} [y/n]: '.format(prompt))
Chris Packham85edfc12017-05-02 21:30:46 +1200274 choice = choice.lower()
Simon Glass793dca32019-10-31 07:42:57 -0600275 print(choice)
Chris Packham85edfc12017-05-02 21:30:46 +1200276 if choice == 'y' or choice == 'n':
277 break
278
279 if choice == 'n':
280 return False
281
282 return True
283
Chris Packham4d9dbb12019-01-30 20:23:16 +1300284def cleanup_empty_blocks(header_path, options):
285 """Clean up empty conditional blocks
286
287 Arguments:
288 header_path: path to the cleaned file.
289 options: option flags.
290 """
291 pattern = re.compile(r'^\s*#\s*if.*$\n^\s*#\s*endif.*$\n*', flags=re.M)
292 with open(header_path) as f:
Simon Glass7570d9b2021-03-26 16:17:29 +1300293 try:
294 data = f.read()
295 except UnicodeDecodeError as e:
296 print("Failed on file %s': %s" % (header_path, e))
297 return
Chris Packham4d9dbb12019-01-30 20:23:16 +1300298
299 new_data = pattern.sub('\n', data)
300
301 show_diff(data.splitlines(True), new_data.splitlines(True), header_path,
302 options.color)
303
304 if options.dry_run:
305 return
306
307 with open(header_path, 'w') as f:
308 f.write(new_data)
309
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900310def cleanup_one_header(header_path, patterns, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900311 """Clean regex-matched lines away from a file.
312
313 Arguments:
314 header_path: path to the cleaned file.
315 patterns: list of regex patterns. Any lines matching to these
316 patterns are deleted.
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900317 options: option flags.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900318 """
319 with open(header_path) as f:
Simon Glass7570d9b2021-03-26 16:17:29 +1300320 try:
321 lines = f.readlines()
322 except UnicodeDecodeError as e:
323 print("Failed on file %s': %s" % (header_path, e))
324 return
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900325
326 matched = []
327 for i, line in enumerate(lines):
Masahiro Yamadaa3a779f2016-07-25 19:15:27 +0900328 if i - 1 in matched and lines[i - 1][-2:] == '\\\n':
329 matched.append(i)
330 continue
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900331 for pattern in patterns:
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900332 if pattern.search(line):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900333 matched.append(i)
334 break
335
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900336 if not matched:
337 return
338
339 # remove empty #ifdef ... #endif, successive blank lines
340 pattern_if = re.compile(r'#\s*if(def|ndef)?\W') # #if, #ifdef, #ifndef
341 pattern_elif = re.compile(r'#\s*el(if|se)\W') # #elif, #else
342 pattern_endif = re.compile(r'#\s*endif\W') # #endif
343 pattern_blank = re.compile(r'^\s*$') # empty line
344
345 while True:
346 old_matched = copy.copy(matched)
347 extend_matched_lines(lines, matched, [pattern_if],
348 [pattern_endif], True, True)
349 extend_matched_lines(lines, matched, [pattern_elif],
350 [pattern_elif, pattern_endif], True, False)
351 extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
352 [pattern_blank], False, True)
353 extend_matched_lines(lines, matched, [pattern_blank],
354 [pattern_elif, pattern_endif], True, False)
355 extend_matched_lines(lines, matched, [pattern_blank],
356 [pattern_blank], True, False)
357 if matched == old_matched:
358 break
359
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900360 tolines = copy.copy(lines)
361
362 for i in reversed(matched):
363 tolines.pop(i)
364
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900365 show_diff(lines, tolines, header_path, options.color)
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900366
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900367 if options.dry_run:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900368 return
369
370 with open(header_path, 'w') as f:
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900371 for line in tolines:
372 f.write(line)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900373
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900374def cleanup_headers(configs, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900375 """Delete config defines from board headers.
376
377 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900378 configs: A list of CONFIGs to remove.
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900379 options: option flags.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900380 """
Chris Packham85edfc12017-05-02 21:30:46 +1200381 if not confirm(options, 'Clean up headers?'):
382 return
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900383
384 patterns = []
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900385 for config in configs:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900386 patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
387 patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
388
Joe Hershberger60727f52015-05-19 13:21:21 -0500389 for dir in 'include', 'arch', 'board':
390 for (dirpath, dirnames, filenames) in os.walk(dir):
Masahiro Yamadadc6de502016-07-25 19:15:22 +0900391 if dirpath == os.path.join('include', 'generated'):
392 continue
Joe Hershberger60727f52015-05-19 13:21:21 -0500393 for filename in filenames:
Simon Glassa38cc172020-08-11 11:23:34 -0600394 if not filename.endswith(('~', '.dts', '.dtsi', '.bin',
Trevor Woernerdc514d72021-03-15 12:01:33 -0400395 '.elf','.aml','.dat')):
Chris Packham4d9dbb12019-01-30 20:23:16 +1300396 header_path = os.path.join(dirpath, filename)
Tom Rini02b56702019-11-10 21:19:37 -0500397 # This file contains UTF-16 data and no CONFIG symbols
398 if header_path == 'include/video_font_data.h':
399 continue
Chris Packham4d9dbb12019-01-30 20:23:16 +1300400 cleanup_one_header(header_path, patterns, options)
401 cleanup_empty_blocks(header_path, options)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900402
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900403def cleanup_one_extra_option(defconfig_path, configs, options):
404 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file.
405
406 Arguments:
407 defconfig_path: path to the cleaned defconfig file.
408 configs: A list of CONFIGs to remove.
409 options: option flags.
410 """
411
412 start = 'CONFIG_SYS_EXTRA_OPTIONS="'
413 end = '"\n'
414
415 with open(defconfig_path) as f:
416 lines = f.readlines()
417
418 for i, line in enumerate(lines):
419 if line.startswith(start) and line.endswith(end):
420 break
421 else:
422 # CONFIG_SYS_EXTRA_OPTIONS was not found in this defconfig
423 return
424
425 old_tokens = line[len(start):-len(end)].split(',')
426 new_tokens = []
427
428 for token in old_tokens:
429 pos = token.find('=')
430 if not (token[:pos] if pos >= 0 else token) in configs:
431 new_tokens.append(token)
432
433 if new_tokens == old_tokens:
434 return
435
436 tolines = copy.copy(lines)
437
438 if new_tokens:
439 tolines[i] = start + ','.join(new_tokens) + end
440 else:
441 tolines.pop(i)
442
443 show_diff(lines, tolines, defconfig_path, options.color)
444
445 if options.dry_run:
446 return
447
448 with open(defconfig_path, 'w') as f:
449 for line in tolines:
450 f.write(line)
451
452def cleanup_extra_options(configs, options):
453 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files.
454
455 Arguments:
456 configs: A list of CONFIGs to remove.
457 options: option flags.
458 """
Chris Packham85edfc12017-05-02 21:30:46 +1200459 if not confirm(options, 'Clean up CONFIG_SYS_EXTRA_OPTIONS?'):
460 return
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900461
462 configs = [ config[len('CONFIG_'):] for config in configs ]
463
464 defconfigs = get_all_defconfigs()
465
466 for defconfig in defconfigs:
467 cleanup_one_extra_option(os.path.join('configs', defconfig), configs,
468 options)
469
Chris Packhamca438342017-05-02 21:30:47 +1200470def cleanup_whitelist(configs, options):
471 """Delete config whitelist entries
472
473 Arguments:
474 configs: A list of CONFIGs to remove.
475 options: option flags.
476 """
477 if not confirm(options, 'Clean up whitelist entries?'):
478 return
479
480 with open(os.path.join('scripts', 'config_whitelist.txt')) as f:
481 lines = f.readlines()
482
483 lines = [x for x in lines if x.strip() not in configs]
484
485 with open(os.path.join('scripts', 'config_whitelist.txt'), 'w') as f:
486 f.write(''.join(lines))
487
Chris Packhamf90df592017-05-02 21:30:48 +1200488def find_matching(patterns, line):
489 for pat in patterns:
490 if pat.search(line):
491 return True
492 return False
493
494def cleanup_readme(configs, options):
495 """Delete config description in README
496
497 Arguments:
498 configs: A list of CONFIGs to remove.
499 options: option flags.
500 """
501 if not confirm(options, 'Clean up README?'):
502 return
503
504 patterns = []
505 for config in configs:
506 patterns.append(re.compile(r'^\s+%s' % config))
507
508 with open('README') as f:
509 lines = f.readlines()
510
511 found = False
512 newlines = []
513 for line in lines:
514 if not found:
515 found = find_matching(patterns, line)
516 if found:
517 continue
518
519 if found and re.search(r'^\s+CONFIG', line):
520 found = False
521
522 if not found:
523 newlines.append(line)
524
525 with open('README', 'w') as f:
526 f.write(''.join(newlines))
527
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200528def try_expand(line):
529 """If value looks like an expression, try expanding it
530 Otherwise just return the existing value
531 """
532 if line.find('=') == -1:
533 return line
534
535 try:
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +0100536 aeval = asteval.Interpreter( usersyms=SIZES, minimal=True )
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200537 cfg, val = re.split("=", line)
538 val= val.strip('\"')
Simon Glassdaa694d2021-12-18 14:54:30 -0700539 if re.search(r'[*+-/]|<<|SZ_+|\(([^\)]+)\)', val):
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +0100540 newval = hex(aeval(val))
Simon Glassdaa694d2021-12-18 14:54:30 -0700541 print('\tExpanded expression %s to %s' % (val, newval))
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200542 return cfg+'='+newval
543 except:
Simon Glassdaa694d2021-12-18 14:54:30 -0700544 print('\tFailed to expand expression in %s' % line)
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200545
546 return line
547
Chris Packhamca438342017-05-02 21:30:47 +1200548
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900549### classes ###
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900550class Progress:
551
552 """Progress Indicator"""
553
554 def __init__(self, total):
555 """Create a new progress indicator.
556
557 Arguments:
558 total: A number of defconfig files to process.
559 """
560 self.current = 0
561 self.total = total
562
563 def inc(self):
564 """Increment the number of processed defconfig files."""
565
566 self.current += 1
567
568 def show(self):
569 """Display the progress."""
Simon Glass793dca32019-10-31 07:42:57 -0600570 print(' %d defconfigs out of %d\r' % (self.current, self.total), end=' ')
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900571 sys.stdout.flush()
572
Simon Glasscb008832017-06-15 21:39:33 -0600573
574class KconfigScanner:
575 """Kconfig scanner."""
576
577 def __init__(self):
578 """Scan all the Kconfig files and create a Config object."""
579 # Define environment variables referenced from Kconfig
580 os.environ['srctree'] = os.getcwd()
581 os.environ['UBOOTVERSION'] = 'dummy'
582 os.environ['KCONFIG_OBJDIR'] = ''
Tom Rini65e05dd2019-09-20 17:42:09 -0400583 self.conf = kconfiglib.Kconfig()
Simon Glasscb008832017-06-15 21:39:33 -0600584
585
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900586class KconfigParser:
587
588 """A parser of .config and include/autoconf.mk."""
589
590 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
591 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
592
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900593 def __init__(self, configs, options, build_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900594 """Create a new parser.
595
596 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900597 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900598 options: option flags.
599 build_dir: Build directory.
600 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900601 self.configs = configs
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900602 self.options = options
Masahiro Yamada1f169922016-05-19 15:52:00 +0900603 self.dotconfig = os.path.join(build_dir, '.config')
604 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
Masahiro Yamada07913d12016-08-22 22:18:22 +0900605 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
606 'autoconf.mk')
Simon Glassf3b8e642017-06-01 19:39:01 -0600607 self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900608 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900609
Simon Glass6821a742017-07-10 14:47:47 -0600610 def get_arch(self):
611 """Parse .config file and return the architecture.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900612
613 Returns:
Simon Glass6821a742017-07-10 14:47:47 -0600614 Architecture name (e.g. 'arm').
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900615 """
616 arch = ''
617 cpu = ''
Masahiro Yamada1f169922016-05-19 15:52:00 +0900618 for line in open(self.dotconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900619 m = self.re_arch.match(line)
620 if m:
621 arch = m.group(1)
622 continue
623 m = self.re_cpu.match(line)
624 if m:
625 cpu = m.group(1)
626
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900627 if not arch:
628 return None
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900629
630 # fix-up for aarch64
631 if arch == 'arm' and cpu == 'armv8':
632 arch = 'aarch64'
633
Simon Glass6821a742017-07-10 14:47:47 -0600634 return arch
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900635
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900636 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900637 """Parse .config, defconfig, include/autoconf.mk for one config.
638
639 This function looks for the config options in the lines from
640 defconfig, .config, and include/autoconf.mk in order to decide
641 which action should be taken for this defconfig.
642
643 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900644 config: CONFIG name to parse.
Masahiro Yamadacc008292016-05-19 15:51:56 +0900645 dotconfig_lines: lines from the .config file.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900646 autoconf_lines: lines from the include/autoconf.mk file.
647
648 Returns:
649 A tupple of the action for this defconfig and the line
650 matched for the config.
651 """
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900652 not_set = '# %s is not set' % config
653
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900654 for line in autoconf_lines:
655 line = line.rstrip()
656 if line.startswith(config + '='):
Masahiro Yamadacc008292016-05-19 15:51:56 +0900657 new_val = line
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900658 break
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900659 else:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900660 new_val = not_set
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900661
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200662 new_val = try_expand(new_val)
663
Masahiro Yamada916224c2016-08-22 22:18:21 +0900664 for line in dotconfig_lines:
665 line = line.rstrip()
666 if line.startswith(config + '=') or line == not_set:
667 old_val = line
668 break
669 else:
670 if new_val == not_set:
671 return (ACTION_NO_ENTRY, config)
672 else:
673 return (ACTION_NO_ENTRY_WARN, config)
674
Masahiro Yamadacc008292016-05-19 15:51:56 +0900675 # If this CONFIG is neither bool nor trisate
676 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
677 # tools/scripts/define2mk.sed changes '1' to 'y'.
678 # This is a problem if the CONFIG is int type.
679 # Check the type in Kconfig and handle it correctly.
680 if new_val[-2:] == '=y':
681 new_val = new_val[:-1] + '1'
682
Masahiro Yamada50301592016-06-15 14:33:50 +0900683 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
684 new_val)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900685
Masahiro Yamada1d085562016-05-19 15:52:02 +0900686 def update_dotconfig(self):
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900687 """Parse files for the config options and update the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900688
Masahiro Yamadacc008292016-05-19 15:51:56 +0900689 This function parses the generated .config and include/autoconf.mk
690 searching the target options.
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900691 Move the config option(s) to the .config as needed.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900692
693 Arguments:
694 defconfig: defconfig name.
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900695
696 Returns:
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900697 Return a tuple of (updated flag, log string).
698 The "updated flag" is True if the .config was updated, False
699 otherwise. The "log string" shows what happend to the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900700 """
701
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900702 results = []
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900703 updated = False
Masahiro Yamada916224c2016-08-22 22:18:21 +0900704 suspicious = False
Masahiro Yamada07913d12016-08-22 22:18:22 +0900705 rm_files = [self.config_autoconf, self.autoconf]
706
707 if self.options.spl:
708 if os.path.exists(self.spl_autoconf):
709 autoconf_path = self.spl_autoconf
710 rm_files.append(self.spl_autoconf)
711 else:
712 for f in rm_files:
713 os.remove(f)
714 return (updated, suspicious,
715 color_text(self.options.color, COLOR_BROWN,
716 "SPL is not enabled. Skipped.") + '\n')
717 else:
718 autoconf_path = self.autoconf
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900719
Masahiro Yamada1f169922016-05-19 15:52:00 +0900720 with open(self.dotconfig) as f:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900721 dotconfig_lines = f.readlines()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900722
Masahiro Yamada07913d12016-08-22 22:18:22 +0900723 with open(autoconf_path) as f:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900724 autoconf_lines = f.readlines()
725
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900726 for config in self.configs:
727 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger96464ba2015-05-19 13:21:17 -0500728 autoconf_lines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900729 results.append(result)
730
731 log = ''
732
733 for (action, value) in results:
734 if action == ACTION_MOVE:
735 actlog = "Move '%s'" % value
736 log_color = COLOR_LIGHT_GREEN
Masahiro Yamadacc008292016-05-19 15:51:56 +0900737 elif action == ACTION_NO_ENTRY:
Simon Glassdaa694d2021-12-18 14:54:30 -0700738 actlog = '%s is not defined in Kconfig. Do nothing.' % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900739 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada916224c2016-08-22 22:18:21 +0900740 elif action == ACTION_NO_ENTRY_WARN:
Simon Glassdaa694d2021-12-18 14:54:30 -0700741 actlog = '%s is not defined in Kconfig (suspicious). Do nothing.' % value
Masahiro Yamada916224c2016-08-22 22:18:21 +0900742 log_color = COLOR_YELLOW
743 suspicious = True
Masahiro Yamadacc008292016-05-19 15:51:56 +0900744 elif action == ACTION_NO_CHANGE:
745 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
746 % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900747 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada07913d12016-08-22 22:18:22 +0900748 elif action == ACTION_SPL_NOT_EXIST:
Simon Glassdaa694d2021-12-18 14:54:30 -0700749 actlog = 'SPL is not enabled for this defconfig. Skip.'
Masahiro Yamada07913d12016-08-22 22:18:22 +0900750 log_color = COLOR_PURPLE
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900751 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700752 sys.exit('Internal Error. This should not happen.')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900753
Masahiro Yamada1d085562016-05-19 15:52:02 +0900754 log += color_text(self.options.color, log_color, actlog) + '\n'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900755
Masahiro Yamada1f169922016-05-19 15:52:00 +0900756 with open(self.dotconfig, 'a') as f:
Masahiro Yamadae423d172016-05-19 15:51:49 +0900757 for (action, value) in results:
758 if action == ACTION_MOVE:
759 f.write(value + '\n')
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900760 updated = True
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900761
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900762 self.results = results
Masahiro Yamada07913d12016-08-22 22:18:22 +0900763 for f in rm_files:
764 os.remove(f)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900765
Masahiro Yamada916224c2016-08-22 22:18:21 +0900766 return (updated, suspicious, log)
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900767
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900768 def check_defconfig(self):
769 """Check the defconfig after savedefconfig
770
771 Returns:
772 Return additional log if moved CONFIGs were removed again by
773 'make savedefconfig'.
774 """
775
776 log = ''
777
778 with open(self.defconfig) as f:
779 defconfig_lines = f.readlines()
780
781 for (action, value) in self.results:
782 if action != ACTION_MOVE:
783 continue
784 if not value + '\n' in defconfig_lines:
785 log += color_text(self.options.color, COLOR_YELLOW,
786 "'%s' was removed by savedefconfig.\n" %
787 value)
788
789 return log
790
Simon Glassd73fcb12017-06-01 19:39:02 -0600791
792class DatabaseThread(threading.Thread):
793 """This thread processes results from Slot threads.
794
795 It collects the data in the master config directary. There is only one
796 result thread, and this helps to serialise the build output.
797 """
798 def __init__(self, config_db, db_queue):
799 """Set up a new result thread
800
801 Args:
802 builder: Builder which will be sent each result
803 """
804 threading.Thread.__init__(self)
805 self.config_db = config_db
806 self.db_queue= db_queue
807
808 def run(self):
809 """Called to start up the result thread.
810
811 We collect the next result job and pass it on to the build.
812 """
813 while True:
814 defconfig, configs = self.db_queue.get()
815 self.config_db[defconfig] = configs
816 self.db_queue.task_done()
817
818
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900819class Slot:
820
821 """A slot to store a subprocess.
822
823 Each instance of this class handles one subprocess.
824 This class is useful to control multiple threads
825 for faster processing.
826 """
827
Simon Glass6821a742017-07-10 14:47:47 -0600828 def __init__(self, toolchains, configs, options, progress, devnull,
829 make_cmd, reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900830 """Create a new process slot.
831
832 Arguments:
Simon Glass6821a742017-07-10 14:47:47 -0600833 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900834 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900835 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900836 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900837 devnull: A file object of '/dev/null'.
838 make_cmd: command name of GNU Make.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500839 reference_src_dir: Determine the true starting config state from this
840 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -0600841 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900842 """
Simon Glass6821a742017-07-10 14:47:47 -0600843 self.toolchains = toolchains
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900844 self.options = options
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900845 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900846 self.build_dir = tempfile.mkdtemp()
847 self.devnull = devnull
848 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500849 self.reference_src_dir = reference_src_dir
Simon Glassd73fcb12017-06-01 19:39:02 -0600850 self.db_queue = db_queue
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900851 self.parser = KconfigParser(configs, options, self.build_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900852 self.state = STATE_IDLE
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900853 self.failed_boards = set()
854 self.suspicious_boards = set()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900855
856 def __del__(self):
857 """Delete the working directory
858
859 This function makes sure the temporary directory is cleaned away
860 even if Python suddenly dies due to error. It should be done in here
Joe Hershbergerf2dae752016-06-10 14:53:29 -0500861 because it is guaranteed the destructor is always invoked when the
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900862 instance of the class gets unreferenced.
863
864 If the subprocess is still running, wait until it finishes.
865 """
866 if self.state != STATE_IDLE:
867 while self.ps.poll() == None:
868 pass
869 shutil.rmtree(self.build_dir)
870
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900871 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900872 """Assign a new subprocess for defconfig and add it to the slot.
873
874 If the slot is vacant, create a new subprocess for processing the
875 given defconfig and add it to the slot. Just returns False if
876 the slot is occupied (i.e. the current subprocess is still running).
877
878 Arguments:
879 defconfig: defconfig name.
880
881 Returns:
882 Return True on success or False on failure
883 """
884 if self.state != STATE_IDLE:
885 return False
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900886
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900887 self.defconfig = defconfig
Masahiro Yamada1d085562016-05-19 15:52:02 +0900888 self.log = ''
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900889 self.current_src_dir = self.reference_src_dir
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900890 self.do_defconfig()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900891 return True
892
893 def poll(self):
894 """Check the status of the subprocess and handle it as needed.
895
896 Returns True if the slot is vacant (i.e. in idle state).
897 If the configuration is successfully finished, assign a new
898 subprocess to build include/autoconf.mk.
899 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900900 parse the .config and the include/autoconf.mk, moving
901 config options to the .config as needed.
902 If the .config was updated, run "make savedefconfig" to sync
903 it, update the original defconfig, and then set the slot back
904 to the idle state.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900905
906 Returns:
907 Return True if the subprocess is terminated, False otherwise
908 """
909 if self.state == STATE_IDLE:
910 return True
911
912 if self.ps.poll() == None:
913 return False
914
915 if self.ps.poll() != 0:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900916 self.handle_error()
917 elif self.state == STATE_DEFCONFIG:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900918 if self.reference_src_dir and not self.current_src_dir:
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500919 self.do_savedefconfig()
920 else:
921 self.do_autoconf()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900922 elif self.state == STATE_AUTOCONF:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900923 if self.current_src_dir:
924 self.current_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500925 self.do_defconfig()
Simon Glassd73fcb12017-06-01 19:39:02 -0600926 elif self.options.build_db:
927 self.do_build_db()
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500928 else:
929 self.do_savedefconfig()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900930 elif self.state == STATE_SAVEDEFCONFIG:
931 self.update_defconfig()
932 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700933 sys.exit('Internal Error. This should not happen.')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900934
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900935 return True if self.state == STATE_IDLE else False
Joe Hershberger96464ba2015-05-19 13:21:17 -0500936
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900937 def handle_error(self):
938 """Handle error cases."""
Masahiro Yamada8513dc02016-05-19 15:52:08 +0900939
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900940 self.log += color_text(self.options.color, COLOR_LIGHT_RED,
Simon Glassdaa694d2021-12-18 14:54:30 -0700941 'Failed to process.\n')
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900942 if self.options.verbose:
943 self.log += color_text(self.options.color, COLOR_LIGHT_CYAN,
Markus Klotzbuecher4f5c5e92020-02-12 20:46:45 +0100944 self.ps.stderr.read().decode())
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900945 self.finish(False)
Joe Hershberger96464ba2015-05-19 13:21:17 -0500946
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900947 def do_defconfig(self):
948 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900949
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900950 cmd = list(self.make_cmd)
951 cmd.append(self.defconfig)
952 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900953 stderr=subprocess.PIPE,
954 cwd=self.current_src_dir)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900955 self.state = STATE_DEFCONFIG
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900956
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900957 def do_autoconf(self):
Simon Glassf3b8e642017-06-01 19:39:01 -0600958 """Run 'make AUTO_CONF_PATH'."""
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900959
Simon Glass6821a742017-07-10 14:47:47 -0600960 arch = self.parser.get_arch()
961 try:
962 toolchain = self.toolchains.Select(arch)
963 except ValueError:
Masahiro Yamada1d085562016-05-19 15:52:02 +0900964 self.log += color_text(self.options.color, COLOR_YELLOW,
Chris Packhamce3ba452017-08-27 20:00:51 +1200965 "Tool chain for '%s' is missing. Do nothing.\n" % arch)
Masahiro Yamada4efef992016-05-19 15:52:03 +0900966 self.finish(False)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900967 return
Simon Glass793dca32019-10-31 07:42:57 -0600968 env = toolchain.MakeEnvironment(False)
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900969
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900970 cmd = list(self.make_cmd)
Joe Hershberger7740f652015-05-19 13:21:18 -0500971 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Simon Glassf3b8e642017-06-01 19:39:01 -0600972 cmd.append(AUTO_CONF_PATH)
Simon Glass6821a742017-07-10 14:47:47 -0600973 self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900974 stderr=subprocess.PIPE,
975 cwd=self.current_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900976 self.state = STATE_AUTOCONF
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900977
Simon Glassd73fcb12017-06-01 19:39:02 -0600978 def do_build_db(self):
979 """Add the board to the database"""
980 configs = {}
981 with open(os.path.join(self.build_dir, AUTO_CONF_PATH)) as fd:
982 for line in fd.readlines():
983 if line.startswith('CONFIG'):
984 config, value = line.split('=', 1)
985 configs[config] = value.rstrip()
986 self.db_queue.put([self.defconfig, configs])
987 self.finish(True)
988
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900989 def do_savedefconfig(self):
990 """Update the .config and run 'make savedefconfig'."""
991
Masahiro Yamada916224c2016-08-22 22:18:21 +0900992 (updated, suspicious, log) = self.parser.update_dotconfig()
993 if suspicious:
994 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900995 self.log += log
996
997 if not self.options.force_sync and not updated:
998 self.finish(True)
999 return
1000 if updated:
1001 self.log += color_text(self.options.color, COLOR_LIGHT_GREEN,
Simon Glassdaa694d2021-12-18 14:54:30 -07001002 'Syncing by savedefconfig...\n')
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001003 else:
Simon Glassdaa694d2021-12-18 14:54:30 -07001004 self.log += 'Syncing by savedefconfig (forced by option)...\n'
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001005
1006 cmd = list(self.make_cmd)
1007 cmd.append('savedefconfig')
1008 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
1009 stderr=subprocess.PIPE)
1010 self.state = STATE_SAVEDEFCONFIG
1011
1012 def update_defconfig(self):
1013 """Update the input defconfig and go back to the idle state."""
1014
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001015 log = self.parser.check_defconfig()
1016 if log:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001017 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001018 self.log += log
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001019 orig_defconfig = os.path.join('configs', self.defconfig)
1020 new_defconfig = os.path.join(self.build_dir, 'defconfig')
1021 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
1022
1023 if updated:
Joe Hershberger06cc1d32016-06-10 14:53:30 -05001024 self.log += color_text(self.options.color, COLOR_LIGHT_BLUE,
Simon Glassdaa694d2021-12-18 14:54:30 -07001025 'defconfig was updated.\n')
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001026
1027 if not self.options.dry_run and updated:
1028 shutil.move(new_defconfig, orig_defconfig)
1029 self.finish(True)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001030
Masahiro Yamada4efef992016-05-19 15:52:03 +09001031 def finish(self, success):
1032 """Display log along with progress and go to the idle state.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001033
1034 Arguments:
Masahiro Yamada4efef992016-05-19 15:52:03 +09001035 success: Should be True when the defconfig was processed
1036 successfully, or False when it fails.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001037 """
1038 # output at least 30 characters to hide the "* defconfigs out of *".
1039 log = self.defconfig.ljust(30) + '\n'
1040
1041 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
1042 # Some threads are running in parallel.
1043 # Print log atomically to not mix up logs from different threads.
Simon Glass793dca32019-10-31 07:42:57 -06001044 print(log, file=(sys.stdout if success else sys.stderr))
Masahiro Yamada4efef992016-05-19 15:52:03 +09001045
1046 if not success:
1047 if self.options.exit_on_error:
Simon Glassdaa694d2021-12-18 14:54:30 -07001048 sys.exit('Exit on error.')
Masahiro Yamada4efef992016-05-19 15:52:03 +09001049 # If --exit-on-error flag is not set, skip this board and continue.
1050 # Record the failed board.
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001051 self.failed_boards.add(self.defconfig)
Masahiro Yamada4efef992016-05-19 15:52:03 +09001052
Masahiro Yamada1d085562016-05-19 15:52:02 +09001053 self.progress.inc()
1054 self.progress.show()
Masahiro Yamada4efef992016-05-19 15:52:03 +09001055 self.state = STATE_IDLE
Masahiro Yamada1d085562016-05-19 15:52:02 +09001056
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001057 def get_failed_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001058 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001059 """
1060 return self.failed_boards
1061
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001062 def get_suspicious_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001063 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001064 """
Masahiro Yamada916224c2016-08-22 22:18:21 +09001065 return self.suspicious_boards - self.failed_boards
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001066
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001067class Slots:
1068
1069 """Controller of the array of subprocess slots."""
1070
Simon Glass6821a742017-07-10 14:47:47 -06001071 def __init__(self, toolchains, configs, options, progress,
1072 reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001073 """Create a new slots controller.
1074
1075 Arguments:
Simon Glass6821a742017-07-10 14:47:47 -06001076 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001077 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001078 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001079 progress: A progress indicator.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001080 reference_src_dir: Determine the true starting config state from this
1081 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -06001082 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001083 """
1084 self.options = options
1085 self.slots = []
1086 devnull = get_devnull()
1087 make_cmd = get_make_cmd()
1088 for i in range(options.jobs):
Simon Glass6821a742017-07-10 14:47:47 -06001089 self.slots.append(Slot(toolchains, configs, options, progress,
1090 devnull, make_cmd, reference_src_dir,
1091 db_queue))
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001092
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001093 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001094 """Add a new subprocess if a vacant slot is found.
1095
1096 Arguments:
1097 defconfig: defconfig name to be put into.
1098
1099 Returns:
1100 Return True on success or False on failure
1101 """
1102 for slot in self.slots:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001103 if slot.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001104 return True
1105 return False
1106
1107 def available(self):
1108 """Check if there is a vacant slot.
1109
1110 Returns:
1111 Return True if at lease one vacant slot is found, False otherwise.
1112 """
1113 for slot in self.slots:
1114 if slot.poll():
1115 return True
1116 return False
1117
1118 def empty(self):
1119 """Check if all slots are vacant.
1120
1121 Returns:
1122 Return True if all the slots are vacant, False otherwise.
1123 """
1124 ret = True
1125 for slot in self.slots:
1126 if not slot.poll():
1127 ret = False
1128 return ret
1129
1130 def show_failed_boards(self):
1131 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001132 boards = set()
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001133 output_file = 'moveconfig.failed'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001134
1135 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001136 boards |= slot.get_failed_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001137
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001138 if boards:
1139 boards = '\n'.join(boards) + '\n'
Simon Glassdaa694d2021-12-18 14:54:30 -07001140 msg = 'The following boards were not processed due to error:\n'
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001141 msg += boards
Simon Glassdaa694d2021-12-18 14:54:30 -07001142 msg += '(the list has been saved in %s)\n' % output_file
Simon Glass793dca32019-10-31 07:42:57 -06001143 print(color_text(self.options.color, COLOR_LIGHT_RED,
1144 msg), file=sys.stderr)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001145
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001146 with open(output_file, 'w') as f:
1147 f.write(boards)
Joe Hershberger2559cd82015-05-19 13:21:22 -05001148
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001149 def show_suspicious_boards(self):
1150 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001151 boards = set()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001152 output_file = 'moveconfig.suspicious'
1153
1154 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001155 boards |= slot.get_suspicious_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001156
1157 if boards:
1158 boards = '\n'.join(boards) + '\n'
Simon Glassdaa694d2021-12-18 14:54:30 -07001159 msg = 'The following boards might have been converted incorrectly.\n'
1160 msg += 'It is highly recommended to check them manually:\n'
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001161 msg += boards
Simon Glassdaa694d2021-12-18 14:54:30 -07001162 msg += '(the list has been saved in %s)\n' % output_file
Simon Glass793dca32019-10-31 07:42:57 -06001163 print(color_text(self.options.color, COLOR_YELLOW,
1164 msg), file=sys.stderr)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001165
1166 with open(output_file, 'w') as f:
1167 f.write(boards)
1168
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001169class ReferenceSource:
1170
1171 """Reference source against which original configs should be parsed."""
1172
1173 def __init__(self, commit):
1174 """Create a reference source directory based on a specified commit.
1175
1176 Arguments:
1177 commit: commit to git-clone
1178 """
1179 self.src_dir = tempfile.mkdtemp()
Simon Glassdaa694d2021-12-18 14:54:30 -07001180 print('Cloning git repo to a separate work directory...')
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001181 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1182 cwd=self.src_dir)
Simon Glass793dca32019-10-31 07:42:57 -06001183 print("Checkout '%s' to build the original autoconf.mk." % \
1184 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip())
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001185 subprocess.check_output(['git', 'checkout', commit],
1186 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001187
1188 def __del__(self):
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001189 """Delete the reference source directory
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001190
1191 This function makes sure the temporary directory is cleaned away
1192 even if Python suddenly dies due to error. It should be done in here
1193 because it is guaranteed the destructor is always invoked when the
1194 instance of the class gets unreferenced.
1195 """
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001196 shutil.rmtree(self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001197
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001198 def get_dir(self):
1199 """Return the absolute path to the reference source directory."""
1200
1201 return self.src_dir
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001202
Simon Glass6821a742017-07-10 14:47:47 -06001203def move_config(toolchains, configs, options, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001204 """Move config options to defconfig files.
1205
1206 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001207 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001208 options: option flags
1209 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001210 if len(configs) == 0:
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001211 if options.force_sync:
Simon Glass793dca32019-10-31 07:42:57 -06001212 print('No CONFIG is specified. You are probably syncing defconfigs.', end=' ')
Simon Glassd73fcb12017-06-01 19:39:02 -06001213 elif options.build_db:
Simon Glass793dca32019-10-31 07:42:57 -06001214 print('Building %s database' % CONFIG_DATABASE)
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001215 else:
Simon Glass793dca32019-10-31 07:42:57 -06001216 print('Neither CONFIG nor --force-sync is specified. Nothing will happen.', end=' ')
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001217 else:
Simon Glass793dca32019-10-31 07:42:57 -06001218 print('Move ' + ', '.join(configs), end=' ')
1219 print('(jobs: %d)\n' % options.jobs)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001220
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001221 if options.git_ref:
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001222 reference_src = ReferenceSource(options.git_ref)
1223 reference_src_dir = reference_src.get_dir()
1224 else:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001225 reference_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001226
Joe Hershberger91040e82015-05-19 13:21:19 -05001227 if options.defconfigs:
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +09001228 defconfigs = get_matched_defconfigs(options.defconfigs)
Joe Hershberger91040e82015-05-19 13:21:19 -05001229 else:
Masahiro Yamada684c3062016-07-25 19:15:28 +09001230 defconfigs = get_all_defconfigs()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001231
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001232 progress = Progress(len(defconfigs))
Simon Glass6821a742017-07-10 14:47:47 -06001233 slots = Slots(toolchains, configs, options, progress, reference_src_dir,
1234 db_queue)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001235
1236 # Main loop to process defconfig files:
1237 # Add a new subprocess into a vacant slot.
1238 # Sleep if there is no available slot.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001239 for defconfig in defconfigs:
1240 while not slots.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001241 while not slots.available():
1242 # No available slot: sleep for a while
1243 time.sleep(SLEEP_TIME)
1244
1245 # wait until all the subprocesses finish
1246 while not slots.empty():
1247 time.sleep(SLEEP_TIME)
1248
Simon Glass793dca32019-10-31 07:42:57 -06001249 print('')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001250 slots.show_failed_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001251 slots.show_suspicious_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001252
Simon Glasscb008832017-06-15 21:39:33 -06001253def find_kconfig_rules(kconf, config, imply_config):
1254 """Check whether a config has a 'select' or 'imply' keyword
1255
1256 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -04001257 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -06001258 config: Name of config to check (without CONFIG_ prefix)
1259 imply_config: Implying config (without CONFIG_ prefix) which may or
1260 may not have an 'imply' for 'config')
1261
1262 Returns:
1263 Symbol object for 'config' if found, else None
1264 """
Tom Rini65e05dd2019-09-20 17:42:09 -04001265 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -06001266 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001267 for sel, cond in (sym.selects + sym.implies):
Simon Glassa3627082021-12-18 08:09:42 -07001268 if sel.name == config:
Simon Glasscb008832017-06-15 21:39:33 -06001269 return sym
1270 return None
1271
1272def check_imply_rule(kconf, config, imply_config):
1273 """Check if we can add an 'imply' option
1274
1275 This finds imply_config in the Kconfig and looks to see if it is possible
1276 to add an 'imply' for 'config' to that part of the Kconfig.
1277
1278 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -04001279 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -06001280 config: Name of config to check (without CONFIG_ prefix)
1281 imply_config: Implying config (without CONFIG_ prefix) which may or
1282 may not have an 'imply' for 'config')
1283
1284 Returns:
1285 tuple:
1286 filename of Kconfig file containing imply_config, or None if none
1287 line number within the Kconfig file, or 0 if none
1288 message indicating the result
1289 """
Tom Rini65e05dd2019-09-20 17:42:09 -04001290 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -06001291 if not sym:
1292 return 'cannot find sym'
Simon Glassea40b202021-07-21 21:35:53 -06001293 nodes = sym.nodes
1294 if len(nodes) != 1:
1295 return '%d locations' % len(nodes)
Simon Glassa3627082021-12-18 08:09:42 -07001296 node = nodes[0]
1297 fname, linenum = node.filename, node.linenr
Simon Glasscb008832017-06-15 21:39:33 -06001298 cwd = os.getcwd()
1299 if cwd and fname.startswith(cwd):
1300 fname = fname[len(cwd) + 1:]
1301 file_line = ' at %s:%d' % (fname, linenum)
1302 with open(fname) as fd:
1303 data = fd.read().splitlines()
1304 if data[linenum - 1] != 'config %s' % imply_config:
1305 return None, 0, 'bad sym format %s%s' % (data[linenum], file_line)
1306 return fname, linenum, 'adding%s' % file_line
1307
1308def add_imply_rule(config, fname, linenum):
1309 """Add a new 'imply' option to a Kconfig
1310
1311 Args:
1312 config: config option to add an imply for (without CONFIG_ prefix)
1313 fname: Kconfig filename to update
1314 linenum: Line number to place the 'imply' before
1315
1316 Returns:
1317 Message indicating the result
1318 """
1319 file_line = ' at %s:%d' % (fname, linenum)
1320 data = open(fname).read().splitlines()
1321 linenum -= 1
1322
1323 for offset, line in enumerate(data[linenum:]):
1324 if line.strip().startswith('help') or not line:
1325 data.insert(linenum + offset, '\timply %s' % config)
1326 with open(fname, 'w') as fd:
1327 fd.write('\n'.join(data) + '\n')
1328 return 'added%s' % file_line
1329
1330 return 'could not insert%s'
1331
1332(IMPLY_MIN_2, IMPLY_TARGET, IMPLY_CMD, IMPLY_NON_ARCH_BOARD) = (
1333 1, 2, 4, 8)
Simon Glass9b2a2e82017-06-15 21:39:32 -06001334
1335IMPLY_FLAGS = {
1336 'min2': [IMPLY_MIN_2, 'Show options which imply >2 boards (normally >5)'],
1337 'target': [IMPLY_TARGET, 'Allow CONFIG_TARGET_... options to imply'],
1338 'cmd': [IMPLY_CMD, 'Allow CONFIG_CMD_... to imply'],
Simon Glasscb008832017-06-15 21:39:33 -06001339 'non-arch-board': [
1340 IMPLY_NON_ARCH_BOARD,
1341 'Allow Kconfig options outside arch/ and /board/ to imply'],
Simon Glass9b2a2e82017-06-15 21:39:32 -06001342};
1343
Simon Glass9d603392021-12-18 08:09:43 -07001344
1345def read_database():
1346 """Read in the config database
1347
1348 Returns:
1349 tuple:
1350 set of all config options seen (each a str)
1351 set of all defconfigs seen (each a str)
1352 dict of configs for each defconfig:
1353 key: defconfig name, e.g. "MPC8548CDS_legacy_defconfig"
1354 value: dict:
1355 key: CONFIG option
1356 value: Value of option
1357 dict of defconfigs for each config:
1358 key: CONFIG option
1359 value: set of boards using that option
1360
1361 """
1362 configs = {}
1363
1364 # key is defconfig name, value is dict of (CONFIG_xxx, value)
1365 config_db = {}
1366
1367 # Set of all config options we have seen
1368 all_configs = set()
1369
1370 # Set of all defconfigs we have seen
1371 all_defconfigs = set()
1372
1373 defconfig_db = collections.defaultdict(set)
1374 with open(CONFIG_DATABASE) as fd:
1375 for line in fd.readlines():
1376 line = line.rstrip()
1377 if not line: # Separator between defconfigs
1378 config_db[defconfig] = configs
1379 all_defconfigs.add(defconfig)
1380 configs = {}
1381 elif line[0] == ' ': # CONFIG line
1382 config, value = line.strip().split('=', 1)
1383 configs[config] = value
1384 defconfig_db[config].add(defconfig)
1385 all_configs.add(config)
1386 else: # New defconfig
1387 defconfig = line
1388
1389 return all_configs, all_defconfigs, config_db, defconfig_db
1390
1391
Simon Glasscb008832017-06-15 21:39:33 -06001392def do_imply_config(config_list, add_imply, imply_flags, skip_added,
1393 check_kconfig=True, find_superset=False):
Simon Glass99b66602017-06-01 19:39:03 -06001394 """Find CONFIG options which imply those in the list
1395
1396 Some CONFIG options can be implied by others and this can help to reduce
1397 the size of the defconfig files. For example, CONFIG_X86 implies
1398 CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
1399 all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
1400 each of the x86 defconfig files.
1401
1402 This function uses the moveconfig database to find such options. It
1403 displays a list of things that could possibly imply those in the list.
1404 The algorithm ignores any that start with CONFIG_TARGET since these
1405 typically refer to only a few defconfigs (often one). It also does not
1406 display a config with less than 5 defconfigs.
1407
1408 The algorithm works using sets. For each target config in config_list:
1409 - Get the set 'defconfigs' which use that target config
1410 - For each config (from a list of all configs):
1411 - Get the set 'imply_defconfig' of defconfigs which use that config
1412 -
1413 - If imply_defconfigs contains anything not in defconfigs then
1414 this config does not imply the target config
1415
1416 Params:
1417 config_list: List of CONFIG options to check (each a string)
Simon Glasscb008832017-06-15 21:39:33 -06001418 add_imply: Automatically add an 'imply' for each config.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001419 imply_flags: Flags which control which implying configs are allowed
1420 (IMPLY_...)
Simon Glasscb008832017-06-15 21:39:33 -06001421 skip_added: Don't show options which already have an imply added.
1422 check_kconfig: Check if implied symbols already have an 'imply' or
1423 'select' for the target config, and show this information if so.
Simon Glass99b66602017-06-01 19:39:03 -06001424 find_superset: True to look for configs which are a superset of those
1425 already found. So for example if CONFIG_EXYNOS5 implies an option,
1426 but CONFIG_EXYNOS covers a larger set of defconfigs and also
1427 implies that option, this will drop the former in favour of the
1428 latter. In practice this option has not proved very used.
1429
1430 Note the terminoloy:
1431 config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
1432 defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
1433 """
Simon Glasscb008832017-06-15 21:39:33 -06001434 kconf = KconfigScanner().conf if check_kconfig else None
1435 if add_imply and add_imply != 'all':
Simon Glassa3627082021-12-18 08:09:42 -07001436 add_imply = add_imply.split(',')
Simon Glasscb008832017-06-15 21:39:33 -06001437
Simon Glass9d603392021-12-18 08:09:43 -07001438 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
Simon Glass99b66602017-06-01 19:39:03 -06001439
Simon Glassa3627082021-12-18 08:09:42 -07001440 # Work through each target config option in turn, independently
Simon Glass99b66602017-06-01 19:39:03 -06001441 for config in config_list:
1442 defconfigs = defconfig_db.get(config)
1443 if not defconfigs:
Simon Glass793dca32019-10-31 07:42:57 -06001444 print('%s not found in any defconfig' % config)
Simon Glass99b66602017-06-01 19:39:03 -06001445 continue
1446
1447 # Get the set of defconfigs without this one (since a config cannot
1448 # imply itself)
1449 non_defconfigs = all_defconfigs - defconfigs
1450 num_defconfigs = len(defconfigs)
Simon Glass793dca32019-10-31 07:42:57 -06001451 print('%s found in %d/%d defconfigs' % (config, num_defconfigs,
1452 len(all_configs)))
Simon Glass99b66602017-06-01 19:39:03 -06001453
1454 # This will hold the results: key=config, value=defconfigs containing it
1455 imply_configs = {}
1456 rest_configs = all_configs - set([config])
1457
1458 # Look at every possible config, except the target one
1459 for imply_config in rest_configs:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001460 if 'ERRATUM' in imply_config:
Simon Glass99b66602017-06-01 19:39:03 -06001461 continue
Simon Glass9b2a2e82017-06-15 21:39:32 -06001462 if not (imply_flags & IMPLY_CMD):
1463 if 'CONFIG_CMD' in imply_config:
1464 continue
1465 if not (imply_flags & IMPLY_TARGET):
1466 if 'CONFIG_TARGET' in imply_config:
1467 continue
Simon Glass99b66602017-06-01 19:39:03 -06001468
1469 # Find set of defconfigs that have this config
1470 imply_defconfig = defconfig_db[imply_config]
1471
1472 # Get the intersection of this with defconfigs containing the
1473 # target config
1474 common_defconfigs = imply_defconfig & defconfigs
1475
1476 # Get the set of defconfigs containing this config which DO NOT
1477 # also contain the taret config. If this set is non-empty it means
1478 # that this config affects other defconfigs as well as (possibly)
1479 # the ones affected by the target config. This means it implies
1480 # things we don't want to imply.
1481 not_common_defconfigs = imply_defconfig & non_defconfigs
1482 if not_common_defconfigs:
1483 continue
1484
1485 # If there are common defconfigs, imply_config may be useful
1486 if common_defconfigs:
1487 skip = False
1488 if find_superset:
Simon Glass793dca32019-10-31 07:42:57 -06001489 for prev in list(imply_configs.keys()):
Simon Glass99b66602017-06-01 19:39:03 -06001490 prev_count = len(imply_configs[prev])
1491 count = len(common_defconfigs)
1492 if (prev_count > count and
1493 (imply_configs[prev] & common_defconfigs ==
1494 common_defconfigs)):
1495 # skip imply_config because prev is a superset
1496 skip = True
1497 break
1498 elif count > prev_count:
1499 # delete prev because imply_config is a superset
1500 del imply_configs[prev]
1501 if not skip:
1502 imply_configs[imply_config] = common_defconfigs
1503
1504 # Now we have a dict imply_configs of configs which imply each config
1505 # The value of each dict item is the set of defconfigs containing that
1506 # config. Rank them so that we print the configs that imply the largest
1507 # number of defconfigs first.
Simon Glasscb008832017-06-15 21:39:33 -06001508 ranked_iconfigs = sorted(imply_configs,
Simon Glass99b66602017-06-01 19:39:03 -06001509 key=lambda k: len(imply_configs[k]), reverse=True)
Simon Glasscb008832017-06-15 21:39:33 -06001510 kconfig_info = ''
1511 cwd = os.getcwd()
1512 add_list = collections.defaultdict(list)
1513 for iconfig in ranked_iconfigs:
1514 num_common = len(imply_configs[iconfig])
Simon Glass99b66602017-06-01 19:39:03 -06001515
1516 # Don't bother if there are less than 5 defconfigs affected.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001517 if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
Simon Glass99b66602017-06-01 19:39:03 -06001518 continue
Simon Glasscb008832017-06-15 21:39:33 -06001519 missing = defconfigs - imply_configs[iconfig]
Simon Glass99b66602017-06-01 19:39:03 -06001520 missing_str = ', '.join(missing) if missing else 'all'
1521 missing_str = ''
Simon Glasscb008832017-06-15 21:39:33 -06001522 show = True
1523 if kconf:
1524 sym = find_kconfig_rules(kconf, config[CONFIG_LEN:],
1525 iconfig[CONFIG_LEN:])
1526 kconfig_info = ''
1527 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001528 nodes = sym.nodes
1529 if len(nodes) == 1:
1530 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001531 if cwd and fname.startswith(cwd):
1532 fname = fname[len(cwd) + 1:]
1533 kconfig_info = '%s:%d' % (fname, linenum)
1534 if skip_added:
1535 show = False
1536 else:
Tom Rini65e05dd2019-09-20 17:42:09 -04001537 sym = kconf.syms.get(iconfig[CONFIG_LEN:])
Simon Glasscb008832017-06-15 21:39:33 -06001538 fname = ''
1539 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001540 nodes = sym.nodes
1541 if len(nodes) == 1:
1542 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001543 if cwd and fname.startswith(cwd):
1544 fname = fname[len(cwd) + 1:]
1545 in_arch_board = not sym or (fname.startswith('arch') or
1546 fname.startswith('board'))
1547 if (not in_arch_board and
1548 not (imply_flags & IMPLY_NON_ARCH_BOARD)):
1549 continue
1550
1551 if add_imply and (add_imply == 'all' or
1552 iconfig in add_imply):
1553 fname, linenum, kconfig_info = (check_imply_rule(kconf,
1554 config[CONFIG_LEN:], iconfig[CONFIG_LEN:]))
1555 if fname:
1556 add_list[fname].append(linenum)
1557
1558 if show and kconfig_info != 'skip':
Simon Glass793dca32019-10-31 07:42:57 -06001559 print('%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
1560 kconfig_info, missing_str))
Simon Glasscb008832017-06-15 21:39:33 -06001561
1562 # Having collected a list of things to add, now we add them. We process
1563 # each file from the largest line number to the smallest so that
1564 # earlier additions do not affect our line numbers. E.g. if we added an
1565 # imply at line 20 it would change the position of each line after
1566 # that.
Simon Glass793dca32019-10-31 07:42:57 -06001567 for fname, linenums in add_list.items():
Simon Glasscb008832017-06-15 21:39:33 -06001568 for linenum in sorted(linenums, reverse=True):
1569 add_imply_rule(config[CONFIG_LEN:], fname, linenum)
Simon Glass99b66602017-06-01 19:39:03 -06001570
1571
Simon Glass65d7fce2021-12-18 08:09:46 -07001572def do_find_config(config_list):
1573 """Find boards with a given combination of CONFIGs
1574
1575 Params:
1576 config_list: List of CONFIG options to check (each a string consisting
1577 of a config option, with or without a CONFIG_ prefix. If an option
1578 is preceded by a tilde (~) then it must be false, otherwise it must
1579 be true)
1580 """
1581 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
1582
1583 # Get the whitelist
1584 with open('scripts/config_whitelist.txt') as inf:
1585 adhoc_configs = set(inf.read().splitlines())
1586
1587 # Start with all defconfigs
1588 out = all_defconfigs
1589
1590 # Work through each config in turn
1591 adhoc = []
1592 for item in config_list:
1593 # Get the real config name and whether we want this config or not
1594 cfg = item
1595 want = True
1596 if cfg[0] == '~':
1597 want = False
1598 cfg = cfg[1:]
1599
1600 if cfg in adhoc_configs:
1601 adhoc.append(cfg)
1602 continue
1603
1604 # Search everything that is still in the running. If it has a config
1605 # that we want, or doesn't have one that we don't, add it into the
1606 # running for the next stage
1607 in_list = out
1608 out = set()
1609 for defc in in_list:
1610 has_cfg = cfg in config_db[defc]
1611 if has_cfg == want:
1612 out.add(defc)
1613 if adhoc:
1614 print(f"Error: Not in Kconfig: %s" % ' '.join(adhoc))
1615 else:
1616 print(f'{len(out)} matches')
1617 print(' '.join(out))
1618
1619
1620def prefix_config(cfg):
1621 """Prefix a config with CONFIG_ if needed
1622
1623 This handles ~ operator, which indicates that the CONFIG should be disabled
1624
1625 >>> prefix_config('FRED')
1626 'CONFIG_FRED'
1627 >>> prefix_config('CONFIG_FRED')
1628 'CONFIG_FRED'
1629 >>> prefix_config('~FRED')
1630 '~CONFIG_FRED'
1631 >>> prefix_config('~CONFIG_FRED')
1632 '~CONFIG_FRED'
1633 >>> prefix_config('A123')
1634 'CONFIG_A123'
1635 """
1636 op = ''
1637 if cfg[0] == '~':
1638 op = cfg[0]
1639 cfg = cfg[1:]
1640 if not cfg.startswith('CONFIG_'):
1641 cfg = 'CONFIG_' + cfg
1642 return op + cfg
1643
1644
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001645def main():
1646 try:
1647 cpu_count = multiprocessing.cpu_count()
1648 except NotImplementedError:
1649 cpu_count = 1
1650
1651 parser = optparse.OptionParser()
1652 # Add options here
Simon Glasscb008832017-06-15 21:39:33 -06001653 parser.add_option('-a', '--add-imply', type='string', default='',
1654 help='comma-separated list of CONFIG options to add '
1655 "an 'imply' statement to for the CONFIG in -i")
1656 parser.add_option('-A', '--skip-added', action='store_true', default=False,
1657 help="don't show options which are already marked as "
1658 'implying others')
Simon Glassd73fcb12017-06-01 19:39:02 -06001659 parser.add_option('-b', '--build-db', action='store_true', default=False,
1660 help='build a CONFIG database')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001661 parser.add_option('-c', '--color', action='store_true', default=False,
1662 help='display the log in color')
Simon Glass9ede2122016-09-12 23:18:21 -06001663 parser.add_option('-C', '--commit', action='store_true', default=False,
1664 help='Create a git commit for the operation')
Joe Hershberger91040e82015-05-19 13:21:19 -05001665 parser.add_option('-d', '--defconfigs', type='string',
Simon Glassee4e61b2017-06-01 19:38:59 -06001666 help='a file containing a list of defconfigs to move, '
1667 "one per line (for example 'snow_defconfig') "
1668 "or '-' to read from stdin")
Simon Glasse1ae5632021-12-18 08:09:44 -07001669 parser.add_option('-e', '--exit-on-error', action='store_true',
1670 default=False,
1671 help='exit immediately on any error')
Simon Glass65d7fce2021-12-18 08:09:46 -07001672 parser.add_option('-f', '--find', action='store_true', default=False,
1673 help='Find boards with a given config combination')
Simon Glasse1ae5632021-12-18 08:09:44 -07001674 parser.add_option('-H', '--headers-only', dest='cleanup_headers_only',
1675 action='store_true', default=False,
1676 help='only cleanup the headers')
Simon Glass99b66602017-06-01 19:39:03 -06001677 parser.add_option('-i', '--imply', action='store_true', default=False,
1678 help='find options which imply others')
Simon Glass9b2a2e82017-06-15 21:39:32 -06001679 parser.add_option('-I', '--imply-flags', type='string', default='',
1680 help="control the -i option ('help' for help")
Simon Glasse1ae5632021-12-18 08:09:44 -07001681 parser.add_option('-j', '--jobs', type='int', default=cpu_count,
1682 help='the number of jobs to run simultaneously')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001683 parser.add_option('-n', '--dry-run', action='store_true', default=False,
1684 help='perform a trial run (show log with no changes)')
Simon Glasse1ae5632021-12-18 08:09:44 -07001685 parser.add_option('-r', '--git-ref', type='string',
1686 help='the git ref to clone for building the autoconf.mk')
Masahiro Yamada8513dc02016-05-19 15:52:08 +09001687 parser.add_option('-s', '--force-sync', action='store_true', default=False,
1688 help='force sync by savedefconfig')
Masahiro Yamada07913d12016-08-22 22:18:22 +09001689 parser.add_option('-S', '--spl', action='store_true', default=False,
1690 help='parse config options defined for SPL build')
Simon Glasse1ae5632021-12-18 08:09:44 -07001691 parser.add_option('-t', '--test', action='store_true', default=False,
1692 help='run unit tests')
Simon Glass6b403df2016-09-12 23:18:20 -06001693 parser.add_option('-y', '--yes', action='store_true', default=False,
1694 help="respond 'yes' to any prompts")
Joe Hershberger95bf9c72015-05-19 13:21:24 -05001695 parser.add_option('-v', '--verbose', action='store_true', default=False,
1696 help='show any build errors as boards are built')
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001697 parser.usage += ' CONFIG ...'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001698
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001699 (options, configs) = parser.parse_args()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001700
Simon Glass84067a52021-12-18 08:09:45 -07001701 if options.test:
1702 sys.argv = [sys.argv[0]]
1703 fail, count = doctest.testmod()
1704 if fail:
1705 return 1
1706 unittest.main()
1707
Simon Glass99b66602017-06-01 19:39:03 -06001708 if len(configs) == 0 and not any((options.force_sync, options.build_db,
Simon Glass65d7fce2021-12-18 08:09:46 -07001709 options.imply, options.find)):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001710 parser.print_usage()
1711 sys.exit(1)
1712
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001713 # prefix the option name with CONFIG_ if missing
Simon Glass65d7fce2021-12-18 08:09:46 -07001714 configs = [prefix_config(cfg) for cfg in configs]
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001715
Joe Hershberger2144f882015-05-19 13:21:20 -05001716 check_top_directory()
1717
Simon Glass99b66602017-06-01 19:39:03 -06001718 if options.imply:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001719 imply_flags = 0
Simon Glassdee36c72017-07-10 14:47:46 -06001720 if options.imply_flags == 'all':
1721 imply_flags = -1
1722
1723 elif options.imply_flags:
1724 for flag in options.imply_flags.split(','):
1725 bad = flag not in IMPLY_FLAGS
1726 if bad:
Simon Glass793dca32019-10-31 07:42:57 -06001727 print("Invalid flag '%s'" % flag)
Simon Glassdee36c72017-07-10 14:47:46 -06001728 if flag == 'help' or bad:
Simon Glass793dca32019-10-31 07:42:57 -06001729 print("Imply flags: (separate with ',')")
1730 for name, info in IMPLY_FLAGS.items():
1731 print(' %-15s: %s' % (name, info[1]))
Simon Glassdee36c72017-07-10 14:47:46 -06001732 parser.print_usage()
1733 sys.exit(1)
1734 imply_flags |= IMPLY_FLAGS[flag][0]
Simon Glass9b2a2e82017-06-15 21:39:32 -06001735
Simon Glasscb008832017-06-15 21:39:33 -06001736 do_imply_config(configs, options.add_imply, imply_flags,
1737 options.skip_added)
Simon Glass99b66602017-06-01 19:39:03 -06001738 return
1739
Simon Glass65d7fce2021-12-18 08:09:46 -07001740 if options.find:
1741 do_find_config(configs)
1742 return
1743
Simon Glassd73fcb12017-06-01 19:39:02 -06001744 config_db = {}
Simon Glass793dca32019-10-31 07:42:57 -06001745 db_queue = queue.Queue()
Simon Glassd73fcb12017-06-01 19:39:02 -06001746 t = DatabaseThread(config_db, db_queue)
1747 t.setDaemon(True)
1748 t.start()
1749
Joe Hershberger2144f882015-05-19 13:21:20 -05001750 if not options.cleanup_headers_only:
Masahiro Yamadaf7536f72016-07-25 19:15:23 +09001751 check_clean_directory()
Simon Glass793dca32019-10-31 07:42:57 -06001752 bsettings.Setup('')
Simon Glass6821a742017-07-10 14:47:47 -06001753 toolchains = toolchain.Toolchains()
1754 toolchains.GetSettings()
1755 toolchains.Scan(verbose=False)
1756 move_config(toolchains, configs, options, db_queue)
Simon Glassd73fcb12017-06-01 19:39:02 -06001757 db_queue.join()
Joe Hershberger2144f882015-05-19 13:21:20 -05001758
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001759 if configs:
Masahiro Yamadae9ea1222016-07-25 19:15:26 +09001760 cleanup_headers(configs, options)
Masahiro Yamada9ab02962016-07-25 19:15:29 +09001761 cleanup_extra_options(configs, options)
Chris Packhamca438342017-05-02 21:30:47 +12001762 cleanup_whitelist(configs, options)
Chris Packhamf90df592017-05-02 21:30:48 +12001763 cleanup_readme(configs, options)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001764
Simon Glass9ede2122016-09-12 23:18:21 -06001765 if options.commit:
1766 subprocess.call(['git', 'add', '-u'])
1767 if configs:
1768 msg = 'Convert %s %sto Kconfig' % (configs[0],
1769 'et al ' if len(configs) > 1 else '')
1770 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1771 '\n '.join(configs))
1772 else:
1773 msg = 'configs: Resync with savedefconfig'
1774 msg += '\n\nRsync all defconfig files using moveconfig.py'
1775 subprocess.call(['git', 'commit', '-s', '-m', msg])
1776
Simon Glassd73fcb12017-06-01 19:39:02 -06001777 if options.build_db:
1778 with open(CONFIG_DATABASE, 'w') as fd:
Simon Glass793dca32019-10-31 07:42:57 -06001779 for defconfig, configs in config_db.items():
Simon Glassc79d18c2017-08-13 16:02:54 -06001780 fd.write('%s\n' % defconfig)
Simon Glassd73fcb12017-06-01 19:39:02 -06001781 for config in sorted(configs.keys()):
Simon Glassc79d18c2017-08-13 16:02:54 -06001782 fd.write(' %s=%s\n' % (config, configs[config]))
1783 fd.write('\n')
Simon Glassd73fcb12017-06-01 19:39:02 -06001784
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001785if __name__ == '__main__':
Simon Glass65d7fce2021-12-18 08:09:46 -07001786 sys.exit(main())