Skip to content
1 change: 1 addition & 0 deletions mesonbuild/dependencies/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class DependencyObjectKWs(TypedDict, total=False):
silent: bool
tools: T.List[str]
version_arg: str
build_config: str

_MissingCompilerBase = Compiler
else:
Expand Down
20 changes: 10 additions & 10 deletions mesonbuild/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -1825,7 +1825,7 @@ def _exe_to_shlib_kwargs(self, kwargs: kwtypes.Executable) -> kwtypes.SharedLibr

@permittedKwargs(build.known_exe_kwargs)
@typed_pos_args('executable', str, varargs=SOURCES_VARARGS)
@typed_kwargs('executable', *EXECUTABLE_KWS, allow_unknown=True)
@typed_kwargs('executable', *EXECUTABLE_KWS)
def func_executable(self, node: mparser.BaseNode,
args: T.Tuple[str, SourcesVarargsType],
kwargs: kwtypes.Executable) -> T.Union[build.Executable, build.SharedLibrary]:
Expand All @@ -1839,15 +1839,15 @@ def func_executable(self, node: mparser.BaseNode,

@permittedKwargs(build.known_stlib_kwargs)
@typed_pos_args('static_library', str, varargs=SOURCES_VARARGS)
@typed_kwargs('static_library', *STATIC_LIB_KWS, allow_unknown=True)
@typed_kwargs('static_library', *STATIC_LIB_KWS)
def func_static_lib(self, node: mparser.BaseNode,
args: T.Tuple[str, SourcesVarargsType],
kwargs: kwtypes.StaticLibrary) -> build.StaticLibrary:
return self.build_target(node, args, kwargs, build.StaticLibrary)

@permittedKwargs(build.known_shlib_kwargs)
@typed_pos_args('shared_library', str, varargs=SOURCES_VARARGS)
@typed_kwargs('shared_library', *SHARED_LIB_KWS, allow_unknown=True)
@typed_kwargs('shared_library', *SHARED_LIB_KWS)
def func_shared_lib(self, node: mparser.BaseNode,
args: T.Tuple[str, SourcesVarargsType],
kwargs: kwtypes.SharedLibrary) -> build.SharedLibrary:
Expand All @@ -1857,7 +1857,7 @@ def func_shared_lib(self, node: mparser.BaseNode,

@permittedKwargs(known_library_kwargs)
@typed_pos_args('both_libraries', str, varargs=SOURCES_VARARGS)
@typed_kwargs('both_libraries', *LIBRARY_KWS, allow_unknown=True)
@typed_kwargs('both_libraries', *LIBRARY_KWS)
@noSecondLevelHolderResolving
def func_both_lib(self, node: mparser.BaseNode,
args: T.Tuple[str, SourcesVarargsType],
Expand All @@ -1867,15 +1867,15 @@ def func_both_lib(self, node: mparser.BaseNode,
@FeatureNew('shared_module', '0.37.0')
@permittedKwargs(build.known_shmod_kwargs)
@typed_pos_args('shared_module', str, varargs=SOURCES_VARARGS)
@typed_kwargs('shared_module', *SHARED_MOD_KWS, allow_unknown=True)
@typed_kwargs('shared_module', *SHARED_MOD_KWS)
def func_shared_module(self, node: mparser.BaseNode,
args: T.Tuple[str, SourcesVarargsType],
kwargs: kwtypes.SharedModule) -> build.SharedModule:
return self.build_target(node, args, kwargs, build.SharedModule)

@permittedKwargs(known_library_kwargs)
@typed_pos_args('library', str, varargs=SOURCES_VARARGS)
@typed_kwargs('library', *LIBRARY_KWS, allow_unknown=True)
@typed_kwargs('library', *LIBRARY_KWS)
@noSecondLevelHolderResolving
def func_library(self, node: mparser.BaseNode,
args: T.Tuple[str, SourcesVarargsType],
Expand All @@ -1884,7 +1884,7 @@ def func_library(self, node: mparser.BaseNode,

@permittedKwargs(build.known_jar_kwargs)
@typed_pos_args('jar', str, varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList, build.ExtractedObjects, build.BuildTarget))
@typed_kwargs('jar', *JAR_KWS, allow_unknown=True)
@typed_kwargs('jar', *JAR_KWS)
def func_jar(self, node: mparser.BaseNode,
args: T.Tuple[str, T.List[T.Union[str, mesonlib.File, build.GeneratedTypes]]],
kwargs: kwtypes.Jar) -> build.Jar:
Expand All @@ -1893,7 +1893,7 @@ def func_jar(self, node: mparser.BaseNode,
@FeatureNewKwargs('build_target', '0.40.0', ['link_whole', 'override_options'])
@permittedKwargs(known_build_target_kwargs)
@typed_pos_args('build_target', str, varargs=SOURCES_VARARGS)
@typed_kwargs('build_target', *BUILD_TARGET_KWS, allow_unknown=True)
@typed_kwargs('build_target', *BUILD_TARGET_KWS)
def func_build_target(self, node: mparser.BaseNode,
args: T.Tuple[str, SourcesVarargsType],
kwargs: kwtypes.BuildTarget
Expand Down Expand Up @@ -3398,11 +3398,11 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs

@T.overload
def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType],
kwargs: kwtypes.SharedLibrary, targetclass: T.Type[build.SharedLibrary]) -> build.SharedLibrary: ...
kwargs: kwtypes.SharedModule, targetclass: T.Type[build.SharedModule]) -> build.SharedModule: ...

@T.overload
def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType],
kwargs: kwtypes.SharedModule, targetclass: T.Type[build.SharedModule]) -> build.SharedModule: ...
kwargs: kwtypes.SharedLibrary, targetclass: T.Type[build.SharedLibrary]) -> build.SharedLibrary: ...

@T.overload
def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType],
Expand Down
1 change: 1 addition & 0 deletions mesonbuild/interpreter/kwargs.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ class _BuildTarget(_BaseBuildTarget):
swift_interoperability_mode: Literal['c', 'cpp']
swift_module_name: str
sources: SourcesVarargsType
link_args: T.List[str]
c_pch: T.List[str]
cpp_pch: T.List[str]
c_args: T.List[str]
Expand Down
6 changes: 3 additions & 3 deletions mesonbuild/interpreter/type_checking.py
Original file line number Diff line number Diff line change
Expand Up @@ -613,13 +613,15 @@ def _extra_files_validator(args: T.List[T.Union[File, str]]) -> T.Optional[str]:
_ALL_TARGET_KWS: T.List[KwargInfo] = [
OVERRIDE_OPTIONS_KW,
KwargInfo('build_by_default', bool, default=True, since='0.38.0'),
DEPENDENCIES_KW,
KwargInfo(
'extra_files',
ContainerTypeInfo(list, (str, File)),
default=[],
listify=True,
validator=_extra_files_validator,
),
INCLUDE_DIRECTORIES.evolve(since_values={ContainerTypeInfo(list, str): '0.50.0'}),
KwargInfo(
'install',
object,
Expand All @@ -636,6 +638,7 @@ def _extra_files_validator(args: T.List[T.Union[File, str]]) -> T.Optional[str]:
listify=True,
),
KwargInfo('implicit_include_directories', bool, default=True, since='0.42.0'),
LINK_WITH_KW,
NATIVE_KW,
KwargInfo('resources', ContainerTypeInfo(list, str), default=[], listify=True),
KwargInfo(
Expand Down Expand Up @@ -735,11 +738,8 @@ def _pch_convertor(args: T.List[str]) -> T.Optional[T.Tuple[str, T.Optional[str]
*_ALL_TARGET_KWS,
*_LANGUAGE_KWS,
BT_SOURCES_KW,
INCLUDE_DIRECTORIES.evolve(since_values={ContainerTypeInfo(list, str): '0.50.0'}),
DEPENDENCIES_KW,
INCLUDE_DIRECTORIES.evolve(name='d_import_dirs'),
LINK_WHOLE_KW,
LINK_WITH_KW,
_NAME_PREFIX_KW,
_NAME_PREFIX_KW.evolve(name='name_suffix', validator=_name_suffix_validator),
RUST_CRATE_TYPE_KW,
Expand Down
77 changes: 43 additions & 34 deletions mesonbuild/modules/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,24 @@
from .. import mesonlib
from .. import mlog
from ..options import UserFeatureOption
from ..build import known_shmod_kwargs, CustomTarget, CustomTargetIndex, BuildTarget, GeneratedList, StructuredSources, ExtractedObjects, SharedModule
from ..build import CustomTarget, CustomTargetIndex, BuildTarget, GeneratedList, StructuredSources, ExtractedObjects, SharedModule
from ..dependencies import NotFoundDependency
from ..dependencies.detect import get_dep_identifier, find_external_dependency
from ..dependencies.python import BasicPythonExternalProgram, python_factory, _PythonDependencyBase
from ..interpreter import extract_required_kwarg, primitives as P_OBJ
from ..interpreter.interpreterobjects import _ExternalProgramHolder
from ..interpreter.type_checking import NoneType, DEPENDENCY_KWS, PRESERVE_PATH_KW, SHARED_MOD_KWS
from ..interpreterbase import (
noPosargs, noKwargs, permittedKwargs, ContainerTypeInfo,
noPosargs, noKwargs, ContainerTypeInfo,
InvalidArguments, typed_pos_args, typed_kwargs, KwargInfo,
FeatureNew, disablerIfNotFound, InterpreterObject
)
from ..mesonlib import MachineChoice
from ..options import OptionKey
from ..programs import ExternalProgram, NonExistingExternalProgram

_T = T.TypeVar('_T')

if T.TYPE_CHECKING:
from typing_extensions import TypedDict, NotRequired

Expand All @@ -35,13 +37,15 @@
from ..interpreter import Interpreter
from ..interpreter.interpreter import BuildTargetSource
from ..interpreter.kwargs import ExtractRequired, SharedModule as SharedModuleKw, FuncDependency
from ..interpreter.type_checking import SourcesVarargsType
from ..interpreterbase.baseobjects import TYPE_var, TYPE_kwargs

class PyInstallKw(TypedDict):

pure: T.Optional[bool]
subdir: str
install_tag: T.Optional[str]
preserve_path: bool

class FindInstallationKw(ExtractRequired):

Expand All @@ -53,15 +57,12 @@ class ExtensionModuleKw(SharedModuleKw):

# Yes, these are different between SharedModule and ExtensionModule
install_dir: T.Union[str, bool, None] # type: ignore[misc]
limited_api: str
subdir: NotRequired[T.Optional[str]]

MaybePythonProg = T.Union[NonExistingExternalProgram, 'PythonExternalProgram']


mod_kwargs = {'subdir', 'limited_api'}
mod_kwargs.update(known_shmod_kwargs)
mod_kwargs -= {'name_prefix', 'name_suffix'}

_MOD_KWARGS = [k for k in SHARED_MOD_KWS if
k.name not in {'name_prefix', 'name_suffix', 'install_dir'}]

Expand Down Expand Up @@ -120,12 +121,16 @@ def __init__(self, python: 'PythonExternalProgram', interpreter: 'Interpreter'):
assert isinstance(prefix, str), 'for mypy'

if python.build_config:
self.version = python.build_config['language']['version']
self.platform = python.build_config['platform']
self.suffix = python.build_config['abi']['extension_suffix']
self.limited_api_suffix = python.build_config['abi']['stable_abi_suffix']
self.link_libpython = python.build_config['libpython']['link_extensions']
self.is_pypy = python.build_config['implementation']['name'] == 'pypy'
def as_(obj: object, type_: T.Type[_T]) -> _T:
assert isinstance(obj, type_), 'for mypy'
return obj

self.version = as_(python.build_config['language']['version'], str)
self.platform = as_(python.build_config['platform'], str)
self.suffix = as_(python.build_config['abi']['extension_suffix'], str)
self.limited_api_suffix = as_(python.build_config['abi']['stable_abi_suffix'], str)
self.link_libpython = as_(python.build_config['libpython']['link_extensions'], bool)
self.is_pypy = as_(python.build_config['implementation']['name'], str) == 'pypy'
else:
self.version = info['version']
self.platform = info['platform']
Expand All @@ -140,34 +145,34 @@ def __init__(self, python: 'PythonExternalProgram', interpreter: 'Interpreter'):
self.platlib_install_path = os.path.join(prefix, python.platlib)
self.purelib_install_path = os.path.join(prefix, python.purelib)

@permittedKwargs(mod_kwargs)
@typed_pos_args('python.extension_module', str, varargs=(str, mesonlib.File, CustomTarget, CustomTargetIndex, GeneratedList, StructuredSources, ExtractedObjects, BuildTarget))
@typed_kwargs(
'python.extension_module',
*_MOD_KWARGS,
_DEFAULTABLE_SUBDIR_KW,
_LIMITED_API_KW,
KwargInfo('install_dir', (str, bool, NoneType)),
allow_unknown=True
)
@InterpreterObject.method('extension_module')
def extension_module_method(self, args: T.Tuple[str, T.List[BuildTargetSource]], kwargs: ExtensionModuleKw) -> 'SharedModule':
target_kwargs = T.cast('SharedModuleKw', {k: v for k, v in kwargs.items() if k not in {'install_dir', 'subdir', 'limited_api'}})

if kwargs['install_dir'] is not None:
if kwargs['subdir'] is not None:
raise InvalidArguments('"subdir" and "install_dir" are mutually exclusive')
# the build_target() method now expects this to be correct.
kwargs['install_dir'] = [kwargs['install_dir']]
target_kwargs['install_dir'] = [kwargs['install_dir']]
else:
# We want to remove 'subdir', but it may be None and we want to replace it with ''
# It must be done this way since we don't allow both `install_dir`
# and `subdir` to be set at the same time
subdir = kwargs.pop('subdir') or ''
subdir = kwargs.get('subdir') or ''

kwargs['install_dir'] = [self._get_install_dir_impl(False, subdir)]
target_kwargs['install_dir'] = [self._get_install_dir_impl(False, subdir)]

target_suffix = self.suffix

new_deps = mesonlib.extract_as_list(kwargs, 'dependencies')
new_deps = kwargs['dependencies'].copy()
pydep = next((dep for dep in new_deps if isinstance(dep, _PythonDependencyBase)), None)
if pydep is None:
pydep = self._dependency_method_impl({'native': kwargs['native']})
Expand All @@ -178,7 +183,7 @@ def extension_module_method(self, args: T.Tuple[str, T.List[BuildTargetSource]],
'0.63.0', self.subproject, 'use python_installation.dependency()',
self.current_node)

limited_api_version = kwargs.pop('limited_api')
limited_api_version = kwargs.get('limited_api')
allow_limited_api = self.interpreter.environment.coredata.optstore.get_value_for(OptionKey('python.allow_limited_api'))
if limited_api_version != '' and allow_limited_api:

Expand All @@ -187,19 +192,20 @@ def extension_module_method(self, args: T.Tuple[str, T.List[BuildTargetSource]],
limited_api_version_hex = self._convert_api_version_to_py_version_hex(limited_api_version, pydep.version)
limited_api_definition = f'-DPy_LIMITED_API={limited_api_version_hex}'

new_c_args = mesonlib.extract_as_list(kwargs, 'c_args')
new_c_args = kwargs['c_args'].copy()
new_c_args.append(limited_api_definition)
kwargs['c_args'] = new_c_args
target_kwargs['c_args'] = new_c_args

new_cpp_args = mesonlib.extract_as_list(kwargs, 'cpp_args')
new_cpp_args = kwargs['cpp_args'].copy()
new_cpp_args.append(limited_api_definition)
kwargs['cpp_args'] = new_cpp_args
target_kwargs['cpp_args'] = new_cpp_args

# On Windows, the limited API DLL is python3.dll, not python3X.dll.
for_machine = kwargs['native']
if self.interpreter.environment.machines[for_machine].is_windows():
pydep_copy = copy.copy(pydep)
pydep_copy.find_libpy_windows(self.env, limited_api=True)
if isinstance(pydep_copy, _PythonDependencyBase):
pydep_copy.find_libpy_windows(self.env, limited_api=True)
if not pydep_copy.found():
raise mesonlib.MesonException('Python dependency supporting limited API not found')

Expand All @@ -216,31 +222,33 @@ def extension_module_method(self, args: T.Tuple[str, T.List[BuildTargetSource]],
python_windows_debug_link_exception = f'/NODEFAULTLIB:python{pyver}_d.lib'
python_windows_release_link_exception = f'/NODEFAULTLIB:python{pyver}.lib'

new_link_args = mesonlib.extract_as_list(kwargs, 'link_args')
new_link_args = kwargs['link_args'].copy()

is_debug = self.interpreter.environment.coredata.optstore.get_value_for('debug')
if is_debug:
new_link_args.append(python_windows_debug_link_exception)
else:
new_link_args.append(python_windows_release_link_exception)

kwargs['link_args'] = new_link_args
target_kwargs['link_args'] = new_link_args

kwargs['dependencies'] = new_deps
target_kwargs['dependencies'] = new_deps

# msys2's python3 has "-cpython-36m.dll", we have to be clever
# FIXME: explain what the specific cleverness is here
split, target_suffix = target_suffix.rsplit('.', 1)
args = (args[0] + split, args[1])

kwargs['name_prefix'] = ''
kwargs['name_suffix'] = target_suffix
target_kwargs['name_prefix'] = ''
target_kwargs['name_suffix'] = target_suffix

if kwargs['gnu_symbol_visibility'] == '' and \
(self.is_pypy or mesonlib.version_compare(self.version, '>=3.9')):
kwargs['gnu_symbol_visibility'] = 'inlineshidden'
target_kwargs['gnu_symbol_visibility'] = 'inlineshidden'

return self.interpreter.build_target(self.current_node, args, kwargs, SharedModule)
return self.interpreter.build_target(
self.current_node, T.cast('T.Tuple[str, SourcesVarargsType]', args),
target_kwargs, SharedModule)

def _convert_api_version_to_py_version_hex(self, api_version: str, detected_version: str) -> str:
python_api_version_format = re.compile(r'[0-9]\.[0-9]{1,2}')
Expand All @@ -267,6 +275,7 @@ def _dependency_method_impl(self, kwargs: DependencyObjectKWs) -> Dependency:
return dep

build_config = self.interpreter.environment.coredata.optstore.get_value_for(OptionKey('python.build_config'))
assert isinstance(build_config, str), 'for mypy'

new_kwargs = kwargs.copy()
new_kwargs['required'] = False
Expand Down Expand Up @@ -401,7 +410,7 @@ def __init__(self, interpreter: 'Interpreter') -> None:

def _get_install_scripts(self) -> T.List[mesonlib.ExecutableSerialisation]:
backend = self.interpreter.backend
ret = []
ret: T.List[mesonlib.ExecutableSerialisation] = []
optlevel = self.interpreter.environment.coredata.optstore.get_value_for(OptionKey('python.bytecompile'))
if optlevel == -1:
return ret
Expand All @@ -411,7 +420,7 @@ def _get_install_scripts(self) -> T.List[mesonlib.ExecutableSerialisation]:
installdata = backend.create_install_data()
py_files = []

def should_append(f, isdir: bool = False):
def should_append(f: str, isdir: bool = False) -> bool:
# This uses the install_plan decorated names to see if the original source was propagated via
# install_sources() or get_install_dir().
return f.startswith(('{py_platlib}', '{py_purelib}')) and (f.endswith('.py') or isdir)
Expand All @@ -433,7 +442,6 @@ def should_append(f, isdir: bool = False):

for i in self.installations.values():
if isinstance(i, PythonExternalProgram) and i.run_bytecompile[i.info['version']]:
i = T.cast('PythonExternalProgram', i)
manifest = f'python-{i.info["version"]}-installed.json'
manifest_json = []
for name, f in py_files:
Expand Down Expand Up @@ -470,6 +478,7 @@ def _get_win_pythonpath(name_or_path: str) -> T.Optional[str]:

def _find_installation_impl(self, state: 'ModuleState', display_name: str, name_or_path: str, required: bool) -> MaybePythonProg:
build_config = self.interpreter.environment.coredata.optstore.get_value_for(OptionKey('python.build_config'))
assert isinstance(build_config, str), 'for mypy'

if not name_or_path:
python = PythonExternalProgram('python3', mesonlib.python_command, build_config_path=build_config)
Expand Down
Loading
Loading