| 
16 | 16 | import contextlib  | 
17 | 17 | import copy  | 
18 | 18 | import difflib  | 
 | 19 | +import fnmatch  | 
19 | 20 | import functools  | 
20 | 21 | import importlib.machinery  | 
21 | 22 | import io  | 
@@ -111,13 +112,29 @@ class InvalidLicenseExpression(Exception):  # type: ignore[no-redef]  | 
111 | 112 | }  | 
112 | 113 | 
 
  | 
113 | 114 | 
 
  | 
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]]]:  | 
115 | 127 |     """Map files to the wheel, organized by wheel installation directory."""  | 
116 | 128 |     wheel_files: DefaultDict[str, List[Tuple[pathlib.Path, str]]] = collections.defaultdict(list)  | 
117 | 129 |     packages: Dict[str, str] = {}  | 
 | 130 | +    excluded = _compile_patterns(exclude)  | 
 | 131 | +    included = _compile_patterns(include)  | 
118 | 132 | 
 
  | 
119 | 133 |     for key, group in sources.items():  | 
120 | 134 |         for src, target in group.items():  | 
 | 135 | +            if excluded(target['destination']) and not included(target['destination']):  | 
 | 136 | +                continue  | 
 | 137 | + | 
121 | 138 |             destination = pathlib.Path(target['destination'])  | 
122 | 139 |             anchor = destination.parts[0]  | 
123 | 140 |             dst = pathlib.Path(*destination.parts[1:])  | 
@@ -581,6 +598,10 @@ def _string_or_path(value: Any, name: str) -> str:  | 
581 | 598 |         'args': _table({  | 
582 | 599 |             name: _strings for name in _MESON_ARGS_KEYS  | 
583 | 600 |         }),  | 
 | 601 | +        'wheel': _table({  | 
 | 602 | +            'exclude': _strings,  | 
 | 603 | +            'include': _strings,  | 
 | 604 | +        }),  | 
584 | 605 |     })  | 
585 | 606 | 
 
  | 
586 | 607 |     table = pyproject.get('tool', {}).get('meson-python', {})  | 
@@ -823,6 +844,10 @@ def __init__(  | 
823 | 844 |         # from the package, make sure the developers acknowledge this.  | 
824 | 845 |         self._allow_windows_shared_libs = pyproject_config.get('allow-windows-internal-shared-libs', False)  | 
825 | 846 | 
 
  | 
 | 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 | + | 
826 | 851 |     def _run(self, cmd: Sequence[str]) -> None:  | 
827 | 852 |         """Invoke a subprocess."""  | 
828 | 853 |         # 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]]]:  | 
906 | 931 |                 sources[key][target] = details  | 
907 | 932 | 
 
  | 
908 | 933 |         # 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)  | 
910 | 935 | 
 
  | 
911 | 936 |     @property  | 
912 | 937 |     def _meson_name(self) -> str:  | 
 | 
0 commit comments