buildman: Add an option to check maintainers

Rather than using the -R option to get this report as a side effect, add
a dedicated option for it.

Disable CI for now as there are some missing maintainers, unfortunately.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass 2023-07-19 17:48:27 -06:00
parent 9a7cc8121f
commit 1b21842eab
7 changed files with 63 additions and 25 deletions

View file

@ -123,7 +123,7 @@ stages:
options: $(container_option) options: $(container_option)
steps: steps:
- script: | - script: |
./tools/buildman/buildman -R ./tools/buildman/buildman --maintainer-check || exit 0
- job: tools_only - job: tools_only
displayName: 'Ensure host tools build' displayName: 'Ensure host tools build'

View file

@ -187,7 +187,7 @@ sloccount:
Check for configs without MAINTAINERS entry: Check for configs without MAINTAINERS entry:
stage: testsuites stage: testsuites
script: script:
- ./tools/buildman/buildman -R - ./tools/buildman/buildman --maintainer-check || exit 0
# Ensure host tools build # Ensure host tools build
Build tools-only: Build tools-only:

View file

@ -213,11 +213,13 @@ class KconfigScanner:
if self._tmpfile: if self._tmpfile:
try_remove(self._tmpfile) try_remove(self._tmpfile)
def scan(self, defconfig): def scan(self, defconfig, warn_targets):
"""Load a defconfig file to obtain board parameters. """Load a defconfig file to obtain board parameters.
Args: Args:
defconfig (str): path to the defconfig file to be processed defconfig (str): path to the defconfig file to be processed
warn_targets (bool): True to warn about missing or duplicate
CONFIG_TARGET options
Returns: Returns:
tuple: dictionary of board parameters. It has a form of: tuple: dictionary of board parameters. It has a form of:
@ -252,6 +254,7 @@ class KconfigScanner:
params[key] = '-' params[key] = '-'
# Check there is exactly one TARGET_xxx set # Check there is exactly one TARGET_xxx set
if warn_targets:
target = None target = None
for name, sym in self._conf.syms.items(): for name, sym in self._conf.syms.items():
if name.startswith('TARGET_') and sym.str_value == 'y': if name.startswith('TARGET_') and sym.str_value == 'y':
@ -666,7 +669,8 @@ class Boards:
return result, warnings return result, warnings
@classmethod @classmethod
def scan_defconfigs_for_multiprocess(cls, srcdir, queue, defconfigs): def scan_defconfigs_for_multiprocess(cls, srcdir, queue, defconfigs,
warn_targets):
"""Scan defconfig files and queue their board parameters """Scan defconfig files and queue their board parameters
This function is intended to be passed to multiprocessing.Process() This function is intended to be passed to multiprocessing.Process()
@ -678,10 +682,12 @@ class Boards:
written into this. written into this.
defconfigs (sequence of str): A sequence of defconfig files to be defconfigs (sequence of str): A sequence of defconfig files to be
scanned. scanned.
warn_targets (bool): True to warn about missing or duplicate
CONFIG_TARGET options
""" """
kconf_scanner = KconfigScanner(srcdir) kconf_scanner = KconfigScanner(srcdir)
for defconfig in defconfigs: for defconfig in defconfigs:
queue.put(kconf_scanner.scan(defconfig)) queue.put(kconf_scanner.scan(defconfig, warn_targets))
@classmethod @classmethod
def read_queues(cls, queues, params_list, warnings): def read_queues(cls, queues, params_list, warnings):
@ -698,7 +704,7 @@ class Boards:
params_list.append(params) params_list.append(params)
warnings.update(warn) warnings.update(warn)
def scan_defconfigs(self, config_dir, srcdir, jobs=1): def scan_defconfigs(self, config_dir, srcdir, jobs=1, warn_targets=False):
"""Collect board parameters for all defconfig files. """Collect board parameters for all defconfig files.
This function invokes multiple processes for faster processing. This function invokes multiple processes for faster processing.
@ -707,6 +713,8 @@ class Boards:
config_dir (str): Directory containing the defconfig files config_dir (str): Directory containing the defconfig files
srcdir (str): Directory containing source code (Kconfig files) srcdir (str): Directory containing source code (Kconfig files)
jobs (int): The number of jobs to run simultaneously jobs (int): The number of jobs to run simultaneously
warn_targets (bool): True to warn about missing or duplicate
CONFIG_TARGET options
Returns: Returns:
tuple: tuple:
@ -732,7 +740,7 @@ class Boards:
que = multiprocessing.Queue(maxsize=-1) que = multiprocessing.Queue(maxsize=-1)
proc = multiprocessing.Process( proc = multiprocessing.Process(
target=self.scan_defconfigs_for_multiprocess, target=self.scan_defconfigs_for_multiprocess,
args=(srcdir, que, defconfigs)) args=(srcdir, que, defconfigs, warn_targets))
proc.start() proc.start()
processes.append(proc) processes.append(proc)
queues.append(que) queues.append(que)
@ -819,7 +827,8 @@ class Boards:
with open(output, 'w', encoding="utf-8") as outf: with open(output, 'w', encoding="utf-8") as outf:
outf.write(COMMENT_BLOCK + '\n'.join(output_lines) + '\n') outf.write(COMMENT_BLOCK + '\n'.join(output_lines) + '\n')
def build_board_list(self, config_dir, srcdir, jobs=1): def build_board_list(self, config_dir=CONFIG_DIR, srcdir='.', jobs=1,
warn_targets=False):
"""Generate a board-database file """Generate a board-database file
This works by reading the Kconfig, then loading each board's defconfig This works by reading the Kconfig, then loading each board's defconfig
@ -830,6 +839,8 @@ class Boards:
config_dir (str): Directory containing the defconfig files config_dir (str): Directory containing the defconfig files
srcdir (str): Directory containing source code (Kconfig files) srcdir (str): Directory containing source code (Kconfig files)
jobs (int): The number of jobs to run simultaneously jobs (int): The number of jobs to run simultaneously
warn_targets (bool): True to warn about missing or duplicate
CONFIG_TARGET options
Returns: Returns:
tuple: tuple:
@ -839,7 +850,8 @@ class Boards:
value: string value of the key value: string value of the key
list of str: Warnings that came up list of str: Warnings that came up
""" """
params_list, warnings = self.scan_defconfigs(config_dir, srcdir, jobs) params_list, warnings = self.scan_defconfigs(config_dir, srcdir, jobs,
warn_targets)
m_warnings = self.insert_maintainers_info(srcdir, params_list) m_warnings = self.insert_maintainers_info(srcdir, params_list)
return params_list, warnings + m_warnings return params_list, warnings + m_warnings

