diff --git a/mesonpy/__init__.py b/mesonpy/__init__.py index 77b0be327..546f8723a 100644 --- a/mesonpy/__init__.py +++ b/mesonpy/__init__.py @@ -659,6 +659,14 @@ def _string_or_strings(value: Any, name: str) -> List[str]: return config +def _get_meson_args(args: Dict[str, List[str]]) -> MesonArgs: + # This mostly exists so that it can be monkeypatched in tests + meson_args: MesonArgs = collections.defaultdict(list) + for key, value in args.items(): + meson_args[key].extend(value) # type: ignore[index] + return meson_args + + class Project(): """Meson project wrapper to generate Python artifacts.""" @@ -674,16 +682,14 @@ def __init__( self._editable_verbose = editable_verbose 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) self._limited_api = False # load pyproject.toml pyproject = tomllib.loads(self._source_dir.joinpath('pyproject.toml').read_text(encoding='utf-8')) - - # load meson args from pyproject.toml pyproject_config = _validate_pyproject_config(pyproject) - for key, value in pyproject_config.get('args', {}).items(): - self._meson_args[key].extend(value) + + # get meson args from pyproject.toml + self._meson_args = _get_meson_args(pyproject_config.get('args', {})) # meson arguments from the command line take precedence over # arguments from the configuration file thus are added later @@ -838,8 +844,8 @@ def __init__( if self._limited_api: # check whether limited API is disabled for the Meson project options = self._info('intro-buildoptions') - value = next((option['value'] for option in options if option['name'] == 'python.allow_limited_api'), None) - if not value: + allow_limited_api = next((opt['value'] for opt in options if opt['name'] == 'python.allow_limited_api'), None) + if not allow_limited_api: self._limited_api = False if self._limited_api and bool(sysconfig.get_config_var('Py_GIL_DISABLED')): diff --git a/tests/conftest.py b/tests/conftest.py index af737968c..f069ef714 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,6 +2,7 @@ # # SPDX-License-Identifier: MIT +import argparse import contextlib import importlib.metadata import os @@ -192,3 +193,28 @@ def cleanenv(): # $MACOSX_DEPLOYMENT_TARGET affects the computation of the platform tag on macOS. yield mpatch.delenv('MACOSX_DEPLOYMENT_TARGET', raising=False) mpatch.undo() + + +@pytest.fixture(autouse=True, scope='session') +def meson_fatal_warnings(): + # Cannot use the 'monkeypatch' fixture because of scope mismatch. + mpatch = pytest.MonkeyPatch() + mesonpy_get_meson_args = mesonpy._get_meson_args + + def _get_meson_args(args): + meson_args = mesonpy_get_meson_args(args) + meson_setup_args = meson_args['setup'] + + # Add ``--fatal-meson-warnings`` to the ``meson setup`` arguments + # unless the project specifies ``--no-fatal-meson-warnings`` in + # ``tool.meson-build.args.setup``. + parser = argparse.ArgumentParser(add_help=False) + parser.add_argument('--no-fatal-meson-warnings', action='store_true') + args, meson_setup_args = parser.parse_known_args(meson_setup_args) + if not args.no_fatal_meson_warnings: + meson_setup_args.append('--fatal-meson-warnings') + + meson_args['setup'] = meson_setup_args + return meson_args + + mpatch.setattr(mesonpy, '_get_meson_args', _get_meson_args) diff --git a/tests/packages/limited-api/meson.build b/tests/packages/limited-api/meson.build index 4973c6f14..606a12242 100644 --- a/tests/packages/limited-api/meson.build +++ b/tests/packages/limited-api/meson.build @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: MIT -project('limited-api', 'c', version: '1.0.0') +project('limited-api', 'c', version: '1.0.0', meson_version: '>= 1.3') py = import('python').find_installation(pure: false) diff --git a/tests/packages/link-against-local-lib/pyproject.toml b/tests/packages/link-against-local-lib/pyproject.toml index ef8c7eed6..88faeaa37 100644 --- a/tests/packages/link-against-local-lib/pyproject.toml +++ b/tests/packages/link-against-local-lib/pyproject.toml @@ -8,3 +8,6 @@ requires = ['meson-python'] [tool.meson-python] allow-windows-internal-shared-libs = true + +[tool.meson-python.args] +setup = ['--no-fatal-meson-warnings'] diff --git a/tests/packages/subproject/pyproject.toml b/tests/packages/subproject/pyproject.toml index d339a1bb7..cf1dd4a82 100644 --- a/tests/packages/subproject/pyproject.toml +++ b/tests/packages/subproject/pyproject.toml @@ -8,10 +8,7 @@ requires = ['meson-python'] [tool.meson-python.wheel] exclude = [ - # Meson before version 1.3.0 install data files in - # ``{datadir}/{project name}/``, later versions install - # in the more correct ``{datadir}/{subproject name}/``. - '{datadir}/*/data.txt', + '{datadir}/dep/*.txt', '{py_purelib}/dep.*', ] include = [ diff --git a/tests/packages/subproject/subprojects/dep/meson.build b/tests/packages/subproject/subprojects/dep/meson.build index 34c6b7508..cc7c5c029 100644 --- a/tests/packages/subproject/subprojects/dep/meson.build +++ b/tests/packages/subproject/subprojects/dep/meson.build @@ -6,6 +6,15 @@ project('dep') py = import('python').find_installation() -py.install_sources('dep.py') +py.install_sources( + 'dep.py', +) -install_data('data.txt') +install_data( + 'data.txt', + # Prior to meson 1.3.0, the default install path for subproject data files + # is ``{datadir}/{main-project-name}/``. In later versions it is the more + # correct ``{datadir}/{subproject-name}/``. Specify the install path to + # obtain uniform behavior across different meson versions. + install_dir: '{datadir}' / meson.project_name(), +) diff --git a/tests/test_project.py b/tests/test_project.py index 11b229ab7..b511b2171 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -399,7 +399,7 @@ def test_ios_project(package_simple, monkeypatch, multiarch, tmp_path): project = mesonpy.Project(source_dir=package_simple, build_dir=tmp_path) # Meson configuration points at the cross file - assert project._meson_args['setup'] == ['--cross-file', os.fspath(tmp_path / 'meson-python-cross-file.ini')] + assert project._meson_args['setup'][-2:] == ['--cross-file', os.fspath(tmp_path / 'meson-python-cross-file.ini')] # Meson config files exist, and have some relevant keys assert (tmp_path / 'meson-python-native-file.ini').exists() diff --git a/tests/test_wheel.py b/tests/test_wheel.py index 350798e58..e3c935599 100644 --- a/tests/test_wheel.py +++ b/tests/test_wheel.py @@ -186,7 +186,7 @@ def test_sharedlib_in_package(venv, wheel_sharedlib_in_package): assert int(output) == 42 -@pytest.mark.skipif(MESON_VERSION < (1, 3, 0), reason='Meson version too old') +@pytest.mark.skipif(MESON_VERSION < (1, 3, 0), reason='meson too old') def test_link_library_in_subproject(venv, wheel_link_library_in_subproject): venv.pip('install', wheel_link_library_in_subproject) output = venv.python('-c', 'import foo; print(foo.example_sum(3, 6))') @@ -306,7 +306,7 @@ def test_subprojects(package_subproject, tmp_path): # Requires Meson 1.2.0, see https://github.com/mesonbuild/meson/pull/11909. -@pytest.mark.skipif(MESON_VERSION < (1, 1, 99), reason='Meson version too old') +@pytest.mark.skipif(MESON_VERSION < (1, 1, 99), reason='meson too old') @pytest.mark.parametrize(('arg'), ['--skip-subprojects', '--skip-subprojects=dep']) def test_skip_subprojects(package_subproject, tmp_path, arg): filename = mesonpy.build_wheel(tmp_path, {'install-args': [arg]}) @@ -320,7 +320,7 @@ def test_skip_subprojects(package_subproject, tmp_path, arg): # Requires Meson 1.3.0, see https://github.com/mesonbuild/meson/pull/11745. -@pytest.mark.skipif(MESON_VERSION < (1, 2, 99), reason='Meson version too old') +@pytest.mark.skipif(MESON_VERSION < (1, 2, 99), reason='meson too old') @pytest.mark.skipif(NOGIL_BUILD, reason='Free-threaded CPython does not support the limited API') @pytest.mark.xfail('__pypy__' in sys.builtin_module_names, reason='PyPy does not support the abi3 platform tag for wheels') def test_limited_api(wheel_limited_api): @@ -332,7 +332,7 @@ def test_limited_api(wheel_limited_api): # Requires Meson 1.3.0, see https://github.com/mesonbuild/meson/pull/11745. -@pytest.mark.skipif(MESON_VERSION < (1, 2, 99), reason='Meson version too old') +@pytest.mark.skipif(MESON_VERSION < (1, 2, 99), reason='meson too old') @pytest.mark.skipif(NOGIL_BUILD, reason='Free-threaded CPython does not support the limited API') @pytest.mark.xfail('__pypy__' in sys.builtin_module_names, reason='PyPy does not use special modules suffix for stable ABI') def test_limited_api_bad(package_limited_api, tmp_path): @@ -342,7 +342,7 @@ def test_limited_api_bad(package_limited_api, tmp_path): # Requires Meson 1.3.0, see https://github.com/mesonbuild/meson/pull/11745. -@pytest.mark.skipif(MESON_VERSION < (1, 2, 99), reason='Meson version too old') +@pytest.mark.skipif(MESON_VERSION < (1, 2, 99), reason='meson too old') def test_limited_api_disabled(package_limited_api, tmp_path): filename = mesonpy.build_wheel(tmp_path, {'setup-args': ['-Dpython.allow_limited_api=false']}) artifact = wheel.wheelfile.WheelFile(tmp_path / filename) @@ -401,7 +401,7 @@ def test_custom_target_install_dir(package_custom_target_dir, tmp_path): } # On Linux, Meson 1.10 or later is required, see https://github.com/mesonbuild/meson/pull/15141 -@pytest.mark.skipif(sys.platform == 'linux' and MESON_VERSION < (1, 9, 99), reason='Meson version too old') +@pytest.mark.skipif(sys.platform == 'linux' and MESON_VERSION < (1, 9, 99), reason='meson too old') @pytest.mark.skipif(sys.platform not in {'linux', 'darwin'}, reason='Not supported on this platform') def test_cmake_subproject(wheel_cmake_subproject): artifact = wheel.wheelfile.WheelFile(wheel_cmake_subproject)