files: # Copyright 2012-2016 The Meson development team
  
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  from collections import OrderedDict
  from functools import lru_cache
  import enum
  import json
  import os
  import pickle
  import re
  import shlex
  import subprocess
  import textwrap
  import typing as T
  
  from .. import build
  from .. import dependencies
  from .. import mesonlib
  from .. import mlog
  from ..compilers import CompilerArgs, VisualStudioLikeCompiler
  from ..mesonlib import (
      File, MachineChoice, MesonException, OrderedSet, OptionOverrideProxy,
      classify_unity_sources, unholder
  )
  
  if T.TYPE_CHECKING:
      from ..interpreter import Interpreter
  
  
  class TestProtocol(enum.Enum):
  
      EXITCODE = 0
      TAP = 1
  
      @classmethod
      def from_str(cls, string: str) -> 'TestProtocol':
          if string == 'exitcode':
              return cls.EXITCODE
          elif string == 'tap':
              return cls.TAP
          raise MesonException('unknown test format {}'.format(string))
  
      def __str__(self) -> str:
          if self is self.EXITCODE:
              return 'exitcode'
          return 'tap'
  
  
  class CleanTrees:
      '''
      Directories outputted by custom targets that have to be manually cleaned
      because on Linux `ninja clean` only deletes empty directories.
      '''
      def __init__(self, build_dir, trees):
          self.build_dir = build_dir
          self.trees = trees
  
  class InstallData:
      def __init__(self, source_dir, build_dir, prefix, strip_bin,
                   install_umask, mesonintrospect):
          self.source_dir = source_dir
          self.build_dir = build_dir
          self.prefix = prefix
          self.strip_bin = strip_bin
          self.install_umask = install_umask
          self.targets = []
          self.headers = []
          self.man = []
          self.data = []
          self.po_package_name = ''
          self.po = []
          self.install_scripts = []
          self.install_subdirs = []
          self.mesonintrospect = mesonintrospect
  
  class TargetInstallData:
      def __init__(self, fname, outdir, aliases, strip, install_name_mappings, install_rpath, install_mode, optional=False):
          self.fname = fname
          self.outdir = outdir
          self.aliases = aliases
          self.strip = strip
          self.install_name_mappings = install_name_mappings
          self.install_rpath = install_rpath
          self.install_mode = install_mode
          self.optional = optional
  
  class ExecutableSerialisation:
      def __init__(self, cmd_args, env=None, exe_wrapper=None,
                   workdir=None, extra_paths=None, capture=None):
          self.cmd_args = cmd_args
          self.env = env or {}
          if exe_wrapper is not None:
              assert(isinstance(exe_wrapper, dependencies.ExternalProgram))
          self.exe_runner = exe_wrapper
          self.workdir = workdir
          self.extra_paths = extra_paths
          self.capture = capture
  
  class TestSerialisation:
      def __init__(self, name: str, project: str, suite: str, fname: T.List[str],
                   is_cross_built: bool, exe_wrapper: T.Optional[build.Executable],
                   needs_exe_wrapper: bool, is_parallel: bool, cmd_args: T.List[str],
                   env: build.EnvironmentVariables, should_fail: bool,
                   timeout: T.Optional[int], workdir: T.Optional[str],
                   extra_paths: T.List[str], protocol: TestProtocol, priority: int):
          self.name = name
          self.project_name = project
          self.suite = suite
          self.fname = fname
          self.is_cross_built = is_cross_built
          if exe_wrapper is not None:
              assert(isinstance(exe_wrapper, dependencies.ExternalProgram))
          self.exe_runner = exe_wrapper
          self.is_parallel = is_parallel
          self.cmd_args = cmd_args
          self.env = env
          self.should_fail = should_fail
          self.timeout = timeout
          self.workdir = workdir
          self.extra_paths = extra_paths
          self.protocol = protocol
          self.priority = priority
          self.needs_exe_wrapper = needs_exe_wrapper
  
  def get_backend_from_name(backend: str, build: T.Optional[build.Build] = None, interpreter: T.Optional['Interpreter'] = None) -> T.Optional['Backend']:
      if backend == 'ninja':
          from . import ninjabackend
          return ninjabackend.NinjaBackend(build, interpreter)
      elif backend == 'vs':
          from . import vs2010backend
          return vs2010backend.autodetect_vs_version(build, interpreter)
      elif backend == 'vs2010':
          from . import vs2010backend
          return vs2010backend.Vs2010Backend(build, interpreter)
      elif backend == 'vs2015':
          from . import vs2015backend
          return vs2015backend.Vs2015Backend(build, interpreter)
      elif backend == 'vs2017':
          from . import vs2017backend
          return vs2017backend.Vs2017Backend(build, interpreter)
      elif backend == 'vs2019':
          from . import vs2019backend
          return vs2019backend.Vs2019Backend(build, interpreter)
      elif backend == 'xcode':
          from . import xcodebackend
          return xcodebackend.XCodeBackend(build, interpreter)
      return None
  
  # This class contains the basic functionality that is needed by all backends.
  # Feel free to move stuff in and out of it as you see fit.
  class Backend:
      def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional['Interpreter']):
          # Make it possible to construct a dummy backend
          # This is used for introspection without a build directory
          if build is None:
              self.environment = None
              return
          self.build = build
          self.interpreter = interpreter
          self.environment = build.environment
          self.processed_targets = {}
          self.build_dir = self.environment.get_build_dir()
          self.source_dir = self.environment.get_source_dir()
          self.build_to_src = mesonlib.relpath(self.environment.get_source_dir(),
                                               self.environment.get_build_dir())
  
      def get_target_filename(self, t):
          if isinstance(t, build.CustomTarget):
              if len(t.get_outputs()) != 1:
                  mlog.warning('custom_target {!r} has more than one output! '
                               'Using the first one.'.format(t.name))
              filename = t.get_outputs()[0]
          elif isinstance(t, build.CustomTargetIndex):
              filename = t.get_outputs()[0]
          else:
              assert(isinstance(t, build.BuildTarget))
              filename = t.get_filename()
          return os.path.join(self.get_target_dir(t), filename)
  
      def get_target_filename_abs(self, target):
          return os.path.join(self.environment.get_build_dir(), self.get_target_filename(target))
  
      def get_base_options_for_target(self, target):
          return OptionOverrideProxy(target.option_overrides_base,
                                     self.environment.coredata.builtins,
                                     self.environment.coredata.base_options)
  
      def get_compiler_options_for_target(self, target):
          comp_reg = self.environment.coredata.compiler_options[target.for_machine]
          comp_override = target.option_overrides_compiler
          return {
              lang: OptionOverrideProxy(comp_override[lang], comp_reg[lang])
              for lang in set(comp_reg.keys()) | set(comp_override.keys())
          }
  
      def get_option_for_target(self, option_name, target):
          if option_name in target.option_overrides_base:
              override = target.option_overrides_base[option_name]
              return self.environment.coredata.validate_option_value(option_name, override)
          return self.environment.coredata.get_builtin_option(option_name, target.subproject)
  
      def get_target_filename_for_linking(self, target):
          # On some platforms (msvc for instance), the file that is used for
          # dynamic linking is not the same as the dynamic library itself. This
          # file is called an import
  
  [... the rest is omitted ...]
