@ -16,7 +16,6 @@ from __future__ import annotations
from pathlib import Path
from pathlib import Path
import copy
import copy
import functools
import functools
import json
import os
import os
import shutil
import shutil
import typing as T
import typing as T
@ -30,6 +29,7 @@ from ..dependencies import (DependencyMethods, PkgConfigDependency, NotFoundDepe
DependencyTypeName , ExternalDependency )
DependencyTypeName , ExternalDependency )
from . . dependencies . base import process_method_kw
from . . dependencies . base import process_method_kw
from . . dependencies . detect import get_dep_identifier
from . . dependencies . detect import get_dep_identifier
from . . dependencies . python import BasicPythonExternalProgram
from . . environment import detect_cpu_family
from . . environment import detect_cpu_family
from . . interpreter import ExternalProgramHolder , extract_required_kwarg , permitted_dependency_kwargs
from . . interpreter import ExternalProgramHolder , extract_required_kwarg , permitted_dependency_kwargs
from . . interpreter import primitives as P_OBJ
from . . interpreter import primitives as P_OBJ
@ -54,19 +54,6 @@ if T.TYPE_CHECKING:
from . . interpreter . kwargs import ExtractRequired
from . . interpreter . kwargs import ExtractRequired
from . . interpreterbase . interpreterbase import TYPE_var , TYPE_kwargs
from . . interpreterbase . interpreterbase import TYPE_var , TYPE_kwargs
class PythonIntrospectionDict ( TypedDict ) :
install_paths : T . Dict [ str , str ]
is_pypy : bool
is_venv : bool
link_libpython : bool
sysconfig_paths : T . Dict [ str , str ]
paths : T . Dict [ str , str ]
platform : str
suffix : str
variables : T . Dict [ str , str ]
version : str
class PyInstallKw ( TypedDict ) :
class PyInstallKw ( TypedDict ) :
pure : T . Optional [ bool ]
pure : T . Optional [ bool ]
@ -91,7 +78,7 @@ mod_kwargs -= {'name_prefix', 'name_suffix'}
class _PythonDependencyBase ( _Base ) :
class _PythonDependencyBase ( _Base ) :
def __init__ ( self , python_holder : ' PythonExternalProgram ' , embed : bool ) :
def __init__ ( self , python_holder : ' Basic PythonExternalProgram' , embed : bool ) :
self . embed = embed
self . embed = embed
self . version : str = python_holder . info [ ' version ' ]
self . version : str = python_holder . info [ ' version ' ]
self . platform = python_holder . info [ ' platform ' ]
self . platform = python_holder . info [ ' platform ' ]
@ -109,7 +96,7 @@ class _PythonDependencyBase(_Base):
class PythonPkgConfigDependency ( PkgConfigDependency , _PythonDependencyBase ) :
class PythonPkgConfigDependency ( PkgConfigDependency , _PythonDependencyBase ) :
def __init__ ( self , name : str , environment : ' Environment ' ,
def __init__ ( self , name : str , environment : ' Environment ' ,
kwargs : T . Dict [ str , T . Any ] , installation : ' PythonExternalProgram ' ,
kwargs : T . Dict [ str , T . Any ] , installation : ' Basic PythonExternalProgram' ,
libpc : bool = False ) :
libpc : bool = False ) :
if libpc :
if libpc :
mlog . debug ( f ' Searching for { name !r} via pkgconfig lookup in LIBPC ' )
mlog . debug ( f ' Searching for { name !r} via pkgconfig lookup in LIBPC ' )
@ -137,7 +124,7 @@ class PythonPkgConfigDependency(PkgConfigDependency, _PythonDependencyBase):
class PythonFrameworkDependency ( ExtraFrameworkDependency , _PythonDependencyBase ) :
class PythonFrameworkDependency ( ExtraFrameworkDependency , _PythonDependencyBase ) :
def __init__ ( self , name : str , environment : ' Environment ' ,
def __init__ ( self , name : str , environment : ' Environment ' ,
kwargs : T . Dict [ str , T . Any ] , installation : ' PythonExternalProgram ' ) :
kwargs : T . Dict [ str , T . Any ] , installation : ' Basic PythonExternalProgram' ) :
ExtraFrameworkDependency . __init__ ( self , name , environment , kwargs )
ExtraFrameworkDependency . __init__ ( self , name , environment , kwargs )
_PythonDependencyBase . __init__ ( self , installation , kwargs . get ( ' embed ' , False ) )
_PythonDependencyBase . __init__ ( self , installation , kwargs . get ( ' embed ' , False ) )
@ -145,7 +132,7 @@ class PythonFrameworkDependency(ExtraFrameworkDependency, _PythonDependencyBase)
class PythonSystemDependency ( SystemDependency , _PythonDependencyBase ) :
class PythonSystemDependency ( SystemDependency , _PythonDependencyBase ) :
def __init__ ( self , name : str , environment : ' Environment ' ,
def __init__ ( self , name : str , environment : ' Environment ' ,
kwargs : T . Dict [ str , T . Any ] , installation : ' PythonExternalProgram ' ) :
kwargs : T . Dict [ str , T . Any ] , installation : ' Basic PythonExternalProgram' ) :
SystemDependency . __init__ ( self , name , environment , kwargs )
SystemDependency . __init__ ( self , name , environment , kwargs )
_PythonDependencyBase . __init__ ( self , installation , kwargs . get ( ' embed ' , False ) )
_PythonDependencyBase . __init__ ( self , installation , kwargs . get ( ' embed ' , False ) )
@ -283,7 +270,7 @@ class PythonSystemDependency(SystemDependency, _PythonDependencyBase):
def python_factory ( env : ' Environment ' , for_machine : ' MachineChoice ' ,
def python_factory ( env : ' Environment ' , for_machine : ' MachineChoice ' ,
kwargs : T . Dict [ str , T . Any ] ,
kwargs : T . Dict [ str , T . Any ] ,
installation : ' PythonExternalProgram ' ) - > T . List [ ' DependencyGenerator ' ] :
installation : ' Basic PythonExternalProgram' ) - > T . List [ ' DependencyGenerator ' ] :
# We can't use the factory_methods decorator here, as we need to pass the
# We can't use the factory_methods decorator here, as we need to pass the
# extra installation argument
# extra installation argument
methods = process_method_kw ( { DependencyMethods . PKGCONFIG , DependencyMethods . SYSTEM } , kwargs )
methods = process_method_kw ( { DependencyMethods . PKGCONFIG , DependencyMethods . SYSTEM } , kwargs )
@ -298,7 +285,7 @@ def python_factory(env: 'Environment', for_machine: 'MachineChoice',
# If python-X.Y.pc exists in LIBPC, we will try to use it
# If python-X.Y.pc exists in LIBPC, we will try to use it
def wrap_in_pythons_pc_dir ( name : str , env : ' Environment ' , kwargs : T . Dict [ str , T . Any ] ,
def wrap_in_pythons_pc_dir ( name : str , env : ' Environment ' , kwargs : T . Dict [ str , T . Any ] ,
installation : ' PythonExternalProgram ' ) - > ' ExternalDependency ' :
installation : ' Basic PythonExternalProgram' ) - > ' ExternalDependency ' :
if not pkg_libdir :
if not pkg_libdir :
# there is no LIBPC, so we can't search in it
# there is no LIBPC, so we can't search in it
empty = ExternalDependency ( DependencyTypeName ( ' pkgconfig ' ) , env , { } )
empty = ExternalDependency ( DependencyTypeName ( ' pkgconfig ' ) , env , { } )
@ -339,66 +326,13 @@ def python_factory(env: 'Environment', for_machine: 'MachineChoice',
return candidates
return candidates
class PythonExternalProgram ( ExternalProgram ) :
class PythonExternalProgram ( BasicPythonExternalProgram ) :
def __init__ ( self , name : str , command : T . Optional [ T . List [ str ] ] = None ,
ext_prog : T . Optional [ ExternalProgram ] = None ) :
if ext_prog is None :
super ( ) . __init__ ( name , command = command , silent = True )
else :
self . name = name
self . command = ext_prog . command
self . path = ext_prog . path
# We want strong key values, so we always populate this with bogus data.
# Otherwise to make the type checkers happy we'd have to do .get() for
# everycall, even though we know that the introspection data will be
# complete
self . info : ' PythonIntrospectionDict ' = {
' install_paths ' : { } ,
' is_pypy ' : False ,
' is_venv ' : False ,
' link_libpython ' : False ,
' sysconfig_paths ' : { } ,
' paths ' : { } ,
' platform ' : ' sentinal ' ,
' suffix ' : ' sentinel ' ,
' variables ' : { } ,
' version ' : ' 0.0 ' ,
}
self . pure : bool = True
def _check_version ( self , version : str ) - > bool :
if self . name == ' python2 ' :
return mesonlib . version_compare ( version , ' < 3.0 ' )
elif self . name == ' python3 ' :
return mesonlib . version_compare ( version , ' >= 3.0 ' )
return True
def sanity ( self , state : T . Optional [ ' ModuleState ' ] = None ) - > bool :
def sanity ( self , state : T . Optional [ ' ModuleState ' ] = None ) - > bool :
# Sanity check, we expect to have something that at least quacks in tune
ret = super ( ) . sanity ( )
if ret :
import importlib . resources
with importlib . resources . path ( ' mesonbuild.scripts ' , ' python_info.py ' ) as f :
cmd = self . get_command ( ) + [ str ( f ) ]
p , stdout , stderr = mesonlib . Popen_safe ( cmd )
try :
info = json . loads ( stdout )
except json . JSONDecodeError :
info = None
mlog . debug ( ' Could not introspect Python ( %s ): exit code %d ' % ( str ( p . args ) , p . returncode ) )
mlog . debug ( ' Program stdout: \n ' )
mlog . debug ( stdout )
mlog . debug ( ' Program stderr: \n ' )
mlog . debug ( stderr )
if info is not None and self . _check_version ( info [ ' version ' ] ) :
self . info = T . cast ( ' PythonIntrospectionDict ' , info )
self . platlib = self . _get_path ( state , ' platlib ' )
self . platlib = self . _get_path ( state , ' platlib ' )
self . purelib = self . _get_path ( state , ' purelib ' )
self . purelib = self . _get_path ( state , ' purelib ' )
return True
return ret
else :
return False
def _get_path ( self , state : T . Optional [ ' ModuleState ' ] , key : str ) - > None :
def _get_path ( self , state : T . Optional [ ' ModuleState ' ] , key : str ) - > None :
rel_path = self . info [ ' install_paths ' ] [ key ] [ 1 : ]
rel_path = self . info [ ' install_paths ' ] [ key ] [ 1 : ]