View file

@ -1311,6 +1311,19 @@ You should use 'buildman -nv <criteria>' instead of greoing the boards.cfg file,
since it may be dropped altogether in future. since it may be dropped altogether in future.
Checking maintainers
--------------------
Sometimes a board is added without a corresponding entry in a MAINTAINERS file.
Use the `--maintainer-check` option to check this::
$ buildman --maintainer-check
WARNING: board/mikrotik/crs3xx-98dx3236/MAINTAINERS: missing defconfig ending at line 7
WARNING: no maintainers for 'clearfog_spi'
Buildman returns with an exit code of 2 if there area any warnings.
Checking the command Checking the command
-------------------- --------------------

View file

@ -85,6 +85,8 @@ def ParseArgs():
parser.add_option( parser.add_option(
'-M', '--allow-missing', action='store_true', default=False, '-M', '--allow-missing', action='store_true', default=False,
help='Tell binman to allow missing blobs and generate fake ones as needed'), help='Tell binman to allow missing blobs and generate fake ones as needed'),
parser.add_option('--maintainer-check', action='store_true',
help='Check that maintainer entries exist for each board')
parser.add_option( parser.add_option(
'--no-allow-missing', action='store_true', default=False, '--no-allow-missing', action='store_true', default=False,
help='Disable telling binman to allow missing blobs'), help='Disable telling binman to allow missing blobs'),

View file

@ -202,6 +202,8 @@ def DoBuildman(options, args, toolchains=None, make_func=None, brds=None,
sys.exit(col.build(col.RED, '-w requires that you specify -o')) sys.exit(col.build(col.RED, '-w requires that you specify -o'))
options.output_dir = '..' options.output_dir = '..'
nr_cups = options.threads or multiprocessing.cpu_count()
# Work out what subset of the boards we are building # Work out what subset of the boards we are building
if not brds: if not brds:
if not os.path.exists(options.output_dir): if not os.path.exists(options.output_dir):
@ -209,8 +211,15 @@ def DoBuildman(options, args, toolchains=None, make_func=None, brds=None,
board_file = os.path.join(options.output_dir, 'boards.cfg') board_file = os.path.join(options.output_dir, 'boards.cfg')
brds = boards.Boards() brds = boards.Boards()
ok = brds.ensure_board_list(board_file, if options.maintainer_check:
options.threads or multiprocessing.cpu_count(), warnings = brds.build_board_list(jobs=nr_cups)[1]
if warnings:
for warn in warnings:
print(warn, file=sys.stderr)
return 2
return 0
ok = brds.ensure_board_list(board_file, nr_cups,
force=options.regen_board_list, force=options.regen_board_list,
quiet=not options.verbose) quiet=not options.verbose)
if options.regen_board_list: if options.regen_board_list:

View file

@ -976,7 +976,8 @@ config TARGET_OTHER
endif endif
''') ''')
tools.write_file(kc_file, orig_kc_data + extra) tools.write_file(kc_file, orig_kc_data + extra)
params_list, warnings = self._boards.build_board_list(config_dir, src) params_list, warnings = self._boards.build_board_list(config_dir, src,
warn_targets=True)
self.assertEquals(2, len(params_list)) self.assertEquals(2, len(params_list))
self.assertEquals( self.assertEquals(
['WARNING: board2_defconfig: Duplicate TARGET_xxx: board2 and other'], ['WARNING: board2_defconfig: Duplicate TARGET_xxx: board2 and other'],
@ -986,7 +987,8 @@ endif
lines = [b'' if line == b'config TARGET_BOARD2\n' else line lines = [b'' if line == b'config TARGET_BOARD2\n' else line
for line in orig_kc_data.splitlines(keepends=True)] for line in orig_kc_data.splitlines(keepends=True)]
tools.write_file(kc_file, b''.join(lines)) tools.write_file(kc_file, b''.join(lines))
params_list, warnings = self._boards.build_board_list(config_dir, src) params_list, warnings = self._boards.build_board_list(config_dir, src,
warn_targets=True)
self.assertEquals(2, len(params_list)) self.assertEquals(2, len(params_list))
self.assertEquals( self.assertEquals(
['WARNING: board2_defconfig: No TARGET_BOARD2 enabled'], ['WARNING: board2_defconfig: No TARGET_BOARD2 enabled'],