diff --git a/mesonpy/__init__.py b/mesonpy/__init__.py index 928d8acb0..4b11d5a86 100644 --- a/mesonpy/__init__.py +++ b/mesonpy/__init__.py @@ -146,7 +146,6 @@ def _map_to_wheel(sources: Dict[str, Dict[str, Any]], exclude: List[str], include: List[str]) -> DefaultDict[str, List[_Entry]]: """Map files to the wheel, organized by wheel installation directory.""" wheel_files: DefaultDict[str, List[_Entry]] = collections.defaultdict(list) - packages: Dict[str, str] = {} excluded = _compile_patterns(exclude) included = _compile_patterns(include) @@ -165,18 +164,6 @@ def _map_to_wheel(sources: Dict[str, Dict[str, Any]], if path is None: raise BuildError(f'Could not map installation path to an equivalent wheel directory: {str(destination)!r}') - if path == 'purelib' or path == 'platlib': - package = destination.parts[1] - other = packages.setdefault(package, path) - if other != path: - this = os.fspath(pathlib.Path(path, *destination.parts[1:])) - module = next(entry.dst for entry in wheel_files[other] if entry.dst.parts[0] == destination.parts[1]) - that = os.fspath(other / module) - raise BuildError( - f'The {package} package is split between {path} and {other}: ' - f'{this!r} and {that!r}, a "pure: false" argument may be missing in meson.build. ' - f'It is recommended to set it in "import(\'python\').find_installation()"') - if key == 'install_subdirs' or key == 'targets' and os.path.isdir(src): exclude_files = {os.path.normpath(x) for x in target.get('exclude_files', [])} exclude_dirs = {os.path.normpath(x) for x in target.get('exclude_dirs', [])} @@ -208,6 +195,21 @@ def _map_to_wheel(sources: Dict[str, Dict[str, Any]], wheel_files[path].append(_Entry(dst, src, install_rpath, build_rpath)) + # Verify that packages are not split between ``platlib`` and ``purelib``. + if wheel_files.get('purelib') and wheel_files.get('platlib'): + packages: dict[str, tuple[str, pathlib.Path]] = {} + for path in 'purelib', 'platlib': + for entry in wheel_files[path]: + package = entry.dst.parts[0] + other, module = packages.setdefault(package, (path, entry.dst)) + if other != path: + this = os.fspath(path / entry.dst) + that = os.fspath(other / module) + raise BuildError( + f'The {package} package is split between {path} and {other}: ' + f'{this!r} and {that!r}, a "pure: false" argument may be missing in meson.build. ' + f'It is recommended to set it in "import(\'python\').find_installation()"') + return wheel_files diff --git a/tests/packages/install-subdir/meson.build b/tests/packages/install-subdir/meson.build index fc799e2fe..6b1d01e4c 100644 --- a/tests/packages/install-subdir/meson.build +++ b/tests/packages/install-subdir/meson.build @@ -7,21 +7,43 @@ project('install-subdir', version: '1.0.0') py = import('python').find_installation() install_subdir( - 'subdir', - exclude_files: 'excluded.py', - exclude_directories: 'excluded', - install_dir: py.get_install_dir(pure: false), + 'subdir', + exclude_files: 'excluded.py', + exclude_directories: 'excluded', + install_dir: py.get_install_dir(pure: false), ) install_subdir( - 'strip', - strip_directory: true, - exclude_files: 'excluded.py', - install_dir: py.get_install_dir(pure: false) / 'test', + 'strip', + strip_directory: true, + exclude_files: 'excluded.py', + install_dir: py.get_install_dir(pure: false) / 'test', ) install_subdir( - 'nested', - exclude_files: 'deep/excluded.py', - install_dir: py.get_install_dir(pure: false), + 'nested', + exclude_files: 'deep/excluded.py', + install_dir: py.get_install_dir(pure: false), +) + +# The two install_subdir() below are used to verify package split checks in +# presence of install_subdir() that do not result in any file added to the +# wheel. + +# Due to the exclude_files argument, this install_subdir() does not result +# into any file installed, however, this is supported only with Meson 1.1.0 +# and later. For simplicity, leave it out on older Meson versions. +if meson.version().version_compare('>= 1.1') + install_subdir( + 'purelib', + strip_directory: true, + exclude_files: 'excluded.py', + install_dir: py.get_install_dir(pure: true) / 'example', + ) +endif + +install_subdir( + 'platlib', + strip_directory: true, + install_dir: py.get_install_dir(pure: false) / 'example', ) diff --git a/tests/packages/install-subdir/platlib/lib.py b/tests/packages/install-subdir/platlib/lib.py new file mode 100644 index 000000000..775d000c4 --- /dev/null +++ b/tests/packages/install-subdir/platlib/lib.py @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2026 The meson-python developers +# +# SPDX-License-Identifier: MIT diff --git a/tests/packages/install-subdir/purelib/excluded.py b/tests/packages/install-subdir/purelib/excluded.py new file mode 100644 index 000000000..775d000c4 --- /dev/null +++ b/tests/packages/install-subdir/purelib/excluded.py @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2026 The meson-python developers +# +# SPDX-License-Identifier: MIT diff --git a/tests/test_wheel.py b/tests/test_wheel.py index 3fdb14942..41ae5fbe9 100644 --- a/tests/test_wheel.py +++ b/tests/test_wheel.py @@ -441,11 +441,12 @@ def test_install_subdir(wheel_install_subdir): # Meson 1.1.0, see https://github.com/mesonbuild/meson/pull/11432. # Run the test anyway to ensure that meson-python can produce a # wheel also for older versions of Meson. - if MESON_VERSION >= (1, 1, 99): + if MESON_VERSION >= (1, 1, 0): assert set(artifact.namelist()) == { 'install_subdir-1.0.0.dist-info/METADATA', 'install_subdir-1.0.0.dist-info/RECORD', 'install_subdir-1.0.0.dist-info/WHEEL', + 'example/lib.py', 'subdir/__init__.py', 'subdir/test.py', 'test/module.py',