@@ -76,7 +76,8 @@ def __init__(self, name: str, environment: Environment, kwargs: T.Dict[str, T.An
7676
7777class BasicPythonExternalProgram (ExternalProgram ):
7878 def __init__ (self , name : str , command : T .Optional [T .List [str ]] = None ,
79- ext_prog : T .Optional [ExternalProgram ] = None ):
79+ ext_prog : T .Optional [ExternalProgram ] = None ,
80+ build_config_path : T .Optional [str ] = None ):
8081 if ext_prog is None :
8182 super ().__init__ (name , command = command , silent = True )
8283 else :
@@ -86,6 +87,15 @@ def __init__(self, name: str, command: T.Optional[T.List[str]] = None,
8687 self .cached_version = None
8788 self .version_arg = '--version'
8889
90+ self .build_config = None
91+
92+ if build_config_path :
93+ try :
94+ with open (build_config_path , encoding = 'utf8' ) as f :
95+ self .build_config = json .load (f )
96+ except OSError as e :
97+ mlog .warning (f'Failed to read python.build_config: { e } ' )
98+
8999 # We want strong key values, so we always populate this with bogus data.
90100 # Otherwise to make the type checkers happy we'd have to do .get() for
91101 # everycall, even though we know that the introspection data will be
@@ -116,6 +126,14 @@ def _check_version(self, version: str) -> bool:
116126 def sanity (self ) -> bool :
117127 # Sanity check, we expect to have something that at least quacks in tune
118128
129+ if self .build_config :
130+ if not self .build_config ['libpython' ]:
131+ mlog .debug ('This Python installation does not provide a libpython' )
132+ return False
133+ if not self .build_config ['c_api' ]:
134+ mlog .debug ('This Python installation does support the C API' )
135+ return False
136+
119137 import importlib .resources
120138
121139 with importlib .resources .path ('mesonbuild.scripts' , 'python_info.py' ) as f :
@@ -145,12 +163,24 @@ class _PythonDependencyBase(_Base):
145163
146164 def __init__ (self , python_holder : 'BasicPythonExternalProgram' , embed : bool ):
147165 self .embed = embed
148- self .version : str = python_holder .info ['version' ]
149- self .platform = python_holder .info ['platform' ]
150- self .variables = python_holder .info ['variables' ]
166+ self .build_config = python_holder .build_config
167+
168+ if self .build_config :
169+ self .version = self .build_config ['language' ]['version' ]
170+ self .platform = self .build_config ['platform' ]
171+ self .is_freethreaded = 't' in self .build_config ['abi' ]['flags' ]
172+ self .link_libpython = self .build_config ['libpython' ]['link_extensions' ]
173+ else :
174+ self .version = python_holder .info ['version' ]
175+ self .platform = python_holder .info ['platform' ]
176+ self .is_freethreaded = python_holder .info ['is_freethreaded' ]
177+ self .link_libpython = python_holder .info ['link_libpython' ]
178+ # This data shouldn't be needed when build_config is set
179+ self .is_pypy = python_holder .info ['is_pypy' ]
180+ self .variables = python_holder .info ['variables' ]
181+
151182 self .paths = python_holder .info ['paths' ]
152- self .is_pypy = python_holder .info ['is_pypy' ]
153- self .is_freethreaded = python_holder .info ['is_freethreaded' ]
183+
154184 # The "-embed" version of python.pc / python-config was introduced in 3.8,
155185 # and distutils extension linking was changed to be considered a non embed
156186 # usage. Before then, this dependency always uses the embed=True handling
@@ -159,7 +189,9 @@ def __init__(self, python_holder: 'BasicPythonExternalProgram', embed: bool):
159189 # On macOS and some Linux distros (Debian) distutils doesn't link extensions
160190 # against libpython, even on 3.7 and below. We call into distutils and
161191 # mirror its behavior. See https://github.com/mesonbuild/meson/issues/4117
162- self .link_libpython = python_holder .info ['link_libpython' ] or embed
192+ if not self .link_libpython :
193+ self .link_libpython = embed
194+
163195 self .info : T .Optional [T .Dict [str , str ]] = None
164196 if mesonlib .version_compare (self .version , '>= 3.0' ):
165197 self .major_version = 3
@@ -173,20 +205,27 @@ def __init__(self, python_holder: 'BasicPythonExternalProgram', embed: bool):
173205 self .compile_args += ['-DPy_GIL_DISABLED' ]
174206
175207 def find_libpy (self , environment : 'Environment' ) -> None :
176- if self .is_pypy :
177- if self .major_version == 3 :
178- libname = 'pypy3-c'
179- else :
180- libname = 'pypy-c'
181- libdir = os .path .join (self .variables .get ('base' ), 'bin' )
182- libdirs = [libdir ]
208+ if self .build_config :
209+ path = self .build_config ['libpython' ].get ('dynamic' )
210+ if not path :
211+ raise DependencyException ('Python does not provide a dynamic libpython library' )
212+ libdirs = [os .path .dirname (path )]
213+ libname = os .path .basename (path )
183214 else :
184- libname = f'python{ self .version } '
185- if 'DEBUG_EXT' in self .variables :
186- libname += self .variables ['DEBUG_EXT' ]
187- if 'ABIFLAGS' in self .variables :
188- libname += self .variables ['ABIFLAGS' ]
189- libdirs = []
215+ if self .is_pypy :
216+ if self .major_version == 3 :
217+ libname = 'pypy3-c'
218+ else :
219+ libname = 'pypy-c'
220+ libdir = os .path .join (self .variables .get ('base' ), 'bin' )
221+ libdirs = [libdir ]
222+ else :
223+ libname = f'python{ self .version } '
224+ if 'DEBUG_EXT' in self .variables :
225+ libname += self .variables ['DEBUG_EXT' ]
226+ if 'ABIFLAGS' in self .variables :
227+ libname += self .variables ['ABIFLAGS' ]
228+ libdirs = []
190229
191230 largs = self .clib_compiler .find_library (libname , environment , libdirs )
192231 if largs is not None :
@@ -212,6 +251,15 @@ def get_windows_python_arch(self) -> str:
212251 raise DependencyException ('Unknown Windows Python platform {self.platform!r}' )
213252
214253 def get_windows_link_args (self , limited_api : bool ) -> T .Optional [T .List [str ]]:
254+ if self .build_config :
255+ if self .static :
256+ key = 'static'
257+ elif limited_api :
258+ key = 'dynamic-stableabi'
259+ else :
260+ key = 'dynamic'
261+ return [self .build_config ['libpython' ][key ]]
262+
215263 if self .platform .startswith ('win' ):
216264 vernum = self .variables .get ('py_version_nodot' )
217265 verdot = self .variables .get ('py_version_short' )
@@ -354,10 +402,13 @@ def __init__(self, name: str, environment: 'Environment',
354402 self .is_found = True
355403
356404 # compile args
357- inc_paths = mesonlib .OrderedSet ([
358- self .variables .get ('INCLUDEPY' ),
359- self .paths .get ('include' ),
360- self .paths .get ('platinclude' )])
405+ if self .build_config :
406+ inc_paths = mesonlib .OrderedSet ([self .build_config ['c_api' ]['headers' ]])
407+ else :
408+ inc_paths = mesonlib .OrderedSet ([
409+ self .variables .get ('INCLUDEPY' ),
410+ self .paths .get ('include' ),
411+ self .paths .get ('platinclude' )])
361412
362413 self .compile_args += ['-I' + path for path in inc_paths if path ]
363414
@@ -386,11 +437,18 @@ def python_factory(env: 'Environment', for_machine: 'MachineChoice',
386437 if installation is None :
387438 installation = BasicPythonExternalProgram ('python3' , mesonlib .python_command )
388439 installation .sanity ()
389- pkg_version = installation .info ['variables' ].get ('LDVERSION' ) or installation .info ['version' ]
440+
441+ if installation .build_config :
442+ pkg_version = installation .build_config ['language' ]['version' ]
443+ else :
444+ pkg_version = installation .info ['variables' ].get ('LDVERSION' ) or installation .info ['version' ]
390445
391446 if DependencyMethods .PKGCONFIG in methods :
392447 if from_installation :
393- pkg_libdir = installation .info ['variables' ].get ('LIBPC' )
448+ if installation .build_config :
449+ pkg_libdir = installation .build_config ['c_api' ]['pkgconfig_path' ]
450+ else :
451+ pkg_libdir = installation .info ['variables' ].get ('LIBPC' )
394452 pkg_embed = '-embed' if embed and mesonlib .version_compare (installation .info ['version' ], '>=3.8' ) else ''
395453 pkg_name = f'python-{ pkg_version } { pkg_embed } '
396454
0 commit comments