files: mesonbuild/backend/backends.py
files: # Copyright 2012-2016 The Meson development team
  
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  from collections import OrderedDict
  from functools import lru_cache
  import json
  import os
  import pickle
  import re
  import shlex
  import subprocess
  import textwrap
  import typing as T
  
  from .. import build
  from .. import dependencies
  from .. import mesonlib
  from .. import mlog
  from ..compilers import CompilerArgs, VisualStudioLikeCompiler
  from ..interpreter import Interpreter
  from ..mesonlib import (
      File, MachineChoice, MesonException, OrderedSet, OptionOverrideProxy,
      classify_unity_sources, unholder
  )
  
  
  class CleanTrees:
      '''
      Directories outputted by custom targets that have to be manually cleaned
      because on Linux `ninja clean` only deletes empty directories.
      '''
      def __init__(self, build_dir, trees):
          self.build_dir = build_dir
          self.trees = trees
  
  class InstallData:
      def __init__(self, source_dir, build_dir, prefix, strip_bin,
                   install_umask, mesonintrospect):
          self.source_dir = source_dir
          self.build_dir = build_dir
          self.prefix = prefix
          self.strip_bin = strip_bin
          self.install_umask = install_umask
          self.targets = []
          self.headers = []
          self.man = []
          self.data = []
          self.po_package_name = ''
          self.po = []
          self.install_scripts = []
          self.install_subdirs = []
          self.mesonintrospect = mesonintrospect
  
  class TargetInstallData:
      def __init__(self, fname, outdir, aliases, strip, install_name_mappings, install_rpath, install_mode, optional=False):
          self.fname = fname
          self.outdir = outdir
          self.aliases = aliases
          self.strip = strip
          self.install_name_mappings = install_name_mappings
          self.install_rpath = install_rpath
          self.install_mode = install_mode
          self.optional = optional
  
  class ExecutableSerialisation:
      def __init__(self, cmd_args, env=None, exe_wrapper=None,
                   workdir=None, extra_paths=None, capture=None):
          self.cmd_args = cmd_args
          self.env = env or {}
          if exe_wrapper is not None:
              assert(isinstance(exe_wrapper, dependencies.ExternalProgram))
          self.exe_runner = exe_wrapper
          self.workdir = workdir
          self.extra_paths = extra_paths
          self.capture = capture
  
  class TestSerialisation:
      def __init__(self, name: str, project: str, suite: str, fname: T.List[str],
                   is_cross_built: bool, exe_wrapper: T.Optional[build.Executable],
                   needs_exe_wrapper: bool, is_parallel: bool, cmd_args: T.List[str],
                   env: build.EnvironmentVariables, should_fail: bool,
                   timeout: T.Optional[int], workdir: T.Optional[str],
                   extra_paths: T.List[str], protocol: str, priority: int):
          self.name = name
          self.project_name = project
          self.suite = suite
          self.fname = fname
          self.is_cross_built = is_cross_built
          if exe_wrapper is not None:
              assert(isinstance(exe_wrapper, dependencies.ExternalProgram))
          self.exe_runner = exe_wrapper
          self.is_parallel = is_parallel
          self.cmd_args = cmd_args
          self.env = env
          self.should_fail = should_fail
          self.timeout = timeout
          self.workdir = workdir
          self.extra_paths = extra_paths
          self.protocol = protocol
          self.priority = priority
          self.needs_exe_wrapper = needs_exe_wrapper
  
  def get_backend_from_name(backend: str, build: T.Optional[build.Build] = None, interpreter: T.Optional[Interpreter] = None) -> T.Optional['Backend']:
      if backend == 'ninja':
          from . import ninjabackend
          return ninjabackend.NinjaBackend(build, interpreter)
      elif backend == 'vs':
          from . import vs2010backend
          return vs2010backend.autodetect_vs_version(build, interpreter)
      elif backend == 'vs2010':
          from . import vs2010backend
          return vs2010backend.Vs2010Backend(build, interpreter)
      elif backend == 'vs2015':
          from . import vs2015backend
          return vs2015backend.Vs2015Backend(build, interpreter)
      elif backend == 'vs2017':
          from . import vs2017backend
          return vs2017backend.Vs2017Backend(build, interpreter)
      elif backend == 'vs2019':
          from . import vs2019backend
          return vs2019backend.Vs2019Backend(build, interpreter)
      elif backend == 'xcode':
          from . import xcodebackend
          return xcodebackend.XCodeBackend(build, interpreter)
      return None
  
  # This class contains the basic functionality that is needed by all backends.
  # Feel free to move stuff in and out of it as you see fit.
  class Backend:
      def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional[Interpreter]):
          # Make it possible to construct a dummy backend
          # This is used for introspection without a build directory
          if build is None:
              self.environment = None
              return
          self.build = build
          self.interpreter = interpreter
          self.environment = build.environment
          self.processed_targets = {}
          self.build_dir = self.environment.get_build_dir()
          self.source_dir = self.environment.get_source_dir()
          self.build_to_src = mesonlib.relpath(self.environment.get_source_dir(),
                                               self.environment.get_build_dir())
  
      def get_target_filename(self, t):
          if isinstance(t, build.CustomTarget):
              if len(t.get_outputs()) != 1:
                  mlog.warning('custom_target {!r} has more than one output! '
                               'Using the first one.'.format(t.name))
              filename = t.get_outputs()[0]
          elif isinstance(t, build.CustomTargetIndex):
              filename = t.get_outputs()[0]
          else:
              assert(isinstance(t, build.BuildTarget))
              filename = t.get_filename()
          return os.path.join(self.get_target_dir(t), filename)
  
      def get_target_filename_abs(self, target):
          return os.path.join(self.environment.get_build_dir(), self.get_target_filename(target))
  
      def get_base_options_for_target(self, target):
          return OptionOverrideProxy(target.option_overrides_base,
                                     self.environment.coredata.builtins,
                                     self.environment.coredata.base_options)
  
      def get_compiler_options_for_target(self, target):
          comp_reg = self.environment.coredata.compiler_options[target.for_machine]
          comp_override = target.option_overrides_compiler
          return {
              lang: OptionOverrideProxy(comp_override[lang], comp_reg[lang])
              for lang in set(comp_reg.keys()) | set(comp_override.keys())
          }
  
      def get_option_for_target(self, option_name, target):
          if option_name in target.option_overrides_base:
              override = target.option_overrides_base[option_name]
              return self.environment.coredata.validate_option_value(option_name, override)
          return self.environment.coredata.get_builtin_option(option_name, target.subproject)
  
      def get_target_filename_for_linking(self, target):
          # On some platforms (msvc for instance), the file that is used for
          # dynamic linking is not the same as the dynamic library itself. This
          # file is called an import library, and we want to link against that.
          # On all other platforms, we link to the library directly.
          if isinstance(target, build.SharedLibrary):
              link_lib = target.get_import_filename() or target.get_filename()
              return os.path.join(self.get_target_dir(target), link_lib)
          elif isinstance(target, build.StaticLibrary):
              return os.path.join(self.get_target_dir(target), target.get_filename())
          elif isinstance(target, (build.CustomTarget, build.CustomTargetIndex)):
              if not target.is_linkable_target():
                  raise MesonException('Tried to link against custom target
  
  [... the rest is omitted ...]
files: mesonbuild/backend/backends.py
files: # Copyright 2012-2019 The Meson development team
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  from . import mparser
  from . import environment
  from . import coredata
  from . import dependencies
  from . import mlog
  from . import build
  from . import optinterpreter
  from . import compilers
  from .wrap import wrap, WrapMode
  from . import mesonlib
  from .mesonlib import FileMode, MachineChoice, Popen_safe, listify, extract_as_list, has_path_sep, unholder
  from .dependencies import ExternalProgram
  from .dependencies import InternalDependency, Dependency, NotFoundDependency, DependencyException
  from .depfile import DepFile
  from .interpreterbase import InterpreterBase
  from .interpreterbase import check_stringlist, flatten, noPosargs, noKwargs, stringArgs, permittedKwargs, noArgsFlattening
  from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode, SubdirDoneRequest
  from .interpreterbase import InterpreterObject, MutableInterpreterObject, Disabler, disablerIfNotFound
  from .interpreterbase import FeatureNew, FeatureDeprecated, FeatureNewKwargs
  from .interpreterbase import ObjectHolder
  from .modules import ModuleReturnValue
  from .cmake import CMakeInterpreter
  from .backend.backends import TestProtocol
  
  from pathlib import Path, PurePath
  import os
  import shutil
  import uuid
  import re
  import shlex
  import subprocess
  import collections
  import functools
  import typing as T
  
  import importlib
  
  permitted_method_kwargs = {
      'partial_dependency': {'compile_args', 'link_args', 'links', 'includes',
                             'sources'},
  }
  
  def stringifyUserArguments(args):
      if isinstance(args, list):
          return '[%s]' % ', '.join([stringifyUserArguments(x) for x in args])
      elif isinstance(args, dict):
          return '{%s}' % ', '.join(['%s : %s' % (stringifyUserArguments(k), stringifyUserArguments(v)) for k, v in args.items()])
      elif isinstance(args, int):
          return str(args)
      elif isinstance(args, str):
          return "'%s'" % args
      raise InvalidArguments('Function accepts only strings, integers, lists and lists thereof.')
  
  
  class OverrideProgram(dependencies.ExternalProgram):
      pass
  
  
  class FeatureOptionHolder(InterpreterObject, ObjectHolder):
      def __init__(self, env, name, option):
          InterpreterObject.__init__(self)
          ObjectHolder.__init__(self, option)
          if option.is_auto():
              self.held_object = env.coredata.builtins['auto_features']
          self.name = name
          self.methods.update({'enabled': self.enabled_method,
                               'disabled': self.disabled_method,
                               'auto': self.auto_method,
                               })
  
      @noPosargs
      @permittedKwargs({})
      def enabled_method(self, args, kwargs):
          return self.held_object.is_enabled()
  
      @noPosargs
      @permittedKwargs({})
      def disabled_method(self, args, kwargs):
          return self.held_object.is_disabled()
  
      @noPosargs
      @permittedKwargs({})
      def auto_method(self, args, kwargs):
          return self.held_object.is_auto()
  
  def extract_required_kwarg(kwargs, subproject, feature_check=None, default=True):
      val = kwargs.get('required', default)
      disabled = False
      required = False
      feature = None
      if isinstance(val, FeatureOptionHolder):
          if not feature_check:
              feature_check = FeatureNew('User option "feature"', '0.47.0')
          feature_check.use(subproject)
          option = val.held_object
          feature = val.name
          if option.is_disabled():
              disabled = True
          elif option.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.
      kwargs['required'] = required
  
      return disabled, required, feature
  
  def extract_search_dirs(kwargs):
      search_dirs = mesonlib.stringlistify(kwargs.get('dirs', []))
      search_dirs = [Path(d).expanduser() for d in search_dirs]
      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('Search directory {} is not an absolute path.'.format(d))
      return list(map(str, search_dirs))
  
  class TryRunResultHolder(InterpreterObject):
      def __init__(self, res):
          super().__init__()
          self.res = res
          self.methods.update({'returncode': self.returncode_method,
                               'compiled': self.compiled_method,
                               'stdout': self.stdout_method,
                               'stderr': self.stderr_method,
                               })
  
      @noPosargs
      @permittedKwargs({})
      def returncode_method(self, args, kwargs):
          return self.res.returncode
  
      @noPosargs
      @permittedKwargs({})
      def compiled_method(self, args, kwargs):
          return self.res.compiled
  
      @noPosargs
      @permittedKwargs({})
      def stdout_method(self, args, kwargs):
          return self.res.stdout
  
      @noPosargs
      @permittedKwargs({})
      def stderr_method(self, args, kwargs):
          return self.res.stderr
  
  class RunProcess(InterpreterObject):
  
      def __init__(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir=False, check=False, capture=True):
          super().__init__()
          if not isinstance(cmd, ExternalProgram):
              raise AssertionError('BUG: RunProcess must be passed an ExternalProgram')
          self.capture = capture
          pc, self.stdout, self.stderr = self.run_command(cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check)
          self.returncode = pc.returncode
          self.methods.update({'returncode': self.returncode_method,
                               'stdout': self.stdout_method,
                               'stderr': self.stderr_method,
                               })
  
      def run_command(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check=False):
          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)
          stdout = subprocess.PIPE if self.capture else subprocess.DEVNULL
          mlog.debug('Running command:', ' '.join(command_array))
          try:
              p, o, e = Popen_safe(command_array, stdout=stdout, env=child_env, cwd=cwd)
              if self.capture:
                  mlog.debug('--- stdout ---')
                  mlog.debug(o)
              else:
                  o = ''
                  mlog.debug('--- stdout disabled ---')
              mlog.debug('--- stderr ---')
              mlog.debug(e)
              mlog.debug('')
  
              if check and p.returncode != 0:
                  raise InterpreterException('Command "{}" failed with status {}.'.format(' '.join(command_array), p.returncode))
  
              return p, o, e
          except FileNotFoundError:
              raise InterpreterException('Could not execute command "%s".' % ' '.join(command_array))
  
      @noPosargs
      @permittedKwargs({})
      def returncode_method(self, args, kwargs):
          return self.returncode
  
      @noPosargs
      @permittedKwargs({})
      def stdout_method(self, args, kwargs):
          return self.stdout
  
      @noPosargs
      @per
  
  [... the rest is omitted ...]
