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.
  
  import configparser, os, platform, re, shlex, shutil, subprocess
  
  from . import coredata
  from .linkers import ArLinker, VisualStudioLinker
  from . import mesonlib
  from .mesonlib import EnvironmentException, Popen_safe
  from . import mlog
  
  from . import compilers
  from .compilers import (
      CLANG_OSX,
      CLANG_STANDARD,
      CLANG_WIN,
      GCC_CYGWIN,
      GCC_MINGW,
      GCC_OSX,
      GCC_STANDARD,
      ICC_STANDARD,
      is_assembly,
      is_header,
      is_library,
      is_llvm_ir,
      is_object,
      is_source,
  )
  from .compilers import (
      ArmCCompiler,
      ArmCPPCompiler,
      ArmclangCCompiler,
      ArmclangCPPCompiler,
      ClangCCompiler,
      ClangCPPCompiler,
      ClangObjCCompiler,
      ClangObjCPPCompiler,
      G95FortranCompiler,
      GnuCCompiler,
      GnuCPPCompiler,
      GnuFortranCompiler,
      GnuObjCCompiler,
      GnuObjCPPCompiler,
      ElbrusCCompiler,
      ElbrusCPPCompiler,
      ElbrusFortranCompiler,
      IntelCCompiler,
      IntelCPPCompiler,
      IntelFortranCompiler,
      JavaCompiler,
      MonoCompiler,
      VisualStudioCsCompiler,
      NAGFortranCompiler,
      Open64FortranCompiler,
      PathScaleFortranCompiler,
      PGIFortranCompiler,
      RustCompiler,
      SunFortranCompiler,
      ValaCompiler,
      VisualStudioCCompiler,
      VisualStudioCPPCompiler,
  )
  
  build_filename = 'meson.build'
  
  known_cpu_families = (
      'aarch64',
      'arm',
      'e2k',
      'ia64',
      'mips',
      'mips64',
      'parisc',
      'ppc',
      'ppc64',
      'riscv32',
      'riscv64',
      'sparc',
      'sparc64',
      'x86',
      'x86_64'
  )
  
  def detect_gcovr(version='3.1', log=False):
      gcovr_exe = 'gcovr'
      try:
          p, found = Popen_safe([gcovr_exe, '--version'])[0:2]
      except (FileNotFoundError, PermissionError):
          # Doesn't exist in PATH or isn't executable
          return None, None
      found = search_version(found)
      if p.returncode == 0:
          if log:
              mlog.log('Found gcovr-{} at {}'.format(found, shlex.quote(shutil.which(gcovr_exe))))
          return gcovr_exe, mesonlib.version_compare(found, '>=' + version)
      return None, None
  
  def find_coverage_tools():
      gcovr_exe, gcovr_new_rootdir = detect_gcovr()
  
      lcov_exe = 'lcov'
      genhtml_exe = 'genhtml'
  
      if not mesonlib.exe_exists([lcov_exe, '--version']):
          lcov_exe = None
      if not mesonlib.exe_exists([genhtml_exe, '--version']):
          genhtml_exe = None
  
      return gcovr_exe, gcovr_new_rootdir, lcov_exe, genhtml_exe
  
  def detect_ninja(version='1.5', log=False):
      for n in ['ninja', 'ninja-build']:
          try:
              p, found = Popen_safe([n, '--version'])[0:2]
          except (FileNotFoundError, PermissionError):
              # Doesn't exist in PATH or isn't executable
              continue
          found = found.strip()
          # Perhaps we should add a way for the caller to know the failure mode
          # (not found or too old)
          if p.returncode == 0 and mesonlib.version_compare(found, '>=' + version):
              if log:
                  mlog.log('Found ninja-{} at {}'.format(found, shlex.quote(shutil.which(n))))
              return n
  
  def detect_native_windows_arch():
      """
      The architecture of Windows itself: x86 or amd64
      """
      # These env variables are always available. See:
      # https://msdn.microsoft.com/en-us/library/aa384274(VS.85).aspx
      # https://blogs.msdn.microsoft.com/david.wang/2006/03/27/howto-detect-process-bitness/
      arch = os.environ.get('PROCESSOR_ARCHITEW6432', '').lower()
      if not arch:
          try:
              # If this doesn't exist, something is messing with the environment
              arch = os.environ['PROCESSOR_ARCHITECTURE'].lower()
          except KeyError:
              raise EnvironmentException('Unable to detect native OS architecture')
      return arch
  
  def detect_windows_arch(compilers):
      """
      Detecting the 'native' architecture of Windows is not a trivial task. We
      cannot trust that the architecture that Python is built for is the 'native'
      one because you can run 32-bit apps on 64-bit Windows using WOW64 and
      people sometimes install 32-bit Python on 64-bit Windows.
  
      We also can't rely on the architecture of the OS itself, since it's
      perfectly normal to compile and run 32-bit applications on Windows as if
      they were native applications. It's a terrible experience to require the
      user to supply a cross-info file to compile 32-bit applications on 64-bit
      Windows. Thankfully, the only way to compile things with Visual Studio on
      Windows is by entering the 'msvc toolchain' environment, which can be
      easily detected.
  
      In the end, the sanest method is as follows:
      1. Check if we're in an MSVC toolchain environment, and if so, return the
         MSVC toolchain architecture as our 'native' architecture.
      2. If not, check environment variables that are set by Windows and WOW64 to
         find out the architecture that Windows is built for, and use that as our
         'native' architecture.
      """
      os_arch = detect_native_windows_arch()
      if os_arch != 'amd64':
          return os_arch
      # If we're on 64-bit Windows, 32-bit apps can be compiled without
      # cross-compilation. So if we're doing that, just set the native arch as
      # 32-bit and pretend like we're running under WOW64. Else, return the
      # actual Windows architecture that we deduced above.
      for compiler in compilers.values():
          # Check if we're using and inside an MSVC toolchain environment
          if compiler.id == 'msvc' and 'VCINSTALLDIR' in os.environ:
              if float(compiler.get_toolset_version()) < 10.0:
                  # On MSVC 2008 and earlier, check 'BUILD_PLAT', where
                  # 'Win32' means 'x86'
                  platform = os.environ.get('BUILD_PLAT', 'x86')
                  if platform == 'Win32':
                      return 'x86'
              else:
                  # On MSVC 2010 and later 'Platform' is only set when the
                  # target arch is not 'x86'.  It's 'x64' when targeting
                  # x86_64 and 'arm' when targeting ARM.
                  platform = os.environ.get('Platform', 'x86').lower()
              if platform == 'x86':
                  return platform
          if compiler.id == 'gcc' and compiler.has_builtin_define('__i386__'):
              return 'x86'
      return os_arch
  
  def detect_cpu_family(compilers):
      """
      Python is inconsistent in its platform module.
      It returns different values for the same cpu.
      For x86 it might return 'x86', 'i686' or somesuch.
      Do some canonicalization.
      """
      if mesonlib.is_windows():
          trial = detect_windows_arch(compilers)
      else:
          trial = platform.machine().lower()
      if trial.startswith('i') and trial.endswith('86'):
          return 'x86'
      if trial.startswith('arm'):
          return 'arm'
      if trial.startswith
  
  [... the rest is omitted ...]
