Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/markdown/Commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,9 @@ The following options are recognized:
- tab_width (int): Width of tab stops, used to compute line length
when `indent_by` uses tab characters (default is 4).
- sort_files (bool): When true, arguments of `files()` function are
sorted alphabetically (default is true).
sorted (default is true). *Since 1.10.0*, arguments are sorted
[naturally](Style-guide.md#sorting-source-paths) rather than
alphabetically.
- group_arg_value (bool): When true, string argument with `--` prefix
followed by string argument without `--` prefix are grouped on the
same line, in multiline arguments (default is false).
Expand Down
5 changes: 5 additions & 0 deletions docs/markdown/snippets/meson-format-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## `meson format` sorts `files()` arguments naturally

If the `sort_files` option is enabled, as it is by default, `meson format`
now sorts `files()` arguments [naturally](Style-guide.md#sorting-source-paths)
rather than alphabetically.
7 changes: 3 additions & 4 deletions mesonbuild/mformat.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import sys

from . import mparser
from .mesonlib import MesonException
from .mesonlib import MesonException, pathname_sort_key
from .ast.postprocess import AstConditionLevel
from .ast.printer import RawPrinter
from .ast.visitor import FullAstVisitor
Expand Down Expand Up @@ -301,13 +301,12 @@ def dedent(self, value: str) -> str:
return value

def sort_arguments(self, node: mparser.ArgumentNode) -> None:
# TODO: natsort
def sort_key(arg: mparser.BaseNode) -> str:
if isinstance(arg, mparser.StringNode):
return arg.raw_value
return getattr(node, 'value', '')

node.arguments.sort(key=sort_key)
node.arguments.sort(key=lambda arg: pathname_sort_key(sort_key(arg)))

def visit_EmptyNode(self, node: mparser.EmptyNode) -> None:
self.enter_node(node)
Expand Down Expand Up @@ -1064,5 +1063,5 @@ def run(options: argparse.Namespace) -> int:
# - Option to simplify string literals
# - Option to recognize and parse meson.build in subdirs
# - Correctly compute line length when using tabs
# - By default, arguments in files() are sorted alphabetically
# - By default, arguments in files() are sorted naturally
# - Option to group '--arg', 'value' on same line in multiline arguments
13 changes: 2 additions & 11 deletions mesonbuild/rewriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from .ast.interpreter import IntrospectionBuildTarget, IntrospectionDependency, _symbol
from .interpreterbase import UnknownValue, TV_func
from .interpreterbase.helpers import flatten
from mesonbuild.mesonlib import MesonException, setup_vsenv, relpath
from mesonbuild.mesonlib import MesonException, pathname_sort_key, relpath, setup_vsenv
from . import mlog, environment
from functools import wraps
from .mparser import Token, ArrayNode, ArgumentNode, ArithmeticNode, AssignmentNode, BaseNode, StringNode, BooleanNode, ElementaryNode, IdNode, FunctionNode, PlusAssignmentNode
Expand Down Expand Up @@ -951,15 +951,6 @@ def rel_source(src: str) -> str:

# Sort files
for i in to_sort_nodes:
def convert(text: str) -> T.Union[int, str]:
return int(text) if text.isdigit() else text.lower()

def alphanum_key(key: str) -> T.List[T.Union[int, str]]:
return [convert(c) for c in re.split('([0-9]+)', key)]

def path_sorter(key: str) -> T.List[T.Tuple[bool, T.List[T.Union[int, str]]]]:
return [(key.count('/') <= idx, alphanum_key(x)) for idx, x in enumerate(key.split('/'))]

if isinstance(i, FunctionNode) and i.func_name.value in BUILD_TARGET_FUNCTIONS:
src_args = i.args.arguments[1:]
target_name = [i.args.arguments[0]]
Expand All @@ -968,7 +959,7 @@ def path_sorter(key: str) -> T.List[T.Tuple[bool, T.List[T.Union[int, str]]]]:
target_name = []
unknown: T.List[BaseNode] = [x for x in src_args if not isinstance(x, StringNode)]
sources: T.List[StringNode] = [x for x in src_args if isinstance(x, StringNode)]
sources = sorted(sources, key=lambda x: path_sorter(x.value))
sources = sorted(sources, key=lambda x: pathname_sort_key(x.value))
i.args.arguments = target_name + unknown + T.cast(T.List[BaseNode], sources)

def process(self, cmd: T.Dict[str, T.Any]) -> None:
Expand Down
15 changes: 15 additions & 0 deletions mesonbuild/utils/universal.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ class _VerPickleLoadable(Protocol):
'listify_array_value',
'partition',
'path_is_in_root',
'pathname_sort_key',
'pickle_load',
'Popen_safe',
'Popen_safe_logged',
Expand Down Expand Up @@ -2499,3 +2500,17 @@ def __get__(self, instance: object, cls: T.Type) -> _T:
value = self.__func(instance)
setattr(instance, self.__name, value)
return value


def pathname_sort_key(key: str) -> tuple[tuple[bool, tuple[int | str, ...]], ...]:
'''Sort key for natural pathname sort, as defined in the Meson style guide.
Use as the key= argument to sort() or sorted().'''

def convert(text: str) -> int | str:
return int(text) if text.isdigit() else text.lower()

def alphanum_key(key: str) -> tuple[int | str, ...]:
return tuple(convert(c) for c in re.split('([0-9]+)', key))

return tuple((key.count('/') <= idx, alphanum_key(x))
for idx, x in enumerate(key.split('/')))
1 change: 1 addition & 0 deletions test cases/format/1 default/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ meson_files = {
'comments': files('crazy_comments.meson'),
'indentation': files('indentation.meson'),
'gh13242': files('gh13242.meson'),
'sort': files('sort_files.meson'),
}

# Ensure empty function are formatted correctly on long lines
Expand Down
19 changes: 19 additions & 0 deletions test cases/format/1 default/sort_files.meson
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Based on the example in the Meson style guide

sources = files(
'aaa/a1.c',
'aaa/A2.c',
'aaa/a3.c',
'bbb/subdir1/b1.c',
'bbb/subdir2/b2.c',
'bbb/subdir10/b3.c',
'bbb/subdir20/b4.c',
'bbb/b5.c',
'bbb/b6.c',
'c/a111.c',
'c/ab0.c',
'f1.c',
'f2.c',
'f10.c',
'f20.c',
)
Loading