files: mesonbuild/interpreter.py
files: # Copyright 2012-2019 The Meson development team
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  from . import mparser
  from . import environment
  from . import coredata
  from . import dependencies
  from . import mlog
  from . import build
  from . import optinterpreter
  from . import compilers
  from .wrap import wrap, WrapMode
  from . import mesonlib
  from .mesonlib import FileMode, MachineChoice, Popen_safe, listify, extract_as_list, has_path_sep, unholder
  from .dependencies import ExternalProgram
  from .dependencies import InternalDependency, Dependency, NotFoundDependency, DependencyException
  from .depfile import DepFile
  from .interpreterbase import InterpreterBase
  from .interpreterbase import check_stringlist, flatten, noPosargs, noKwargs, stringArgs, permittedKwargs, noArgsFlattening
  from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode, SubdirDoneRequest
  from .interpreterbase import InterpreterObject, MutableInterpreterObject, Disabler, disablerIfNotFound
  from .interpreterbase import FeatureNew, FeatureDeprecated, FeatureNewKwargs
  from .interpreterbase import ObjectHolder
  from .modules import ModuleReturnValue
  from .cmake import CMakeInterpreter
  
  from pathlib import Path, PurePath
  import os
  import shutil
  import uuid
  import re
  import shlex
  import subprocess
  import collections
  import functools
  import typing as T
  
  import importlib
  
  permitted_method_kwargs = {
      'partial_dependency': {'compile_args', 'link_args', 'links', 'includes',
                             'sources'},
  }
  
  def stringifyUserArguments(args):
      if isinstance(args, list):
          return '[%s]' % ', '.join([stringifyUserArguments(x) for x in args])
      elif isinstance(args, dict):
          return '{%s}' % ', '.join(['%s : %s' % (stringifyUserArguments(k), stringifyUserArguments(v)) for k, v in args.items()])
      elif isinstance(args, int):
          return str(args)
      elif isinstance(args, str):
          return "'%s'" % args
      raise InvalidArguments('Function accepts only strings, integers, lists and lists thereof.')
  
  
  class OverrideProgram(dependencies.ExternalProgram):
      pass
  
  
  class FeatureOptionHolder(InterpreterObject, ObjectHolder):
      def __init__(self, env, name, option):
          InterpreterObject.__init__(self)
          ObjectHolder.__init__(self, option)
          if option.is_auto():
              self.held_object = env.coredata.builtins['auto_features']
          self.name = name
          self.methods.update({'enabled': self.enabled_method,
                               'disabled': self.disabled_method,
                               'auto': self.auto_method,
                               })
  
      @noPosargs
      @permittedKwargs({})
      def enabled_method(self, args, kwargs):
          return self.held_object.is_enabled()
  
      @noPosargs
      @permittedKwargs({})
      def disabled_method(self, args, kwargs):
          return self.held_object.is_disabled()
  
      @noPosargs
      @permittedKwargs({})
      def auto_method(self, args, kwargs):
          return self.held_object.is_auto()
  
  def extract_required_kwarg(kwargs, subproject, feature_check=None, default=True):
      val = kwargs.get('required', default)
      disabled = False
      required = False
      feature = None
      if isinstance(val, FeatureOptionHolder):
          if not feature_check:
              feature_check = FeatureNew('User option "feature"', '0.47.0')
          feature_check.use(subproject)
          option = val.held_object
          feature = val.name
          if option.is_disabled():
              disabled = True
          elif option.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.
      kwargs['required'] = required
  
      return disabled, required, feature
  
  def extract_search_dirs(kwargs):
      search_dirs = mesonlib.stringlistify(kwargs.get('dirs', []))
      search_dirs = [Path(d).expanduser() for d in search_dirs]
      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('Search directory {} is not an absolute path.'.format(d))
      return list(map(str, search_dirs))
  
  class TryRunResultHolder(InterpreterObject):
      def __init__(self, res):
          super().__init__()
          self.res = res
          self.methods.update({'returncode': self.returncode_method,
                               'compiled': self.compiled_method,
                               'stdout': self.stdout_method,
                               'stderr': self.stderr_method,
                               })
  
      @noPosargs
      @permittedKwargs({})
      def returncode_method(self, args, kwargs):
          return self.res.returncode
  
      @noPosargs
      @permittedKwargs({})
      def compiled_method(self, args, kwargs):
          return self.res.compiled
  
      @noPosargs
      @permittedKwargs({})
      def stdout_method(self, args, kwargs):
          return self.res.stdout
  
      @noPosargs
      @permittedKwargs({})
      def stderr_method(self, args, kwargs):
          return self.res.stderr
  
  class RunProcess(InterpreterObject):
  
      def __init__(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir=False, check=False, capture=True):
          super().__init__()
          if not isinstance(cmd, ExternalProgram):
              raise AssertionError('BUG: RunProcess must be passed an ExternalProgram')
          self.capture = capture
          pc, self.stdout, self.stderr = self.run_command(cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check)
          self.returncode = pc.returncode
          self.methods.update({'returncode': self.returncode_method,
                               'stdout': self.stdout_method,
                               'stderr': self.stderr_method,
                               })
  
      def run_command(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check=False):
          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)
          stdout = subprocess.PIPE if self.capture else subprocess.DEVNULL
          mlog.debug('Running command:', ' '.join(command_array))
          try:
              p, o, e = Popen_safe(command_array, stdout=stdout, env=child_env, cwd=cwd)
              if self.capture:
                  mlog.debug('--- stdout ---')
                  mlog.debug(o)
              else:
                  o = ''
                  mlog.debug('--- stdout disabled ---')
              mlog.debug('--- stderr ---')
              mlog.debug(e)
              mlog.debug('')
  
              if check and p.returncode != 0:
                  raise InterpreterException('Command "{}" failed with status {}.'.format(' '.join(command_array), p.returncode))
  
              return p, o, e
          except FileNotFoundError:
              raise InterpreterException('Could not execute command "%s".' % ' '.join(command_array))
  
      @noPosargs
      @permittedKwargs({})
      def returncode_method(self, args, kwargs):
          return self.returncode
  
      @noPosargs
      @permittedKwargs({})
      def stdout_method(self, args, kwargs):
          return self.stdout
  
      @noPosargs
      @permittedKwargs({})
      def stderr
  
  [... the rest is omitted ...]
