]>
git.ipfire.org Git - thirdparty/u-boot.git/blob - tools/qconfig.py
2 # SPDX-License-Identifier: GPL-2.0+
4 # Author: Masahiro Yamada <yamada.masahiro@socionext.com>
8 Build and query a Kconfig database for boards.
10 See doc/develop/moveconfig.rst for documentation.
13 from argparse
import ArgumentParser
15 from contextlib
import ExitStack
20 import multiprocessing
33 from buildman
import bsettings
34 from buildman
import kconfiglib
35 from buildman
import toolchain
36 from u_boot_pylib
import terminal
38 SHOW_GNU_MAKE
= 'scripts/show-gnu-make'
44 STATE_SAVEDEFCONFIG
= 3
46 AUTO_CONF_PATH
= 'include/config/auto.conf'
47 CONFIG_DATABASE
= 'qconfig.db'
48 FAILED_LIST
= 'qconfig.failed'
50 CONFIG_LEN
= len('CONFIG_')
53 'SZ_1': 0x00000001, 'SZ_2': 0x00000002,
54 'SZ_4': 0x00000004, 'SZ_8': 0x00000008,
55 'SZ_16': 0x00000010, 'SZ_32': 0x00000020,
56 'SZ_64': 0x00000040, 'SZ_128': 0x00000080,
57 'SZ_256': 0x00000100, 'SZ_512': 0x00000200,
58 'SZ_1K': 0x00000400, 'SZ_2K': 0x00000800,
59 'SZ_4K': 0x00001000, 'SZ_8K': 0x00002000,
60 'SZ_16K': 0x00004000, 'SZ_32K': 0x00008000,
61 'SZ_64K': 0x00010000, 'SZ_128K': 0x00020000,
62 'SZ_256K': 0x00040000, 'SZ_512K': 0x00080000,
63 'SZ_1M': 0x00100000, 'SZ_2M': 0x00200000,
64 'SZ_4M': 0x00400000, 'SZ_8M': 0x00800000,
65 'SZ_16M': 0x01000000, 'SZ_32M': 0x02000000,
66 'SZ_64M': 0x04000000, 'SZ_128M': 0x08000000,
67 'SZ_256M': 0x10000000, 'SZ_512M': 0x20000000,
68 'SZ_1G': 0x40000000, 'SZ_2G': 0x80000000,
72 RE_REMOVE_DEFCONFIG
= re
.compile(r
'(.*)_defconfig')
74 # CONFIG symbols present in the build system (from Linux) but not actually used
75 # in U-Boot; KCONFIG symbols
76 IGNORE_SYMS
= ['DEBUG_SECTION_MISMATCH', 'FTRACE_MCOUNT_RECORD', 'GCOV_KERNEL',
77 'GCOV_PROFILE_ALL', 'KALLSYMS', 'KASAN', 'MODVERSIONS', 'SHELL',
78 'TPL_BUILD', 'VPL_BUILD', 'IS_ENABLED', 'FOO', 'IF_ENABLED_INT',
79 'IS_ENABLED_', 'IS_ENABLED_1', 'IS_ENABLED_2', 'IS_ENABLED_3',
80 'SPL_', 'TPL_', 'SPL_FOO', 'TPL_FOO', 'TOOLS_FOO',
81 'ACME', 'SPL_ACME', 'TPL_ACME', 'TRACE_BRANCH_PROFILING',
82 'VAL', '_UNDEFINED', 'SPL_BUILD', ]
84 SPL_PREFIXES
= ['SPL_', 'TPL_', 'VPL_', 'TOOLS_']
86 ### helper functions ###
87 def check_top_directory():
88 """Exit if we are not at the top of source directory."""
89 for fname
in 'README', 'Licenses':
90 if not os
.path
.exists(fname
):
91 sys
.exit('Please run at the top of source directory.')
93 def check_clean_directory():
94 """Exit if the source tree is not clean."""
95 for fname
in '.config', 'include/config':
96 if os
.path
.exists(fname
):
97 sys
.exit("source tree is not clean, please run 'make mrproper'")
100 """Get the command name of GNU Make.
102 U-Boot needs GNU Make for building, but the command name is not
103 necessarily "make". (for example, "gmake" on FreeBSD).
104 Returns the most appropriate command name on your system.
106 with subprocess
.Popen([SHOW_GNU_MAKE
], stdout
=subprocess
.PIPE
) as proc
:
107 ret
= proc
.communicate()
109 sys
.exit('GNU Make not found')
110 return ret
[0].rstrip()
112 def get_matched_defconfig(line
):
113 """Get the defconfig files that match a pattern
116 line (str): Path or filename to match, e.g. 'configs/snow_defconfig' or
117 'k2*_defconfig'. If no directory is provided, 'configs/' is
121 list of str: a list of matching defconfig files
123 dirname
= os
.path
.dirname(line
)
127 pattern
= os
.path
.join('configs', line
)
128 return glob
.glob(pattern
) + glob
.glob(pattern
+ '_defconfig')
130 def get_matched_defconfigs(defconfigs_file
):
131 """Get all the defconfig files that match the patterns in a file.
134 defconfigs_file (str): File containing a list of defconfigs to process,
135 or '-' to read the list from stdin
138 list of str: A list of paths to defconfig files, with no duplicates
141 with
ExitStack() as stack
:
142 if defconfigs_file
== '-':
144 defconfigs_file
= 'stdin'
146 inf
= stack
.enter_context(open(defconfigs_file
, encoding
='utf-8'))
147 for i
, line
in enumerate(inf
):
150 continue # skip blank lines silently
152 line
= line
.split(' ')[0] # handle 'git log' input
153 matched
= get_matched_defconfig(line
)
155 print(f
"warning: {defconfigs_file}:{i + 1}: no defconfig matched '{line}'",
158 defconfigs
+= matched
160 # use set() to drop multiple matching
161 return [defconfig
[len('configs') + 1:] for defconfig
in set(defconfigs
)]
163 def get_all_defconfigs():
164 """Get all the defconfig files under the configs/ directory.
167 list of str: List of paths to defconfig files
170 for (dirpath
, _
, filenames
) in os
.walk('configs'):
171 dirpath
= dirpath
[len('configs') + 1:]
172 for filename
in fnmatch
.filter(filenames
, '*_defconfig'):
173 defconfigs
.append(os
.path
.join(dirpath
, filename
))
177 def write_file(fname
, data
):
178 """Write data to a file
181 fname (str): Filename to write to
182 data (list of str): Lines to write (with or without trailing newline);
185 with
open(fname
, 'w', encoding
='utf-8') as out
:
186 if isinstance(data
, list):
188 print(line
.rstrip('\n'), file=out
)
192 def read_file(fname
, as_lines
=True, skip_unicode
=False):
193 """Read a file and return the contents
196 fname (str): Filename to read from
197 as_lines (bool): Return file contents as a list of lines
198 skip_unicode (bool): True to report unicode errors and continue
201 iter of str: List of ;ines from the file with newline removed; str if
202 as_lines is False with newlines intact; or None if a unicode error
206 UnicodeDecodeError: Unicode error occurred when reading
208 with
open(fname
, encoding
='utf-8') as inf
:
211 return [line
.rstrip('\n') for line
in inf
.readlines()]
213 except UnicodeDecodeError as exc
:
216 print(f
"Failed on file '{fname}: {exc}")
219 def try_expand(line
):
220 """If value looks like an expression, try expanding it
221 Otherwise just return the existing value
223 if line
.find('=') == -1:
227 aeval
= asteval
.Interpreter( usersyms
=SIZES
, minimal
=True )
228 cfg
, val
= re
.split("=", line
)
230 if re
.search(r
'[*+-/]|<<|SZ_+|\(([^\)]+)\)', val
):
231 newval
= hex(aeval(val
))
232 print(f
'\tExpanded expression {val} to {newval}')
233 return cfg
+'='+newval
235 print(f
'\tFailed to expand expression in {line}')
243 """Progress Indicator"""
245 def __init__(self
, col
, total
):
246 """Create a new progress indicator.
249 color_enabled (bool): True for colour output
250 total (int): A number of defconfig files to process.
257 def inc(self
, success
):
258 """Increment the number of processed defconfig files.
261 success (bool): True if processing succeeded
267 """Display the progress."""
268 if self
.current
!= self
.total
:
269 line
= self
.col
.build(self
.col
.GREEN
, f
'{self.good:5d}')
270 line
+= self
.col
.build(self
.col
.RED
,
271 f
'{self.current - self.good:5d}')
272 line
+= self
.col
.build(self
.col
.MAGENTA
,
273 f
'/{self.total - self.current}')
274 print(f
'{line} \r', end
='')
278 class KconfigScanner
:
279 """Kconfig scanner."""
282 """Scan all the Kconfig files and create a Config object."""
283 # Define environment variables referenced from Kconfig
284 os
.environ
['srctree'] = os
.getcwd()
285 os
.environ
['UBOOTVERSION'] = 'dummy'
286 os
.environ
['KCONFIG_OBJDIR'] = ''
287 os
.environ
['CC'] = 'gcc'
288 self
.conf
= kconfiglib
.Kconfig()
293 """A parser of .config and include/autoconf.mk."""
295 re_arch
= re
.compile(r
'CONFIG_SYS_ARCH="(.*)"')
296 re_cpu
= re
.compile(r
'CONFIG_SYS_CPU="(.*)"')
298 def __init__(self
, args
, build_dir
):
299 """Create a new parser.
302 args (Namespace): program arguments
303 build_dir: Build directory.
306 self
.dotconfig
= os
.path
.join(build_dir
, '.config')
307 self
.autoconf
= os
.path
.join(build_dir
, 'include', 'autoconf.mk')
308 self
.spl_autoconf
= os
.path
.join(build_dir
, 'spl', 'include',
310 self
.config_autoconf
= os
.path
.join(build_dir
, AUTO_CONF_PATH
)
311 self
.defconfig
= os
.path
.join(build_dir
, 'defconfig')
314 """Parse .config file and return the architecture.
317 Architecture name (e.g. 'arm').
321 for line
in read_file(self
.dotconfig
):
322 m_arch
= self
.re_arch
.match(line
)
324 arch
= m_arch
.group(1)
326 m_cpu
= self
.re_cpu
.match(line
)
334 if arch
== 'arm' and cpu
== 'armv8':
340 class DatabaseThread(threading
.Thread
):
341 """This thread processes results from Slot threads.
343 It collects the data in the master config directary. There is only one
344 result thread, and this helps to serialise the build output.
346 def __init__(self
, config_db
, db_queue
):
347 """Set up a new result thread
350 builder: Builder which will be sent each result
352 threading
.Thread
.__init
__(self
)
353 self
.config_db
= config_db
354 self
.db_queue
= db_queue
357 """Called to start up the result thread.
359 We collect the next result job and pass it on to the build.
362 defconfig
, configs
= self
.db_queue
.get()
363 self
.config_db
[defconfig
] = configs
364 self
.db_queue
.task_done()
369 """A slot to store a subprocess.
371 Each instance of this class handles one subprocess.
372 This class is useful to control multiple threads
373 for faster processing.
376 def __init__(self
, toolchains
, args
, progress
, devnull
, make_cmd
,
377 reference_src_dir
, db_queue
, col
):
378 """Create a new process slot.
381 toolchains: Toolchains object containing toolchains.
382 args: Program arguments
383 progress: A progress indicator.
384 devnull: A file object of '/dev/null'.
385 make_cmd: command name of GNU Make.
386 reference_src_dir: Determine the true starting config state from this
388 db_queue: output queue to write config info for the database
389 col (terminal.Color): Colour object
391 self
.toolchains
= toolchains
393 self
.progress
= progress
394 self
.build_dir
= tempfile
.mkdtemp()
395 self
.devnull
= devnull
396 self
.make_cmd
= (make_cmd
, 'O=' + self
.build_dir
)
397 self
.reference_src_dir
= reference_src_dir
398 self
.db_queue
= db_queue
400 self
.parser
= KconfigParser(args
, self
.build_dir
)
401 self
.state
= STATE_IDLE
402 self
.failed_boards
= set()
403 self
.defconfig
= None
405 self
.current_src_dir
= None
409 """Delete the working directory
411 This function makes sure the temporary directory is cleaned away
412 even if Python suddenly dies due to error. It should be done in here
413 because it is guaranteed the destructor is always invoked when the
414 instance of the class gets unreferenced.
416 If the subprocess is still running, wait until it finishes.
418 if self
.state
!= STATE_IDLE
:
419 while self
.proc
.poll() is None:
421 shutil
.rmtree(self
.build_dir
)
423 def add(self
, defconfig
):
424 """Assign a new subprocess for defconfig and add it to the slot.
426 If the slot is vacant, create a new subprocess for processing the
427 given defconfig and add it to the slot. Just returns False if
428 the slot is occupied (i.e. the current subprocess is still running).
431 defconfig (str): defconfig name.
434 Return True on success or False on failure
436 if self
.state
!= STATE_IDLE
:
439 self
.defconfig
= defconfig
441 self
.current_src_dir
= self
.reference_src_dir
446 """Check the status of the subprocess and handle it as needed.
448 Returns True if the slot is vacant (i.e. in idle state).
449 If the configuration is successfully finished, assign a new
450 subprocess to build include/autoconf.mk.
451 If include/autoconf.mk is generated, invoke the parser to
452 parse the .config and the include/autoconf.mk, moving
453 config options to the .config as needed.
454 If the .config was updated, run "make savedefconfig" to sync
455 it, update the original defconfig, and then set the slot back
459 Return True if the subprocess is terminated, False otherwise
461 if self
.state
== STATE_IDLE
:
464 if self
.proc
.poll() is None:
467 if self
.proc
.poll() != 0:
469 elif self
.state
== STATE_DEFCONFIG
:
470 if self
.reference_src_dir
and not self
.current_src_dir
:
471 self
.do_savedefconfig()
474 elif self
.state
== STATE_AUTOCONF
:
475 if self
.current_src_dir
:
476 self
.current_src_dir
= None
478 elif self
.args
.build_db
:
481 self
.do_savedefconfig()
482 elif self
.state
== STATE_SAVEDEFCONFIG
:
483 self
.update_defconfig()
485 sys
.exit('Internal Error. This should not happen.')
487 return self
.state
== STATE_IDLE
489 def handle_error(self
):
490 """Handle error cases."""
492 self
.log
.append(self
.col
.build(self
.col
.RED
, 'Failed to process',
494 if self
.args
.verbose
:
495 for line
in self
.proc
.stderr
.read().decode().splitlines():
496 self
.log
.append(self
.col
.build(self
.col
.CYAN
, line
, True))
499 def do_defconfig(self
):
500 """Run 'make <board>_defconfig' to create the .config file."""
502 cmd
= list(self
.make_cmd
)
503 cmd
.append(self
.defconfig
)
504 self
.proc
= subprocess
.Popen(cmd
, stdout
=self
.devnull
,
505 stderr
=subprocess
.PIPE
,
506 cwd
=self
.current_src_dir
)
507 self
.state
= STATE_DEFCONFIG
509 def do_autoconf(self
):
510 """Run 'make AUTO_CONF_PATH'."""
512 arch
= self
.parser
.get_arch()
514 tchain
= self
.toolchains
.Select(arch
)
516 self
.log
.append(self
.col
.build(
518 f
"Tool chain for '{arch}' is missing: do nothing"))
521 env
= tchain
.MakeEnvironment(False)
523 cmd
= list(self
.make_cmd
)
524 cmd
.append('KCONFIG_IGNORE_DUPLICATES=1')
525 cmd
.append(AUTO_CONF_PATH
)
526 self
.proc
= subprocess
.Popen(cmd
, stdout
=self
.devnull
, env
=env
,
527 stderr
=subprocess
.PIPE
,
528 cwd
=self
.current_src_dir
)
529 self
.state
= STATE_AUTOCONF
531 def do_build_db(self
):
532 """Add the board to the database"""
534 for line
in read_file(os
.path
.join(self
.build_dir
, AUTO_CONF_PATH
)):
535 if line
.startswith('CONFIG'):
536 config
, value
= line
.split('=', 1)
537 configs
[config
] = value
.rstrip()
538 self
.db_queue
.put([self
.defconfig
, configs
])
541 def do_savedefconfig(self
):
542 """Update the .config and run 'make savedefconfig'."""
543 if not self
.args
.force_sync
:
547 cmd
= list(self
.make_cmd
)
548 cmd
.append('savedefconfig')
549 self
.proc
= subprocess
.Popen(cmd
, stdout
=self
.devnull
,
550 stderr
=subprocess
.PIPE
)
551 self
.state
= STATE_SAVEDEFCONFIG
553 def update_defconfig(self
):
554 """Update the input defconfig and go back to the idle state."""
555 orig_defconfig
= os
.path
.join('configs', self
.defconfig
)
556 new_defconfig
= os
.path
.join(self
.build_dir
, 'defconfig')
557 updated
= not filecmp
.cmp(orig_defconfig
, new_defconfig
)
561 self
.col
.build(self
.col
.BLUE
, 'defconfig updated', bright
=True))
563 if not self
.args
.dry_run
and updated
:
564 shutil
.move(new_defconfig
, orig_defconfig
)
567 def finish(self
, success
):
568 """Display log along with progress and go to the idle state.
571 success (bool): Should be True when the defconfig was processed
572 successfully, or False when it fails.
574 # output at least 30 characters to hide the "* defconfigs out of *".
575 name
= self
.defconfig
[:-len('_defconfig')]
578 # Put the first log line on the first line
579 log
= name
.ljust(20) + ' ' + self
.log
[0]
581 if len(self
.log
) > 1:
582 log
+= '\n' + '\n'.join([' ' + s
for s
in self
.log
[1:]])
583 # Some threads are running in parallel.
584 # Print log atomically to not mix up logs from different threads.
585 print(log
, file=(sys
.stdout
if success
else sys
.stderr
))
588 if self
.args
.exit_on_error
:
589 sys
.exit('Exit on error.')
590 # If --exit-on-error flag is not set, skip this board and continue.
591 # Record the failed board.
592 self
.failed_boards
.add(name
)
594 self
.progress
.inc(success
)
596 self
.state
= STATE_IDLE
598 def get_failed_boards(self
):
599 """Returns a set of failed boards (defconfigs) in this slot.
601 return self
.failed_boards
605 """Controller of the array of subprocess slots."""
607 def __init__(self
, toolchains
, args
, progress
, reference_src_dir
, db_queue
,
609 """Create a new slots controller.
612 toolchains (Toolchains): Toolchains object containing toolchains
613 args (Namespace): Program arguments
614 progress (Progress): A progress indicator.
615 reference_src_dir (str): Determine the true starting config state
616 from this source tree (None for none)
617 db_queue (Queue): output queue to write config info for the database
618 col (terminal.Color): Colour object
622 self
.progress
= progress
624 devnull
= subprocess
.DEVNULL
625 make_cmd
= get_make_cmd()
626 for _
in range(args
.jobs
):
627 self
.slots
.append(Slot(toolchains
, args
, progress
, devnull
,
628 make_cmd
, reference_src_dir
, db_queue
, col
))
630 def add(self
, defconfig
):
631 """Add a new subprocess if a vacant slot is found.
634 defconfig (str): defconfig name to be put into.
637 Return True on success or False on failure
639 for slot
in self
.slots
:
640 if slot
.add(defconfig
):
645 """Check if there is a vacant slot.
648 Return True if at lease one vacant slot is found, False otherwise.
650 for slot
in self
.slots
:
656 """Check if all slots are vacant.
659 Return True if all the slots are vacant, False otherwise.
662 for slot
in self
.slots
:
667 def write_failed_boards(self
):
668 """Show the results of processing"""
671 for slot
in self
.slots
:
672 boards |
= slot
.get_failed_boards()
675 boards
= '\n'.join(sorted(boards
)) + '\n'
676 write_file(FAILED_LIST
, boards
)
679 class ReferenceSource
:
681 """Reference source against which original configs should be parsed."""
683 def __init__(self
, commit
):
684 """Create a reference source directory based on a specified commit.
687 commit: commit to git-clone
689 self
.src_dir
= tempfile
.mkdtemp()
690 print('Cloning git repo to a separate work directory...')
691 subprocess
.check_output(['git', 'clone', os
.getcwd(), '.'],
693 rev
= subprocess
.check_output(['git', 'rev-parse', '--short',
695 print(f
"Checkout '{rev}' to build the original autoconf.mk.")
696 subprocess
.check_output(['git', 'checkout', commit
],
697 stderr
=subprocess
.STDOUT
, cwd
=self
.src_dir
)
700 """Delete the reference source directory
702 This function makes sure the temporary directory is cleaned away
703 even if Python suddenly dies due to error. It should be done in here
704 because it is guaranteed the destructor is always invoked when the
705 instance of the class gets unreferenced.
707 shutil
.rmtree(self
.src_dir
)
710 """Return the absolute path to the reference source directory."""
714 def move_config(toolchains
, args
, db_queue
, col
):
715 """Build database or sync config options to defconfig files.
718 toolchains (Toolchains): Toolchains to use
719 args (Namespace): Program arguments
720 db_queue (Queue): Queue for database updates
721 col (terminal.Color): Colour object
724 Progress: Progress indicator
727 reference_src
= ReferenceSource(args
.git_ref
)
728 reference_src_dir
= reference_src
.get_dir()
730 reference_src_dir
= None
733 defconfigs
= get_matched_defconfigs(args
.defconfigs
)
735 defconfigs
= get_all_defconfigs()
737 progress
= Progress(col
, len(defconfigs
))
738 slots
= Slots(toolchains
, args
, progress
, reference_src_dir
, db_queue
, col
)
740 # Main loop to process defconfig files:
741 # Add a new subprocess into a vacant slot.
742 # Sleep if there is no available slot.
743 for defconfig
in defconfigs
:
744 while not slots
.add(defconfig
):
745 while not slots
.available():
746 # No available slot: sleep for a while
747 time
.sleep(SLEEP_TIME
)
749 # wait until all the subprocesses finish
750 while not slots
.empty():
751 time
.sleep(SLEEP_TIME
)
753 slots
.write_failed_boards()
756 def find_kconfig_rules(kconf
, config
, imply_config
):
757 """Check whether a config has a 'select' or 'imply' keyword
760 kconf (Kconfiglib.Kconfig): Kconfig object
761 config (str): Name of config to check (without CONFIG_ prefix)
762 imply_config (str): Implying config (without CONFIG_ prefix) which may
763 or may not have an 'imply' for 'config')
766 Symbol object for 'config' if found, else None
768 sym
= kconf
.syms
.get(imply_config
)
770 for sel
, _
in (sym
.selects
+ sym
.implies
):
771 if sel
.name
== config
:
775 def check_imply_rule(kconf
, config
, imply_config
):
776 """Check if we can add an 'imply' option
778 This finds imply_config in the Kconfig and looks to see if it is possible
779 to add an 'imply' for 'config' to that part of the Kconfig.
782 kconf (Kconfiglib.Kconfig): Kconfig object
783 config (str): Name of config to check (without CONFIG_ prefix)
784 imply_config (str): Implying config (without CONFIG_ prefix) which may
785 or may not have an 'imply' for 'config')
789 str: filename of Kconfig file containing imply_config, or None if
791 int: line number within the Kconfig file, or 0 if none
792 str: message indicating the result
794 sym
= kconf
.syms
.get(imply_config
)
796 return 'cannot find sym'
799 return f
'{len(nodes)} locations'
801 fname
, linenum
= node
.filename
, node
.linenr
803 if cwd
and fname
.startswith(cwd
):
804 fname
= fname
[len(cwd
) + 1:]
805 file_line
= f
' at {fname}:{linenum}'
806 data
= read_file(fname
)
807 if data
[linenum
- 1] != f
'config {imply_config}':
808 return None, 0, f
'bad sym format {data[linenum]}{file_line})'
809 return fname
, linenum
, f
'adding{file_line}'
811 def add_imply_rule(config
, fname
, linenum
):
812 """Add a new 'imply' option to a Kconfig
815 config (str): config option to add an imply for (without CONFIG_ prefix)
816 fname (str): Kconfig filename to update
817 linenum (int): Line number to place the 'imply' before
820 Message indicating the result
822 file_line
= f
' at {fname}:{linenum}'
823 data
= read_file(fname
)
826 for offset
, line
in enumerate(data
[linenum
:]):
827 if line
.strip().startswith('help') or not line
:
828 data
.insert(linenum
+ offset
, f
'\timply {config}')
829 write_file(fname
, data
)
830 return f
'added{file_line}'
832 return 'could not insert%s'
834 (IMPLY_MIN_2
, IMPLY_TARGET
, IMPLY_CMD
, IMPLY_NON_ARCH_BOARD
) = (
838 'min2': [IMPLY_MIN_2
, 'Show options which imply >2 boards (normally >5)'],
839 'target': [IMPLY_TARGET
, 'Allow CONFIG_TARGET_... options to imply'],
840 'cmd': [IMPLY_CMD
, 'Allow CONFIG_CMD_... to imply'],
842 IMPLY_NON_ARCH_BOARD
,
843 'Allow Kconfig options outside arch/ and /board/ to imply'],
848 """Read in the config database
852 set of all config options seen (each a str)
853 set of all defconfigs seen (each a str)
854 dict of configs for each defconfig:
855 key: defconfig name, e.g. "MPC8548CDS_legacy_defconfig"
858 value: Value of option
859 dict of defconfigs for each config:
861 value: set of boards using that option
866 # key is defconfig name, value is dict of (CONFIG_xxx, value)
869 # Set of all config options we have seen
872 # Set of all defconfigs we have seen
873 all_defconfigs
= set()
875 defconfig_db
= collections
.defaultdict(set)
876 for line
in read_file(CONFIG_DATABASE
):
878 if not line
: # Separator between defconfigs
879 config_db
[defconfig
] = configs
880 all_defconfigs
.add(defconfig
)
882 elif line
[0] == ' ': # CONFIG line
883 config
, value
= line
.strip().split('=', 1)
884 configs
[config
] = value
885 defconfig_db
[config
].add(defconfig
)
886 all_configs
.add(config
)
887 else: # New defconfig
890 return all_configs
, all_defconfigs
, config_db
, defconfig_db
893 def do_imply_config(config_list
, add_imply
, imply_flags
, skip_added
,
894 check_kconfig
=True, find_superset
=False):
895 """Find CONFIG options which imply those in the list
897 Some CONFIG options can be implied by others and this can help to reduce
898 the size of the defconfig files. For example, CONFIG_X86 implies
899 CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
900 all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
901 each of the x86 defconfig files.
903 This function uses the qconfig database to find such options. It
904 displays a list of things that could possibly imply those in the list.
905 The algorithm ignores any that start with CONFIG_TARGET since these
906 typically refer to only a few defconfigs (often one). It also does not
907 display a config with less than 5 defconfigs.
909 The algorithm works using sets. For each target config in config_list:
910 - Get the set 'defconfigs' which use that target config
911 - For each config (from a list of all configs):
912 - Get the set 'imply_defconfig' of defconfigs which use that config
914 - If imply_defconfigs contains anything not in defconfigs then
915 this config does not imply the target config
918 config_list: List of CONFIG options to check (each a string)
919 add_imply: Automatically add an 'imply' for each config.
920 imply_flags: Flags which control which implying configs are allowed
922 skip_added: Don't show options which already have an imply added.
923 check_kconfig: Check if implied symbols already have an 'imply' or
924 'select' for the target config, and show this information if so.
925 find_superset: True to look for configs which are a superset of those
926 already found. So for example if CONFIG_EXYNOS5 implies an option,
927 but CONFIG_EXYNOS covers a larger set of defconfigs and also
928 implies that option, this will drop the former in favour of the
929 latter. In practice this option has not proved very used.
932 config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
933 defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
935 kconf
= KconfigScanner().conf
if check_kconfig
else None
936 if add_imply
and add_imply
!= 'all':
937 add_imply
= add_imply
.split(',')
939 all_configs
, all_defconfigs
, _
, defconfig_db
= read_database()
941 # Work through each target config option in turn, independently
942 for config
in config_list
:
943 defconfigs
= defconfig_db
.get(config
)
945 print(f
'{config} not found in any defconfig')
948 # Get the set of defconfigs without this one (since a config cannot
950 non_defconfigs
= all_defconfigs
- defconfigs
951 num_defconfigs
= len(defconfigs
)
952 print(f
'{config} found in {num_defconfigs}/{len(all_configs)} defconfigs')
954 # This will hold the results: key=config, value=defconfigs containing it
956 rest_configs
= all_configs
- set([config
])
958 # Look at every possible config, except the target one
959 for imply_config
in rest_configs
:
960 if 'ERRATUM' in imply_config
:
962 if not imply_flags
& IMPLY_CMD
:
963 if 'CONFIG_CMD' in imply_config
:
965 if not imply_flags
& IMPLY_TARGET
:
966 if 'CONFIG_TARGET' in imply_config
:
969 # Find set of defconfigs that have this config
970 imply_defconfig
= defconfig_db
[imply_config
]
972 # Get the intersection of this with defconfigs containing the
974 common_defconfigs
= imply_defconfig
& defconfigs
976 # Get the set of defconfigs containing this config which DO NOT
977 # also contain the taret config. If this set is non-empty it means
978 # that this config affects other defconfigs as well as (possibly)
979 # the ones affected by the target config. This means it implies
980 # things we don't want to imply.
981 not_common_defconfigs
= imply_defconfig
& non_defconfigs
982 if not_common_defconfigs
:
985 # If there are common defconfigs, imply_config may be useful
986 if common_defconfigs
:
989 for prev
in list(imply_configs
.keys()):
990 prev_count
= len(imply_configs
[prev
])
991 count
= len(common_defconfigs
)
992 if (prev_count
> count
and
993 (imply_configs
[prev
] & common_defconfigs
==
995 # skip imply_config because prev is a superset
998 if count
> prev_count
:
999 # delete prev because imply_config is a superset
1000 del imply_configs
[prev
]
1002 imply_configs
[imply_config
] = common_defconfigs
1004 # Now we have a dict imply_configs of configs which imply each config
1005 # The value of each dict item is the set of defconfigs containing that
1006 # config. Rank them so that we print the configs that imply the largest
1007 # number of defconfigs first.
1008 ranked_iconfigs
= sorted(imply_configs
,
1009 key
=lambda k
: len(imply_configs
[k
]), reverse
=True)
1012 add_list
= collections
.defaultdict(list)
1013 for iconfig
in ranked_iconfigs
:
1014 num_common
= len(imply_configs
[iconfig
])
1016 # Don't bother if there are less than 5 defconfigs affected.
1017 if num_common
< (2 if imply_flags
& IMPLY_MIN_2
else 5):
1019 missing
= defconfigs
- imply_configs
[iconfig
]
1020 missing_str
= ', '.join(missing
) if missing
else 'all'
1024 sym
= find_kconfig_rules(kconf
, config
[CONFIG_LEN
:],
1025 iconfig
[CONFIG_LEN
:])
1030 fname
, linenum
= nodes
[0].filename
, nodes
[0].linenr
1031 if cwd
and fname
.startswith(cwd
):
1032 fname
= fname
[len(cwd
) + 1:]
1033 kconfig_info
= f
'{fname}:{linenum}'
1037 sym
= kconf
.syms
.get(iconfig
[CONFIG_LEN
:])
1042 fname
, linenum
= nodes
[0].filename
, nodes
[0].linenr
1043 if cwd
and fname
.startswith(cwd
):
1044 fname
= fname
[len(cwd
) + 1:]
1045 in_arch_board
= not sym
or (fname
.startswith('arch') or
1046 fname
.startswith('board'))
1047 if (not in_arch_board
and
1048 not imply_flags
& IMPLY_NON_ARCH_BOARD
):
1051 if add_imply
and (add_imply
== 'all' or
1052 iconfig
in add_imply
):
1053 fname
, linenum
, kconfig_info
= (check_imply_rule(kconf
,
1054 config
[CONFIG_LEN
:], iconfig
[CONFIG_LEN
:]))
1056 add_list
[fname
].append(linenum
)
1058 if show
and kconfig_info
!= 'skip':
1059 print(f
'{num_common:5d} : '
1060 f
'{iconfig.ljust(30):-30s}{kconfig_info:-25s} {missing_str}')
1062 # Having collected a list of things to add, now we add them. We process
1063 # each file from the largest line number to the smallest so that
1064 # earlier additions do not affect our line numbers. E.g. if we added an
1065 # imply at line 20 it would change the position of each line after
1067 for fname
, linenums
in add_list
.items():
1068 for linenum
in sorted(linenums
, reverse
=True):
1069 add_imply_rule(config
[CONFIG_LEN
:], fname
, linenum
)
1071 def defconfig_matches(configs
, re_match
):
1072 """Check if any CONFIG option matches a regex
1074 The match must be complete, i.e. from the start to end of the CONFIG option.
1077 configs (dict): Dict of CONFIG options:
1079 value: Value of option
1080 re_match (re.Pattern): Match to check
1083 bool: True if any CONFIG matches the regex
1086 if re_match
.fullmatch(cfg
):
1090 def do_find_config(config_list
):
1091 """Find boards with a given combination of CONFIGs
1094 config_list: List of CONFIG options to check (each a regex consisting
1095 of a config option, with or without a CONFIG_ prefix. If an option
1096 is preceded by a tilde (~) then it must be false, otherwise it must
1099 _
, all_defconfigs
, config_db
, _
= read_database()
1101 # Start with all defconfigs
1102 out
= all_defconfigs
1104 # Work through each config in turn
1105 for item
in config_list
:
1106 # Get the real config name and whether we want this config or not
1113 # Search everything that is still in the running. If it has a config
1114 # that we want, or doesn't have one that we don't, add it into the
1115 # running for the next stage
1118 re_match
= re
.compile(cfg
)
1119 for defc
in in_list
:
1120 has_cfg
= defconfig_matches(config_db
[defc
], re_match
)
1123 print(f
'{len(out)} matches')
1124 print(' '.join(item
.split('_defconfig')[0] for item
in out
))
1127 def prefix_config(cfg
):
1128 """Prefix a config with CONFIG_ if needed
1130 This handles ~ operator, which indicates that the CONFIG should be disabled
1132 >>> prefix_config('FRED')
1134 >>> prefix_config('CONFIG_FRED')
1136 >>> prefix_config('~FRED')
1138 >>> prefix_config('~CONFIG_FRED')
1140 >>> prefix_config('A123')
1147 if not cfg
.startswith('CONFIG_'):
1148 cfg
= 'CONFIG_' + cfg
1152 RE_MK_CONFIGS
= re
.compile(r
'CONFIG_(\$\(SPL_(?:TPL_)?\))?([A-Za-z0-9_]*)')
1153 RE_IFDEF
= re
.compile(r
'(ifdef|ifndef)')
1154 RE_C_CONFIGS
= re
.compile(r
'CONFIG_([A-Za-z0-9_]*)')
1155 RE_CONFIG_IS
= re
.compile(r
'CONFIG_IS_ENABLED\(([A-Za-z0-9_]*)\)')
1158 def __init__(self
, cfg
, is_spl
, fname
, rest
):
1160 self
.is_spl
= is_spl
1165 return hash((self
.cfg
, self
.is_spl
))
1167 def scan_makefiles(fnames
):
1168 """Scan Makefiles looking for Kconfig options
1170 Looks for uses of CONFIG options in Makefiles
1173 fnames (list of tuple):
1174 str: Makefile filename where the option was found
1175 str: Line of the Makefile
1180 key (ConfigUse): object
1181 value (list of str): matching lines
1182 dict: Uses by filename
1184 value (set of ConfigUse): uses in that filename
1186 >>> RE_MK_CONFIGS.search('CONFIG_FRED').groups()
1188 >>> RE_MK_CONFIGS.search('CONFIG_$(SPL_)MARY').groups()
1190 >>> RE_MK_CONFIGS.search('CONFIG_$(SPL_TPL_)MARY').groups()
1191 ('$(SPL_TPL_)', 'MARY')
1193 all_uses
= collections
.defaultdict(list)
1195 for fname
, rest
in fnames
:
1196 m_iter
= RE_MK_CONFIGS
.finditer(rest
)
1198 real_opt
= mat
.group(2)
1204 use
= ConfigUse(real_opt
, is_spl
, fname
, rest
)
1205 if fname
not in fname_uses
:
1206 fname_uses
[fname
] = set()
1207 fname_uses
[fname
].add(use
)
1208 all_uses
[use
].append(rest
)
1209 return all_uses
, fname_uses
1212 def scan_src_files(fnames
):
1213 """Scan source files (other than Makefiles) looking for Kconfig options
1215 Looks for uses of CONFIG options
1218 fnames (list of tuple):
1219 str: Makefile filename where the option was found
1220 str: Line of the Makefile
1225 key (ConfigUse): object
1226 value (list of str): matching lines
1227 dict: Uses by filename
1229 value (set of ConfigUse): uses in that filename
1231 >>> RE_C_CONFIGS.search('CONFIG_FRED').groups()
1233 >>> RE_CONFIG_IS.search('CONFIG_IS_ENABLED(MARY)').groups()
1235 >>> RE_CONFIG_IS.search('#if CONFIG_IS_ENABLED(OF_PLATDATA)').groups()
1241 def add_uses(m_iter
, is_spl
):
1243 real_opt
= mat
.group(1)
1246 use
= ConfigUse(real_opt
, is_spl
, fname
, rest
)
1247 if fname
not in fname_uses
:
1248 fname_uses
[fname
] = set()
1249 fname_uses
[fname
].add(use
)
1250 all_uses
[use
].append(rest
)
1252 all_uses
= collections
.defaultdict(list)
1254 for fname
, rest
in fnames
:
1255 m_iter
= RE_C_CONFIGS
.finditer(rest
)
1256 add_uses(m_iter
, False)
1258 m_iter2
= RE_CONFIG_IS
.finditer(rest
)
1259 add_uses(m_iter2
, True)
1261 return all_uses
, fname_uses
1264 MODE_NORMAL
, MODE_SPL
, MODE_PROPER
= range(3)
1266 def do_scan_source(path
, do_update
):
1267 """Scan the source tree for Kconfig inconsistencies
1270 path (str): Path to source tree
1271 do_update (bool) : True to write to scripts/kconf_... files
1273 def is_not_proper(name
):
1274 for prefix
in SPL_PREFIXES
:
1275 if name
.startswith(prefix
):
1276 return name
[len(prefix
):]
1279 def check_not_found(all_uses
, spl_mode
):
1280 """Check for Kconfig options mentioned in the source but not in Kconfig
1284 key (ConfigUse): object
1285 value (list of str): matching lines
1286 spl_mode (int): If MODE_SPL, look at source code which implies
1287 an SPL_ option, but for which there is none;
1288 for MOD_PROPER, look at source code which implies a Proper
1289 option (i.e. use of CONFIG_IS_ENABLED() or $(SPL_) or
1290 $(SPL_TPL_) but for which there none;
1291 if MODE_NORMAL, ignore SPL
1295 key (str): CONFIG name (without 'CONFIG_' prefix
1296 value (list of ConfigUse): List of uses of this CONFIG
1298 # Make sure we know about all the options
1299 not_found
= collections
.defaultdict(list)
1300 for use
, _
in all_uses
.items():
1302 if name
in IGNORE_SYMS
:
1306 if spl_mode
== MODE_SPL
:
1309 # If it is an SPL symbol, try prepending all SPL_ prefixes to
1310 # find at least one SPL symbol
1312 for prefix
in SPL_PREFIXES
:
1313 try_name
= prefix
+ name
1314 sym
= kconf
.syms
.get(try_name
)
1318 not_found
[f
'SPL_{name}'].append(use
)
1320 elif spl_mode
== MODE_PROPER
:
1321 # Try to find the Proper version of this symbol, i.e. without
1323 proper_name
= is_not_proper(name
)
1326 elif not use
.is_spl
:
1329 sym
= kconf
.syms
.get(name
)
1331 proper_name
= is_not_proper(name
)
1334 sym
= kconf
.syms
.get(name
)
1336 for prefix
in SPL_PREFIXES
:
1337 try_name
= prefix
+ name
1338 sym
= kconf
.syms
.get(try_name
)
1342 not_found
[name
].append(use
)
1345 sym
= kconf
.syms
.get(name
)
1346 if not sym
and check
:
1347 not_found
[name
].append(use
)
1350 def show_uses(uses
):
1351 """Show a list of uses along with their filename and code snippet
1355 key (str): CONFIG name (without 'CONFIG_' prefix
1356 value (list of ConfigUse): List of uses of this CONFIG
1358 for name
in sorted(uses
):
1359 print(f
'{name}: ', end
='')
1360 for i
, use
in enumerate(uses
[name
]):
1361 print(f
'{" " if i else ""}{use.fname}: {use.rest.strip()}')
1364 print('Scanning Kconfig')
1365 kconf
= KconfigScanner().conf
1366 print(f
'Scanning source in {path}')
1367 args
= ['git', 'grep', '-E', r
'IS_ENABLED|\bCONFIG']
1368 with subprocess
.Popen(args
, stdout
=subprocess
.PIPE
) as proc
:
1369 out
, _
= proc
.communicate()
1370 lines
= out
.splitlines()
1371 re_fname
= re
.compile('^([^:]*):(.*)')
1375 linestr
= line
.decode('utf-8')
1376 m_fname
= re_fname
.search(linestr
)
1379 fname
, rest
= m_fname
.groups()
1380 dirname
, leaf
= os
.path
.split(fname
)
1381 root
, ext
= os
.path
.splitext(leaf
)
1382 if ext
== '.autoconf':
1384 elif ext
in ['.c', '.h', '.S', '.lds', '.dts', '.dtsi', '.asl', '.cfg',
1386 src_list
.append([fname
, rest
])
1387 elif 'Makefile' in root
or ext
== '.mk':
1388 mk_list
.append([fname
, rest
])
1389 elif ext
in ['.yml', '.sh', '.py', '.awk', '.pl', '.rst', '', '.sed']:
1391 elif 'Kconfig' in root
or 'Kbuild' in root
:
1393 elif 'README' in root
:
1395 elif dirname
in ['configs']:
1397 elif dirname
.startswith('doc') or dirname
.startswith('scripts/kconfig'):
1400 print(f
'Not sure how to handle file {fname}')
1402 # Scan the Makefiles
1403 all_uses
, _
= scan_makefiles(mk_list
)
1405 spl_not_found
= set()
1406 proper_not_found
= set()
1408 # Make sure we know about all the options
1409 print('\nCONFIG options present in Makefiles but not Kconfig:')
1410 not_found
= check_not_found(all_uses
, MODE_NORMAL
)
1411 show_uses(not_found
)
1413 print('\nCONFIG options present in Makefiles but not Kconfig (SPL):')
1414 not_found
= check_not_found(all_uses
, MODE_SPL
)
1415 show_uses(not_found
)
1416 spl_not_found |
= {is_not_proper(key
) or key
for key
in not_found
.keys()}
1418 print('\nCONFIG options used as Proper in Makefiles but without a non-SPL_ variant:')
1419 not_found
= check_not_found(all_uses
, MODE_PROPER
)
1420 show_uses(not_found
)
1421 proper_not_found |
= {not_found
.keys()}
1423 # Scan the source code
1424 all_uses
, _
= scan_src_files(src_list
)
1426 # Make sure we know about all the options
1427 print('\nCONFIG options present in source but not Kconfig:')
1428 not_found
= check_not_found(all_uses
, MODE_NORMAL
)
1429 show_uses(not_found
)
1431 print('\nCONFIG options present in source but not Kconfig (SPL):')
1432 not_found
= check_not_found(all_uses
, MODE_SPL
)
1433 show_uses(not_found
)
1434 spl_not_found |
= {is_not_proper(key
) or key
for key
in not_found
.keys()}
1436 print('\nCONFIG options used as Proper in source but without a non-SPL_ variant:')
1437 not_found
= check_not_found(all_uses
, MODE_PROPER
)
1438 show_uses(not_found
)
1439 proper_not_found |
= {not_found
.keys()}
1441 print('\nCONFIG options used as SPL but without an SPL_ variant:')
1442 for item
in sorted(spl_not_found
):
1445 print('\nCONFIG options used as Proper but without a non-SPL_ variant:')
1446 for item
in sorted(proper_not_found
):
1449 # Write out the updated information
1451 with
open(os
.path
.join(path
, 'scripts', 'conf_nospl'), 'w',
1452 encoding
='utf-8') as out
:
1453 print('# These options should not be enabled in SPL builds\n',
1455 for item
in sorted(spl_not_found
):
1456 print(item
, file=out
)
1457 with
open(os
.path
.join(path
, 'scripts', 'conf_noproper'), 'w',
1458 encoding
='utf-8') as out
:
1459 print('# These options should not be enabled in Proper builds\n',
1461 for item
in sorted(proper_not_found
):
1462 print(item
, file=out
)
1467 cpu_count
= multiprocessing
.cpu_count()
1468 except NotImplementedError:
1471 epilog
= '''Move config options from headers to defconfig files. See
1472 doc/develop/moveconfig.rst for documentation.'''
1474 parser
= ArgumentParser(epilog
=epilog
)
1475 # Add arguments here
1476 parser
.add_argument('-a', '--add-imply', type=str, default
='',
1477 help='comma-separated list of CONFIG options to add '
1478 "an 'imply' statement to for the CONFIG in -i")
1479 parser
.add_argument('-A', '--skip-added', action
='store_true', default
=False,
1480 help="don't show options which are already marked as "
1482 parser
.add_argument('-b', '--build-db', action
='store_true', default
=False,
1483 help='build a CONFIG database')
1484 parser
.add_argument('-C', '--commit', action
='store_true', default
=False,
1485 help='Create a git commit for the operation')
1486 parser
.add_argument('--nocolour', action
='store_true', default
=False,
1487 help="don't display the log in colour")
1488 parser
.add_argument('-d', '--defconfigs', type=str,
1489 help='a file containing a list of defconfigs to move, '
1490 "one per line (for example 'snow_defconfig') "
1491 "or '-' to read from stdin")
1492 parser
.add_argument('-e', '--exit-on-error', action
='store_true',
1494 help='exit immediately on any error')
1495 parser
.add_argument('-f', '--find', action
='store_true', default
=False,
1496 help='Find boards with a given config combination')
1497 parser
.add_argument('-i', '--imply', action
='store_true', default
=False,
1498 help='find options which imply others')
1499 parser
.add_argument('-I', '--imply-flags', type=str, default
='',
1500 help="control the -i option ('help' for help")
1501 parser
.add_argument('-j', '--jobs', type=int, default
=cpu_count
,
1502 help='the number of jobs to run simultaneously')
1503 parser
.add_argument('-n', '--dry-run', action
='store_true', default
=False,
1504 help='perform a trial run (show log with no changes)')
1505 parser
.add_argument('-r', '--git-ref', type=str,
1506 help='the git ref to clone for building the autoconf.mk')
1507 parser
.add_argument('-s', '--force-sync', action
='store_true', default
=False,
1508 help='force sync by savedefconfig')
1509 parser
.add_argument('-S', '--spl', action
='store_true', default
=False,
1510 help='parse config options defined for SPL build')
1511 parser
.add_argument('--scan-source', action
='store_true', default
=False,
1512 help='scan source for uses of CONFIG options')
1513 parser
.add_argument('-t', '--test', action
='store_true', default
=False,
1514 help='run unit tests')
1515 parser
.add_argument('-y', '--yes', action
='store_true', default
=False,
1516 help="respond 'yes' to any prompts")
1517 parser
.add_argument('-u', '--update', action
='store_true', default
=False,
1518 help="update scripts/ files (use with --scan-source)")
1519 parser
.add_argument('-v', '--verbose', action
='store_true', default
=False,
1520 help='show any build errors as boards are built')
1521 parser
.add_argument('configs', nargs
='*')
1523 args
= parser
.parse_args()
1526 sys
.argv
= [sys
.argv
[0]]
1527 fail
, _
= doctest
.testmod()
1532 col
= terminal
.Color(terminal
.COLOR_NEVER
if args
.nocolour
1533 else terminal
.COLOR_IF_TERMINAL
)
1535 if args
.scan_source
:
1536 do_scan_source(os
.getcwd(), args
.update
)
1539 if not any((args
.force_sync
, args
.build_db
, args
.imply
, args
.find
)):
1540 parser
.print_usage()
1543 # prefix the option name with CONFIG_ if missing
1544 configs
= [prefix_config(cfg
) for cfg
in args
.configs
]
1546 check_top_directory()
1550 if args
.imply_flags
== 'all':
1553 elif args
.imply_flags
:
1554 for flag
in args
.imply_flags
.split(','):
1555 bad
= flag
not in IMPLY_FLAGS
1557 print(f
"Invalid flag '{flag}'")
1558 if flag
== 'help' or bad
:
1559 print("Imply flags: (separate with ',')")
1560 for name
, info
in IMPLY_FLAGS
.items():
1561 print(f
' {name:-15s}: {info[1]}')
1562 parser
.print_usage()
1564 imply_flags |
= IMPLY_FLAGS
[flag
][0]
1566 do_imply_config(configs
, args
.add_imply
, imply_flags
, args
.skip_added
)
1570 do_find_config(configs
)
1573 # We are either building the database or forcing a sync of defconfigs
1575 db_queue
= queue
.Queue()
1576 dbt
= DatabaseThread(config_db
, db_queue
)
1580 check_clean_directory()
1582 toolchains
= toolchain
.Toolchains()
1583 toolchains
.GetSettings()
1584 toolchains
.Scan(verbose
=False)
1585 progress
= move_config(toolchains
, args
, db_queue
, col
)
1589 subprocess
.call(['git', 'add', '-u'])
1591 msg
= 'Convert %s %sto Kconfig' % (configs
[0],
1592 'et al ' if len(configs
) > 1 else '')
1593 msg
+= ('\n\nThis converts the following to Kconfig:\n %s\n' %
1594 '\n '.join(configs
))
1596 msg
= 'configs: Resync with savedefconfig'
1597 msg
+= '\n\nRsync all defconfig files using moveconfig.py'
1598 subprocess
.call(['git', 'commit', '-s', '-m', msg
])
1600 failed
= progress
.total
- progress
.good
1601 failure
= f
'{failed} failed, ' if failed
else ''
1603 with
open(CONFIG_DATABASE
, 'w', encoding
='utf-8') as outf
:
1604 for defconfig
, configs
in config_db
.items():
1605 outf
.write(f
'{defconfig}\n')
1606 for config
in sorted(configs
.keys()):
1607 outf
.write(f
' {config}={configs[config]}\n')
1610 col
.RED
if failed
else col
.GREEN
,
1611 f
'{failure}{len(config_db)} boards written to {CONFIG_DATABASE}'))
1614 print(col
.build(col
.RED
, f
'{failure}see {FAILED_LIST}', True))
1616 # Add enough spaces to overwrite the progress indicator
1618 col
.GREEN
, f
'{progress.total} processed ', bright
=True))
1623 if __name__
== '__main__':