@@ -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+
925965def _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-
955979def _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