files: mesonbuild/interpreter.py
files: # Copyright 2014-2016 The Meson development team
  
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  """This is a helper script for IDE developers. It allows you to
  extract information such as list of targets, files, compiler flags,
  tests and so on. All output is in JSON for simple parsing.
  
  Currently only works for the Ninja backend. Others use generated
  project files and don't need this info."""
  
  import json
  from . import build, coredata as cdata
  from . import mesonlib
  from .ast import IntrospectionInterpreter, build_target_functions, AstConditionLevel, AstIDGenerator, AstIndentationGenerator
  from . import mlog
  from .backend import backends
  from .mparser import BaseNode, FunctionNode, ArrayNode, ArgumentNode, StringNode
  from .interpreter import Interpreter
  from pathlib import PurePath
  import typing as T
  import os
  
  def get_meson_info_file(info_dir: str) -> str:
      return os.path.join(info_dir, 'meson-info.json')
  
  def get_meson_introspection_version() -> str:
      return '1.0.0'
  
  def get_meson_introspection_required_version() -> T.List[str]:
      return ['>=1.0', '<2.0']
  
  class IntroCommand:
      def __init__(self,
                   desc: str,
                   func: T.Optional[T.Callable[[], T.Union[dict, list]]] = None,
                   no_bd: T.Optional[T.Callable[[IntrospectionInterpreter], T.Union[dict, list]]] = None) -> None:
          self.desc = desc + '.'
          self.func = func
          self.no_bd = no_bd
  
  def get_meson_introspection_types(coredata: T.Optional[cdata.CoreData] = None,
                                    builddata: T.Optional[build.Build] = None,
                                    backend: T.Optional[backends.Backend] = None,
                                    sourcedir: T.Optional[str] = None) -> T.Dict[str, IntroCommand]:
      if backend and builddata:
          benchmarkdata = backend.create_test_serialisation(builddata.get_benchmarks())
          testdata = backend.create_test_serialisation(builddata.get_tests())
          installdata = backend.create_install_data()
          interpreter = backend.interpreter
      else:
          benchmarkdata = testdata = installdata = None
  
      return {
          'benchmarks': IntroCommand('List all benchmarks', func=lambda: list_benchmarks(benchmarkdata)),
          'buildoptions': IntroCommand('List all build options', func=lambda: list_buildoptions(coredata), no_bd=list_buildoptions_from_source),
          'buildsystem_files': IntroCommand('List files that make up the build system', func=lambda: list_buildsystem_files(builddata, interpreter)),
          'dependencies': IntroCommand('List external dependencies', func=lambda: list_deps(coredata), no_bd=list_deps_from_source),
          'scan_dependencies': IntroCommand('Scan for dependencies used in the meson.build file', no_bd=list_deps_from_source),
          'installed': IntroCommand('List all installed files and directories', func=lambda: list_installed(installdata)),
          'projectinfo': IntroCommand('Information about projects', func=lambda: list_projinfo(builddata), no_bd=list_projinfo_from_source),
          'targets': IntroCommand('List top level targets', func=lambda: list_targets(builddata, installdata, backend), no_bd=list_targets_from_source),
          'tests': IntroCommand('List all unit tests', func=lambda: list_tests(testdata)),
      }
  
  def add_arguments(parser):
      intro_types = get_meson_introspection_types()
      for key, val in intro_types.items():
          flag = '--' + key.replace('_', '-')
          parser.add_argument(flag, action='store_true', dest=key, default=False, help=val.desc)
  
      parser.add_argument('--backend', choices=cdata.backendlist, dest='backend', default='ninja',
                          help='The backend to use for the --buildoptions introspection.')
      parser.add_argument('-a', '--all', action='store_true', dest='all', default=False,
                          help='Print all available information.')
      parser.add_argument('-i', '--indent', action='store_true', dest='indent', default=False,
                          help='Enable pretty printed JSON.')
      parser.add_argument('-f', '--force-object-output', action='store_true', dest='force_dict', default=False,
                          help='Always use the new JSON format for multiple entries (even for 0 and 1 introspection commands)')
      parser.add_argument('builddir', nargs='?', default='.', help='The build directory')
  
  def list_installed(installdata):
      res = {}
      if installdata is not None:
          for t in installdata.targets:
              res[os.path.join(installdata.build_dir, t.fname)] = \
                  os.path.join(installdata.prefix, t.outdir, os.path.basename(t.fname))
              for alias in t.aliases.keys():
                  res[os.path.join(installdata.build_dir, alias)] = \
                      os.path.join(installdata.prefix, t.outdir, os.path.basename(alias))
          for path, installpath, _ in installdata.data:
              res[path] = os.path.join(installdata.prefix, installpath)
          for path, installdir, _ in installdata.headers:
              res[path] = os.path.join(installdata.prefix, installdir, os.path.basename(path))
          for path, installpath, _ in installdata.man:
              res[path] = os.path.join(installdata.prefix, installpath)
          for path, installpath, _, _ in installdata.install_subdirs:
              res[path] = os.path.join(installdata.prefix, installpath)
      return res
  
  def list_targets_from_source(intr: IntrospectionInterpreter) -> T.List[T.Dict[str, T.Union[bool, str, T.List[T.Union[str, T.Dict[str, T.Union[str, T.List[str], bool]]]]]]]:
      tlist = []  # type: T.List[T.Dict[str, T.Union[bool, str, T.List[T.Union[str, T.Dict[str, T.Union[str, T.List[str], bool]]]]]]]
      for i in intr.targets:
          sources = []  # type: T.List[str]
          for n in i['sources']:
              args = []  # type: T.List[BaseNode]
              if isinstance(n, FunctionNode):
                  args = list(n.args.arguments)
                  if n.func_name in build_target_functions:
                      args.pop(0)
              elif isinstance(n, ArrayNode):
                  args = n.args.arguments
              elif isinstance(n, ArgumentNode):
                  args = n.arguments
              for j in args:
                  if isinstance(j, StringNode):
                      assert isinstance(j.value, str)
                      sources += [j.value]
                  elif isinstance(j, str):
                      sources += [j]
  
          tlist += [{
              'name': i['name'],
              'id': i['id'],
              'type': i['type'],
              'defined_in': i['defined_in'],
              'filename': [os.path.join(i['subdir'], x) for x in i['outputs']],
              'build_by_default': i['build_by_default'],
              'target_sources': [{
                  'language': 'unknown',
                  'compiler': [],
                  'parameters': [],
                  'sources': [os.path.normpath(os.path.join(os.path.abspath(intr.source_root), i['subdir'], x)) for x in sources],
                  'generated_sources': []
              }],
              'subproject': None, # Subprojects are not supported
              'installed': i['installed']
          }]
  
      return tlist
  
  def list_targets(builddata: build.Build, installdata, backend: backends.Backend) -> T.List[T.Dict[str, T.Union[bool, str, T.List[T.Union[str, T.Dict[str, T.Union[str, T.List[str], bool]]]]]]]:
      tlist = []  # type: T.List[T.Dict[str, T.Union[bool, str, T.List[T.Union[str, T.Dict[str, T.Union[str, T.List[str], bool]]]]]]]
      build_dir = builddata.en
  
  [... the rest is omitted ...]
