blob: 058d72cf4bccad263efd981c343b317ae0b4d22d [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
Simon Glassf876e962024-07-17 16:56:52 +01004"""Build and query a Kconfig database for boards.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09005
Simon Glass495e58c2024-07-17 16:56:54 +01006See doc/develop/qconfig.rst for documentation.
Simon Glassf876e962024-07-17 16:56:52 +01007
8Author: Masahiro Yamada <yamada.masahiro@socionext.com>
9Author: Simon Glass <sjg@chromium.org>
Masahiro Yamada5a27c732015-05-20 11:36:07 +090010"""
11
Simon Glassb2e83c62021-12-18 14:54:31 -070012from argparse import ArgumentParser
Simon Glass99b66602017-06-01 19:39:03 -060013import collections
Simon Glass91197aa2021-12-18 14:54:35 -070014from contextlib import ExitStack
Simon Glass84067a52021-12-18 08:09:45 -070015import doctest
Masahiro Yamadac8e1b102016-05-19 15:52:07 +090016import filecmp
Masahiro Yamada5a27c732015-05-20 11:36:07 +090017import fnmatch
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +090018import glob
Masahiro Yamada5a27c732015-05-20 11:36:07 +090019import multiprocessing
Masahiro Yamada5a27c732015-05-20 11:36:07 +090020import os
Simon Glass793dca32019-10-31 07:42:57 -060021import queue
Masahiro Yamada5a27c732015-05-20 11:36:07 +090022import re
23import shutil
24import subprocess
25import sys
26import tempfile
Simon Glassd73fcb12017-06-01 19:39:02 -060027import threading
Masahiro Yamada5a27c732015-05-20 11:36:07 +090028import time
Simon Glass84067a52021-12-18 08:09:45 -070029import unittest
Masahiro Yamada5a27c732015-05-20 11:36:07 +090030
Simon Glass0ede00f2020-04-17 18:09:02 -060031from buildman import bsettings
32from buildman import kconfiglib
33from buildman import toolchain
Simon Glass15f19ab2023-09-23 13:44:09 -060034from u_boot_pylib import terminal
Simon Glasscb008832017-06-15 21:39:33 -060035
Masahiro Yamada5a27c732015-05-20 11:36:07 +090036SHOW_GNU_MAKE = 'scripts/show-gnu-make'
37SLEEP_TIME=0.03
38
Masahiro Yamada5a27c732015-05-20 11:36:07 +090039STATE_IDLE = 0
40STATE_DEFCONFIG = 1
41STATE_AUTOCONF = 2
Joe Hershberger96464ba2015-05-19 13:21:17 -050042STATE_SAVEDEFCONFIG = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +090043
Simon Glassf3b8e642017-06-01 19:39:01 -060044AUTO_CONF_PATH = 'include/config/auto.conf'
Simon Glass51963792023-09-23 13:44:15 -060045CONFIG_DATABASE = 'qconfig.db'
46FAILED_LIST = 'qconfig.failed'
Simon Glassf3b8e642017-06-01 19:39:01 -060047
Simon Glasscb008832017-06-15 21:39:33 -060048CONFIG_LEN = len('CONFIG_')
Simon Glassf3b8e642017-06-01 19:39:01 -060049
Markus Klotzbuecherb237d352019-05-15 15:15:52 +020050SIZES = {
Simon Glassdaa694d2021-12-18 14:54:30 -070051 'SZ_1': 0x00000001, 'SZ_2': 0x00000002,
52 'SZ_4': 0x00000004, 'SZ_8': 0x00000008,
53 'SZ_16': 0x00000010, 'SZ_32': 0x00000020,
54 'SZ_64': 0x00000040, 'SZ_128': 0x00000080,
55 'SZ_256': 0x00000100, 'SZ_512': 0x00000200,
56 'SZ_1K': 0x00000400, 'SZ_2K': 0x00000800,
57 'SZ_4K': 0x00001000, 'SZ_8K': 0x00002000,
58 'SZ_16K': 0x00004000, 'SZ_32K': 0x00008000,
59 'SZ_64K': 0x00010000, 'SZ_128K': 0x00020000,
60 'SZ_256K': 0x00040000, 'SZ_512K': 0x00080000,
61 'SZ_1M': 0x00100000, 'SZ_2M': 0x00200000,
62 'SZ_4M': 0x00400000, 'SZ_8M': 0x00800000,
63 'SZ_16M': 0x01000000, 'SZ_32M': 0x02000000,
64 'SZ_64M': 0x04000000, 'SZ_128M': 0x08000000,
65 'SZ_256M': 0x10000000, 'SZ_512M': 0x20000000,
66 'SZ_1G': 0x40000000, 'SZ_2G': 0x80000000,
67 'SZ_4G': 0x100000000
Markus Klotzbuecherb237d352019-05-15 15:15:52 +020068}
69
Simon Glassb8d11da2022-02-08 11:49:45 -070070RE_REMOVE_DEFCONFIG = re.compile(r'(.*)_defconfig')
71
Simon Glass65e62032023-02-01 13:19:12 -070072# CONFIG symbols present in the build system (from Linux) but not actually used
73# in U-Boot; KCONFIG symbols
74IGNORE_SYMS = ['DEBUG_SECTION_MISMATCH', 'FTRACE_MCOUNT_RECORD', 'GCOV_KERNEL',
75 'GCOV_PROFILE_ALL', 'KALLSYMS', 'KASAN', 'MODVERSIONS', 'SHELL',
76 'TPL_BUILD', 'VPL_BUILD', 'IS_ENABLED', 'FOO', 'IF_ENABLED_INT',
77 'IS_ENABLED_', 'IS_ENABLED_1', 'IS_ENABLED_2', 'IS_ENABLED_3',
78 'SPL_', 'TPL_', 'SPL_FOO', 'TPL_FOO', 'TOOLS_FOO',
79 'ACME', 'SPL_ACME', 'TPL_ACME', 'TRACE_BRANCH_PROFILING',
Simon Glass90fb55e2024-09-29 19:49:33 -060080 'VAL', '_UNDEFINED', 'SPL_BUILD', 'XPL_BUILD', ]
Simon Glass65e62032023-02-01 13:19:12 -070081
82SPL_PREFIXES = ['SPL_', 'TPL_', 'VPL_', 'TOOLS_']
83
Masahiro Yamada5a27c732015-05-20 11:36:07 +090084### helper functions ###
Masahiro Yamada5a27c732015-05-20 11:36:07 +090085def check_top_directory():
86 """Exit if we are not at the top of source directory."""
Simon Glass91197aa2021-12-18 14:54:35 -070087 for fname in 'README', 'Licenses':
88 if not os.path.exists(fname):
Masahiro Yamada5a27c732015-05-20 11:36:07 +090089 sys.exit('Please run at the top of source directory.')
90
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +090091def check_clean_directory():
92 """Exit if the source tree is not clean."""
Simon Glass91197aa2021-12-18 14:54:35 -070093 for fname in '.config', 'include/config':
94 if os.path.exists(fname):
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +090095 sys.exit("source tree is not clean, please run 'make mrproper'")
96
Masahiro Yamada5a27c732015-05-20 11:36:07 +090097def get_make_cmd():
98 """Get the command name of GNU Make.
99
100 U-Boot needs GNU Make for building, but the command name is not
101 necessarily "make". (for example, "gmake" on FreeBSD).
102 Returns the most appropriate command name on your system.
103 """
Simon Glass91197aa2021-12-18 14:54:35 -0700104 with subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE) as proc:
105 ret = proc.communicate()
106 if proc.returncode:
107 sys.exit('GNU Make not found')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900108 return ret[0].rstrip()
109
Simon Glass25f978c2017-06-01 19:38:58 -0600110def get_matched_defconfig(line):
111 """Get the defconfig files that match a pattern
112
113 Args:
Simon Glass91197aa2021-12-18 14:54:35 -0700114 line (str): Path or filename to match, e.g. 'configs/snow_defconfig' or
Simon Glass25f978c2017-06-01 19:38:58 -0600115 'k2*_defconfig'. If no directory is provided, 'configs/' is
116 prepended
117
118 Returns:
Simon Glass91197aa2021-12-18 14:54:35 -0700119 list of str: a list of matching defconfig files
Simon Glass25f978c2017-06-01 19:38:58 -0600120 """
121 dirname = os.path.dirname(line)
122 if dirname:
123 pattern = line
124 else:
125 pattern = os.path.join('configs', line)
126 return glob.glob(pattern) + glob.glob(pattern + '_defconfig')
127
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900128def get_matched_defconfigs(defconfigs_file):
Simon Glassee4e61b2017-06-01 19:38:59 -0600129 """Get all the defconfig files that match the patterns in a file.
130
131 Args:
Simon Glass91197aa2021-12-18 14:54:35 -0700132 defconfigs_file (str): File containing a list of defconfigs to process,
133 or '-' to read the list from stdin
Simon Glassee4e61b2017-06-01 19:38:59 -0600134
135 Returns:
Simon Glass91197aa2021-12-18 14:54:35 -0700136 list of str: A list of paths to defconfig files, with no duplicates
Simon Glassee4e61b2017-06-01 19:38:59 -0600137 """
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900138 defconfigs = []
Simon Glass91197aa2021-12-18 14:54:35 -0700139 with ExitStack() as stack:
140 if defconfigs_file == '-':
141 inf = sys.stdin
142 defconfigs_file = 'stdin'
143 else:
144 inf = stack.enter_context(open(defconfigs_file, encoding='utf-8'))
145 for i, line in enumerate(inf):
146 line = line.strip()
147 if not line:
148 continue # skip blank lines silently
149 if ' ' in line:
150 line = line.split(' ')[0] # handle 'git log' input
151 matched = get_matched_defconfig(line)
152 if not matched:
153 print(f"warning: {defconfigs_file}:{i + 1}: no defconfig matched '{line}'",
154 file=sys.stderr)
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900155
Simon Glass91197aa2021-12-18 14:54:35 -0700156 defconfigs += matched
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900157
158 # use set() to drop multiple matching
Simon Glass91197aa2021-12-18 14:54:35 -0700159 return [defconfig[len('configs') + 1:] for defconfig in set(defconfigs)]
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900160
Masahiro Yamada684c3062016-07-25 19:15:28 +0900161def get_all_defconfigs():
Simon Glass91197aa2021-12-18 14:54:35 -0700162 """Get all the defconfig files under the configs/ directory.
163
164 Returns:
165 list of str: List of paths to defconfig files
166 """
Masahiro Yamada684c3062016-07-25 19:15:28 +0900167 defconfigs = []
Simon Glass91197aa2021-12-18 14:54:35 -0700168 for (dirpath, _, filenames) in os.walk('configs'):
Masahiro Yamada684c3062016-07-25 19:15:28 +0900169 dirpath = dirpath[len('configs') + 1:]
170 for filename in fnmatch.filter(filenames, '*_defconfig'):
171 defconfigs.append(os.path.join(dirpath, filename))
172
173 return defconfigs
174
Simon Glass2fd85bd2021-12-18 14:54:33 -0700175def write_file(fname, data):
176 """Write data to a file
177
178 Args:
179 fname (str): Filename to write to
180 data (list of str): Lines to write (with or without trailing newline);
181 or str to write
182 """
183 with open(fname, 'w', encoding='utf-8') as out:
184 if isinstance(data, list):
185 for line in data:
186 print(line.rstrip('\n'), file=out)
187 else:
188 out.write(data)
189
Simon Glass37f815c2021-12-18 14:54:34 -0700190def read_file(fname, as_lines=True, skip_unicode=False):
191 """Read a file and return the contents
192
193 Args:
194 fname (str): Filename to read from
Simon Glass549d4222023-09-23 13:43:58 -0600195 as_lines (bool): Return file contents as a list of lines
Simon Glass37f815c2021-12-18 14:54:34 -0700196 skip_unicode (bool): True to report unicode errors and continue
197
198 Returns:
199 iter of str: List of ;ines from the file with newline removed; str if
200 as_lines is False with newlines intact; or None if a unicode error
201 occurred
202
203 Raises:
204 UnicodeDecodeError: Unicode error occurred when reading
205 """
206 with open(fname, encoding='utf-8') as inf:
207 try:
208 if as_lines:
209 return [line.rstrip('\n') for line in inf.readlines()]
Simon Glassf297ba32023-09-23 13:44:05 -0600210 return inf.read()
Simon Glassa4c9d172023-09-23 13:44:01 -0600211 except UnicodeDecodeError as exc:
Simon Glass37f815c2021-12-18 14:54:34 -0700212 if not skip_unicode:
Simon Glass68a0b712022-02-11 13:23:22 -0700213 raise
Simon Glassa4c9d172023-09-23 13:44:01 -0600214 print(f"Failed on file '{fname}: {exc}")
Simon Glass37f815c2021-12-18 14:54:34 -0700215 return None
216
Chris Packhamca438342017-05-02 21:30:47 +1200217
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900218### classes ###
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900219class Progress:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900220 """Progress Indicator"""
221
Simon Glass6b25d212023-09-23 13:44:10 -0600222 def __init__(self, col, total):
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900223 """Create a new progress indicator.
224
Simon Glass91197aa2021-12-18 14:54:35 -0700225 Args:
Simon Glass0229eee2024-07-17 16:57:07 +0100226 col (terminal.Color): Colour-output class
Simon Glass6b25d212023-09-23 13:44:10 -0600227 total (int): A number of defconfig files to process.
Simon Glass0229eee2024-07-17 16:57:07 +0100228
229 current (int): Number of boards processed so far
230 failed (int): Number of failed boards
231 failure_msg (str): Message indicating number of failures, '' if none
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900232 """
Simon Glass6b25d212023-09-23 13:44:10 -0600233 self.col = col
Simon Glass0229eee2024-07-17 16:57:07 +0100234 self.total = total
235
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900236 self.current = 0
Simon Glass6b25d212023-09-23 13:44:10 -0600237 self.good = 0
Simon Glass0229eee2024-07-17 16:57:07 +0100238 self.failed = None
239 self.failure_msg = None
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900240
Simon Glass6b25d212023-09-23 13:44:10 -0600241 def inc(self, success):
242 """Increment the number of processed defconfig files.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900243
Simon Glass6b25d212023-09-23 13:44:10 -0600244 Args:
245 success (bool): True if processing succeeded
246 """
247 self.good += success
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900248 self.current += 1
249
250 def show(self):
251 """Display the progress."""
Simon Glass95f09142023-09-23 13:44:08 -0600252 if self.current != self.total:
Simon Glass6b25d212023-09-23 13:44:10 -0600253 line = self.col.build(self.col.GREEN, f'{self.good:5d}')
254 line += self.col.build(self.col.RED,
255 f'{self.current - self.good:5d}')
256 line += self.col.build(self.col.MAGENTA,
257 f'/{self.total - self.current}')
258 print(f'{line} \r', end='')
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900259 sys.stdout.flush()
260
Simon Glass0229eee2024-07-17 16:57:07 +0100261 def completed(self):
262 """Set up extra properties when completed"""
263 self.failed = self.total - self.good
264 self.failure_msg = f'{self.failed} failed, ' if self.failed else ''
265
Simon Glasscb008832017-06-15 21:39:33 -0600266
Simon Glass0a0c1242024-07-17 16:56:51 +0100267def scan_kconfig():
268 """Scan all the Kconfig files and create a Config object
Simon Glasscb008832017-06-15 21:39:33 -0600269
Simon Glass0a0c1242024-07-17 16:56:51 +0100270 Returns:
271 Kconfig object
272 """
273 # Define environment variables referenced from Kconfig
274 os.environ['srctree'] = os.getcwd()
275 os.environ['UBOOTVERSION'] = 'dummy'
276 os.environ['KCONFIG_OBJDIR'] = ''
277 os.environ['CC'] = 'gcc'
278 return kconfiglib.Kconfig()
Simon Glasscb008832017-06-15 21:39:33 -0600279
280
Simon Glassf876e962024-07-17 16:56:52 +0100281# pylint: disable=R0903
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900282class KconfigParser:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900283 """A parser of .config and include/autoconf.mk."""
284
285 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
286 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
287
Simon Glass882c8e42023-09-23 13:43:54 -0600288 def __init__(self, args, build_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900289 """Create a new parser.
290
Simon Glass91197aa2021-12-18 14:54:35 -0700291 Args:
Simon Glass91197aa2021-12-18 14:54:35 -0700292 args (Namespace): program arguments
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900293 build_dir: Build directory.
294 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700295 self.args = args
Masahiro Yamada1f169922016-05-19 15:52:00 +0900296 self.dotconfig = os.path.join(build_dir, '.config')
297 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
Masahiro Yamada07913d12016-08-22 22:18:22 +0900298 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
299 'autoconf.mk')
Simon Glassf3b8e642017-06-01 19:39:01 -0600300 self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900301 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900302
Simon Glass6821a742017-07-10 14:47:47 -0600303 def get_arch(self):
304 """Parse .config file and return the architecture.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900305
306 Returns:
Simon Glass6821a742017-07-10 14:47:47 -0600307 Architecture name (e.g. 'arm').
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900308 """
309 arch = ''
310 cpu = ''
Simon Glass37f815c2021-12-18 14:54:34 -0700311 for line in read_file(self.dotconfig):
Simon Glassa4c9d172023-09-23 13:44:01 -0600312 m_arch = self.re_arch.match(line)
313 if m_arch:
314 arch = m_arch.group(1)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900315 continue
Simon Glassa4c9d172023-09-23 13:44:01 -0600316 m_cpu = self.re_cpu.match(line)
317 if m_cpu:
318 cpu = m_cpu.group(1)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900319
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900320 if not arch:
321 return None
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900322
323 # fix-up for aarch64
324 if arch == 'arm' and cpu == 'armv8':
325 arch = 'aarch64'
326
Simon Glass6821a742017-07-10 14:47:47 -0600327 return arch
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900328
Simon Glassd73fcb12017-06-01 19:39:02 -0600329
330class DatabaseThread(threading.Thread):
331 """This thread processes results from Slot threads.
332
333 It collects the data in the master config directary. There is only one
334 result thread, and this helps to serialise the build output.
335 """
336 def __init__(self, config_db, db_queue):
337 """Set up a new result thread
338
339 Args:
340 builder: Builder which will be sent each result
341 """
342 threading.Thread.__init__(self)
343 self.config_db = config_db
344 self.db_queue= db_queue
345
346 def run(self):
347 """Called to start up the result thread.
348
349 We collect the next result job and pass it on to the build.
350 """
351 while True:
352 defconfig, configs = self.db_queue.get()
353 self.config_db[defconfig] = configs
354 self.db_queue.task_done()
355
356
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900357class Slot:
358
359 """A slot to store a subprocess.
360
361 Each instance of this class handles one subprocess.
362 This class is useful to control multiple threads
363 for faster processing.
364 """
365
Simon Glass15f19ab2023-09-23 13:44:09 -0600366 def __init__(self, toolchains, args, progress, devnull, make_cmd,
Simon Glasse23a5832024-07-17 16:57:10 +0100367 reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900368 """Create a new process slot.
369
Simon Glass91197aa2021-12-18 14:54:35 -0700370 Args:
Simon Glass6821a742017-07-10 14:47:47 -0600371 toolchains: Toolchains object containing toolchains.
Simon Glassb2e83c62021-12-18 14:54:31 -0700372 args: Program arguments
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900373 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900374 devnull: A file object of '/dev/null'.
375 make_cmd: command name of GNU Make.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500376 reference_src_dir: Determine the true starting config state from this
377 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -0600378 db_queue: output queue to write config info for the database
Simon Glass15f19ab2023-09-23 13:44:09 -0600379 col (terminal.Color): Colour object
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900380 """
Simon Glass6821a742017-07-10 14:47:47 -0600381 self.toolchains = toolchains
Simon Glassb2e83c62021-12-18 14:54:31 -0700382 self.args = args
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900383 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900384 self.build_dir = tempfile.mkdtemp()
385 self.devnull = devnull
386 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500387 self.reference_src_dir = reference_src_dir
Simon Glassd73fcb12017-06-01 19:39:02 -0600388 self.db_queue = db_queue
Simon Glasse23a5832024-07-17 16:57:10 +0100389 self.col = progress.col
Simon Glass882c8e42023-09-23 13:43:54 -0600390 self.parser = KconfigParser(args, self.build_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900391 self.state = STATE_IDLE
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900392 self.failed_boards = set()
Simon Glassa6ab4db2023-09-23 13:44:02 -0600393 self.defconfig = None
Simon Glass9461bf02023-09-23 13:44:07 -0600394 self.log = []
Simon Glassa6ab4db2023-09-23 13:44:02 -0600395 self.current_src_dir = None
396 self.proc = None
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900397
398 def __del__(self):
399 """Delete the working directory
400
401 This function makes sure the temporary directory is cleaned away
402 even if Python suddenly dies due to error. It should be done in here
Joe Hershbergerf2dae752016-06-10 14:53:29 -0500403 because it is guaranteed the destructor is always invoked when the
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900404 instance of the class gets unreferenced.
405
406 If the subprocess is still running, wait until it finishes.
407 """
408 if self.state != STATE_IDLE:
Simon Glassf297ba32023-09-23 13:44:05 -0600409 while self.proc.poll() is None:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900410 pass
411 shutil.rmtree(self.build_dir)
412
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900413 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900414 """Assign a new subprocess for defconfig and add it to the slot.
415
416 If the slot is vacant, create a new subprocess for processing the
417 given defconfig and add it to the slot. Just returns False if
418 the slot is occupied (i.e. the current subprocess is still running).
419
Simon Glass91197aa2021-12-18 14:54:35 -0700420 Args:
Simon Glass549d4222023-09-23 13:43:58 -0600421 defconfig (str): defconfig name.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900422
423 Returns:
424 Return True on success or False on failure
425 """
426 if self.state != STATE_IDLE:
427 return False
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900428
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900429 self.defconfig = defconfig
Simon Glass9461bf02023-09-23 13:44:07 -0600430 self.log = []
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900431 self.current_src_dir = self.reference_src_dir
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900432 self.do_defconfig()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900433 return True
434
435 def poll(self):
436 """Check the status of the subprocess and handle it as needed.
437
438 Returns True if the slot is vacant (i.e. in idle state).
439 If the configuration is successfully finished, assign a new
440 subprocess to build include/autoconf.mk.
441 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900442 parse the .config and the include/autoconf.mk, moving
443 config options to the .config as needed.
444 If the .config was updated, run "make savedefconfig" to sync
445 it, update the original defconfig, and then set the slot back
446 to the idle state.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900447
448 Returns:
449 Return True if the subprocess is terminated, False otherwise
450 """
451 if self.state == STATE_IDLE:
452 return True
453
Simon Glassf297ba32023-09-23 13:44:05 -0600454 if self.proc.poll() is None:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900455 return False
456
Simon Glassa4c9d172023-09-23 13:44:01 -0600457 if self.proc.poll() != 0:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900458 self.handle_error()
459 elif self.state == STATE_DEFCONFIG:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900460 if self.reference_src_dir and not self.current_src_dir:
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500461 self.do_savedefconfig()
462 else:
463 self.do_autoconf()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900464 elif self.state == STATE_AUTOCONF:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900465 if self.current_src_dir:
466 self.current_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500467 self.do_defconfig()
Simon Glassb2e83c62021-12-18 14:54:31 -0700468 elif self.args.build_db:
Simon Glassd73fcb12017-06-01 19:39:02 -0600469 self.do_build_db()
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500470 else:
471 self.do_savedefconfig()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900472 elif self.state == STATE_SAVEDEFCONFIG:
473 self.update_defconfig()
474 else:
Simon Glassdaa694d2021-12-18 14:54:30 -0700475 sys.exit('Internal Error. This should not happen.')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900476
Simon Glassf297ba32023-09-23 13:44:05 -0600477 return self.state == STATE_IDLE
Joe Hershberger96464ba2015-05-19 13:21:17 -0500478
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900479 def handle_error(self):
480 """Handle error cases."""
Masahiro Yamada8513dc02016-05-19 15:52:08 +0900481
Simon Glass15f19ab2023-09-23 13:44:09 -0600482 self.log.append(self.col.build(self.col.RED, 'Failed to process',
483 bright=True))
Simon Glassb2e83c62021-12-18 14:54:31 -0700484 if self.args.verbose:
Simon Glass9461bf02023-09-23 13:44:07 -0600485 for line in self.proc.stderr.read().decode().splitlines():
Simon Glass15f19ab2023-09-23 13:44:09 -0600486 self.log.append(self.col.build(self.col.CYAN, line, True))
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900487 self.finish(False)
Joe Hershberger96464ba2015-05-19 13:21:17 -0500488
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900489 def do_defconfig(self):
490 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900491
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900492 cmd = list(self.make_cmd)
493 cmd.append(self.defconfig)
Simon Glassf876e962024-07-17 16:56:52 +0100494 # pylint: disable=R1732
Simon Glassa4c9d172023-09-23 13:44:01 -0600495 self.proc = subprocess.Popen(cmd, stdout=self.devnull,
496 stderr=subprocess.PIPE,
497 cwd=self.current_src_dir)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900498 self.state = STATE_DEFCONFIG
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900499
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900500 def do_autoconf(self):
Simon Glassf3b8e642017-06-01 19:39:01 -0600501 """Run 'make AUTO_CONF_PATH'."""
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900502
Simon Glass6821a742017-07-10 14:47:47 -0600503 arch = self.parser.get_arch()
504 try:
Simon Glassf297ba32023-09-23 13:44:05 -0600505 tchain = self.toolchains.Select(arch)
Simon Glass6821a742017-07-10 14:47:47 -0600506 except ValueError:
Simon Glass15f19ab2023-09-23 13:44:09 -0600507 self.log.append(self.col.build(
508 self.col.YELLOW,
509 f"Tool chain for '{arch}' is missing: do nothing"))
Masahiro Yamada4efef992016-05-19 15:52:03 +0900510 self.finish(False)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900511 return
Simon Glassf297ba32023-09-23 13:44:05 -0600512 env = tchain.MakeEnvironment(False)
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900513
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900514 cmd = list(self.make_cmd)
Joe Hershberger7740f652015-05-19 13:21:18 -0500515 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Simon Glassf3b8e642017-06-01 19:39:01 -0600516 cmd.append(AUTO_CONF_PATH)
Simon Glassf876e962024-07-17 16:56:52 +0100517 # pylint: disable=R1732
Simon Glassa4c9d172023-09-23 13:44:01 -0600518 self.proc = subprocess.Popen(cmd, stdout=self.devnull, env=env,
519 stderr=subprocess.PIPE,
520 cwd=self.current_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900521 self.state = STATE_AUTOCONF
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900522
Simon Glassd73fcb12017-06-01 19:39:02 -0600523 def do_build_db(self):
524 """Add the board to the database"""
525 configs = {}
Simon Glass37f815c2021-12-18 14:54:34 -0700526 for line in read_file(os.path.join(self.build_dir, AUTO_CONF_PATH)):
527 if line.startswith('CONFIG'):
528 config, value = line.split('=', 1)
529 configs[config] = value.rstrip()
Simon Glassd73fcb12017-06-01 19:39:02 -0600530 self.db_queue.put([self.defconfig, configs])
531 self.finish(True)
532
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900533 def do_savedefconfig(self):
534 """Update the .config and run 'make savedefconfig'."""
Simon Glassc7345612023-09-23 13:43:55 -0600535 if not self.args.force_sync:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900536 self.finish(True)
537 return
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900538
539 cmd = list(self.make_cmd)
540 cmd.append('savedefconfig')
Simon Glassf876e962024-07-17 16:56:52 +0100541 # pylint: disable=R1732
Simon Glassa4c9d172023-09-23 13:44:01 -0600542 self.proc = subprocess.Popen(cmd, stdout=self.devnull,
543 stderr=subprocess.PIPE)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900544 self.state = STATE_SAVEDEFCONFIG
545
546 def update_defconfig(self):
547 """Update the input defconfig and go back to the idle state."""
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900548 orig_defconfig = os.path.join('configs', self.defconfig)
549 new_defconfig = os.path.join(self.build_dir, 'defconfig')
550 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
551
552 if updated:
Simon Glass15f19ab2023-09-23 13:44:09 -0600553 self.log.append(
554 self.col.build(self.col.BLUE, 'defconfig updated', bright=True))
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900555
Simon Glassb2e83c62021-12-18 14:54:31 -0700556 if not self.args.dry_run and updated:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900557 shutil.move(new_defconfig, orig_defconfig)
558 self.finish(True)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900559
Masahiro Yamada4efef992016-05-19 15:52:03 +0900560 def finish(self, success):
561 """Display log along with progress and go to the idle state.
Masahiro Yamada1d085562016-05-19 15:52:02 +0900562
Simon Glass91197aa2021-12-18 14:54:35 -0700563 Args:
Simon Glass549d4222023-09-23 13:43:58 -0600564 success (bool): Should be True when the defconfig was processed
Masahiro Yamada4efef992016-05-19 15:52:03 +0900565 successfully, or False when it fails.
Masahiro Yamada1d085562016-05-19 15:52:02 +0900566 """
567 # output at least 30 characters to hide the "* defconfigs out of *".
Simon Glassdc1d2e62023-09-23 13:44:11 -0600568 name = self.defconfig[:-len('_defconfig')]
Simon Glass5aba58c2023-09-23 13:44:06 -0600569 if self.log:
Masahiro Yamada1d085562016-05-19 15:52:02 +0900570
Simon Glass9461bf02023-09-23 13:44:07 -0600571 # Put the first log line on the first line
572 log = name.ljust(20) + ' ' + self.log[0]
573
574 if len(self.log) > 1:
575 log += '\n' + '\n'.join([' ' + s for s in self.log[1:]])
Simon Glass5aba58c2023-09-23 13:44:06 -0600576 # Some threads are running in parallel.
577 # Print log atomically to not mix up logs from different threads.
578 print(log, file=(sys.stdout if success else sys.stderr))
Masahiro Yamada4efef992016-05-19 15:52:03 +0900579
580 if not success:
Simon Glassb2e83c62021-12-18 14:54:31 -0700581 if self.args.exit_on_error:
Simon Glassdaa694d2021-12-18 14:54:30 -0700582 sys.exit('Exit on error.')
Masahiro Yamada4efef992016-05-19 15:52:03 +0900583 # If --exit-on-error flag is not set, skip this board and continue.
584 # Record the failed board.
Simon Glassdc1d2e62023-09-23 13:44:11 -0600585 self.failed_boards.add(name)
Masahiro Yamada4efef992016-05-19 15:52:03 +0900586
Simon Glass6b25d212023-09-23 13:44:10 -0600587 self.progress.inc(success)
Masahiro Yamada1d085562016-05-19 15:52:02 +0900588 self.progress.show()
Masahiro Yamada4efef992016-05-19 15:52:03 +0900589 self.state = STATE_IDLE
Masahiro Yamada1d085562016-05-19 15:52:02 +0900590
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900591 def get_failed_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900592 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900593 """
594 return self.failed_boards
595
596class Slots:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900597 """Controller of the array of subprocess slots."""
598
Simon Glasse23a5832024-07-17 16:57:10 +0100599 def __init__(self, toolchains, args, progress, reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900600 """Create a new slots controller.
601
Simon Glass91197aa2021-12-18 14:54:35 -0700602 Args:
Simon Glass15f19ab2023-09-23 13:44:09 -0600603 toolchains (Toolchains): Toolchains object containing toolchains
604 args (Namespace): Program arguments
605 progress (Progress): A progress indicator.
606 reference_src_dir (str): Determine the true starting config state
607 from this source tree (None for none)
608 db_queue (Queue): output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900609 """
Simon Glassb2e83c62021-12-18 14:54:31 -0700610 self.args = args
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900611 self.slots = []
Simon Glassdc1d2e62023-09-23 13:44:11 -0600612 self.progress = progress
Simon Glasse23a5832024-07-17 16:57:10 +0100613 self.col = progress.col
Simon Glass478920d2021-12-18 14:54:32 -0700614 devnull = subprocess.DEVNULL
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900615 make_cmd = get_make_cmd()
Simon Glass62fae4b2023-09-23 13:44:00 -0600616 for _ in range(args.jobs):
Simon Glass882c8e42023-09-23 13:43:54 -0600617 self.slots.append(Slot(toolchains, args, progress, devnull,
Simon Glasse23a5832024-07-17 16:57:10 +0100618 make_cmd, reference_src_dir, db_queue))
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900619
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900620 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900621 """Add a new subprocess if a vacant slot is found.
622
Simon Glass91197aa2021-12-18 14:54:35 -0700623 Args:
Simon Glass549d4222023-09-23 13:43:58 -0600624 defconfig (str): defconfig name to be put into.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900625
626 Returns:
627 Return True on success or False on failure
628 """
629 for slot in self.slots:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900630 if slot.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900631 return True
632 return False
633
634 def available(self):
635 """Check if there is a vacant slot.
636
637 Returns:
638 Return True if at lease one vacant slot is found, False otherwise.
639 """
640 for slot in self.slots:
641 if slot.poll():
642 return True
643 return False
644
645 def empty(self):
646 """Check if all slots are vacant.
647
648 Returns:
649 Return True if all the slots are vacant, False otherwise.
650 """
651 ret = True
652 for slot in self.slots:
653 if not slot.poll():
654 ret = False
655 return ret
656
Simon Glass94e2ed72023-09-23 13:44:13 -0600657 def write_failed_boards(self):
Simon Glassdc1d2e62023-09-23 13:44:11 -0600658 """Show the results of processing"""
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900659 boards = set()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900660
661 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900662 boards |= slot.get_failed_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900663
Masahiro Yamada96dccd92016-06-15 14:33:53 +0900664 if boards:
Simon Glassdc1d2e62023-09-23 13:44:11 -0600665 boards = '\n'.join(sorted(boards)) + '\n'
Simon Glass94e2ed72023-09-23 13:44:13 -0600666 write_file(FAILED_LIST, boards)
667
Joe Hershberger2559cd82015-05-19 13:21:22 -0500668
Masahiro Yamada5cc42a52016-06-15 14:33:51 +0900669class ReferenceSource:
670
671 """Reference source against which original configs should be parsed."""
672
673 def __init__(self, commit):
674 """Create a reference source directory based on a specified commit.
675
Simon Glass91197aa2021-12-18 14:54:35 -0700676 Args:
Masahiro Yamada5cc42a52016-06-15 14:33:51 +0900677 commit: commit to git-clone
678 """
679 self.src_dir = tempfile.mkdtemp()
Simon Glassdaa694d2021-12-18 14:54:30 -0700680 print('Cloning git repo to a separate work directory...')
Masahiro Yamada5cc42a52016-06-15 14:33:51 +0900681 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
682 cwd=self.src_dir)
Simon Glass1bd43062023-09-23 13:43:59 -0600683 rev = subprocess.check_output(['git', 'rev-parse', '--short',
684 commit]).strip()
685 print(f"Checkout '{rev}' to build the original autoconf.mk.")
Masahiro Yamada5cc42a52016-06-15 14:33:51 +0900686 subprocess.check_output(['git', 'checkout', commit],
687 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500688
689 def __del__(self):
Masahiro Yamada5cc42a52016-06-15 14:33:51 +0900690 """Delete the reference source directory
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500691
692 This function makes sure the temporary directory is cleaned away
693 even if Python suddenly dies due to error. It should be done in here
694 because it is guaranteed the destructor is always invoked when the
695 instance of the class gets unreferenced.
696 """
Masahiro Yamada5cc42a52016-06-15 14:33:51 +0900697 shutil.rmtree(self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500698
Masahiro Yamada5cc42a52016-06-15 14:33:51 +0900699 def get_dir(self):
700 """Return the absolute path to the reference source directory."""
701
702 return self.src_dir
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500703
Simon Glass948d0b42024-07-17 16:57:09 +0100704def move_config(args):
Simon Glass882c8e42023-09-23 13:43:54 -0600705 """Build database or sync config options to defconfig files.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900706
Simon Glass91197aa2021-12-18 14:54:35 -0700707 Args:
Simon Glass15f19ab2023-09-23 13:44:09 -0600708 args (Namespace): Program arguments
Simon Glass94e2ed72023-09-23 13:44:13 -0600709
710 Returns:
Simon Glasseb1df3f2024-07-17 16:57:05 +0100711 tuple:
712 config_db (dict of configs for each defconfig):
713 key: defconfig name, e.g. "MPC8548CDS_legacy_defconfig"
714 value: dict:
715 key: CONFIG option
716 value: Value of option
717 Progress: Progress indicator
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900718 """
Simon Glasseb1df3f2024-07-17 16:57:05 +0100719 config_db = {}
720 db_queue = queue.Queue()
721 dbt = DatabaseThread(config_db, db_queue)
722 dbt.daemon = True
723 dbt.start()
724
725 check_clean_directory()
726 bsettings.setup('')
727
728 # Get toolchains to use
729 toolchains = toolchain.Toolchains()
730 toolchains.GetSettings()
731 toolchains.Scan(verbose=False)
732
Simon Glassb2e83c62021-12-18 14:54:31 -0700733 if args.git_ref:
734 reference_src = ReferenceSource(args.git_ref)
Masahiro Yamada5cc42a52016-06-15 14:33:51 +0900735 reference_src_dir = reference_src.get_dir()
736 else:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900737 reference_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500738
Simon Glassb2e83c62021-12-18 14:54:31 -0700739 if args.defconfigs:
740 defconfigs = get_matched_defconfigs(args.defconfigs)
Joe Hershberger91040e82015-05-19 13:21:19 -0500741 else:
Masahiro Yamada684c3062016-07-25 19:15:28 +0900742 defconfigs = get_all_defconfigs()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900743
Simon Glass948d0b42024-07-17 16:57:09 +0100744 col = terminal.Color(terminal.COLOR_NEVER if args.nocolour
745 else terminal.COLOR_IF_TERMINAL)
Simon Glass6b25d212023-09-23 13:44:10 -0600746 progress = Progress(col, len(defconfigs))
Simon Glasse23a5832024-07-17 16:57:10 +0100747 slots = Slots(toolchains, args, progress, reference_src_dir, db_queue)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900748
749 # Main loop to process defconfig files:
750 # Add a new subprocess into a vacant slot.
751 # Sleep if there is no available slot.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900752 for defconfig in defconfigs:
753 while not slots.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900754 while not slots.available():
755 # No available slot: sleep for a while
756 time.sleep(SLEEP_TIME)
757
758 # wait until all the subprocesses finish
759 while not slots.empty():
760 time.sleep(SLEEP_TIME)
761
Simon Glass94e2ed72023-09-23 13:44:13 -0600762 slots.write_failed_boards()
Simon Glasseb1df3f2024-07-17 16:57:05 +0100763 db_queue.join()
Simon Glass0229eee2024-07-17 16:57:07 +0100764 progress.completed()
Simon Glasseb1df3f2024-07-17 16:57:05 +0100765 return config_db, progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900766
Simon Glasscb008832017-06-15 21:39:33 -0600767def find_kconfig_rules(kconf, config, imply_config):
768 """Check whether a config has a 'select' or 'imply' keyword
769
770 Args:
Simon Glass549d4222023-09-23 13:43:58 -0600771 kconf (Kconfiglib.Kconfig): Kconfig object
772 config (str): Name of config to check (without CONFIG_ prefix)
773 imply_config (str): Implying config (without CONFIG_ prefix) which may
774 or may not have an 'imply' for 'config')
Simon Glasscb008832017-06-15 21:39:33 -0600775
776 Returns:
777 Symbol object for 'config' if found, else None
778 """
Tom Rini65e05dd2019-09-20 17:42:09 -0400779 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -0600780 if sym:
Simon Glass62fae4b2023-09-23 13:44:00 -0600781 for sel, _ in (sym.selects + sym.implies):
Simon Glassa3627082021-12-18 08:09:42 -0700782 if sel.name == config:
Simon Glasscb008832017-06-15 21:39:33 -0600783 return sym
784 return None
785
Simon Glassf876e962024-07-17 16:56:52 +0100786def check_imply_rule(kconf, imply_config):
Simon Glasscb008832017-06-15 21:39:33 -0600787 """Check if we can add an 'imply' option
788
789 This finds imply_config in the Kconfig and looks to see if it is possible
790 to add an 'imply' for 'config' to that part of the Kconfig.
791
792 Args:
Simon Glass549d4222023-09-23 13:43:58 -0600793 kconf (Kconfiglib.Kconfig): Kconfig object
Simon Glass549d4222023-09-23 13:43:58 -0600794 imply_config (str): Implying config (without CONFIG_ prefix) which may
795 or may not have an 'imply' for 'config')
Simon Glasscb008832017-06-15 21:39:33 -0600796
797 Returns:
798 tuple:
Simon Glass549d4222023-09-23 13:43:58 -0600799 str: filename of Kconfig file containing imply_config, or None if
800 none
801 int: line number within the Kconfig file, or 0 if none
802 str: message indicating the result
Simon Glasscb008832017-06-15 21:39:33 -0600803 """
Tom Rini65e05dd2019-09-20 17:42:09 -0400804 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -0600805 if not sym:
806 return 'cannot find sym'
Simon Glassea40b202021-07-21 21:35:53 -0600807 nodes = sym.nodes
808 if len(nodes) != 1:
Simon Glass1bd43062023-09-23 13:43:59 -0600809 return f'{len(nodes)} locations'
Simon Glassa3627082021-12-18 08:09:42 -0700810 node = nodes[0]
811 fname, linenum = node.filename, node.linenr
Simon Glasscb008832017-06-15 21:39:33 -0600812 cwd = os.getcwd()
813 if cwd and fname.startswith(cwd):
814 fname = fname[len(cwd) + 1:]
Simon Glass1bd43062023-09-23 13:43:59 -0600815 file_line = f' at {fname}:{linenum}'
Simon Glass37f815c2021-12-18 14:54:34 -0700816 data = read_file(fname)
Simon Glass1bd43062023-09-23 13:43:59 -0600817 if data[linenum - 1] != f'config {imply_config}':
818 return None, 0, f'bad sym format {data[linenum]}{file_line})'
819 return fname, linenum, f'adding{file_line}'
Simon Glasscb008832017-06-15 21:39:33 -0600820
821def add_imply_rule(config, fname, linenum):
822 """Add a new 'imply' option to a Kconfig
823
824 Args:
Simon Glass549d4222023-09-23 13:43:58 -0600825 config (str): config option to add an imply for (without CONFIG_ prefix)
826 fname (str): Kconfig filename to update
827 linenum (int): Line number to place the 'imply' before
Simon Glasscb008832017-06-15 21:39:33 -0600828
829 Returns:
830 Message indicating the result
831 """
Simon Glass1bd43062023-09-23 13:43:59 -0600832 file_line = f' at {fname}:{linenum}'
Simon Glass37f815c2021-12-18 14:54:34 -0700833 data = read_file(fname)
Simon Glasscb008832017-06-15 21:39:33 -0600834 linenum -= 1
835
836 for offset, line in enumerate(data[linenum:]):
837 if line.strip().startswith('help') or not line:
Simon Glass1bd43062023-09-23 13:43:59 -0600838 data.insert(linenum + offset, f'\timply {config}')
Simon Glass2fd85bd2021-12-18 14:54:33 -0700839 write_file(fname, data)
Simon Glass1bd43062023-09-23 13:43:59 -0600840 return f'added{file_line}'
Simon Glasscb008832017-06-15 21:39:33 -0600841
842 return 'could not insert%s'
843
844(IMPLY_MIN_2, IMPLY_TARGET, IMPLY_CMD, IMPLY_NON_ARCH_BOARD) = (
845 1, 2, 4, 8)
Simon Glass9b2a2e82017-06-15 21:39:32 -0600846
847IMPLY_FLAGS = {
848 'min2': [IMPLY_MIN_2, 'Show options which imply >2 boards (normally >5)'],
849 'target': [IMPLY_TARGET, 'Allow CONFIG_TARGET_... options to imply'],
850 'cmd': [IMPLY_CMD, 'Allow CONFIG_CMD_... to imply'],
Simon Glasscb008832017-06-15 21:39:33 -0600851 'non-arch-board': [
852 IMPLY_NON_ARCH_BOARD,
853 'Allow Kconfig options outside arch/ and /board/ to imply'],
Simon Glass91197aa2021-12-18 14:54:35 -0700854}
Simon Glass9b2a2e82017-06-15 21:39:32 -0600855
Simon Glass9d603392021-12-18 08:09:43 -0700856
857def read_database():
858 """Read in the config database
859
860 Returns:
861 tuple:
862 set of all config options seen (each a str)
863 set of all defconfigs seen (each a str)
864 dict of configs for each defconfig:
865 key: defconfig name, e.g. "MPC8548CDS_legacy_defconfig"
866 value: dict:
867 key: CONFIG option
868 value: Value of option
869 dict of defconfigs for each config:
870 key: CONFIG option
871 value: set of boards using that option
872
873 """
874 configs = {}
875
876 # key is defconfig name, value is dict of (CONFIG_xxx, value)
877 config_db = {}
878
879 # Set of all config options we have seen
880 all_configs = set()
881
882 # Set of all defconfigs we have seen
883 all_defconfigs = set()
884
885 defconfig_db = collections.defaultdict(set)
Simon Glass61d555d2024-07-17 16:56:49 +0100886 defconfig = None
Simon Glass37f815c2021-12-18 14:54:34 -0700887 for line in read_file(CONFIG_DATABASE):
888 line = line.rstrip()
889 if not line: # Separator between defconfigs
890 config_db[defconfig] = configs
891 all_defconfigs.add(defconfig)
892 configs = {}
893 elif line[0] == ' ': # CONFIG line
894 config, value = line.strip().split('=', 1)
895 configs[config] = value
896 defconfig_db[config].add(defconfig)
897 all_configs.add(config)
898 else: # New defconfig
899 defconfig = line
Simon Glass9d603392021-12-18 08:09:43 -0700900
901 return all_configs, all_defconfigs, config_db, defconfig_db
902
903
Simon Glasscb008832017-06-15 21:39:33 -0600904def do_imply_config(config_list, add_imply, imply_flags, skip_added,
905 check_kconfig=True, find_superset=False):
Simon Glass99b66602017-06-01 19:39:03 -0600906 """Find CONFIG options which imply those in the list
907
908 Some CONFIG options can be implied by others and this can help to reduce
909 the size of the defconfig files. For example, CONFIG_X86 implies
910 CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
911 all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
912 each of the x86 defconfig files.
913
Simon Glassea4d6de2023-09-23 13:44:14 -0600914 This function uses the qconfig database to find such options. It
Simon Glass99b66602017-06-01 19:39:03 -0600915 displays a list of things that could possibly imply those in the list.
916 The algorithm ignores any that start with CONFIG_TARGET since these
917 typically refer to only a few defconfigs (often one). It also does not
918 display a config with less than 5 defconfigs.
919
920 The algorithm works using sets. For each target config in config_list:
921 - Get the set 'defconfigs' which use that target config
922 - For each config (from a list of all configs):
923 - Get the set 'imply_defconfig' of defconfigs which use that config
924 -
925 - If imply_defconfigs contains anything not in defconfigs then
926 this config does not imply the target config
927
Simon Glasscc628f52024-07-17 16:57:03 +0100928 Args:
929 config_list (list of str): List of CONFIG options to check
930 add_imply (bool): Automatically add an 'imply' for each config.
931 imply_flags (int): Flags which control which implying configs are allowed
Simon Glass9b2a2e82017-06-15 21:39:32 -0600932 (IMPLY_...)
Simon Glasscc628f52024-07-17 16:57:03 +0100933 skip_added (bool): Don't show options which already have an imply added.
934 check_kconfig (bool): Check if implied symbols already have an 'imply' or
Simon Glasscb008832017-06-15 21:39:33 -0600935 'select' for the target config, and show this information if so.
Simon Glasscc628f52024-07-17 16:57:03 +0100936 find_superset (bool): True to look for configs which are a superset of those
Simon Glass99b66602017-06-01 19:39:03 -0600937 already found. So for example if CONFIG_EXYNOS5 implies an option,
938 but CONFIG_EXYNOS covers a larger set of defconfigs and also
939 implies that option, this will drop the former in favour of the
940 latter. In practice this option has not proved very used.
941
942 Note the terminoloy:
943 config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
944 defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
945 """
Simon Glass0a0c1242024-07-17 16:56:51 +0100946 kconf = scan_kconfig() if check_kconfig else None
Simon Glasscb008832017-06-15 21:39:33 -0600947 if add_imply and add_imply != 'all':
Simon Glassa3627082021-12-18 08:09:42 -0700948 add_imply = add_imply.split(',')
Simon Glasscb008832017-06-15 21:39:33 -0600949
Simon Glass62fae4b2023-09-23 13:44:00 -0600950 all_configs, all_defconfigs, _, defconfig_db = read_database()
Simon Glass99b66602017-06-01 19:39:03 -0600951
Simon Glassa3627082021-12-18 08:09:42 -0700952 # Work through each target config option in turn, independently
Simon Glass99b66602017-06-01 19:39:03 -0600953 for config in config_list:
954 defconfigs = defconfig_db.get(config)
955 if not defconfigs:
Simon Glass1bd43062023-09-23 13:43:59 -0600956 print(f'{config} not found in any defconfig')
Simon Glass99b66602017-06-01 19:39:03 -0600957 continue
958
959 # Get the set of defconfigs without this one (since a config cannot
960 # imply itself)
961 non_defconfigs = all_defconfigs - defconfigs
962 num_defconfigs = len(defconfigs)
Simon Glass1bd43062023-09-23 13:43:59 -0600963 print(f'{config} found in {num_defconfigs}/{len(all_configs)} defconfigs')
Simon Glass99b66602017-06-01 19:39:03 -0600964
965 # This will hold the results: key=config, value=defconfigs containing it
966 imply_configs = {}
967 rest_configs = all_configs - set([config])
968
969 # Look at every possible config, except the target one
970 for imply_config in rest_configs:
Simon Glass9b2a2e82017-06-15 21:39:32 -0600971 if 'ERRATUM' in imply_config:
Simon Glass99b66602017-06-01 19:39:03 -0600972 continue
Simon Glass91197aa2021-12-18 14:54:35 -0700973 if not imply_flags & IMPLY_CMD:
Simon Glass9b2a2e82017-06-15 21:39:32 -0600974 if 'CONFIG_CMD' in imply_config:
975 continue
Simon Glass91197aa2021-12-18 14:54:35 -0700976 if not imply_flags & IMPLY_TARGET:
Simon Glass9b2a2e82017-06-15 21:39:32 -0600977 if 'CONFIG_TARGET' in imply_config:
978 continue
Simon Glass99b66602017-06-01 19:39:03 -0600979
980 # Find set of defconfigs that have this config
981 imply_defconfig = defconfig_db[imply_config]
982
983 # Get the intersection of this with defconfigs containing the
984 # target config
985 common_defconfigs = imply_defconfig & defconfigs
986
987 # Get the set of defconfigs containing this config which DO NOT
988 # also contain the taret config. If this set is non-empty it means
989 # that this config affects other defconfigs as well as (possibly)
990 # the ones affected by the target config. This means it implies
991 # things we don't want to imply.
992 not_common_defconfigs = imply_defconfig & non_defconfigs
993 if not_common_defconfigs:
994 continue
995
996 # If there are common defconfigs, imply_config may be useful
997 if common_defconfigs:
998 skip = False
999 if find_superset:
Simon Glass793dca32019-10-31 07:42:57 -06001000 for prev in list(imply_configs.keys()):
Simon Glass99b66602017-06-01 19:39:03 -06001001 prev_count = len(imply_configs[prev])
1002 count = len(common_defconfigs)
1003 if (prev_count > count and
1004 (imply_configs[prev] & common_defconfigs ==
1005 common_defconfigs)):
1006 # skip imply_config because prev is a superset
1007 skip = True
1008 break
Simon Glassf297ba32023-09-23 13:44:05 -06001009 if count > prev_count:
Simon Glass99b66602017-06-01 19:39:03 -06001010 # delete prev because imply_config is a superset
1011 del imply_configs[prev]
1012 if not skip:
1013 imply_configs[imply_config] = common_defconfigs
1014
1015 # Now we have a dict imply_configs of configs which imply each config
1016 # The value of each dict item is the set of defconfigs containing that
1017 # config. Rank them so that we print the configs that imply the largest
1018 # number of defconfigs first.
Simon Glasscb008832017-06-15 21:39:33 -06001019 ranked_iconfigs = sorted(imply_configs,
Simon Glass99b66602017-06-01 19:39:03 -06001020 key=lambda k: len(imply_configs[k]), reverse=True)
Simon Glasscb008832017-06-15 21:39:33 -06001021 kconfig_info = ''
1022 cwd = os.getcwd()
1023 add_list = collections.defaultdict(list)
1024 for iconfig in ranked_iconfigs:
1025 num_common = len(imply_configs[iconfig])
Simon Glass99b66602017-06-01 19:39:03 -06001026
1027 # Don't bother if there are less than 5 defconfigs affected.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001028 if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
Simon Glass99b66602017-06-01 19:39:03 -06001029 continue
Simon Glasscb008832017-06-15 21:39:33 -06001030 missing = defconfigs - imply_configs[iconfig]
Simon Glass99b66602017-06-01 19:39:03 -06001031 missing_str = ', '.join(missing) if missing else 'all'
1032 missing_str = ''
Simon Glasscb008832017-06-15 21:39:33 -06001033 show = True
1034 if kconf:
1035 sym = find_kconfig_rules(kconf, config[CONFIG_LEN:],
1036 iconfig[CONFIG_LEN:])
1037 kconfig_info = ''
1038 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001039 nodes = sym.nodes
1040 if len(nodes) == 1:
1041 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001042 if cwd and fname.startswith(cwd):
1043 fname = fname[len(cwd) + 1:]
Simon Glass1bd43062023-09-23 13:43:59 -06001044 kconfig_info = f'{fname}:{linenum}'
Simon Glasscb008832017-06-15 21:39:33 -06001045 if skip_added:
1046 show = False
1047 else:
Tom Rini65e05dd2019-09-20 17:42:09 -04001048 sym = kconf.syms.get(iconfig[CONFIG_LEN:])
Simon Glasscb008832017-06-15 21:39:33 -06001049 fname = ''
1050 if sym:
Simon Glassea40b202021-07-21 21:35:53 -06001051 nodes = sym.nodes
1052 if len(nodes) == 1:
1053 fname, linenum = nodes[0].filename, nodes[0].linenr
Simon Glasscb008832017-06-15 21:39:33 -06001054 if cwd and fname.startswith(cwd):
1055 fname = fname[len(cwd) + 1:]
1056 in_arch_board = not sym or (fname.startswith('arch') or
1057 fname.startswith('board'))
1058 if (not in_arch_board and
Simon Glass91197aa2021-12-18 14:54:35 -07001059 not imply_flags & IMPLY_NON_ARCH_BOARD):
Simon Glasscb008832017-06-15 21:39:33 -06001060 continue
1061
1062 if add_imply and (add_imply == 'all' or
1063 iconfig in add_imply):
1064 fname, linenum, kconfig_info = (check_imply_rule(kconf,
Simon Glassf876e962024-07-17 16:56:52 +01001065 iconfig[CONFIG_LEN:]))
Simon Glasscb008832017-06-15 21:39:33 -06001066 if fname:
1067 add_list[fname].append(linenum)
1068
1069 if show and kconfig_info != 'skip':
Simon Glass0e03fb12024-07-17 16:56:53 +01001070 print(f'{num_common:5} : '
1071 f'{iconfig.ljust(30)}{kconfig_info.ljust(25)} {missing_str}')
Simon Glasscb008832017-06-15 21:39:33 -06001072
1073 # Having collected a list of things to add, now we add them. We process
1074 # each file from the largest line number to the smallest so that
1075 # earlier additions do not affect our line numbers. E.g. if we added an
1076 # imply at line 20 it would change the position of each line after
1077 # that.
Simon Glass793dca32019-10-31 07:42:57 -06001078 for fname, linenums in add_list.items():
Simon Glasscb008832017-06-15 21:39:33 -06001079 for linenum in sorted(linenums, reverse=True):
1080 add_imply_rule(config[CONFIG_LEN:], fname, linenum)
Simon Glass99b66602017-06-01 19:39:03 -06001081
Simon Glass7ff80ec2024-07-18 10:11:23 +01001082def defconfig_matches(configs, re_match, re_val):
Simon Glass941671a2022-02-08 11:49:46 -07001083 """Check if any CONFIG option matches a regex
1084
1085 The match must be complete, i.e. from the start to end of the CONFIG option.
1086
1087 Args:
1088 configs (dict): Dict of CONFIG options:
1089 key: CONFIG option
1090 value: Value of option
1091 re_match (re.Pattern): Match to check
Simon Glass7ff80ec2024-07-18 10:11:23 +01001092 re_val (re.Pattern): Regular expression to check against value (or None)
Simon Glass941671a2022-02-08 11:49:46 -07001093
1094 Returns:
1095 bool: True if any CONFIG matches the regex
1096 """
Simon Glass7ff80ec2024-07-18 10:11:23 +01001097 for cfg, val in configs.items():
Simon Glassd9c958f2022-03-05 20:18:54 -07001098 if re_match.fullmatch(cfg):
Simon Glass7ff80ec2024-07-18 10:11:23 +01001099 if not re_val or re_val.fullmatch(val):
1100 return True
Simon Glass941671a2022-02-08 11:49:46 -07001101 return False
Simon Glass99b66602017-06-01 19:39:03 -06001102
Simon Glass00b08552024-07-18 10:11:25 +01001103def do_find_config(config_list, list_format):
Simon Glass65d7fce2021-12-18 08:09:46 -07001104 """Find boards with a given combination of CONFIGs
1105
Simon Glass630a9c92024-07-17 16:57:04 +01001106 Args:
1107 config_list (list of str): List of CONFIG options to check (each a regex
1108 consisting of a config option, with or without a CONFIG_ prefix. If
1109 an option is preceded by a tilde (~) then it must be false,
1110 otherwise it must be true)
Simon Glass00b08552024-07-18 10:11:25 +01001111 list_format (bool): True to write in 'list' format, one board name per
1112 line
Simon Glass630a9c92024-07-17 16:57:04 +01001113
1114 Returns:
1115 int: exit code (0 for success)
Simon Glass65d7fce2021-12-18 08:09:46 -07001116 """
Simon Glass62fae4b2023-09-23 13:44:00 -06001117 _, all_defconfigs, config_db, _ = read_database()
Simon Glass65d7fce2021-12-18 08:09:46 -07001118
Simon Glass65d7fce2021-12-18 08:09:46 -07001119 # Start with all defconfigs
1120 out = all_defconfigs
1121
1122 # Work through each config in turn
Simon Glass65d7fce2021-12-18 08:09:46 -07001123 for item in config_list:
1124 # Get the real config name and whether we want this config or not
1125 cfg = item
1126 want = True
1127 if cfg[0] == '~':
1128 want = False
1129 cfg = cfg[1:]
Simon Glass7ff80ec2024-07-18 10:11:23 +01001130 val = None
1131 re_val = None
1132 if '=' in cfg:
1133 cfg, val = cfg.split('=', maxsplit=1)
1134 re_val = re.compile(val)
Simon Glass65d7fce2021-12-18 08:09:46 -07001135
Simon Glass65d7fce2021-12-18 08:09:46 -07001136 # Search everything that is still in the running. If it has a config
1137 # that we want, or doesn't have one that we don't, add it into the
1138 # running for the next stage
1139 in_list = out
1140 out = set()
Simon Glass941671a2022-02-08 11:49:46 -07001141 re_match = re.compile(cfg)
Simon Glass65d7fce2021-12-18 08:09:46 -07001142 for defc in in_list:
Simon Glass7ff80ec2024-07-18 10:11:23 +01001143 has_cfg = defconfig_matches(config_db[defc], re_match, re_val)
Simon Glass65d7fce2021-12-18 08:09:46 -07001144 if has_cfg == want:
1145 out.add(defc)
Simon Glass00b08552024-07-18 10:11:25 +01001146 if not list_format:
1147 print(f'{len(out)} matches')
1148 sep = '\n' if list_format else ' '
1149 print(sep.join(item.split('_defconfig')[0] for item in sorted(list(out))))
Simon Glass630a9c92024-07-17 16:57:04 +01001150 return 0
Simon Glass65d7fce2021-12-18 08:09:46 -07001151
1152
1153def prefix_config(cfg):
1154 """Prefix a config with CONFIG_ if needed
1155
1156 This handles ~ operator, which indicates that the CONFIG should be disabled
1157
1158 >>> prefix_config('FRED')
1159 'CONFIG_FRED'
1160 >>> prefix_config('CONFIG_FRED')
1161 'CONFIG_FRED'
1162 >>> prefix_config('~FRED')
1163 '~CONFIG_FRED'
1164 >>> prefix_config('~CONFIG_FRED')
1165 '~CONFIG_FRED'
1166 >>> prefix_config('A123')
1167 'CONFIG_A123'
1168 """
Simon Glassa4c9d172023-09-23 13:44:01 -06001169 oper = ''
Simon Glass65d7fce2021-12-18 08:09:46 -07001170 if cfg[0] == '~':
Simon Glassa4c9d172023-09-23 13:44:01 -06001171 oper = cfg[0]
Simon Glass65d7fce2021-12-18 08:09:46 -07001172 cfg = cfg[1:]
1173 if not cfg.startswith('CONFIG_'):
1174 cfg = 'CONFIG_' + cfg
Simon Glassa4c9d172023-09-23 13:44:01 -06001175 return oper + cfg
Simon Glass65d7fce2021-12-18 08:09:46 -07001176
1177
Simon Glass0220a682024-09-29 19:49:56 -06001178RE_MK_CONFIGS = re.compile(r'CONFIG_(\$\(XPL_\)|\$\(PHASE_\))?([A-Za-z0-9_]*)')
Simon Glass98275712023-09-23 13:43:57 -06001179RE_IFDEF = re.compile(r'(ifdef|ifndef)')
1180RE_C_CONFIGS = re.compile(r'CONFIG_([A-Za-z0-9_]*)')
1181RE_CONFIG_IS = re.compile(r'CONFIG_IS_ENABLED\(([A-Za-z0-9_]*)\)')
Simon Glass65e62032023-02-01 13:19:12 -07001182
1183class ConfigUse:
Simon Glassf876e962024-07-17 16:56:52 +01001184 """Tracks whether a config relates to SPL or not"""
Simon Glass65e62032023-02-01 13:19:12 -07001185 def __init__(self, cfg, is_spl, fname, rest):
Simon Glassf876e962024-07-17 16:56:52 +01001186 """Set up a new ConfigUse
1187
1188 Args:
Simon Glass0220a682024-09-29 19:49:56 -06001189 cfg (str): CONFIG option, without any CONFIG_ or xPL_ prefix
Simon Glassf876e962024-07-17 16:56:52 +01001190 is_spl (bool): True if this option relates to SPL
1191 fname (str): Makefile filename where the CONFIG option was found
1192 rest (str): Line of the Makefile
1193 """
Simon Glass65e62032023-02-01 13:19:12 -07001194 self.cfg = cfg
1195 self.is_spl = is_spl
1196 self.fname = fname
1197 self.rest = rest
1198
1199 def __hash__(self):
1200 return hash((self.cfg, self.is_spl))
1201
1202def scan_makefiles(fnames):
1203 """Scan Makefiles looking for Kconfig options
1204
1205 Looks for uses of CONFIG options in Makefiles
1206
1207 Args:
1208 fnames (list of tuple):
1209 str: Makefile filename where the option was found
1210 str: Line of the Makefile
1211
1212 Returns:
1213 tuple:
1214 dict: all_uses
1215 key (ConfigUse): object
1216 value (list of str): matching lines
1217 dict: Uses by filename
1218 key (str): filename
1219 value (set of ConfigUse): uses in that filename
1220
1221 >>> RE_MK_CONFIGS.search('CONFIG_FRED').groups()
1222 (None, 'FRED')
Simon Glassc46760d2024-09-29 19:49:53 -06001223 >>> RE_MK_CONFIGS.search('CONFIG_$(XPL_)MARY').groups()
1224 ('$(XPL_)', 'MARY')
Simon Glass5c10c8b2024-09-29 19:49:54 -06001225 >>> RE_MK_CONFIGS.search('CONFIG_$(PHASE_)MARY').groups()
1226 ('$(PHASE_)', 'MARY')
Simon Glass65e62032023-02-01 13:19:12 -07001227 """
1228 all_uses = collections.defaultdict(list)
1229 fname_uses = {}
1230 for fname, rest in fnames:
1231 m_iter = RE_MK_CONFIGS.finditer(rest)
Simon Glassa4c9d172023-09-23 13:44:01 -06001232 for mat in m_iter:
1233 real_opt = mat.group(2)
Simon Glass65e62032023-02-01 13:19:12 -07001234 if real_opt == '':
1235 continue
1236 is_spl = False
Simon Glassa4c9d172023-09-23 13:44:01 -06001237 if mat.group(1):
Simon Glass65e62032023-02-01 13:19:12 -07001238 is_spl = True
1239 use = ConfigUse(real_opt, is_spl, fname, rest)
1240 if fname not in fname_uses:
1241 fname_uses[fname] = set()
1242 fname_uses[fname].add(use)
1243 all_uses[use].append(rest)
1244 return all_uses, fname_uses
1245
1246
1247def scan_src_files(fnames):
1248 """Scan source files (other than Makefiles) looking for Kconfig options
1249
1250 Looks for uses of CONFIG options
1251
1252 Args:
1253 fnames (list of tuple):
1254 str: Makefile filename where the option was found
1255 str: Line of the Makefile
1256
1257 Returns:
1258 tuple:
1259 dict: all_uses
1260 key (ConfigUse): object
1261 value (list of str): matching lines
1262 dict: Uses by filename
1263 key (str): filename
1264 value (set of ConfigUse): uses in that filename
1265
1266 >>> RE_C_CONFIGS.search('CONFIG_FRED').groups()
1267 ('FRED',)
1268 >>> RE_CONFIG_IS.search('CONFIG_IS_ENABLED(MARY)').groups()
1269 ('MARY',)
1270 >>> RE_CONFIG_IS.search('#if CONFIG_IS_ENABLED(OF_PLATDATA)').groups()
1271 ('OF_PLATDATA',)
1272 """
Simon Glass62fae4b2023-09-23 13:44:00 -06001273 fname = None
1274 rest = None
1275
Simon Glass65e62032023-02-01 13:19:12 -07001276 def add_uses(m_iter, is_spl):
Simon Glassa4c9d172023-09-23 13:44:01 -06001277 for mat in m_iter:
1278 real_opt = mat.group(1)
Simon Glass65e62032023-02-01 13:19:12 -07001279 if real_opt == '':
1280 continue
1281 use = ConfigUse(real_opt, is_spl, fname, rest)
1282 if fname not in fname_uses:
1283 fname_uses[fname] = set()
1284 fname_uses[fname].add(use)
1285 all_uses[use].append(rest)
1286
1287 all_uses = collections.defaultdict(list)
1288 fname_uses = {}
1289 for fname, rest in fnames:
1290 m_iter = RE_C_CONFIGS.finditer(rest)
1291 add_uses(m_iter, False)
1292
1293 m_iter2 = RE_CONFIG_IS.finditer(rest)
1294 add_uses(m_iter2, True)
1295
1296 return all_uses, fname_uses
1297
1298
1299MODE_NORMAL, MODE_SPL, MODE_PROPER = range(3)
1300
1301def do_scan_source(path, do_update):
1302 """Scan the source tree for Kconfig inconsistencies
1303
1304 Args:
1305 path (str): Path to source tree
1306 do_update (bool) : True to write to scripts/kconf_... files
1307 """
1308 def is_not_proper(name):
1309 for prefix in SPL_PREFIXES:
1310 if name.startswith(prefix):
1311 return name[len(prefix):]
1312 return False
1313
1314 def check_not_found(all_uses, spl_mode):
1315 """Check for Kconfig options mentioned in the source but not in Kconfig
1316
1317 Args:
1318 all_uses (dict):
1319 key (ConfigUse): object
1320 value (list of str): matching lines
1321 spl_mode (int): If MODE_SPL, look at source code which implies
Simon Glass0220a682024-09-29 19:49:56 -06001322 an xPL_ option, but for which there is none;
Simon Glass65e62032023-02-01 13:19:12 -07001323 for MOD_PROPER, look at source code which implies a Proper
Simon Glassc46760d2024-09-29 19:49:53 -06001324 option (i.e. use of CONFIG_IS_ENABLED() or $(XPL_) or
Simon Glass5c10c8b2024-09-29 19:49:54 -06001325 $(PHASE_) but for which there none;
Simon Glass65e62032023-02-01 13:19:12 -07001326 if MODE_NORMAL, ignore SPL
1327
1328 Returns:
1329 dict:
1330 key (str): CONFIG name (without 'CONFIG_' prefix
1331 value (list of ConfigUse): List of uses of this CONFIG
1332 """
1333 # Make sure we know about all the options
1334 not_found = collections.defaultdict(list)
Simon Glass62fae4b2023-09-23 13:44:00 -06001335 for use, _ in all_uses.items():
Simon Glass65e62032023-02-01 13:19:12 -07001336 name = use.cfg
1337 if name in IGNORE_SYMS:
1338 continue
1339 check = True
1340
1341 if spl_mode == MODE_SPL:
1342 check = use.is_spl
1343
Simon Glass0220a682024-09-29 19:49:56 -06001344 # If it is an SPL symbol, try prepending all xPL_ prefixes to
Simon Glass65e62032023-02-01 13:19:12 -07001345 # find at least one SPL symbol
1346 if use.is_spl:
Simon Glass65e62032023-02-01 13:19:12 -07001347 for prefix in SPL_PREFIXES:
1348 try_name = prefix + name
1349 sym = kconf.syms.get(try_name)
1350 if sym:
1351 break
1352 if not sym:
1353 not_found[f'SPL_{name}'].append(use)
1354 continue
1355 elif spl_mode == MODE_PROPER:
1356 # Try to find the Proper version of this symbol, i.e. without
Simon Glass0220a682024-09-29 19:49:56 -06001357 # the xPL_ prefix
Simon Glass65e62032023-02-01 13:19:12 -07001358 proper_name = is_not_proper(name)
1359 if proper_name:
1360 name = proper_name
1361 elif not use.is_spl:
1362 check = False
1363 else: # MODE_NORMAL
Simon Glass65e62032023-02-01 13:19:12 -07001364 sym = kconf.syms.get(name)
1365 if not sym:
1366 proper_name = is_not_proper(name)
1367 if proper_name:
1368 name = proper_name
1369 sym = kconf.syms.get(name)
1370 if not sym:
1371 for prefix in SPL_PREFIXES:
1372 try_name = prefix + name
1373 sym = kconf.syms.get(try_name)
1374 if sym:
1375 break
1376 if not sym:
1377 not_found[name].append(use)
1378 continue
1379
1380 sym = kconf.syms.get(name)
1381 if not sym and check:
1382 not_found[name].append(use)
1383 return not_found
1384
1385 def show_uses(uses):
1386 """Show a list of uses along with their filename and code snippet
1387
1388 Args:
1389 uses (dict):
1390 key (str): CONFIG name (without 'CONFIG_' prefix
1391 value (list of ConfigUse): List of uses of this CONFIG
1392 """
1393 for name in sorted(uses):
1394 print(f'{name}: ', end='')
1395 for i, use in enumerate(uses[name]):
1396 print(f'{" " if i else ""}{use.fname}: {use.rest.strip()}')
1397
1398
1399 print('Scanning Kconfig')
Simon Glass0a0c1242024-07-17 16:56:51 +01001400 kconf = scan_kconfig()
Simon Glass65e62032023-02-01 13:19:12 -07001401 print(f'Scanning source in {path}')
1402 args = ['git', 'grep', '-E', r'IS_ENABLED|\bCONFIG']
1403 with subprocess.Popen(args, stdout=subprocess.PIPE) as proc:
Simon Glass62fae4b2023-09-23 13:44:00 -06001404 out, _ = proc.communicate()
Simon Glass65e62032023-02-01 13:19:12 -07001405 lines = out.splitlines()
1406 re_fname = re.compile('^([^:]*):(.*)')
1407 src_list = []
1408 mk_list = []
1409 for line in lines:
1410 linestr = line.decode('utf-8')
1411 m_fname = re_fname.search(linestr)
1412 if not m_fname:
1413 continue
1414 fname, rest = m_fname.groups()
1415 dirname, leaf = os.path.split(fname)
1416 root, ext = os.path.splitext(leaf)
1417 if ext == '.autoconf':
1418 pass
1419 elif ext in ['.c', '.h', '.S', '.lds', '.dts', '.dtsi', '.asl', '.cfg',
1420 '.env', '.tmpl']:
1421 src_list.append([fname, rest])
1422 elif 'Makefile' in root or ext == '.mk':
1423 mk_list.append([fname, rest])
1424 elif ext in ['.yml', '.sh', '.py', '.awk', '.pl', '.rst', '', '.sed']:
1425 pass
1426 elif 'Kconfig' in root or 'Kbuild' in root:
1427 pass
1428 elif 'README' in root:
1429 pass
1430 elif dirname in ['configs']:
1431 pass
1432 elif dirname.startswith('doc') or dirname.startswith('scripts/kconfig'):
1433 pass
1434 else:
1435 print(f'Not sure how to handle file {fname}')
1436
1437 # Scan the Makefiles
Simon Glass62fae4b2023-09-23 13:44:00 -06001438 all_uses, _ = scan_makefiles(mk_list)
Simon Glass65e62032023-02-01 13:19:12 -07001439
1440 spl_not_found = set()
1441 proper_not_found = set()
1442
1443 # Make sure we know about all the options
1444 print('\nCONFIG options present in Makefiles but not Kconfig:')
1445 not_found = check_not_found(all_uses, MODE_NORMAL)
1446 show_uses(not_found)
1447
1448 print('\nCONFIG options present in Makefiles but not Kconfig (SPL):')
1449 not_found = check_not_found(all_uses, MODE_SPL)
1450 show_uses(not_found)
Simon Glassb774ba52023-09-23 13:44:03 -06001451 spl_not_found |= {is_not_proper(key) or key for key in not_found.keys()}
Simon Glass65e62032023-02-01 13:19:12 -07001452
Simon Glass0220a682024-09-29 19:49:56 -06001453 print('\nCONFIG options used as Proper in Makefiles but without a non-xPL_ variant:')
Simon Glass65e62032023-02-01 13:19:12 -07001454 not_found = check_not_found(all_uses, MODE_PROPER)
1455 show_uses(not_found)
Simon Glassb774ba52023-09-23 13:44:03 -06001456 proper_not_found |= {not_found.keys()}
Simon Glass65e62032023-02-01 13:19:12 -07001457
1458 # Scan the source code
Simon Glass62fae4b2023-09-23 13:44:00 -06001459 all_uses, _ = scan_src_files(src_list)
Simon Glass65e62032023-02-01 13:19:12 -07001460
1461 # Make sure we know about all the options
1462 print('\nCONFIG options present in source but not Kconfig:')
1463 not_found = check_not_found(all_uses, MODE_NORMAL)
1464 show_uses(not_found)
1465
1466 print('\nCONFIG options present in source but not Kconfig (SPL):')
1467 not_found = check_not_found(all_uses, MODE_SPL)
1468 show_uses(not_found)
Simon Glassb774ba52023-09-23 13:44:03 -06001469 spl_not_found |= {is_not_proper(key) or key for key in not_found.keys()}
Simon Glass65e62032023-02-01 13:19:12 -07001470
Simon Glass0220a682024-09-29 19:49:56 -06001471 print('\nCONFIG options used as Proper in source but without a non-xPL_ variant:')
Simon Glass65e62032023-02-01 13:19:12 -07001472 not_found = check_not_found(all_uses, MODE_PROPER)
1473 show_uses(not_found)
Simon Glassb774ba52023-09-23 13:44:03 -06001474 proper_not_found |= {not_found.keys()}
Simon Glass65e62032023-02-01 13:19:12 -07001475
Simon Glass0220a682024-09-29 19:49:56 -06001476 print('\nCONFIG options used as SPL but without an xPL_ variant:')
Simon Glass65e62032023-02-01 13:19:12 -07001477 for item in sorted(spl_not_found):
1478 print(f' {item}')
1479
Simon Glass0220a682024-09-29 19:49:56 -06001480 print('\nCONFIG options used as Proper but without a non-xPL_ variant:')
Simon Glass65e62032023-02-01 13:19:12 -07001481 for item in sorted(proper_not_found):
1482 print(f' {item}')
1483
1484 # Write out the updated information
1485 if do_update:
Simon Glasse6c686f2023-09-23 13:44:04 -06001486 with open(os.path.join(path, 'scripts', 'conf_nospl'), 'w',
1487 encoding='utf-8') as out:
Simon Glass65e62032023-02-01 13:19:12 -07001488 print('# These options should not be enabled in SPL builds\n',
1489 file=out)
1490 for item in sorted(spl_not_found):
1491 print(item, file=out)
Simon Glasse6c686f2023-09-23 13:44:04 -06001492 with open(os.path.join(path, 'scripts', 'conf_noproper'), 'w',
1493 encoding='utf-8') as out:
Simon Glass65e62032023-02-01 13:19:12 -07001494 print('# These options should not be enabled in Proper builds\n',
1495 file=out)
1496 for item in sorted(proper_not_found):
1497 print(item, file=out)
Simon Glassd63357e2024-07-17 16:57:02 +01001498 return 0
Simon Glass65e62032023-02-01 13:19:12 -07001499
1500
Simon Glassa056c422024-07-17 16:56:55 +01001501def parse_args():
1502 """Parse the program arguments
1503
1504 Returns:
1505 tuple:
1506 argparse.ArgumentParser: parser
1507 argparse.Namespace: Parsed arguments
1508 """
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001509 try:
1510 cpu_count = multiprocessing.cpu_count()
1511 except NotImplementedError:
1512 cpu_count = 1
1513
Simon Glassb2e83c62021-12-18 14:54:31 -07001514 epilog = '''Move config options from headers to defconfig files. See
1515doc/develop/moveconfig.rst for documentation.'''
1516
1517 parser = ArgumentParser(epilog=epilog)
1518 # Add arguments here
1519 parser.add_argument('-a', '--add-imply', type=str, default='',
Simon Glasscb008832017-06-15 21:39:33 -06001520 help='comma-separated list of CONFIG options to add '
1521 "an 'imply' statement to for the CONFIG in -i")
Simon Glassb2e83c62021-12-18 14:54:31 -07001522 parser.add_argument('-A', '--skip-added', action='store_true', default=False,
Simon Glasscb008832017-06-15 21:39:33 -06001523 help="don't show options which are already marked as "
1524 'implying others')
Simon Glassb2e83c62021-12-18 14:54:31 -07001525 parser.add_argument('-b', '--build-db', action='store_true', default=False,
Simon Glassd73fcb12017-06-01 19:39:02 -06001526 help='build a CONFIG database')
Simon Glassb2e83c62021-12-18 14:54:31 -07001527 parser.add_argument('-C', '--commit', action='store_true', default=False,
Simon Glass9ede2122016-09-12 23:18:21 -06001528 help='Create a git commit for the operation')
Simon Glass15f19ab2023-09-23 13:44:09 -06001529 parser.add_argument('--nocolour', action='store_true', default=False,
1530 help="don't display the log in colour")
Simon Glassb2e83c62021-12-18 14:54:31 -07001531 parser.add_argument('-d', '--defconfigs', type=str,
Simon Glassee4e61b2017-06-01 19:38:59 -06001532 help='a file containing a list of defconfigs to move, '
1533 "one per line (for example 'snow_defconfig') "
1534 "or '-' to read from stdin")
Simon Glassb2e83c62021-12-18 14:54:31 -07001535 parser.add_argument('-e', '--exit-on-error', action='store_true',
Simon Glasse1ae5632021-12-18 08:09:44 -07001536 default=False,
1537 help='exit immediately on any error')
Simon Glassb2e83c62021-12-18 14:54:31 -07001538 parser.add_argument('-f', '--find', action='store_true', default=False,
Simon Glass65d7fce2021-12-18 08:09:46 -07001539 help='Find boards with a given config combination')
Simon Glassb2e83c62021-12-18 14:54:31 -07001540 parser.add_argument('-i', '--imply', action='store_true', default=False,
Simon Glass99b66602017-06-01 19:39:03 -06001541 help='find options which imply others')
Simon Glass00b08552024-07-18 10:11:25 +01001542 parser.add_argument('-l', '--list', action='store_true', default=False,
1543 help='Show a sorted list of board names, one per line')
Simon Glassb2e83c62021-12-18 14:54:31 -07001544 parser.add_argument('-I', '--imply-flags', type=str, default='',
Simon Glass9b2a2e82017-06-15 21:39:32 -06001545 help="control the -i option ('help' for help")
Simon Glassb2e83c62021-12-18 14:54:31 -07001546 parser.add_argument('-j', '--jobs', type=int, default=cpu_count,
Simon Glasse1ae5632021-12-18 08:09:44 -07001547 help='the number of jobs to run simultaneously')
Simon Glassb2e83c62021-12-18 14:54:31 -07001548 parser.add_argument('-n', '--dry-run', action='store_true', default=False,
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001549 help='perform a trial run (show log with no changes)')
Simon Glassb2e83c62021-12-18 14:54:31 -07001550 parser.add_argument('-r', '--git-ref', type=str,
Simon Glasse1ae5632021-12-18 08:09:44 -07001551 help='the git ref to clone for building the autoconf.mk')
Simon Glassb2e83c62021-12-18 14:54:31 -07001552 parser.add_argument('-s', '--force-sync', action='store_true', default=False,
Masahiro Yamada8513dc02016-05-19 15:52:08 +09001553 help='force sync by savedefconfig')
Simon Glassb2e83c62021-12-18 14:54:31 -07001554 parser.add_argument('-S', '--spl', action='store_true', default=False,
Masahiro Yamada07913d12016-08-22 22:18:22 +09001555 help='parse config options defined for SPL build')
Simon Glass65e62032023-02-01 13:19:12 -07001556 parser.add_argument('--scan-source', action='store_true', default=False,
1557 help='scan source for uses of CONFIG options')
Simon Glassb2e83c62021-12-18 14:54:31 -07001558 parser.add_argument('-t', '--test', action='store_true', default=False,
Simon Glasse1ae5632021-12-18 08:09:44 -07001559 help='run unit tests')
Simon Glassb2e83c62021-12-18 14:54:31 -07001560 parser.add_argument('-y', '--yes', action='store_true', default=False,
Simon Glass6b403df2016-09-12 23:18:20 -06001561 help="respond 'yes' to any prompts")
Simon Glass65e62032023-02-01 13:19:12 -07001562 parser.add_argument('-u', '--update', action='store_true', default=False,
1563 help="update scripts/ files (use with --scan-source)")
Simon Glassb2e83c62021-12-18 14:54:31 -07001564 parser.add_argument('-v', '--verbose', action='store_true', default=False,
Joe Hershberger95bf9c72015-05-19 13:21:24 -05001565 help='show any build errors as boards are built')
Simon Glassb2e83c62021-12-18 14:54:31 -07001566 parser.add_argument('configs', nargs='*')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001567
Simon Glassc57d4062024-07-17 16:57:11 +01001568 args = parser.parse_args()
1569 if not any((args.force_sync, args.build_db, args.imply, args.find,
1570 args.scan_source, args.test)):
1571 parser.print_usage()
1572 sys.exit(1)
1573
1574 return parser, args
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001575
Simon Glassa056c422024-07-17 16:56:55 +01001576
Simon Glasscc628f52024-07-17 16:57:03 +01001577def imply(args):
1578 """Handle checking for flags which imply others
1579
1580 Args:
1581 args (argparse.Namespace): Program arguments
1582
1583 Returns:
1584 int: exit code (0 for success)
1585 """
1586 imply_flags = 0
1587 if args.imply_flags == 'all':
1588 imply_flags = -1
1589
1590 elif args.imply_flags:
1591 for flag in args.imply_flags.split(','):
1592 bad = flag not in IMPLY_FLAGS
1593 if bad:
1594 print(f"Invalid flag '{flag}'")
1595 if flag == 'help' or bad:
1596 print("Imply flags: (separate with ',')")
1597 for name, info in IMPLY_FLAGS.items():
Simon Glass4597acb2024-08-21 17:52:24 -06001598 print(f' {name.ljust(15)}: {info[1]}')
Simon Glasscc628f52024-07-17 16:57:03 +01001599 return 1
1600 imply_flags |= IMPLY_FLAGS[flag][0]
1601
1602 do_imply_config(args.configs, args.add_imply, imply_flags, args.skip_added)
1603 return 0
1604
1605
Simon Glass6c2a4382024-07-17 16:57:06 +01001606def add_commit(configs):
1607 """Add a commit indicating which CONFIG options were converted
1608
1609 Args:
1610 configs (list of str) List of CONFIG_... options to process
1611 """
1612 subprocess.call(['git', 'add', '-u'])
1613 if configs:
1614 part = 'et al ' if len(configs) > 1 else ''
1615 msg = f'Convert {configs[0]} {part}to Kconfig'
1616 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1617 '\n '.join(configs))
1618 else:
1619 msg = 'configs: Resync with savedefconfig'
1620 msg += '\n\nRsync all defconfig files using moveconfig.py'
1621 subprocess.call(['git', 'commit', '-s', '-m', msg])
1622
1623
Simon Glass948d0b42024-07-17 16:57:09 +01001624def write_db(config_db, progress):
Simon Glassab1bfd42024-07-17 16:57:08 +01001625 """Write the database to a file
1626
1627 Args:
1628 config_db (dict of dict): configs for each defconfig
1629 key: defconfig name, e.g. "MPC8548CDS_legacy_defconfig"
1630 value: dict:
1631 key: CONFIG option
1632 value: Value of option
Simon Glassab1bfd42024-07-17 16:57:08 +01001633 progress (Progress): Progress indicator.
1634
1635 Returns:
1636 int: exit code (0 for success)
1637 """
Simon Glass948d0b42024-07-17 16:57:09 +01001638 col = progress.col
Simon Glassab1bfd42024-07-17 16:57:08 +01001639 with open(CONFIG_DATABASE, 'w', encoding='utf-8') as outf:
1640 for defconfig, configs in config_db.items():
1641 outf.write(f'{defconfig}\n')
1642 for config in sorted(configs.keys()):
1643 outf.write(f' {config}={configs[config]}\n')
1644 outf.write('\n')
1645 print(col.build(
1646 col.RED if progress.failed else col.GREEN,
1647 f'{progress.failure_msg}{len(config_db)} boards written to {CONFIG_DATABASE}'))
1648 return 0
1649
1650
Simon Glass948d0b42024-07-17 16:57:09 +01001651def move_done(progress):
Simon Glassab1bfd42024-07-17 16:57:08 +01001652 """Write a message indicating that the move is done
1653
1654 Args:
Simon Glassab1bfd42024-07-17 16:57:08 +01001655 progress (Progress): Progress indicator.
1656
1657 Returns:
1658 int: exit code (0 for success)
1659 """
Simon Glass948d0b42024-07-17 16:57:09 +01001660 col = progress.col
Simon Glassab1bfd42024-07-17 16:57:08 +01001661 if progress.failed:
1662 print(col.build(col.RED, f'{progress.failure_msg}see {FAILED_LIST}', True))
1663 else:
1664 # Add enough spaces to overwrite the progress indicator
1665 print(col.build(
1666 col.GREEN, f'{progress.total} processed ', bright=True))
1667 return 0
1668
Simon Glass7e688042024-07-17 16:57:01 +01001669def do_tests():
1670 """Run doctests and unit tests (so far there are no unit tests)"""
1671 sys.argv = [sys.argv[0]]
1672 fail, _ = doctest.testmod()
1673 if fail:
1674 return 1
1675 unittest.main()
1676 return 0
1677
1678
Simon Glassa056c422024-07-17 16:56:55 +01001679def main():
1680 """Main program"""
1681 parser, args = parse_args()
Simon Glassc50b6f12024-07-17 16:56:59 +01001682 check_top_directory()
1683
Simon Glassfd35fbe2024-07-17 16:57:00 +01001684 # prefix the option name with CONFIG_ if missing
1685 args.configs = [prefix_config(cfg) for cfg in args.configs]
1686
Simon Glassb2e83c62021-12-18 14:54:31 -07001687 if args.test:
Simon Glass7e688042024-07-17 16:57:01 +01001688 return do_tests()
Simon Glass65e62032023-02-01 13:19:12 -07001689 if args.scan_source:
Simon Glassd63357e2024-07-17 16:57:02 +01001690 return do_scan_source(os.getcwd(), args.update)
Simon Glassb2e83c62021-12-18 14:54:31 -07001691 if args.imply:
Simon Glasscc628f52024-07-17 16:57:03 +01001692 if imply(args):
1693 parser.print_usage()
1694 sys.exit(1)
Simon Glassf297ba32023-09-23 13:44:05 -06001695 return 0
Simon Glassb2e83c62021-12-18 14:54:31 -07001696 if args.find:
Simon Glass00b08552024-07-18 10:11:25 +01001697 return do_find_config(args.configs, args.list)
Simon Glass65d7fce2021-12-18 08:09:46 -07001698
Simon Glass948d0b42024-07-17 16:57:09 +01001699 config_db, progress = move_config(args)
Joe Hershberger2144f882015-05-19 13:21:20 -05001700
Simon Glassb2e83c62021-12-18 14:54:31 -07001701 if args.commit:
Simon Glass6c2a4382024-07-17 16:57:06 +01001702 add_commit(args.configs)
Simon Glass9ede2122016-09-12 23:18:21 -06001703
Simon Glassb2e83c62021-12-18 14:54:31 -07001704 if args.build_db:
Simon Glass948d0b42024-07-17 16:57:09 +01001705 return write_db(config_db, progress)
1706 return move_done(progress)
Simon Glassf297ba32023-09-23 13:44:05 -06001707
Simon Glassd73fcb12017-06-01 19:39:02 -06001708
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001709if __name__ == '__main__':
Simon Glass65d7fce2021-12-18 08:09:46 -07001710 sys.exit(main())