Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions mesonpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely happy with adding this function, but I didn't find a better way.



class Project():
"""Meson project wrapper to generate Python artifacts."""

Expand All @@ -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
Expand Down Expand Up @@ -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')):
Expand Down
26 changes: 26 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: MIT

import argparse
import contextlib
import importlib.metadata
import os
Expand Down Expand Up @@ -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)
2 changes: 1 addition & 1 deletion tests/packages/limited-api/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
3 changes: 3 additions & 0 deletions tests/packages/link-against-local-lib/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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']
5 changes: 1 addition & 4 deletions tests/packages/subproject/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down
13 changes: 11 additions & 2 deletions tests/packages/subproject/subprojects/dep/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
)
2 changes: 1 addition & 1 deletion tests/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
12 changes: 6 additions & 6 deletions tests/test_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))')
Expand Down Expand Up @@ -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]})
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
Loading