files: mesonbuild/mintro.py
files: # Copyright 2014-2016 The Meson development team
  
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  """This is a helper script for IDE developers. It allows you to
  extract information such as list of targets, files, compiler flags,
  tests and so on. All output is in JSON for simple parsing.
  
  Currently only works for the Ninja backend. Others use generated
  project files and don't need this info."""
  
  import json
  from . import build, coredata as cdata
  from . import mesonlib
  from .ast import IntrospectionInterpreter, build_target_functions, AstConditionLevel, AstIDGenerator, AstIndentationGenerator
  from . import mlog
  from .backend import backends
  from .mparser import BaseNode, FunctionNode, ArrayNode, ArgumentNode, StringNode
  from .interpreter import Interpreter
  from pathlib import PurePath
  import typing as T
  import os
  
  def get_meson_info_file(info_dir: str) -> str:
      return os.path.join(info_dir, 'meson-info.json')
  
  def get_meson_introspection_version() -> str:
      return '1.0.0'
  
  def get_meson_introspection_required_version() -> T.List[str]:
      return ['>=1.0', '<2.0']
  
  class IntroCommand:
      def __init__(self,
                   desc: str,
                   func: T.Optional[T.Callable[[], T.Union[dict, list]]] = None,
                   no_bd: T.Optional[T.Callable[[IntrospectionInterpreter], T.Union[dict, list]]] = None) -> None:
          self.desc = desc + '.'
          self.func = func
          self.no_bd = no_bd
  
  def get_meson_introspection_types(coredata: T.Optional[cdata.CoreData] = None,
                                    builddata: T.Optional[build.Build] = None,
                                    backend: T.Optional[backends.Backend] = None,
                                    sourcedir: T.Optional[str] = None) -> T.Dict[str, IntroCommand]:
      if backend and builddata:
          benchmarkdata = backend.create_test_serialisation(builddata.get_benchmarks())
          testdata = backend.create_test_serialisation(builddata.get_tests())
          installdata = backend.create_install_data()
          interpreter = backend.interpreter
      else:
          benchmarkdata = testdata = installdata = None
  
      return {
          'benchmarks': IntroCommand('List all benchmarks', func=lambda: list_benchmarks(benchmarkdata)),
          'buildoptions': IntroCommand('List all build options', func=lambda: list_buildoptions(coredata), no_bd=list_buildoptions_from_source),
          'buildsystem_files': IntroCommand('List files that make up the build system', func=lambda: list_buildsystem_files(builddata, interpreter)),
          'dependencies': IntroCommand('List external dependencies', func=lambda: list_deps(coredata), no_bd=list_deps_from_source),
          'scan_dependencies': IntroCommand('Scan for dependencies used in the meson.build file', no_bd=list_deps_from_source),
          'installed': IntroCommand('List all installed files and directories', func=lambda: list_installed(installdata)),
          'projectinfo': IntroCommand('Information about projects', func=lambda: list_projinfo(builddata), no_bd=list_projinfo_from_source),
          'targets': IntroCommand('List top level targets', func=lambda: list_targets(builddata, installdata, backend), no_bd=list_targets_from_source),
          'tests': IntroCommand('List all unit tests', func=lambda: list_tests(testdata)),
      }
  
  def add_arguments(parser):
      intro_types = get_meson_introspection_types()
      for key, val in intro_types.items():
          flag = '--' + key.replace('_', '-')
          parser.add_argument(flag, action='store_true', dest=key, default=False, help=val.desc)
  
      parser.add_argument('--backend', choices=cdata.backendlist, dest='backend', default='ninja',
                          help='The backend to use for the --buildoptions introspection.')
      parser.add_argument('-a', '--all', action='store_true', dest='all', default=False,
                          help='Print all available information.')
      parser.add_argument('-i', '--indent', action='store_true', dest='indent', default=False,
                          help='Enable pretty printed JSON.')
      parser.add_argument('-f', '--force-object-output', action='store_true', dest='force_dict', default=False,
                          help='Always use the new JSON format for multiple entries (even for 0 and 1 introspection commands)')
      parser.add_argument('builddir', nargs='?', default='.', help='The build directory')
  
  def list_installed(installdata):
      res = {}
      if installdata is not None:
          for t in installdata.targets:
              res[os.path.join(installdata.build_dir, t.fname)] = \
                  os.path.join(installdata.prefix, t.outdir, os.path.basename(t.fname))
              for alias in t.aliases.keys():
                  res[os.path.join(installdata.build_dir, alias)] = \
                      os.path.join(installdata.prefix, t.outdir, os.path.basename(alias))
          for path, installpath, _ in installdata.data:
              res[path] = os.path.join(installdata.prefix, installpath)
          for path, installdir, _ in installdata.headers:
              res[path] = os.path.join(installdata.prefix, installdir, os.path.basename(path))
          for path, installpath, _ in installdata.man:
              res[path] = os.path.join(installdata.prefix, installpath)
          for path, installpath, _, _ in installdata.install_subdirs:
              res[path] = os.path.join(installdata.prefix, installpath)
      return res
  
  def list_targets_from_source(intr: IntrospectionInterpreter) -> T.List[T.Dict[str, T.Union[bool, str, T.List[T.Union[str, T.Dict[str, T.Union[str, T.List[str], bool]]]]]]]:
      tlist = []  # type: T.List[T.Dict[str, T.Union[bool, str, T.List[T.Union[str, T.Dict[str, T.Union[str, T.List[str], bool]]]]]]]
      for i in intr.targets:
          sources = []  # type: T.List[str]
          for n in i['sources']:
              args = []  # type: T.List[BaseNode]
              if isinstance(n, FunctionNode):
                  args = list(n.args.arguments)
                  if n.func_name in build_target_functions:
                      args.pop(0)
              elif isinstance(n, ArrayNode):
                  args = n.args.arguments
              elif isinstance(n, ArgumentNode):
                  args = n.arguments
              for j in args:
                  if isinstance(j, StringNode):
                      assert isinstance(j.value, str)
                      sources += [j.value]
                  elif isinstance(j, str):
                      sources += [j]
  
          tlist += [{
              'name': i['name'],
              'id': i['id'],
              'type': i['type'],
              'defined_in': i['defined_in'],
              'filename': [os.path.join(i['subdir'], x) for x in i['outputs']],
              'build_by_default': i['build_by_default'],
              'target_sources': [{
                  'language': 'unknown',
                  'compiler': [],
                  'parameters': [],
                  'sources': [os.path.normpath(os.path.join(os.path.abspath(intr.source_root), i['subdir'], x)) for x in sources],
                  'generated_sources': []
              }],
              'subproject': None, # Subprojects are not supported
              'installed': i['installed']
          }]
  
      return tlist
  
  def list_targets(builddata: build.Build, installdata, backend: backends.Backend) -> T.List[T.Dict[str, T.Union[bool, str, T.List[T.Union[str, T.Dict[str, T.Union[str, T.List[str], bool]]]]]]]:
      tlist = []  # type: T.List[T.Dict[str, T.Union[bool, str, T.List[T.Union[str, T.Dict[str, T.Union[str, T.List[str], bool]]]]]]]
      build_dir = builddata.en
  
  [... the rest is omitted ...]
