files: import os, subprocess
  import argparse
  import asyncio
  import threading
  import copy
  import shutil
  from concurrent.futures.thread import ThreadPoolExecutor
  from pathlib import Path
  import typing as T
  
  from . import mlog
  from .mesonlib import quiet_git, GitException, Popen_safe, MesonException, windows_proof_rmtree
  from .wrap.wrap import PackageDefinition, Resolver, WrapException, ALL_TYPES
  from .wrap import wraptool
  
  ALL_TYPES_STRING = ', '.join(ALL_TYPES)
  
  class Logger:
      def __init__(self, total_tasks: int) -> None:
          self.lock = threading.Lock()
          self.total_tasks = total_tasks
          self.completed_tasks = 0
          self.running_tasks = set()
          self.should_erase_line = ''
  
      def flush(self) -> None:
          if self.should_erase_line:
              print(self.should_erase_line, end='\r')
              self.should_erase_line = ''
  
      def print_progress(self) -> None:
          line = f'Progress: {self.completed_tasks} / {self.total_tasks}'
          max_len = shutil.get_terminal_size().columns - len(line)
          running = ', '.join(self.running_tasks)
          if len(running) + 3 > max_len:
              running = running[:max_len - 6] + '...'
          line = line + f' ({running})'
          print(self.should_erase_line, line, sep='', end='\r')
          self.should_erase_line = '\x1b[K'
  
      def start(self, wrap_name: str) -> None:
          with self.lock:
              self.running_tasks.add(wrap_name)
              self.print_progress()
  
      def done(self, wrap_name: str, log_queue: T.List[T.Tuple[mlog.TV_LoggableList, T.Any]]) -> None:
          with self.lock:
              self.flush()
              for args, kwargs in log_queue:
                  mlog.log(*args, **kwargs)
              self.running_tasks.remove(wrap_name)
              self.completed_tasks += 1
              self.print_progress()
  
  
  class Runner:
      def __init__(self, logger: Logger, r: Resolver, wrap: PackageDefinition, repo_dir: str, options: argparse.Namespace) -> None:
          # FIXME: Do a copy because Resolver.resolve() is stateful method that
          # cannot be called from multiple threads.
          self.wrap_resolver = copy.copy(r)
          self.wrap = wrap
          self.repo_dir = repo_dir
          self.options = options
          self.run_method = options.subprojects_func.__get__(self)
          self.log_queue = []
          self.logger = logger
  
      def log(self, *args, **kwargs):
          self.log_queue.append((args, kwargs))
  
      def run(self):
          self.logger.start(self.wrap.name)
          result = self.run_method()
          self.logger.done(self.wrap.name, self.log_queue)
          return result
  
      def update_wrapdb_file(self):
          try:
              patch_url = self.wrap.get('patch_url')
              branch, revision = wraptool.parse_patch_url(patch_url)
          except WrapException:
              return
          new_branch, new_revision = wraptool.get_latest_version(self.wrap.name)
          if new_branch != branch or new_revision != revision:
              wraptool.update_wrap_file(self.wrap.filename, self.wrap.name, new_branch, new_revision)
              self.log('  -> New wrap file downloaded.')
  
      def update_file(self):
          self.update_wrapdb_file()
          if not os.path.isdir(self.repo_dir):
              # The subproject is not needed, or it is a tarball extracted in
              # 'libfoo-1.0' directory and the version has been bumped and the new
              # directory is 'libfoo-2.0'. In that case forcing a meson
              # reconfigure will download and use the new tarball.
              self.log('  -> Not used.')
              return True
          elif self.options.reset:
              # Delete existing directory and redownload. It is possible that nothing
              # changed but we have no way to know. Hopefully tarballs are still
              # cached.
              windows_proof_rmtree(self.repo_dir)
              try:
                  self.wrap_resolver.resolve(self.wrap.name, 'meson')
                  self.log('  -> New version extracted')
                  return True
              except WrapException as e:
                  self.log('  ->', mlog.red(str(e)))
                  return False
          else:
              # The subproject has not changed, or the new source and/or patch
              # tarballs should be extracted in the same directory than previous
              # version.
              self.log('  -> Subproject has not changed, or the new source/patch needs to be extracted on the same location.')
              self.log('     Pass --reset option to delete directory and redownload.')
              return False
  
      def git_output(self, cmd):
          return quiet_git(cmd, self.repo_dir, check=True)[1]
  
      def git_verbose(self, cmd):
          self.log(self.git_output(cmd))
  
      def git_stash(self):
          # That git command return 1 (failure) when there is something to stash.
          # We don't want to stash when there is nothing to stash because that would
          # print spurious "No local changes to save".
          if not quiet_git(['diff', '--quiet', 'HEAD'], self.repo_dir)[0]:
              # Don't pipe stdout here because we want the user to see their changes have
              # been saved.
              self.git_verbose(['stash'])
  
      def git_show(self):
          commit_message = self.git_output(['show', '--quiet', '--pretty=format:%h%n%d%n%s%n[%an]'])
          parts = [s.strip() for s in commit_message.split('\n')]
          self.log('
  
  [... the rest is omitted ...]
files: mesonbuild/msubprojects.py
files: import os, subprocess
  import argparse
  import asyncio
  import threading
  import copy
  import shutil
  from concurrent.futures.thread import ThreadPoolExecutor
  from pathlib import Path
  import typing as T
  
  from . import mlog
  from .mesonlib import quiet_git, GitException, Popen_safe, MesonException, windows_proof_rmtree
  from .wrap.wrap import PackageDefinition, Resolver, WrapException, ALL_TYPES
  from .wrap import wraptool
  
  ALL_TYPES_STRING = ', '.join(ALL_TYPES)
  
  class Logger:
      def __init__(self, total_tasks: int) -> None:
          self.lock = threading.Lock()
          self.total_tasks = total_tasks
          self.completed_tasks = 0
          self.running_tasks = set()
          self.should_erase_line = ''
  
      def flush(self) -> None:
          if self.should_erase_line:
              print(self.should_erase_line, end='\r')
              self.should_erase_line = ''
  
      def print_progress(self) -> None:
          line = f'Progress: {self.completed_tasks} / {self.total_tasks}'
          max_len = shutil.get_terminal_size().columns - len(line)
          running = ', '.join(self.running_tasks)
          if len(running) + 3 > max_len:
              running = running[:max_len - 6] + '...'
          line = line + f' ({running})'
          print(self.should_erase_line, line, sep='', end='\r')
          self.should_erase_line = '\x1b[K'
  
      def start(self, wrap_name: str) -> None:
          with self.lock:
              self.running_tasks.add(wrap_name)
              self.print_progress()
  
      def done(self, wrap_name: str, log_queue: T.List[T.Tuple[mlog.TV_LoggableList, T.Any]]) -> None:
          with self.lock:
              self.flush()
              for args, kwargs in log_queue:
                  mlog.log(*args, **kwargs)
              self.running_tasks.remove(wrap_name)
              self.completed_tasks += 1
              self.print_progress()
  
  
  class Runner:
      def __init__(self, logger: Logger, r: Resolver, wrap: PackageDefinition, repo_dir: str, options: argparse.Namespace) -> None:
          # FIXME: Do a copy because Resolver.resolve() is stateful method that
          # cannot be called from multiple threads.
          self.wrap_resolver = copy.copy(r)
          self.wrap = wrap
          self.repo_dir = repo_dir
          self.options = options
          self.run_method = options.subprojects_func.__get__(self)
          self.log_queue = []
          self.logger = logger
  
      def log(self, *args, **kwargs):
          self.log_queue.append((args, kwargs))
  
      def run(self):
          self.logger.start(self.wrap.name)
          result = self.run_method()
          self.logger.done(self.wrap.name, self.log_queue)
          return result
  
      def update_wrapdb_file(self):
          try:
              patch_url = self.wrap.get('patch_url')
              branch, revision = wraptool.parse_patch_url(patch_url)
          except WrapException:
              return
          new_branch, new_revision = wraptool.get_latest_version(self.wrap.name)
          if new_branch != branch or new_revision != revision:
              wraptool.update_wrap_file(self.wrap.filename, self.wrap.name, new_branch, new_revision)
              self.log('  -> New wrap file downloaded.')
  
      def update_file(self):
          self.update_wrapdb_file()
          if not os.path.isdir(self.repo_dir):
              # The subproject is not needed, or it is a tarball extracted in
              # 'libfoo-1.0' directory and the version has been bumped and the new
              # directory is 'libfoo-2.0'. In that case forcing a meson
              # reconfigure will download and use the new tarball.
              self.log('  -> Not used.')
              return True
          elif self.options.reset:
              # Delete existing directory and redownload. It is possible that nothing
              # changed but we have no way to know. Hopefully tarballs are still
              # cached.
              windows_proof_rmtree(self.repo_dir)
              try:
                  self.wrap_resolver.resolve(self.wrap.name, 'meson')
                  self.log('  -> New version extracted')
                  return True
              except WrapException as e:
                  self.log('  ->', mlog.red(str(e)))
                  return False
          else:
              # The subproject has not changed, or the new source and/or patch
              # tarballs should be extracted in the same directory than previous
              # version.
              self.log('  -> Subproject has not changed, or the new source/patch needs to be extracted on the same location.')
              self.log('     Pass --reset option to delete directory and redownload.')
              return False
  
      def git_output(self, cmd):
          return quiet_git(cmd, self.repo_dir, check=True)[1]
  
      def git_verbose(self, cmd):
          self.log(self.git_output(cmd))
  
      def git_stash(self):
          # That git command return 1 (failure) when there is something to stash.
          # We don't want to stash when there is nothing to stash because that would
          # print spurious "No local changes to save".
          if not quiet_git(['diff', '--quiet', 'HEAD'], self.repo_dir)[0]:
              # Don't pipe stdout here because we want the user to see their changes have
              # been saved.
              self.git_verbose(['stash'])
  
      def git_show(self):
          commit_message = self.git_output(['show', '--quiet', '--pretty=format:%h%n%d%n%s%n[%an]'])
          parts = [s.strip() for s in commit_message.split('\n')]
          self.log('
  
  [... the rest is omitted ...]
files: mesonbuild/msubprojects.py
files: # Copyright 2015 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 mlog
  import contextlib
  import urllib.request
  import urllib.error
  import urllib.parse
  import os
  import hashlib
  import shutil
  import tempfile
  import stat
  import subprocess
  import sys
  import configparser
  import typing as T
  import textwrap
  
  from pathlib import Path
  from . import WrapMode
  from .. import coredata
  from ..mesonlib import quiet_git, GIT, ProgressBar, MesonException
  from  .. import mesonlib
  
  if T.TYPE_CHECKING:
      import http.client
  
  try:
      # Importing is just done to check if SSL exists, so all warnings
      # regarding 'imported but unused' can be safely ignored
      import ssl  # noqa
      has_ssl = True
  except ImportError:
      has_ssl = False
  
  REQ_TIMEOUT = 600.0
  SSL_WARNING_PRINTED = False
  WHITELIST_SUBDOMAIN = 'wrapdb.mesonbuild.com'
  
  ALL_TYPES = ['file', 'git', 'hg', 'svn']
  
  def whitelist_wrapdb(urlstr: str) -> urllib.parse.ParseResult:
      """ raises WrapException if not whitelisted subdomain """
      url = urllib.parse.urlparse(urlstr)
      if not url.hostname:
          raise WrapException(f'{urlstr} is not a valid URL')
      if not url.hostname.endswith(WHITELIST_SUBDOMAIN):
          raise WrapException(f'{urlstr} is not a whitelisted WrapDB URL')
      if has_ssl and not url.scheme == 'https':
          raise WrapException(f'WrapDB did not have expected SSL https url, instead got {urlstr}')
      return url
  
  def open_wrapdburl(urlstring: str) -> 'http.client.HTTPResponse':
      global SSL_WARNING_PRINTED
  
      url = whitelist_wrapdb(urlstring)
      if has_ssl:
          try:
              return T.cast('http.client.HTTPResponse', urllib.request.urlopen(urllib.parse.urlunparse(url), timeout=REQ_TIMEOUT))
          except urllib.error.URLError as excp:
              raise WrapException(f'WrapDB connection failed to {urlstring} with error {excp}')
  
      # following code is only for those without Python SSL
      nossl_url = url._replace(scheme='http')
      if not SSL_WARNING_PRINTED:
          mlog.warning(f'SSL module not available in {sys.executable}: WrapDB traffic not authenticated.')
          SSL_WARNING_PRINTED = True
      try:
          return T.cast('http.client.HTTPResponse', urllib.request.urlopen(urllib.parse.urlunparse(nossl_url), timeout=REQ_TIMEOUT))
      except urllib.error.URLError as excp:
          raise WrapException(f'WrapDB connection failed to {urlstring} with error {excp}')
  
  
  class WrapException(MesonException):
      pass
  
  class WrapNotFoundException(WrapException):
      pass
  
  class PackageDefinition:
      def __init__(self, fname: str):
          self.filename = fname
          self.type = None  # type: T.Optional[str]
          self.values = {} # type: T.Dict[str, str]
          self.provided_deps = {} # type: T.Dict[str, T.Optional[str]]
          self.provided_programs = [] # type: T.List[str]
          self.basename = os.path.basename(fname)
          self.has_wrap = self.basename.endswith('.wrap')
          self.name = self.basename[:-5] if self.has_wrap else self.basename
          self.directory = self.name
          self.provided_deps[self.name] = None
          self.original_filename = fname
          self.redirected = False
          if self.has_wrap:
              self.parse_wrap()
          self.directory = self.values.get('directory', self.name)
          if os.path.dirname(self.directory):
              raise WrapException('Directory key must be a name and not a path')
          if self.type and self.type not in ALL_TYPES:
              raise WrapException(f'Unknown wrap type {self.type!r}')
          self.filesdir = os.path.join(os.path.dirname(self.filename), 'packagefiles')
          # What the original file name was before redirection
  
      def parse_wrap(self) -> None:
          try:
              config = configparser.ConfigParser(interpolation=None)
              config.read(self.filename)
          except configparser.Error as e:
              raise WrapException('Failed to parse {}: {}'.format(self.basename, str(e)))
          self.parse_wrap_section(config)
          if self.type == 'redirect':
              # [wrap-redirect] have a `filename` value pointing to the real wrap
              # file we should parse instead. It must be relative to the current
              # wrap file location and must be in the form foo/subprojects/bar.wrap.
              dirname = Path(self.filename).parent
              fname = Path(self.values['filename'])
              for i, p in enumerate(fname.parts):
                  if i % 2 == 0:
                      if p == '..':
                          raise WrapException('wrap-redirect filename cannot contain ".."')
                  else:
                      if p != 'subprojects':
                          raise WrapException('wrap-redirect filename must be in the form foo/subprojects/bar.wrap')
              if fname.suffix != '.wrap':
                  raise WrapException
  
  [... the rest is omitted ...]
files: mesonbuild/wrap/wrap.py
files: # Copyright 2015 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 mlog
  import contextlib
  import urllib.request
  import urllib.error
  import urllib.parse
  import os
  import hashlib
  import shutil
  import tempfile
  import stat
  import subprocess
  import sys
  import configparser
  import typing as T
  import textwrap
  
  from pathlib import Path
  from . import WrapMode
  from .. import coredata
  from ..mesonlib import quiet_git, GIT, ProgressBar, MesonException
  from  .. import mesonlib
  
  if T.TYPE_CHECKING:
      import http.client
  
  try:
      # Importing is just done to check if SSL exists, so all warnings
      # regarding 'imported but unused' can be safely ignored
      import ssl  # noqa
      has_ssl = True
  except ImportError:
      has_ssl = False
  
  REQ_TIMEOUT = 600.0
  SSL_WARNING_PRINTED = False
  WHITELIST_SUBDOMAIN = 'wrapdb.mesonbuild.com'
  
  ALL_TYPES = ['file', 'git', 'hg', 'svn']
  
  def whitelist_wrapdb(urlstr: str) -> urllib.parse.ParseResult:
      """ raises WrapException if not whitelisted subdomain """
      url = urllib.parse.urlparse(urlstr)
      if not url.hostname:
          raise WrapException(f'{urlstr} is not a valid URL')
      if not url.hostname.endswith(WHITELIST_SUBDOMAIN):
          raise WrapException(f'{urlstr} is not a whitelisted WrapDB URL')
      if has_ssl and not url.scheme == 'https':
          raise WrapException(f'WrapDB did not have expected SSL https url, instead got {urlstr}')
      return url
  
  def open_wrapdburl(urlstring: str) -> 'http.client.HTTPResponse':
      global SSL_WARNING_PRINTED
  
      url = whitelist_wrapdb(urlstring)
      if has_ssl:
          try:
              return T.cast('http.client.HTTPResponse', urllib.request.urlopen(urllib.parse.urlunparse(url), timeout=REQ_TIMEOUT))
          except urllib.error.URLError as excp:
              raise WrapException(f'WrapDB connection failed to {urlstring} with error {excp}')
  
      # following code is only for those without Python SSL
      nossl_url = url._replace(scheme='http')
      if not SSL_WARNING_PRINTED:
          mlog.warning(f'SSL module not available in {sys.executable}: WrapDB traffic not authenticated.')
          SSL_WARNING_PRINTED = True
      try:
          return T.cast('http.client.HTTPResponse', urllib.request.urlopen(urllib.parse.urlunparse(nossl_url), timeout=REQ_TIMEOUT))
      except urllib.error.URLError as excp:
          raise WrapException(f'WrapDB connection failed to {urlstring} with error {excp}')
  
  
  class WrapException(MesonException):
      pass
  
  class WrapNotFoundException(WrapException):
      pass
  
  class PackageDefinition:
      def __init__(self, fname: str):
          self.filename = fname
          self.type = None  # type: T.Optional[str]
          self.values = {} # type: T.Dict[str, str]
          self.provided_deps = {} # type: T.Dict[str, T.Optional[str]]
          self.provided_programs = [] # type: T.List[str]
          self.basename = os.path.basename(fname)
          self.has_wrap = self.basename.endswith('.wrap')
          self.name = self.basename[:-5] if self.has_wrap else self.basename
          self.directory = self.name
          self.provided_deps[self.name] = None
          if self.has_wrap:
              self.parse_wrap()
          self.directory = self.values.get('directory', self.name)
          if os.path.dirname(self.directory):
              raise WrapException('Directory key must be a name and not a path')
          if self.type and self.type not in ALL_TYPES:
              raise WrapException(f'Unknown wrap type {self.type!r}')
          self.filesdir = os.path.join(os.path.dirname(self.filename), 'packagefiles')
  
      def parse_wrap(self) -> None:
          try:
              config = configparser.ConfigParser(interpolation=None)
              config.read(self.filename)
          except configparser.Error as e:
              raise WrapException('Failed to parse {}: {}'.format(self.basename, str(e)))
          self.parse_wrap_section(config)
          if self.type == 'redirect':
              # [wrap-redirect] have a `filename` value pointing to the real wrap
              # file we should parse instead. It must be relative to the current
              # wrap file location and must be in the form foo/subprojects/bar.wrap.
              dirname = Path(self.filename).parent
              fname = Path(self.values['filename'])
              for i, p in enumerate(fname.parts):
                  if i % 2 == 0:
                      if p == '..':
                          raise WrapException('wrap-redirect filename cannot contain ".."')
                  else:
                      if p != 'subprojects':
                          raise WrapException('wrap-redirect filename must be in the form foo/subprojects/bar.wrap')
              if fname.suffix != '.wrap':
                  raise WrapException('wrap-redirect filename must be a .wrap file')
              fname = dirname / fname
              if not fname.is_file
  
  [... the rest is omitted ...]
files: mesonbuild/wrap/wrap.py
files: #!/usr/bin/env python3
  # Copyright 2016-2021 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.
  
  # Work around some pathlib bugs...
  from mesonbuild import _pathlib
  import sys
  sys.modules['pathlib'] = _pathlib
  
  import time
  import stat
  import subprocess
  import re
  import json
  import tempfile
  import textwrap
  import os
  import shutil
  import unittest
  import platform
  import pickle
  import functools
  import io
  import operator
  import threading
  import zipfile, tarfile
  import hashlib
  from itertools import chain
  from unittest import mock
  from configparser import ConfigParser
  from contextlib import contextmanager
  from glob import glob
  from pathlib import (PurePath, Path)
  from distutils.dir_util import copy_tree
  import typing as T
  
  import mesonbuild.mlog
  import mesonbuild.depfile
  import mesonbuild.dependencies.base
  import mesonbuild.dependencies.factory
  import mesonbuild.compilers
  import mesonbuild.envconfig
  import mesonbuild.environment
  import mesonbuild.mesonlib
  import mesonbuild.coredata
  import mesonbuild.modules.gnome
  from mesonbuild.interpreter import Interpreter
  from mesonbuild.interpreterbase import typed_pos_args, InvalidArguments, ObjectHolder
  from mesonbuild.interpreterbase import typed_pos_args, InvalidArguments, typed_kwargs, ContainerTypeInfo, KwargInfo
  from mesonbuild.ast import AstInterpreter
  from mesonbuild.mesonlib import (
      BuildDirLock, LibType, MachineChoice, PerMachine, Version, is_windows,
      is_osx, is_cygwin, is_dragonflybsd, is_openbsd, is_haiku, is_sunos,
      windows_proof_rmtree, windows_proof_rm, python_command,
      version_compare, split_args, quote_arg, relpath, is_linux, git,
      search_version
  )
  from mesonbuild.compilers import (
      detect_static_linker, detect_c_compiler, detect_cpp_compiler,
      detect_objc_compiler, detect_objcpp_compiler, detect_d_compiler,
      detect_swift_compiler, compiler_from_language
  )
  from mesonbuild.environment import detect_ninja
  from mesonbuild.mesonlib import MesonException, EnvironmentException, OptionKey
  from mesonbuild.dependencies import PkgConfigDependency
  from mesonbuild.programs import ExternalProgram
  import mesonbuild.dependencies.base
  from mesonbuild.build import Target, ConfigurationData
  import mesonbuild.modules.pkgconfig
  from mesonbuild.scripts import destdir_join
  
  from mesonbuild.mtest import TAPParser, TestResult
  from mesonbuild.mesonmain import setup_vsenv
  from mesonbuild.wrap.wrap import PackageDefinition, WrapException
  
  from run_tests import (
      Backend, FakeBuild, FakeCompilerOptions,
      ensure_backend_detects_changes, exe_suffix, get_backend_commands,
      get_builddir_target_args, get_fake_env, get_fake_options, get_meson_script,
      run_configure_inprocess, run_mtest_inprocess
  )
  
  if T.TYPE_CHECKING:
      from mesonbuild.compilers import Compiler
  
  URLOPEN_TIMEOUT = 5
  
  @contextmanager
  def chdir(path: str):
      curdir = os.getcwd()
      os.chdir(path)
      try:
          yield
      finally:
          os.chdir(curdir)
  
  
  def get_dynamic_section_entry(fname: str, entry: str) -> T.Optional[str]:
      if is_cygwin() or is_osx():
          raise unittest.SkipTest('Test only applicable to ELF platforms')
  
      try:
          raw_out = subprocess.check_output(['readelf', '-d', fname],
                                            universal_newlines=True)
      except FileNotFoundError:
          # FIXME: Try using depfixer.py:Elf() as a fallback
          raise unittest.SkipTest('readelf not found')
      pattern = re.compile(entry + r': \[(.*?)\]')
      for line in raw_out.split('\n'):
          m = pattern.search(line)
          if m is not None:
              return str(m.group(1))
      return None # The file did not contain the specified entry.
  
  def get_soname(fname: str) -> T.Optional[str]:
      return get_dynamic_section_entry(fname, 'soname')
  
  def get_rpath(fname: str) -> T.Optional[str]:
      raw = get_dynamic_section_entry(fname, r'(?:rpath|runpath)')
      # Get both '' and None here
      if not raw:
          return None
      # nix/nixos adds a bunch of stuff to the rpath out of necessity that we
      # don't check for, so clear those
      final = ':'.join([e for e in raw.split(':') if not e.startswith('/nix')])
      return final
  
  def is_tarball():
      if not os.path.isdir('docs'):
          return True
      return False
  
  def is_ci():
      if 'CI' in os.environ:
          return True
      return False
  
  def _git_init(project_dir):
      # If a user has git configuration init.defaultBranch set we want to override that
      with tempfile.TemporaryDirectory() as d:
          out = git(['--version'], str(d))[1]
      if version_compare(search_version(out), '>= 2.28'):
          extra_cmd = ['--initial-branch', '
  
  [... the rest is omitted ...]
files: run_unittests.py
files: #!/usr/bin/env python3
  # Copyright 2016-2021 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.
  
  # Work around some pathlib bugs...
  from mesonbuild import _pathlib
  import sys
  sys.modules['pathlib'] = _pathlib
  
  import time
  import stat
  import subprocess
  import re
  import json
  import tempfile
  import textwrap
  import os
  import shutil
  import unittest
  import platform
  import pickle
  import functools
  import io
  import operator
  import threading
  import zipfile, tarfile
  import hashlib
  from itertools import chain
  from unittest import mock
  from configparser import ConfigParser
  from contextlib import contextmanager
  from glob import glob
  from pathlib import (PurePath, Path)
  from distutils.dir_util import copy_tree
  import typing as T
  
  import mesonbuild.mlog
  import mesonbuild.depfile
  import mesonbuild.dependencies.base
  import mesonbuild.dependencies.factory
  import mesonbuild.compilers
  import mesonbuild.envconfig
  import mesonbuild.environment
  import mesonbuild.mesonlib
  import mesonbuild.coredata
  import mesonbuild.modules.gnome
  from mesonbuild.interpreter import Interpreter
  from mesonbuild.interpreterbase import typed_pos_args, InvalidArguments, ObjectHolder
  from mesonbuild.interpreterbase import typed_pos_args, InvalidArguments, typed_kwargs, ContainerTypeInfo, KwargInfo
  from mesonbuild.ast import AstInterpreter
  from mesonbuild.mesonlib import (
      BuildDirLock, LibType, MachineChoice, PerMachine, Version, is_windows,
      is_osx, is_cygwin, is_dragonflybsd, is_openbsd, is_haiku, is_sunos,
      windows_proof_rmtree, windows_proof_rm, python_command,
      version_compare, split_args, quote_arg, relpath, is_linux, git,
      search_version
  )
  from mesonbuild.compilers import (
      detect_static_linker, detect_c_compiler, detect_cpp_compiler,
      detect_objc_compiler, detect_objcpp_compiler, detect_d_compiler,
      detect_swift_compiler, compiler_from_language
  )
  from mesonbuild.environment import detect_ninja
  from mesonbuild.mesonlib import MesonException, EnvironmentException, OptionKey
  from mesonbuild.dependencies import PkgConfigDependency
  from mesonbuild.programs import ExternalProgram
  import mesonbuild.dependencies.base
  from mesonbuild.build import Target, ConfigurationData
  import mesonbuild.modules.pkgconfig
  from mesonbuild.scripts import destdir_join
  
  from mesonbuild.mtest import TAPParser, TestResult
  from mesonbuild.mesonmain import setup_vsenv
  from mesonbuild.wrap.wrap import PackageDefinition, WrapException
  
  from run_tests import (
      Backend, FakeBuild, FakeCompilerOptions,
      ensure_backend_detects_changes, exe_suffix, get_backend_commands,
      get_builddir_target_args, get_fake_env, get_fake_options, get_meson_script,
      run_configure_inprocess, run_mtest_inprocess
  )
  
  if T.TYPE_CHECKING:
      from mesonbuild.compilers import Compiler
  
  URLOPEN_TIMEOUT = 5
  
  @contextmanager
  def chdir(path: str):
      curdir = os.getcwd()
      os.chdir(path)
      try:
          yield
      finally:
          os.chdir(curdir)
  
  
  def get_dynamic_section_entry(fname: str, entry: str) -> T.Optional[str]:
      if is_cygwin() or is_osx():
          raise unittest.SkipTest('Test only applicable to ELF platforms')
  
      try:
          raw_out = subprocess.check_output(['readelf', '-d', fname],
                                            universal_newlines=True)
      except FileNotFoundError:
          # FIXME: Try using depfixer.py:Elf() as a fallback
          raise unittest.SkipTest('readelf not found')
      pattern = re.compile(entry + r': \[(.*?)\]')
      for line in raw_out.split('\n'):
          m = pattern.search(line)
          if m is not None:
              return str(m.group(1))
      return None # The file did not contain the specified entry.
  
  def get_soname(fname: str) -> T.Optional[str]:
      return get_dynamic_section_entry(fname, 'soname')
  
  def get_rpath(fname: str) -> T.Optional[str]:
      raw = get_dynamic_section_entry(fname, r'(?:rpath|runpath)')
      # Get both '' and None here
      if not raw:
          return None
      # nix/nixos adds a bunch of stuff to the rpath out of necessity that we
      # don't check for, so clear those
      final = ':'.join([e for e in raw.split(':') if not e.startswith('/nix')])
      return final
  
  def is_tarball():
      if not os.path.isdir('docs'):
          return True
      return False
  
  def is_ci():
      if 'CI' in os.environ:
          return True
      return False
  
  def _git_init(project_dir):
      # If a user has git configuration init.defaultBranch set we want to override that
      with tempfile.TemporaryDirectory() as d:
          out = git(['--version'], str(d))[1]
      if version_compare(search_version(out), '>= 2.28'):
          extra_cmd = ['--initial-branch', '
  
  [... the rest is omitted ...]
files: run_unittests.py