Skip to content

Commit 29936e4

Browse files
committed
EHN: add tool.meson-python.wheel.{exclude,include} settings
1 parent cd44214 commit 29936e4

File tree

5 files changed

+65
-2
lines changed

5 files changed

+65
-2
lines changed

docs/reference/pyproject-settings.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,29 @@ use them and examples.
6363

6464
Extra arguments to be passed to the ``meson install`` command.
6565

66+
.. option:: tool.meson-python.wheel.exclude
67+
68+
List of glob patterns matching paths of files that must be excluded from
69+
the Python wheel. The accepted glob patterns are the ones implemented by
70+
the Python :mod:`fnmatch` with case sensitive matching. The paths to be
71+
matched are as they appear in the Meson introspection data, namely they are
72+
rooted in one of the Meson install locations: ``{bindir}``, ``{datadir}``,
73+
``{includedir}``, ``{libdir_shared}``, ``{libdir_static}``, et cetera.
74+
75+
This configuration setting is measure of last resort to exclude installed
76+
files from a Python wheel. It is to be used when the project includes
77+
subprojects that do not allow fine control on the installed files. Better
78+
solutions include the use of Meson install tags and excluding subprojects
79+
to be installed via :option:`tool.meson-python.args.install`.
80+
81+
.. option:: tool.meson-python.wheel.include
82+
83+
List of glob patterns matching paths of files that must not be excluded
84+
from the Python wheel. All files recorded for installation in the Meson
85+
project are included in the Python wheel unless matching an exclude glob
86+
pattern specified in :option:`tool.meson-python.wheel.exclude`. An include
87+
glob pattern is useful exclusively to limit the effect of an exclude
88+
pattern that matches too many files.
6689

6790
__ https://docs.python.org/3/c-api/stable.html?highlight=limited%20api#stable-application-binary-interface
6891
__ https://mesonbuild.com/Python-module.html#extension_module

mesonpy/__init__.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import contextlib
1717
import copy
1818
import difflib
19+
import fnmatch
1920
import functools
2021
import importlib.machinery
2122
import io
@@ -111,13 +112,29 @@ class InvalidLicenseExpression(Exception): # type: ignore[no-redef]
111112
}
112113

113114

114-
def _map_to_wheel(sources: Dict[str, Dict[str, Any]]) -> DefaultDict[str, List[Tuple[pathlib.Path, str]]]:
115+
def _compile_patterns(patterns: List[str]) -> Callable[[str], bool]:
116+
if not patterns:
117+
return lambda x: False
118+
func = re.compile('|'.join(fnmatch.translate(os.path.normpath(p)) for p in patterns)).match
119+
return typing.cast('Callable[[str], bool]', func)
120+
121+
122+
def _map_to_wheel(
123+
sources: Dict[str, Dict[str, Any]],
124+
exclude: List[str],
125+
include: List[str],
126+
) -> DefaultDict[str, List[Tuple[pathlib.Path, str]]]:
115127
"""Map files to the wheel, organized by wheel installation directory."""
116128
wheel_files: DefaultDict[str, List[Tuple[pathlib.Path, str]]] = collections.defaultdict(list)
117129
packages: Dict[str, str] = {}
130+
excluded = _compile_patterns(exclude)
131+
included = _compile_patterns(include)
118132

119133
for key, group in sources.items():
120134
for src, target in group.items():
135+
if excluded(target['destination']) and not included(target['destination']):
136+
continue
137+
121138
destination = pathlib.Path(target['destination'])
122139
anchor = destination.parts[0]
123140
dst = pathlib.Path(*destination.parts[1:])
@@ -581,6 +598,10 @@ def _string_or_path(value: Any, name: str) -> str:
581598
'args': _table({
582599
name: _strings for name in _MESON_ARGS_KEYS
583600
}),
601+
'wheel': _table({
602+
'exclude': _strings,
603+
'include': _strings,
604+
}),
584605
})
585606

586607
table = pyproject.get('tool', {}).get('meson-python', {})
@@ -823,6 +844,10 @@ def __init__(
823844
# from the package, make sure the developers acknowledge this.
824845
self._allow_windows_shared_libs = pyproject_config.get('allow-windows-internal-shared-libs', False)
825846

847+
# Files to be excluded from the wheel
848+
self._excluded_files = pyproject_config.get('wheel', {}).get('exclude', [])
849+
self._included_files = pyproject_config.get('wheel', {}).get('include', [])
850+
826851
def _run(self, cmd: Sequence[str]) -> None:
827852
"""Invoke a subprocess."""
828853
# Flush the line to ensure that the log line with the executed
@@ -906,7 +931,7 @@ def _manifest(self) -> DefaultDict[str, List[Tuple[pathlib.Path, str]]]:
906931
sources[key][target] = details
907932

908933
# Map Meson installation locations to wheel paths.
909-
return _map_to_wheel(sources)
934+
return _map_to_wheel(sources, self._excluded_files, self._included_files)
910935

911936
@property
912937
def _meson_name(self) -> str:

tests/packages/subproject/pyproject.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,15 @@
55
[build-system]
66
build-backend = 'mesonpy'
77
requires = ['meson-python']
8+
9+
[tool.meson-python.wheel]
10+
exclude = [
11+
# Meson before version 1.3.0 install data files in
12+
# ``{datadir}/{project name}/``, later versions install
13+
# in the more correct ``{datadir}/{subproject name}/``.
14+
'{datadir}/*/data.txt',
15+
'{py_purelib}/dep.*',
16+
]
17+
include = [
18+
'{py_purelib}/dep.py',
19+
]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
excluded via tool.meson-python.wheel.exclude

tests/packages/subproject/subprojects/dep/meson.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ project('dep')
77
py = import('python').find_installation()
88

99
py.install_sources('dep.py')
10+
11+
install_data('data.txt')

0 commit comments

Comments
 (0)