files: mesonbuild/mintro.py
files: # Copyright 2016-2017 The Meson development team
  
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  # A tool to run tests in many different ways.
  
  from pathlib import Path
  from collections import namedtuple
  from copy import deepcopy
  import argparse
  import concurrent.futures as conc
  import datetime
  import enum
  import io
  import json
  import multiprocessing
  import os
  import pickle
  import platform
  import random
  import re
  import signal
  import subprocess
  import sys
  import tempfile
  import textwrap
  import time
  import typing as T
  import xml.etree.ElementTree as et
  
  from . import build
  from . import environment
  from . import mlog
  from .dependencies import ExternalProgram
  from .mesonlib import MesonException, get_wine_shortpath, split_args
  from .backend.backends import TestProtocol
  
  if T.TYPE_CHECKING:
      from .backend.backends import TestSerialisation
  
  # GNU autotools interprets a return code of 77 from tests it executes to
  # mean that the test should be skipped.
  GNU_SKIP_RETURNCODE = 77
  
  # GNU autotools interprets a return code of 99 from tests it executes to
  # mean that the test failed even before testing what it is supposed to test.
  GNU_ERROR_RETURNCODE = 99
  
  def is_windows() -> bool:
      platname = platform.system().lower()
      return platname == 'windows' or 'mingw' in platname
  
  def is_cygwin() -> bool:
      platname = platform.system().lower()
      return 'cygwin' in platname
  
  def determine_worker_count() -> int:
      varname = 'MESON_TESTTHREADS'
      if varname in os.environ:
          try:
              num_workers = int(os.environ[varname])
          except ValueError:
              print('Invalid value in {}, using 1 thread.'.format(varname))
              num_workers = 1
      else:
          try:
              # Fails in some weird environments such as Debian
              # reproducible build.
              num_workers = multiprocessing.cpu_count()
          except Exception:
              num_workers = 1
      return num_workers
  
  def add_arguments(parser: argparse.ArgumentParser) -> None:
      parser.add_argument('--repeat', default=1, dest='repeat', type=int,
                          help='Number of times to run the tests.')
      parser.add_argument('--no-rebuild', default=False, action='store_true',
                          help='Do not rebuild before running tests.')
      parser.add_argument('--gdb', default=False, dest='gdb', action='store_true',
                          help='Run test under gdb.')
      parser.add_argument('--gdb-path', default='gdb', dest='gdb_path',
                          help='Path to the gdb binary (default: gdb).')
      parser.add_argument('--list', default=False, dest='list', action='store_true',
                          help='List available tests.')
      parser.add_argument('--wrapper', default=None, dest='wrapper', type=split_args,
                          help='wrapper to run tests with (e.g. Valgrind)')
      parser.add_argument('-C', default='.', dest='wd', type=os.path.abspath,
                          help='directory to cd into before running')
      parser.add_argument('--suite', default=[], dest='include_suites', action='append', metavar='SUITE',
                          help='Only run tests belonging to the given suite.')
      parser.add_argument('--no-suite', default=[], dest='exclude_suites', action='append', metavar='SUITE',
                          help='Do not run tests belonging to the given suite.')
      parser.add_argument('--no-stdsplit', default=True, dest='split', action='store_false',
                          help='Do not split stderr and stdout in test logs.')
      parser.add_argument('--print-errorlogs', default=False, action='store_true',
                          help="Whether to print failing tests' logs.")
      parser.add_argument('--benchmark', default=False, action='store_true',
                          help="Run benchmarks instead of tests.")
      parser.add_argument('--logbase', default='testlog',
                          help="Base name for log file.")
      parser.add_argument('--num-processes', default=determine_worker_count(), type=int,
                          help='How many parallel processes to use.')
      parser.add_argument('-v', '--verbose', default=False, action='store_true',
                          help='Do not redirect stdout and stderr')
      parser.add_argument('-q', '--quiet', default=False, action='store_true',
                          help='Produce less output to the terminal.')
      parser.add_argument('-t', '--timeout-multiplier', type=float, default=None,
                          help='Define a multiplier for test timeout, for example '
                          ' when running tests in particular conditions they might take'
                          ' more time to execute.')
      parser.add_argument('--setup', default=None, dest='setup',
                          help='Which test setup to use.')
      parser.add_argument('--test-args', default=[], type=split_args,
                          help='Arguments to pass to the specified test(s) or all tests')
      parser.add_argument('args', nargs='*',
                          help='Optional list of tests to run')
  
  
  def returncode_to_status(retcode: int) -> str:
      # Note: We can't use `os.WIFSIGNALED(result.returncode)` and the related
      # functions here because the status returned by subprocess is munged. It
      # returns a negative value if the process was killed by a signal rather than
      # the raw status returned by `wait()`. Also, If a shell sits between Meson
      # the the actual unit test that shell is likely to convert a termination due
      # to a signal into an exit status of 128 plus the signal number.
      if retcode < 0:
          signum = -retcode
          try:
              signame = signal.Signals(signum).name
          except ValueError:
              signame = 'SIGinvalid'
          return '(killed by signal {} {})'.format(signum, signame)
  
      if retcode <= 128:
          return '(exit status {})'.format(retcode)
  
      signum = retcode - 128
      try:
          signame = signal.Signals(signum).name
      except ValueError:
          signame = 'SIGinvalid'
      return '(exit status {} or signal {} {})'.format(retcode, signum, signame)
  
  def env_tuple_to_str(env: T.Iterable[T.Tuple[str, str]]) -> str:
      return ''.join(["{}='{}' ".format(k, v) for k, v in env])
  
  
  class TestException(MesonException):
      pass
  
  
  @enum.unique
  class TestResult(enum.Enum):
  
      OK = 'OK'
      TIMEOUT = 'TIMEOUT'
      SKIP = 'SKIP'
      FAIL = 'FAIL'
      EXPECTEDFAIL = 'EXPECTEDFAIL'
      UNEXPECTEDPASS = 'UNEXPECTEDPASS'
      ERROR = 'ERROR'
  
      @staticmethod
      def maxlen() -> int:
          return 14 # len(UNEXPECTEDPASS)
  
  
  class TAPParser:
      Plan = namedtuple('Plan', ['count', 'late', 'skipped', 'explanation'])
      Bailout = namedtuple('Bailout', ['message'])
      Test = namedtuple('Test', ['number', 'name', 'result', 'explanation'])
      Error = namedtuple('Error', ['message'])
      Version = namedtuple('Version', ['version'])
  
      _MAIN = 1
      _AFTER_TEST = 2
      _YAML = 3
  
      _RE_BAILOUT = re.compile(r'Bail out!\s*(.*)')
      _RE_DIRECTIVE = re.compile(r'(?:\s*\#\s*([Ss][Kk][Ii][Pp]\S*|[Tt][Oo][Dd][Oo])\b\s*(.*))?')
      _RE_PLAN = re.compile(r'1\.\.([0-9]+)' + _RE_DIRECTIVE.pattern)
      _RE_TEST = re.compile(r'((?:not )?ok)\s*(?:([0-9]+)\s*)?([^#]*)' + _RE_DIRECTIVE.pattern)
      _RE_VERSION = re.compile(r'TAP version ([0-9]+)')
      _RE_YAML_START = re.compile(r'(\s+)---.*')
      _RE_YAML_END = re.compile(r'\s+\.\.\.\s*')
  
  
  
  [... the rest is omitted ...]
