mformat: Make --recursive recurse subprojects with --subprojects flag

Currently, `meson format --recursive` only recurse meson.build files
that are reachable via a `subdir` function. Add `--subprojects` flag to
also recurse subprojects.
This commit is contained in:
Arthur Grillo
2026-05-16 20:43:44 -03:00
committed by Paolo Bonzini
parent 97d7e6091b
commit 31a1a87bd8
6 changed files with 89 additions and 4 deletions

View File

@@ -0,0 +1,4 @@
## New option `--subprojects` for `meson format`
A new option `--subprojects`, to be specified together with `--recursive`,
tells `meson format` to also recurse into subprojects.

View File

@@ -874,11 +874,16 @@ class ComputeLineLengths(FullAstVisitor):
class SubdirFetcher(FullAstVisitor):
def __init__(self, current_dir: Path):
def __init__(self, current_dir: Path, fetch_subprojects: bool):
self.current_dir = current_dir
self.fetch_subprojects = fetch_subprojects
self.subdirs: T.List[Path] = []
def visit_FunctionNode(self, node: mparser.FunctionNode) -> None:
if self.fetch_subprojects and node.func_name.value == 'subproject':
if node.args.arguments and isinstance(node.args.arguments[0], mparser.StringNode):
subdir = node.args.arguments[0].value
self.subdirs.append(self.current_dir / 'subprojects' / subdir)
if node.func_name.value == 'subdir':
if node.args.arguments and isinstance(node.args.arguments[0], mparser.StringNode):
subdir = node.args.arguments[0].value
@@ -888,8 +893,9 @@ class SubdirFetcher(FullAstVisitor):
class Formatter:
def __init__(self, configuration_file: T.Optional[Path], use_editor_config: bool, fetch_subdirs: bool):
def __init__(self, configuration_file: T.Optional[Path], use_editor_config: bool, fetch_subdirs: bool, fetch_subprojects: bool = False):
self.fetch_subdirs = fetch_subdirs
self.fetch_subprojects = fetch_subprojects
self.use_editor_config = use_editor_config
self.config = self.load_configuration(configuration_file)
self.current_config = self.config
@@ -974,7 +980,7 @@ class Formatter:
ast = mparser.Parser(code, source_file.as_posix()).parse()
if self.fetch_subdirs:
subdir_fetcher = SubdirFetcher(self.current_dir)
subdir_fetcher = SubdirFetcher(self.current_dir, self.fetch_subprojects)
ast.accept(subdir_fetcher)
self.subdirs = subdir_fetcher.subdirs
@@ -1016,6 +1022,11 @@ def add_arguments(parser: argparse.ArgumentParser) -> None:
action='store_true',
help='recurse subdirs (requires --check-only, --check-diff or --inplace option)',
)
parser.add_argument(
'-s', '--subprojects',
action='store_true',
help='recurse subprojects (requires --recursive)',
)
parser.add_argument(
'-c', '--configuration',
metavar='meson.format',
@@ -1058,6 +1069,8 @@ def run(options: argparse.Namespace) -> int:
raise MesonException('--output argument implies having exactly one source file')
if options.recursive and not (options.inplace or options.check_only or options.check_diff):
raise MesonException('--recursive argument requires one of --inplace, --check-diff or --check-only')
if options.subprojects and not options.recursive:
raise MesonException('--subprojects argument requires --recursive option')
from_stdin = len(options.sources) == 1 and options.sources[0].name == '-' and options.sources[0].parent == Path()
if options.recursive and from_stdin:
@@ -1074,7 +1087,7 @@ def run(options: argparse.Namespace) -> int:
if not options.configuration:
options.configuration = get_meson_format(sources)
formatter = Formatter(options.configuration, options.editor_config, options.recursive)
formatter = Formatter(options.configuration, options.editor_config, options.recursive, options.subprojects)
err = 0
while sources:

View File

@@ -0,0 +1,44 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
# Copyright © 2026 Arthur Grillo
import argparse
import subprocess
import sys
import difflib
def compare(actual: str, expected: str) -> int:
if actual == expected:
return 0
diff = difflib.ndiff(expected, actual)
for line in diff:
print(line, file=sys.stderr, end='')
return 1
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument('meson_cmd_path', nargs='+')
args = parser.parse_args()
result = subprocess.run([
*args.meson_cmd_path,
'format',
'--recursive',
'--subprojects',
'--check-diff',
], capture_output=True, text=True)
with open('subprojects/find-me/expected.diff', 'r', encoding='utf-8') as f:
actual = result.stdout.replace('\\', '/')
expected = f.read()
return compare(
actual,
expected,
)
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,14 @@
project('format')
python = import('python')
meson_cmd = [python.find_installation(), find_program('meson').full_path()]
file_compare = find_program(files('compare_format_diff.py'))
test(
'recurse subprojects',
file_compare,
args: meson_cmd,
workdir: meson.project_source_root(),
)
subproject('find-me')

View File

@@ -0,0 +1,7 @@
--- subprojects/find-me/meson.build (original)
+++ subprojects/find-me/meson.build (reformatted)
@@ -1,3 +1 @@
-project(
- 'a'
-) # should be on one line
+project('a') # should be on one line

View File

@@ -0,0 +1,3 @@
project(
'a'
) # should be on one line