files: # Copyright 2012-2018 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 pickle, os, uuid, shlex
  import sys
  from pathlib import PurePath
  from collections import OrderedDict
  from .mesonlib import MesonException
  from .mesonlib import default_libdir, default_libexecdir, default_prefix
  import ast
  import argparse
  
  version = '0.47.0.dev1'
  backendlist = ['ninja', 'vs', 'vs2010', 'vs2015', 'vs2017', 'xcode']
  
  default_yielding = False
  
  class UserOption:
      def __init__(self, name, description, choices, yielding):
          super().__init__()
          self.name = name
          self.choices = choices
          self.description = description
          if yielding is None:
              yielding = default_yielding
          if not isinstance(yielding, bool):
              raise MesonException('Value of "yielding" must be a boolean.')
          self.yielding = yielding
  
      # Check that the input is a valid value and return the
      # "cleaned" or "native" version. For example the Boolean
      # option could take the string "true" and return True.
      def validate_value(self, value):
          raise RuntimeError('Derived option class did not override validate_value.')
  
      def set_value(self, newvalue):
          self.value = self.validate_value(newvalue)
  
  class UserStringOption(UserOption):
      def __init__(self, name, description, value, choices=None, yielding=None):
          super().__init__(name, description, choices, yielding)
          self.set_value(value)
  
      def validate_value(self, value):
          if not isinstance(value, str):
              raise MesonException('Value "%s" for string option "%s" is not a string.' % (str(value), self.name))
          return value
  
  class UserBooleanOption(UserOption):
      def __init__(self, name, description, value, yielding=None):
          super().__init__(name, description, [True, False], yielding)
          self.set_value(value)
  
      def __bool__(self):
          return self.value
  
      def validate_value(self, value):
          if isinstance(value, bool):
              return value
          if value.lower() == 'true':
              return True
          if value.lower() == 'false':
              return False
          raise MesonException('Value %s is not boolean (true or false).' % value)
  
  class UserIntegerOption(UserOption):
      def __init__(self, name, description, min_value, max_value, value, yielding=None):
          super().__init__(name, description, [True, False], yielding)
          self.min_value = min_value
          self.max_value = max_value
          self.set_value(value)
          c = []
          if min_value is not None:
              c.append('>=' + str(min_value))
          if max_value is not None:
              c.append('<=' + str(max_value))
          self.choices = ', '.join(c)
  
      def validate_value(self, value):
          if isinstance(value, str):
              value = self.toint(value)
          if not isinstance(value, int):
              raise MesonException('New value for integer option is not an integer.')
          if self.min_value is not None and value < self.min_value:
              raise MesonException('New value %d is less than minimum value %d.' % (value, self.min_value))
          if self.max_value is not None and value > self.max_value:
              raise MesonException('New value %d is more than maximum value %d.' % (value, self.max_value))
          return value
  
      def toint(self, valuestring):
          try:
              return int(valuestring)
          except ValueError:
              raise MesonException('Value string "%s" is not convertable to an integer.' % valuestring)
  
  class UserUmaskOption(UserIntegerOption):
      def __init__(self, name, description, value, yielding=None):
          super().__init__(name, description, 0, 0o777, value, yielding)
  
      def set_value(self, newvalue):
          if newvalue is None or newvalue == 'preserve':
              self.value = None
          else:
              super().set_value(newvalue)
  
      def toint(self, valuestring):
          try:
              return int(valuestring, 8)
          except ValueError as e:
              raise MesonException('Invalid mode: {}'.format(e))
  
  class UserComboOption(UserOption):
      def __init__(self, name, description, choices, value, yielding=None):
          super().__init__(name, description, choices, yielding)
          if not isinstance(self.choices, list):
              raise MesonException('Combo choices must be an array.')
          for i in self.choices:
              if not isinstance(i, str):
                  raise MesonException('Combo choice elements must be strings.')
          self.set_value(value)
  
      def validate_value(self, value):
          if value not in self.choices:
              optionsstring = ', '.join(['"%s"' % (item,) for item in self.choices])
              raise MesonException('Value "%s" for combo option "%s" is not one of the choices. Possible choices are: %s.' % (value, self.name, optionsstring))
          return value
  
  class UserArrayOption(UserOption):
      def __init__(self, name, description, value, shlex_split=False, user_input=False, **kwargs):
          super().__init__(name, description, kwargs.get('choices', []), yielding=kwargs.get('yielding', None))
          self.shlex_split = shlex_split
          self.value = self.validate_value(value, user_input=user_input)
  
      def validate_value(self, value, user_input=True):
          # User input is for options defined on the command line (via -D
          # options). Users can put their input in as a comma separated
          # string, but for defining options in meson_options.txt the format
          # should match that of a combo
          if not user_input and isinstance(value, str) and not value.startswith('['):
              raise MesonException('Value does not define an array: ' + value)
  
          if isinstance(value, str):
              if value.startswith('['):
                  newvalue = ast.literal_eval(value)
              else:
                  if self.shlex_split:
                      newvalue = shlex.split(value)
                  else:
                      newvalue = [v.strip() for v in value.split(',')]
          elif isinstance(value, list):
              newvalue = value
          else:
              raise MesonException('"{0}" should be a string array, but it is not'.format(str(newvalue)))
  
          if len(set(newvalue)) != len(newvalue):
              msg = 'Duplicated values in array option "%s" is deprecated. ' \
                    'This will become a hard error in the future.' % (self.name)
              mlog.log(mlog.red('DEPRECATION:'), msg)
          for i in newvalue:
              if not isinstance(i, str):
                  raise MesonException('String array element "{0}" is not a string.'.format(str(newvalue)))
          if self.choices:
              bad = [x for x in newvalue if x not in self.choices]
              if bad:
                  raise MesonException('Options "{}" are not in allowed choices: "{}"'.format(
                      ', '.join(bad), ', '.join(self.choices)))
          return newvalue
  
  # This class contains all data that must persist over multiple
  # invocations of Meson. It is roughly the same thing as
  # cmakecache.
  
  class CoreData:
  
      def __init__(self, options):
          self.lang_guids = {
              'default': '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942',
              'c': '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942',
              'cpp': '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942',
              'test': '3AC096D0-A1C2-E12C-1390-A8335801FDAB',
              'directory': '2150E333-8FDC-42A3-9474-1A3956D46DE8',
          }
          self.test_guid = str(uuid.uuid4()).upper()
          self.regen_guid = str(uuid.uuid4()).upper()
          self.target_guids = {}
          self.version = version
          self.init_builtins(options)
          self.backend_options = {}
          self.user_options = {}
          self.compiler_options = {}
          self.base_options = {}
          self.external_preprocess_args = {} # CPPFLAGS only
          self.cross_file = self.__load_cross_file(options.cross_file)
          self.wrap_mode = options.wrap_mode
          self.compilers = OrderedDict()
          self.cross_compilers = OrderedDict()
          self.deps = OrderedDict()
          # Only to print a warning if it changes between Meson invocations.
          self.pkgconf_envvar = os.environ.get('PKG_CONFIG_PATH', '')
  
      @staticmethod
      def __load_cross_file(filename):
          """Try to load the cross file.
  
          If the filename is None return None. If the filename is an absolute
          (after resolving variables and ~), return that absolute path. Next,
          check if the file is relative to the current source dir. If the path
          still isn't resolved do the following:
              Windows:
                  - Error
              *:
                  - $XDG_DATA_HOME/meson/cross (or ~/.local/share/meson/cross if
                    undefined)
                  - $XDG_DATA_DIRS/meson/cross (or
                    /usr/local/share/meson/cross:/usr/share/meson/cross if undefined)
                  - Error
  
          Non-Windows follows the Linux path and will honor XDG_* if set. This
          simplifies the implementation somewhat.
          """
          if filename is None:
              return None
          filename = os.path.expanduser(os.path.expandvars(filename))
          if os.path.isabs(filename):
              return filename
          path_to_try = os.path.abspath(filename)
          if os.path.exists(path_to_try):
              return path_to_try
          if sys.platform != 'win32':
              paths = [
                  os.environ.get('XDG_DATA_HOME', os.path.expanduser('~/.local/share')),
              ] + os.environ.get('XDG_DATA_DIRS', '/usr/local/share:/usr/share').split(':')
              for path in paths:
                  path_to_try = os.path.join(path, 'meson', 'cross', filename)
                  if os.path.exists(path_to_try):
                      return path_to_try
              raise MesonException('Cannot find specified cross file: ' + filename)
  
          raise MesonException('Cannot find specified cross file: ' + filename)
  
      def sanitize_prefix(self, prefix):
          if not os.path.isabs(prefix):
              raise MesonException('prefix value {!r} must be an absolute path'
                                   ''.format(prefix))
          if prefix.endswith('/') or prefix.endswith('\\'):
              # On Windows we need to preserve the trailing slash if the
              # string is of type 'C:\' because 'C:' is not an absolute path.
              if len(prefix) == 3 and prefix[1] == ':':
                  pass
              # If prefix is a single character, preserve it since it is
              # the root directory.
              elif len(prefix) == 1:
                  pass
              else:
                  prefix = prefix[:-1]
          return prefix
  
      def sanitize_dir_option_value(self, prefix, option, value):
          '''
          If the option is an installation directory option and the value is an
          absolute path, check that it resides within prefix and return the value
          as a path relative to the prefix.
  
          This way everyone can do f.ex, get_option('libdir') and be sure to get
          the library directory relative to prefix.
          '''
          if option.endswith('dir') and os.path.isabs(value) and \
             option not in builtin_dir_noprefix_options:
              # Value must be a subdir of the prefix
              # commonpath will always return a path in the native format, so we
              # must use pathlib.PurePath to do the same conversion before
              # comparing.
              if os.path.commonpath([value, prefix]) != str(PurePath(prefix)):
                  m = 'The value of the {!r} option is {!r} which must be a ' \
                      'subdir of the prefix {!r}.\nNote that if you pass a ' \
                      'relative path, it is assumed to be a subdir of prefix.'
                  raise MesonException(m.format(option, value, prefix))
              # Convert path to be relative to prefix
              skip = len(prefix) + 1
              value = value[skip:]
          return value
  
      def init_builtins(self, options):
          self.builtins = {}
          # Sanitize prefix
          options.prefix = self.sanitize_prefix(options.prefix)
          # Initialize other builtin options
          for key in get_builtin_options():
              if hasattr(options, key):
                  value = getattr(options, key)
                  value = self.sanitize_dir_option_value(options.prefix, key, value)
                  setattr(options, key, value)
              else:
                  value = get_builtin_option_default(key, prefix=options.prefix)
              args = [key] + builtin_options[key][1:-1] + [value]
              self.builtins[key] = builtin_options[key][0](*args)
  
      def init_backend_options(self, backend_name, options):
          if backend_name == 'ninja':
              self.backend_options['backend_max_links'] = UserIntegerOption('backend_max_links',
                                                                            'Maximum number of linker processes to run or 0 for no limit',
                                                                            0, None, 0)
          for o in options:
              key, value = o.split('=', 1)
              if not key.startswith('backend_'):
                  continue
              if key not in self.backend_options:
                  raise MesonException('Unknown backend option %s' % key)
              self.backend_options[key].set_value(value)
  
      def get_builtin_option(self, optname):
          if optname in self.builtins:
              return self.builtins[optname].value
          raise RuntimeError('Tried to get unknown builtin option %s.' % optname)
  
      def set_builtin_option(self, optname, value):
          if optname == 'prefix':
              value = self.sanitize_prefix(value)
          elif optname in self.builtins:
              prefix = self.builtins['prefix'].value
              value = self.sanitize_dir_option_value(prefix, optname, value)
          else:
              raise RuntimeError('Tried to set unknown builtin option %s.' % optname)
          self.builtins[optname].set_value(value)
  
      def validate_option_value(self, option_name, override_value):
          for opts in (self.builtins, self.base_options, self.compiler_options, self.user_options):
              if option_name in opts:
                  opt = opts[option_name]
                  return opt.validate_value(override_value)
          raise MesonException('Tried to validate unknown option %s.' % option_name)
  
      def get_external_args(self, lang):
          return self.compiler_options[lang + '_args'].value
  
      def get_external_link_args(self, lang):
          return self.compiler_options[lang + '_link_args'].value
  
      def get_external_preprocess_args(self, lang):
          return self.external_preprocess_args[lang]
  
      def merge_user_options(self, options):
          for (name, value) in options.items():
              if name not in self.user_options:
                  self.user_options[name] = value
              else:
                  oldval = self.user_options[name]
                  if type(oldval) != type(value):
                      self.user_options[name] = value
  
      def set_options(self, options):
          for o in options:
              if '=' not in o:
                  raise MesonException('Value "%s" not of type "a=b".' % o)
              (k, v) = o.split('=', 1)
              if is_builtin_option(k):
                  self.set_builtin_option(k, v)
              elif k in self.backend_options:
                  tgt = self.backend_options[k]
                  tgt.set_value(v)
              elif k in self.user_options:
                  tgt = self.user_options[k]
                  tgt.set_value(v)
              elif k in self.compiler_options:
                  tgt = self.compiler_options[k]
                  tgt.set_value(v)
              elif k in self.base_options:
                  tgt = self.base_options[k]
                  tgt.set_value(v)
              else:
                  raise MesonException('Unknown option %s.' % k)
  
  def load(build_dir):
      filename = os.path.join(build_dir, 'meson-private', 'coredata.dat')
      load_fail_msg = 'Coredata file {!r} is corrupted. Try with a fresh build tree.'.format(filename)
      try:
          with open(filename, 'rb') as f:
              obj = pickle.load(f)
      except pickle.UnpicklingError:
          raise MesonException(load_fail_msg)
      if not isinstance(obj, CoreData):
          raise MesonException(load_fail_msg)
      if obj.version != version:
          raise MesonException('Build directory has been generated with Meson version %s, which is incompatible with current version %s.\nPlease delete this build directory AND create a new one.' %
                               (obj.version, version))
      return obj
  
  
  
  [... the rest is omitted ...]