files: mesonbuild/mtest.py
files: # Copyright 2016-2017 The Meson development team
  
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  # A tool to run tests in many different ways.
  
  from pathlib import Path
  from collections import namedtuple
  from copy import deepcopy
  import argparse
  import concurrent.futures as conc
  import datetime
  import enum
  import io
  import json
  import multiprocessing
  import os
  import pickle
  import platform
  import random
  import re
  import signal
  import subprocess
  import sys
  import tempfile
  import textwrap
  import time
  import typing as T
  import xml.etree.ElementTree as et
  
  from . import build
  from . import environment
  from . import mlog
  from .dependencies import ExternalProgram
  from .mesonlib import MesonException, get_wine_shortpath, split_args
  
  if T.TYPE_CHECKING:
      from .backend.backends import TestSerialisation
  
  # GNU autotools interprets a return code of 77 from tests it executes to
  # mean that the test should be skipped.
  GNU_SKIP_RETURNCODE = 77
  
  # GNU autotools interprets a return code of 99 from tests it executes to
  # mean that the test failed even before testing what it is supposed to test.
  GNU_ERROR_RETURNCODE = 99
  
  def is_windows() -> bool:
      platname = platform.system().lower()
      return platname == 'windows' or 'mingw' in platname
  
  def is_cygwin() -> bool:
      platname = platform.system().lower()
      return 'cygwin' in platname
  
  def determine_worker_count() -> int:
      varname = 'MESON_TESTTHREADS'
      if varname in os.environ:
          try:
              num_workers = int(os.environ[varname])
          except ValueError:
              print('Invalid value in {}, using 1 thread.'.format(varname))
              num_workers = 1
      else:
          try:
              # Fails in some weird environments such as Debian
              # reproducible build.
              num_workers = multiprocessing.cpu_count()
          except Exception:
              num_workers = 1
      return num_workers
  
  def add_arguments(parser: argparse.ArgumentParser) -> None:
      parser.add_argument('--repeat', default=1, dest='repeat', type=int,
                          help='Number of times to run the tests.')
      parser.add_argument('--no-rebuild', default=False, action='store_true',
                          help='Do not rebuild before running tests.')
      parser.add_argument('--gdb', default=False, dest='gdb', action='store_true',
                          help='Run test under gdb.')
      parser.add_argument('--gdb-path', default='gdb', dest='gdb_path',
                          help='Path to the gdb binary (default: gdb).')
      parser.add_argument('--list', default=False, dest='list', action='store_true',
                          help='List available tests.')
      parser.add_argument('--wrapper', default=None, dest='wrapper', type=split_args,
                          help='wrapper to run tests with (e.g. Valgrind)')
      parser.add_argument('-C', default='.', dest='wd', type=os.path.abspath,
                          help='directory to cd into before running')
      parser.add_argument('--suite', default=[], dest='include_suites', action='append', metavar='SUITE',
                          help='Only run tests belonging to the given suite.')
      parser.add_argument('--no-suite', default=[], dest='exclude_suites', action='append', metavar='SUITE',
                          help='Do not run tests belonging to the given suite.')
      parser.add_argument('--no-stdsplit', default=True, dest='split', action='store_false',
                          help='Do not split stderr and stdout in test logs.')
      parser.add_argument('--print-errorlogs', default=False, action='store_true',
                          help="Whether to print failing tests' logs.")
      parser.add_argument('--benchmark', default=False, action='store_true',
                          help="Run benchmarks instead of tests.")
      parser.add_argument('--logbase', default='testlog',
                          help="Base name for log file.")
      parser.add_argument('--num-processes', default=determine_worker_count(), type=int,
                          help='How many parallel processes to use.')
      parser.add_argument('-v', '--verbose', default=False, action='store_true',
                          help='Do not redirect stdout and stderr')
      parser.add_argument('-q', '--quiet', default=False, action='store_true',
                          help='Produce less output to the terminal.')
      parser.add_argument('-t', '--timeout-multiplier', type=float, default=None,
                          help='Define a multiplier for test timeout, for example '
                          ' when running tests in particular conditions they might take'
                          ' more time to execute.')
      parser.add_argument('--setup', default=None, dest='setup',
                          help='Which test setup to use.')
      parser.add_argument('--test-args', default=[], type=split_args,
                          help='Arguments to pass to the specified test(s) or all tests')
      parser.add_argument('args', nargs='*',
                          help='Optional list of tests to run')
  
  
  def returncode_to_status(retcode: int) -> str:
      # Note: We can't use `os.WIFSIGNALED(result.returncode)` and the related
      # functions here because the status returned by subprocess is munged. It
      # returns a negative value if the process was killed by a signal rather than
      # the raw status returned by `wait()`. Also, If a shell sits between Meson
      # the the actual unit test that shell is likely to convert a termination due
      # to a signal into an exit status of 128 plus the signal number.
      if retcode < 0:
          signum = -retcode
          try:
              signame = signal.Signals(signum).name
          except ValueError:
              signame = 'SIGinvalid'
          return '(killed by signal {} {})'.format(signum, signame)
  
      if retcode <= 128:
          return '(exit status {})'.format(retcode)
  
      signum = retcode - 128
      try:
          signame = signal.Signals(signum).name
      except ValueError:
          signame = 'SIGinvalid'
      return '(exit status {} or signal {} {})'.format(retcode, signum, signame)
  
  def env_tuple_to_str(env: T.Iterable[T.Tuple[str, str]]) -> str:
      return ''.join(["{}='{}' ".format(k, v) for k, v in env])
  
  
  class TestException(MesonException):
      pass
  
  
  @enum.unique
  class TestResult(enum.Enum):
  
      OK = 'OK'
      TIMEOUT = 'TIMEOUT'
      SKIP = 'SKIP'
      FAIL = 'FAIL'
      EXPECTEDFAIL = 'EXPECTEDFAIL'
      UNEXPECTEDPASS = 'UNEXPECTEDPASS'
      ERROR = 'ERROR'
  
      @staticmethod
      def maxlen() -> int:
          return 14 # len(UNEXPECTEDPASS)
  
  
  class TAPParser:
      Plan = namedtuple('Plan', ['count', 'late', 'skipped', 'explanation'])
      Bailout = namedtuple('Bailout', ['message'])
      Test = namedtuple('Test', ['number', 'name', 'result', 'explanation'])
      Error = namedtuple('Error', ['message'])
      Version = namedtuple('Version', ['version'])
  
      _MAIN = 1
      _AFTER_TEST = 2
      _YAML = 3
  
      _RE_BAILOUT = re.compile(r'Bail out!\s*(.*)')
      _RE_DIRECTIVE = re.compile(r'(?:\s*\#\s*([Ss][Kk][Ii][Pp]\S*|[Tt][Oo][Dd][Oo])\b\s*(.*))?')
      _RE_PLAN = re.compile(r'1\.\.([0-9]+)' + _RE_DIRECTIVE.pattern)
      _RE_TEST = re.compile(r'((?:not )?ok)\s*(?:([0-9]+)\s*)?([^#]*)' + _RE_DIRECTIVE.pattern)
      _RE_VERSION = re.compile(r'TAP version ([0-9]+)')
      _RE_YAML_START = re.compile(r'(\s+)---.*')
      _RE_YAML_END = re.compile(r'\s+\.\.\.\s*')
  
      def __init__(self, io:
  
  [... the rest is omitted ...]
files: mesonbuild/mtest.py