files: mesonbuild/environment.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.
  
  import configparser, os, platform, re, shlex, shutil, subprocess
  
  from . import coredata
  from .linkers import ArLinker, VisualStudioLinker
  from . import mesonlib
  from .mesonlib import EnvironmentException, Popen_safe
  from . import mlog
  
  from . import compilers
  from .compilers import (
      CLANG_OSX,
      CLANG_STANDARD,
      CLANG_WIN,
      GCC_CYGWIN,
      GCC_MINGW,
      GCC_OSX,
      GCC_STANDARD,
      ICC_STANDARD,
      is_assembly,
      is_header,
      is_library,
      is_llvm_ir,
      is_object,
      is_source,
  )
  from .compilers import (
      ArmCCompiler,
      ArmCPPCompiler,
      ArmclangCCompiler,
      ArmclangCPPCompiler,
      ClangCCompiler,
      ClangCPPCompiler,
      ClangObjCCompiler,
      ClangObjCPPCompiler,
      G95FortranCompiler,
      GnuCCompiler,
      GnuCPPCompiler,
      GnuFortranCompiler,
      GnuObjCCompiler,
      GnuObjCPPCompiler,
      ElbrusCCompiler,
      ElbrusCPPCompiler,
      ElbrusFortranCompiler,
      IntelCCompiler,
      IntelCPPCompiler,
      IntelFortranCompiler,
      JavaCompiler,
      MonoCompiler,
      VisualStudioCsCompiler,
      NAGFortranCompiler,
      Open64FortranCompiler,
      PathScaleFortranCompiler,
      PGIFortranCompiler,
      RustCompiler,
      SunFortranCompiler,
      ValaCompiler,
      VisualStudioCCompiler,
      VisualStudioCPPCompiler,
  )
  
  build_filename = 'meson.build'
  
  known_cpu_families = (
      'aarch64',
      'arm',
      'e2k',
      'ia64',
      'mips',
      'mips64',
      'parisc',
      'ppc',
      'ppc64',
      'riscv32',
      'riscv64',
      'sparc',
      'sparc64',
      'x86',
      'x86_64'
  )
  
  def detect_gcovr(version='3.1', log=False):
      gcovr_exe = 'gcovr'
      try:
          p, found = Popen_safe([gcovr_exe, '--version'])[0:2]
      except (FileNotFoundError, PermissionError):
          # Doesn't exist in PATH or isn't executable
          return None, None
      found = search_version(found)
      if p.returncode == 0:
          if log:
              mlog.log('Found gcovr-{} at {}'.format(found, shlex.quote(shutil.which(gcovr_exe))))
          return gcovr_exe, mesonlib.version_compare(found, '>=' + version)
      return None, None
  
  def find_coverage_tools():
      gcovr_exe, gcovr_new_rootdir = detect_gcovr()
  
      lcov_exe = 'lcov'
      genhtml_exe = 'genhtml'
  
      if not mesonlib.exe_exists([lcov_exe, '--version']):
          lcov_exe = None
      if not mesonlib.exe_exists([genhtml_exe, '--version']):
          genhtml_exe = None
  
      return gcovr_exe, gcovr_new_rootdir, lcov_exe, genhtml_exe
  
  def detect_ninja(version='1.5', log=False):
      for n in ['ninja', 'ninja-build']:
          try:
              p, found = Popen_safe([n, '--version'])[0:2]
          except (FileNotFoundError, PermissionError):
              # Doesn't exist in PATH or isn't executable
              continue
          found = found.strip()
          # Perhaps we should add a way for the caller to know the failure mode
          # (not found or too old)
          if p.returncode == 0 and mesonlib.version_compare(found, '>=' + version):
              if log:
                  mlog.log('Found ninja-{} at {}'.format(found, shlex.quote(shutil.which(n))))
              return n
  
  def detect_native_windows_arch():
      """
      The architecture of Windows itself: x86 or amd64
      """
      # These env variables are always available. See:
      # https://msdn.microsoft.com/en-us/library/aa384274(VS.85).aspx
      # https://blogs.msdn.microsoft.com/david.wang/2006/03/27/howto-detect-process-bitness/
      arch = os.environ.get('PROCESSOR_ARCHITEW6432', '').lower()
      if not arch:
          try:
              # If this doesn't exist, something is messing with the environment
              arch = os.environ['PROCESSOR_ARCHITECTURE'].lower()
          except KeyError:
              raise EnvironmentException('Unable to detect native OS architecture')
      return arch
  
  def detect_windows_arch(compilers):
      """
      Detecting the 'native' architecture of Windows is not a trivial task. We
      cannot trust that the architecture that Python is built for is the 'native'
      one because you can run 32-bit apps on 64-bit Windows using WOW64 and
      people sometimes install 32-bit Python on 64-bit Windows.
  
      We also can't rely on the architecture of the OS itself, since it's
      perfectly normal to compile and run 32-bit applications on Windows as if
      they were native applications. It's a terrible experience to require the
      user to supply a cross-info file to compile 32-bit applications on 64-bit
      Windows. Thankfully, the only way to compile things with Visual Studio on
      Windows is by entering the 'msvc toolchain' environment, which can be
      easily detected.
  
      In the end, the sanest method is as follows:
      1. Check if we're in an MSVC toolchain environment, and if so, return the
         MSVC toolchain architecture as our 'native' architecture.
      2. If not, check environment variables that are set by Windows and WOW64 to
         find out the architecture that Windows is built for, and use that as our
         'native' architecture.
      """
      os_arch = detect_native_windows_arch()
      if os_arch != 'amd64':
          return os_arch
      # If we're on 64-bit Windows, 32-bit apps can be compiled without
      # cross-compilation. So if we're doing that, just set the native arch as
      # 32-bit and pretend like we're running under WOW64. Else, return the
      # actual Windows architecture that we deduced above.
      for compiler in compilers.values():
          # Check if we're using and inside an MSVC toolchain environment
          if compiler.id == 'msvc' and 'VCINSTALLDIR' in os.environ:
              if float(compiler.get_toolset_version()) < 10.0:
                  # On MSVC 2008 and earlier, check 'BUILD_PLAT', where
                  # 'Win32' means 'x86'
                  platform = os.environ.get('BUILD_PLAT', 'x86')
                  if platform == 'Win32':
                      return 'x86'
              else:
                  # On MSVC 2010 and later 'Platform' is only set when the
                  # target arch is not 'x86'.  It's 'x64' when targeting
                  # x86_64 and 'arm' when targeting ARM.
                  platform = os.environ.get('Platform', 'x86').lower()
              if platform == 'x86':
                  return platform
          if compiler.id == 'gcc' and compiler.has_builtin_define('__i386__'):
              return 'x86'
      return os_arch
  
  def detect_cpu_family(compilers):
      """
      Python is inconsistent in its platform module.
      It returns different values for the same cpu.
      For x86 it might return 'x86', 'i686' or somesuch.
      Do some canonicalization.
      """
      if mesonlib.is_windows():
          trial = detect_windows_arch(compilers)
      else:
          trial = platform.machine().lower()
      if trial.startswith('i') and trial.endswith('86'):
          return 'x86'
      if trial.startswith('arm'):
          return 'arm'
      if trial.startswith
  
  [... the rest is omitted ...]
