@@ -531,7 +531,15 @@ def _bool(value: Any, name: str) -> bool:
531
531
raise ConfigError (f'Configuration entry "{ name } " must be a boolean' )
532
532
return value
533
533
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
+
534
541
scheme = _table ({
542
+ 'meson' : _string_or_path ,
535
543
'limited-api' : _bool ,
536
544
'args' : _table ({
537
545
name : _strings for name in _MESON_ARGS_KEYS
@@ -610,7 +618,22 @@ def __init__( # noqa: C901
610
618
self ._meson_args : MesonArgs = collections .defaultdict (list )
611
619
self ._limited_api = False
612
620
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' ))
614
637
615
638
self ._ninja = _env_ninja_command ()
616
639
if self ._ninja is None :
@@ -648,20 +671,6 @@ def __init__( # noqa: C901
648
671
self ._meson_cross_file .write_text (cross_file_data )
649
672
self ._meson_args ['setup' ].extend (('--cross-file' , os .fspath (self ._meson_cross_file )))
650
673
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
-
665
674
# write the native file
666
675
native_file_data = textwrap .dedent (f'''
667
676
[binaries]
@@ -743,7 +752,7 @@ def _configure(self, reconfigure: bool = False) -> None:
743
752
]
744
753
if reconfigure :
745
754
setup_args .insert (0 , '--reconfigure' )
746
- self ._run ([ 'meson' , 'setup' , * setup_args ])
755
+ self ._run (self . _meson + [ 'setup' , * setup_args ])
747
756
748
757
@property
749
758
def _build_command (self ) -> List [str ]:
@@ -753,7 +762,7 @@ def _build_command(self) -> List[str]:
753
762
# environment. Using the --ninja-args option allows to
754
763
# provide the exact same semantics for the compile arguments
755
764
# provided by the users.
756
- cmd = [ 'meson' , 'compile' ]
765
+ cmd = self . _meson + [ 'compile' ]
757
766
args = list (self ._meson_args ['compile' ])
758
767
if args :
759
768
cmd .append (f'--ninja-args={ args !r} ' )
@@ -827,7 +836,7 @@ def version(self) -> str:
827
836
def sdist (self , directory : Path ) -> pathlib .Path :
828
837
"""Generates a sdist (source distribution) in the specified directory."""
829
838
# 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' ]])
831
840
832
841
# move meson dist file to output path
833
842
dist_name = f'{ self .name } -{ self .version } '
@@ -922,6 +931,37 @@ def _parse_version_string(string: str) -> Tuple[int, ...]:
922
931
return (0 , )
923
932
924
933
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
+
925
965
def _env_ninja_command (* , version : str = _NINJA_REQUIRED_VERSION ) -> Optional [str ]:
926
966
"""Returns the path to ninja, or None if no ninja found."""
927
967
required_version = _parse_version_string (version )
@@ -936,22 +976,6 @@ def _env_ninja_command(*, version: str = _NINJA_REQUIRED_VERSION) -> Optional[st
936
976
return None
937
977
938
978
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
-
955
979
def _add_ignore_files (directory : pathlib .Path ) -> None :
956
980
directory .joinpath ('.gitignore' ).write_text (textwrap .dedent ('''
957
981
# This file is generated by meson-python. It will not be recreated if deleted or modified.
0 commit comments