diff --git a/meson.build b/meson.build index 0fe1f1b89..df9d466f1 100644 --- a/meson.build +++ b/meson.build @@ -17,7 +17,6 @@ py.install_sources( 'mesonpy/_dylib.py', 'mesonpy/_editable.py', 'mesonpy/_elf.py', - 'mesonpy/_introspection.py', 'mesonpy/_tags.py', 'mesonpy/_util.py', 'mesonpy/_wheelfile.py', diff --git a/mesonpy/__init__.py b/mesonpy/__init__.py index 594632d5f..80843d059 100644 --- a/mesonpy/__init__.py +++ b/mesonpy/__init__.py @@ -48,7 +48,6 @@ import mesonpy._compat import mesonpy._dylib import mesonpy._elf -import mesonpy._introspection import mesonpy._tags import mesonpy._util import mesonpy._wheelfile @@ -628,16 +627,13 @@ class Project(): def __init__( self, source_dir: Path, - working_dir: Path, - build_dir: Optional[Path] = None, + build_dir: Path, meson_args: Optional[MesonArgs] = None, editable_verbose: bool = False, ) -> None: self._source_dir = pathlib.Path(source_dir).absolute() - self._working_dir = pathlib.Path(working_dir).absolute() - self._build_dir = pathlib.Path(build_dir).absolute() if build_dir else (self._working_dir / 'build') + self._build_dir = pathlib.Path(build_dir).absolute() self._editable_verbose = editable_verbose - self._install_dir = self._working_dir / 'install' self._meson_native_file = self._build_dir / 'meson-python-native-file.ini' self._meson_cross_file = self._build_dir / 'meson-python-cross-file.ini' self._meson_args: MesonArgs = collections.defaultdict(list) @@ -692,7 +688,6 @@ def __init__( # make sure the build dir exists self._build_dir.mkdir(exist_ok=True, parents=True) - self._install_dir.mkdir(exist_ok=True, parents=True) # write the native file native_file_data = textwrap.dedent(f''' @@ -737,26 +732,15 @@ def _run(self, cmd: Sequence[str]) -> None: def _configure(self, reconfigure: bool = False) -> None: """Configure Meson project.""" - sys_paths = mesonpy._introspection.SYSCONFIG_PATHS setup_args = [ - f'--prefix={sys.base_prefix}', os.fspath(self._source_dir), os.fspath(self._build_dir), f'--native-file={os.fspath(self._meson_native_file)}', - # TODO: Allow configuring these arguments + # default arguments '-Ddebug=false', '-Db_ndebug=if-release', '-Doptimization=2', - - # XXX: This should not be needed, but Meson is using the wrong paths - # in some scenarios, like on macOS. - # https://github.com/mesonbuild/meson-python/pull/87#discussion_r1047041306 - '--python.purelibdir', - sys_paths['purelib'], - '--python.platlibdir', - sys_paths['platlib'], - - # user args + # user arguments *self._meson_args['setup'], ] if reconfigure: @@ -795,38 +779,11 @@ def _wheel_builder(self) -> _WheelBuilder: self._install_plan, ) - def build_commands(self, install_dir: Optional[pathlib.Path] = None) -> Sequence[Sequence[str]]: - assert self._ninja is not None # help mypy out - return ( - (self._ninja, *self._meson_args['compile'],), - ( - 'meson', - 'install', - '--only-changed', - '--destdir', - os.fspath(install_dir or self._install_dir), - *self._meson_args['install'], - ), - ) - @functools.lru_cache(maxsize=None) def build(self) -> None: - """Trigger the Meson build.""" - for cmd in self.build_commands(): - self._run(cmd) - - @classmethod - @contextlib.contextmanager - def with_temp_working_dir( - cls, - source_dir: Path = os.path.curdir, - build_dir: Optional[Path] = None, - meson_args: Optional[MesonArgs] = None, - editable_verbose: bool = False, - ) -> Iterator[Project]: - """Creates a project instance pointing to a temporary working directory.""" - with tempfile.TemporaryDirectory(prefix='.mesonpy-', dir=os.fspath(source_dir)) as tmpdir: - yield cls(source_dir, tmpdir, build_dir, meson_args, editable_verbose) + """Build Meson project.""" + assert self._ninja is not None # help mypy + self._run([self._ninja, *self._meson_args['compile']]) @functools.lru_cache() def _info(self, name: str) -> Dict[str, Any]: @@ -975,18 +932,19 @@ def editable(self, directory: Path) -> pathlib.Path: @contextlib.contextmanager -def _project(config_settings: Optional[Dict[Any, Any]]) -> Iterator[Project]: +def _project(config_settings: Optional[Dict[Any, Any]] = None) -> Iterator[Project]: """Create the project given the given config settings.""" settings = _validate_config_settings(config_settings or {}) - meson_args = {name: settings.get(f'{name}-args', []) for name in _MESON_ARGS_KEYS} - - with Project.with_temp_working_dir( - build_dir=settings.get('builddir'), - meson_args=typing.cast(MesonArgs, meson_args), - editable_verbose=bool(settings.get('editable-verbose')) - ) as project: - yield project + meson_args = typing.cast(MesonArgs, {name: settings.get(f'{name}-args', []) for name in _MESON_ARGS_KEYS}) + source_dir = os.path.curdir + build_dir = settings.get('builddir') + editable_verbose = bool(settings.get('editable-verbose')) + + with contextlib.ExitStack() as ctx: + if build_dir is None: + build_dir = ctx.enter_context(tempfile.TemporaryDirectory(prefix='.mesonpy-', dir=source_dir)) + yield Project(source_dir, build_dir, meson_args, editable_verbose) def _parse_version_string(string: str) -> Tuple[int, ...]: diff --git a/mesonpy/_introspection.py b/mesonpy/_introspection.py deleted file mode 100644 index 1681528e4..000000000 --- a/mesonpy/_introspection.py +++ /dev/null @@ -1,27 +0,0 @@ -# SPDX-FileCopyrightText: 2022 The meson-python developers -# -# SPDX-License-Identifier: MIT - -from __future__ import annotations - -import sys -import sysconfig -import typing - - -if typing.TYPE_CHECKING: # pragma: no cover - from mesonpy._compat import Mapping - - -def sysconfig_paths() -> Mapping[str, str]: - sys_vars = sysconfig.get_config_vars().copy() - sys_vars['base'] = sys_vars['platbase'] = sys.base_prefix - return sysconfig.get_paths(vars=sys_vars) - - -SYSCONFIG_PATHS = sysconfig_paths() - - -__all__ = [ - 'SYSCONFIG_PATHS', -] diff --git a/tests/test_editable.py b/tests/test_editable.py index 10671304d..c0ac4f495 100644 --- a/tests/test_editable.py +++ b/tests/test_editable.py @@ -61,13 +61,12 @@ def test_collect(package_complex): def test_mesonpy_meta_finder(package_complex, tmp_build_path): # build a package in a temporary directory mesonpy.Project(package_complex, tmp_build_path) - build_path = tmp_build_path / 'build' # point the meta finder to the build directory - finder = _editable.MesonpyMetaFinder({'complex'}, os.fspath(build_path), ['ninja']) + finder = _editable.MesonpyMetaFinder({'complex'}, os.fspath(tmp_build_path), ['ninja']) # check repr - assert repr(finder) == f'MesonpyMetaFinder({str(build_path)!r})' + assert repr(finder) == f'MesonpyMetaFinder({str(tmp_build_path)!r})' # verify that we can look up a pure module in the source directory spec = finder.find_spec('complex') @@ -79,7 +78,7 @@ def test_mesonpy_meta_finder(package_complex, tmp_build_path): spec = finder.find_spec('complex.test') assert spec.name == 'complex.test' assert isinstance(spec.loader, _editable.ExtensionFileLoader) - assert spec.origin == os.fspath(build_path / f'test{EXT_SUFFIX}') + assert spec.origin == os.fspath(tmp_build_path / f'test{EXT_SUFFIX}') try: # install the finder in the meta path @@ -89,7 +88,7 @@ def test_mesonpy_meta_finder(package_complex, tmp_build_path): assert complex.__spec__.origin == os.fspath(package_complex / 'complex/__init__.py') assert complex.__file__ == os.fspath(package_complex / 'complex/__init__.py') import complex.test - assert complex.test.__spec__.origin == os.fspath(build_path / f'test{EXT_SUFFIX}') + assert complex.test.__spec__.origin == os.fspath(tmp_build_path / f'test{EXT_SUFFIX}') assert complex.test.answer() == 42 import complex.namespace.foo assert complex.namespace.foo.__spec__.origin == os.fspath(package_complex / 'complex/namespace/foo.py') @@ -128,7 +127,7 @@ def test_resources(tmp_path): mesonpy.Project(package_path, tmp_path) # point the meta finder to the build directory - finder = _editable.MesonpyMetaFinder({'simple'}, os.fspath(tmp_path / 'build'), ['ninja']) + finder = _editable.MesonpyMetaFinder({'simple'}, os.fspath(tmp_path), ['ninja']) # verify that we can look up resources spec = finder.find_spec('simple') @@ -147,7 +146,7 @@ def test_importlib_resources(tmp_path): mesonpy.Project(package_path, tmp_path) # point the meta finder to the build directory - finder = _editable.MesonpyMetaFinder({'simple'}, os.fspath(tmp_path / 'build'), ['ninja']) + finder = _editable.MesonpyMetaFinder({'simple'}, os.fspath(tmp_path), ['ninja']) try: # install the finder in the meta path diff --git a/tests/test_project.py b/tests/test_project.py index 4c68a50c0..1562ae05e 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -28,7 +28,7 @@ ] ) def test_name(package): - with chdir(package_dir / package), mesonpy.Project.with_temp_working_dir() as project: + with chdir(package_dir / package), mesonpy._project() as project: assert project.name == package.replace('-', '_') @@ -40,13 +40,13 @@ def test_name(package): ] ) def test_version(package): - with chdir(package_dir / package), mesonpy.Project.with_temp_working_dir() as project: + with chdir(package_dir / package), mesonpy._project() as project: assert project.version == '1.0.0' def test_unsupported_dynamic(package_unsupported_dynamic): with pytest.raises(mesonpy.MesonBuilderError, match='Unsupported dynamic fields: "dependencies"'): - with mesonpy.Project.with_temp_working_dir(): + with mesonpy._project(): pass @@ -54,7 +54,7 @@ def test_unsupported_python_version(package_unsupported_python_version): with pytest.raises(mesonpy.MesonBuilderError, match=( f'Unsupported Python version {platform.python_version()}, expected ==1.0.0' )): - with mesonpy.Project.with_temp_working_dir(): + with mesonpy._project(): pass @@ -76,7 +76,7 @@ def last_two_meson_args(): 'dist-args': ('cli-dist',), 'setup-args': ('cli-setup',), 'compile-args': ('cli-compile',), - 'install-args': ('cli-install',), + 'install-args': ('cli-install',), # 'meson install' is not called thus we cannot test this } mesonpy.build_sdist(tmp_path, config_settings) @@ -86,10 +86,9 @@ def last_two_meson_args(): # sdist: calls to 'meson setup' and 'meson dist' ('config-setup', 'cli-setup'), ('config-dist', 'cli-dist'), - # wheel: calls to 'meson setup', 'meson compile', and 'meson install' + # wheel: calls to 'meson setup' and 'ninja' ('config-setup', 'cli-setup'), ('config-compile', 'cli-compile'), - ('config-install', 'cli-install'), ] @@ -174,7 +173,7 @@ def test_invalid_build_dir(package_pure, tmp_path, mocker): meson.reset_mock() # corrupting the build direcory setup is run again - tmp_path.joinpath('build/meson-private/coredata.dat').unlink() + tmp_path.joinpath('meson-private/coredata.dat').unlink() project = mesonpy.Project(package_pure, tmp_path) assert len(meson.call_args_list) == 1 assert meson.call_args_list[0].args[1][1] == 'setup' @@ -183,7 +182,7 @@ def test_invalid_build_dir(package_pure, tmp_path, mocker): meson.reset_mock() # removing the build directory things should still work - shutil.rmtree(tmp_path.joinpath('build')) + shutil.rmtree(tmp_path) project = mesonpy.Project(package_pure, tmp_path) assert len(meson.call_args_list) == 1 assert meson.call_args_list[0].args[1][1] == 'setup' diff --git a/tests/test_wheel.py b/tests/test_wheel.py index be2d4b0ae..e7afdb30a 100644 --- a/tests/test_wheel.py +++ b/tests/test_wheel.py @@ -232,7 +232,7 @@ def test_entrypoints(wheel_full_metadata): def test_top_level_modules(package_module_types): - with mesonpy.Project.with_temp_working_dir() as project: + with mesonpy._project() as project: assert set(project._wheel_builder.top_level_modules) == { 'file', 'package',