blob: 0b0ce64e81fecc1d5d70611f618fd5d260a47cfc [file] [log] [blame]
Simon Glass1f701862019-10-31 07:42:57 -06001#!/usr/bin/env python3
Tom Rini10e47792018-05-06 17:58:06 -04002# SPDX-License-Identifier: GPL-2.0+
Masahiro Yamadab6160812015-05-20 11:36:07 +09003#
4# Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5#
Masahiro Yamadab6160812015-05-20 11:36:07 +09006
7"""
8Move config options from headers to defconfig files.
9
Simon Glass83cc72e2021-07-21 21:35:51 -060010See doc/develop/moveconfig.rst for documentation.
Masahiro Yamadab6160812015-05-20 11:36:07 +090011"""
12
Simon Glassd9c1da22021-12-18 14:54:31 -070013from argparse import ArgumentParser
Simon Glassc6e73cf2017-06-01 19:39:03 -060014import collections
Simon Glassb3464eb2021-12-18 14:54:35 -070015from contextlib import ExitStack
Simon Glassbb57be72021-12-18 08:09:45 -070016import doctest
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
Masahiro Yamadab6160812015-05-20 11:36:07 +090021import os
Simon Glass1f701862019-10-31 07:42:57 -060022import queue
Masahiro Yamadab6160812015-05-20 11:36:07 +090023import re
24import shutil
25import subprocess
26import sys
27import tempfile
Simon Glass43cf08f2017-06-01 19:39:02 -060028import threading
Masahiro Yamadab6160812015-05-20 11:36:07 +090029import time
Simon Glassbb57be72021-12-18 08:09:45 -070030import unittest
Masahiro Yamadab6160812015-05-20 11:36:07 +090031
Simon Glass5367e572023-09-23 13:43:52 -060032import asteval
Simon Glassf0d9c102020-04-17 18:09:02 -060033from buildman import bsettings
34from buildman import kconfiglib
35from buildman import toolchain
Simon Glass44116332017-06-15 21:39:33 -060036
Masahiro Yamadab6160812015-05-20 11:36:07 +090037SHOW_GNU_MAKE = 'scripts/show-gnu-make'
38SLEEP_TIME=0.03
39
Masahiro Yamadab6160812015-05-20 11:36:07 +090040STATE_IDLE = 0
41STATE_DEFCONFIG = 1
42STATE_AUTOCONF = 2
Joe Hershberger166edec2015-05-19 13:21:17 -050043STATE_SAVEDEFCONFIG = 3
Masahiro Yamadab6160812015-05-20 11:36:07 +090044
Masahiro Yamadab6160812015-05-20 11:36:07 +090045COLOR_BLACK = '0;30'
46COLOR_RED = '0;31'
47COLOR_GREEN = '0;32'
48COLOR_BROWN = '0;33'
49COLOR_BLUE = '0;34'
50COLOR_PURPLE = '0;35'
51COLOR_CYAN = '0;36'
52COLOR_LIGHT_GRAY = '0;37'
53COLOR_DARK_GRAY = '1;30'
54COLOR_LIGHT_RED = '1;31'
55COLOR_LIGHT_GREEN = '1;32'
56COLOR_YELLOW = '1;33'
57COLOR_LIGHT_BLUE = '1;34'
58COLOR_LIGHT_PURPLE = '1;35'
59COLOR_LIGHT_CYAN = '1;36'
60COLOR_WHITE = '1;37'
61
Simon Glass8fb5bd02017-06-01 19:39:01 -060062AUTO_CONF_PATH = 'include/config/auto.conf'
Simon Glass43cf08f2017-06-01 19:39:02 -060063CONFIG_DATABASE = 'moveconfig.db'
Simon Glass8fb5bd02017-06-01 19:39:01 -060064
Simon Glass44116332017-06-15 21:39:33 -060065CONFIG_LEN = len('CONFIG_')
Simon Glass8fb5bd02017-06-01 19:39:01 -060066
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +020067SIZES = {
Simon Glassdc634d92021-12-18 14:54:30 -070068 'SZ_1': 0x00000001, 'SZ_2': 0x00000002,
69 'SZ_4': 0x00000004, 'SZ_8': 0x00000008,
70 'SZ_16': 0x00000010, 'SZ_32': 0x00000020,
71 'SZ_64': 0x00000040, 'SZ_128': 0x00000080,
72 'SZ_256': 0x00000100, 'SZ_512': 0x00000200,
73 'SZ_1K': 0x00000400, 'SZ_2K': 0x00000800,
74 'SZ_4K': 0x00001000, 'SZ_8K': 0x00002000,
75 'SZ_16K': 0x00004000, 'SZ_32K': 0x00008000,
76 'SZ_64K': 0x00010000, 'SZ_128K': 0x00020000,
77 'SZ_256K': 0x00040000, 'SZ_512K': 0x00080000,
78 'SZ_1M': 0x00100000, 'SZ_2M': 0x00200000,
79 'SZ_4M': 0x00400000, 'SZ_8M': 0x00800000,
80 'SZ_16M': 0x01000000, 'SZ_32M': 0x02000000,
81 'SZ_64M': 0x04000000, 'SZ_128M': 0x08000000,
82 'SZ_256M': 0x10000000, 'SZ_512M': 0x20000000,
83 'SZ_1G': 0x40000000, 'SZ_2G': 0x80000000,
84 'SZ_4G': 0x100000000
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +020085}
86
Simon Glasse8037552022-02-08 11:49:45 -070087RE_REMOVE_DEFCONFIG = re.compile(r'(.*)_defconfig')
88
Simon Glass4c4eb7c2023-02-01 13:19:12 -070089# CONFIG symbols present in the build system (from Linux) but not actually used
90# in U-Boot; KCONFIG symbols
91IGNORE_SYMS = ['DEBUG_SECTION_MISMATCH', 'FTRACE_MCOUNT_RECORD', 'GCOV_KERNEL',
92 'GCOV_PROFILE_ALL', 'KALLSYMS', 'KASAN', 'MODVERSIONS', 'SHELL',
93 'TPL_BUILD', 'VPL_BUILD', 'IS_ENABLED', 'FOO', 'IF_ENABLED_INT',
94 'IS_ENABLED_', 'IS_ENABLED_1', 'IS_ENABLED_2', 'IS_ENABLED_3',
95 'SPL_', 'TPL_', 'SPL_FOO', 'TPL_FOO', 'TOOLS_FOO',
96 'ACME', 'SPL_ACME', 'TPL_ACME', 'TRACE_BRANCH_PROFILING',
97 'VAL', '_UNDEFINED', 'SPL_BUILD', ]
98
99SPL_PREFIXES = ['SPL_', 'TPL_', 'VPL_', 'TOOLS_']
100
Masahiro Yamadab6160812015-05-20 11:36:07 +0900101### helper functions ###
Masahiro Yamadab6160812015-05-20 11:36:07 +0900102def check_top_directory():
103 """Exit if we are not at the top of source directory."""
Simon Glassb3464eb2021-12-18 14:54:35 -0700104 for fname in 'README', 'Licenses':
105 if not os.path.exists(fname):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900106 sys.exit('Please run at the top of source directory.')
107
Masahiro Yamada990e6772016-05-19 15:51:54 +0900108def check_clean_directory():
109 """Exit if the source tree is not clean."""
Simon Glassb3464eb2021-12-18 14:54:35 -0700110 for fname in '.config', 'include/config':
111 if os.path.exists(fname):
Masahiro Yamada990e6772016-05-19 15:51:54 +0900112 sys.exit("source tree is not clean, please run 'make mrproper'")
113
Masahiro Yamadab6160812015-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 """
Simon Glassb3464eb2021-12-18 14:54:35 -0700121 with subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE) as proc:
122 ret = proc.communicate()
123 if proc.returncode:
124 sys.exit('GNU Make not found')
Masahiro Yamadab6160812015-05-20 11:36:07 +0900125 return ret[0].rstrip()
126
Simon Glass18774bc2017-06-01 19:38:58 -0600127def get_matched_defconfig(line):
128 """Get the defconfig files that match a pattern
129
130 Args:
Simon Glassb3464eb2021-12-18 14:54:35 -0700131 line (str): Path or filename to match, e.g. 'configs/snow_defconfig' or
Simon Glass18774bc2017-06-01 19:38:58 -0600132 'k2*_defconfig'. If no directory is provided, 'configs/' is
133 prepended
134
135 Returns:
Simon Glassb3464eb2021-12-18 14:54:35 -0700136 list of str: a list of matching defconfig files
Simon Glass18774bc2017-06-01 19:38:58 -0600137 """
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 Yamada3984d6e2016-10-19 14:39:54 +0900145def get_matched_defconfigs(defconfigs_file):
Simon Glass8f3cf312017-06-01 19:38:59 -0600146 """Get all the defconfig files that match the patterns in a file.
147
148 Args:
Simon Glassb3464eb2021-12-18 14:54:35 -0700149 defconfigs_file (str): File containing a list of defconfigs to process,
150 or '-' to read the list from stdin
Simon Glass8f3cf312017-06-01 19:38:59 -0600151
152 Returns:
Simon Glassb3464eb2021-12-18 14:54:35 -0700153 list of str: A list of paths to defconfig files, with no duplicates
Simon Glass8f3cf312017-06-01 19:38:59 -0600154 """
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900155 defconfigs = []
Simon Glassb3464eb2021-12-18 14:54:35 -0700156 with ExitStack() as stack:
157 if defconfigs_file == '-':
158 inf = sys.stdin
159 defconfigs_file = 'stdin'
160 else:
161 inf = stack.enter_context(open(defconfigs_file, encoding='utf-8'))
162 for i, line in enumerate(inf):
163 line = line.strip()
164 if not line:
165 continue # skip blank lines silently
166 if ' ' in line:
167 line = line.split(' ')[0] # handle 'git log' input
168 matched = get_matched_defconfig(line)
169 if not matched:
170 print(f"warning: {defconfigs_file}:{i + 1}: no defconfig matched '{line}'",
171 file=sys.stderr)
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900172
Simon Glassb3464eb2021-12-18 14:54:35 -0700173 defconfigs += matched
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900174
175 # use set() to drop multiple matching
Simon Glassb3464eb2021-12-18 14:54:35 -0700176 return [defconfig[len('configs') + 1:] for defconfig in set(defconfigs)]
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900177
Masahiro Yamada58175e32016-07-25 19:15:28 +0900178def get_all_defconfigs():
Simon Glassb3464eb2021-12-18 14:54:35 -0700179 """Get all the defconfig files under the configs/ directory.
180
181 Returns:
182 list of str: List of paths to defconfig files
183 """
Masahiro Yamada58175e32016-07-25 19:15:28 +0900184 defconfigs = []
Simon Glassb3464eb2021-12-18 14:54:35 -0700185 for (dirpath, _, filenames) in os.walk('configs'):
Masahiro Yamada58175e32016-07-25 19:15:28 +0900186 dirpath = dirpath[len('configs') + 1:]
187 for filename in fnmatch.filter(filenames, '*_defconfig'):
188 defconfigs.append(os.path.join(dirpath, filename))
189
190 return defconfigs
191
Masahiro Yamadab6160812015-05-20 11:36:07 +0900192def color_text(color_enabled, color, string):
193 """Return colored string."""
194 if color_enabled:
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900195 # LF should not be surrounded by the escape sequence.
196 # Otherwise, additional whitespace or line-feed might be printed.
197 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
198 for s in string.split('\n') ])
Simon Glassb3464eb2021-12-18 14:54:35 -0700199 return string
Masahiro Yamadab6160812015-05-20 11:36:07 +0900200
Simon Glassb09ae452021-12-18 14:54:33 -0700201def write_file(fname, data):
202 """Write data to a file
203
204 Args:
205 fname (str): Filename to write to
206 data (list of str): Lines to write (with or without trailing newline);
207 or str to write
208 """
209 with open(fname, 'w', encoding='utf-8') as out:
210 if isinstance(data, list):
211 for line in data:
212 print(line.rstrip('\n'), file=out)
213 else:
214 out.write(data)
215
Simon Glassaba238f2021-12-18 14:54:34 -0700216def read_file(fname, as_lines=True, skip_unicode=False):
217 """Read a file and return the contents
218
219 Args:
220 fname (str): Filename to read from
221 as_lines: Return file contents as a list of lines
222 skip_unicode (bool): True to report unicode errors and continue
223
224 Returns:
225 iter of str: List of ;ines from the file with newline removed; str if
226 as_lines is False with newlines intact; or None if a unicode error
227 occurred
228
229 Raises:
230 UnicodeDecodeError: Unicode error occurred when reading
231 """
232 with open(fname, encoding='utf-8') as inf:
233 try:
234 if as_lines:
235 return [line.rstrip('\n') for line in inf.readlines()]
236 else:
237 return inf.read()
238 except UnicodeDecodeError as e:
239 if not skip_unicode:
Simon Glassafaddc72022-02-11 13:23:22 -0700240 raise
Simon Glassaba238f2021-12-18 14:54:34 -0700241 print("Failed on file %s': %s" % (fname, e))
242 return None
243
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200244def try_expand(line):
245 """If value looks like an expression, try expanding it
246 Otherwise just return the existing value
247 """
248 if line.find('=') == -1:
249 return line
250
251 try:
Markus Klotzbuecherbac0bb52020-02-12 20:46:44 +0100252 aeval = asteval.Interpreter( usersyms=SIZES, minimal=True )
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200253 cfg, val = re.split("=", line)
254 val= val.strip('\"')
Simon Glassdc634d92021-12-18 14:54:30 -0700255 if re.search(r'[*+-/]|<<|SZ_+|\(([^\)]+)\)', val):
Markus Klotzbuecherbac0bb52020-02-12 20:46:44 +0100256 newval = hex(aeval(val))
Simon Glassdc634d92021-12-18 14:54:30 -0700257 print('\tExpanded expression %s to %s' % (val, newval))
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200258 return cfg+'='+newval
259 except:
Simon Glassdc634d92021-12-18 14:54:30 -0700260 print('\tFailed to expand expression in %s' % line)
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200261
262 return line
263
Chris Packham9d5274f2017-05-02 21:30:47 +1200264
Masahiro Yamadab6160812015-05-20 11:36:07 +0900265### classes ###
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900266class Progress:
267
268 """Progress Indicator"""
269
270 def __init__(self, total):
271 """Create a new progress indicator.
272
Simon Glassb3464eb2021-12-18 14:54:35 -0700273 Args:
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900274 total: A number of defconfig files to process.
275 """
276 self.current = 0
277 self.total = total
278
279 def inc(self):
280 """Increment the number of processed defconfig files."""
281
282 self.current += 1
283
284 def show(self):
285 """Display the progress."""
Simon Glass1f701862019-10-31 07:42:57 -0600286 print(' %d defconfigs out of %d\r' % (self.current, self.total), end=' ')
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900287 sys.stdout.flush()
288
Simon Glass44116332017-06-15 21:39:33 -0600289
290class KconfigScanner:
291 """Kconfig scanner."""
292
293 def __init__(self):
294 """Scan all the Kconfig files and create a Config object."""
295 # Define environment variables referenced from Kconfig
296 os.environ['srctree'] = os.getcwd()
297 os.environ['UBOOTVERSION'] = 'dummy'
298 os.environ['KCONFIG_OBJDIR'] = ''
Simon Glass4c4eb7c2023-02-01 13:19:12 -0700299 os.environ['CC'] = 'gcc'
Tom Rini3c5f4152019-09-20 17:42:09 -0400300 self.conf = kconfiglib.Kconfig()
Simon Glass44116332017-06-15 21:39:33 -0600301
302
Masahiro Yamadab6160812015-05-20 11:36:07 +0900303class KconfigParser:
304
305 """A parser of .config and include/autoconf.mk."""
306
307 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
308 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
309
Simon Glass08d148a2023-09-23 13:43:54 -0600310 def __init__(self, args, build_dir):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900311 """Create a new parser.
312
Simon Glassb3464eb2021-12-18 14:54:35 -0700313 Args:
Simon Glassb3464eb2021-12-18 14:54:35 -0700314 args (Namespace): program arguments
Masahiro Yamadab6160812015-05-20 11:36:07 +0900315 build_dir: Build directory.
316 """
Simon Glassd9c1da22021-12-18 14:54:31 -0700317 self.args = args
Masahiro Yamada5393b612016-05-19 15:52:00 +0900318 self.dotconfig = os.path.join(build_dir, '.config')
319 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
Masahiro Yamada6d139172016-08-22 22:18:22 +0900320 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
321 'autoconf.mk')
Simon Glass8fb5bd02017-06-01 19:39:01 -0600322 self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
Masahiro Yamada07f98522016-05-19 15:52:06 +0900323 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamadab6160812015-05-20 11:36:07 +0900324
Simon Glass257f5232017-07-10 14:47:47 -0600325 def get_arch(self):
326 """Parse .config file and return the architecture.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900327
328 Returns:
Simon Glass257f5232017-07-10 14:47:47 -0600329 Architecture name (e.g. 'arm').
Masahiro Yamadab6160812015-05-20 11:36:07 +0900330 """
331 arch = ''
332 cpu = ''
Simon Glassaba238f2021-12-18 14:54:34 -0700333 for line in read_file(self.dotconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900334 m = self.re_arch.match(line)
335 if m:
336 arch = m.group(1)
337 continue
338 m = self.re_cpu.match(line)
339 if m:
340 cpu = m.group(1)
341
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900342 if not arch:
343 return None
Masahiro Yamadab6160812015-05-20 11:36:07 +0900344
345 # fix-up for aarch64
346 if arch == 'arm' and cpu == 'armv8':
347 arch = 'aarch64'
348
Simon Glass257f5232017-07-10 14:47:47 -0600349 return arch
Masahiro Yamadab6160812015-05-20 11:36:07 +0900350
Simon Glass43cf08f2017-06-01 19:39:02 -0600351
352class DatabaseThread(threading.Thread):
353 """This thread processes results from Slot threads.
354
355 It collects the data in the master config directary. There is only one
356 result thread, and this helps to serialise the build output.
357 """
358 def __init__(self, config_db, db_queue):
359 """Set up a new result thread
360
361 Args:
362 builder: Builder which will be sent each result
363 """
364 threading.Thread.__init__(self)
365 self.config_db = config_db
366 self.db_queue= db_queue
367
368 def run(self):
369 """Called to start up the result thread.
370
371 We collect the next result job and pass it on to the build.
372 """
373 while True:
374 defconfig, configs = self.db_queue.get()
375 self.config_db[defconfig] = configs
376 self.db_queue.task_done()
377
378
Masahiro Yamadab6160812015-05-20 11:36:07 +0900379class Slot:
380
381 """A slot to store a subprocess.
382
383 Each instance of this class handles one subprocess.
384 This class is useful to control multiple threads
385 for faster processing.
386 """
387
Simon Glass08d148a2023-09-23 13:43:54 -0600388 def __init__(self, toolchains, args, progress, devnull,
Simon Glass257f5232017-07-10 14:47:47 -0600389 make_cmd, reference_src_dir, db_queue):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900390 """Create a new process slot.
391
Simon Glassb3464eb2021-12-18 14:54:35 -0700392 Args:
Simon Glass257f5232017-07-10 14:47:47 -0600393 toolchains: Toolchains object containing toolchains.
Simon Glassd9c1da22021-12-18 14:54:31 -0700394 args: Program arguments
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900395 progress: A progress indicator.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900396 devnull: A file object of '/dev/null'.
397 make_cmd: command name of GNU Make.
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500398 reference_src_dir: Determine the true starting config state from this
399 source tree.
Simon Glass43cf08f2017-06-01 19:39:02 -0600400 db_queue: output queue to write config info for the database
Masahiro Yamadab6160812015-05-20 11:36:07 +0900401 """
Simon Glass257f5232017-07-10 14:47:47 -0600402 self.toolchains = toolchains
Simon Glassd9c1da22021-12-18 14:54:31 -0700403 self.args = args
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900404 self.progress = progress
Masahiro Yamadab6160812015-05-20 11:36:07 +0900405 self.build_dir = tempfile.mkdtemp()
406 self.devnull = devnull
407 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500408 self.reference_src_dir = reference_src_dir
Simon Glass43cf08f2017-06-01 19:39:02 -0600409 self.db_queue = db_queue
Simon Glass08d148a2023-09-23 13:43:54 -0600410 self.parser = KconfigParser(args, self.build_dir)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900411 self.state = STATE_IDLE
Masahiro Yamada1271b672016-08-22 22:18:20 +0900412 self.failed_boards = set()
Masahiro Yamadab6160812015-05-20 11:36:07 +0900413
414 def __del__(self):
415 """Delete the working directory
416
417 This function makes sure the temporary directory is cleaned away
418 even if Python suddenly dies due to error. It should be done in here
Joe Hershberger640de872016-06-10 14:53:29 -0500419 because it is guaranteed the destructor is always invoked when the
Masahiro Yamadab6160812015-05-20 11:36:07 +0900420 instance of the class gets unreferenced.
421
422 If the subprocess is still running, wait until it finishes.
423 """
424 if self.state != STATE_IDLE:
425 while self.ps.poll() == None:
426 pass
427 shutil.rmtree(self.build_dir)
428
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900429 def add(self, defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900430 """Assign a new subprocess for defconfig and add it to the slot.
431
432 If the slot is vacant, create a new subprocess for processing the
433 given defconfig and add it to the slot. Just returns False if
434 the slot is occupied (i.e. the current subprocess is still running).
435
Simon Glassb3464eb2021-12-18 14:54:35 -0700436 Args:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900437 defconfig: defconfig name.
438
439 Returns:
440 Return True on success or False on failure
441 """
442 if self.state != STATE_IDLE:
443 return False
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900444
Masahiro Yamadab6160812015-05-20 11:36:07 +0900445 self.defconfig = defconfig
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900446 self.log = ''
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900447 self.current_src_dir = self.reference_src_dir
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900448 self.do_defconfig()
Masahiro Yamadab6160812015-05-20 11:36:07 +0900449 return True
450
451 def poll(self):
452 """Check the status of the subprocess and handle it as needed.
453
454 Returns True if the slot is vacant (i.e. in idle state).
455 If the configuration is successfully finished, assign a new
456 subprocess to build include/autoconf.mk.
457 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada263d1372016-05-19 15:52:04 +0900458 parse the .config and the include/autoconf.mk, moving
459 config options to the .config as needed.
460 If the .config was updated, run "make savedefconfig" to sync
461 it, update the original defconfig, and then set the slot back
462 to the idle state.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900463
464 Returns:
465 Return True if the subprocess is terminated, False otherwise
466 """
467 if self.state == STATE_IDLE:
468 return True
469
470 if self.ps.poll() == None:
471 return False
472
473 if self.ps.poll() != 0:
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900474 self.handle_error()
475 elif self.state == STATE_DEFCONFIG:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900476 if self.reference_src_dir and not self.current_src_dir:
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500477 self.do_savedefconfig()
478 else:
479 self.do_autoconf()
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900480 elif self.state == STATE_AUTOCONF:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900481 if self.current_src_dir:
482 self.current_src_dir = None
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500483 self.do_defconfig()
Simon Glassd9c1da22021-12-18 14:54:31 -0700484 elif self.args.build_db:
Simon Glass43cf08f2017-06-01 19:39:02 -0600485 self.do_build_db()
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500486 else:
487 self.do_savedefconfig()
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900488 elif self.state == STATE_SAVEDEFCONFIG:
489 self.update_defconfig()
490 else:
Simon Glassdc634d92021-12-18 14:54:30 -0700491 sys.exit('Internal Error. This should not happen.')
Masahiro Yamadab6160812015-05-20 11:36:07 +0900492
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900493 return True if self.state == STATE_IDLE else False
Joe Hershberger166edec2015-05-19 13:21:17 -0500494
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900495 def handle_error(self):
496 """Handle error cases."""
Masahiro Yamada83c17672016-05-19 15:52:08 +0900497
Simon Glassd9c1da22021-12-18 14:54:31 -0700498 self.log += color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glassdc634d92021-12-18 14:54:30 -0700499 'Failed to process.\n')
Simon Glassd9c1da22021-12-18 14:54:31 -0700500 if self.args.verbose:
501 self.log += color_text(self.args.color, COLOR_LIGHT_CYAN,
Markus Klotzbuecher8773f352020-02-12 20:46:45 +0100502 self.ps.stderr.read().decode())
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900503 self.finish(False)
Joe Hershberger166edec2015-05-19 13:21:17 -0500504
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900505 def do_defconfig(self):
506 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamada0f6beda2016-05-19 15:52:07 +0900507
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900508 cmd = list(self.make_cmd)
509 cmd.append(self.defconfig)
510 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900511 stderr=subprocess.PIPE,
512 cwd=self.current_src_dir)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900513 self.state = STATE_DEFCONFIG
Masahiro Yamada0f6beda2016-05-19 15:52:07 +0900514
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900515 def do_autoconf(self):
Simon Glass8fb5bd02017-06-01 19:39:01 -0600516 """Run 'make AUTO_CONF_PATH'."""
Masahiro Yamadab6160812015-05-20 11:36:07 +0900517
Simon Glass257f5232017-07-10 14:47:47 -0600518 arch = self.parser.get_arch()
519 try:
520 toolchain = self.toolchains.Select(arch)
521 except ValueError:
Simon Glassd9c1da22021-12-18 14:54:31 -0700522 self.log += color_text(self.args.color, COLOR_YELLOW,
Chris Packham1ebcbd12017-08-27 20:00:51 +1200523 "Tool chain for '%s' is missing. Do nothing.\n" % arch)
Masahiro Yamada274a5ee2016-05-19 15:52:03 +0900524 self.finish(False)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900525 return
Simon Glass1f701862019-10-31 07:42:57 -0600526 env = toolchain.MakeEnvironment(False)
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900527
Masahiro Yamadab6160812015-05-20 11:36:07 +0900528 cmd = list(self.make_cmd)
Joe Hershberger765442b2015-05-19 13:21:18 -0500529 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Simon Glass8fb5bd02017-06-01 19:39:01 -0600530 cmd.append(AUTO_CONF_PATH)
Simon Glass257f5232017-07-10 14:47:47 -0600531 self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900532 stderr=subprocess.PIPE,
533 cwd=self.current_src_dir)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900534 self.state = STATE_AUTOCONF
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900535
Simon Glass43cf08f2017-06-01 19:39:02 -0600536 def do_build_db(self):
537 """Add the board to the database"""
538 configs = {}
Simon Glassaba238f2021-12-18 14:54:34 -0700539 for line in read_file(os.path.join(self.build_dir, AUTO_CONF_PATH)):
540 if line.startswith('CONFIG'):
541 config, value = line.split('=', 1)
542 configs[config] = value.rstrip()
Simon Glass43cf08f2017-06-01 19:39:02 -0600543 self.db_queue.put([self.defconfig, configs])
544 self.finish(True)
545
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900546 def do_savedefconfig(self):
547 """Update the .config and run 'make savedefconfig'."""
Simon Glassc1c10c22023-09-23 13:43:55 -0600548 if not self.args.force_sync:
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900549 self.finish(True)
550 return
Simon Glassc1c10c22023-09-23 13:43:55 -0600551 self.log += 'Syncing by savedefconfig (forced by option)...\n'
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900552
553 cmd = list(self.make_cmd)
554 cmd.append('savedefconfig')
555 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
556 stderr=subprocess.PIPE)
557 self.state = STATE_SAVEDEFCONFIG
558
559 def update_defconfig(self):
560 """Update the input defconfig and go back to the idle state."""
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900561 orig_defconfig = os.path.join('configs', self.defconfig)
562 new_defconfig = os.path.join(self.build_dir, 'defconfig')
563 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
564
565 if updated:
Simon Glassd9c1da22021-12-18 14:54:31 -0700566 self.log += color_text(self.args.color, COLOR_LIGHT_BLUE,
Simon Glassdc634d92021-12-18 14:54:30 -0700567 'defconfig was updated.\n')
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900568
Simon Glassd9c1da22021-12-18 14:54:31 -0700569 if not self.args.dry_run and updated:
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900570 shutil.move(new_defconfig, orig_defconfig)
571 self.finish(True)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900572
Masahiro Yamada274a5ee2016-05-19 15:52:03 +0900573 def finish(self, success):
574 """Display log along with progress and go to the idle state.
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900575
Simon Glassb3464eb2021-12-18 14:54:35 -0700576 Args:
Masahiro Yamada274a5ee2016-05-19 15:52:03 +0900577 success: Should be True when the defconfig was processed
578 successfully, or False when it fails.
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900579 """
580 # output at least 30 characters to hide the "* defconfigs out of *".
581 log = self.defconfig.ljust(30) + '\n'
582
583 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
584 # Some threads are running in parallel.
585 # Print log atomically to not mix up logs from different threads.
Simon Glass1f701862019-10-31 07:42:57 -0600586 print(log, file=(sys.stdout if success else sys.stderr))
Masahiro Yamada274a5ee2016-05-19 15:52:03 +0900587
588 if not success:
Simon Glassd9c1da22021-12-18 14:54:31 -0700589 if self.args.exit_on_error:
Simon Glassdc634d92021-12-18 14:54:30 -0700590 sys.exit('Exit on error.')
Masahiro Yamada274a5ee2016-05-19 15:52:03 +0900591 # If --exit-on-error flag is not set, skip this board and continue.
592 # Record the failed board.
Masahiro Yamada1271b672016-08-22 22:18:20 +0900593 self.failed_boards.add(self.defconfig)
Masahiro Yamada274a5ee2016-05-19 15:52:03 +0900594
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900595 self.progress.inc()
596 self.progress.show()
Masahiro Yamada274a5ee2016-05-19 15:52:03 +0900597 self.state = STATE_IDLE
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900598
Masahiro Yamadab6160812015-05-20 11:36:07 +0900599 def get_failed_boards(self):
Masahiro Yamada1271b672016-08-22 22:18:20 +0900600 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900601 """
602 return self.failed_boards
603
604class Slots:
605
606 """Controller of the array of subprocess slots."""
607
Simon Glass08d148a2023-09-23 13:43:54 -0600608 def __init__(self, toolchains, args, progress, reference_src_dir, db_queue):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900609 """Create a new slots controller.
610
Simon Glassb3464eb2021-12-18 14:54:35 -0700611 Args:
Simon Glass257f5232017-07-10 14:47:47 -0600612 toolchains: Toolchains object containing toolchains.
Simon Glassd9c1da22021-12-18 14:54:31 -0700613 args: Program arguments
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900614 progress: A progress indicator.
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500615 reference_src_dir: Determine the true starting config state from this
616 source tree.
Simon Glass43cf08f2017-06-01 19:39:02 -0600617 db_queue: output queue to write config info for the database
Masahiro Yamadab6160812015-05-20 11:36:07 +0900618 """
Simon Glassd9c1da22021-12-18 14:54:31 -0700619 self.args = args
Masahiro Yamadab6160812015-05-20 11:36:07 +0900620 self.slots = []
Simon Glass34c505f2021-12-18 14:54:32 -0700621 devnull = subprocess.DEVNULL
Masahiro Yamadab6160812015-05-20 11:36:07 +0900622 make_cmd = get_make_cmd()
Simon Glassd9c1da22021-12-18 14:54:31 -0700623 for i in range(args.jobs):
Simon Glass08d148a2023-09-23 13:43:54 -0600624 self.slots.append(Slot(toolchains, args, progress, devnull,
625 make_cmd, reference_src_dir, db_queue))
Masahiro Yamadab6160812015-05-20 11:36:07 +0900626
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900627 def add(self, defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900628 """Add a new subprocess if a vacant slot is found.
629
Simon Glassb3464eb2021-12-18 14:54:35 -0700630 Args:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900631 defconfig: defconfig name to be put into.
632
633 Returns:
634 Return True on success or False on failure
635 """
636 for slot in self.slots:
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900637 if slot.add(defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900638 return True
639 return False
640
641 def available(self):
642 """Check if there is a vacant slot.
643
644 Returns:
645 Return True if at lease one vacant slot is found, False otherwise.
646 """
647 for slot in self.slots:
648 if slot.poll():
649 return True
650 return False
651
652 def empty(self):
653 """Check if all slots are vacant.
654
655 Returns:
656 Return True if all the slots are vacant, False otherwise.
657 """
658 ret = True
659 for slot in self.slots:
660 if not slot.poll():
661 ret = False
662 return ret
663
664 def show_failed_boards(self):
665 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada1271b672016-08-22 22:18:20 +0900666 boards = set()
Masahiro Yamada0153f032016-06-15 14:33:53 +0900667 output_file = 'moveconfig.failed'
Masahiro Yamadab6160812015-05-20 11:36:07 +0900668
669 for slot in self.slots:
Masahiro Yamada1271b672016-08-22 22:18:20 +0900670 boards |= slot.get_failed_boards()
Masahiro Yamadab6160812015-05-20 11:36:07 +0900671
Masahiro Yamada0153f032016-06-15 14:33:53 +0900672 if boards:
673 boards = '\n'.join(boards) + '\n'
Simon Glassdc634d92021-12-18 14:54:30 -0700674 msg = 'The following boards were not processed due to error:\n'
Masahiro Yamada0153f032016-06-15 14:33:53 +0900675 msg += boards
Simon Glassdc634d92021-12-18 14:54:30 -0700676 msg += '(the list has been saved in %s)\n' % output_file
Simon Glassd9c1da22021-12-18 14:54:31 -0700677 print(color_text(self.args.color, COLOR_LIGHT_RED,
Simon Glass1f701862019-10-31 07:42:57 -0600678 msg), file=sys.stderr)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900679
Simon Glassb09ae452021-12-18 14:54:33 -0700680 write_file(output_file, boards)
Joe Hershbergerdade12e2015-05-19 13:21:22 -0500681
Masahiro Yamada2e74fee2016-06-15 14:33:51 +0900682class ReferenceSource:
683
684 """Reference source against which original configs should be parsed."""
685
686 def __init__(self, commit):
687 """Create a reference source directory based on a specified commit.
688
Simon Glassb3464eb2021-12-18 14:54:35 -0700689 Args:
Masahiro Yamada2e74fee2016-06-15 14:33:51 +0900690 commit: commit to git-clone
691 """
692 self.src_dir = tempfile.mkdtemp()
Simon Glassdc634d92021-12-18 14:54:30 -0700693 print('Cloning git repo to a separate work directory...')
Masahiro Yamada2e74fee2016-06-15 14:33:51 +0900694 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
695 cwd=self.src_dir)
Simon Glass1f701862019-10-31 07:42:57 -0600696 print("Checkout '%s' to build the original autoconf.mk." % \
697 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip())
Masahiro Yamada2e74fee2016-06-15 14:33:51 +0900698 subprocess.check_output(['git', 'checkout', commit],
699 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500700
701 def __del__(self):
Masahiro Yamada2e74fee2016-06-15 14:33:51 +0900702 """Delete the reference source directory
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500703
704 This function makes sure the temporary directory is cleaned away
705 even if Python suddenly dies due to error. It should be done in here
706 because it is guaranteed the destructor is always invoked when the
707 instance of the class gets unreferenced.
708 """
Masahiro Yamada2e74fee2016-06-15 14:33:51 +0900709 shutil.rmtree(self.src_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500710
Masahiro Yamada2e74fee2016-06-15 14:33:51 +0900711 def get_dir(self):
712 """Return the absolute path to the reference source directory."""
713
714 return self.src_dir
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500715
Simon Glass08d148a2023-09-23 13:43:54 -0600716def move_config(toolchains, args, db_queue):
717 """Build database or sync config options to defconfig files.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900718
Simon Glassb3464eb2021-12-18 14:54:35 -0700719 Args:
Simon Glass08d148a2023-09-23 13:43:54 -0600720 toolchains: Toolchains to use
Simon Glassd9c1da22021-12-18 14:54:31 -0700721 args: Program arguments
Simon Glass08d148a2023-09-23 13:43:54 -0600722 db_queue: Queue for database updates
Masahiro Yamadab6160812015-05-20 11:36:07 +0900723 """
Simon Glass08d148a2023-09-23 13:43:54 -0600724 if args.force_sync:
725 print('Syncing defconfigs', end=' ')
726 elif args.build_db:
727 print('Building %s database' % CONFIG_DATABASE)
Simon Glassd9c1da22021-12-18 14:54:31 -0700728 print('(jobs: %d)\n' % args.jobs)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900729
Simon Glassd9c1da22021-12-18 14:54:31 -0700730 if args.git_ref:
731 reference_src = ReferenceSource(args.git_ref)
Masahiro Yamada2e74fee2016-06-15 14:33:51 +0900732 reference_src_dir = reference_src.get_dir()
733 else:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900734 reference_src_dir = None
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500735
Simon Glassd9c1da22021-12-18 14:54:31 -0700736 if args.defconfigs:
737 defconfigs = get_matched_defconfigs(args.defconfigs)
Joe Hershbergerc6e043a2015-05-19 13:21:19 -0500738 else:
Masahiro Yamada58175e32016-07-25 19:15:28 +0900739 defconfigs = get_all_defconfigs()
Masahiro Yamadab6160812015-05-20 11:36:07 +0900740
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900741 progress = Progress(len(defconfigs))
Simon Glass08d148a2023-09-23 13:43:54 -0600742 slots = Slots(toolchains, args, progress, reference_src_dir, db_queue)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900743
744 # Main loop to process defconfig files:
745 # Add a new subprocess into a vacant slot.
746 # Sleep if there is no available slot.
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900747 for defconfig in defconfigs:
748 while not slots.add(defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900749 while not slots.available():
750 # No available slot: sleep for a while
751 time.sleep(SLEEP_TIME)
752
753 # wait until all the subprocesses finish
754 while not slots.empty():
755 time.sleep(SLEEP_TIME)
756
Simon Glass1f701862019-10-31 07:42:57 -0600757 print('')
Masahiro Yamadab6160812015-05-20 11:36:07 +0900758 slots.show_failed_boards()
759
Simon Glass44116332017-06-15 21:39:33 -0600760def find_kconfig_rules(kconf, config, imply_config):
761 """Check whether a config has a 'select' or 'imply' keyword
762
763 Args:
Tom Rini3c5f4152019-09-20 17:42:09 -0400764 kconf: Kconfiglib.Kconfig object
Simon Glass44116332017-06-15 21:39:33 -0600765 config: Name of config to check (without CONFIG_ prefix)
766 imply_config: Implying config (without CONFIG_ prefix) which may or
767 may not have an 'imply' for 'config')
768
769 Returns:
770 Symbol object for 'config' if found, else None
771 """
Tom Rini3c5f4152019-09-20 17:42:09 -0400772 sym = kconf.syms.get(imply_config)
Simon Glass44116332017-06-15 21:39:33 -0600773 if sym:
Simon Glass520b47a2021-07-21 21:35:53 -0600774 for sel, cond in (sym.selects + sym.implies):
Simon Glass93c0a9e2021-12-18 08:09:42 -0700775 if sel.name == config:
Simon Glass44116332017-06-15 21:39:33 -0600776 return sym
777 return None
778
779def check_imply_rule(kconf, config, imply_config):
780 """Check if we can add an 'imply' option
781
782 This finds imply_config in the Kconfig and looks to see if it is possible
783 to add an 'imply' for 'config' to that part of the Kconfig.
784
785 Args:
Tom Rini3c5f4152019-09-20 17:42:09 -0400786 kconf: Kconfiglib.Kconfig object
Simon Glass44116332017-06-15 21:39:33 -0600787 config: Name of config to check (without CONFIG_ prefix)
788 imply_config: Implying config (without CONFIG_ prefix) which may or
789 may not have an 'imply' for 'config')
790
791 Returns:
792 tuple:
793 filename of Kconfig file containing imply_config, or None if none
794 line number within the Kconfig file, or 0 if none
795 message indicating the result
796 """
Tom Rini3c5f4152019-09-20 17:42:09 -0400797 sym = kconf.syms.get(imply_config)
Simon Glass44116332017-06-15 21:39:33 -0600798 if not sym:
799 return 'cannot find sym'
Simon Glass520b47a2021-07-21 21:35:53 -0600800 nodes = sym.nodes
801 if len(nodes) != 1:
802 return '%d locations' % len(nodes)
Simon Glass93c0a9e2021-12-18 08:09:42 -0700803 node = nodes[0]
804 fname, linenum = node.filename, node.linenr
Simon Glass44116332017-06-15 21:39:33 -0600805 cwd = os.getcwd()
806 if cwd and fname.startswith(cwd):
807 fname = fname[len(cwd) + 1:]
808 file_line = ' at %s:%d' % (fname, linenum)
Simon Glassaba238f2021-12-18 14:54:34 -0700809 data = read_file(fname)
Simon Glass44116332017-06-15 21:39:33 -0600810 if data[linenum - 1] != 'config %s' % imply_config:
811 return None, 0, 'bad sym format %s%s' % (data[linenum], file_line)
812 return fname, linenum, 'adding%s' % file_line
813
814def add_imply_rule(config, fname, linenum):
815 """Add a new 'imply' option to a Kconfig
816
817 Args:
818 config: config option to add an imply for (without CONFIG_ prefix)
819 fname: Kconfig filename to update
820 linenum: Line number to place the 'imply' before
821
822 Returns:
823 Message indicating the result
824 """
825 file_line = ' at %s:%d' % (fname, linenum)
Simon Glassaba238f2021-12-18 14:54:34 -0700826 data = read_file(fname)
Simon Glass44116332017-06-15 21:39:33 -0600827 linenum -= 1
828
829 for offset, line in enumerate(data[linenum:]):
830 if line.strip().startswith('help') or not line:
831 data.insert(linenum + offset, '\timply %s' % config)
Simon Glassb09ae452021-12-18 14:54:33 -0700832 write_file(fname, data)
Simon Glass44116332017-06-15 21:39:33 -0600833 return 'added%s' % file_line
834
835 return 'could not insert%s'
836
837(IMPLY_MIN_2, IMPLY_TARGET, IMPLY_CMD, IMPLY_NON_ARCH_BOARD) = (
838 1, 2, 4, 8)
Simon Glass92e55582017-06-15 21:39:32 -0600839
840IMPLY_FLAGS = {
841 'min2': [IMPLY_MIN_2, 'Show options which imply >2 boards (normally >5)'],
842 'target': [IMPLY_TARGET, 'Allow CONFIG_TARGET_... options to imply'],
843 'cmd': [IMPLY_CMD, 'Allow CONFIG_CMD_... to imply'],
Simon Glass44116332017-06-15 21:39:33 -0600844 'non-arch-board': [
845 IMPLY_NON_ARCH_BOARD,
846 'Allow Kconfig options outside arch/ and /board/ to imply'],
Simon Glassb3464eb2021-12-18 14:54:35 -0700847}
Simon Glass92e55582017-06-15 21:39:32 -0600848
Simon Glassf931c2f2021-12-18 08:09:43 -0700849
850def read_database():
851 """Read in the config database
852
853 Returns:
854 tuple:
855 set of all config options seen (each a str)
856 set of all defconfigs seen (each a str)
857 dict of configs for each defconfig:
858 key: defconfig name, e.g. "MPC8548CDS_legacy_defconfig"
859 value: dict:
860 key: CONFIG option
861 value: Value of option
862 dict of defconfigs for each config:
863 key: CONFIG option
864 value: set of boards using that option
865
866 """
867 configs = {}
868
869 # key is defconfig name, value is dict of (CONFIG_xxx, value)
870 config_db = {}
871
872 # Set of all config options we have seen
873 all_configs = set()
874
875 # Set of all defconfigs we have seen
876 all_defconfigs = set()
877
878 defconfig_db = collections.defaultdict(set)
Simon Glassaba238f2021-12-18 14:54:34 -0700879 for line in read_file(CONFIG_DATABASE):
880 line = line.rstrip()
881 if not line: # Separator between defconfigs
882 config_db[defconfig] = configs
883 all_defconfigs.add(defconfig)
884 configs = {}
885 elif line[0] == ' ': # CONFIG line
886 config, value = line.strip().split('=', 1)
887 configs[config] = value
888 defconfig_db[config].add(defconfig)
889 all_configs.add(config)
890 else: # New defconfig
891 defconfig = line
Simon Glassf931c2f2021-12-18 08:09:43 -0700892
893 return all_configs, all_defconfigs, config_db, defconfig_db
894
895
Simon Glass44116332017-06-15 21:39:33 -0600896def do_imply_config(config_list, add_imply, imply_flags, skip_added,
897 check_kconfig=True, find_superset=False):
Simon Glassc6e73cf2017-06-01 19:39:03 -0600898 """Find CONFIG options which imply those in the list
899
900 Some CONFIG options can be implied by others and this can help to reduce
901 the size of the defconfig files. For example, CONFIG_X86 implies
902 CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
903 all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
904 each of the x86 defconfig files.
905
906 This function uses the moveconfig database to find such options. It
907 displays a list of things that could possibly imply those in the list.
908 The algorithm ignores any that start with CONFIG_TARGET since these
909 typically refer to only a few defconfigs (often one). It also does not
910 display a config with less than 5 defconfigs.
911
912 The algorithm works using sets. For each target config in config_list:
913 - Get the set 'defconfigs' which use that target config
914 - For each config (from a list of all configs):
915 - Get the set 'imply_defconfig' of defconfigs which use that config
916 -
917 - If imply_defconfigs contains anything not in defconfigs then
918 this config does not imply the target config
919
920 Params:
921 config_list: List of CONFIG options to check (each a string)
Simon Glass44116332017-06-15 21:39:33 -0600922 add_imply: Automatically add an 'imply' for each config.
Simon Glass92e55582017-06-15 21:39:32 -0600923 imply_flags: Flags which control which implying configs are allowed
924 (IMPLY_...)
Simon Glass44116332017-06-15 21:39:33 -0600925 skip_added: Don't show options which already have an imply added.
926 check_kconfig: Check if implied symbols already have an 'imply' or
927 'select' for the target config, and show this information if so.
Simon Glassc6e73cf2017-06-01 19:39:03 -0600928 find_superset: True to look for configs which are a superset of those
929 already found. So for example if CONFIG_EXYNOS5 implies an option,
930 but CONFIG_EXYNOS covers a larger set of defconfigs and also
931 implies that option, this will drop the former in favour of the
932 latter. In practice this option has not proved very used.
933
934 Note the terminoloy:
935 config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
936 defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
937 """
Simon Glass44116332017-06-15 21:39:33 -0600938 kconf = KconfigScanner().conf if check_kconfig else None
939 if add_imply and add_imply != 'all':
Simon Glass93c0a9e2021-12-18 08:09:42 -0700940 add_imply = add_imply.split(',')
Simon Glass44116332017-06-15 21:39:33 -0600941
Simon Glassf931c2f2021-12-18 08:09:43 -0700942 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
Simon Glassc6e73cf2017-06-01 19:39:03 -0600943
Simon Glass93c0a9e2021-12-18 08:09:42 -0700944 # Work through each target config option in turn, independently
Simon Glassc6e73cf2017-06-01 19:39:03 -0600945 for config in config_list:
946 defconfigs = defconfig_db.get(config)
947 if not defconfigs:
Simon Glass1f701862019-10-31 07:42:57 -0600948 print('%s not found in any defconfig' % config)
Simon Glassc6e73cf2017-06-01 19:39:03 -0600949 continue
950
951 # Get the set of defconfigs without this one (since a config cannot
952 # imply itself)
953 non_defconfigs = all_defconfigs - defconfigs
954 num_defconfigs = len(defconfigs)
Simon Glass1f701862019-10-31 07:42:57 -0600955 print('%s found in %d/%d defconfigs' % (config, num_defconfigs,
956 len(all_configs)))
Simon Glassc6e73cf2017-06-01 19:39:03 -0600957
958 # This will hold the results: key=config, value=defconfigs containing it
959 imply_configs = {}
960 rest_configs = all_configs - set([config])
961
962 # Look at every possible config, except the target one
963 for imply_config in rest_configs:
Simon Glass92e55582017-06-15 21:39:32 -0600964 if 'ERRATUM' in imply_config:
Simon Glassc6e73cf2017-06-01 19:39:03 -0600965 continue
Simon Glassb3464eb2021-12-18 14:54:35 -0700966 if not imply_flags & IMPLY_CMD:
Simon Glass92e55582017-06-15 21:39:32 -0600967 if 'CONFIG_CMD' in imply_config:
968 continue
Simon Glassb3464eb2021-12-18 14:54:35 -0700969 if not imply_flags & IMPLY_TARGET:
Simon Glass92e55582017-06-15 21:39:32 -0600970 if 'CONFIG_TARGET' in imply_config:
971 continue
Simon Glassc6e73cf2017-06-01 19:39:03 -0600972
973 # Find set of defconfigs that have this config
974 imply_defconfig = defconfig_db[imply_config]
975
976 # Get the intersection of this with defconfigs containing the
977 # target config
978 common_defconfigs = imply_defconfig & defconfigs
979
980 # Get the set of defconfigs containing this config which DO NOT
981 # also contain the taret config. If this set is non-empty it means
982 # that this config affects other defconfigs as well as (possibly)
983 # the ones affected by the target config. This means it implies
984 # things we don't want to imply.
985 not_common_defconfigs = imply_defconfig & non_defconfigs
986 if not_common_defconfigs:
987 continue
988
989 # If there are common defconfigs, imply_config may be useful
990 if common_defconfigs:
991 skip = False
992 if find_superset:
Simon Glass1f701862019-10-31 07:42:57 -0600993 for prev in list(imply_configs.keys()):
Simon Glassc6e73cf2017-06-01 19:39:03 -0600994 prev_count = len(imply_configs[prev])
995 count = len(common_defconfigs)
996 if (prev_count > count and
997 (imply_configs[prev] & common_defconfigs ==
998 common_defconfigs)):
999 # skip imply_config because prev is a superset
1000 skip = True
1001 break
1002 elif count > prev_count:
1003 # delete prev because imply_config is a superset
1004 del imply_configs[prev]
1005 if not skip:
1006 imply_configs[imply_config] = common_defconfigs
1007
1008 # Now we have a dict imply_configs of configs which imply each config
1009 # The value of each dict item is the set of defconfigs containing that
1010 # config. Rank them so that we print the configs that imply the largest
1011 # number of defconfigs first.
Simon Glass44116332017-06-15 21:39:33 -06001012 ranked_iconfigs = sorted(imply_configs,
Simon Glassc6e73cf2017-06-01 19:39:03 -06001013 key=lambda k: len(imply_configs[k]), reverse=True)
Simon Glass44116332017-06-15 21:39:33 -06001014 kconfig_info = ''
1015 cwd = os.getcwd()
1016 add_list = collections.defaultdict(list)
1017 for iconfig in ranked_iconfigs:
1018 num_common = len(imply_configs[iconfig])
Simon Glassc6e73cf2017-06-01 19:39:03 -06001019
1020 # Don't bother if there are less than 5 defconfigs affected.
Simon Glass92e55582017-06-15 21:39:32 -06001021 if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
Simon Glassc6e73cf2017-06-01 19:39:03 -06001022 continue
Simon Glass44116332017-06-15 21:39:33 -06001023 missing = defconfigs - imply_configs[iconfig]
Simon Glassc6e73cf2017-06-01 19:39:03 -06001024 missing_str = ', '.join(missing) if missing else 'all'
1025 missing_str = ''
Simon Glass44116332017-06-15 21:39:33 -06001026 show = True
1027 if kconf:
1028 sym = find_kconfig_rules(kconf, config[CONFIG_LEN:],
1029 iconfig[CONFIG_LEN:])
1030 kconfig_info = ''
1031 if sym:
Simon Glass520b47a2021-07-21 21:35:53 -06001032 nodes = sym.nodes
1033 if len(nodes) == 1:
1034 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glass44116332017-06-15 21:39:33 -06001035 if cwd and fname.startswith(cwd):
1036 fname = fname[len(cwd) + 1:]
1037 kconfig_info = '%s:%d' % (fname, linenum)
1038 if skip_added:
1039 show = False
1040 else:
Tom Rini3c5f4152019-09-20 17:42:09 -04001041 sym = kconf.syms.get(iconfig[CONFIG_LEN:])
Simon Glass44116332017-06-15 21:39:33 -06001042 fname = ''
1043 if sym:
Simon Glass520b47a2021-07-21 21:35:53 -06001044 nodes = sym.nodes
1045 if len(nodes) == 1:
1046 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glass44116332017-06-15 21:39:33 -06001047 if cwd and fname.startswith(cwd):
1048 fname = fname[len(cwd) + 1:]
1049 in_arch_board = not sym or (fname.startswith('arch') or
1050 fname.startswith('board'))
1051 if (not in_arch_board and
Simon Glassb3464eb2021-12-18 14:54:35 -07001052 not imply_flags & IMPLY_NON_ARCH_BOARD):
Simon Glass44116332017-06-15 21:39:33 -06001053 continue
1054
1055 if add_imply and (add_imply == 'all' or
1056 iconfig in add_imply):
1057 fname, linenum, kconfig_info = (check_imply_rule(kconf,
1058 config[CONFIG_LEN:], iconfig[CONFIG_LEN:]))
1059 if fname:
1060 add_list[fname].append(linenum)
Simon Glassc6e73cf2017-06-01 19:39:03 -06001061
Simon Glass44116332017-06-15 21:39:33 -06001062 if show and kconfig_info != 'skip':
Simon Glass1f701862019-10-31 07:42:57 -06001063 print('%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
1064 kconfig_info, missing_str))
Simon Glass44116332017-06-15 21:39:33 -06001065
1066 # Having collected a list of things to add, now we add them. We process
1067 # each file from the largest line number to the smallest so that
1068 # earlier additions do not affect our line numbers. E.g. if we added an
1069 # imply at line 20 it would change the position of each line after
1070 # that.
Simon Glass1f701862019-10-31 07:42:57 -06001071 for fname, linenums in add_list.items():
Simon Glass44116332017-06-15 21:39:33 -06001072 for linenum in sorted(linenums, reverse=True):
1073 add_imply_rule(config[CONFIG_LEN:], fname, linenum)
1074
Simon Glass99f79422022-02-08 11:49:46 -07001075def defconfig_matches(configs, re_match):
1076 """Check if any CONFIG option matches a regex
1077
1078 The match must be complete, i.e. from the start to end of the CONFIG option.
1079
1080 Args:
1081 configs (dict): Dict of CONFIG options:
1082 key: CONFIG option
1083 value: Value of option
1084 re_match (re.Pattern): Match to check
1085
1086 Returns:
1087 bool: True if any CONFIG matches the regex
1088 """
1089 for cfg in configs:
Simon Glassfea71c92022-03-05 20:18:54 -07001090 if re_match.fullmatch(cfg):
Simon Glass99f79422022-02-08 11:49:46 -07001091 return True
1092 return False
Simon Glassc6e73cf2017-06-01 19:39:03 -06001093
Simon Glass0082b2e2021-12-18 08:09:46 -07001094def do_find_config(config_list):
1095 """Find boards with a given combination of CONFIGs
1096
1097 Params:
Simon Glass99f79422022-02-08 11:49:46 -07001098 config_list: List of CONFIG options to check (each a regex consisting
Simon Glass0082b2e2021-12-18 08:09:46 -07001099 of a config option, with or without a CONFIG_ prefix. If an option
1100 is preceded by a tilde (~) then it must be false, otherwise it must
1101 be true)
1102 """
1103 all_configs, all_defconfigs, config_db, defconfig_db = read_database()
1104
Simon Glass0082b2e2021-12-18 08:09:46 -07001105 # Start with all defconfigs
1106 out = all_defconfigs
1107
1108 # Work through each config in turn
Simon Glass0082b2e2021-12-18 08:09:46 -07001109 for item in config_list:
1110 # Get the real config name and whether we want this config or not
1111 cfg = item
1112 want = True
1113 if cfg[0] == '~':
1114 want = False
1115 cfg = cfg[1:]
1116
Simon Glass0082b2e2021-12-18 08:09:46 -07001117 # Search everything that is still in the running. If it has a config
1118 # that we want, or doesn't have one that we don't, add it into the
1119 # running for the next stage
1120 in_list = out
1121 out = set()
Simon Glass99f79422022-02-08 11:49:46 -07001122 re_match = re.compile(cfg)
Simon Glass0082b2e2021-12-18 08:09:46 -07001123 for defc in in_list:
Simon Glass99f79422022-02-08 11:49:46 -07001124 has_cfg = defconfig_matches(config_db[defc], re_match)
Simon Glass0082b2e2021-12-18 08:09:46 -07001125 if has_cfg == want:
1126 out.add(defc)
Tom Rinic1b64aa2022-12-04 10:14:16 -05001127 print(f'{len(out)} matches')
1128 print(' '.join(item.split('_defconfig')[0] for item in out))
Simon Glass0082b2e2021-12-18 08:09:46 -07001129
1130
1131def prefix_config(cfg):
1132 """Prefix a config with CONFIG_ if needed
1133
1134 This handles ~ operator, which indicates that the CONFIG should be disabled
1135
1136 >>> prefix_config('FRED')
1137 'CONFIG_FRED'
1138 >>> prefix_config('CONFIG_FRED')
1139 'CONFIG_FRED'
1140 >>> prefix_config('~FRED')
1141 '~CONFIG_FRED'
1142 >>> prefix_config('~CONFIG_FRED')
1143 '~CONFIG_FRED'
1144 >>> prefix_config('A123')
1145 'CONFIG_A123'
1146 """
1147 op = ''
1148 if cfg[0] == '~':
1149 op = cfg[0]
1150 cfg = cfg[1:]
1151 if not cfg.startswith('CONFIG_'):
1152 cfg = 'CONFIG_' + cfg
1153 return op + cfg
1154
Simon Glass4c4eb7c2023-02-01 13:19:12 -07001155
Simon Glass2deee262023-09-23 13:43:57 -06001156RE_MK_CONFIGS = re.compile(r'CONFIG_(\$\(SPL_(?:TPL_)?\))?([A-Za-z0-9_]*)')
1157RE_IFDEF = re.compile(r'(ifdef|ifndef)')
1158RE_C_CONFIGS = re.compile(r'CONFIG_([A-Za-z0-9_]*)')
1159RE_CONFIG_IS = re.compile(r'CONFIG_IS_ENABLED\(([A-Za-z0-9_]*)\)')
Simon Glass4c4eb7c2023-02-01 13:19:12 -07001160
1161class ConfigUse:
1162 def __init__(self, cfg, is_spl, fname, rest):
1163 self.cfg = cfg
1164 self.is_spl = is_spl
1165 self.fname = fname
1166 self.rest = rest
1167
1168 def __hash__(self):
1169 return hash((self.cfg, self.is_spl))
1170
1171def scan_makefiles(fnames):
1172 """Scan Makefiles looking for Kconfig options
1173
1174 Looks for uses of CONFIG options in Makefiles
1175
1176 Args:
1177 fnames (list of tuple):
1178 str: Makefile filename where the option was found
1179 str: Line of the Makefile
1180
1181 Returns:
1182 tuple:
1183 dict: all_uses
1184 key (ConfigUse): object
1185 value (list of str): matching lines
1186 dict: Uses by filename
1187 key (str): filename
1188 value (set of ConfigUse): uses in that filename
1189
1190 >>> RE_MK_CONFIGS.search('CONFIG_FRED').groups()
1191 (None, 'FRED')
1192 >>> RE_MK_CONFIGS.search('CONFIG_$(SPL_)MARY').groups()
1193 ('$(SPL_)', 'MARY')
1194 >>> RE_MK_CONFIGS.search('CONFIG_$(SPL_TPL_)MARY').groups()
1195 ('$(SPL_TPL_)', 'MARY')
1196 """
1197 all_uses = collections.defaultdict(list)
1198 fname_uses = {}
1199 for fname, rest in fnames:
1200 m_iter = RE_MK_CONFIGS.finditer(rest)
1201 found = False
1202 for m in m_iter:
1203 found = True
1204 real_opt = m.group(2)
1205 if real_opt == '':
1206 continue
1207 is_spl = False
1208 if m.group(1):
1209 is_spl = True
1210 use = ConfigUse(real_opt, is_spl, fname, rest)
1211 if fname not in fname_uses:
1212 fname_uses[fname] = set()
1213 fname_uses[fname].add(use)
1214 all_uses[use].append(rest)
1215 return all_uses, fname_uses
1216
1217
1218def scan_src_files(fnames):
1219 """Scan source files (other than Makefiles) looking for Kconfig options
1220
1221 Looks for uses of CONFIG options
1222
1223 Args:
1224 fnames (list of tuple):
1225 str: Makefile filename where the option was found
1226 str: Line of the Makefile
1227
1228 Returns:
1229 tuple:
1230 dict: all_uses
1231 key (ConfigUse): object
1232 value (list of str): matching lines
1233 dict: Uses by filename
1234 key (str): filename
1235 value (set of ConfigUse): uses in that filename
1236
1237 >>> RE_C_CONFIGS.search('CONFIG_FRED').groups()
1238 ('FRED',)
1239 >>> RE_CONFIG_IS.search('CONFIG_IS_ENABLED(MARY)').groups()
1240 ('MARY',)
1241 >>> RE_CONFIG_IS.search('#if CONFIG_IS_ENABLED(OF_PLATDATA)').groups()
1242 ('OF_PLATDATA',)
1243 """
1244 def add_uses(m_iter, is_spl):
1245 for m in m_iter:
1246 found = True
1247 real_opt = m.group(1)
1248 if real_opt == '':
1249 continue
1250 use = ConfigUse(real_opt, is_spl, fname, rest)
1251 if fname not in fname_uses:
1252 fname_uses[fname] = set()
1253 fname_uses[fname].add(use)
1254 all_uses[use].append(rest)
1255
1256 all_uses = collections.defaultdict(list)
1257 fname_uses = {}
1258 for fname, rest in fnames:
1259 m_iter = RE_C_CONFIGS.finditer(rest)
1260 add_uses(m_iter, False)
1261
1262 m_iter2 = RE_CONFIG_IS.finditer(rest)
1263 add_uses(m_iter2, True)
1264
1265 return all_uses, fname_uses
1266
1267
1268MODE_NORMAL, MODE_SPL, MODE_PROPER = range(3)
1269
1270def do_scan_source(path, do_update):
1271 """Scan the source tree for Kconfig inconsistencies
1272
1273 Args:
1274 path (str): Path to source tree
1275 do_update (bool) : True to write to scripts/kconf_... files
1276 """
1277 def is_not_proper(name):
1278 for prefix in SPL_PREFIXES:
1279 if name.startswith(prefix):
1280 return name[len(prefix):]
1281 return False
1282
1283 def check_not_found(all_uses, spl_mode):
1284 """Check for Kconfig options mentioned in the source but not in Kconfig
1285
1286 Args:
1287 all_uses (dict):
1288 key (ConfigUse): object
1289 value (list of str): matching lines
1290 spl_mode (int): If MODE_SPL, look at source code which implies
1291 an SPL_ option, but for which there is none;
1292 for MOD_PROPER, look at source code which implies a Proper
1293 option (i.e. use of CONFIG_IS_ENABLED() or $(SPL_) or
1294 $(SPL_TPL_) but for which there none;
1295 if MODE_NORMAL, ignore SPL
1296
1297 Returns:
1298 dict:
1299 key (str): CONFIG name (without 'CONFIG_' prefix
1300 value (list of ConfigUse): List of uses of this CONFIG
1301 """
1302 # Make sure we know about all the options
1303 not_found = collections.defaultdict(list)
1304 for use, rest in all_uses.items():
1305 name = use.cfg
1306 if name in IGNORE_SYMS:
1307 continue
1308 check = True
1309
1310 if spl_mode == MODE_SPL:
1311 check = use.is_spl
1312
1313 # If it is an SPL symbol, try prepending all SPL_ prefixes to
1314 # find at least one SPL symbol
1315 if use.is_spl:
1316 add_to_dict = False
1317 for prefix in SPL_PREFIXES:
1318 try_name = prefix + name
1319 sym = kconf.syms.get(try_name)
1320 if sym:
1321 break
1322 if not sym:
1323 not_found[f'SPL_{name}'].append(use)
1324 continue
1325 elif spl_mode == MODE_PROPER:
1326 # Try to find the Proper version of this symbol, i.e. without
1327 # the SPL_ prefix
1328 proper_name = is_not_proper(name)
1329 if proper_name:
1330 name = proper_name
1331 elif not use.is_spl:
1332 check = False
1333 else: # MODE_NORMAL
1334 debug = False
1335 sym = kconf.syms.get(name)
1336 if not sym:
1337 proper_name = is_not_proper(name)
1338 if proper_name:
1339 name = proper_name
1340 sym = kconf.syms.get(name)
1341 if not sym:
1342 for prefix in SPL_PREFIXES:
1343 try_name = prefix + name
1344 sym = kconf.syms.get(try_name)
1345 if sym:
1346 break
1347 if not sym:
1348 not_found[name].append(use)
1349 continue
1350
1351 sym = kconf.syms.get(name)
1352 if not sym and check:
1353 not_found[name].append(use)
1354 return not_found
1355
1356 def show_uses(uses):
1357 """Show a list of uses along with their filename and code snippet
1358
1359 Args:
1360 uses (dict):
1361 key (str): CONFIG name (without 'CONFIG_' prefix
1362 value (list of ConfigUse): List of uses of this CONFIG
1363 """
1364 for name in sorted(uses):
1365 print(f'{name}: ', end='')
1366 for i, use in enumerate(uses[name]):
1367 print(f'{" " if i else ""}{use.fname}: {use.rest.strip()}')
1368
1369
1370 print('Scanning Kconfig')
1371 kconf = KconfigScanner().conf
1372 print(f'Scanning source in {path}')
1373 args = ['git', 'grep', '-E', r'IS_ENABLED|\bCONFIG']
1374 with subprocess.Popen(args, stdout=subprocess.PIPE) as proc:
1375 out, err = proc.communicate()
1376 lines = out.splitlines()
1377 re_fname = re.compile('^([^:]*):(.*)')
1378 src_list = []
1379 mk_list = []
1380 for line in lines:
1381 linestr = line.decode('utf-8')
1382 m_fname = re_fname.search(linestr)
1383 if not m_fname:
1384 continue
1385 fname, rest = m_fname.groups()
1386 dirname, leaf = os.path.split(fname)
1387 root, ext = os.path.splitext(leaf)
1388 if ext == '.autoconf':
1389 pass
1390 elif ext in ['.c', '.h', '.S', '.lds', '.dts', '.dtsi', '.asl', '.cfg',
1391 '.env', '.tmpl']:
1392 src_list.append([fname, rest])
1393 elif 'Makefile' in root or ext == '.mk':
1394 mk_list.append([fname, rest])
1395 elif ext in ['.yml', '.sh', '.py', '.awk', '.pl', '.rst', '', '.sed']:
1396 pass
1397 elif 'Kconfig' in root or 'Kbuild' in root:
1398 pass
1399 elif 'README' in root:
1400 pass
1401 elif dirname in ['configs']:
1402 pass
1403 elif dirname.startswith('doc') or dirname.startswith('scripts/kconfig'):
1404 pass
1405 else:
1406 print(f'Not sure how to handle file {fname}')
1407
1408 # Scan the Makefiles
1409 all_uses, fname_uses = scan_makefiles(mk_list)
1410
1411 spl_not_found = set()
1412 proper_not_found = set()
1413
1414 # Make sure we know about all the options
1415 print('\nCONFIG options present in Makefiles but not Kconfig:')
1416 not_found = check_not_found(all_uses, MODE_NORMAL)
1417 show_uses(not_found)
1418
1419 print('\nCONFIG options present in Makefiles but not Kconfig (SPL):')
1420 not_found = check_not_found(all_uses, MODE_SPL)
1421 show_uses(not_found)
1422 spl_not_found |= set([is_not_proper(key) or key for key in not_found.keys()])
1423
1424 print('\nCONFIG options used as Proper in Makefiles but without a non-SPL_ variant:')
1425 not_found = check_not_found(all_uses, MODE_PROPER)
1426 show_uses(not_found)
1427 proper_not_found |= set([key for key in not_found.keys()])
1428
1429 # Scan the source code
1430 all_uses, fname_uses = scan_src_files(src_list)
1431
1432 # Make sure we know about all the options
1433 print('\nCONFIG options present in source but not Kconfig:')
1434 not_found = check_not_found(all_uses, MODE_NORMAL)
1435 show_uses(not_found)
1436
1437 print('\nCONFIG options present in source but not Kconfig (SPL):')
1438 not_found = check_not_found(all_uses, MODE_SPL)
1439 show_uses(not_found)
1440 spl_not_found |= set([is_not_proper(key) or key for key in not_found.keys()])
1441
1442 print('\nCONFIG options used as Proper in source but without a non-SPL_ variant:')
1443 not_found = check_not_found(all_uses, MODE_PROPER)
1444 show_uses(not_found)
1445 proper_not_found |= set([key for key in not_found.keys()])
1446
1447 print('\nCONFIG options used as SPL but without an SPL_ variant:')
1448 for item in sorted(spl_not_found):
1449 print(f' {item}')
1450
1451 print('\nCONFIG options used as Proper but without a non-SPL_ variant:')
1452 for item in sorted(proper_not_found):
1453 print(f' {item}')
1454
1455 # Write out the updated information
1456 if do_update:
1457 with open(os.path.join(path, 'scripts', 'conf_nospl'), 'w') as out:
1458 print('# These options should not be enabled in SPL builds\n',
1459 file=out)
1460 for item in sorted(spl_not_found):
1461 print(item, file=out)
1462 with open(os.path.join(path, 'scripts', 'conf_noproper'), 'w') as out:
1463 print('# These options should not be enabled in Proper builds\n',
1464 file=out)
1465 for item in sorted(proper_not_found):
1466 print(item, file=out)
1467
Simon Glass0082b2e2021-12-18 08:09:46 -07001468
Masahiro Yamadab6160812015-05-20 11:36:07 +09001469def main():
1470 try:
1471 cpu_count = multiprocessing.cpu_count()
1472 except NotImplementedError:
1473 cpu_count = 1
1474
Simon Glassd9c1da22021-12-18 14:54:31 -07001475 epilog = '''Move config options from headers to defconfig files. See
1476doc/develop/moveconfig.rst for documentation.'''
1477
1478 parser = ArgumentParser(epilog=epilog)
1479 # Add arguments here
1480 parser.add_argument('-a', '--add-imply', type=str, default='',
Simon Glass44116332017-06-15 21:39:33 -06001481 help='comma-separated list of CONFIG options to add '
1482 "an 'imply' statement to for the CONFIG in -i")
Simon Glassd9c1da22021-12-18 14:54:31 -07001483 parser.add_argument('-A', '--skip-added', action='store_true', default=False,
Simon Glass44116332017-06-15 21:39:33 -06001484 help="don't show options which are already marked as "
1485 'implying others')
Simon Glassd9c1da22021-12-18 14:54:31 -07001486 parser.add_argument('-b', '--build-db', action='store_true', default=False,
Simon Glass43cf08f2017-06-01 19:39:02 -06001487 help='build a CONFIG database')
Simon Glassd9c1da22021-12-18 14:54:31 -07001488 parser.add_argument('-c', '--color', action='store_true', default=False,
Masahiro Yamadab6160812015-05-20 11:36:07 +09001489 help='display the log in color')
Simon Glassd9c1da22021-12-18 14:54:31 -07001490 parser.add_argument('-C', '--commit', action='store_true', default=False,
Simon Glass8bf41c22016-09-12 23:18:21 -06001491 help='Create a git commit for the operation')
Simon Glassd9c1da22021-12-18 14:54:31 -07001492 parser.add_argument('-d', '--defconfigs', type=str,
Simon Glass8f3cf312017-06-01 19:38:59 -06001493 help='a file containing a list of defconfigs to move, '
1494 "one per line (for example 'snow_defconfig') "
1495 "or '-' to read from stdin")
Simon Glassd9c1da22021-12-18 14:54:31 -07001496 parser.add_argument('-e', '--exit-on-error', action='store_true',
Masahiro Yamadab6160812015-05-20 11:36:07 +09001497 default=False,
1498 help='exit immediately on any error')
Simon Glassd9c1da22021-12-18 14:54:31 -07001499 parser.add_argument('-f', '--find', action='store_true', default=False,
Simon Glass0082b2e2021-12-18 08:09:46 -07001500 help='Find boards with a given config combination')
Simon Glassd9c1da22021-12-18 14:54:31 -07001501 parser.add_argument('-i', '--imply', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001502 help='find options which imply others')
Simon Glassd9c1da22021-12-18 14:54:31 -07001503 parser.add_argument('-I', '--imply-flags', type=str, default='',
Simon Glass0559a742021-12-18 08:09:44 -07001504 help="control the -i option ('help' for help")
Simon Glassd9c1da22021-12-18 14:54:31 -07001505 parser.add_argument('-j', '--jobs', type=int, default=cpu_count,
Masahiro Yamadab6160812015-05-20 11:36:07 +09001506 help='the number of jobs to run simultaneously')
Simon Glassd9c1da22021-12-18 14:54:31 -07001507 parser.add_argument('-n', '--dry-run', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001508 help='perform a trial run (show log with no changes)')
Simon Glassd9c1da22021-12-18 14:54:31 -07001509 parser.add_argument('-r', '--git-ref', type=str,
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001510 help='the git ref to clone for building the autoconf.mk')
Simon Glassd9c1da22021-12-18 14:54:31 -07001511 parser.add_argument('-s', '--force-sync', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001512 help='force sync by savedefconfig')
Simon Glassd9c1da22021-12-18 14:54:31 -07001513 parser.add_argument('-S', '--spl', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001514 help='parse config options defined for SPL build')
Simon Glass4c4eb7c2023-02-01 13:19:12 -07001515 parser.add_argument('--scan-source', action='store_true', default=False,
1516 help='scan source for uses of CONFIG options')
Simon Glassd9c1da22021-12-18 14:54:31 -07001517 parser.add_argument('-t', '--test', action='store_true', default=False,
Simon Glass0559a742021-12-18 08:09:44 -07001518 help='run unit tests')
Simon Glassd9c1da22021-12-18 14:54:31 -07001519 parser.add_argument('-y', '--yes', action='store_true', default=False,
Simon Glass13e05a02016-09-12 23:18:20 -06001520 help="respond 'yes' to any prompts")
Simon Glass4c4eb7c2023-02-01 13:19:12 -07001521 parser.add_argument('-u', '--update', action='store_true', default=False,
1522 help="update scripts/ files (use with --scan-source)")
Simon Glassd9c1da22021-12-18 14:54:31 -07001523 parser.add_argument('-v', '--verbose', action='store_true', default=False,
Joe Hershberger808b63f2015-05-19 13:21:24 -05001524 help='show any build errors as boards are built')
Simon Glassd9c1da22021-12-18 14:54:31 -07001525 parser.add_argument('configs', nargs='*')
Masahiro Yamadab6160812015-05-20 11:36:07 +09001526
Simon Glassd9c1da22021-12-18 14:54:31 -07001527 args = parser.parse_args()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001528
Simon Glassd9c1da22021-12-18 14:54:31 -07001529 if args.test:
Simon Glassbb57be72021-12-18 08:09:45 -07001530 sys.argv = [sys.argv[0]]
1531 fail, count = doctest.testmod()
1532 if fail:
1533 return 1
1534 unittest.main()
1535
Simon Glass4c4eb7c2023-02-01 13:19:12 -07001536 if args.scan_source:
1537 do_scan_source(os.getcwd(), args.update)
1538 return
1539
Simon Glass08d148a2023-09-23 13:43:54 -06001540 if not any((args.force_sync, args.build_db, args.imply, args.find)):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001541 parser.print_usage()
1542 sys.exit(1)
1543
Masahiro Yamadab903c4e2016-05-19 15:51:58 +09001544 # prefix the option name with CONFIG_ if missing
Simon Glass08d148a2023-09-23 13:43:54 -06001545 configs = [prefix_config(cfg) for cfg in args.configs]
Masahiro Yamadab6160812015-05-20 11:36:07 +09001546
Joe Hershberger23475932015-05-19 13:21:20 -05001547 check_top_directory()
1548
Simon Glassd9c1da22021-12-18 14:54:31 -07001549 if args.imply:
Simon Glass92e55582017-06-15 21:39:32 -06001550 imply_flags = 0
Simon Glassd9c1da22021-12-18 14:54:31 -07001551 if args.imply_flags == 'all':
Simon Glass5f096922017-07-10 14:47:46 -06001552 imply_flags = -1
1553
Simon Glassd9c1da22021-12-18 14:54:31 -07001554 elif args.imply_flags:
1555 for flag in args.imply_flags.split(','):
Simon Glass5f096922017-07-10 14:47:46 -06001556 bad = flag not in IMPLY_FLAGS
1557 if bad:
Simon Glass1f701862019-10-31 07:42:57 -06001558 print("Invalid flag '%s'" % flag)
Simon Glass5f096922017-07-10 14:47:46 -06001559 if flag == 'help' or bad:
Simon Glass1f701862019-10-31 07:42:57 -06001560 print("Imply flags: (separate with ',')")
1561 for name, info in IMPLY_FLAGS.items():
1562 print(' %-15s: %s' % (name, info[1]))
Simon Glass5f096922017-07-10 14:47:46 -06001563 parser.print_usage()
1564 sys.exit(1)
1565 imply_flags |= IMPLY_FLAGS[flag][0]
Simon Glass92e55582017-06-15 21:39:32 -06001566
Simon Glassd9c1da22021-12-18 14:54:31 -07001567 do_imply_config(configs, args.add_imply, imply_flags, args.skip_added)
Simon Glassc6e73cf2017-06-01 19:39:03 -06001568 return
1569
Simon Glassd9c1da22021-12-18 14:54:31 -07001570 if args.find:
Simon Glass0082b2e2021-12-18 08:09:46 -07001571 do_find_config(configs)
1572 return
1573
Simon Glass35688b62023-09-23 13:43:53 -06001574 # We are either building the database or forcing a sync of defconfigs
Simon Glass43cf08f2017-06-01 19:39:02 -06001575 config_db = {}
Simon Glass1f701862019-10-31 07:42:57 -06001576 db_queue = queue.Queue()
Simon Glass43cf08f2017-06-01 19:39:02 -06001577 t = DatabaseThread(config_db, db_queue)
Simon Glass5cda9022023-09-23 13:43:51 -06001578 t.daemon = True
Simon Glass43cf08f2017-06-01 19:39:02 -06001579 t.start()
1580
Simon Glass1572b3f2023-09-23 13:43:50 -06001581 check_clean_directory()
1582 bsettings.setup('')
1583 toolchains = toolchain.Toolchains()
1584 toolchains.GetSettings()
1585 toolchains.Scan(verbose=False)
Simon Glass08d148a2023-09-23 13:43:54 -06001586 move_config(toolchains, args, db_queue)
Simon Glass1572b3f2023-09-23 13:43:50 -06001587 db_queue.join()
Joe Hershberger23475932015-05-19 13:21:20 -05001588
Simon Glassd9c1da22021-12-18 14:54:31 -07001589 if args.commit:
Simon Glass8bf41c22016-09-12 23:18:21 -06001590 subprocess.call(['git', 'add', '-u'])
1591 if configs:
1592 msg = 'Convert %s %sto Kconfig' % (configs[0],
1593 'et al ' if len(configs) > 1 else '')
1594 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1595 '\n '.join(configs))
1596 else:
1597 msg = 'configs: Resync with savedefconfig'
1598 msg += '\n\nRsync all defconfig files using moveconfig.py'
1599 subprocess.call(['git', 'commit', '-s', '-m', msg])
1600
Simon Glassd9c1da22021-12-18 14:54:31 -07001601 if args.build_db:
Simon Glassb3464eb2021-12-18 14:54:35 -07001602 with open(CONFIG_DATABASE, 'w', encoding='utf-8') as fd:
Simon Glass1f701862019-10-31 07:42:57 -06001603 for defconfig, configs in config_db.items():
Simon Glass1c879312017-08-13 16:02:54 -06001604 fd.write('%s\n' % defconfig)
Simon Glass43cf08f2017-06-01 19:39:02 -06001605 for config in sorted(configs.keys()):
Simon Glass1c879312017-08-13 16:02:54 -06001606 fd.write(' %s=%s\n' % (config, configs[config]))
1607 fd.write('\n')
Simon Glass43cf08f2017-06-01 19:39:02 -06001608
Masahiro Yamadab6160812015-05-20 11:36:07 +09001609if __name__ == '__main__':
Simon Glass0082b2e2021-12-18 08:09:46 -07001610 sys.exit(main())