files: mesonbuild/coredata.py
files: # Copyright 2012-2018 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 pickle, os, uuid, shlex
  import sys
  from pathlib import PurePath
  from collections import OrderedDict
  from .mesonlib import MesonException
  from .mesonlib import default_libdir, default_libexecdir, default_prefix
  import ast
  import argparse
  
  version = '0.47.0.dev1'
  backendlist = ['ninja', 'vs', 'vs2010', 'vs2015', 'vs2017', 'xcode']
  
  default_yielding = False
  
  class UserOption:
      def __init__(self, name, description, choices, yielding):
          super().__init__()
          self.name = name
          self.choices = choices
          self.description = description
          if yielding is None:
              yielding = default_yielding
          if not isinstance(yielding, bool):
              raise MesonException('Value of "yielding" must be a boolean.')
          self.yielding = yielding
  
      # Check that the input is a valid value and return the
      # "cleaned" or "native" version. For example the Boolean
      # option could take the string "true" and return True.
      def validate_value(self, value):
          raise RuntimeError('Derived option class did not override validate_value.')
  
      def set_value(self, newvalue):
          self.value = self.validate_value(newvalue)
  
  class UserStringOption(UserOption):
      def __init__(self, name, description, value, choices=None, yielding=None):
          super().__init__(name, description, choices, yielding)
          self.set_value(value)
  
      def validate_value(self, value):
          if not isinstance(value, str):
              raise MesonException('Value "%s" for string option "%s" is not a string.' % (str(value), self.name))
          return value
  
  class UserBooleanOption(UserOption):
      def __init__(self, name, description, value, yielding=None):
          super().__init__(name, description, [True, False], yielding)
          self.set_value(value)
  
      def __bool__(self):
          return self.value
  
      def validate_value(self, value):
          if isinstance(value, bool):
              return value
          if value.lower() == 'true':
              return True
          if value.lower() == 'false':
              return False
          raise MesonException('Value %s is not boolean (true or false).' % value)
  
  class UserIntegerOption(UserOption):
      def __init__(self, name, description, min_value, max_value, value, yielding=None):
          super().__init__(name, description, [True, False], yielding)
          self.min_value = min_value
          self.max_value = max_value
          self.set_value(value)
          c = []
          if min_value is not None:
              c.append('>=' + str(min_value))
          if max_value is not None:
              c.append('<=' + str(max_value))
          self.choices = ', '.join(c)
  
      def validate_value(self, value):
          if isinstance(value, str):
              value = self.toint(value)
          if not isinstance(value, int):
              raise MesonException('New value for integer option is not an integer.')
          if self.min_value is not None and value < self.min_value:
              raise MesonException('New value %d is less than minimum value %d.' % (value, self.min_value))
          if self.max_value is not None and value > self.max_value:
              raise MesonException('New value %d is more than maximum value %d.' % (value, self.max_value))
          return value
  
      def toint(self, valuestring):
          try:
              return int(valuestring)
          except ValueError:
              raise MesonException('Value string "%s" is not convertable to an integer.' % valuestring)
  
  class UserUmaskOption(UserIntegerOption):
      def __init__(self, name, description, value, yielding=None):
          super().__init__(name, description, 0, 0o777, value, yielding)
  
      def set_value(self, newvalue):
          if newvalue is None or newvalue == 'preserve':
              self.value = None
          else:
              super().set_value(newvalue)
  
      def toint(self, valuestring):
          try:
              return int(valuestring, 8)
          except ValueError as e:
              raise MesonException('Invalid mode: {}'.format(e))
  
  class UserComboOption(UserOption):
      def __init__(self, name, description, choices, value, yielding=None):
          super().__init__(name, description, choices, yielding)
          if not isinstance(self.choices, list):
              raise MesonException('Combo choices must be an array.')
          for i in self.choices:
              if not isinstance(i, str):
                  raise MesonException('Combo choice elements must be strings.')
          self.set_value(value)
  
      def validate_value(self, value):
          if value not in self.choices:
              optionsstring = ', '.join(['"%s"' % (item,) for item in self.choices])
              raise MesonException('Value "%s" for combo option "%s" is not one of the choices. Possible choices are: %s.' % (value, self.name, optionsstring))
          return value
  
  class UserArrayOption(UserOption):
      def __init__(self, name, description, value, shlex_split=False, user_input=False, **kwargs):
          super().__init__(name, description, kwargs.get('choices', []), yielding=kwargs.get('yielding', None))
          self.shlex_split = shlex_split
          self.value = self.validate_value(value, user_input=user_input)
  
      def validate_value(self, value, user_input=True):
          # User input is for options defined on the command line (via -D
          # options). Users can put their input in as a comma separated
          # string, but for defining options in meson_options.txt the format
          # should match that of a combo
          if not user_input and isinstance(value, str) and not value.startswith('['):
              raise MesonException('Value does not define an array: ' + value)
  
          if isinstance(value, str):
              if value.startswith('['):
                  newvalue = ast.literal_eval(value)
              else:
                  if self.shlex_split:
                      newvalue = shlex.split(value)
                  else:
                      newvalue = [v.strip() for v in value.split(',')]
          elif isinstance(value, list):
              newvalue = value
          else:
              raise MesonException('"{0}" should be a string array, but it is not'.format(str(newvalue)))
  
          if len(set(newvalue)) != len(newvalue):
              msg = 'Duplicated values in array option "%s" is deprecated. ' \
                    'This will become a hard error in the future.' % (self.name)
              mlog.log(mlog.red('DEPRECATION:'), msg)
          for i in newvalue:
              if not isinstance(i, str):
                  raise MesonException('String array element "{0}" is not a string.'.format(str(newvalue)))
          if self.choices:
              bad = [x for x in newvalue if x not in self.choices]
              if bad:
                  raise MesonException('Options "{}" are not in allowed choices: "{}"'.format(
                      ', '.join(bad), ', '.join(self.choices)))
          return newvalue
  
  # This class contains all data that must persist over multiple
  # invocations of Meson. It is roughly the same thing as
  # cmakecache.
  
  class CoreData:
  
      def __init__(self, options):
          self.lang_guids = {
              'default': '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942',
              'c': '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942',
              'cpp': '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942',
              'test': '3AC096D0-A1C2-E12C-1390-A8335801FDAB',
              'directory': '2150E333-8FDC-42A3-9474-1A3956D46DE8',
          }
          self.test_guid = str(uuid.uuid4()).upper()
          self.regen_guid = str(uuid.uuid4()).upper()
          self.target_guids = {}
          self.version = version
          self.init_builtins(options)
          self.backend_options = {}
          self.user_options = {}
          self.compiler_options = {}
          self.base_options = {}
          self.external_preprocess_args = {} # CPPFLAGS only
          self.cross_file = self.__load_cross_file(options.cross_file)
          self.wrap_mode = options.wrap_mode
          self.compilers = OrderedDict()
          self.cross_compilers = OrderedDict()
          self.deps = OrderedDict()
          # Only to print a warning if it changes between Meson invocations.
          self.pkgconf_envvar = os.environ.get('PKG_CONFIG_PATH', '')
  
      @staticmethod
      def __load_cross_file(filename):
          """Try to load the cross file.
  
          If the filename is None return None. If the filename is an absolute
          (after resolving variables and ~), return that absolute path. Next,
          check if the file is relative to the current source dir. If the path
          still isn't resolved do the following:
              Windows:
                  - Error
              *:
                  - $XDG_DATA_HOME/meson/cross (or ~/.local/share/meson/cross if
                    undefined)
                  - $XDG_DATA_DIRS/meson/cross (or
                    /usr/local/share/meson/cross:/usr/share/meson/cross if undefined)
                  - Error
  
          Non-Windows follows the Linux path and will honor XDG_* if set. This
          simplifies the implementation somewhat.
          """
          if filename is None:
              return None
          filename = os.path.expanduser(os.path.expandvars(filename))
          if os.path.isabs(filename):
              return filename
          path_to_try = os.path.abspath(filename)
          if os.path.exists(path_to_try):
              return path_to_try
          if sys.platform != 'win32':
              paths = [
                  os.environ.get('XDG_DATA_HOME', os.path.expanduser('~/.local/share')),
              ] + os.environ.get('XDG_DATA_DIRS', '/usr/local/share:/usr/share').split(':')
              for path in paths:
                  path_to_try = os.path.join(path, 'meson', 'cross', filename)
                  if os.path.exists(path_to_try):
                      return path_to_try
              raise MesonException('Cannot find specified cross file: ' + filename)
  
          raise MesonException('Cannot find specified cross file: ' + filename)
  
      def sanitize_prefix(self, prefix):
          if not os.path.isabs(prefix):
              raise MesonException('prefix value {!r} must be an absolute path'
                                   ''.format(prefix))
          if prefix.endswith('/') or prefix.endswith('\\'):
              # On Windows we need to preserve the trailing slash if the
              # string is of type 'C:\' because 'C:' is not an absolute path.
              if len(prefix) == 3 and prefix[1] == ':':
                  pass
              # If prefix is a single character, preserve it since it is
              # the root directory.
              elif len(prefix) == 1:
                  pass
              else:
                  prefix = prefix[:-1]
          return prefix
  
      def sanitize_dir_option_value(self, prefix, option, value):
          '''
          If the option is an installation directory option and the value is an
          absolute path, check that it resides within prefix and return the value
          as a path relative to the prefix.
  
          This way everyone can do f.ex, get_option('libdir') and be sure to get
          the library directory relative to prefix.
          '''
          if option.endswith('dir') and os.path.isabs(value) and \
             option not in builtin_dir_noprefix_options:
              # Value must be a subdir of the prefix
              # commonpath will always return a path in the native format, so we
              # must use pathlib.PurePath to do the same conversion before
              # comparing.
              if os.path.commonpath([value, prefix]) != str(PurePath(prefix)):
                  m = 'The value of the {!r} option is {!r} which must be a ' \
                      'subdir of the prefix {!r}.\nNote that if you pass a ' \
                      'relative path, it is assumed to be a subdir of prefix.'
                  raise MesonException(m.format(option, value, prefix))
              # Convert path to be relative to prefix
              skip = len(prefix) + 1
              value = value[skip:]
          return value
  
      def init_builtins(self, options):
          self.builtins = {}
          # Sanitize prefix
          options.prefix = self.sanitize_prefix(options.prefix)
          # Initialize other builtin options
          for key in get_builtin_options():
              if hasattr(options, key):
                  value = getattr(options, key)
                  value = self.sanitize_dir_option_value(options.prefix, key, value)
                  setattr(options, key, value)
              else:
                  value = get_builtin_option_default(key, prefix=options.prefix)
              args = [key] + builtin_options[key][1:-1] + [value]
              self.builtins[key] = builtin_options[key][0](*args)
  
      def init_backend_options(self, backend_name, options):
          if backend_name == 'ninja':
              self.backend_options['backend_max_links'] = UserIntegerOption('backend_max_links',
                                                                            'Maximum number of linker processes to run or 0 for no limit',
                                                                            0, None, 0)
          for o in options:
              key, value = o.split('=', 1)
              if not key.startswith('backend_'):
                  continue
              if key not in self.backend_options:
                  raise MesonException('Unknown backend option %s' % key)
              self.backend_options[key].set_value(value)
  
      def get_builtin_option(self, optname):
          if optname in self.builtins:
              return self.builtins[optname].value
          raise RuntimeError('Tried to get unknown builtin option %s.' % optname)
  
      def set_builtin_option(self, optname, value):
          if optname == 'prefix':
              value = self.sanitize_prefix(value)
          elif optname in self.builtins:
              prefix = self.builtins['prefix'].value
              value = self.sanitize_dir_option_value(prefix, optname, value)
          else:
              raise RuntimeError('Tried to set unknown builtin option %s.' % optname)
          self.builtins[optname].set_value(value)
  
      def validate_option_value(self, option_name, override_value):
          for opts in (self.builtins, self.base_options, self.compiler_options, self.user_options):
              if option_name in opts:
                  opt = opts[option_name]
                  return opt.validate_value(override_value)
          raise MesonException('Tried to validate unknown option %s.' % option_name)
  
      def get_external_args(self, lang):
          return self.compiler_options[lang + '_args'].value
  
      def get_external_link_args(self, lang):
          return self.compiler_options[lang + '_link_args'].value
  
      def get_external_preprocess_args(self, lang):
          return self.external_preprocess_args[lang]
  
  def load(build_dir):
      filename = os.path.join(build_dir, 'meson-private', 'coredata.dat')
      load_fail_msg = 'Coredata file {!r} is corrupted. Try with a fresh build tree.'.format(filename)
      try:
          with open(filename, 'rb') as f:
              obj = pickle.load(f)
      except pickle.UnpicklingError:
          raise MesonException(load_fail_msg)
      if not isinstance(obj, CoreData):
          raise MesonException(load_fail_msg)
      if obj.version != version:
          raise MesonException('Build directory has been generated with Meson version %s, which is incompatible with current version %s.\nPlease delete this build directory AND create a new one.' %
                               (obj.version, version))
      return obj
  
  def save(obj, build_dir):
      filename = os.path.join(build_dir, 'meson-private', 'coredata.dat')
      prev_filename = filename + '.prev'
      tempfilename = filename + '~'
      if obj.version != version:
          raise MesonException('Fatal version mismatch corruption.')
      if os.path.exists(filename):
          import shutil
          shutil.copyfile(filename, prev_filename)
      with open(tempfilename, 'wb') as f:
          pickle.dump(obj, f)
          f.flush()
          os.fsync(f.fileno())
      os.replace(tempfilename, filename)
      return filename
  
  def get_builtin_options():
      return list(builtin_options.keys())
  
  def is_builtin_option(optname):
      return optname in get_builtin_options()
  
  def get_builtin_option_choices(optname):
      if is_builtin_option(optname):
          if builtin_options[optname][0] == UserComboOption:
              return builtin_options[optname][2]
          elif builtin_options[optname][0] == UserBooleanOption:
              return [True, False]
          else:
              return None
      else:
          raise RuntimeError('Tried to get
  
  [... the rest is omitted ...]
files: mesonbuild/coredata.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,
      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'
  
  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 in ('amd64', 'x64'):
          trial = 'x86_64'
      if trial == 'x86_64':
          # On Linux (and maybe others) there can be any mixture of 32/64 bit
          # code in the kernel, Python, system etc. The only reliable way
          # to know is to check the compiler defines.
          for c in compilers.values():
              try:
                  if c.has_builtin_define('__i386__'):
                      return 'x86'
              except mesonlib.MesonException:
                  # Ignore compilers that do not support has_builtin_define.
                  pass
          return 'x86_64'
      # Add fixes here as bugs are reported.
      return trial
  
  def detect_cpu(compilers):
      if mesonlib.is_windows():
          trial = detect_windows_arch(compilers)
      else:
          trial = platform.machine().lower()
      if trial in ('amd64', 'x64'):
          trial = 'x86_64'
      if trial == 'x86_64':
          # Same check as above for cpu_family
          for c in compilers.values():
              try:
                  if c.has_builtin_define('__i386__'):
                      return 'i686' # All 64 bit cpus have at least this level of x86 support.
              except mesonlib.MesonException:
                  pass
          return 'x86_64'
      if trial == 'e2k':
          # Make more precise CPU detection for Elbrus platform.
          trial = platform.processor().lower()
      # Add fixes here as bugs are reported.
      return trial
  
  def detect_system():
      system = platform.system().lower()
      if system.startswith('cygwin'):
          return 'cygwin'
      return system
  
  def detect_msys2_arch():
      if 'MSYSTEM_CARCH' in os.environ:
          return os.environ['MSYSTEM_CARCH']
      return None
  
  def search_version(text):
      # Usually of the type 4.1.4 but compiler output may contain
      # stuff like this:
      # (Sourcery CodeBench Lite 2014.05-29) 4.8.3 20140320 (prerelease)
      # Limiting major version number to two digits seems to work
      # thus far. When we get to GCC 100, this will break, but
      # if we are still relevant when that happens, it can be
      # considered an achievement in itself.
      #
      # This regex is reaching magic levels. If it ever needs
      # to be updated, do not complexify but convert to something
      # saner instead.
      version_regex = '(?<!(\d|\.))(\d{1,2}(\.\d+)+(-[a-zA-Z0-9]+)?)'
      match = re.search(version_regex, text)
      if match:
          return match.group(0)
      return 'unknown version'
  
  class Environment:
      private_dir = 'meson-private'
      log_dir = 'meson-logs'
  
      def __init__(self, source_dir, build_dir, options, original_cmd_line_args):
          self.source_dir = source_dir
          self.build_dir = build_dir
          self.scratch_dir = os.path.join(build_dir, Environment.private_dir)
          self.log_dir = os.path.join(build_dir, Environment.log_dir)
          os.makedirs(self.scratch_dir, exist_ok=True)
          os.makedirs(self.log_dir, exist_ok=True)
          try:
              self.coredata = coredata.load(self.get_build_dir())
              self.first_invocation = False
          except FileNotFoundError:
              # WARNING: Don't use any values from coredata in __init__. It gets
              # re-initialized with project options by the interpreter during
              # build file parsing.
              self.coredata = coredata.CoreData(options)
              # Used by the regenchecker script, which runs meson
              self.coredata.meson_command = mesonlib.meson_command
              self.first_invocation = True
          if self.coredata.cross_file:
              self.cross_info = CrossBuildInfo(self.coredata.cross_file)
          else:
              self.cross_info = None
          self.cmd_line_options = options
          self.original_cmd_line_args = original_cmd_line_args
  
          # List of potential compilers.
          if mesonlib.is_windows():
              self.default_c = ['cl', 'cc', 'gcc', 'clang']
              self.default_cpp = ['cl', 'c++', 'g++', 'clang++']
          else:
              self.default_c = ['cc', 'gcc', 'clang']
              self.default_cpp = ['c++', 'g++', 'clang++']
          if mesonlib.is_windows():
              self.default_cs = ['csc', 'mcs']
          else:
              self.default_cs = ['mcs', 'csc']
          self.default_objc = ['cc']
          self.default_objcpp = ['c++']
          self.default_fortran = ['gfortran', 'g95', 'f95', 'f90', 'f77', 'ifort']
          self.default_rust = ['rustc']
          self.default_static_linker = ['ar']
          self.vs_static_linker = ['lib']
          self.gcc_static_linker = ['gcc-ar']
          self.clang_static_linker = ['llvm-ar']
  
          # Various prefixes and suffixes for import libraries, shared libraries,
          # static libraries, and executables.
          # Versioning is added to these names in the backends as-needed.
          cross = self.is_cross_build()
          if (not cross and mesonlib.is_windows()) \
                  or (cross and self.cross_info.has_host() and self.cross_info.config['host_machine']['system'] == 'windows'):
              self.exe_suffix = 'exe'
              self.object_suffix = 'obj'
              self.win_libdir_layout = True
          elif (not cross and mesonlib.is_cygwin()) \
                  or (cross and self.cross_info.has_host() and self.cross_info.config['host_machine']['system'] == 'cygwin'):
              self.exe_suffix = 'exe'
              self.object_suffix = 'o'
              self.win_libdir_layout = True
          else:
              self.exe_suffix = ''
              self.object_suffix = 'o'
              self.win_libdir_layout = False
          if 'STRIP' in os.environ:
              self.native_strip_bin = shlex.split(os.environ['STRIP'])
          else:
              self.native_strip_bin = ['strip']
  
      def is_cross_build(self):
          return self.cross_info is not None
  
      def dump_coredata(self):
          return coredata.save(self.coredata, self.get_build_dir())
  
      def get_script_dir(self):
          import mesonbuild.scripts
          return os.path.dirname(mesonbuild.scripts.__file__)
  
      def get_log_dir(self):
          return self.log_dir
  
      def get_coredata(self):
          return self.coredata
  
      def get_build_command(self, unbuffered=False):
          cmd = mesonlib.meson_command[:]
          if unbuffered and 'python' in cmd[0]:
              cmd.insert(1, '-u')
          return cmd
  
      def is_header(self, fname):
          return is_header(fname)
  
      def is_source(self, fname):
          return is_source(fname)
  
      def is_assembly(self, fname):
          return is_assembly(fname)
  
      def is_llvm_ir(self, fname):
          return is_llvm_ir(fname)
  
      def is_object(self, fname):
          return is_object(fname)
  
      def is_library(self, fname):
          return is_library(fname)
  
      def had_argument_for(self, option):
          trial1 = coredata.get_builtin_option_cmdline_name(option)
          trial2 = '-D' + option
          previous_is_plaind = False
          for i in self.original_cmd_line_args:
              if i.startswith(trial1) or i.startswith(trial2):
                  return True
              if previous_is_plaind and i.startswith(option):
                  return True
              previous_is_plaind = i == '-D'
          return False
  
      @staticmethod
      def get_gnu_compiler_defines(compiler):
          """
          Detect GNU compiler platform type (Apple, MinGW, Unix)
          """
          # Arguments to output compiler pre-processor defines to stdout
          # gcc, g++, and gfortran all support these arguments
          args = compiler + ['-E', '-dM', '-']
          p, output, error = Popen_safe(args, write='', stdin=subprocess.PIPE)
          if p.returncode != 0:
              raise EnvironmentException('Unable to detect GNU compiler type:\n' + output + error)
          # Parse several lines of the type:
          # `#define ___SOME_DEF some_value`
          # and extract `___SOME_DEF`
          defines = {}
          for line in output.split('\n'):
              if not line:
                  continue
              d, *rest = line.split(' ', 2)
              if d != '#define':
                  continue
              if len(rest) == 1:
                  defines[rest] = True
              if len(rest) == 2:
                  defines[rest[0]] = rest[
  
  [... 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,
      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'
  
  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 in ('amd64', 'x64'):
          trial = 'x86_64'
      if trial == 'x86_64':
          # On Linux (and maybe others) there can be any mixture of 32/64 bit
          # code in the kernel, Python, system etc. The only reliable way
          # to know is to check the compiler defines.
          for c in compilers.values():
              try:
                  if c.has_builtin_define('__i386__'):
                      return 'x86'
              except mesonlib.MesonException:
                  # Ignore compilers that do not support has_builtin_define.
                  pass
          return 'x86_64'
      # Add fixes here as bugs are reported.
      return trial
  
  def detect_cpu(compilers):
      if mesonlib.is_windows():
          trial = detect_windows_arch(compilers)
      else:
          trial = platform.machine().lower()
      if trial in ('amd64', 'x64'):
          trial = 'x86_64'
      if trial == 'x86_64':
          # Same check as above for cpu_family
          for c in compilers.values():
              try:
                  if c.has_builtin_define('__i386__'):
                      return 'i686' # All 64 bit cpus have at least this level of x86 support.
              except mesonlib.MesonException:
                  pass
          return 'x86_64'
      if trial == 'e2k':
          # Make more precise CPU detection for Elbrus platform.
          trial = platform.processor().lower()
      # Add fixes here as bugs are reported.
      return trial
  
  def detect_system():
      system = platform.system().lower()
      if system.startswith('cygwin'):
          return 'cygwin'
      return system
  
  def detect_msys2_arch():
      if 'MSYSTEM_CARCH' in os.environ:
          return os.environ['MSYSTEM_CARCH']
      return None
  
  def search_version(text):
      # Usually of the type 4.1.4 but compiler output may contain
      # stuff like this:
      # (Sourcery CodeBench Lite 2014.05-29) 4.8.3 20140320 (prerelease)
      # Limiting major version number to two digits seems to work
      # thus far. When we get to GCC 100, this will break, but
      # if we are still relevant when that happens, it can be
      # considered an achievement in itself.
      #
      # This regex is reaching magic levels. If it ever needs
      # to be updated, do not complexify but convert to something
      # saner instead.
      version_regex = '(?<!(\d|\.))(\d{1,2}(\.\d+)+(-[a-zA-Z0-9]+)?)'
      match = re.search(version_regex, text)
      if match:
          return match.group(0)
      return 'unknown version'
  
  class Environment:
      private_dir = 'meson-private'
      log_dir = 'meson-logs'
  
      def __init__(self, source_dir, build_dir, options, original_cmd_line_args):
          self.source_dir = source_dir
          self.build_dir = build_dir
          self.scratch_dir = os.path.join(build_dir, Environment.private_dir)
          self.log_dir = os.path.join(build_dir, Environment.log_dir)
          os.makedirs(self.scratch_dir, exist_ok=True)
          os.makedirs(self.log_dir, exist_ok=True)
          try:
              self.coredata = coredata.load(self.get_build_dir())
              self.first_invocation = False
          except FileNotFoundError:
              # WARNING: Don't use any values from coredata in __init__. It gets
              # re-initialized with project options by the interpreter during
              # build file parsing.
              self.coredata = coredata.CoreData(options)
              # Used by the regenchecker script, which runs meson
              self.coredata.meson_command = mesonlib.meson_command
              self.first_invocation = True
          if self.coredata.cross_file:
              self.cross_info = CrossBuildInfo(self.coredata.cross_file)
          else:
              self.cross_info = None
          self.cmd_line_options = options
          self.original_cmd_line_args = original_cmd_line_args
  
          # List of potential compilers.
          if mesonlib.is_windows():
              self.default_c = ['cl', 'cc', 'gcc', 'clang']
              self.default_cpp = ['cl', 'c++', 'g++', 'clang++']
          else:
              self.default_c = ['cc', 'gcc', 'clang']
              self.default_cpp = ['c++', 'g++', 'clang++']
          if mesonlib.is_windows():
              self.default_cs = ['csc', 'mcs']
          else:
              self.default_cs = ['mcs', 'csc']
          self.default_objc = ['cc']
          self.default_objcpp = ['c++']
          self.default_fortran = ['gfortran', 'g95', 'f95', 'f90', 'f77', 'ifort']
          self.default_rust = ['rustc']
          self.default_static_linker = ['ar']
          self.vs_static_linker = ['lib']
          self.gcc_static_linker = ['gcc-ar']
          self.clang_static_linker = ['llvm-ar']
  
          # Various prefixes and suffixes for import libraries, shared libraries,
          # static libraries, and executables.
          # Versioning is added to these names in the backends as-needed.
          cross = self.is_cross_build()
          if (not cross and mesonlib.is_windows()) \
                  or (cross and self.cross_info.has_host() and self.cross_info.config['host_machine']['system'] == 'windows'):
              self.exe_suffix = 'exe'
              self.object_suffix = 'obj'
              self.win_libdir_layout = True
          elif (not cross and mesonlib.is_cygwin()) \
                  or (cross and self.cross_info.has_host() and self.cross_info.config['host_machine']['system'] == 'cygwin'):
              self.exe_suffix = 'exe'
              self.object_suffix = 'o'
              self.win_libdir_layout = True
          else:
              self.exe_suffix = ''
              self.object_suffix = 'o'
              self.win_libdir_layout = False
          if 'STRIP' in os.environ:
              self.native_strip_bin = shlex.split(os.environ['STRIP'])
          else:
              self.native_strip_bin = ['strip']
  
      def is_cross_build(self):
          return self.cross_info is not None
  
      def dump_coredata(self):
          return coredata.save(self.coredata, self.get_build_dir())
  
      def get_script_dir(self):
          import mesonbuild.scripts
          return os.path.dirname(mesonbuild.scripts.__file__)
  
      def get_log_dir(self):
          return self.log_dir
  
      def get_coredata(self):
          return self.coredata
  
      def get_build_command(self, unbuffered=False):
          cmd = mesonlib.meson_command[:]
          if unbuffered and 'python' in cmd[0]:
              cmd.insert(1, '-u')
          return cmd
  
      def is_header(self, fname):
          return is_header(fname)
  
      def is_source(self, fname):
          return is_source(fname)
  
      def is_assembly(self, fname):
          return is_assembly(fname)
  
      def is_llvm_ir(self, fname):
          return is_llvm_ir(fname)
  
      def is_object(self, fname):
          return is_object(fname)
  
      def is_library(self, fname):
          return is_library(fname)
  
      def had_argument_for(self, option):
          trial1 = coredata.get_builtin_option_cmdline_name(option)
          trial2 = '-D' + option
          previous_is_plaind = False
          for i in self.original_cmd_line_args:
              if i.startswith(trial1) or i.startswith(trial2):
                  return True
              if previous_is_plaind and i.startswith(option):
                  return True
              previous_is_plaind = i == '-D'
          return False
  
      def merge_options(self, options):
          for (name, value) in options.items():
              if name not in self.coredata.user_options:
                  self.coredata.user_options[name] = value
              else:
                  oldval = self.coredata.user_options[name]
                  if type(oldval) != type(value):
                      self.coredata.user_options[name] = value
  
      @staticmethod
      def get_gnu_compiler_defines(compiler):
          """
          Detect GNU compiler platform type (Apple, MinGW, Unix)
          """
          # Arguments to output compiler pre-processor defines to stdout
          # gcc, g++, and gfortran all support these arguments
          args = compiler + ['-E', '-dM', '-']
          p, output, error = Popen_safe(args, write='', stdin=subprocess.PIPE)
          if p.returncode != 0:
              raise EnvironmentException('Unable to detect GNU compiler type:\n' + output + error)
          # Parse several lines of the type:
          # `#define ___SOME_DEF some_value`
          # and extract `___SOME_DEF`
          defines
  
  [... the rest is omitted ...]
files: mesonbuild/environment.py
files: # Copyright 2012-2018 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, Popen_safe, listify, extract_as_list, has_path_sep
  from .dependencies import ExternalProgram
  from .dependencies import InternalDependency, Dependency, NotFoundDependency, DependencyException
  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
  from .interpreterbase import FeatureNew, FeatureDeprecated, FeatureNewKwargs
  from .modules import ModuleReturnValue
  
  import os, sys, shutil, uuid
  import re, shlex
  from collections import namedtuple
  from pathlib import PurePath
  
  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 ObjectHolder:
      def __init__(self, obj):
          self.held_object = obj
  
      def __repr__(self):
          return '<Holder: {!r}>'.format(self.held_object)
  
  
  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, source_dir, build_dir, subdir, mesonintrospect, in_builddir=False, check=False):
          super().__init__()
          if not isinstance(cmd, ExternalProgram):
              raise AssertionError('BUG: RunProcess must be passed an ExternalProgram')
          pc, self.stdout, self.stderr = self.run_command(cmd, args, 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, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check=False):
          command_array = cmd.get_command() + args
          env = {'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(env)
          mlog.debug('Running command:', ' '.join(command_array))
          try:
              p, o, e = Popen_safe(command_array, env=child_env, cwd=cwd)
              mlog.debug('--- stdout----')
              mlog.debug(o)
              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_method(self, args, kwargs):
          return self.stderr
  
  class ConfigureFileHolder(InterpreterObject, ObjectHolder):
  
      def __init__(self, subdir, sourcename, targetname, configuration_data):
          InterpreterObject.__init__(self)
          ObjectHolder.__init__(self, build.ConfigureFile(subdir, sourcename,
                                                          targetname, configuration_data))
  
  
  class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder):
      def __init__(self):
          MutableInterpreterObject.__init__(self)
          ObjectHolder.__init__(self, build.EnvironmentVariables())
          self.methods.update({'set': self.set_method,
                               'append': self.append_method,
                               'prepend': self.prepend_method,
                               })
  
      def __repr__(self):
          repr_str = "<{0}: {1}>"
          return repr_str.format(self.__class__.__name__, self.held_object.envvars)
  
      def add_var(self, method, args, kwargs):
          if not isinstance(kwargs.get("separator", ""), str):
              raise InterpreterException("EnvironmentVariablesHolder methods 'separator'"
                                         " argument needs to be a string.")
          if len(args) < 2:
              raise InterpreterException("EnvironmentVariablesHolder methods require at least"
                                         "2 arguments, first is the name of the variable and"
                                         " following one are values")
          self.held_object.envvars.append((method, args[0], args[1:], kwargs))
  
      @stringArgs
      @permittedKwargs({'separator'})
      def set_method(self, args, kwargs):
          self.add_var(self.held_object.set, args, kwargs)
  
      @stringArgs
      @permittedKwargs({'separator'})
      def append_method(self, args, kwargs):
          self.add_var(self.held_object.append, args, kwargs)
  
      @stringArgs
      @permittedKwargs({'separator'})
      def prepend_method(self, args, kwargs):
          self.add_var(self.held_object.prepend, args, kwargs)
  
  
  class ConfigurationDataHolder(MutableInterpreterObject, ObjectHolder):
      def __init__(self):
          MutableInterpreterObject.__init__(self)
          self.used = False # These objects become immutable after use in configure_file.
          ObjectHolder.__init__(self, build.ConfigurationData())
          self.methods.update({'set': self.set_method,
                               'set10': self.set10_method,
                               'set_quoted': self.set_quoted_method,
                               'has': self.has_method,
                               'get': self.get_method,
                               'get_unquoted': self.get_unquoted_method,
                               'merge_from': self.merge_from_method,
                               })
  
      def is_used(self):
          return self.used
  
      def mark_used(self):
          self.used = True
  
      def validate_args(self, args, kwargs):
          if len(args) == 1 and isinstance(args[0], list) and len(args[0]) == 2:
              mlog.log(mlog.red('DEPRECATION:'),
                       '''Passing a list as the single argument to configuration_data.set is deprecated.
  This will become a hard error in the future''')
              args = args[0]
  
          if len(args) != 2:
              raise InterpreterException("Configuration set requires 2 arguments.")
          if self.used:
              raise InterpreterException("Can not set values on configuration object that has been used.")
          name = args[0]
          val = args[1]
          desc = kwargs.get('description', None)
          if not isinstance(name, str):
              raise InterpreterException("First argument to set must be a string.")
          if desc is not None and not isinstance(desc, str):
              raise InterpreterException('Description must be a string.')
  
          return name, val, desc
  
      @noArgsFlattening
      def set_method(self, args, kwargs):
          (name, val, desc) = self.validate_args(args, kwargs)
          self.held_object.values[name] = (val, desc)
  
      def set_quoted_method(self, args, kwargs):
          (name, val, desc) = self.validate_args(args, kwargs)
          if not isinstance(val, str):
              raise InterpreterException("Second argument to set_quoted must be a string.")
          escaped_val = '\\"'.join(val.split('"'))
          self.held_object.values[name] = ('"' + escaped_val + '"', desc)
  
      def set10_method(self, args, kwargs):
          (name, val, desc) = self.validate_args(args, kwargs)
          if val:
              self.held_object.values[name] = (1, desc)
          else:
              self.held_object.values[name] = (0, desc)
  
      def has_method(self, args, kwargs):
          return args[0] in self.held_object.values
  
      @FeatureNew('configuration_data.get()', '0.38.0')
      @noArgsFlattening
      def get_method(self, args, kwargs):
          if len(args) < 1 or len(args) > 2:
              raise InterpreterException('Get method takes one or two arguments.')
          name = args[0]
          if name in self.held_object:
              return self.held_object.get(name)[0]
          if len(args) > 1:
              return args[1]
          raise InterpreterException('Entry %s not in configuration data.' % name)
  
      @FeatureNew('get_unquoted', '0.44.0')
      def get_unquoted_method(self, args, kwargs):
          if len(args) < 1 or len(args) > 2:
              raise InterpreterException('Get method takes one or two arguments.')
          name = args[0]
          if name in self.held_object:
              val = self.held_object.get(name)[0]
          elif len(args) > 1:
              val = args[1]
          else:
              raise InterpreterException('Entry %s not in configuration data.' % name)
          if val[0] == '"' and val[-1] == '"':
              return val[1:-1]
          return val
  
      def get(self, name):
          return self.held_object.values[name] # (val, desc)
  
      def keys(self):
          return self.held_object.values.keys()
  
      def merge_from_method(self, args, kwargs):
          if len(args) != 1:
              raise InterpreterException('Merge_from takes one positional argument.')
          from_object = args[0]
          if not isinstance(from_object, ConfigurationDataHolder):
              raise InterpreterException('Merge_from argument must be a configuration data object.')
          from_object = from_object.held_object
          for k, v in from_object.values.items():
              self.held_object.values[k] = v
  
  # Interpreter objects can not be pickled so we must have
  # these wrappers.
  
  class DependencyHolder(InterpreterObject, ObjectHolder):
      def __init__(self, dep):
          InterpreterObject.__init__(self)
          ObjectHolder.__init__(self, dep)
          self.methods.update({'found': self.found_method,
                               'type_name': self.type_name_method,
                               'version': self.version_method,
                               'get_pkgconfig_variable': self.pkgconfig_method,
                               'get_configtool_variable': self.configtool_method,
                               'partial_dependency': self.partial_dependency_method,
                               })
  
      @noPosargs
      @permittedKwargs({})
      def type_name_method(self, args, kwargs):
          return self.held_object.type_name
  
      @noPosargs
      @permittedKwargs({})
      def found_method(self, args, kwargs):
          if self.held_object.type_name == 'internal':
              return True
          return self.held_object.found()
  
      @noPosargs
      @permittedKwargs({})
      def version_method(self, args, kwargs):
          return self.held_object.get_version()
  
      @permittedKwargs({'define_variable'})
      def pkgconfig_method(self, args, kwargs):
          args = listify(args)
          if len(args) != 1:
              raise InterpreterException('get_pkgconfig_variable takes exactly one argument.')
          varname = args[0]
          if not isinstance(varname, str):
              raise InterpreterException('Variable name must be a string.')
          return self.held_object.get_pkgconfig_variable(varname, kwargs)
  
      @FeatureNew('get_configtool_variable', '0.44.0')
      @permittedKwargs({})
      def configtool_method(self, args, kwargs):
          args = listify(args)
          if len(args) != 1:
              raise InterpreterException('get_configtool_variable takes exactly one argument.')
          varname = args[0]
          if not isinstance(varname, str):
              raise InterpreterException('Variable name must be a string.')
          return self.held_object.get_configtool_variable(varname)
  
      @FeatureNew('dep.partial_dependency', '0.46.0')
      @noPosargs
      @permittedKwargs(permitted_method_kwargs['partial_dependency'])
      def partial_dependency_method(self, args, kwargs):
          return DependencyHolder(self.held_object.get_partial_dependency(**kwargs))
  
  class InternalDependencyHolder(InterpreterObject, ObjectHolder):
      def __init__(self, dep):
          InterpreterObject.__init__(self)
          ObjectHolder.__init__(self, dep)
          self.methods.update({'found': self.found_method,
                               'version': self.version_method,
                               'partial_dependency': self.partial_dependency_method,
                               })
  
      @noPosargs
      @permittedKwargs({})
      def found_method(self, args, kwargs):
          return True
  
      @noPosargs
      @permittedKwargs({})
      def version_method(self, args, kwargs):
          return self.held_object.get_version()
  
      @FeatureNew('dep.partial_dependency', '0.46.0')
      @noPosargs
      @permittedKwargs(permitted_method_kwargs['partial_dependency'])
      def partial_dependency_method(self, args, kwargs):
          return DependencyHolder(self.held_object.get_partial_dependency(**kwargs))
  
  class ExternalProgramHolder(InterpreterObject, ObjectHolder):
      def __init__(self, ep):
          InterpreterObject.__init__(self)
          ObjectHolder.__init__(self, ep)
          self.methods.update({'found': self.found_method,
                               'path': self.path_method})
  
      @noPosargs
      @permittedKwargs({})
      def found_method(self, args, kwargs):
          return self.found()
  
      @noPosargs
      @permittedKwargs({})
      def path_method(self, args, kwargs):
          return self.held_object.get_path()
  
      def found(self):
          return self.held_object.found()
  
      def get_command(self):
          return self.held_object.get_command()
  
      def get_name(self):
          return self.held_object.get_name()
  
  class ExternalLibraryHolder(InterpreterObject, ObjectHolder):
      def __init__(self, el):
          InterpreterObject.__init__(self)
          ObjectHolder.__init__(self, el)
          self.methods.update({'found': self.found_method,
                               'partial_dependency': self.partial_dependency_method,
                               })
  
      def found(self):
          return self.held_object.found()
  
      @noPosargs
      @permittedKwargs({})
      def found_method(self, args, kwargs):
          return self.found()
  
      def get_name(self):
          return self.held_object.name
  
      def get_compile_args(self):
          return self.held_object.get_compile_args()
  
      def get_link_args(self):
          return self.held_object.get_link_args()
  
      def get_exe_args(self):
          return self.held_object.get_exe_args()
  
      @FeatureNew('dep.partial_dependency', '0.46.0')
      @noPosargs
      @permittedKwargs(permitted_method_kwargs['partial_dependency'])
      def partial_dependency_method(self, args, kwargs):
          return DependencyHolder(self.held_object.get_partial_dependency(**kwargs))
  
  class GeneratorHolder(InterpreterObject, ObjectHolder):
      def __init__(self, interpreter,
  
  [... the rest is omitted ...]
files: mesonbuild/interpreter.py
files: # Copyright 2012-2018 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, Popen_safe, listify, extract_as_list, has_path_sep
  from .dependencies import ExternalProgram
  from .dependencies import InternalDependency, Dependency, NotFoundDependency, DependencyException
  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
  from .interpreterbase import FeatureNew, FeatureDeprecated, FeatureNewKwargs
  from .modules import ModuleReturnValue
  
  import os, sys, shutil, uuid
  import re, shlex
  from collections import namedtuple
  from pathlib import PurePath
  
  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 ObjectHolder:
      def __init__(self, obj):
          self.held_object = obj
  
      def __repr__(self):
          return '<Holder: {!r}>'.format(self.held_object)
  
  
  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, source_dir, build_dir, subdir, mesonintrospect, in_builddir=False, check=False):
          super().__init__()
          if not isinstance(cmd, ExternalProgram):
              raise AssertionError('BUG: RunProcess must be passed an ExternalProgram')
          pc, self.stdout, self.stderr = self.run_command(cmd, args, 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, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check=False):
          command_array = cmd.get_command() + args
          env = {'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(env)
          mlog.debug('Running command:', ' '.join(command_array))
          try:
              p, o, e = Popen_safe(command_array, env=child_env, cwd=cwd)
              mlog.debug('--- stdout----')
              mlog.debug(o)
              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_method(self, args, kwargs):
          return self.stderr
  
  class ConfigureFileHolder(InterpreterObject, ObjectHolder):
  
      def __init__(self, subdir, sourcename, targetname, configuration_data):
          InterpreterObject.__init__(self)
          ObjectHolder.__init__(self, build.ConfigureFile(subdir, sourcename,
                                                          targetname, configuration_data))
  
  
  class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder):
      def __init__(self):
          MutableInterpreterObject.__init__(self)
          ObjectHolder.__init__(self, build.EnvironmentVariables())
          self.methods.update({'set': self.set_method,
                               'append': self.append_method,
                               'prepend': self.prepend_method,
                               })
  
      def __repr__(self):
          repr_str = "<{0}: {1}>"
          return repr_str.format(self.__class__.__name__, self.held_object.envvars)
  
      def add_var(self, method, args, kwargs):
          if not isinstance(kwargs.get("separator", ""), str):
              raise InterpreterException("EnvironmentVariablesHolder methods 'separator'"
                                         " argument needs to be a string.")
          if len(args) < 2:
              raise InterpreterException("EnvironmentVariablesHolder methods require at least"
                                         "2 arguments, first is the name of the variable and"
                                         " following one are values")
          self.held_object.envvars.append((method, args[0], args[1:], kwargs))
  
      @stringArgs
      @permittedKwargs({'separator'})
      def set_method(self, args, kwargs):
          self.add_var(self.held_object.set, args, kwargs)
  
      @stringArgs
      @permittedKwargs({'separator'})
      def append_method(self, args, kwargs):
          self.add_var(self.held_object.append, args, kwargs)
  
      @stringArgs
      @permittedKwargs({'separator'})
      def prepend_method(self, args, kwargs):
          self.add_var(self.held_object.prepend, args, kwargs)
  
  
  class ConfigurationDataHolder(MutableInterpreterObject, ObjectHolder):
      def __init__(self):
          MutableInterpreterObject.__init__(self)
          self.used = False # These objects become immutable after use in configure_file.
          ObjectHolder.__init__(self, build.ConfigurationData())
          self.methods.update({'set': self.set_method,
                               'set10': self.set10_method,
                               'set_quoted': self.set_quoted_method,
                               'has': self.has_method,
                               'get': self.get_method,
                               'get_unquoted': self.get_unquoted_method,
                               'merge_from': self.merge_from_method,
                               })
  
      def is_used(self):
          return self.used
  
      def mark_used(self):
          self.used = True
  
      def validate_args(self, args, kwargs):
          if len(args) == 1 and isinstance(args[0], list) and len(args[0]) == 2:
              mlog.log(mlog.red('DEPRECATION:'),
                       '''Passing a list as the single argument to configuration_data.set is deprecated.
  This will become a hard error in the future''')
              args = args[0]
  
          if len(args) != 2:
              raise InterpreterException("Configuration set requires 2 arguments.")
          if self.used:
              raise InterpreterException("Can not set values on configuration object that has been used.")
          name = args[0]
          val = args[1]
          desc = kwargs.get('description', None)
          if not isinstance(name, str):
              raise InterpreterException("First argument to set must be a string.")
          if desc is not None and not isinstance(desc, str):
              raise InterpreterException('Description must be a string.')
  
          return name, val, desc
  
      @noArgsFlattening
      def set_method(self, args, kwargs):
          (name, val, desc) = self.validate_args(args, kwargs)
          self.held_object.values[name] = (val, desc)
  
      def set_quoted_method(self, args, kwargs):
          (name, val, desc) = self.validate_args(args, kwargs)
          if not isinstance(val, str):
              raise InterpreterException("Second argument to set_quoted must be a string.")
          escaped_val = '\\"'.join(val.split('"'))
          self.held_object.values[name] = ('"' + escaped_val + '"', desc)
  
      def set10_method(self, args, kwargs):
          (name, val, desc) = self.validate_args(args, kwargs)
          if val:
              self.held_object.values[name] = (1, desc)
          else:
              self.held_object.values[name] = (0, desc)
  
      def has_method(self, args, kwargs):
          return args[0] in self.held_object.values
  
      @FeatureNew('configuration_data.get()', '0.38.0')
      @noArgsFlattening
      def get_method(self, args, kwargs):
          if len(args) < 1 or len(args) > 2:
              raise InterpreterException('Get method takes one or two arguments.')
          name = args[0]
          if name in self.held_object:
              return self.held_object.get(name)[0]
          if len(args) > 1:
              return args[1]
          raise InterpreterException('Entry %s not in configuration data.' % name)
  
      @FeatureNew('get_unquoted', '0.44.0')
      def get_unquoted_method(self, args, kwargs):
          if len(args) < 1 or len(args) > 2:
              raise InterpreterException('Get method takes one or two arguments.')
          name = args[0]
          if name in self.held_object:
              val = self.held_object.get(name)[0]
          elif len(args) > 1:
              val = args[1]
          else:
              raise InterpreterException('Entry %s not in configuration data.' % name)
          if val[0] == '"' and val[-1] == '"':
              return val[1:-1]
          return val
  
      def get(self, name):
          return self.held_object.values[name] # (val, desc)
  
      def keys(self):
          return self.held_object.values.keys()
  
      def merge_from_method(self, args, kwargs):
          if len(args) != 1:
              raise InterpreterException('Merge_from takes one positional argument.')
          from_object = args[0]
          if not isinstance(from_object, ConfigurationDataHolder):
              raise InterpreterException('Merge_from argument must be a configuration data object.')
          from_object = from_object.held_object
          for k, v in from_object.values.items():
              self.held_object.values[k] = v
  
  # Interpreter objects can not be pickled so we must have
  # these wrappers.
  
  class DependencyHolder(InterpreterObject, ObjectHolder):
      def __init__(self, dep):
          InterpreterObject.__init__(self)
          ObjectHolder.__init__(self, dep)
          self.methods.update({'found': self.found_method,
                               'type_name': self.type_name_method,
                               'version': self.version_method,
                               'get_pkgconfig_variable': self.pkgconfig_method,
                               'get_configtool_variable': self.configtool_method,
                               'partial_dependency': self.partial_dependency_method,
                               })
  
      @noPosargs
      @permittedKwargs({})
      def type_name_method(self, args, kwargs):
          return self.held_object.type_name
  
      @noPosargs
      @permittedKwargs({})
      def found_method(self, args, kwargs):
          if self.held_object.type_name == 'internal':
              return True
          return self.held_object.found()
  
      @noPosargs
      @permittedKwargs({})
      def version_method(self, args, kwargs):
          return self.held_object.get_version()
  
      @permittedKwargs({'define_variable'})
      def pkgconfig_method(self, args, kwargs):
          args = listify(args)
          if len(args) != 1:
              raise InterpreterException('get_pkgconfig_variable takes exactly one argument.')
          varname = args[0]
          if not isinstance(varname, str):
              raise InterpreterException('Variable name must be a string.')
          return self.held_object.get_pkgconfig_variable(varname, kwargs)
  
      @FeatureNew('get_configtool_variable', '0.44.0')
      @permittedKwargs({})
      def configtool_method(self, args, kwargs):
          args = listify(args)
          if len(args) != 1:
              raise InterpreterException('get_configtool_variable takes exactly one argument.')
          varname = args[0]
          if not isinstance(varname, str):
              raise InterpreterException('Variable name must be a string.')
          return self.held_object.get_configtool_variable(varname)
  
      @FeatureNew('dep.partial_dependency', '0.46.0')
      @noPosargs
      @permittedKwargs(permitted_method_kwargs['partial_dependency'])
      def partial_dependency_method(self, args, kwargs):
          return DependencyHolder(self.held_object.get_partial_dependency(**kwargs))
  
  class InternalDependencyHolder(InterpreterObject, ObjectHolder):
      def __init__(self, dep):
          InterpreterObject.__init__(self)
          ObjectHolder.__init__(self, dep)
          self.methods.update({'found': self.found_method,
                               'version': self.version_method,
                               'partial_dependency': self.partial_dependency_method,
                               })
  
      @noPosargs
      @permittedKwargs({})
      def found_method(self, args, kwargs):
          return True
  
      @noPosargs
      @permittedKwargs({})
      def version_method(self, args, kwargs):
          return self.held_object.get_version()
  
      @FeatureNew('dep.partial_dependency', '0.46.0')
      @noPosargs
      @permittedKwargs(permitted_method_kwargs['partial_dependency'])
      def partial_dependency_method(self, args, kwargs):
          return DependencyHolder(self.held_object.get_partial_dependency(**kwargs))
  
  class ExternalProgramHolder(InterpreterObject, ObjectHolder):
      def __init__(self, ep):
          InterpreterObject.__init__(self)
          ObjectHolder.__init__(self, ep)
          self.methods.update({'found': self.found_method,
                               'path': self.path_method})
  
      @noPosargs
      @permittedKwargs({})
      def found_method(self, args, kwargs):
          return self.found()
  
      @noPosargs
      @permittedKwargs({})
      def path_method(self, args, kwargs):
          return self.held_object.get_path()
  
      def found(self):
          return self.held_object.found()
  
      def get_command(self):
          return self.held_object.get_command()
  
      def get_name(self):
          return self.held_object.get_name()
  
  class ExternalLibraryHolder(InterpreterObject, ObjectHolder):
      def __init__(self, el):
          InterpreterObject.__init__(self)
          ObjectHolder.__init__(self, el)
          self.methods.update({'found': self.found_method,
                               'partial_dependency': self.partial_dependency_method,
                               })
  
      def found(self):
          return self.held_object.found()
  
      @noPosargs
      @permittedKwargs({})
      def found_method(self, args, kwargs):
          return self.found()
  
      def get_name(self):
          return self.held_object.name
  
      def get_compile_args(self):
          return self.held_object.get_compile_args()
  
      def get_link_args(self):
          return self.held_object.get_link_args()
  
      def get_exe_args(self):
          return self.held_object.get_exe_args()
  
      @FeatureNew('dep.partial_dependency', '0.46.0')
      @noPosargs
      @permittedKwargs(permitted_method_kwargs['partial_dependency'])
      def partial_dependency_method(self, args, kwargs):
          return DependencyHolder(self.held_object.get_partial_dependency(**kwargs))
  
  class GeneratorHolder(InterpreterObject, ObjectHolder):
      def __init__(self, interpreter,
  
  [... 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.
  
  import os
  import sys
  import argparse
  from . import (coredata, mesonlib, build)
  
  def buildparser():
      parser = argparse.ArgumentParser(prog='meson configure')
      coredata.register_builtin_arguments(parser)
  
      parser.add_argument('directory', nargs='*')
      parser.add_argument('--clearcache', action='store_true', default=False,
                          help='Clear cached state (e.g. found dependencies)')
      return parser
  
  
  class ConfException(mesonlib.MesonException):
      pass
  
  
  class Conf:
      def __init__(self, build_dir):
          self.build_dir = build_dir
          if not os.path.isdir(os.path.join(build_dir, 'meson-private')):
              raise ConfException('Directory %s does not seem to be a Meson build directory.' % build_dir)
          self.build = build.load(self.build_dir)
          self.coredata = coredata.load(self.build_dir)
  
      def clear_cache(self):
          self.coredata.deps = {}
  
      def set_options(self, options):
          self.coredata.set_options(options)
  
      def save(self):
          # Only called if something has changed so overwrite unconditionally.
          coredata.save(self.coredata, self.build_dir)
          # We don't write the build file because any changes to it
          # are erased when Meson is executed the next time, i.e. when
          # Ninja is run.
  
      @staticmethod
      def print_aligned(arr):
          def make_lower_case(val):
              if isinstance(val, bool):
                  return str(val).lower()
              elif isinstance(val, list):
                  return [make_lower_case(i) for i in val]
              else:
                  return str(val)
  
          if not arr:
              return
  
          titles = {'name': 'Option', 'descr': 'Description', 'value': 'Current Value', 'choices': 'Possible Values'}
  
          name_col = [titles['name'], '-' * len(titles['name'])]
          value_col = [titles['value'], '-' * len(titles['value'])]
          choices_col = [titles['choices'], '-' * len(titles['choices'])]
          descr_col = [titles['descr'], '-' * len(titles['descr'])]
  
          choices_found = False
          for opt in arr:
              name_col.append(opt['name'])
              descr_col.append(opt['descr'])
              if isinstance(opt['value'], list):
                  value_col.append('[{0}]'.format(', '.join(make_lower_case(opt['value']))))
              else:
                  value_col.append(make_lower_case(opt['value']))
              if opt['choices']:
                  choices_found = True
                  choices_col.append('[{0}]'.format(', '.join(make_lower_case(opt['choices']))))
              else:
                  choices_col.append('')
  
          col_widths = (max([len(i) for i in name_col], default=0),
                        max([len(i) for i in value_col], default=0),
                        max([len(i) for i in choices_col], default=0),
                        max([len(i) for i in descr_col], default=0))
  
          for line in zip(name_col, value_col, choices_col, descr_col):
              if choices_found:
                  print('  {0:{width[0]}} {1:{width[1]}} {2:{width[2]}} {3:{width[3]}}'.format(*line, width=col_widths))
              else:
                  print('  {0:{width[0]}} {1:{width[1]}} {3:{width[3]}}'.format(*line, width=col_widths))
  
      def print_conf(self):
          print('Core properties:')
          print('  Source dir', self.build.environment.source_dir)
          print('  Build dir ', self.build.environment.build_dir)
          print('\nCore options:')
          carr = []
          for key in ['buildtype', 'warning_level', 'werror', 'strip', 'unity', 'default_library', 'install_umask']:
              carr.append({'name': key,
                           'descr': coredata.get_builtin_option_description(key),
                           'value': self.coredata.get_builtin_option(key),
                           'choices': coredata.get_builtin_option_choices(key)})
          self.print_aligned(carr)
          print('\nBackend options:')
          if not self.coredata.backend_options:
              print('  No backend options\n')
          else:
              bearr = []
              for k in sorted(self.coredata.backend_options):
                  o = self.coredata.backend_options[k]
                  bearr.append({'name': k, 'descr': o.description, 'value': o.value, 'choices': ''})
              self.print_aligned(bearr)
          print('\nBase options:')
          if not self.coredata.base_options:
              print('  No base options\n')
          else:
              coarr = []
              for k in sorted(self.coredata.base_options):
                  o = self.coredata.base_options[k]
                  coarr.append({'name': k, 'descr': o.description, 'value': o.value, 'choices': o.choices})
              self.print_aligned(coarr)
          print('\nCompiler options:')
          if not self.coredata.compiler_options:
              print('  No compiler options\n')
          else:
              coarr = []
              for k in self.coredata.compiler_options:
                  o = self.coredata.compiler_options[k]
                  coarr.append({'name': k, 'descr': o.description, 'value': o.value, 'choices': ''})
              self.print_aligned(coarr)
          print('\nDirectories:')
          parr = []
          for key in ['prefix',
                      'libdir',
                      'libexecdir',
                      'bindir',
                      'sbindir',
                      'includedir',
                      'datadir',
                      'mandir',
                      'infodir',
                      'localedir',
                      'sysconfdir',
                      'localstatedir',
                      'sharedstatedir',
                      ]:
              parr.append({'name': key,
                           'descr': coredata.get_builtin_option_description(key),
                           'value': self.coredata.get_builtin_option(key),
                           'choices': coredata.get_builtin_option_choices(key)})
          self.print_aligned(parr)
          print('\nProject options:')
          if not self.coredata.user_options:
              print('  This project does not have any options')
          else:
              optarr = []
              for key in sorted(self.coredata.user_options):
                  opt = self.coredata.user_options[key]
                  if (opt.choices is None) or (not opt.choices):
                      # Zero length list or string
                      choices = ''
                  else:
                      choices = opt.choices
                  optarr.append({'name': key,
                                 'descr': opt.description,
                                 'value': opt.value,
                                 'choices': choices})
              self.print_aligned(optarr)
          print('\nTesting options:')
          tarr = []
          for key in ['stdsplit', 'errorlogs']:
              tarr.append({'name': key,
                           'descr': coredata.get_builtin_option_description(key),
                           'value': self.coredata.get_builtin_option(key),
                           'choices': coredata.get_builtin_option_choices(key)})
          self.print_aligned(tarr)
  
  
  def run(args):
      args = mesonlib.expand_arguments(args)
      if not args:
          args = [os.getcwd()]
      options = buildparser().parse_args(args)
      coredata.filter_builtin_options(options, args)
      if len(options.directory) > 1:
          print('%s <build directory>' % args[0])
          print('If you omit the build directory, the current directory is substituted.')
          return 1
      if not options.directory:
          builddir = os.getcwd()
      else:
          builddir = options.directory[0]
      try:
          c = Conf(builddir)
          save = False
          if len(options.projectoptions) > 0:
              c.set_options(options.projectoptions)
              save = True
          elif options.clearcache:
              c.clear_cache()
              save = True
          else:
              c.print_conf()
          if save:
              c.save()
      except ConfException as e:
          print('Meson configurator encountered an error:')
          raise e
      return 0
  
  
  if __name__ == '__main__':
      sys.exit(run(sys.argv[1:]))
  
  
  [... the rest is omitted ...]
files: mesonbuild/mconf.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.
  
  import os
  import sys
  import argparse
  from . import (coredata, mesonlib, build)
  
  def buildparser():
      parser = argparse.ArgumentParser(prog='meson configure')
      coredata.register_builtin_arguments(parser)
  
      parser.add_argument('directory', nargs='*')
      parser.add_argument('--clearcache', action='store_true', default=False,
                          help='Clear cached state (e.g. found dependencies)')
      return parser
  
  
  class ConfException(mesonlib.MesonException):
      pass
  
  
  class Conf:
      def __init__(self, build_dir):
          self.build_dir = build_dir
          if not os.path.isdir(os.path.join(build_dir, 'meson-private')):
              raise ConfException('Directory %s does not seem to be a Meson build directory.' % build_dir)
          self.build = build.load(self.build_dir)
          self.coredata = coredata.load(self.build_dir)
  
      def clear_cache(self):
          self.coredata.deps = {}
  
      def save(self):
          # Only called if something has changed so overwrite unconditionally.
          coredata.save(self.coredata, self.build_dir)
          # We don't write the build file because any changes to it
          # are erased when Meson is executed the next time, i.e. when
          # Ninja is run.
  
      @staticmethod
      def print_aligned(arr):
          def make_lower_case(val):
              if isinstance(val, bool):
                  return str(val).lower()
              elif isinstance(val, list):
                  return [make_lower_case(i) for i in val]
              else:
                  return str(val)
  
          if not arr:
              return
  
          titles = {'name': 'Option', 'descr': 'Description', 'value': 'Current Value', 'choices': 'Possible Values'}
  
          name_col = [titles['name'], '-' * len(titles['name'])]
          value_col = [titles['value'], '-' * len(titles['value'])]
          choices_col = [titles['choices'], '-' * len(titles['choices'])]
          descr_col = [titles['descr'], '-' * len(titles['descr'])]
  
          choices_found = False
          for opt in arr:
              name_col.append(opt['name'])
              descr_col.append(opt['descr'])
              if isinstance(opt['value'], list):
                  value_col.append('[{0}]'.format(', '.join(make_lower_case(opt['value']))))
              else:
                  value_col.append(make_lower_case(opt['value']))
              if opt['choices']:
                  choices_found = True
                  choices_col.append('[{0}]'.format(', '.join(make_lower_case(opt['choices']))))
              else:
                  choices_col.append('')
  
          col_widths = (max([len(i) for i in name_col], default=0),
                        max([len(i) for i in value_col], default=0),
                        max([len(i) for i in choices_col], default=0),
                        max([len(i) for i in descr_col], default=0))
  
          for line in zip(name_col, value_col, choices_col, descr_col):
              if choices_found:
                  print('  {0:{width[0]}} {1:{width[1]}} {2:{width[2]}} {3:{width[3]}}'.format(*line, width=col_widths))
              else:
                  print('  {0:{width[0]}} {1:{width[1]}} {3:{width[3]}}'.format(*line, width=col_widths))
  
      def set_options(self, options):
          for o in options:
              if '=' not in o:
                  raise ConfException('Value "%s" not of type "a=b".' % o)
              (k, v) = o.split('=', 1)
              if coredata.is_builtin_option(k):
                  self.coredata.set_builtin_option(k, v)
              elif k in self.coredata.backend_options:
                  tgt = self.coredata.backend_options[k]
                  tgt.set_value(v)
              elif k in self.coredata.user_options:
                  tgt = self.coredata.user_options[k]
                  tgt.set_value(v)
              elif k in self.coredata.compiler_options:
                  tgt = self.coredata.compiler_options[k]
                  tgt.set_value(v)
              elif k in self.coredata.base_options:
                  tgt = self.coredata.base_options[k]
                  tgt.set_value(v)
              else:
                  raise ConfException('Unknown option %s.' % k)
  
      def print_conf(self):
          print('Core properties:')
          print('  Source dir', self.build.environment.source_dir)
          print('  Build dir ', self.build.environment.build_dir)
          print('\nCore options:')
          carr = []
          for key in ['buildtype', 'warning_level', 'werror', 'strip', 'unity', 'default_library', 'install_umask']:
              carr.append({'name': key,
                           'descr': coredata.get_builtin_option_description(key),
                           'value': self.coredata.get_builtin_option(key),
                           'choices': coredata.get_builtin_option_choices(key)})
          self.print_aligned(carr)
          print('\nBackend options:')
          if not self.coredata.backend_options:
              print('  No backend options\n')
          else:
              bearr = []
              for k in sorted(self.coredata.backend_options):
                  o = self.coredata.backend_options[k]
                  bearr.append({'name': k, 'descr': o.description, 'value': o.value, 'choices': ''})
              self.print_aligned(bearr)
          print('\nBase options:')
          if not self.coredata.base_options:
              print('  No base options\n')
          else:
              coarr = []
              for k in sorted(self.coredata.base_options):
                  o = self.coredata.base_options[k]
                  coarr.append({'name': k, 'descr': o.description, 'value': o.value, 'choices': o.choices})
              self.print_aligned(coarr)
          print('\nCompiler options:')
          if not self.coredata.compiler_options:
              print('  No compiler options\n')
          else:
              coarr = []
              for k in self.coredata.compiler_options:
                  o = self.coredata.compiler_options[k]
                  coarr.append({'name': k, 'descr': o.description, 'value': o.value, 'choices': ''})
              self.print_aligned(coarr)
          print('\nDirectories:')
          parr = []
          for key in ['prefix',
                      'libdir',
                      'libexecdir',
                      'bindir',
                      'sbindir',
                      'includedir',
                      'datadir',
                      'mandir',
                      'infodir',
                      'localedir',
                      'sysconfdir',
                      'localstatedir',
                      'sharedstatedir',
                      ]:
              parr.append({'name': key,
                           'descr': coredata.get_builtin_option_description(key),
                           'value': self.coredata.get_builtin_option(key),
                           'choices': coredata.get_builtin_option_choices(key)})
          self.print_aligned(parr)
          print('\nProject options:')
          if not self.coredata.user_options:
              print('  This project does not have any options')
          else:
              optarr = []
              for key in sorted(self.coredata.user_options):
                  opt = self.coredata.user_options[key]
                  if (opt.choices is None) or (not opt.choices):
                      # Zero length list or string
                      choices = ''
                  else:
                      choices = opt.choices
                  optarr.append({'name': key,
                                 'descr': opt.description,
                                 'value': opt.value,
                                 'choices': choices})
              self.print_aligned(optarr)
          print('\nTesting options:')
          tarr = []
          for key in ['stdsplit', 'errorlogs']:
              tarr.append({'name': key,
                           'descr': coredata.get_builtin_option_description(key),
                           'value': self.coredata.get_builtin_option(key),
                           'choices': coredata.get_builtin_option_choices(key)})
          self.print_aligned(tarr)
  
  
  def run(args):
      args = mesonlib.expand_arguments(args)
      if not args:
          args = [os.getcwd()]
      options = buildparser().parse_args(args)
      coredata.filter_builtin_options(options, args)
      if len(options.directory) > 1:
          print('%s <build directory>' % args[0])
          print('If you omit the build directory, the current directory is substituted.')
          return 1
      if not options.directory:
          builddir = os.getcwd()
      else:
          builddir = options.directory[0]
      try:
          c = Conf(builddir)
          save = False
          if len(options.projectoptions) > 0:
              c.set_options(options.projectoptions)
              save = True
          elif options.clearcache:
              c.clear_cache()
              save = True
          else:
              c.print_conf()
          if save:
              c.save()
      except ConfException as e:
          print('Meson configurator encountered an error:')
          raise e
      return 0
  
  
  if __name__ == '__main__':
      sys.exit(run(sys.argv[1:]))
  
  
  [... the rest is omitted ...]
files: mesonbuild/mconf.py