typing: more fixes

pull/7657/head
Daniel Mensinger 4 years ago
parent 1b19822a2d
commit 23818fc5a3
No known key found for this signature in database
GPG Key ID: 54DD94C131E277D4
  1. 3
      mesonbuild/ast/introspection.py
  2. 18
      mesonbuild/backend/backends.py
  3. 2
      mesonbuild/backend/ninjabackend.py
  4. 4
      mesonbuild/backend/vs2010backend.py
  5. 51
      mesonbuild/build.py
  6. 81
      mesonbuild/coredata.py
  7. 4
      mesonbuild/dependencies/boost.py
  8. 25
      mesonbuild/environment.py
  9. 42
      mesonbuild/interpreter.py
  10. 6
      mesonbuild/mcompile.py
  11. 8
      mesonbuild/mconf.py
  12. 19
      mesonbuild/mintro.py
  13. 12
      mesonbuild/modules/__init__.py
  14. 18
      mesonbuild/msetup.py
  15. 67
      mesonbuild/mtest.py
  16. 11
      mesonbuild/optinterpreter.py
  17. 2
      mesonbuild/scripts/depfixer.py
  18. 2
      mesonbuild/scripts/meson_exe.py
  19. 1
      run_mypy.py

@ -25,10 +25,11 @@ from ..build import BuildTarget, Executable, Jar, SharedLibrary, SharedModule, S
from ..mparser import BaseNode, ArithmeticNode, ArrayNode, ElementaryNode, IdNode, FunctionNode, StringNode from ..mparser import BaseNode, ArithmeticNode, ArrayNode, ElementaryNode, IdNode, FunctionNode, StringNode
import typing as T import typing as T
import os import os
import argparse
build_target_functions = ['executable', 'jar', 'library', 'shared_library', 'shared_module', 'static_library', 'both_libraries'] build_target_functions = ['executable', 'jar', 'library', 'shared_library', 'shared_module', 'static_library', 'both_libraries']
class IntrospectionHelper: class IntrospectionHelper(argparse.Namespace):
# mimic an argparse namespace # mimic an argparse namespace
def __init__(self, cross_file: str): def __init__(self, cross_file: str):
self.cross_file = cross_file # type: str self.cross_file = cross_file # type: str

