Skip to content

Commit 9f92d7e

Browse files
rgommersdnicolodi
authored andcommitted
ENH: make the Meson that is used user-configurable
Allow using a MESON environment variable, analogous to the NINJA one, or a string to an executable or Python script in the `[tool.meson-python]` section of pyproject.toml Closes gh-458
1 parent b4d4240 commit 9f92d7e

File tree

1 file changed

+58
-34
lines changed

1 file changed

+58
-34
lines changed

mesonpy/__init__.py

Lines changed: 58 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,15 @@ def _bool(value: Any, name: str) -> bool:
531531
raise ConfigError(f'Configuration entry "{name}" must be a boolean')
532532
return value
533533

534+
def _string_or_path(value: Any, name: str) -> str:
535+
if not isinstance(value, str):
536+
raise ConfigError(f'Configuration entry "{name}" must be a string')
537+
if os.path.isfile(value):
538+
value = os.path.abspath(value)
539+
return value
540+
534541
scheme = _table({
542+
'meson': _string_or_path,
535543
'limited-api': _bool,
536544
'args': _table({
537545
name: _strings for name in _MESON_ARGS_KEYS
@@ -610,7 +618,22 @@ def __init__( # noqa: C901
610618
self._meson_args: MesonArgs = collections.defaultdict(list)
611619
self._limited_api = False
612620

613-
_check_meson_version()
621+
# load pyproject.toml
622+
pyproject = tomllib.loads(self._source_dir.joinpath('pyproject.toml').read_text())
623+
624+
# load meson args from pyproject.toml
625+
pyproject_config = _validate_pyproject_config(pyproject)
626+
for key, value in pyproject_config.get('args', {}).items():
627+
self._meson_args[key].extend(value)
628+
629+
# meson arguments from the command line take precedence over
630+
# arguments from the configuration file thus are added later
631+
if meson_args:
632+
for key, value in meson_args.items():
633+
self._meson_args[key].extend(value)
634+
635+
# determine command to invoke meson
636+
self._meson = _get_meson_command(pyproject_config.get('meson'))
614637

615638
self._ninja = _env_ninja_command()
616639
if self._ninja is None:
@@ -648,20 +671,6 @@ def __init__( # noqa: C901
648671
self._meson_cross_file.write_text(cross_file_data)
649672
self._meson_args['setup'].extend(('--cross-file', os.fspath(self._meson_cross_file)))
650673

651-
# load pyproject.toml
652-
pyproject = tomllib.loads(self._source_dir.joinpath('pyproject.toml').read_text())
653-
654-
# load meson args from pyproject.toml
655-
pyproject_config = _validate_pyproject_config(pyproject)
656-
for key, value in pyproject_config.get('args', {}).items():
657-
self._meson_args[key].extend(value)
658-
659-
# meson arguments from the command line take precedence over
660-
# arguments from the configuration file thus are added later
661-
if meson_args:
662-
for key, value in meson_args.items():
663-
self._meson_args[key].extend(value)
664-
665674
# write the native file
666675
native_file_data = textwrap.dedent(f'''
667676
[binaries]
@@ -743,7 +752,7 @@ def _configure(self, reconfigure: bool = False) -> None:
743752
]
744753
if reconfigure:
745754
setup_args.insert(0, '--reconfigure')
746-
self._run(['meson', 'setup', *setup_args])
755+
self._run(self._meson + ['setup', *setup_args])
747756

748757
@property
749758
def _build_command(self) -> List[str]:
@@ -753,7 +762,7 @@ def _build_command(self) -> List[str]:
753762
# environment. Using the --ninja-args option allows to
754763
# provide the exact same semantics for the compile arguments
755764
# provided by the users.
756-
cmd = ['meson', 'compile']
765+
cmd = self._meson + ['compile']
757766
args = list(self._meson_args['compile'])
758767
if args:
759768
cmd.append(f'--ninja-args={args!r}')
@@ -827,7 +836,7 @@ def version(self) -> str:
827836
def sdist(self, directory: Path) -> pathlib.Path:
828837
"""Generates a sdist (source distribution) in the specified directory."""
829838
# generate meson dist file
830-
self._run(['meson', 'dist', '--allow-dirty', '--no-tests', '--formats', 'gztar', *self._meson_args['dist']])
839+
self._run(self._meson + ['dist', '--allow-dirty', '--no-tests', '--formats', 'gztar', *self._meson_args['dist']])
831840

832841
# move meson dist file to output path
833842
dist_name = f'{self.name}-{self.version}'
@@ -922,6 +931,37 @@ def _parse_version_string(string: str) -> Tuple[int, ...]:
922931
return (0, )
923932

924933

934+
def _get_meson_command(
935+
meson: Optional[str] = None, *, version: str = _MESON_REQUIRED_VERSION
936+
) -> List[str]:
937+
"""Return the command to invoke meson."""
938+
939+
# The MESON env var, if set, overrides the config value from pyproject.toml.
940+
# The config value, if given, is an absolute path or the name of an executable.
941+
meson = os.environ.get('MESON', meson or 'meson')
942+
943+
# If the specified Meson string ends in `.py`, we run it with the current
944+
# Python executable. This avoids problems for users on Windows, where
945+
# making a script executable isn't enough to get it to run when invoked
946+
# directly. For packages that vendor a forked Meson, the `meson.py` in the
947+
# root of the Meson repo can be used this way.
948+
if meson.endswith('.py'):
949+
cmd = [sys.executable, meson]
950+
else:
951+
cmd = [meson]
952+
953+
# The meson Python package is a dependency of the meson-python Python
954+
# package, however, it may occur that the meson Python package is installed
955+
# but the corresponding meson command is not available in $PATH. Implement
956+
# a runtime check to verify that the build environment is setup correcly.
957+
required_version = _parse_version_string(version)
958+
meson_version = subprocess.run(cmd + ['--version'], check=False, text=True, capture_output=True).stdout
959+
if _parse_version_string(meson_version) < required_version:
960+
raise ConfigError(f'Could not find meson version {version} or newer, found {meson_version}.')
961+
962+
return cmd
963+
964+
925965
def _env_ninja_command(*, version: str = _NINJA_REQUIRED_VERSION) -> Optional[str]:
926966
"""Returns the path to ninja, or None if no ninja found."""
927967
required_version = _parse_version_string(version)
@@ -936,22 +976,6 @@ def _env_ninja_command(*, version: str = _NINJA_REQUIRED_VERSION) -> Optional[st
936976
return None
937977

938978

939-
def _check_meson_version(*, version: str = _MESON_REQUIRED_VERSION) -> None:
940-
"""Check that the meson executable in the path has an appropriate version.
941-
942-
The meson Python package is a dependency of the meson-python
943-
Python package, however, it may occur that the meson Python
944-
package is installed but the corresponding meson command is not
945-
available in $PATH. Implement a runtime check to verify that the
946-
build environment is setup correcly.
947-
948-
"""
949-
required_version = _parse_version_string(version)
950-
meson_version = subprocess.run(['meson', '--version'], check=False, text=True, capture_output=True).stdout
951-
if _parse_version_string(meson_version) < required_version:
952-
raise ConfigError(f'Could not find meson version {version} or newer, found {meson_version}.')
953-
954-
955979
def _add_ignore_files(directory: pathlib.Path) -> None:
956980
directory.joinpath('.gitignore').write_text(textwrap.dedent('''
957981
# This file is generated by meson-python. It will not be recreated if deleted or modified.

0 commit comments

Comments
 (0)