mirror of
https://github.com/mesonbuild/meson.git
synced 2026-11-06 05:16:42 +00:00
1268 lines
53 KiB
Python
1268 lines
53 KiB
Python
from __future__ import annotations
|
|
import os
|
|
import shlex
|
|
import subprocess
|
|
import copy
|
|
import textwrap
|
|
import threading
|
|
import sys
|
|
|
|
from pathlib import Path, PurePath
|
|
|
|
from .. import mesonlib
|
|
from .. import build
|
|
from .. import mlog
|
|
|
|
from ..modules import ModuleReturnValue, ModuleObject, ModuleState, ExtensionModule, NewExtensionModule
|
|
from ..backend.backends import TestProtocol
|
|
from ..interpreterbase import (
|
|
ContainerTypeInfo, KwargInfo, InterpreterObject, MesonOperator,
|
|
MesonInterpreterObject, ObjectHolder, MutableInterpreterObject,
|
|
FeatureNew, FeatureDeprecated,
|
|
typed_pos_args, typed_kwargs, typed_operator,
|
|
noArgsFlattening, noPosargs, noKwargs, unholder_return,
|
|
flatten, resolve_second_level_holders, Feature, FeatureValue,
|
|
InterpreterException, InvalidArguments, InvalidCode)
|
|
from ..interpreter.type_checking import NoneType, ENV_KW, ENV_SEPARATOR_KW, PKGCONFIG_DEFINE_KW
|
|
from ..dependencies import Dependency, ExternalLibrary, InternalDependency
|
|
from ..programs import Program
|
|
from ..mesonlib import File, HoldableObject, listify, MachineChoice, MesonException
|
|
|
|
import typing as T
|
|
|
|
if T.TYPE_CHECKING:
|
|
from . import kwargs
|
|
from ..cmake.interpreter import CMakeInterpreter
|
|
from ..dependencies.base import IncludeType
|
|
from ..envconfig import MachineInfo
|
|
from ..interpreterbase import FeatureCheckBase, TYPE_var, TYPE_kwargs, TYPE_nvar, TYPE_nkwargs
|
|
from ..mesonlib import SubProject
|
|
from .interpreter import Interpreter
|
|
|
|
from typing_extensions import Literal, TypedDict
|
|
|
|
class EnvironmentSeparatorKW(TypedDict):
|
|
|
|
separator: str
|
|
|
|
class InternalDependencyAsKW(TypedDict):
|
|
|
|
recursive: bool
|
|
|
|
_ERROR_MSG_KW: KwargInfo[T.Optional[str]] = KwargInfo('error_message', (str, NoneType))
|
|
|
|
|
|
def extract_required_kwarg(kwargs: 'kwargs.ExtractRequired',
|
|
subproject: 'SubProject',
|
|
feature_check: T.Optional[FeatureCheckBase] = None,
|
|
default: bool = True
|
|
) -> T.Union[T.Tuple[Literal[True], bool, str],
|
|
T.Tuple[Literal[False], bool, None]]:
|
|
"""Check common keyword arguments for required status.
|
|
|
|
This handles booleans vs feature option.
|
|
|
|
:param kwargs:
|
|
keyword arguments from the Interpreter, containing a `required` argument
|
|
:param subproject: The subproject this is
|
|
:param feature_check:
|
|
A custom feature check for this use of `required` with a
|
|
`Feature`, defaults to None.
|
|
:param default:
|
|
The default value is `required` is not set in `kwargs`, defaults to
|
|
True
|
|
:raises InterpreterException: If the type of `kwargs['required']` is invalid
|
|
:return:
|
|
a tuple of `disabled, required, feature_name`. If `disabled` is `True`
|
|
`feature_name` will be a string, otherwise it is `None`
|
|
"""
|
|
val = kwargs.get('required', default)
|
|
required = False
|
|
feature: T.Optional[str] = None
|
|
if isinstance(val, Feature):
|
|
if not feature_check:
|
|
feature_check = FeatureNew('User option "feature"', '0.47.0')
|
|
feature_check.use(subproject)
|
|
feature = val.name
|
|
if val.is_disabled():
|
|
return True, required, feature
|
|
elif val.is_enabled():
|
|
required = True
|
|
elif isinstance(val, bool):
|
|
required = val
|
|
else:
|
|
raise InterpreterException('required keyword argument must be boolean or a feature option')
|
|
|
|
# Keep boolean value in kwargs to simplify other places where this kwarg is
|
|
# checked.
|
|
# TODO: this should be removed, and those callers should learn about FeatureOptions
|
|
kwargs['required'] = required
|
|
|
|
return False, required, None
|
|
|
|
def extract_search_dirs(kwargs: 'kwargs.ExtractSearchDirs') -> T.List[str]:
|
|
search_dirs_str = mesonlib.stringlistify(kwargs.get('dirs', []))
|
|
search_dirs = [Path(d).expanduser() for d in search_dirs_str]
|
|
for d in search_dirs:
|
|
if mesonlib.is_windows() and d.root.startswith('\\'):
|
|
# a Unix-path starting with `/` that is not absolute on Windows.
|
|
# discard without failing for end-user ease of cross-platform directory arrays
|
|
continue
|
|
if not d.is_absolute():
|
|
raise InvalidCode(f'Search directory {d} is not an absolute path.')
|
|
return [str(s) for s in search_dirs]
|
|
|
|
class FeatureOptionHolder(ObjectHolder[Feature]):
|
|
def __init__(self, option: Feature, interpreter: 'Interpreter'):
|
|
if option.is_auto():
|
|
auto_value = T.cast('str', interpreter.environment.coredata.optstore.get_value_for('auto_features'))
|
|
option = option.with_value(FeatureValue(auto_value))
|
|
super().__init__(option, interpreter)
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('enabled')
|
|
def enabled_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
|
|
return self.held_object.is_enabled()
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('disabled')
|
|
def disabled_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
|
|
return self.held_object.is_disabled()
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@FeatureNew('feature_option.allowed()', '0.59.0')
|
|
@InterpreterObject.method('allowed')
|
|
def allowed_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
|
|
return not self.held_object.is_disabled()
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('auto')
|
|
def auto_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
|
|
return self.held_object.is_auto()
|
|
|
|
def _disable_if(self, condition: bool, message: T.Optional[str]) -> Feature:
|
|
if not condition:
|
|
return self.held_object
|
|
|
|
if self.held_object.is_enabled():
|
|
err_msg = f'Feature {self.held_object.name} cannot be enabled'
|
|
if message:
|
|
err_msg += f': {message}'
|
|
raise InterpreterException(err_msg)
|
|
return self.held_object.as_disabled()
|
|
|
|
@FeatureNew('feature_option.require()', '0.59.0')
|
|
@typed_pos_args('feature_option.require', bool)
|
|
@typed_kwargs(
|
|
'feature_option.require',
|
|
_ERROR_MSG_KW,
|
|
)
|
|
@InterpreterObject.method('require')
|
|
def require_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> Feature:
|
|
return self._disable_if(not args[0], kwargs['error_message'])
|
|
|
|
@FeatureNew('feature_option.disable_if()', '1.1.0')
|
|
@typed_pos_args('feature_option.disable_if', bool)
|
|
@typed_kwargs(
|
|
'feature_option.disable_if',
|
|
_ERROR_MSG_KW,
|
|
)
|
|
@InterpreterObject.method('disable_if')
|
|
def disable_if_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> Feature:
|
|
return self._disable_if(args[0], kwargs['error_message'])
|
|
|
|
@FeatureNew('feature_option.enable_if()', '1.1.0')
|
|
@typed_pos_args('feature_option.enable_if', bool)
|
|
@typed_kwargs(
|
|
'feature_option.enable_if',
|
|
_ERROR_MSG_KW,
|
|
)
|
|
@InterpreterObject.method('enable_if')
|
|
def enable_if_method(self, args: T.Tuple[bool], kwargs: 'kwargs.FeatureOptionRequire') -> Feature:
|
|
if not args[0]:
|
|
return self.held_object
|
|
|
|
if self.held_object.is_disabled():
|
|
err_msg = f'Feature {self.held_object.name} cannot be disabled'
|
|
if kwargs['error_message']:
|
|
err_msg += f': {kwargs["error_message"]}'
|
|
raise InterpreterException(err_msg)
|
|
return self.held_object.as_enabled()
|
|
|
|
@FeatureNew('feature_option.disable_auto_if()', '0.59.0')
|
|
@noKwargs
|
|
@typed_pos_args('feature_option.disable_auto_if', bool)
|
|
@InterpreterObject.method('disable_auto_if')
|
|
def disable_auto_if_method(self, args: T.Tuple[bool], kwargs: TYPE_kwargs) -> Feature:
|
|
return self.held_object.as_disabled() if self.held_object.is_auto() and args[0] else self.held_object
|
|
|
|
@FeatureNew('feature_option.enable_auto_if()', '1.1.0')
|
|
@noKwargs
|
|
@typed_pos_args('feature_option.enable_auto_if', bool)
|
|
@InterpreterObject.method('enable_auto_if')
|
|
def enable_auto_if_method(self, args: T.Tuple[bool], kwargs: TYPE_kwargs) -> Feature:
|
|
return self.held_object.as_enabled() if self.held_object.is_auto() and args[0] else self.held_object
|
|
|
|
|
|
class RunProcess(MesonInterpreterObject):
|
|
|
|
def __init__(self,
|
|
cmd: Program,
|
|
args: T.List[str],
|
|
env: mesonlib.EnvironmentVariables,
|
|
source_dir: str,
|
|
build_dir: str,
|
|
subdir: str,
|
|
mesonintrospect: T.List[str],
|
|
in_builddir: bool = False,
|
|
check: bool = False,
|
|
capture: bool = True,
|
|
console: bool = False) -> None:
|
|
super().__init__()
|
|
self.capture = capture
|
|
self.console = console
|
|
self.returncode, self.stdout, self.stderr = self.run_command(cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check)
|
|
|
|
def run_command(self,
|
|
cmd: Program,
|
|
args: T.List[str],
|
|
env: mesonlib.EnvironmentVariables,
|
|
source_dir: str,
|
|
build_dir: str,
|
|
subdir: str,
|
|
mesonintrospect: T.List[str],
|
|
in_builddir: bool,
|
|
check: bool = False) -> T.Tuple[int, str, str]:
|
|
command_array = cmd.get_command() + args
|
|
menv = {'MESON_SOURCE_ROOT': source_dir,
|
|
'MESON_BUILD_ROOT': build_dir,
|
|
'MESON_SUBDIR': subdir,
|
|
'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in mesonintrospect]),
|
|
}
|
|
if in_builddir:
|
|
cwd = os.path.join(build_dir, subdir)
|
|
else:
|
|
cwd = os.path.join(source_dir, subdir)
|
|
child_env = os.environ.copy()
|
|
child_env.update(menv)
|
|
child_env = env.get_env(child_env)
|
|
|
|
def proc_output_thread(pipe: T.IO, capture_list: T.List, io_obj: T.TextIO) -> None:
|
|
while True:
|
|
line = pipe.readline()
|
|
if not line:
|
|
break
|
|
if self.console:
|
|
io_obj.write(line.decode('utf-8', errors='replace'))
|
|
io_obj.flush()
|
|
if self.capture:
|
|
capture_list.append(line)
|
|
pipe.close()
|
|
|
|
stdin: T.Union[T.TextIO, int] = sys.stdin if self.console else subprocess.DEVNULL
|
|
stdout = subprocess.PIPE
|
|
stderr = subprocess.PIPE
|
|
|
|
mlog.debug('Running command:', mesonlib.join_args(command_array))
|
|
try:
|
|
p = subprocess.Popen(command_array, stdin=stdin, stdout=stdout, stderr=stderr, env=child_env, cwd=cwd)
|
|
except FileNotFoundError:
|
|
raise InterpreterException('Could not execute command `%s`.' % mesonlib.join_args(command_array))
|
|
|
|
o_list: T.List = []
|
|
e_list: T.List = []
|
|
stdout_thread = threading.Thread(
|
|
target=proc_output_thread,
|
|
kwargs={'pipe': p.stdout, 'capture_list': o_list, 'io_obj': sys.stdout}, daemon=True)
|
|
stderr_thread = threading.Thread(
|
|
target=proc_output_thread,
|
|
kwargs={'pipe': p.stderr, 'capture_list': e_list, 'io_obj': sys.stderr}, daemon=True)
|
|
for t in (stdout_thread, stderr_thread):
|
|
t.start()
|
|
|
|
p.wait()
|
|
for t in (stdout_thread, stderr_thread):
|
|
t.join()
|
|
|
|
o = b''.join(o_list).decode('utf-8', errors='replace').replace('\r\n', '\n')
|
|
e = b''.join(e_list).decode('utf-8', errors='replace').replace('\r\n', '\n')
|
|
|
|
if self.capture:
|
|
mlog.debug('--- stdout ---')
|
|
mlog.debug(o)
|
|
mlog.debug('--- stderr ---')
|
|
mlog.debug(e)
|
|
else:
|
|
mlog.debug('--- capture output disabled ---')
|
|
mlog.debug('')
|
|
|
|
if check and p.returncode != 0:
|
|
raise InterpreterException('Command `{}` failed with status {}.'.format(mesonlib.join_args(command_array), p.returncode))
|
|
|
|
return p.returncode, o, e
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('returncode')
|
|
def returncode_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> int:
|
|
return self.returncode
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('stdout')
|
|
def stdout_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self.stdout
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('stderr')
|
|
def stderr_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self.stderr
|
|
|
|
class EnvironmentVariablesHolder(ObjectHolder[mesonlib.EnvironmentVariables], MutableInterpreterObject):
|
|
|
|
def __init__(self, obj: mesonlib.EnvironmentVariables, interpreter: 'Interpreter'):
|
|
super().__init__(obj, interpreter)
|
|
|
|
def __repr__(self) -> str:
|
|
repr_str = "<{0}: {1}>"
|
|
return repr_str.format(self.__class__.__name__, self.held_object.envvars)
|
|
|
|
def __deepcopy__(self, memo: T.Dict[str, object]) -> 'EnvironmentVariablesHolder':
|
|
# Avoid trying to copy the interpreter
|
|
return EnvironmentVariablesHolder(copy.deepcopy(self.held_object), self.interpreter)
|
|
|
|
def warn_if_has_name(self, name: str) -> None:
|
|
# Multiple append/prepend operations was not supported until 0.58.0.
|
|
if self.held_object.has_name(name):
|
|
m = f'Overriding previous value of environment variable {name!r} with a new one'
|
|
FeatureNew(m, '0.58.0').use(self.subproject, self.current_node)
|
|
|
|
@typed_pos_args('environment.set', str, varargs=str, min_varargs=1)
|
|
@typed_kwargs('environment.set', ENV_SEPARATOR_KW)
|
|
@InterpreterObject.method('set')
|
|
def set_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'EnvironmentSeparatorKW') -> None:
|
|
name, values = args
|
|
self.held_object.set(name, values, kwargs['separator'])
|
|
|
|
@FeatureNew('environment.unset', '1.4.0')
|
|
@typed_pos_args('environment.unset', str)
|
|
@noKwargs
|
|
@InterpreterObject.method('unset')
|
|
def unset_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> None:
|
|
self.held_object.unset(args[0])
|
|
|
|
@typed_pos_args('environment.append', str, varargs=str, min_varargs=1)
|
|
@typed_kwargs('environment.append', ENV_SEPARATOR_KW)
|
|
@InterpreterObject.method('append')
|
|
def append_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'EnvironmentSeparatorKW') -> None:
|
|
name, values = args
|
|
self.warn_if_has_name(name)
|
|
self.held_object.append(name, values, kwargs['separator'])
|
|
|
|
@typed_pos_args('environment.prepend', str, varargs=str, min_varargs=1)
|
|
@typed_kwargs('environment.prepend', ENV_SEPARATOR_KW)
|
|
@InterpreterObject.method('prepend')
|
|
def prepend_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'EnvironmentSeparatorKW') -> None:
|
|
name, values = args
|
|
self.warn_if_has_name(name)
|
|
self.held_object.prepend(name, values, kwargs['separator'])
|
|
|
|
|
|
_CONF_DATA_SET_KWS: KwargInfo[T.Optional[str]] = KwargInfo('description', (str, NoneType))
|
|
|
|
|
|
class ConfigurationDataHolder(ObjectHolder[build.ConfigurationData], MutableInterpreterObject):
|
|
|
|
def __init__(self, obj: build.ConfigurationData, interpreter: 'Interpreter'):
|
|
super().__init__(obj, interpreter)
|
|
|
|
def __deepcopy__(self, memo: T.Dict) -> 'ConfigurationDataHolder':
|
|
return ConfigurationDataHolder(copy.deepcopy(self.held_object), self.interpreter)
|
|
|
|
def is_used(self) -> bool:
|
|
return self.held_object.used
|
|
|
|
def __check_used(self) -> None:
|
|
if self.is_used():
|
|
raise InterpreterException("Can not set values on configuration object that has been used.")
|
|
|
|
@typed_pos_args('configuration_data.set', str, (str, int, bool))
|
|
@typed_kwargs('configuration_data.set', _CONF_DATA_SET_KWS)
|
|
@InterpreterObject.method('set')
|
|
def set_method(self, args: T.Tuple[str, T.Union[str, int, bool]], kwargs: 'kwargs.ConfigurationDataSet') -> None:
|
|
self.__check_used()
|
|
self.held_object.values[args[0]] = (args[1], kwargs['description'])
|
|
|
|
@typed_pos_args('configuration_data.set_quoted', str, str)
|
|
@typed_kwargs('configuration_data.set_quoted', _CONF_DATA_SET_KWS)
|
|
@InterpreterObject.method('set_quoted')
|
|
def set_quoted_method(self, args: T.Tuple[str, str], kwargs: 'kwargs.ConfigurationDataSet') -> None:
|
|
self.__check_used()
|
|
escaped_val = '\\"'.join(args[1].split('"'))
|
|
self.held_object.values[args[0]] = (f'"{escaped_val}"', kwargs['description'])
|
|
|
|
@typed_pos_args('configuration_data.set10', str, (int, bool))
|
|
@typed_kwargs('configuration_data.set10', _CONF_DATA_SET_KWS)
|
|
@InterpreterObject.method('set10')
|
|
def set10_method(self, args: T.Tuple[str, T.Union[int, bool]], kwargs: 'kwargs.ConfigurationDataSet') -> None:
|
|
self.__check_used()
|
|
# bool is a subclass of int, so we need to check for bool explicitly.
|
|
# We already have typed_pos_args checking that this is either a bool or
|
|
# an int.
|
|
if not isinstance(args[1], bool):
|
|
mlog.deprecation('configuration_data.set10 with number. The `set10` '
|
|
'method should only be used with booleans',
|
|
location=self.interpreter.current_node)
|
|
if args[1] < 0:
|
|
mlog.warning('Passing a number that is less than 0 may not have the intended result, '
|
|
'as meson will treat all non-zero values as true.',
|
|
location=self.interpreter.current_node)
|
|
self.held_object.values[args[0]] = (int(args[1]), kwargs['description'])
|
|
|
|
@typed_pos_args('configuration_data.has', (str, int, bool))
|
|
@noKwargs
|
|
@InterpreterObject.method('has')
|
|
def has_method(self, args: T.Tuple[T.Union[str, int, bool]], kwargs: TYPE_kwargs) -> bool:
|
|
return args[0] in self.held_object.values
|
|
|
|
@FeatureNew('configuration_data.get()', '0.38.0')
|
|
@typed_pos_args('configuration_data.get', str, optargs=[(str, int, bool)])
|
|
@noKwargs
|
|
@InterpreterObject.method('get')
|
|
def get_method(self, args: T.Tuple[str, T.Optional[T.Union[str, int, bool]]],
|
|
kwargs: TYPE_kwargs) -> T.Union[str, int, bool]:
|
|
name = args[0]
|
|
if name in self.held_object:
|
|
return self.held_object.get(name)[0]
|
|
elif args[1] is not None:
|
|
return args[1]
|
|
raise InterpreterException(f'Entry {name} not in configuration data.')
|
|
|
|
@FeatureNew('configuration_data.get_unquoted()', '0.44.0')
|
|
@typed_pos_args('configuration_data.get_unquoted', str, optargs=[(str, int, bool)])
|
|
@noKwargs
|
|
@InterpreterObject.method('get_unquoted')
|
|
def get_unquoted_method(self, args: T.Tuple[str, T.Optional[T.Union[str, int, bool]]],
|
|
kwargs: TYPE_kwargs) -> T.Union[str, int, bool]:
|
|
name = args[0]
|
|
if name in self.held_object:
|
|
val = self.held_object.get(name)[0]
|
|
elif args[1] is not None:
|
|
val = args[1]
|
|
else:
|
|
raise InterpreterException(f'Entry {name} not in configuration data.')
|
|
if isinstance(val, str) and val[0] == '"' and val[-1] == '"':
|
|
return val[1:-1]
|
|
return val
|
|
|
|
def get(self, name: str) -> T.Tuple[T.Union[str, int, bool], T.Optional[str]]:
|
|
return self.held_object.values[name]
|
|
|
|
@FeatureNew('configuration_data.keys()', '0.57.0')
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('keys')
|
|
def keys_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[str]:
|
|
return sorted(self.keys())
|
|
|
|
def keys(self) -> T.List[str]:
|
|
return list(self.held_object.values.keys())
|
|
|
|
@typed_pos_args('configuration_data.merge_from', build.ConfigurationData)
|
|
@noKwargs
|
|
@InterpreterObject.method('merge_from')
|
|
def merge_from_method(self, args: T.Tuple[build.ConfigurationData], kwargs: TYPE_kwargs) -> None:
|
|
from_object = args[0]
|
|
self.held_object.values.update(from_object.values)
|
|
|
|
|
|
_PARTIAL_DEP_KWARGS = [
|
|
KwargInfo('compile_args', bool, default=False),
|
|
KwargInfo('link_args', bool, default=False),
|
|
KwargInfo('links', bool, default=False),
|
|
KwargInfo('includes', bool, default=False),
|
|
KwargInfo('sources', bool, default=False),
|
|
]
|
|
|
|
class DependencyHolder(ObjectHolder[Dependency]):
|
|
def __init__(self, dep: Dependency, interpreter: 'Interpreter'):
|
|
super().__init__(dep, interpreter)
|
|
|
|
def found(self) -> bool:
|
|
return self.found_method([], {})
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('type_name')
|
|
def type_name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self.held_object.type_name
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('found')
|
|
def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
|
|
if self.held_object.type_name == 'internal':
|
|
return True
|
|
return self.held_object.found()
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('version')
|
|
def version_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self.held_object.get_version()
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('name')
|
|
def name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self.held_object.get_name()
|
|
|
|
@FeatureDeprecated('dependency.get_pkgconfig_variable', '0.56.0',
|
|
'use dependency.get_variable(pkgconfig : ...) instead')
|
|
@typed_pos_args('dependency.get_pkgconfig_variable', str)
|
|
@typed_kwargs(
|
|
'dependency.get_pkgconfig_variable',
|
|
KwargInfo('default', str, default=''),
|
|
PKGCONFIG_DEFINE_KW.evolve(name='define_variable')
|
|
)
|
|
@InterpreterObject.method('get_pkgconfig_variable')
|
|
def pkgconfig_method(self, args: T.Tuple[str], kwargs: 'kwargs.DependencyPkgConfigVar') -> str:
|
|
from ..dependencies.pkgconfig import PkgConfigDependency
|
|
if not isinstance(self.held_object, PkgConfigDependency):
|
|
raise InvalidArguments(f'{self.held_object.get_name()!r} is not a pkgconfig dependency')
|
|
if kwargs['define_variable'] and len(kwargs['define_variable']) > 1:
|
|
FeatureNew.single_use('dependency.get_pkgconfig_variable keyword argument "define_variable" with more than one pair',
|
|
'1.3.0', self.subproject, location=self.current_node)
|
|
return self.held_object.get_variable(
|
|
pkgconfig=args[0],
|
|
default_value=kwargs['default'],
|
|
pkgconfig_define=kwargs['define_variable'],
|
|
)
|
|
|
|
@FeatureNew('dependency.get_configtool_variable', '0.44.0')
|
|
@FeatureDeprecated('dependency.get_configtool_variable', '0.56.0',
|
|
'use dependency.get_variable(configtool : ...) instead')
|
|
@noKwargs
|
|
@typed_pos_args('dependency.get_config_tool_variable', str)
|
|
@InterpreterObject.method('get_configtool_variable')
|
|
def configtool_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> str:
|
|
from ..dependencies.configtool import ConfigToolDependency
|
|
if not isinstance(self.held_object, ConfigToolDependency):
|
|
raise InvalidArguments(f'{self.held_object.get_name()!r} is not a config-tool dependency')
|
|
return self.held_object.get_variable(
|
|
configtool=args[0],
|
|
default_value='',
|
|
)
|
|
|
|
@FeatureNew('dependency.partial_dependency', '0.46.0')
|
|
@noPosargs
|
|
@typed_kwargs('dependency.partial_dependency', *_PARTIAL_DEP_KWARGS)
|
|
@InterpreterObject.method('partial_dependency')
|
|
def partial_dependency_method(self, args: T.List[TYPE_nvar], kwargs: 'kwargs.DependencyMethodPartialDependency') -> Dependency:
|
|
pdep = self.held_object.get_partial_dependency(**kwargs)
|
|
return pdep
|
|
|
|
@FeatureNew('dependency.get_variable', '0.51.0')
|
|
@typed_pos_args('dependency.get_variable', optargs=[str])
|
|
@typed_kwargs(
|
|
'dependency.get_variable',
|
|
KwargInfo('cmake', (str, NoneType)),
|
|
KwargInfo('pkgconfig', (str, NoneType)),
|
|
KwargInfo('configtool', (str, NoneType)),
|
|
KwargInfo('internal', (str, NoneType), since='0.54.0'),
|
|
KwargInfo('system', (str, NoneType), since='1.6.0'),
|
|
KwargInfo('default_value', (str, NoneType)),
|
|
PKGCONFIG_DEFINE_KW,
|
|
)
|
|
@InterpreterObject.method('get_variable')
|
|
def variable_method(self, args: T.Tuple[T.Optional[str]], kwargs: 'kwargs.DependencyGetVariable') -> str:
|
|
default_varname = args[0]
|
|
if default_varname is not None:
|
|
FeatureNew('Positional argument to dependency.get_variable()', '0.58.0').use(self.subproject, self.current_node)
|
|
if kwargs['pkgconfig_define'] and len(kwargs['pkgconfig_define']) > 1:
|
|
FeatureNew.single_use('dependency.get_variable keyword argument "pkgconfig_define" with more than one pair',
|
|
'1.3.0', self.subproject, 'In previous versions, this silently returned a malformed value.',
|
|
self.current_node)
|
|
return self.held_object.get_variable(
|
|
cmake=kwargs['cmake'] or default_varname,
|
|
pkgconfig=kwargs['pkgconfig'] or default_varname,
|
|
configtool=kwargs['configtool'] or default_varname,
|
|
internal=kwargs['internal'] or default_varname,
|
|
system=kwargs['system'] or default_varname,
|
|
default_value=kwargs['default_value'],
|
|
pkgconfig_define=kwargs['pkgconfig_define'],
|
|
)
|
|
|
|
@FeatureNew('dependency.include_type', '0.52.0')
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('include_type')
|
|
def include_type_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self.held_object.get_include_type()
|
|
|
|
@FeatureNew('dependency.as_system', '0.52.0')
|
|
@noKwargs
|
|
@typed_pos_args('dependency.as_system', optargs=[str])
|
|
@InterpreterObject.method('as_system')
|
|
def as_system_method(self, args: T.Tuple[T.Optional[str]], kwargs: TYPE_kwargs) -> Dependency:
|
|
include_type: IncludeType
|
|
if args[0] is None:
|
|
include_type = 'system'
|
|
elif args[0] not in {'preserve', 'system', 'non-system'}:
|
|
raise InvalidArguments(
|
|
'Dependency.as_system: if an argument is given it must be one '
|
|
f'of: "preserve", "system", "non-system", not: "{args[0]}"')
|
|
else:
|
|
include_type = T.cast('IncludeType', args[0])
|
|
return self.held_object.generate_system_dependency(include_type)
|
|
|
|
@FeatureNew('dependency.as_link_whole', '0.56.0')
|
|
@noKwargs
|
|
@noPosargs
|
|
@InterpreterObject.method('as_link_whole')
|
|
def as_link_whole_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> Dependency:
|
|
if not isinstance(self.held_object, InternalDependency):
|
|
raise InterpreterException('as_link_whole method is only supported on declare_dependency() objects')
|
|
new_dep = self.held_object.generate_link_whole_dependency()
|
|
return new_dep
|
|
|
|
@FeatureNew('dependency.as_static', '1.6.0')
|
|
@noPosargs
|
|
@typed_kwargs(
|
|
'dependency.as_static',
|
|
KwargInfo('recursive', bool, default=False),
|
|
)
|
|
@InterpreterObject.method('as_static')
|
|
def as_static_method(self, args: T.List[TYPE_var], kwargs: InternalDependencyAsKW) -> Dependency:
|
|
if not isinstance(self.held_object, InternalDependency):
|
|
raise InterpreterException('as_static method is only supported on declare_dependency() objects')
|
|
return self.held_object.get_as_static(kwargs['recursive'])
|
|
|
|
@FeatureNew('dependency.as_shared', '1.6.0')
|
|
@noPosargs
|
|
@typed_kwargs(
|
|
'dependency.as_shared',
|
|
KwargInfo('recursive', bool, default=False),
|
|
)
|
|
@InterpreterObject.method('as_shared')
|
|
def as_shared_method(self, args: T.List[TYPE_var], kwargs: InternalDependencyAsKW) -> Dependency:
|
|
if not isinstance(self.held_object, InternalDependency):
|
|
raise InterpreterException('as_shared method is only supported on declare_dependency() objects')
|
|
return self.held_object.get_as_shared(kwargs['recursive'])
|
|
|
|
_PROG = T.TypeVar('_PROG', bound=Program)
|
|
|
|
class ProgramHolder(ObjectHolder[_PROG]):
|
|
def __init__(self, ep: _PROG, interpreter: 'Interpreter') -> None:
|
|
super().__init__(ep, interpreter)
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('found')
|
|
def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
|
|
return self.found()
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@FeatureDeprecated('Program.path', '0.55.0',
|
|
'use Program.full_path() instead')
|
|
@InterpreterObject.method('path')
|
|
def path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self._full_path()
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@FeatureNew('Program.full_path', '0.55.0')
|
|
@InterpreterObject.method('full_path')
|
|
def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self._full_path()
|
|
|
|
def _full_path(self) -> str:
|
|
if not self.found():
|
|
raise InterpreterException('Unable to get the path of a not-found external program')
|
|
if not self.held_object.runnable():
|
|
assert isinstance(self.held_object, build.LocalProgram) and \
|
|
isinstance(self.held_object.program, (build.BuildTarget, build.CustomTargetIndex))
|
|
return self.interpreter.backend.get_target_filename_abs(self.held_object.program)
|
|
path = self.held_object.get_path()
|
|
assert path is not None
|
|
return path
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@FeatureNew('Program.cmd_array', '1.10.0')
|
|
@InterpreterObject.method('cmd_array')
|
|
def cmd_array_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[str]:
|
|
if not self.found():
|
|
raise InterpreterException('Unable to get the path of a not-found external program')
|
|
if not self.held_object.runnable():
|
|
return [self._full_path()]
|
|
cmd = self.held_object.get_command()
|
|
assert cmd is not None
|
|
return cmd
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@FeatureNew('Program.version', '0.62.0')
|
|
@InterpreterObject.method('version')
|
|
def version_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
if isinstance(self.held_object, build.LocalProgram) and isinstance(self.held_object.program, build.Executable):
|
|
FeatureNew.single_use('Program.version with an executable', '1.9.0', subproject=self.subproject, location=self.current_node)
|
|
if not self.found():
|
|
raise InterpreterException('Unable to get the version of a not-found external program')
|
|
try:
|
|
return self.held_object.get_version(self.interpreter)
|
|
except mesonlib.MesonException:
|
|
return 'unknown'
|
|
|
|
def found(self) -> bool:
|
|
return self.held_object.found()
|
|
|
|
|
|
class ExternalLibraryHolder(ObjectHolder[ExternalLibrary]):
|
|
def __init__(self, el: ExternalLibrary, interpreter: 'Interpreter'):
|
|
super().__init__(el, interpreter)
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('type_name')
|
|
def type_name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self.held_object.type_name
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('found')
|
|
def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
|
|
return self.held_object.found()
|
|
|
|
@FeatureNew('dependency.partial_dependency', '0.46.0')
|
|
@noPosargs
|
|
@typed_kwargs('dependency.partial_dependency', *_PARTIAL_DEP_KWARGS)
|
|
@InterpreterObject.method('partial_dependency')
|
|
def partial_dependency_method(self, args: T.List[TYPE_nvar], kwargs: 'kwargs.DependencyMethodPartialDependency') -> Dependency:
|
|
pdep = self.held_object.get_partial_dependency(**kwargs)
|
|
return pdep
|
|
|
|
@FeatureNew('dependency.name', '1.5.0')
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('name')
|
|
def name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self.held_object.name
|
|
|
|
|
|
# A machine that's statically known from the cross file
|
|
class MachineHolder(ObjectHolder['MachineInfo']):
|
|
def __init__(self, machine_info: 'MachineInfo', interpreter: 'Interpreter'):
|
|
super().__init__(machine_info, interpreter)
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('cpu_family')
|
|
def cpu_family_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self.held_object.cpu_family
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('cpu')
|
|
def cpu_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self.held_object.cpu
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('system')
|
|
def system_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self.held_object.system
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('endian')
|
|
def endian_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self.held_object.endian
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('kernel')
|
|
def kernel_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
if self.held_object.kernel is not None:
|
|
return self.held_object.kernel
|
|
raise InterpreterException('Kernel not defined or could not be autodetected.')
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('subsystem')
|
|
def subsystem_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
if self.held_object.subsystem is not None:
|
|
return self.held_object.subsystem
|
|
raise InterpreterException('Subsystem not defined or could not be autodetected.')
|
|
|
|
|
|
class IncludeDirsHolder(ObjectHolder[build.IncludeDirs]):
|
|
pass
|
|
|
|
class FileHolder(ObjectHolder[mesonlib.File]):
|
|
def __init__(self, file: mesonlib.File, interpreter: 'Interpreter'):
|
|
super().__init__(file, interpreter)
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@FeatureNew('file.full_path', '1.4.0')
|
|
@InterpreterObject.method('full_path')
|
|
def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self.held_object.absolute_path(self.env.source_dir, self.env.build_dir)
|
|
|
|
class HeadersHolder(ObjectHolder[build.Headers]):
|
|
pass
|
|
|
|
class DataHolder(ObjectHolder[build.Data]):
|
|
pass
|
|
|
|
class SymlinkDataHolder(ObjectHolder[build.SymlinkData]):
|
|
pass
|
|
|
|
class InstallDirHolder(ObjectHolder[build.InstallDir]):
|
|
pass
|
|
|
|
class ManHolder(ObjectHolder[build.Man]):
|
|
pass
|
|
|
|
class EmptyDirHolder(ObjectHolder[build.EmptyDir]):
|
|
pass
|
|
|
|
class GeneratedObjectsHolder(ObjectHolder[build.ExtractedObjects]):
|
|
pass
|
|
|
|
class Test(MesonInterpreterObject):
|
|
def __init__(self, name: str, project: str, suite: T.List[str],
|
|
exe: T.Union[Program, build.Executable, build.CustomTarget, build.CustomTargetIndex, build.Jar],
|
|
depends: T.Sequence[kwargs.TargetDepends],
|
|
is_parallel: bool,
|
|
cmd_args: T.List[build.CommandTypes],
|
|
env: mesonlib.EnvironmentVariables,
|
|
expected_fail: bool, expected_exitcode: int, timeout: int, workdir: T.Optional[str], protocol: str,
|
|
priority: int, verbose: bool):
|
|
super().__init__()
|
|
self.name = name
|
|
self.suite = listify(suite)
|
|
self.project_name = project
|
|
self.exe = exe
|
|
self.is_parallel = is_parallel
|
|
self.cmd_args = cmd_args
|
|
self.env = env
|
|
self.expected_fail = expected_fail
|
|
self.expected_exitcode = expected_exitcode
|
|
self.timeout = timeout
|
|
self.workdir = workdir
|
|
self.protocol = TestProtocol.from_str(protocol)
|
|
self.priority = priority
|
|
self.verbose = verbose
|
|
|
|
self.depends: T.List[build.BuildTargetTypes] = []
|
|
for d in depends:
|
|
if isinstance(d, build.GeneratedList):
|
|
raise MesonException('generated_list not allowed as a dependency for test')
|
|
if isinstance(d, Program):
|
|
if not isinstance(d, build.LocalProgram):
|
|
continue
|
|
d = d.get_target()
|
|
if isinstance(d, File):
|
|
continue
|
|
self.depends.append(d)
|
|
|
|
def get_exe(self) -> T.Union[Program, build.Executable, build.Jar, build.CustomTarget, build.CustomTargetIndex]:
|
|
return self.exe
|
|
|
|
def get_name(self) -> str:
|
|
return self.name
|
|
|
|
|
|
class Doctest(Test):
|
|
target: T.Optional[build.BuildTarget] = None
|
|
|
|
|
|
class NullSubprojectInterpreter(HoldableObject):
|
|
pass
|
|
|
|
# TODO: This should really be an `ObjectHolder`, but the additional stuff in this
|
|
# class prevents this. Thus, this class should be split into a pure
|
|
# `ObjectHolder` and a class specifically for storing in `Interpreter`.
|
|
class SubprojectHolder(MesonInterpreterObject):
|
|
|
|
def __init__(self, subinterpreter: T.Union['Interpreter', NullSubprojectInterpreter],
|
|
subdir: str,
|
|
warnings: int = 0,
|
|
disabled_feature: T.Optional[str] = None,
|
|
exception: T.Optional[Exception] = None,
|
|
callstack: T.Optional[T.List[T.Tuple[str, MachineChoice]]] = None) -> None:
|
|
super().__init__()
|
|
self.held_object = subinterpreter
|
|
self.warnings = warnings
|
|
self.disabled_feature = disabled_feature
|
|
self.exception = exception
|
|
self.subdir = PurePath(subdir).as_posix()
|
|
self.cm_interpreter: T.Optional[CMakeInterpreter] = None
|
|
self.callstack = callstack
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('found')
|
|
def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
|
|
return self.found()
|
|
|
|
def found(self) -> bool:
|
|
return not isinstance(self.held_object, NullSubprojectInterpreter)
|
|
|
|
@unholder_return
|
|
def get_variable(self, args: T.Tuple[str, T.Optional[str]], kwargs: TYPE_kwargs) -> T.Union[TYPE_var, InterpreterObject]:
|
|
if isinstance(self.held_object, NullSubprojectInterpreter): # == not self.found()
|
|
raise InterpreterException(f'Subproject "{self.subdir}" disabled can\'t get_variable on it.')
|
|
varname, fallback = args
|
|
try:
|
|
return self.held_object.variables[varname]
|
|
except KeyError:
|
|
if fallback is not None:
|
|
return self.held_object._holderify(fallback)
|
|
ustr = f'Requested variable "{varname}" not found.'
|
|
from difflib import get_close_matches
|
|
close_matches = get_close_matches(varname, self.held_object.variables.keys())
|
|
if close_matches:
|
|
ustr += f' Did you mean "{close_matches[0]}"?'
|
|
raise InvalidArguments(ustr)
|
|
|
|
@noKwargs
|
|
@typed_pos_args('subproject.get_variable', str, optargs=[object])
|
|
@noArgsFlattening
|
|
@InterpreterObject.method('get_variable')
|
|
def get_variable_method(self, args: T.Tuple[str, T.Optional[str]], kwargs: TYPE_kwargs) -> T.Union[TYPE_var, InterpreterObject]:
|
|
return self.get_variable(args, kwargs)
|
|
|
|
class ModuleObjectHolder(ObjectHolder[ModuleObject]):
|
|
def method_call(self, method_name: str, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> TYPE_var:
|
|
modobj = self.held_object
|
|
method = modobj.methods.get(method_name)
|
|
if not method:
|
|
ustr = f'Unknown method "{method_name}" in object.'
|
|
from difflib import get_close_matches
|
|
close_matches = get_close_matches(method_name, modobj.methods.keys())
|
|
if close_matches:
|
|
ustr += f' Did you mean "{close_matches[0]}"?'
|
|
raise InvalidCode(ustr)
|
|
if not getattr(method, 'no-args-flattening', False):
|
|
args = flatten(args)
|
|
if not getattr(method, 'no-second-level-holder-flattening', False):
|
|
args, kwargs = resolve_second_level_holders(args, kwargs)
|
|
if not self.interpreter.active_projectname:
|
|
assert isinstance(modobj, (ExtensionModule, NewExtensionModule)), 'for mypy'
|
|
full_method_name = f'{modobj.INFO.name}.{method_name}'
|
|
raise mesonlib.MesonException(f'Module methods ({full_method_name}) cannot be invoked during project declaration.')
|
|
state = ModuleState(self.interpreter)
|
|
# Many modules do for example self.interpreter.find_program_impl(),
|
|
# so we have to ensure they use the current interpreter and not the one
|
|
# that first imported that module, otherwise it will use outdated
|
|
# overrides.
|
|
if isinstance(modobj, ExtensionModule):
|
|
modobj.interpreter = self.interpreter
|
|
ret = method(state, args, kwargs)
|
|
if isinstance(ret, ModuleReturnValue):
|
|
self.interpreter.process_new_values(ret.new_objects)
|
|
ret = ret.return_value
|
|
return ret
|
|
|
|
class MutableModuleObjectHolder(ModuleObjectHolder, MutableInterpreterObject):
|
|
def __deepcopy__(self, memo: T.Dict[int, T.Any]) -> 'MutableModuleObjectHolder':
|
|
# Deepcopy only held object, not interpreter
|
|
modobj = copy.deepcopy(self.held_object, memo)
|
|
return MutableModuleObjectHolder(modobj, self.interpreter)
|
|
|
|
|
|
_BuildTarget = T.TypeVar('_BuildTarget', bound=T.Union[build.BuildTarget, build.BothLibraries])
|
|
|
|
class BuildTargetHolder(ObjectHolder[_BuildTarget]):
|
|
def __init__(self, target: _BuildTarget, interp: 'Interpreter'):
|
|
super().__init__(target, interp)
|
|
|
|
def __repr__(self) -> str:
|
|
r = '<{} {}: {}>'
|
|
h = self.held_object
|
|
assert isinstance(h, build.BuildTarget)
|
|
return r.format(self.__class__.__name__, h.get_id(), h.filename)
|
|
|
|
@property
|
|
def _target_object(self) -> build.BuildTarget:
|
|
if isinstance(self.held_object, build.BothLibraries):
|
|
return self.held_object.get_default_object()
|
|
assert isinstance(self.held_object, build.BuildTarget)
|
|
return self.held_object
|
|
|
|
def is_cross(self) -> bool:
|
|
return not self._target_object.environment.machines.matches_build_machine(self._target_object.for_machine)
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('found')
|
|
@FeatureNew('BuildTarget.found', '0.59.0')
|
|
def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
|
|
return True
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('private_dir_include')
|
|
def private_dir_include_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> build.IncludeDirs:
|
|
return build.IncludeDirs('', [], False, self.interpreter.current_build_project(),
|
|
[self.interpreter.backend.get_target_private_dir(self._target_object)])
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('full_path')
|
|
def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self.interpreter.backend.get_target_filename_abs(self._target_object)
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@FeatureDeprecated('BuildTarget.path', '0.55.0', 'Use BuildTarget.full_path instead')
|
|
@InterpreterObject.method('path')
|
|
def path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self.interpreter.backend.get_target_filename_abs(self._target_object)
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('outdir')
|
|
def outdir_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self.interpreter.backend.get_target_dir(self._target_object)
|
|
|
|
@noKwargs
|
|
@typed_pos_args('extract_objects', varargs=(mesonlib.File, str, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList))
|
|
@InterpreterObject.method('extract_objects')
|
|
def extract_objects_method(self, args: T.Tuple[T.List[str | build.TargetSources]], kwargs: TYPE_nkwargs) -> build.ExtractedObjects:
|
|
if self.subproject != self.held_object.subproject:
|
|
raise InterpreterException('Tried to extract objects from a different subproject.')
|
|
tobj = self._target_object
|
|
unity_value = self.interpreter.coredata.get_option_for_target(tobj, "unity")
|
|
is_unity = (unity_value == 'on' or (unity_value == 'subprojects' and tobj.subproject != ''))
|
|
|
|
obj_src: list[mesonlib.File | build.GeneratedTypes] = []
|
|
for raw_src in args[0]:
|
|
if isinstance(raw_src, str):
|
|
src = File(False, tobj.subdir, raw_src)
|
|
else:
|
|
if isinstance(raw_src, File):
|
|
FeatureNew.single_use('File argument for extract_objects', '0.50.0', self.subproject)
|
|
src = raw_src
|
|
obj_src.append(src)
|
|
|
|
return tobj.extract_objects(obj_src, is_unity)
|
|
|
|
@noPosargs
|
|
@typed_kwargs(
|
|
'extract_all_objects',
|
|
KwargInfo(
|
|
'recursive', bool, default=False, since='0.46.0',
|
|
not_set_warning=textwrap.dedent('''\
|
|
extract_all_objects called without setting recursive
|
|
keyword argument. Meson currently defaults to
|
|
non-recursive to maintain backward compatibility but
|
|
the default will be changed in meson 2.0.
|
|
''')
|
|
)
|
|
)
|
|
@InterpreterObject.method('extract_all_objects')
|
|
def extract_all_objects_method(self, args: T.List[TYPE_nvar], kwargs: 'kwargs.BuildTargeMethodExtractAllObjects') -> build.ExtractedObjects:
|
|
return self._target_object.extract_all_objects(kwargs['recursive'])
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@FeatureDeprecated('BuildTarget.get_id', '1.2.0',
|
|
'This was never formally documented and does not seem to have a real world use. ' +
|
|
'See https://github.com/mesonbuild/meson/pull/6061')
|
|
@InterpreterObject.method('get_id')
|
|
def get_id_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self._target_object.get_id()
|
|
|
|
@FeatureNew('name', '0.54.0')
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('name')
|
|
def name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self._target_object.name
|
|
|
|
@FeatureNew('vala_header', '1.10.0')
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('vala_header')
|
|
def vala_header_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> mesonlib.File:
|
|
if self._target_object.vala_header is None:
|
|
raise mesonlib.MesonException("Attempted to get a Vala header from a target that doesn't generate one")
|
|
|
|
assert self.interpreter.backend is not None, 'for mypy'
|
|
return mesonlib.File.from_built_file(
|
|
self.interpreter.backend.get_target_dir(self._target_object), self._target_object.vala_header)
|
|
|
|
@FeatureNew('vala_vapi', '1.10.0')
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('vala_vapi')
|
|
def vala_vapi_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> mesonlib.File:
|
|
if self._target_object.vala_vapi is None:
|
|
raise mesonlib.MesonException("Attempted to get a Vala VAPI from a target that doesn't generate one")
|
|
|
|
assert self.interpreter.backend is not None, 'for mypy'
|
|
return mesonlib.File.from_built_file(
|
|
self.interpreter.backend.get_target_dir(self._target_object), self._target_object.vala_vapi)
|
|
|
|
@FeatureNew('vala_gir', '1.10.0')
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('vala_gir')
|
|
def vala_gir_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> mesonlib.File:
|
|
if self._target_object.vala_gir is None:
|
|
raise mesonlib.MesonException("Attempted to get a Vala GIR from a target that doesn't generate one")
|
|
|
|
assert self.interpreter.backend is not None, 'for mypy'
|
|
return mesonlib.File.from_built_file(
|
|
self.interpreter.backend.get_target_dir(self._target_object), self._target_object.vala_gir)
|
|
|
|
|
|
class ExecutableHolder(BuildTargetHolder[build.Executable]):
|
|
pass
|
|
|
|
class StaticLibraryHolder(BuildTargetHolder[build.StaticLibrary]):
|
|
pass
|
|
|
|
class SharedLibraryHolder(BuildTargetHolder[build.SharedLibrary]):
|
|
pass
|
|
|
|
class BothLibrariesHolder(BuildTargetHolder[build.BothLibraries]):
|
|
def __init__(self, libs: build.BothLibraries, interp: 'Interpreter'):
|
|
super().__init__(libs, interp)
|
|
|
|
def __repr__(self) -> str:
|
|
r = '<{} {}: {}, {}: {}>'
|
|
h1 = self.held_object.shared
|
|
h2 = self.held_object.static
|
|
return r.format(self.__class__.__name__, h1.get_id(), h1.filename, h2.get_id(), h2.filename)
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('get_shared_lib')
|
|
def get_shared_lib_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> build.SharedLibrary:
|
|
lib = copy.copy(self.held_object.shared)
|
|
lib.both_lib = None
|
|
return lib
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('get_static_lib')
|
|
def get_static_lib_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> build.StaticLibrary:
|
|
lib = copy.copy(self.held_object.static)
|
|
lib.both_lib = None
|
|
return lib
|
|
|
|
class SharedModuleHolder(BuildTargetHolder[build.SharedModule]):
|
|
pass
|
|
|
|
class JarHolder(BuildTargetHolder[build.Jar]):
|
|
pass
|
|
|
|
class CustomTargetIndexHolder(ObjectHolder[build.CustomTargetIndex]):
|
|
def __init__(self, target: build.CustomTargetIndex, interp: 'Interpreter'):
|
|
super().__init__(target, interp)
|
|
|
|
@FeatureNew('custom_target[i].full_path', '0.54.0')
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('full_path')
|
|
def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
assert self.interpreter.backend is not None
|
|
return self.interpreter.backend.get_target_filename_abs(self.held_object)
|
|
|
|
_CT = T.TypeVar('_CT', bound=build.CustomTarget)
|
|
|
|
class _CustomTargetHolder(ObjectHolder[_CT]):
|
|
def __init__(self, target: _CT, interp: 'Interpreter'):
|
|
super().__init__(target, interp)
|
|
|
|
def __repr__(self) -> str:
|
|
r = '<{} {}: {}>'
|
|
h = self.held_object
|
|
return r.format(self.__class__.__name__, h.get_id(), h.command)
|
|
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('full_path')
|
|
def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
|
|
return self.interpreter.backend.get_target_filename_abs(self.held_object)
|
|
|
|
@FeatureNew('custom_target.to_list', '0.54.0')
|
|
@noPosargs
|
|
@noKwargs
|
|
@InterpreterObject.method('to_list')
|
|
def to_list_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[build.CustomTargetIndex]:
|
|
return list(self.held_object)
|
|
|
|
@noKwargs
|
|
@typed_operator(MesonOperator.INDEX, int)
|
|
@InterpreterObject.operator(MesonOperator.INDEX)
|
|
def op_index(self, other: int) -> build.CustomTargetIndex:
|
|
try:
|
|
return self.held_object[other]
|
|
except IndexError:
|
|
raise InvalidArguments(f'Index {other} out of bounds of custom target {self.held_object.name} output of size {len(self.held_object)}.')
|
|
|
|
class CustomTargetHolder(_CustomTargetHolder[build.CustomTarget]):
|
|
pass
|
|
|
|
class RunTargetHolder(ObjectHolder[build.RunTarget]):
|
|
pass
|
|
|
|
class AliasTargetHolder(ObjectHolder[build.AliasTarget]):
|
|
pass
|
|
|
|
class GeneratedListHolder(ObjectHolder[build.GeneratedList]):
|
|
pass
|
|
|
|
class GeneratorHolder(ObjectHolder[build.Generator]):
|
|
def __init__(self, gen: build.Generator, interpreter: 'Interpreter'):
|
|
super().__init__(gen, interpreter)
|
|
|
|
@typed_pos_args('generator.process', min_varargs=1, varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList))
|
|
@typed_kwargs(
|
|
'generator.process',
|
|
KwargInfo('preserve_path_from', (str, NoneType), since='0.45.0'),
|
|
KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]),
|
|
ENV_KW.evolve(since='1.3.0'),
|
|
KwargInfo('depends', ContainerTypeInfo(list, (build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)), listify=True, default=[], since='1.12.0'),
|
|
)
|
|
@InterpreterObject.method('process')
|
|
def process_method(self,
|
|
args: T.Tuple[T.List[str | build.TargetSources]],
|
|
kwargs: 'kwargs.GeneratorProcess') -> build.GeneratedList:
|
|
preserve_path_from = kwargs['preserve_path_from']
|
|
if preserve_path_from is not None:
|
|
preserve_path_from = os.path.normpath(preserve_path_from)
|
|
if not os.path.isabs(preserve_path_from):
|
|
# This is a bit of a hack. Fix properly before merging.
|
|
raise InvalidArguments('Preserve_path_from must be an absolute path for now. Sorry.')
|
|
|
|
if any(isinstance(a, (build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) for a in args[0]):
|
|
FeatureNew.single_use(
|
|
'Calling generator.process with CustomTarget or Index of CustomTarget.',
|
|
'0.57.0', self.interpreter.subproject)
|
|
|
|
sources = self.interpreter.source_strings_to_files(args[0])
|
|
gl = self.held_object.process_files(sources, self.interpreter.subdir,
|
|
preserve_path_from, extra_args=kwargs['extra_args'], env=kwargs['env'],
|
|
extra_depends=kwargs['depends'])
|
|
|
|
return gl
|
|
|
|
|
|
class StructuredSourcesHolder(ObjectHolder[build.StructuredSources]):
|
|
|
|
def __init__(self, sources: build.StructuredSources, interp: 'Interpreter'):
|
|
super().__init__(sources, interp)
|