@ -36,7 +36,7 @@ from ..mesonlib import (
) )
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from ..interpreter import Interpreter from ..interpreter import Interpreter, Test
class TestProtocol(enum.Enum): class TestProtocol(enum.Enum):
@ -104,7 +104,7 @@ class TargetInstallData:
class ExecutableSerialisation: class ExecutableSerialisation:
def __init__(self, cmd_args, env=None, exe_wrapper=None, def __init__(self, cmd_args, env=None, exe_wrapper=None,
workdir=None, extra_paths=None, capture=None): workdir=None, extra_paths=None, capture=None) -> None:
self.cmd_args = cmd_args self.cmd_args = cmd_args
self.env = env or {} self.env = env or {}
if exe_wrapper is not None: if exe_wrapper is not None:
@ -182,11 +182,15 @@ class Backend:
self.interpreter = interpreter self.interpreter = interpreter
self.environment = build.environment self.environment = build.environment
self.processed_targets = {} self.processed_targets = {}
self.name = '<UNKNOWN>'
self.build_dir = self.environment.get_build_dir() self.build_dir = self.environment.get_build_dir()
self.source_dir = self.environment.get_source_dir() self.source_dir = self.environment.get_source_dir()
self.build_to_src = mesonlib.relpath(self.environment.get_source_dir(), self.build_to_src = mesonlib.relpath(self.environment.get_source_dir(),
self.environment.get_build_dir()) self.environment.get_build_dir())
def generate(self) -> None:
raise RuntimeError('generate is not implemented in {}'.format(type(self).__name__))
def get_target_filename(self, t, *, warn_multi_output: bool = True): def get_target_filename(self, t, *, warn_multi_output: bool = True):
if isinstance(t, build.CustomTarget): if isinstance(t, build.CustomTarget):
if warn_multi_output and len(t.get_outputs()) != 1: if warn_multi_output and len(t.get_outputs()) != 1:
@ -794,7 +798,7 @@ class Backend:
def write_test_file(self, datafile): def write_test_file(self, datafile):
self.write_test_serialisation(self.build.get_tests(), datafile) self.write_test_serialisation(self.build.get_tests(), datafile)
def create_test_serialisation(self, tests): def create_test_serialisation(self, tests: T.List['Test']) -> T.List[TestSerialisation]:
arr = [] arr = []
for t in sorted(tests, key=lambda tst: -1 * tst.priority): for t in sorted(tests, key=lambda tst: -1 * tst.priority):
exe = t.get_exe() exe = t.get_exe()
@ -864,7 +868,7 @@ class Backend:
arr.append(ts) arr.append(ts)
return arr return arr
def write_test_serialisation(self, tests, datafile): def write_test_serialisation(self, tests: T.List['Test'], datafile: str):
pickle.dump(self.create_test_serialisation(tests), datafile) pickle.dump(self.create_test_serialisation(tests), datafile)
def construct_target_rel_path(self, a, workdir): def construct_target_rel_path(self, a, workdir):
@ -1128,7 +1132,7 @@ class Backend:
cmd = [i.replace('\\', '/') for i in cmd] cmd = [i.replace('\\', '/') for i in cmd]
return inputs, outputs, cmd return inputs, outputs, cmd
def run_postconf_scripts(self): def run_postconf_scripts(self) -> None:
env = {'MESON_SOURCE_ROOT': self.environment.get_source_dir(), env = {'MESON_SOURCE_ROOT': self.environment.get_source_dir(),
'MESON_BUILD_ROOT': self.environment.get_build_dir(), 'MESON_BUILD_ROOT': self.environment.get_build_dir(),
'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in self.environment.get_build_command() + ['introspect']]), 'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in self.environment.get_build_command() + ['introspect']]),
@ -1140,7 +1144,7 @@ class Backend:
cmd = s['exe'] + s['args'] cmd = s['exe'] + s['args']
subprocess.check_call(cmd, env=child_env) subprocess.check_call(cmd, env=child_env)
def create_install_data(self): def create_install_data(self) -> InstallData:
strip_bin = self.environment.lookup_binary_entry(MachineChoice.HOST, 'strip') strip_bin = self.environment.lookup_binary_entry(MachineChoice.HOST, 'strip')
if strip_bin is None: if strip_bin is None:
if self.environment.is_cross_build(): if self.environment.is_cross_build():
@ -1338,7 +1342,7 @@ class Backend:
d.install_subdirs.append([src_dir, dst_dir, sd.install_mode, d.install_subdirs.append([src_dir, dst_dir, sd.install_mode,
sd.exclude]) sd.exclude])
def get_introspection_data(self, target_id, target): def get_introspection_data(self, target_id: str, target: build.Target) -> T.List[T.Dict[str, T.Union[bool, str, T.List[T.Union[str, T.Dict[str, T.Union[str, T.List[str], bool]]]]]]]:
''' '''
Returns a list of source dicts with the following format for a given target: Returns a list of source dicts with the following format for a given target:
[ [

@ -2997,7 +2997,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
elem = NinjaBuildElement(self.all_outputs, deps, 'phony', '') elem = NinjaBuildElement(self.all_outputs, deps, 'phony', '')
self.add_build(elem) self.add_build(elem)
def get_introspection_data(self, target_id, target): def get_introspection_data(self, target_id: str, target: build.Target) -> T.List[T.Dict[str, T.Union[bool, str, T.List[T.Union[str, T.Dict[str, T.Union[str, T.List[str], bool]]]]]]]:
if target_id not in self.introspection_data or len(self.introspection_data[target_id]) == 0: if target_id not in self.introspection_data or len(self.introspection_data[target_id]) == 0:
return super().get_introspection_data(target_id, target) return super().get_introspection_data(target_id, target)

@ -192,11 +192,11 @@ class Vs2010Backend(backends.Backend):
Vs2010Backend.touch_regen_timestamp(self.environment.get_build_dir()) Vs2010Backend.touch_regen_timestamp(self.environment.get_build_dir())
@staticmethod @staticmethod
def get_regen_stampfile(build_dir): def get_regen_stampfile(build_dir: str) -> None:
return os.path.join(os.path.join(build_dir, Environment.private_dir), 'regen.stamp') return os.path.join(os.path.join(build_dir, Environment.private_dir), 'regen.stamp')
@staticmethod @staticmethod
def touch_regen_timestamp(build_dir): def touch_regen_timestamp(build_dir: str) -> None:
with open(Vs2010Backend.get_regen_stampfile(build_dir), 'w'): with open(Vs2010Backend.get_regen_stampfile(build_dir), 'w'):
pass pass

@ -37,6 +37,9 @@ from .compilers import (
from .linkers import StaticLinker from .linkers import StaticLinker
from .interpreterbase import FeatureNew from .interpreterbase import FeatureNew
if T.TYPE_CHECKING:
from .interpreter import Test
pch_kwargs = set(['c_pch', 'cpp_pch']) pch_kwargs = set(['c_pch', 'cpp_pch'])
lang_arg_kwargs = set([ lang_arg_kwargs = set([
@ -128,14 +131,14 @@ class Build:
self.project_version = None self.project_version = None
self.environment = environment self.environment = environment
self.projects = {} self.projects = {}
self.targets = OrderedDict() self.targets = OrderedDict() # type: T.Dict[str, 'Target']
self.run_target_names = set() # type: T.Set[T.Tuple[str, str]] self.run_target_names = set() # type: T.Set[T.Tuple[str, str]]
self.global_args = PerMachine({}, {}) # type: PerMachine[T.Dict[str, T.List[str]]] self.global_args = PerMachine({}, {}) # type: PerMachine[T.Dict[str, T.List[str]]]
self.projects_args = PerMachine({}, {}) # type: PerMachine[T.Dict[str, T.List[str]]] self.projects_args = PerMachine({}, {}) # type: PerMachine[T.Dict[str, T.List[str]]]
self.global_link_args = PerMachine({}, {}) # type: PerMachine[T.Dict[str, T.List[str]]] self.global_link_args = PerMachine({}, {}) # type: PerMachine[T.Dict[str, T.List[str]]]
self.projects_link_args = PerMachine({}, {}) # type: PerMachine[T.Dict[str, T.List[str]]] self.projects_link_args = PerMachine({}, {}) # type: PerMachine[T.Dict[str, T.List[str]]]
self.tests = [] self.tests = [] # type: T.List['Test']
self.benchmarks = [] self.benchmarks = [] # type: T.List['Test']
self.headers = [] self.headers = []
self.man = [] self.man = []
self.data = [] self.data = []
@ -178,13 +181,13 @@ class Build:
def get_subproject_dir(self): def get_subproject_dir(self):
return self.subproject_dir return self.subproject_dir
def get_targets(self): def get_targets(self) -> T.Dict[str, 'Target']:
return self.targets return self.targets
def get_tests(self): def get_tests(self) -> T.List['Test']:
return self.tests return self.tests
def get_benchmarks(self): def get_benchmarks(self) -> T.List['Test']:
return self.benchmarks return self.benchmarks
def get_headers(self): def get_headers(self):
@ -403,13 +406,13 @@ a hard error in the future.'''.format(name))
outdirs[0] = default_install_dir outdirs[0] = default_install_dir
return outdirs, custom_install_dir return outdirs, custom_install_dir
def get_basename(self): def get_basename(self) -> str:
return self.name return self.name
def get_subdir(self): def get_subdir(self) -> str:
return self.subdir return self.subdir
def get_typename(self): def get_typename(self) -> str:
return self.typename return self.typename
@staticmethod @staticmethod
@ -423,7 +426,7 @@ a hard error in the future.'''.format(name))
return h.hexdigest()[:7] return h.hexdigest()[:7]
@staticmethod @staticmethod
def construct_id_from_path(subdir, name, type_suffix): def construct_id_from_path(subdir: str, name: str, type_suffix: str) -> str:
"""Construct target ID from subdir, name and type suffix. """Construct target ID from subdir, name and type suffix.
This helper function is made public mostly for tests.""" This helper function is made public mostly for tests."""
@ -441,7 +444,7 @@ a hard error in the future.'''.format(name))
return subdir_part + '@@' + my_id return subdir_part + '@@' + my_id
return my_id return my_id
def get_id(self): def get_id(self) -> str:
return self.construct_id_from_path( return self.construct_id_from_path(
self.subdir, self.name, self.type_suffix()) self.subdir, self.name, self.type_suffix())
@ -480,6 +483,12 @@ a hard error in the future.'''.format(name))
def is_linkable_target(self) -> bool: def is_linkable_target(self) -> bool:
return False return False
def get_outputs(self) -> T.List[str]:
return []
def should_install(self) -> bool:
return False
class BuildTarget(Target): class BuildTarget(Target):
known_kwargs = known_build_target_kwargs known_kwargs = known_build_target_kwargs
@ -1006,7 +1015,7 @@ This will become a hard error in a future Meson release.''')
def get_filename(self): def get_filename(self):
return self.filename return self.filename
def get_outputs(self): def get_outputs(self) -> T.List[str]:
return self.outputs return self.outputs
def get_extra_args(self, language): def get_extra_args(self, language):
@ -1036,7 +1045,7 @@ This will become a hard error in a future Meson release.''')
def get_generated_sources(self): def get_generated_sources(self):
return self.generated return self.generated
def should_install(self): def should_install(self) -> bool:
return self.need_install return self.need_install
def has_pch(self): def has_pch(self):
@ -1474,7 +1483,7 @@ class GeneratedList:
def get_inputs(self): def get_inputs(self):
return self.infilelist return self.infilelist
def get_outputs(self): def get_outputs(self) -> T.List[str]:
return self.outfilelist return self.outfilelist
def get_outputs_for(self, filename): def get_outputs_for(self, filename):
@ -2192,7 +2201,7 @@ class CustomTarget(Target):
def get_dependencies(self): def get_dependencies(self):
return self.dependencies return self.dependencies
def should_install(self): def should_install(self) -> bool:
return self.install return self.install
def get_custom_install_dir(self): def get_custom_install_dir(self):
@ -2201,7 +2210,7 @@ class CustomTarget(Target):
def get_custom_install_mode(self): def get_custom_install_mode(self):
return self.install_mode return self.install_mode
def get_outputs(self): def get_outputs(self) -> T.List[str]:
return self.outputs return self.outputs
def get_filename(self): def get_filename(self):
@ -2289,13 +2298,13 @@ class RunTarget(Target):
def get_sources(self): def get_sources(self):
return [] return []
def should_install(self): def should_install(self) -> bool:
return False return False
def get_filename(self): def get_filename(self) -> str:
return self.name return self.name
def get_outputs(self): def get_outputs(self) -> T.List[str]:
if isinstance(self.name, str): if isinstance(self.name, str):
return [self.name] return [self.name]
elif isinstance(self.name, list): elif isinstance(self.name, list):
@ -2367,7 +2376,7 @@ class CustomTargetIndex:
return '<CustomTargetIndex: {!r}[{}]>'.format( return '<CustomTargetIndex: {!r}[{}]>'.format(
self.target, self.target.get_outputs().index(self.output)) self.target, self.target.get_outputs().index(self.output))
def get_outputs(self): def get_outputs(self) -> T.List[str]:
return [self.output] return [self.output]
def get_subdir(self): def get_subdir(self):
@ -2509,6 +2518,6 @@ def load(build_dir: str) -> Build:
raise MesonException(load_fail_msg) raise MesonException(load_fail_msg)
return obj return obj
def save(obj, filename): def save(obj: Build, filename: str) -> None:
with open(filename, 'wb') as f: with open(filename, 'wb') as f:
pickle.dump(obj, f) pickle.dump(obj, f)

@ -47,7 +47,7 @@ _T = T.TypeVar('_T')
class MesonVersionMismatchException(MesonException): class MesonVersionMismatchException(MesonException):
'''Build directory generated with Meson version incompatible with current version''' '''Build directory generated with Meson version incompatible with current version'''
def __init__(self, old_version, current_version): def __init__(self, old_version: str, current_version: str) -> None:
super().__init__('Build directory has been generated with Meson version {}, ' super().__init__('Build directory has been generated with Meson version {}, '
'which is incompatible with current version {}.' 'which is incompatible with current version {}.'
.format(old_version, current_version)) .format(old_version, current_version))
@ -56,7 +56,7 @@ class MesonVersionMismatchException(MesonException):
class UserOption(T.Generic[_T]): class UserOption(T.Generic[_T]):
def __init__(self, description, choices, yielding): def __init__(self, description: str, choices: T.Optional[T.Union[str, T.List[_T]]], yielding: T.Optional[bool]):
super().__init__() super().__init__()
self.choices = choices self.choices = choices
self.description = description self.description = description
@ -66,7 +66,8 @@ class UserOption(T.Generic[_T]):
raise MesonException('Value of "yielding" must be a boolean.') raise MesonException('Value of "yielding" must be a boolean.')
self.yielding = yielding self.yielding = yielding
def printable_value(self): def printable_value(self) -> T.Union[str, int, bool, T.List[T.Union[str, int, bool]]]:
assert isinstance(self.value, (str, int, bool, list))
return self.value return self.value
# Check that the input is a valid value and return the # Check that the input is a valid value and return the
@ -75,30 +76,32 @@ class UserOption(T.Generic[_T]):
def validate_value(self, value: T.Any) -> _T: def validate_value(self, value: T.Any) -> _T:
raise RuntimeError('Derived option class did not override validate_value.') raise RuntimeError('Derived option class did not override validate_value.')
def set_value(self, newvalue): def set_value(self, newvalue: T.Any) -> None:
self.value = self.validate_value(newvalue) self.value = self.validate_value(newvalue)
class UserStringOption(UserOption[str]): class UserStringOption(UserOption[str]):
def __init__(self, description, value, choices=None, yielding=None): def __init__(self, description: str, value: T.Any, yielding: T.Optional[bool] = None):
super().__init__(description, choices, yielding) super().__init__(description, None, yielding)
self.set_value(value) self.set_value(value)
def validate_value(self, value): def validate_value(self, value: T.Any) -> str:
if not isinstance(value, str): if not isinstance(value, str):
raise MesonException('Value "%s" for string option is not a string.' % str(value)) raise MesonException('Value "%s" for string option is not a string.' % str(value))
return value return value
class UserBooleanOption(UserOption[bool]): class UserBooleanOption(UserOption[bool]):
def __init__(self, description, value, yielding=None): def __init__(self, description: str, value, yielding: T.Optional[bool] = None) -> None:
super().__init__(description, [True, False], yielding) super().__init__(description, [True, False], yielding)
self.set_value(value) self.set_value(value)
def __bool__(self) -> bool: def __bool__(self) -> bool:
return self.value return self.value
def validate_value(self, value) -> bool: def validate_value(self, value: T.Any) -> bool:
if isinstance(value, bool): if isinstance(value, bool):
return value return value
if not isinstance(value, str):
raise MesonException('Value {} cannot be converted to a boolean'.format(value))
if value.lower() == 'true': if value.lower() == 'true':
return True return True
if value.lower() == 'false': if value.lower() == 'false':
@ -106,7 +109,7 @@ class UserBooleanOption(UserOption[bool]):
raise MesonException('Value %s is not boolean (true or false).' % value) raise MesonException('Value %s is not boolean (true or false).' % value)
class UserIntegerOption(UserOption[int]): class UserIntegerOption(UserOption[int]):
def __init__(self, description, value, yielding=None): def __init__(self, description: str, value: T.Any, yielding: T.Optional[bool] = None):
min_value, max_value, default_value = value min_value, max_value, default_value = value
self.min_value = min_value self.min_value = min_value
self.max_value = max_value self.max_value = max_value
@ -119,7 +122,7 @@ class UserIntegerOption(UserOption[int]):
super().__init__(description, choices, yielding) super().__init__(description, choices, yielding)
self.set_value(default_value) self.set_value(default_value)
def validate_value(self, value) -> int: def validate_value(self, value: T.Any) -> int:
if isinstance(value, str): if isinstance(value, str):
value = self.toint(value) value = self.toint(value)
if not isinstance(value, int): if not isinstance(value, int):
@ -130,35 +133,35 @@ class UserIntegerOption(UserOption[int]):
raise MesonException('New value %d is more than maximum value %d.' % (value, self.max_value)) raise MesonException('New value %d is more than maximum value %d.' % (value, self.max_value))
return value return value
def toint(self, valuestring) -> int: def toint(self, valuestring: str) -> int:
try: try:
return int(valuestring) return int(valuestring)
except ValueError: except ValueError:
raise MesonException('Value string "%s" is not convertible to an integer.' % valuestring) raise MesonException('Value string "%s" is not convertible to an integer.' % valuestring)
class UserUmaskOption(UserIntegerOption, UserOption[T.Union[str, int]]): class UserUmaskOption(UserIntegerOption, UserOption[T.Union[str, int]]):
def __init__(self, description, value, yielding=None): def __init__(self, description: str, value: T.Any, yielding: T.Optional[bool] = None):
super().__init__(description, (0, 0o777, value), yielding) super().__init__(description, (0, 0o777, value), yielding)
self.choices = ['preserve', '0000-0777'] self.choices = ['preserve', '0000-0777']
def printable_value(self): def printable_value(self) -> str:
if self.value == 'preserve': if self.value == 'preserve':
return self.value return self.value
return format(self.value, '04o') return format(self.value, '04o')
def validate_value(self, value): def validate_value(self, value: T.Any) -> T.Union[str, int]:
if value is None or value == 'preserve': if value is None or value == 'preserve':
return 'preserve' return 'preserve'
return super().validate_value(value) return super().validate_value(value)
def toint(self, valuestring): def toint(self, valuestring: T.Union[str, int]) -> int:
try: try:
return int(valuestring, 8) return int(valuestring, 8)
except ValueError as e: except ValueError as e:
raise MesonException('Invalid mode: {}'.format(e)) raise MesonException('Invalid mode: {}'.format(e))
class UserComboOption(UserOption[str]): class UserComboOption(UserOption[str]):
def __init__(self, description, choices: T.List[str], value, yielding=None): def __init__(self, description: str, choices: T.List[str], value: T.Any, yielding: T.Optional[bool] = None):
super().__init__(description, choices, yielding) super().__init__(description, choices, yielding)
if not isinstance(self.choices, list): if not isinstance(self.choices, list):
raise MesonException('Combo choices must be an array.') raise MesonException('Combo choices must be an array.')
@ -167,7 +170,7 @@ class UserComboOption(UserOption[str]):
raise MesonException('Combo choice elements must be strings.') raise MesonException('Combo choice elements must be strings.')
self.set_value(value) self.set_value(value)
def validate_value(self, value): def validate_value(self, value: T.Any) -> str:
if value not in self.choices: if value not in self.choices:
if isinstance(value, bool): if isinstance(value, bool):
_type = 'boolean' _type = 'boolean'
@ -182,13 +185,13 @@ class UserComboOption(UserOption[str]):
return value return value
class UserArrayOption(UserOption[T.List[str]]): class UserArrayOption(UserOption[T.List[str]]):
def __init__(self, description, value, split_args=False, user_input=False, allow_dups=False, **kwargs): def __init__(self, description: str, value: T.Union[str, T.List[str]], split_args: bool = False, user_input: bool = False, allow_dups: bool = False, **kwargs: T.Any) -> None:
super().__init__(description, kwargs.get('choices', []), yielding=kwargs.get('yielding', None)) super().__init__(description, kwargs.get('choices', []), yielding=kwargs.get('yielding', None))
self.split_args = split_args self.split_args = split_args
self.allow_dups = allow_dups self.allow_dups = allow_dups
self.value = self.validate_value(value, user_input=user_input) self.value = self.validate_value(value, user_input=user_input)
def validate_value(self, value, user_input: bool = True) -> T.List[str]: def validate_value(self, value: T.Union[str, T.List[str]], user_input: bool = True) -> T.List[str]:
# User input is for options defined on the command line (via -D # User input is for options defined on the command line (via -D
# options). Users can put their input in as a comma separated # options). Users can put their input in as a comma separated
# string, but for defining options in meson_options.txt the format # string, but for defining options in meson_options.txt the format
@ -232,16 +235,16 @@ class UserArrayOption(UserOption[T.List[str]]):
class UserFeatureOption(UserComboOption): class UserFeatureOption(UserComboOption):
static_choices = ['enabled', 'disabled', 'auto'] static_choices = ['enabled', 'disabled', 'auto']
def __init__(self, description, value, yielding=None): def __init__(self, description: str, value: T.Any, yielding: T.Optional[bool] = None):
super().__init__(description, self.static_choices, value, yielding) super().__init__(description, self.static_choices, value, yielding)
def is_enabled(self): def is_enabled(self) -> bool:
return self.value == 'enabled' return self.value == 'enabled'
def is_disabled(self): def is_disabled(self) -> bool:
return self.value == 'disabled' return self.value == 'disabled'
def is_auto(self): def is_auto(self) -> bool:
return self.value == 'auto' return self.value == 'auto'
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
@ -534,7 +537,7 @@ class CoreData:
value = None value = None
opts_map[optname] = opt.init_option(key, value, default_prefix()) opts_map[optname] = opt.init_option(key, value, default_prefix())
def init_backend_options(self, backend_name): def init_backend_options(self, backend_name: str) -> None:
if backend_name == 'ninja': if backend_name == 'ninja':
self.backend_options['backend_max_links'] = \ self.backend_options['backend_max_links'] = \
UserIntegerOption( UserIntegerOption(
@ -547,7 +550,7 @@ class CoreData:
'Default project to execute in Visual Studio', 'Default project to execute in Visual Studio',
'') '')
def get_builtin_option(self, optname, subproject=''): def get_builtin_option(self, optname: str, subproject: str = '') -> T.Union[str, int, bool]:
raw_optname = optname raw_optname = optname
if subproject: if subproject:
optname = subproject + ':' + optname optname = subproject + ':' + optname
@ -683,7 +686,7 @@ class CoreData:
def get_external_link_args(self, for_machine: MachineChoice, lang): def get_external_link_args(self, for_machine: MachineChoice, lang):
return self.compiler_options[for_machine][lang]['link_args'].value return self.compiler_options[for_machine][lang]['link_args'].value
def merge_user_options(self, options): def merge_user_options(self, options: T.Dict[str, T.Union[str, bool, int]]) -> None:
for (name, value) in options.items(): for (name, value) in options.items():
if name not in self.user_options: if name not in self.user_options:
self.user_options[name] = value self.user_options[name] = value
@ -715,7 +718,7 @@ class CoreData:
if k in build_opts: if k in build_opts:
build_opts[k].set_value(o.value) build_opts[k].set_value(o.value)
def set_options(self, options, *, subproject='', warn_unknown=True): def set_options(self, options: T.Dict[str, T.Any], subproject: str = '', warn_unknown: bool = True) -> None:
if not self.is_cross_build(): if not self.is_cross_build():
options = self.strip_build_option_names(options) options = self.strip_build_option_names(options)
# Set prefix first because it's needed to sanitize other options # Set prefix first because it's needed to sanitize other options
@ -912,10 +915,10 @@ def parse_machine_files(filenames):
parser = MachineFileParser(filenames) parser = MachineFileParser(filenames)
return parser.sections return parser.sections
def get_cmd_line_file(build_dir): def get_cmd_line_file(build_dir: str) -> str:
return os.path.join(build_dir, 'meson-private', 'cmd_line.txt') return os.path.join(build_dir, 'meson-private', 'cmd_line.txt')
def read_cmd_line_file(build_dir, options): def read_cmd_line_file(build_dir: str, options: argparse.Namespace) -> None:
filename = get_cmd_line_file(build_dir) filename = get_cmd_line_file(build_dir)
if not os.path.isfile(filename): if not os.path.isfile(filename):
return return
@ -937,10 +940,10 @@ def read_cmd_line_file(build_dir, options):
# literal_eval to get it into the list of strings. # literal_eval to get it into the list of strings.
options.native_file = ast.literal_eval(properties.get('native_file', '[]')) options.native_file = ast.literal_eval(properties.get('native_file', '[]'))
def cmd_line_options_to_string(options): def cmd_line_options_to_string(options: argparse.Namespace) -> T.Dict[str, str]:
return {k: str(v) for k, v in options.cmd_line_options.items()} return {k: str(v) for k, v in options.cmd_line_options.items()}
def write_cmd_line_file(build_dir, options): def write_cmd_line_file(build_dir: str, options: argparse.Namespace) -> None:
filename = get_cmd_line_file(build_dir) filename = get_cmd_line_file(build_dir)
config = CmdLineFileParser() config = CmdLineFileParser()
@ -955,7 +958,7 @@ def write_cmd_line_file(build_dir, options):
with open(filename, 'w') as f: with open(filename, 'w') as f:
config.write(f) config.write(f)
def update_cmd_line_file(build_dir, options): def update_cmd_line_file(build_dir: str, options: argparse.Namespace):
filename = get_cmd_line_file(build_dir) filename = get_cmd_line_file(build_dir)
config = CmdLineFileParser() config = CmdLineFileParser()
config.read(filename) config.read(filename)
@ -963,7 +966,7 @@ def update_cmd_line_file(build_dir, options):
with open(filename, 'w') as f: with open(filename, 'w') as f:
config.write(f) config.write(f)
def get_cmd_line_options(build_dir, options): def get_cmd_line_options(build_dir: str, options: argparse.Namespace) -> str:
copy = argparse.Namespace(**vars(options)) copy = argparse.Namespace(**vars(options))
read_cmd_line_file(build_dir, copy) read_cmd_line_file(build_dir, copy)
cmdline = ['-D{}={}'.format(k, v) for k, v in copy.cmd_line_options.items()] cmdline = ['-D{}={}'.format(k, v) for k, v in copy.cmd_line_options.items()]
@ -976,7 +979,7 @@ def get_cmd_line_options(build_dir, options):
def major_versions_differ(v1, v2): def major_versions_differ(v1, v2):
return v1.split('.')[0:2] != v2.split('.')[0:2] return v1.split('.')[0:2] != v2.split('.')[0:2]
def load(build_dir): def load(build_dir: str) -> CoreData:
filename = os.path.join(build_dir, 'meson-private', 'coredata.dat') filename = os.path.join(build_dir, 'meson-private', 'coredata.dat')
load_fail_msg = 'Coredata file {!r} is corrupted. Try with a fresh build tree.'.format(filename) load_fail_msg = 'Coredata file {!r} is corrupted. Try with a fresh build tree.'.format(filename)
try: try:
@ -995,7 +998,7 @@ def load(build_dir):
raise MesonVersionMismatchException(obj.version, version) raise MesonVersionMismatchException(obj.version, version)
return obj return obj
def save(obj, build_dir): def save(obj: CoreData, build_dir: str) -> str:
filename = os.path.join(build_dir, 'meson-private', 'coredata.dat') filename = os.path.join(build_dir, 'meson-private', 'coredata.dat')
prev_filename = filename + '.prev' prev_filename = filename + '.prev'
tempfilename = filename + '~' tempfilename = filename + '~'
@ -1012,7 +1015,7 @@ def save(obj, build_dir):
return filename return filename
def register_builtin_arguments(parser): def register_builtin_arguments(parser: argparse.ArgumentParser) -> None:
for n, b in BUILTIN_OPTIONS.items(): for n, b in BUILTIN_OPTIONS.items():
b.add_to_argparse(n, parser, '', '') b.add_to_argparse(n, parser, '', '')
for n, b in BUILTIN_OPTIONS_PER_MACHINE.items(): for n, b in BUILTIN_OPTIONS_PER_MACHINE.items():
@ -1021,7 +1024,7 @@ def register_builtin_arguments(parser):
parser.add_argument('-D', action='append', dest='projectoptions', default=[], metavar="option", parser.add_argument('-D', action='append', dest='projectoptions', default=[], metavar="option",
help='Set the value of an option, can be used several times to set multiple options.') help='Set the value of an option, can be used several times to set multiple options.')
def create_options_dict(options): def create_options_dict(options: T.List[str]) -> T.Dict[str, str]:
result = OrderedDict() result = OrderedDict()
for o in options: for o in options:
try: try:
@ -1031,7 +1034,7 @@ def create_options_dict(options):
result[key] = value result[key] = value
return result return result
def parse_cmd_line_options(args): def parse_cmd_line_options(args: argparse.Namespace) -> None:
args.cmd_line_options = create_options_dict(args.projectoptions) args.cmd_line_options = create_options_dict(args.projectoptions)
# Merge builtin options set with --option into the dict. # Merge builtin options set with --option into the dict.

@ -341,7 +341,9 @@ class BoostLibraryFile():
class BoostDependency(ExternalDependency): class BoostDependency(ExternalDependency):
def __init__(self, environment: Environment, kwargs: T.Dict[str, T.Any]) -> None: def __init__(self, environment: Environment, kwargs: T.Dict[str, T.Any]) -> None:
super().__init__('boost', environment, kwargs, language='cpp') super().__init__('boost', environment, kwargs, language='cpp')
self.debug = environment.coredata.get_builtin_option('buildtype').startswith('debug') buildtype = environment.coredata.get_builtin_option('buildtype')
assert isinstance(buildtype, str)
self.debug = buildtype.startswith('debug')
self.multithreading = kwargs.get('threading', 'multi') == 'multi' self.multithreading = kwargs.get('threading', 'multi') == 'multi'
self.boost_root = None # type: Path self.boost_root = None # type: Path

@ -132,6 +132,9 @@ build_filename = 'meson.build'
CompilersDict = T.Dict[str, Compiler] CompilersDict = T.Dict[str, Compiler]
if T.TYPE_CHECKING:
import argparse
def detect_gcovr(min_version='3.3', new_rootdir_version='4.2', log=False): def detect_gcovr(min_version='3.3', new_rootdir_version='4.2', log=False):
gcovr_exe = 'gcovr' gcovr_exe = 'gcovr'
try: try:
@ -153,7 +156,7 @@ def detect_llvm_cov():
return tool return tool
return None return None
def find_coverage_tools(): def find_coverage_tools() -> T.Tuple[T.Optional[str], T.Optional[str], T.Optional[str], T.Optional[str], T.Optional[str]]:
gcovr_exe, gcovr_new_rootdir = detect_gcovr() gcovr_exe, gcovr_new_rootdir = detect_gcovr()
llvm_cov_exe = detect_llvm_cov() llvm_cov_exe = detect_llvm_cov()
@ -522,7 +525,7 @@ class Environment:
log_dir = 'meson-logs' log_dir = 'meson-logs'
info_dir = 'meson-info' info_dir = 'meson-info'
def __init__(self, source_dir, build_dir, options): def __init__(self, source_dir: T.Optional[str], build_dir: T.Optional[str], options: 'argparse.Namespace') -> None:
self.source_dir = source_dir self.source_dir = source_dir
self.build_dir = build_dir self.build_dir = build_dir
# Do not try to create build directories when build_dir is none. # Do not try to create build directories when build_dir is none.
@ -535,7 +538,7 @@ class Environment:
os.makedirs(self.log_dir, exist_ok=True) os.makedirs(self.log_dir, exist_ok=True)
os.makedirs(self.info_dir, exist_ok=True) os.makedirs(self.info_dir, exist_ok=True)
try: try:
self.coredata = coredata.load(self.get_build_dir()) self.coredata = coredata.load(self.get_build_dir()) # type: coredata.CoreData
self.first_invocation = False self.first_invocation = False
except FileNotFoundError: except FileNotFoundError:
self.create_new_coredata(options) self.create_new_coredata(options)
@ -807,7 +810,7 @@ class Environment:
self.default_pkgconfig = ['pkg-config'] self.default_pkgconfig = ['pkg-config']
self.wrap_resolver = None self.wrap_resolver = None
def create_new_coredata(self, options): def create_new_coredata(self, options: 'argparse.Namespace') -> None:
# WARNING: Don't use any values from coredata in __init__. It gets # WARNING: Don't use any values from coredata in __init__. It gets
# re-initialized with project options by the interpreter during # re-initialized with project options by the interpreter during
# build file parsing. # build file parsing.
@ -819,17 +822,17 @@ class Environment:
def is_cross_build(self, when_building_for: MachineChoice = MachineChoice.HOST) -> bool: def is_cross_build(self, when_building_for: MachineChoice = MachineChoice.HOST) -> bool:
return self.coredata.is_cross_build(when_building_for) return self.coredata.is_cross_build(when_building_for)
def dump_coredata(self): def dump_coredata(self) -> str:
return coredata.save(self.coredata, self.get_build_dir()) return coredata.save(self.coredata, self.get_build_dir())
def get_script_dir(self): def get_script_dir(self) -> str:
import mesonbuild.scripts import mesonbuild.scripts
return os.path.dirname(mesonbuild.scripts.__file__) return os.path.dirname(mesonbuild.scripts.__file__)
def get_log_dir(self): def get_log_dir(self) -> str:
return self.log_dir return self.log_dir
def get_coredata(self): def get_coredata(self) -> coredata.CoreData:
return self.coredata return self.coredata
def get_build_command(self, unbuffered=False): def get_build_command(self, unbuffered=False):
@ -1535,7 +1538,7 @@ class Environment:
self._handle_exceptions(popen_exceptions, compilers) self._handle_exceptions(popen_exceptions, compilers)
def get_scratch_dir(self): def get_scratch_dir(self) -> str:
return self.scratch_dir return self.scratch_dir
def detect_objc_compiler(self, for_machine: MachineInfo) -> 'Compiler': def detect_objc_compiler(self, for_machine: MachineInfo) -> 'Compiler':
@ -1974,10 +1977,10 @@ class Environment:
self._handle_exceptions(popen_exceptions, linkers, 'linker') self._handle_exceptions(popen_exceptions, linkers, 'linker')
raise EnvironmentException('Unknown static linker "{}"'.format(' '.join(linkers))) raise EnvironmentException('Unknown static linker "{}"'.format(' '.join(linkers)))
def get_source_dir(self): def get_source_dir(self) -> str:
return self.source_dir return self.source_dir
def get_build_dir(self): def get_build_dir(self) -> str:
return self.build_dir return self.build_dir
def get_import_lib_dir(self) -> str: def get_import_lib_dir(self) -> str:

@ -31,9 +31,10 @@ from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode
from .interpreterbase import InterpreterObject, MutableInterpreterObject, Disabler, disablerIfNotFound from .interpreterbase import InterpreterObject, MutableInterpreterObject, Disabler, disablerIfNotFound
from .interpreterbase import FeatureNew, FeatureDeprecated, FeatureNewKwargs, FeatureDeprecatedKwargs from .interpreterbase import FeatureNew, FeatureDeprecated, FeatureNewKwargs, FeatureDeprecatedKwargs
from .interpreterbase import ObjectHolder, MesonVersionString from .interpreterbase import ObjectHolder, MesonVersionString
from .modules import ModuleReturnValue from .interpreterbase import TYPE_var, TYPE_nkwargs
from .modules import ModuleReturnValue, ExtensionModule
from .cmake import CMakeInterpreter from .cmake import CMakeInterpreter
from .backend.backends import TestProtocol from .backend.backends import TestProtocol, Backend
from pathlib import Path, PurePath from pathlib import Path, PurePath
import os import os
@ -673,22 +674,22 @@ class MachineHolder(InterpreterObject, ObjectHolder):
@noPosargs @noPosargs
@permittedKwargs({}) @permittedKwargs({})
def cpu_family_method(self, args, kwargs): def cpu_family_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str:
return self.held_object.cpu_family return self.held_object.cpu_family
@noPosargs @noPosargs
@permittedKwargs({}) @permittedKwargs({})
def cpu_method(self, args, kwargs): def cpu_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str:
return self.held_object.cpu return self.held_object.cpu
@noPosargs @noPosargs
@permittedKwargs({}) @permittedKwargs({})
def system_method(self, args, kwargs): def system_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str:
return self.held_object.system return self.held_object.system
@noPosargs @noPosargs
@permittedKwargs({}) @permittedKwargs({})
def endian_method(self, args, kwargs): def endian_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str:
return self.held_object.endian return self.held_object.endian
class IncludeDirsHolder(InterpreterObject, ObjectHolder): class IncludeDirsHolder(InterpreterObject, ObjectHolder):
@ -2333,8 +2334,18 @@ permitted_kwargs = {'add_global_arguments': {'language', 'native'},
class Interpreter(InterpreterBase): class Interpreter(InterpreterBase):
def __init__(self, build, backend=None, subproject='', subdir='', subproject_dir='subprojects', def __init__(
modules = None, default_project_options=None, mock=False, ast=None): self,
build: build.Build,
backend: T.Optional[Backend] = None,
subproject: str = '',
subdir: str = '',
subproject_dir: str = 'subprojects',
modules: T.Optional[T.Dict[str, ExtensionModule]] = None,
default_project_options: T.Optional[T.Dict[str, str]] = None,
mock: bool = False,
ast: T.Optional[mparser.CodeBlockNode] = None,
) -> None:
super().__init__(build.environment.get_source_dir(), subdir, subproject) super().__init__(build.environment.get_source_dir(), subdir, subproject)
self.an_unpicklable_object = mesonlib.an_unpicklable_object self.an_unpicklable_object = mesonlib.an_unpicklable_object
self.build = build self.build = build
@ -2396,7 +2407,8 @@ class Interpreter(InterpreterBase):
self.builtin['target_machine'] = \ self.builtin['target_machine'] = \
MachineHolder(self.build.environment.machines.target) MachineHolder(self.build.environment.machines.target)
def get_non_matching_default_options(self): # TODO: Why is this in interpreter.py and not CoreData or Environment?
def get_non_matching_default_options(self) -> T.Iterator[T.Tuple[str, str, coredata.UserOption]]:
env = self.environment env = self.environment
for def_opt_name, def_opt_value in self.project_default_options.items(): for def_opt_name, def_opt_value in self.project_default_options.items():
for opts in env.coredata.get_all_options(): for opts in env.coredata.get_all_options():
@ -2530,7 +2542,7 @@ class Interpreter(InterpreterBase):
self.process_new_values(invalues) self.process_new_values(invalues)
return self.holderify(return_object.return_value) return self.holderify(return_object.return_value)
def get_build_def_files(self): def get_build_def_files(self) -> T.List[str]:
return self.build_def_files return self.build_def_files
def add_build_def_file(self, f): def add_build_def_file(self, f):
@ -2599,7 +2611,9 @@ class Interpreter(InterpreterBase):
module = importlib.import_module('mesonbuild.modules.' + modname) module = importlib.import_module('mesonbuild.modules.' + modname)
except ImportError: except ImportError:
raise InvalidArguments('Module "%s" does not exist' % (modname, )) raise InvalidArguments('Module "%s" does not exist' % (modname, ))
self.modules[modname] = module.initialize(self) ext_module = module.initialize(self)
assert isinstance(ext_module, ExtensionModule)
self.modules[modname] = ext_module
@stringArgs @stringArgs
@noKwargs @noKwargs
@ -4598,7 +4612,7 @@ different subdirectory.
def func_join_paths(self, node, args, kwargs): def func_join_paths(self, node, args, kwargs):
return self.join_path_strings(args) return self.join_path_strings(args)
def run(self): def run(self) -> None:
super().run() super().run()
mlog.log('Build targets in project:', mlog.bold(str(len(self.build.targets)))) mlog.log('Build targets in project:', mlog.bold(str(len(self.build.targets))))
FeatureNew.report(self.subproject) FeatureNew.report(self.subproject)
@ -4608,14 +4622,14 @@ different subdirectory.
if self.subproject == '': if self.subproject == '':
self._print_summary() self._print_summary()
def print_extra_warnings(self): def print_extra_warnings(self) -> None:
# TODO cross compilation # TODO cross compilation
for c in self.coredata.compilers.host.values(): for c in self.coredata.compilers.host.values():
if c.get_id() == 'clang': if c.get_id() == 'clang':
self.check_clang_asan_lundef() self.check_clang_asan_lundef()
break break
def check_clang_asan_lundef(self): def check_clang_asan_lundef(self) -> None:
if 'b_lundef' not in self.coredata.base_options: if 'b_lundef' not in self.coredata.base_options:
return return
if 'b_sanitize' not in self.coredata.base_options: if 'b_sanitize' not in self.coredata.base_options:

@ -32,7 +32,7 @@ if T.TYPE_CHECKING:
import argparse import argparse
def array_arg(value: str) -> T.List[str]: def array_arg(value: str) -> T.List[str]:
return T.cast(T.List[str], UserArrayOption(None, value, allow_dups=True, user_input=True).value) return UserArrayOption(None, value, allow_dups=True, user_input=True).value
def validate_builddir(builddir: Path) -> None: def validate_builddir(builddir: Path) -> None:
if not (builddir / 'meson-private' / 'coredata.dat' ).is_file(): if not (builddir / 'meson-private' / 'coredata.dat' ).is_file():
@ -45,7 +45,9 @@ def get_backend_from_coredata(builddir: Path) -> str:
""" """
Gets `backend` option value from coredata Gets `backend` option value from coredata
""" """
return T.cast(str, coredata.load(str(builddir)).get_builtin_option('backend')) backend = coredata.load(str(builddir)).get_builtin_option('backend')
assert isinstance(backend, str)
return backend
def parse_introspect_data(builddir: Path) -> T.Dict[str, T.List[dict]]: def parse_introspect_data(builddir: Path) -> T.Dict[str, T.List[dict]]:
""" """

@ -15,15 +15,19 @@
import os import os
from . import coredata, environment, mesonlib, build, mintro, mlog from . import coredata, environment, mesonlib, build, mintro, mlog
from .ast import AstIDGenerator from .ast import AstIDGenerator
import typing as T
def add_arguments(parser): if T.TYPE_CHECKING:
import argparse
def add_arguments(parser: 'argparse.ArgumentParser') -> None:
coredata.register_builtin_arguments(parser) coredata.register_builtin_arguments(parser)
parser.add_argument('builddir', nargs='?', default='.') parser.add_argument('builddir', nargs='?', default='.')
parser.add_argument('--clearcache', action='store_true', default=False, parser.add_argument('--clearcache', action='store_true', default=False,
help='Clear cached state (e.g. found dependencies)') help='Clear cached state (e.g. found dependencies)')
def make_lower_case(val): def make_lower_case(val: T.Any) -> T.Union[str, T.List[T.Any]]: # T.Any because of recursion...
if isinstance(val, bool): if isinstance(val, bool):
return str(val).lower() return str(val).lower()
elif isinstance(val, list): elif isinstance(val, list):

@ -254,11 +254,9 @@ def list_buildoptions(coredata: cdata.CoreData, subprojects: T.Optional[T.List[s
'compiler', 'compiler',
machine='host', machine='host',
) )
tmp_dict = dict(coredata.flatten_lang_iterator(coredata.compiler_options.build.items())) # type: T.Dict[str, cdata.UserOption]
add_keys( add_keys(
{ {'build.' + k: o for k, o in tmp_dict.items()},
'build.' + k: o for k, o in
coredata.flatten_lang_iterator(coredata.compiler_options.build.items())
},
'compiler', 'compiler',
machine='build', machine='build',
) )
@ -305,10 +303,10 @@ def list_deps(coredata: cdata.CoreData) -> T.List[T.Dict[str, T.Union[str, T.Lis
'link_args': d.get_link_args()}] 'link_args': d.get_link_args()}]
return result return result
def get_test_list(testdata: backends.TestSerialisation) -> T.List[T.Dict[str, T.Union[str, int, T.List[str], T.Dict[str, str]]]]: def get_test_list(testdata: T.List[backends.TestSerialisation]) -> T.List[T.Dict[str, T.Union[str, int, T.List[str], T.Dict[str, str]]]]:
result = [] # type: T.List[T.Dict[str, T.Union[str, int, T.List[str], T.Dict[str, str]]]] result = [] # type: T.List[T.Dict[str, T.Union[str, int, T.List[str], T.Dict[str, str]]]]
for t in testdata: for t in testdata:
to = {} to = {} # type: T.Dict[str, T.Union[str, int, T.List[str], T.Dict[str, str]]]
if isinstance(t.fname, str): if isinstance(t.fname, str):
fname = [t.fname] fname = [t.fname]
else: else:
@ -329,21 +327,21 @@ def get_test_list(testdata: backends.TestSerialisation) -> T.List[T.Dict[str, T.
result.append(to) result.append(to)
return result return result
def list_tests(testdata: backends.TestSerialisation) -> T.List[T.Dict[str, T.Union[str, int, T.List[str], T.Dict[str, str]]]]: def list_tests(testdata: T.List[backends.TestSerialisation]) -> T.List[T.Dict[str, T.Union[str, int, T.List[str], T.Dict[str, str]]]]:
return get_test_list(testdata) return get_test_list(testdata)
def list_benchmarks(benchdata: backends.TestSerialisation) -> T.List[T.Dict[str, T.Union[str, int, T.List[str], T.Dict[str, str]]]]: def list_benchmarks(benchdata: T.List[backends.TestSerialisation]) -> T.List[T.Dict[str, T.Union[str, int, T.List[str], T.Dict[str, str]]]]:
return get_test_list(benchdata) return get_test_list(benchdata)
def list_projinfo(builddata: build.Build) -> T.Dict[str, T.Union[str, T.List[T.Dict[str, str]]]]: def list_projinfo(builddata: build.Build) -> T.Dict[str, T.Union[str, T.List[T.Dict[str, str]]]]:
result = {'version': builddata.project_version, result = {'version': builddata.project_version,
'descriptive_name': builddata.project_name, 'descriptive_name': builddata.project_name,
'subproject_dir': builddata.subproject_dir} 'subproject_dir': builddata.subproject_dir} # type: T.Dict[str, T.Union[str, T.List[T.Dict[str, str]]]]
subprojects = [] subprojects = []
for k, v in builddata.subprojects.items(): for k, v in builddata.subprojects.items():
c = {'name': k, c = {'name': k,
'version': v, 'version': v,
'descriptive_name': builddata.projects.get(k)} 'descriptive_name': builddata.projects.get(k)} # type: T.Dict[str, str]
subprojects.append(c) subprojects.append(c)
result['subprojects'] = subprojects result['subprojects'] = subprojects
return result return result
@ -391,6 +389,7 @@ def run(options: argparse.Namespace) -> int:
# Make sure that log entries in other parts of meson don't interfere with the JSON output # Make sure that log entries in other parts of meson don't interfere with the JSON output
mlog.disable() mlog.disable()
backend = backends.get_backend_from_name(options.backend) backend = backends.get_backend_from_name(options.backend)
assert backend is not None
intr = IntrospectionInterpreter(sourcedir, '', backend.name, visitors = [AstIDGenerator(), AstIndentationGenerator(), AstConditionLevel()]) intr = IntrospectionInterpreter(sourcedir, '', backend.name, visitors = [AstIDGenerator(), AstIndentationGenerator(), AstConditionLevel()])
intr.analyze() intr.analyze()
# Re-enable logging just in case # Re-enable logging just in case

@ -19,14 +19,18 @@ import os
from .. import build from .. import build
from ..mesonlib import unholder from ..mesonlib import unholder
import typing as T
if T.TYPE_CHECKING:
from ..interpreter import Interpreter
from ..interpreterbase import TYPE_var
class ExtensionModule: class ExtensionModule:
def __init__(self, interpreter): def __init__(self, interpreter: 'Interpreter') -> None:
self.interpreter = interpreter self.interpreter = interpreter
self.snippets = set() # List of methods that operate only on the interpreter. self.snippets = set() # type: T.Set[str] # List of methods that operate only on the interpreter.
def is_snippet(self, funcname): def is_snippet(self, funcname: str) -> bool:
return funcname in self.snippets return funcname in self.snippets
@ -69,7 +73,7 @@ def is_module_library(fname):
class ModuleReturnValue: class ModuleReturnValue:
def __init__(self, return_value, new_objects): def __init__(self, return_value: 'TYPE_var', new_objects: T.List['TYPE_var']) -> None:
self.return_value = return_value self.return_value = return_value
assert(isinstance(new_objects, list)) assert(isinstance(new_objects, list))
self.new_objects = new_objects self.new_objects = new_objects

@ -178,12 +178,18 @@ class MesonApp:
logger_fun = mlog.log logger_fun = mlog.log
else: else:
logger_fun = mlog.debug logger_fun = mlog.debug
logger_fun('Build machine cpu family:', mlog.bold(intr.builtin['build_machine'].cpu_family_method([], {}))) build_machine = intr.builtin['build_machine']
logger_fun('Build machine cpu:', mlog.bold(intr.builtin['build_machine'].cpu_method([], {}))) host_machine = intr.builtin['build_machine']
mlog.log('Host machine cpu family:', mlog.bold(intr.builtin['host_machine'].cpu_family_method([], {}))) target_machine = intr.builtin['target_machine']
mlog.log('Host machine cpu:', mlog.bold(intr.builtin['host_machine'].cpu_method([], {}))) assert isinstance(build_machine, interpreter.MachineHolder)
logger_fun('Target machine cpu family:', mlog.bold(intr.builtin['target_machine'].cpu_family_method([], {}))) assert isinstance(host_machine, interpreter.MachineHolder)
logger_fun('Target machine cpu:', mlog.bold(intr.builtin['target_machine'].cpu_method([], {}))) assert isinstance(target_machine, interpreter.MachineHolder)
logger_fun('Build machine cpu family:', mlog.bold(build_machine.cpu_family_method([], {})))
logger_fun('Build machine cpu:', mlog.bold(build_machine.cpu_method([], {})))
mlog.log('Host machine cpu family:', mlog.bold(host_machine.cpu_family_method([], {})))
mlog.log('Host machine cpu:', mlog.bold(host_machine.cpu_method([], {})))
logger_fun('Target machine cpu family:', mlog.bold(target_machine.cpu_family_method([], {})))
logger_fun('Target machine cpu:', mlog.bold(target_machine.cpu_method([], {})))
try: try:
if self.options.profile: if self.options.profile:
fname = os.path.join(self.build_dir, 'meson-private', 'profile-interpreter.log') fname = os.path.join(self.build_dir, 'meson-private', 'profile-interpreter.log')

@ -43,10 +43,7 @@ from . import environment
from . import mlog from . import mlog
from .dependencies import ExternalProgram from .dependencies import ExternalProgram
from .mesonlib import MesonException, get_wine_shortpath, split_args, join_args from .mesonlib import MesonException, get_wine_shortpath, split_args, join_args
from .backend.backends import TestProtocol from .backend.backends import TestProtocol, TestSerialisation
if T.TYPE_CHECKING:
from .backend.backends import TestSerialisation
# GNU autotools interprets a return code of 77 from tests it executes to # GNU autotools interprets a return code of 77 from tests it executes to
# mean that the test should be skipped. # mean that the test should be skipped.
@ -445,7 +442,7 @@ class JunitBuilder:
class TestRun: class TestRun:
@classmethod @classmethod
def make_gtest(cls, test: 'TestSerialisation', test_env: T.Dict[str, str], def make_gtest(cls, test: TestSerialisation, test_env: T.Dict[str, str],
returncode: int, starttime: float, duration: float, returncode: int, starttime: float, duration: float,
stdo: T.Optional[str], stde: T.Optional[str], stdo: T.Optional[str], stde: T.Optional[str],
cmd: T.Optional[T.List[str]]) -> 'TestRun': cmd: T.Optional[T.List[str]]) -> 'TestRun':
@ -459,7 +456,7 @@ class TestRun:
junit=tree) junit=tree)
@classmethod @classmethod
def make_exitcode(cls, test: 'TestSerialisation', test_env: T.Dict[str, str], def make_exitcode(cls, test: TestSerialisation, test_env: T.Dict[str, str],
returncode: int, starttime: float, duration: float, returncode: int, starttime: float, duration: float,
stdo: T.Optional[str], stde: T.Optional[str], stdo: T.Optional[str], stde: T.Optional[str],
cmd: T.Optional[T.List[str]], **kwargs: T.Any) -> 'TestRun': cmd: T.Optional[T.List[str]], **kwargs: T.Any) -> 'TestRun':
@ -474,7 +471,7 @@ class TestRun:
return cls(test, test_env, res, [], returncode, starttime, duration, stdo, stde, cmd, **kwargs) return cls(test, test_env, res, [], returncode, starttime, duration, stdo, stde, cmd, **kwargs)
@classmethod @classmethod
def make_tap(cls, test: 'TestSerialisation', test_env: T.Dict[str, str], def make_tap(cls, test: TestSerialisation, test_env: T.Dict[str, str],
returncode: int, starttime: float, duration: float, returncode: int, starttime: float, duration: float,
stdo: str, stde: str, stdo: str, stde: str,
cmd: T.Optional[T.List[str]]) -> 'TestRun': cmd: T.Optional[T.List[str]]) -> 'TestRun':
@ -511,7 +508,7 @@ class TestRun:
return cls(test, test_env, res, results, returncode, starttime, duration, stdo, stde, cmd) return cls(test, test_env, res, results, returncode, starttime, duration, stdo, stde, cmd)
def __init__(self, test: 'TestSerialisation', test_env: T.Dict[str, str], def __init__(self, test: TestSerialisation, test_env: T.Dict[str, str],
res: TestResult, results: T.List[TestResult], returncode: res: TestResult, results: T.List[TestResult], returncode:
int, starttime: float, duration: float, int, starttime: float, duration: float,
stdo: T.Optional[str], stde: T.Optional[str], stdo: T.Optional[str], stde: T.Optional[str],
@ -577,26 +574,32 @@ def write_json_log(jsonlogfile: T.TextIO, test_name: str, result: TestRun) -> No
def run_with_mono(fname: str) -> bool: def run_with_mono(fname: str) -> bool:
return fname.endswith('.exe') and not (is_windows() or is_cygwin()) return fname.endswith('.exe') and not (is_windows() or is_cygwin())
def load_benchmarks(build_dir: str) -> T.List['TestSerialisation']: def load_benchmarks(build_dir: str) -> T.List[TestSerialisation]:
datafile = Path(build_dir) / 'meson-private' / 'meson_benchmark_setup.dat' datafile = Path(build_dir) / 'meson-private' / 'meson_benchmark_setup.dat'
if not datafile.is_file(): if not datafile.is_file():
raise TestException('Directory {!r} does not seem to be a Meson build directory.'.format(build_dir)) raise TestException('Directory {!r} does not seem to be a Meson build directory.'.format(build_dir))
with datafile.open('rb') as f: with datafile.open('rb') as f:
obj = T.cast(T.List['TestSerialisation'], pickle.load(f)) obj = pickle.load(f)
assert isinstance(obj, list)
for i in obj:
assert isinstance(i, TestSerialisation)
return obj return obj
def load_tests(build_dir: str) -> T.List['TestSerialisation']: def load_tests(build_dir: str) -> T.List[TestSerialisation]:
datafile = Path(build_dir) / 'meson-private' / 'meson_test_setup.dat' datafile = Path(build_dir) / 'meson-private' / 'meson_test_setup.dat'
if not datafile.is_file(): if not datafile.is_file():
raise TestException('Directory {!r} does not seem to be a Meson build directory.'.format(build_dir)) raise TestException('Directory {!r} does not seem to be a Meson build directory.'.format(build_dir))
with datafile.open('rb') as f: with datafile.open('rb') as f:
obj = T.cast(T.List['TestSerialisation'], pickle.load(f)) obj = pickle.load(f)
assert isinstance(obj, list)
for i in obj:
assert isinstance(i, TestSerialisation)
return obj return obj
class SingleTestRunner: class SingleTestRunner:
def __init__(self, test: 'TestSerialisation', test_env: T.Dict[str, str], def __init__(self, test: TestSerialisation, test_env: T.Dict[str, str],
env: T.Dict[str, str], options: argparse.Namespace): env: T.Dict[str, str], options: argparse.Namespace):
self.test = test self.test = test
self.test_env = test_env self.test_env = test_env
@ -605,9 +608,9 @@ class SingleTestRunner:
def _get_cmd(self) -> T.Optional[T.List[str]]: def _get_cmd(self) -> T.Optional[T.List[str]]:
if self.test.fname[0].endswith('.jar'): if self.test.fname[0].endswith('.jar'):
return ['java', '-jar'] + T.cast(T.List[str], self.test.fname) return ['java', '-jar'] + self.test.fname
elif not self.test.is_cross_built and run_with_mono(self.test.fname[0]): elif not self.test.is_cross_built and run_with_mono(self.test.fname[0]):
return ['mono'] + T.cast(T.List[str], self.test.fname) return ['mono'] + self.test.fname
elif self.test.cmd_is_built and self.test.needs_exe_wrapper: elif self.test.cmd_is_built and self.test.needs_exe_wrapper:
if self.test.exe_runner is None: if self.test.exe_runner is None:
# Can not run test on cross compiled executable # Can not run test on cross compiled executable
@ -620,8 +623,8 @@ class SingleTestRunner:
msg = ('The exe_wrapper defined in the cross file {!r} was not ' msg = ('The exe_wrapper defined in the cross file {!r} was not '
'found. Please check the command and/or add it to PATH.') 'found. Please check the command and/or add it to PATH.')
raise TestException(msg.format(self.test.exe_runner.name)) raise TestException(msg.format(self.test.exe_runner.name))
return T.cast(T.List[str], self.test.exe_runner.get_command()) + T.cast(T.List[str], self.test.fname) return self.test.exe_runner.get_command() + self.test.fname
return T.cast(T.List[str], self.test.fname) return self.test.fname
def run(self) -> TestRun: def run(self) -> TestRun:
cmd = self._get_cmd() cmd = self._get_cmd()
@ -833,7 +836,7 @@ class TestHarness:
lfile.close() lfile.close()
setattr(self, f, None) setattr(self, f, None)
def merge_suite_options(self, options: argparse.Namespace, test: 'TestSerialisation') -> T.Dict[str, str]: def merge_suite_options(self, options: argparse.Namespace, test: TestSerialisation) -> T.Dict[str, str]:
if ':' in options.setup: if ':' in options.setup:
if options.setup not in self.build_data.test_setups: if options.setup not in self.build_data.test_setups:
sys.exit("Unknown test setup '{}'.".format(options.setup)) sys.exit("Unknown test setup '{}'.".format(options.setup))
@ -855,9 +858,9 @@ class TestHarness:
sys.exit('Conflict: both test setup and command line specify an exe wrapper.') sys.exit('Conflict: both test setup and command line specify an exe wrapper.')
if options.wrapper is None: if options.wrapper is None:
options.wrapper = current.exe_wrapper options.wrapper = current.exe_wrapper
return T.cast(T.Dict[str, str], current.env.get_env(os.environ.copy())) return current.env.get_env(os.environ.copy())
def get_test_runner(self, test: 'TestSerialisation') -> SingleTestRunner: def get_test_runner(self, test: TestSerialisation) -> SingleTestRunner:
options = deepcopy(self.options) options = deepcopy(self.options)
if not options.setup: if not options.setup:
options.setup = self.build_data.test_setup_default_name options.setup = self.build_data.test_setup_default_name
@ -889,7 +892,7 @@ class TestHarness:
sys.exit('Unknown test result encountered: {}'.format(result.res)) sys.exit('Unknown test result encountered: {}'.format(result.res))
def print_stats(self, test_count: int, name_max_len: int, def print_stats(self, test_count: int, name_max_len: int,
tests: T.List['TestSerialisation'], tests: T.List[TestSerialisation],
name: str, result: TestRun, i: int) -> None: name: str, result: TestRun, i: int) -> None:
ok_statuses = (TestResult.OK, TestResult.EXPECTEDFAIL) ok_statuses = (TestResult.OK, TestResult.EXPECTEDFAIL)
bad_statuses = (TestResult.FAIL, TestResult.TIMEOUT, bad_statuses = (TestResult.FAIL, TestResult.TIMEOUT,
@ -983,14 +986,14 @@ class TestHarness:
@staticmethod @staticmethod
def split_suite_string(suite: str) -> T.Tuple[str, str]: def split_suite_string(suite: str) -> T.Tuple[str, str]:
if ':' in suite: if ':' in suite:
# mypy can't figure out that str.split(n, 1) will return a list of split = suite.split(':', 1)
# length 2, so we have to help it. assert len(split) == 2
return T.cast(T.Tuple[str, str], tuple(suite.split(':', 1))) return split[0], split[1]
else: else:
return suite, "" return suite, ""
@staticmethod @staticmethod
def test_in_suites(test: 'TestSerialisation', suites: T.List[str]) -> bool: def test_in_suites(test: TestSerialisation, suites: T.List[str]) -> bool:
for suite in suites: for suite in suites:
(prj_match, st_match) = TestHarness.split_suite_string(suite) (prj_match, st_match) = TestHarness.split_suite_string(suite)
for prjst in test.suite: for prjst in test.suite:
@ -1021,12 +1024,12 @@ class TestHarness:
return True return True
return False return False
def test_suitable(self, test: 'TestSerialisation') -> bool: def test_suitable(self, test: TestSerialisation) -> bool:
return ((not self.options.include_suites or return ((not self.options.include_suites or
TestHarness.test_in_suites(test, self.options.include_suites)) and not TestHarness.test_in_suites(test, self.options.include_suites)) and not
TestHarness.test_in_suites(test, self.options.exclude_suites)) TestHarness.test_in_suites(test, self.options.exclude_suites))
def get_tests(self) -> T.List['TestSerialisation']: def get_tests(self) -> T.List[TestSerialisation]:
if not self.tests: if not self.tests:
print('No tests defined.') print('No tests defined.')
return [] return []
@ -1089,17 +1092,17 @@ class TestHarness:
wrap += options.wrapper wrap += options.wrapper
return wrap return wrap
def get_pretty_suite(self, test: 'TestSerialisation') -> str: def get_pretty_suite(self, test: TestSerialisation) -> str:
if len(self.suites) > 1 and test.suite: if len(self.suites) > 1 and test.suite:
rv = TestHarness.split_suite_string(test.suite[0])[0] rv = TestHarness.split_suite_string(test.suite[0])[0]
s = "+".join(TestHarness.split_suite_string(s)[1] for s in test.suite) s = "+".join(TestHarness.split_suite_string(s)[1] for s in test.suite)
if s: if s:
rv += ":" rv += ":"
return rv + s + " / " + T.cast(str, test.name) return rv + s + " / " + test.name
else: else:
return T.cast(str, test.name) return test.name
def run_tests(self, tests: T.List['TestSerialisation']) -> None: def run_tests(self, tests: T.List[TestSerialisation]) -> None:
executor = None executor = None
futures = [] # type: T.List[T.Tuple[conc.Future[TestRun], int, int, T.List[TestSerialisation], str, int]] futures = [] # type: T.List[T.Tuple[conc.Future[TestRun], int, int, T.List[TestSerialisation], str, int]]
test_count = len(tests) test_count = len(tests)
@ -1141,7 +1144,7 @@ class TestHarness:
finally: finally:
os.chdir(startdir) os.chdir(startdir)
def drain_futures(self, futures: T.List[T.Tuple['conc.Future[TestRun]', int, int, T.List['TestSerialisation'], str, int]]) -> None: def drain_futures(self, futures: T.List[T.Tuple['conc.Future[TestRun]', int, int, T.List[TestSerialisation], str, int]]) -> None:
for x in futures: for x in futures:
(result, test_count, name_max_len, tests, name, i) = x (result, test_count, name_max_len, tests, name, i) = x
if self.options.repeat > 1 and self.fail_count: if self.options.repeat > 1 and self.fail_count:

@ -62,7 +62,6 @@ optname_regex = re.compile('[^a-zA-Z0-9_-]')
def StringParser(description, kwargs): def StringParser(description, kwargs):
return coredata.UserStringOption(description, return coredata.UserStringOption(description,
kwargs.get('value', ''), kwargs.get('value', ''),
kwargs.get('choices', []),
kwargs.get('yield', coredata.default_yielding)) kwargs.get('yield', coredata.default_yielding))
@permitted_kwargs({'value', 'yield'}) @permitted_kwargs({'value', 'yield'})
@ -134,11 +133,11 @@ option_types = {'string': StringParser,
} # type: T.Dict[str, T.Callable[[str, T.Dict], coredata.UserOption]] } # type: T.Dict[str, T.Callable[[str, T.Dict], coredata.UserOption]]
class OptionInterpreter: class OptionInterpreter:
def __init__(self, subproject): def __init__(self, subproject: str) -> None:
self.options = {} self.options = {}
self.subproject = subproject self.subproject = subproject
def process(self, option_file): def process(self, option_file: str) -> None:
try: try:
with open(option_file, 'r', encoding='utf8') as f: with open(option_file, 'r', encoding='utf8') as f:
ast = mparser.Parser(f.read(), option_file).parse() ast = mparser.Parser(f.read(), option_file).parse()
@ -159,7 +158,7 @@ class OptionInterpreter:
e.file = option_file e.file = option_file
raise e raise e
def reduce_single(self, arg): def reduce_single(self, arg: T.Union[str, mparser.BaseNode]) -> T.Union[str, int, bool]:
if isinstance(arg, str): if isinstance(arg, str):
return arg return arg
elif isinstance(arg, (mparser.StringNode, mparser.BooleanNode, elif isinstance(arg, (mparser.StringNode, mparser.BooleanNode,
@ -189,7 +188,7 @@ class OptionInterpreter:
else: else:
raise OptionException('Arguments may only be string, int, bool, or array of those.') raise OptionException('Arguments may only be string, int, bool, or array of those.')
def reduce_arguments(self, args): def reduce_arguments(self, args: mparser.ArgumentNode) -> T.Tuple[T.List[T.Union[str, int, bool]], T.Dict[str, T.Union[str, int, bool]]]:
assert(isinstance(args, mparser.ArgumentNode)) assert(isinstance(args, mparser.ArgumentNode))
if args.incorrect_order(): if args.incorrect_order():
raise OptionException('All keyword arguments must be after positional arguments.') raise OptionException('All keyword arguments must be after positional arguments.')
@ -202,7 +201,7 @@ class OptionInterpreter:
reduced_kw[key.value] = self.reduce_single(a) reduced_kw[key.value] = self.reduce_single(a)
return reduced_pos, reduced_kw return reduced_pos, reduced_kw
def evaluate_statement(self, node): def evaluate_statement(self, node: mparser.BaseNode) -> None:
if not isinstance(node, mparser.FunctionNode): if not isinstance(node, mparser.FunctionNode):
raise OptionException('Option file may only contain option definitions') raise OptionException('Option file may only contain option definitions')
func_name = node.func_name func_name = node.func_name

@ -311,7 +311,7 @@ class Elf(DataSizes):
old_rpath = self.read_str() old_rpath = self.read_str()
# Some rpath entries may come from multiple sources. # Some rpath entries may come from multiple sources.
# Only add each one once. # Only add each one once.
new_rpaths = OrderedSet() new_rpaths = OrderedSet() # type: OrderedSet[bytes]
if new_rpath: if new_rpath:
new_rpaths.add(new_rpath) new_rpaths.add(new_rpath)
if old_rpath: if old_rpath:

@ -57,7 +57,7 @@ def run_exe(exe: ExecutableSerialisation) -> int:
if p.returncode == 0xc0000135: if p.returncode == 0xc0000135:
# STATUS_DLL_NOT_FOUND on Windows indicating a common problem that is otherwise hard to diagnose # STATUS_DLL_NOT_FOUND on Windows indicating a common problem that is otherwise hard to diagnose
raise FileNotFoundError('Missing DLLs on calling {!r}'.format(exe.name)) raise FileNotFoundError('Missing DLLs on calling {!r}'.format(cmd_args))
if exe.capture and p.returncode == 0: if exe.capture and p.returncode == 0:
skip_write = False skip_write = False

@ -15,6 +15,7 @@ modules = [
# specific files # specific files
'mesonbuild/arglist.py', 'mesonbuild/arglist.py',
# 'mesonbuild/compilers/mixins/intel.py', # 'mesonbuild/compilers/mixins/intel.py',
# 'mesonbuild/coredata.py',
'mesonbuild/dependencies/boost.py', 'mesonbuild/dependencies/boost.py',
'mesonbuild/dependencies/hdf5.py', 'mesonbuild/dependencies/hdf5.py',
'mesonbuild/dependencies/mpi.py', 'mesonbuild/dependencies/mpi.py',

Loading…
Cancel
Save