files: mesonbuild/environment.py
files: # Copyright 2012-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.
  
  """A library of random helper functionality."""
  
  import sys
  import stat
  import time
  import platform, subprocess, operator, os, shutil, re
  import collections
  from mesonbuild import mlog
  
  have_fcntl = False
  have_msvcrt = False
  # {subproject: project_meson_version}
  project_meson_versions = {}
  
  try:
      import fcntl
      have_fcntl = True
  except Exception:
      pass
  
  try:
      import msvcrt
      have_msvcrt = True
  except Exception:
      pass
  
  from glob import glob
  
  if os.path.basename(sys.executable) == 'meson.exe':
      # In Windows and using the MSI installed executable.
      python_command = [sys.executable, 'runpython']
  else:
      python_command = [sys.executable]
  meson_command = None
  
  def is_ascii_string(astring):
      try:
          if isinstance(astring, str):
              astring.encode('ascii')
          if isinstance(astring, bytes):
              astring.decode('ascii')
      except UnicodeDecodeError:
          return False
      return True
  
  def check_direntry_issues(direntry_array):
      import locale
      # Warn if the locale is not UTF-8. This can cause various unfixable issues
      # such as os.stat not being able to decode filenames with unicode in them.
      # There is no way to reset both the preferred encoding and the filesystem
      # encoding, so we can just warn about it.
      e = locale.getpreferredencoding()
      if e.upper() != 'UTF-8' and not is_windows():
          if not isinstance(direntry_array, list):
              direntry_array = [direntry_array]
          for de in direntry_array:
              if is_ascii_string(de):
                  continue
              mlog.warning('''You are using {!r} which is not a Unicode-compatible '
  locale but you are trying to access a file system entry called {!r} which is
  not pure ASCII. This may cause problems.
  '''.format(e, de), file=sys.stderr)
  
  # Put this in objects that should not get dumped to pickle files
  # by accident.
  import threading
  an_unpicklable_object = threading.Lock()
  
  class MesonException(Exception):
      '''Exceptions thrown by Meson'''
  
      def get_msg_with_context(self):
          s = ''
          if hasattr(self, 'lineno') and hasattr(self, 'file'):
              s = get_error_location_string(self.file, self.lineno) + ' '
          s += str(self)
          return s
  
  class EnvironmentException(MesonException):
      '''Exceptions thrown while processing and creating the build environment'''
  
  class FileMode:
      # The first triad is for owner permissions, the second for group permissions,
      # and the third for others (everyone else).
      # For the 1st character:
      #  'r' means can read
      #  '-' means not allowed
      # For the 2nd character:
      #  'w' means can write
      #  '-' means not allowed
      # For the 3rd character:
      #  'x' means can execute
      #  's' means can execute and setuid/setgid is set (owner/group triads only)
      #  'S' means cannot execute and setuid/setgid is set (owner/group triads only)
      #  't' means can execute and sticky bit is set ("others" triads only)
      #  'T' means cannot execute and sticky bit is set ("others" triads only)
      #  '-' means none of these are allowed
      #
      # The meanings of 'rwx' perms is not obvious for directories; see:
      # https://www.hackinglinuxexposed.com/articles/20030424.html
      #
      # For information on this notation such as setuid/setgid/sticky bits, see:
      # https://en.wikipedia.org/wiki/File_system_permissions#Symbolic_notation
      symbolic_perms_regex = re.compile('[r-][w-][xsS-]' # Owner perms
                                        '[r-][w-][xsS-]' # Group perms
                                        '[r-][w-][xtT-]') # Others perms
  
      def __init__(self, perms=None, owner=None, group=None):
          self.perms_s = perms
          self.perms = self.perms_s_to_bits(perms)
          self.owner = owner
          self.group = group
  
      def __repr__(self):
          ret = '<FileMode: {!r} owner={} group={}'
          return ret.format(self.perms_s, self.owner, self.group)
  
      @classmethod
      def perms_s_to_bits(cls, perms_s):
          '''
          Does the opposite of stat.filemode(), converts strings of the form
          'rwxr-xr-x' to st_mode enums which can be passed to os.chmod()
          '''
          if perms_s is None:
              # No perms specified, we will not touch the permissions
              return -1
          eg = 'rwxr-xr-x'
          if not isinstance(perms_s, str):
              msg = 'Install perms must be a string. For example, {!r}'
              raise MesonException(msg.format(eg))
          if len(perms_s) != 9 or not cls.symbolic_perms_regex.match(perms_s):
              msg = 'File perms {!r} must be exactly 9 chars. For example, {!r}'
              raise MesonException(msg.format(perms_s, eg))
          perms = 0
          # Owner perms
          if perms_s[0] == 'r':
              perms |= stat.S_IRUSR
          if perms_s[1] == 'w':
              perms |= stat.S_IWUSR
          if perms_s[2] == 'x':
              perms |= stat.S_IXUSR
          elif perms_s[2] == 'S':
              perms |= stat.S_ISUID
          elif perms_s[2] == 's':
              perms |= stat.S_IXUSR
              perms |= stat.S_ISUID
          # Group perms
          if perms_s[3] == 'r':
              perms |= stat.S_IRGRP
          if perms_s[4] == 'w':
              perms |= stat.S_IWGRP
          if perms_s[5] == 'x':
              perms |= stat.S_IXGRP
          elif perms_s[5] == 'S':
              perms |= stat.S_ISGID
          elif perms_s[5] == 's':
              perms |= stat.S_IXGRP
              perms |= stat.S_ISGID
          # Others perms
          if perms_s[6] == 'r':
              perms |= stat.S_IROTH
          if perms_s[7] == 'w':
              perms |= stat.S_IWOTH
          if perms_s[8] == 'x':
              perms |= stat.S_IXOTH
          elif perms_s[8] == 'T':
              perms |= stat.S_ISVTX
          elif perms_s[8] == 't':
              perms |= stat.S_IXOTH
              perms |= stat.S_ISVTX
          return perms
  
  class File:
      def __init__(self, is_built, subdir, fname):
          self.is_built = is_built
          self.subdir = subdir
          self.fname = fname
          assert(isinstance(self.subdir, str))
          assert(isinstance(self.fname, str))
  
      def __str__(self):
          return self.relative_name()
  
      def __repr__(self):
          ret = '<File: {0}'
          if not self.is_built:
              ret += ' (not built)'
          ret += '>'
          return ret.format(self.relative_name())
  
      @staticmethod
      def from_source_file(source_root, subdir, fname):
          if not os.path.isfile(os.path.join(source_root, subdir, fname)):
              raise MesonException('File %s does not exist.' % fname)
          return File(False, subdir, fname)
  
      @staticmethod
      def from_built_file(subdir, fname):
          return File(True, subdir, fname
  
  [... the rest is omitted ...]
files: mesonbuild/mesonlib.py
files: # Copyright 2012-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.
  
  """A library of random helper functionality."""
  
  import sys
  import stat
  import time
  import platform, subprocess, operator, os, shutil, re
  import collections
  from mesonbuild import mlog
  
  have_fcntl = False
  have_msvcrt = False
  # {subproject: project_meson_version}
  project_meson_versions = {}
  
  try:
      import fcntl
      have_fcntl = True
  except Exception:
      pass
  
  try:
      import msvcrt
      have_msvcrt = True
  except Exception:
      pass
  
  from glob import glob
  
  if os.path.basename(sys.executable) == 'meson.exe':
      # In Windows and using the MSI installed executable.
      python_command = [sys.executable, 'runpython']
  else:
      python_command = [sys.executable]
  meson_command = None
  
  def is_ascii_string(astring):
      try:
          if isinstance(astring, str):
              astring.encode('ascii')
          if isinstance(astring, bytes):
              astring.decode('ascii')
      except UnicodeDecodeError:
          return False
      return True
  
  def check_direntry_issues(direntry_array):
      import locale
      # Warn if the locale is not UTF-8. This can cause various unfixable issues
      # such as os.stat not being able to decode filenames with unicode in them.
      # There is no way to reset both the preferred encoding and the filesystem
      # encoding, so we can just warn about it.
      e = locale.getpreferredencoding()
      if e.upper() != 'UTF-8' and not is_windows():
          if not isinstance(direntry_array, list):
              direntry_array = [direntry_array]
          for de in direntry_array:
              if is_ascii_string(de):
                  continue
              mlog.warning('''You are using {!r} which is not a Unicode-compatible '
  locale but you are trying to access a file system entry called {!r} which is
  not pure ASCII. This may cause problems.
  '''.format(e, de), file=sys.stderr)
  
  # Put this in objects that should not get dumped to pickle files
  # by accident.
  import threading
  an_unpicklable_object = threading.Lock()
  
  class MesonException(Exception):
      '''Exceptions thrown by Meson'''
  
      def get_msg_with_context(self):
          s = ''
          if hasattr(self, 'lineno') and hasattr(self, 'file'):
              s = get_error_location_string(self.file, self.lineno) + ' '
          s += str(self)
          return s
  
  class EnvironmentException(MesonException):
      '''Exceptions thrown while processing and creating the build environment'''
  
  class FileMode:
      # The first triad is for owner permissions, the second for group permissions,
      # and the third for others (everyone else).
      # For the 1st character:
      #  'r' means can read
      #  '-' means not allowed
      # For the 2nd character:
      #  'w' means can write
      #  '-' means not allowed
      # For the 3rd character:
      #  'x' means can execute
      #  's' means can execute and setuid/setgid is set (owner/group triads only)
      #  'S' means cannot execute and setuid/setgid is set (owner/group triads only)
      #  't' means can execute and sticky bit is set ("others" triads only)
      #  'T' means cannot execute and sticky bit is set ("others" triads only)
      #  '-' means none of these are allowed
      #
      # The meanings of 'rwx' perms is not obvious for directories; see:
      # https://www.hackinglinuxexposed.com/articles/20030424.html
      #
      # For information on this notation such as setuid/setgid/sticky bits, see:
      # https://en.wikipedia.org/wiki/File_system_permissions#Symbolic_notation
      symbolic_perms_regex = re.compile('[r-][w-][xsS-]' # Owner perms
                                        '[r-][w-][xsS-]' # Group perms
                                        '[r-][w-][xtT-]') # Others perms
  
      def __init__(self, perms=None, owner=None, group=None):
          self.perms_s = perms
          self.perms = self.perms_s_to_bits(perms)
          self.owner = owner
          self.group = group
  
      def __repr__(self):
          ret = '<FileMode: {!r} owner={} group={}'
          return ret.format(self.perms_s, self.owner, self.group)
  
      @classmethod
      def perms_s_to_bits(cls, perms_s):
          '''
          Does the opposite of stat.filemode(), converts strings of the form
          'rwxr-xr-x' to st_mode enums which can be passed to os.chmod()
          '''
          if perms_s is None:
              # No perms specified, we will not touch the permissions
              return -1
          eg = 'rwxr-xr-x'
          if not isinstance(perms_s, str):
              msg = 'Install perms must be a string. For example, {!r}'
              raise MesonException(msg.format(eg))
          if len(perms_s) != 9 or not cls.symbolic_perms_regex.match(perms_s):
              msg = 'File perms {!r} must be exactly 9 chars. For example, {!r}'
              raise MesonException(msg.format(perms_s, eg))
          perms = 0
          # Owner perms
          if perms_s[0] == 'r':
              perms |= stat.S_IRUSR
          if perms_s[1] == 'w':
              perms |= stat.S_IWUSR
          if perms_s[2] == 'x':
              perms |= stat.S_IXUSR
          elif perms_s[2] == 'S':
              perms |= stat.S_ISUID
          elif perms_s[2] == 's':
              perms |= stat.S_IXUSR
              perms |= stat.S_ISUID
          # Group perms
          if perms_s[3] == 'r':
              perms |= stat.S_IRGRP
          if perms_s[4] == 'w':
              perms |= stat.S_IWGRP
          if perms_s[5] == 'x':
              perms |= stat.S_IXGRP
          elif perms_s[5] == 'S':
              perms |= stat.S_ISGID
          elif perms_s[5] == 's':
              perms |= stat.S_IXGRP
              perms |= stat.S_ISGID
          # Others perms
          if perms_s[6] == 'r':
              perms |= stat.S_IROTH
          if perms_s[7] == 'w':
              perms |= stat.S_IWOTH
          if perms_s[8] == 'x':
              perms |= stat.S_IXOTH
          elif perms_s[8] == 'T':
              perms |= stat.S_ISVTX
          elif perms_s[8] == 't':
              perms |= stat.S_IXOTH
              perms |= stat.S_ISVTX
          return perms
  
  class File:
      def __init__(self, is_built, subdir, fname):
          self.is_built = is_built
          self.subdir = subdir
          self.fname = fname
          assert(isinstance(self.subdir, str))
          assert(isinstance(self.fname, str))
  
      def __str__(self):
          return self.relative_name()
  
      def __repr__(self):
          ret = '<File: {0}'
          if not self.is_built:
              ret += ' (not built)'
          ret += '>'
          return ret.format(self.relative_name())
  
      @staticmethod
      def from_source_file(source_root, subdir, fname):
          if not os.path.isfile(os.path.join(source_root, subdir, fname)):
              raise MesonException('File %s does not exist.' % fname)
          return File(False, subdir, fname)
  
      @staticmethod
      def from_built_file(subdir, fname):
          return File(True, subdir, fname
  
  [... the rest is omitted ...]
files: mesonbuild/mesonlib.py