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
  from . import (coredata, mesonlib, build)
  
  def add_arguments(parser):
      coredata.register_builtin_arguments(parser)
      parser.add_argument('builddir', nargs='?', default='.')
      parser.add_argument('--clearcache', action='store_true', default=False,
                          help='Clear cached state (e.g. found dependencies)')
  
  
  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
                  if isinstance(opt['choices'], list):
                      choices_col.append('[{0}]'.format(', '.join(make_lower_case(opt['choices']))))
                  else:
                      choices_col.append(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_options(self, title, options):
          print('\n{}:'.format(title))
          if not options:
              print('  No {}\n'.format(title.lower()))
          arr = []
          for k in sorted(options):
              o = options[k]
              d = o.description
              v = o.value
              c = o.choices
              if isinstance(o, coredata.UserUmaskOption):
                  v = format(v, '04o')
              arr.append({'name': k, 'descr': d, 'value': v, 'choices': c})
          self.print_aligned(arr)
  
      def print_conf(self):
          print('Core properties:')
          print('  Source dir', self.build.environment.source_dir)
          print('  Build dir ', self.build.environment.build_dir)
  
          dir_option_names = ['prefix',
                              'libdir',
                              'libexecdir',
                              'bindir',
                              'sbindir',
                              'includedir',
                              'datadir',
                              'mandir',
                              'infodir',
                              'localedir',
                              'sysconfdir',
                              'localstatedir',
                              'sharedstatedir']
          test_option_names = ['stdsplit',
                               'errorlogs']
          core_option_names = [k for k in self.coredata.builtins if k not in dir_option_names + test_option_names]
  
          dir_options = {k: o for k, o in self.coredata.builtins.items() if k in dir_option_names}
          test_options = {k: o for k, o in self.coredata.builtins.items() if k in test_option_names}
          core_options = {k: o for k, o in self.coredata.builtins.items() if k in core_option_names}
  
          self.print_options('Core options', core_options)
          self.print_options('Backend options', self.coredata.backend_options)
          self.print_options('Base options', self.coredata.base_options)
          self.print_options('Compiler options', self.coredata.compiler_options)
          self.print_options('Directories', dir_options)
          self.print_options('Project options', self.coredata.user_options)
          self.print_options('Testing options', test_options)
  
  
  def run(options):
      coredata.parse_cmd_line_options(options)
      builddir = os.path.abspath(os.path.realpath(options.builddir))
      try:
          c = Conf
  
  [... 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 argparse
  from . import (coredata, mesonlib, build)
  
  def buildparser():
      parser = argparse.ArgumentParser(prog='meson configure')
      coredata.register_builtin_arguments(parser)
  
      parser.add_argument('builddir', nargs='?', default='.')
      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
                  if isinstance(opt['choices'], list):
                      choices_col.append('[{0}]'.format(', '.join(make_lower_case(opt['choices']))))
                  else:
                      choices_col.append(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_options(self, title, options):
          print('\n{}:'.format(title))
          if not options:
              print('  No {}\n'.format(title.lower()))
          arr = []
          for k in sorted(options):
              o = options[k]
              d = o.description
              v = o.value
              c = o.choices
              if isinstance(o, coredata.UserUmaskOption):
                  v = format(v, '04o')
              arr.append({'name': k, 'descr': d, 'value': v, 'choices': c})
          self.print_aligned(arr)
  
      def print_conf(self):
          print('Core properties:')
          print('  Source dir', self.build.environment.source_dir)
          print('  Build dir ', self.build.environment.build_dir)
  
          dir_option_names = ['prefix',
                              'libdir',
                              'libexecdir',
                              'bindir',
                              'sbindir',
                              'includedir',
                              'datadir',
                              'mandir',
                              'infodir',
                              'localedir',
                              'sysconfdir',
                              'localstatedir',
                              'sharedstatedir']
          test_option_names = ['stdsplit',
                               'errorlogs']
          core_option_names = [k for k in self.coredata.builtins if k not in dir_option_names + test_option_names]
  
          dir_options = {k: o for k, o in self.coredata.builtins.items() if k in dir_option_names}
          test_options = {k: o for k, o in self.coredata.builtins.items() if k in test_option_names}
          core_options = {k: o for k, o in self.coredata.builtins.items() if k in core_option_names}
  
          self.print_options('Core options', core_options)
          self.print_options('Backend options', self.coredata.backend_options)
          self.print_options('Base options', self.coredata.base_options)
          self.print_options('Compiler options', self.coredata.compiler_options)
          self.print_options('Directories', dir_options)
          self.print_options('Project options', self.coredata.user_options)
          self.print_options('Testing options', test_options)
  
  
  def run(args):
      args = mesonlib.expand_arguments(args)
      options = buildparser
  
  [... the rest is omitted ...]
files: mesonbuild/mconf.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 sys
  import os.path
  import importlib
  import traceback
  import argparse
  
  from . import mesonlib
  from . import mlog
  from . import mconf, minit, minstall, mintro, msetup, mtest, rewriter
  from .mesonlib import MesonException
  from .environment import detect_msys2_arch
  from .wrap import wraptool
  
  
  class CommandLineParser:
      def __init__(self):
          self.commands = {}
          self.parser = argparse.ArgumentParser(prog='meson')
          self.subparsers = self.parser.add_subparsers(title='Commands',
                                                       description='If no command is specified it defaults to setup command.')
          self.add_command('setup', msetup.add_arguments, msetup.run,
                           help='Configure the project')
          self.add_command('configure', mconf.add_arguments, mconf.run,
                           help='Change project options',)
          self.add_command('install', minstall.add_arguments, minstall.run,
                           help='Install the project')
          self.add_command('introspect', mintro.add_arguments, mintro.run,
                           help='Introspect project')
          self.add_command('init', minit.add_arguments, minit.run,
                           help='Create a new project')
          self.add_command('test', mtest.add_arguments, mtest.run,
                           help='Run tests')
          self.add_command('rewrite', rewriter.add_arguments, rewriter.run,
                           help='Edit project files')
          self.add_command('wrap', wraptool.add_arguments, wraptool.run,
                           help='Wrap tools')
          self.add_command('runpython', self.add_runpython_arguments, self.run_runpython_command,
                           help='Run a python script')
          self.add_command('help', self.add_help_arguments, self.run_help_command,
                           help='Print help of a subcommand')
  
      def add_command(self, name, add_arguments_func, run_func, help):
          p = self.subparsers.add_parser(name, help=help)
          add_arguments_func(p)
          p.set_defaults(run_func=run_func)
          self.commands[name] = p
  
      def add_runpython_arguments(self, parser):
          parser.add_argument('script_file')
          parser.add_argument('script_args', nargs=argparse.REMAINDER)
  
      def run_runpython_command(self, options):
          import runpy
          sys.argv[1:] = options.script_args
          runpy.run_path(options.script_file, run_name='__main__')
          return 0
  
      def add_help_arguments(self, parser):
          parser.add_argument('command', nargs='?')
  
      def run_help_command(self, options):
          if options.command:
              self.commands[options.command].print_help()
          else:
              self.parser.print_help()
          return 0
  
      def run(self, args):
          # If first arg is not a known command, assume user wants to run the setup
          # command.
          known_commands = list(self.commands.keys()) + ['-h', '--help']
          if len(args) == 0 or args[0] not in known_commands:
              args = ['setup'] + args
  
          args = mesonlib.expand_arguments(args)
          options = self.parser.parse_args(args)
  
          try:
              return options.run_func(options)
          except MesonException as e:
              mlog.exception(e)
              logfile = mlog.shutdown()
              if logfile is not None:
                  mlog.log("\nA full log can be found at", mlog.bold(logfile))
              if os.environ.get('MESON_FORCE_BACKTRACE'):
                  raise
              return 1
          except Exception as e:
              if os.environ.get('MESON_FORCE_BACKTRACE'):
                  raise
              traceback.print_exc()
              return 2
          finally:
              mlog.shutdown()
  
  def run_script_command(script_name, script_args):
      # Map script name to module name for those that doesn't match
      script_map = {'exe': 'meson_exe',
                    'install': 'meson_install',
                    'delsuffix': 'delwithsuffix',
                    'gtkdoc': 'gtkdochelper',
                    'hotdoc': 'hotdochelper',
                    'regencheck': 'regen_checker'}
      module_name = script_map.get(script_name, script_name)
  
      try:
          module = importlib.import_module('mesonbuild.scripts.' + module_name)
      except ModuleNotFoundError as e:
          mlog.exception(e)
          return 1
  
      try:
          return module.run(script_args)
      except MesonException as e:
          mlog.error('Error in {} helper script:'.format(script_name))
          mlog.exception(e)
          return 1
  
  def run(original_args, mainfile):
      if sys.version_info < (3, 5):
          print('Meson works correctly only with python 3.5+.')
          print('You have python %s.' % sys.version)
          print('Please update your environment')
          return 1
  
      # https://github.com/mesonbuild/meson/issues/3653
      if sys.platform.lower() == 'msys':
          mlog.error('This python3 seems to be msys/python on MSYS2 Windows, which is known to have path semantics incompatible with Meson')
          msys2_arch = detect_msys2_arch()
          if msys2_arch:
              mlog.error('Please install and use mingw-w64-i686-python3 and/or mingw-w64-x86_64-python3 with Pacman')
          else:
              mlog.error('Please download and use Python as detailed at: https://mesonbuild.com/Getting-meson.html')
          return 2
  
      # Set the meson command that will be used to run scripts and so on
      mesonlib.set_meson_command(mainfile)
  
      args = original_args[:]
  
      # Special handling of internal commands called from backends, they don
  
  [... the rest is omitted ...]
files: mesonbuild/mesonmain.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 sys
  import os.path
  import importlib
  
  from . import mesonlib
  from . import mlog
  from .mesonlib import MesonException
  from .environment import detect_msys2_arch
  
  def run_script_command(script_name, script_args):
      # Map script name to module name for those that doesn't match
      script_map = {'exe': 'meson_exe',
                    'install': 'meson_install',
                    'delsuffix': 'delwithsuffix',
                    'gtkdoc': 'gtkdochelper',
                    'hotdoc': 'hotdochelper',
                    'regencheck': 'regen_checker'}
      module_name = script_map.get(script_name, script_name)
  
      try:
          module = importlib.import_module('mesonbuild.scripts.' + module_name)
      except ModuleNotFoundError as e:
          mlog.exception(e)
          return 1
  
      try:
          return module.run(script_args)
      except MesonException as e:
          mlog.error('Error in {} helper script:'.format(script_name))
          mlog.exception(e)
          return 1
  
  def run(original_args, mainfile):
      if sys.version_info < (3, 5):
          print('Meson works correctly only with python 3.5+.')
          print('You have python %s.' % sys.version)
          print('Please update your environment')
          return 1
      # https://github.com/mesonbuild/meson/issues/3653
      if sys.platform.lower() == 'msys':
          mlog.error('This python3 seems to be msys/python on MSYS2 Windows, which is known to have path semantics incompatible with Meson')
          msys2_arch = detect_msys2_arch()
          if msys2_arch:
              mlog.error('Please install and use mingw-w64-i686-python3 and/or mingw-w64-x86_64-python3 with Pacman')
          else:
              mlog.error('Please download and use Python as detailed at: https://mesonbuild.com/Getting-meson.html')
          return 2
  
      # Set the meson command that will be used to run scripts and so on
      mesonlib.set_meson_command(mainfile)
  
      args = original_args[:]
  
      # Special handling of internal commands called from backends, they don't
      # need to go through argparse.
      if len(args) >= 2 and args[0] == '--internal':
          if args[1] == 'regenerate':
              # Rewrite "meson --internal regenerate" command line to
              # "meson --reconfigure"
              args = ['--reconfigure'] + args[2:]
          else:
              return run_script_command(args[1], args[2:])
  
      if len(args) > 0:
          # First check if we want to run a subcommand.
          cmd_name = args[0]
          remaining_args = args[1:]
          # "help" is a special case: Since printing of the help may be
          # delegated to a subcommand, we edit cmd_name before executing
          # the rest of the logic here.
          if cmd_name == 'help':
              remaining_args += ['--help']
              args = remaining_args
              cmd_name = args[0]
          if cmd_name == 'test':
              from . import mtest
              return mtest.run(remaining_args)
          elif cmd_name == 'install':
              from . import minstall
              return minstall.run(remaining_args)
          elif cmd_name == 'introspect':
              from . import mintro
              return mintro.run(remaining_args)
          elif cmd_name == 'rewrite':
              from . import rewriter
              return rewriter.run(remaining_args)
          elif cmd_name == 'configure':
              try:
                  from . import mconf
                  return mconf.run(remaining_args)
              except MesonException as e:
                  mlog.exception(e)
                  sys.exit(1)
          elif cmd_name == 'wrap':
              from .wrap import wraptool
              return wraptool.run(remaining_args)
          elif cmd_name == 'init':
              from . import minit
              return minit.run(remaining_args)
          elif cmd_name == 'runpython':
              import runpy
              script_file = remaining_args[0]
              sys.argv[1:] = remaining_args[1:]
              runpy.run_path(script_file, run_name='__main__')
              sys.exit(0)
          else:
              # If cmd_name is not a known command, assume user wants to run the
              # setup command.
              from . import msetup
              if cmd_name != 'setup':
                  remaining_args = args
              return msetup.run(remaining_args)
  
      return 0
  
  def main():
      # Always resolve the command path so Ninja can find it for regen, tests, etc.
      if 'meson.exe' in sys.executable:
          assert(os.path.isabs(sys.executable))
          launcher = sys.executable
      else:
          launcher = os.path.realpath(sys.argv[0])
      return run(sys.argv[1:], launcher)
  
  if __name__ == '__main__':
      sys.exit(main())
  
  
  [... the rest is omitted ...]
files: mesonbuild/mesonmain.py
files: # Copyright 2017 The Meson development team
  
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  """Code that creates simple startup projects."""
  
  import os, sys, re, shutil, subprocess
  from glob import glob
  from mesonbuild import mesonlib
  from mesonbuild.environment import detect_ninja
  
  lib_h_template = '''#pragma once
  #if defined _WIN32 || defined __CYGWIN__
    #ifdef BUILDING_{utoken}
      #define {utoken}_PUBLIC __declspec(dllexport)
    #else
      #define {utoken}_PUBLIC __declspec(dllimport)
    #endif
  #else
    #ifdef BUILDING_{utoken}
        #define {utoken}_PUBLIC __attribute__ ((visibility ("default")))
    #else
        #define {utoken}_PUBLIC
    #endif
  #endif
  
  int {utoken}_PUBLIC {function_name}();
  
  '''
  
  lib_c_template = '''#include <{header_file}>
  
  /* This function will not be exported and is not
   * directly callable by users of this library.
   */
  int internal_function() {{
      return 0;
  }}
  
  int {function_name}() {{
      return internal_function();
  }}
  '''
  
  lib_c_test_template = '''#include <{header_file}>
  #include <stdio.h>
  
  int main(int argc, char **argv) {{
      if(argc != 1) {{
          printf("%s takes no arguments.\\n", argv[0]);
          return 1;
      }}
      return {function_name}();
  }}
  '''
  
  lib_c_meson_template = '''project('{project_name}', 'c',
    version : '{version}',
    default_options : ['warning_level=3'])
  
  # These arguments are only used to build the shared library
  # not the executables that use the library.
  lib_args = ['-DBUILDING_{utoken}']
  
  shlib = shared_library('{lib_name}', '{source_file}',
    install : true,
    c_args : lib_args,
    gnu_symbol_visibility : 'hidden',
  )
  
  test_exe = executable('{test_exe_name}', '{test_source_file}',
    link_with : shlib)
  test('{test_name}', test_exe)
  
  # Make this library usable as a Meson subproject.
  {ltoken}_dep = declare_dependency(
    include_directories: include_directories('.'),
    link_with : shlib)
  
  # Make this library usable from the system's
  # package manager.
  install_headers('{header_file}', subdir : '{header_dir}')
  
  pkg_mod = import('pkgconfig')
  pkg_mod.generate(
    name : '{project_name}',
    filebase : '{ltoken}',
    description : 'Meson sample project.',
    subdirs : '{header_dir}',
    libraries : shlib,
    version : '{version}',
  )
  '''
  
  hello_c_template = '''#include <stdio.h>
  
  #define PROJECT_NAME "{project_name}"
  
  int main(int argc, char **argv) {{
      if(argc != 1) {{
          printf("%s takes no arguments.\\n", argv[0]);
          return 1;
      }}
      printf("This is project %s.\\n", PROJECT_NAME);
      return 0;
  }}
  '''
  
  hello_c_meson_template = '''project('{project_name}', 'c',
    version : '{version}',
    default_options : ['warning_level=3'])
  
  exe = executable('{exe_name}', '{source_name}',
    install : true)
  
  test('basic', exe)
  '''
  
  hello_cpp_template = '''#include <iostream>
  
  #define PROJECT_NAME "{project_name}"
  
  int main(int argc, char **argv) {{
      if(argc != 1) {{
          std::cout << argv[0] <<  "takes no arguments.\\n";
          return 1;
      }}
      std::cout << "This is project " << PROJECT_NAME << ".\\n";
      return 0;
  }}
  '''
  
  hello_cpp_meson_template = '''project('{project_name}', 'cpp',
    version : '{version}',
    default_options : ['warning_level=3',
                       'cpp_std=c++14'])
  
  exe = executable('{exe_name}', '{source_name}',
    install : true)
  
  test('basic', exe)
  '''
  
  lib_hpp_template = '''#pragma once
  #if defined _WIN32 || defined __CYGWIN__
    #ifdef BUILDING_{utoken}
      #define {utoken}_PUBLIC __declspec(dllexport)
    #else
      #define {utoken}_PUBLIC __declspec(dllimport)
    #endif
  #else
    #ifdef BUILDING_{utoken}
        #define {utoken}_PUBLIC __attribute__ ((visibility ("default")))
    #else
        #define {utoken}_PUBLIC
    #endif
  #endif
  
  namespace {namespace} {{
  
  class {utoken}_PUBLIC {class_name} {{
  
  public:
    {class_name}();
    int get_number() const;
  
  private:
  
    int number;
  
  }};
  
  }}
  
  '''
  
  lib_cpp_template = '''#include <{header_file}>
  
  namespace {namespace} {{
  
  {class_name}::{class_name}() {{
      number = 6;
  }}
  
  int {class_name}::get_number() const {{
    return number;
  }}
  
  }}
  '''
  
  lib_cpp_test_template = '''#include <{header_file}>
  #include <iostream>
  
  int main(int argc, char **argv) {{
      if(argc != 1) {{
          std::cout << argv[0] << " takes no arguments.\\n";
          return 1;
      }}
      {namespace}::{class_name} c;
      return c.get_number() != 6;
  }}
  '''
  
  lib_cpp_meson_template = '''project('{project_name}', 'cpp',
    version : '{version}',
    default_options : ['warning_level=3', 'cpp_std=c++14'])
  
  # These arguments are only used to build the shared library
  # not the executables that use the library.
  lib_args = ['-DBUILDING_{utoken}']
  
  shlib = shared_library('{lib_name}', '{source_file}',
    install : true,
    cpp_args : lib_args,
  
  
  [... the rest is omitted ...]
files: mesonbuild/minit.py
files: # Copyright 2017 The Meson development team
  
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  """Code that creates simple startup projects."""
  
  import os, sys, argparse, re, shutil, subprocess
  from glob import glob
  from mesonbuild import mesonlib
  from mesonbuild.environment import detect_ninja
  
  lib_h_template = '''#pragma once
  #if defined _WIN32 || defined __CYGWIN__
    #ifdef BUILDING_{utoken}
      #define {utoken}_PUBLIC __declspec(dllexport)
    #else
      #define {utoken}_PUBLIC __declspec(dllimport)
    #endif
  #else
    #ifdef BUILDING_{utoken}
        #define {utoken}_PUBLIC __attribute__ ((visibility ("default")))
    #else
        #define {utoken}_PUBLIC
    #endif
  #endif
  
  int {utoken}_PUBLIC {function_name}();
  
  '''
  
  lib_c_template = '''#include <{header_file}>
  
  /* This function will not be exported and is not
   * directly callable by users of this library.
   */
  int internal_function() {{
      return 0;
  }}
  
  int {function_name}() {{
      return internal_function();
  }}
  '''
  
  lib_c_test_template = '''#include <{header_file}>
  #include <stdio.h>
  
  int main(int argc, char **argv) {{
      if(argc != 1) {{
          printf("%s takes no arguments.\\n", argv[0]);
          return 1;
      }}
      return {function_name}();
  }}
  '''
  
  lib_c_meson_template = '''project('{project_name}', 'c',
    version : '{version}',
    default_options : ['warning_level=3'])
  
  # These arguments are only used to build the shared library
  # not the executables that use the library.
  lib_args = ['-DBUILDING_{utoken}']
  
  shlib = shared_library('{lib_name}', '{source_file}',
    install : true,
    c_args : lib_args,
    gnu_symbol_visibility : 'hidden',
  )
  
  test_exe = executable('{test_exe_name}', '{test_source_file}',
    link_with : shlib)
  test('{test_name}', test_exe)
  
  # Make this library usable as a Meson subproject.
  {ltoken}_dep = declare_dependency(
    include_directories: include_directories('.'),
    link_with : shlib)
  
  # Make this library usable from the system's
  # package manager.
  install_headers('{header_file}', subdir : '{header_dir}')
  
  pkg_mod = import('pkgconfig')
  pkg_mod.generate(
    name : '{project_name}',
    filebase : '{ltoken}',
    description : 'Meson sample project.',
    subdirs : '{header_dir}',
    libraries : shlib,
    version : '{version}',
  )
  '''
  
  hello_c_template = '''#include <stdio.h>
  
  #define PROJECT_NAME "{project_name}"
  
  int main(int argc, char **argv) {{
      if(argc != 1) {{
          printf("%s takes no arguments.\\n", argv[0]);
          return 1;
      }}
      printf("This is project %s.\\n", PROJECT_NAME);
      return 0;
  }}
  '''
  
  hello_c_meson_template = '''project('{project_name}', 'c',
    version : '{version}',
    default_options : ['warning_level=3'])
  
  exe = executable('{exe_name}', '{source_name}',
    install : true)
  
  test('basic', exe)
  '''
  
  hello_cpp_template = '''#include <iostream>
  
  #define PROJECT_NAME "{project_name}"
  
  int main(int argc, char **argv) {{
      if(argc != 1) {{
          std::cout << argv[0] <<  "takes no arguments.\\n";
          return 1;
      }}
      std::cout << "This is project " << PROJECT_NAME << ".\\n";
      return 0;
  }}
  '''
  
  hello_cpp_meson_template = '''project('{project_name}', 'cpp',
    version : '{version}',
    default_options : ['warning_level=3',
                       'cpp_std=c++14'])
  
  exe = executable('{exe_name}', '{source_name}',
    install : true)
  
  test('basic', exe)
  '''
  
  lib_hpp_template = '''#pragma once
  #if defined _WIN32 || defined __CYGWIN__
    #ifdef BUILDING_{utoken}
      #define {utoken}_PUBLIC __declspec(dllexport)
    #else
      #define {utoken}_PUBLIC __declspec(dllimport)
    #endif
  #else
    #ifdef BUILDING_{utoken}
        #define {utoken}_PUBLIC __attribute__ ((visibility ("default")))
    #else
        #define {utoken}_PUBLIC
    #endif
  #endif
  
  namespace {namespace} {{
  
  class {utoken}_PUBLIC {class_name} {{
  
  public:
    {class_name}();
    int get_number() const;
  
  private:
  
    int number;
  
  }};
  
  }}
  
  '''
  
  lib_cpp_template = '''#include <{header_file}>
  
  namespace {namespace} {{
  
  {class_name}::{class_name}() {{
      number = 6;
  }}
  
  int {class_name}::get_number() const {{
    return number;
  }}
  
  }}
  '''
  
  lib_cpp_test_template = '''#include <{header_file}>
  #include <iostream>
  
  int main(int argc, char **argv) {{
      if(argc != 1) {{
          std::cout << argv[0] << " takes no arguments.\\n";
          return 1;
      }}
      {namespace}::{class_name} c;
      return c.get_number() != 6;
  }}
  '''
  
  lib_cpp_meson_template = '''project('{project_name}', 'cpp',
    version : '{version}',
    default_options : ['warning_level=3', 'cpp_std=c++14'])
  
  # These arguments are only used to build the shared library
  # not the executables that use the library.
  lib_args = ['-DBUILDING_{utoken}']
  
  shlib = shared_library('{lib_name}', '{source_file}',
    install : true,
    cpp_args : lib
  
  [... the rest is omitted ...]
files: mesonbuild/minit.py
files: # Copyright 2013-2014 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 sys, pickle, os, shutil, subprocess, gzip, errno
  import shlex
  from glob import glob
  from .scripts import depfixer
  from .scripts import destdir_join
  from .mesonlib import is_windows, Popen_safe
  from .mtest import rebuild_all
  try:
      from __main__ import __file__ as main_file
  except ImportError:
      # Happens when running as meson.exe which is native Windows.
      # This is only used for pkexec which is not, so this is fine.
      main_file = None
  
  symlink_warning = '''Warning: trying to copy a symlink that points to a file. This will copy the file,
  but this will be changed in a future version of Meson to copy the symlink as is. Please update your
  build definitions so that it will not break when the change happens.'''
  
  selinux_updates = []
  
  def add_arguments(parser):
      parser.add_argument('-C', default='.', dest='wd',
                          help='directory to cd into before running')
      parser.add_argument('--no-rebuild', default=False, action='store_true',
                          help='Do not rebuild before installing.')
      parser.add_argument('--only-changed', default=False, action='store_true',
                          help='Only overwrite files that are older than the copied file.')
  
  class DirMaker:
      def __init__(self, lf):
          self.lf = lf
          self.dirs = []
  
      def makedirs(self, path, exist_ok=False):
          dirname = os.path.normpath(path)
          dirs = []
          while dirname != os.path.dirname(dirname):
              if not os.path.exists(dirname):
                  dirs.append(dirname)
              dirname = os.path.dirname(dirname)
          os.makedirs(path, exist_ok=exist_ok)
  
          # store the directories in creation order, with the parent directory
          # before the child directories. Future calls of makedir() will not
          # create the parent directories, so the last element in the list is
          # the last one to be created. That is the first one to be removed on
          # __exit__
          dirs.reverse()
          self.dirs += dirs
  
      def __enter__(self):
          return self
  
      def __exit__(self, type, value, traceback):
          self.dirs.reverse()
          for d in self.dirs:
              append_to_log(self.lf, d)
  
  def is_executable(path, follow_symlinks=False):
      '''Checks whether any of the "x" bits are set in the source file mode.'''
      return bool(os.stat(path, follow_symlinks=follow_symlinks).st_mode & 0o111)
  
  def append_to_log(lf, line):
      lf.write(line)
      if not line.endswith('\n'):
          lf.write('\n')
      lf.flush()
  
  def set_chown(path, user=None, group=None, dir_fd=None, follow_symlinks=True):
      # shutil.chown will call os.chown without passing all the parameters
      # and particularly follow_symlinks, thus we replace it temporary
      # with a lambda with all the parameters so that follow_symlinks will
      # be actually passed properly.
      # Not nice, but better than actually rewriting shutil.chown until
      # this python bug is fixed: https://bugs.python.org/issue18108
      real_os_chown = os.chown
      try:
          os.chown = lambda p, u, g: real_os_chown(p, u, g,
                                                   dir_fd=dir_fd,
                                                   follow_symlinks=follow_symlinks)
          shutil.chown(path, user, group)
      except:
          raise
      finally:
          os.chown = real_os_chown
  
  def set_chmod(path, mode, dir_fd=None, follow_symlinks=True):
      try:
          os.chmod(path, mode, dir_fd=dir_fd, follow_symlinks=follow_symlinks)
      except (NotImplementedError, OSError, SystemError) as e:
          if not os.path.islink(path):
              os.chmod(path, mode, dir_fd=dir_fd)
  
  def sanitize_permissions(path, umask):
      if umask is None:
          return
      new_perms = 0o777 if is_executable(path, follow_symlinks=False) else 0o666
      new_perms &= ~umask
      try:
          set_chmod(path, new_perms, follow_symlinks=False)
      except PermissionError as e:
          msg = '{!r}: Unable to set permissions {!r}: {}, ignoring...'
          print(msg.format(path, new_perms, e.strerror))
  
  def set_mode(path, mode, default_umask):
      if mode is None or (mode.perms_s or mode.owner or mode.group) is None:
          # Just sanitize permissions with the default umask
          sanitize_permissions(path, default_umask)
          return
      # No chown() on Windows, and must set one of owner/group
      if not is_windows() and (mode.owner or mode.group) is not None:
          try:
              set_chown(path, mode.owner, mode.group, follow_symlinks=False)
          except PermissionError as e:
              msg = '{!r}: Unable to set owner {!r} and group {!r}: {}, ignoring...'
              print(msg.format(path, mode.owner, mode.group, e.strerror))
          except LookupError:
              msg = '{!r}: Non-existent owner {!r} or group {!r}: ignoring...'
              print(msg.format(path, mode.owner, mode.group))
          except OSError as e:
              if e.errno == errno.EINVAL:
                  msg = '{!r}: Non-existent numeric owner {!r} or group {!r}: ignoring...'
                  print(msg.format(path, mode.owner, mode.group))
              else:
                  raise
      # Must set permissions *after* setting owner/group
  
  [... the rest is omitted ...]
files: mesonbuild/minstall.py
files: # Copyright 2013-2014 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 sys, pickle, os, shutil, subprocess, gzip, errno
  import shlex
  import argparse
  from glob import glob
  from .scripts import depfixer
  from .scripts import destdir_join
  from .mesonlib import is_windows, Popen_safe
  from .mtest import rebuild_all
  try:
      from __main__ import __file__ as main_file
  except ImportError:
      # Happens when running as meson.exe which is native Windows.
      # This is only used for pkexec which is not, so this is fine.
      main_file = None
  
  symlink_warning = '''Warning: trying to copy a symlink that points to a file. This will copy the file,
  but this will be changed in a future version of Meson to copy the symlink as is. Please update your
  build definitions so that it will not break when the change happens.'''
  
  selinux_updates = []
  
  def buildparser():
      parser = argparse.ArgumentParser(prog='meson install')
      parser.add_argument('-C', default='.', dest='wd',
                          help='directory to cd into before running')
      parser.add_argument('--no-rebuild', default=False, action='store_true',
                          help='Do not rebuild before installing.')
      parser.add_argument('--only-changed', default=False, action='store_true',
                          help='Only overwrite files that are older than the copied file.')
      return parser
  
  class DirMaker:
      def __init__(self, lf):
          self.lf = lf
          self.dirs = []
  
      def makedirs(self, path, exist_ok=False):
          dirname = os.path.normpath(path)
          dirs = []
          while dirname != os.path.dirname(dirname):
              if not os.path.exists(dirname):
                  dirs.append(dirname)
              dirname = os.path.dirname(dirname)
          os.makedirs(path, exist_ok=exist_ok)
  
          # store the directories in creation order, with the parent directory
          # before the child directories. Future calls of makedir() will not
          # create the parent directories, so the last element in the list is
          # the last one to be created. That is the first one to be removed on
          # __exit__
          dirs.reverse()
          self.dirs += dirs
  
      def __enter__(self):
          return self
  
      def __exit__(self, type, value, traceback):
          self.dirs.reverse()
          for d in self.dirs:
              append_to_log(self.lf, d)
  
  def is_executable(path, follow_symlinks=False):
      '''Checks whether any of the "x" bits are set in the source file mode.'''
      return bool(os.stat(path, follow_symlinks=follow_symlinks).st_mode & 0o111)
  
  def append_to_log(lf, line):
      lf.write(line)
      if not line.endswith('\n'):
          lf.write('\n')
      lf.flush()
  
  def set_chown(path, user=None, group=None, dir_fd=None, follow_symlinks=True):
      # shutil.chown will call os.chown without passing all the parameters
      # and particularly follow_symlinks, thus we replace it temporary
      # with a lambda with all the parameters so that follow_symlinks will
      # be actually passed properly.
      # Not nice, but better than actually rewriting shutil.chown until
      # this python bug is fixed: https://bugs.python.org/issue18108
      real_os_chown = os.chown
      try:
          os.chown = lambda p, u, g: real_os_chown(p, u, g,
                                                   dir_fd=dir_fd,
                                                   follow_symlinks=follow_symlinks)
          shutil.chown(path, user, group)
      except:
          raise
      finally:
          os.chown = real_os_chown
  
  def set_chmod(path, mode, dir_fd=None, follow_symlinks=True):
      try:
          os.chmod(path, mode, dir_fd=dir_fd, follow_symlinks=follow_symlinks)
      except (NotImplementedError, OSError, SystemError) as e:
          if not os.path.islink(path):
              os.chmod(path, mode, dir_fd=dir_fd)
  
  def sanitize_permissions(path, umask):
      if umask is None:
          return
      new_perms = 0o777 if is_executable(path, follow_symlinks=False) else 0o666
      new_perms &= ~umask
      try:
          set_chmod(path, new_perms, follow_symlinks=False)
      except PermissionError as e:
          msg = '{!r}: Unable to set permissions {!r}: {}, ignoring...'
          print(msg.format(path, new_perms, e.strerror))
  
  def set_mode(path, mode, default_umask):
      if mode is None or (mode.perms_s or mode.owner or mode.group) is None:
          # Just sanitize permissions with the default umask
          sanitize_permissions(path, default_umask)
          return
      # No chown() on Windows, and must set one of owner/group
      if not is_windows() and (mode.owner or mode.group) is not None:
          try:
              set_chown(path, mode.owner, mode.group, follow_symlinks=False)
          except PermissionError as e:
              msg = '{!r}: Unable to set owner {!r} and group {!r}: {}, ignoring...'
              print(msg.format(path, mode.owner, mode.group, e.strerror))
          except LookupError:
              msg = '{!r}: Non-existent owner {!r} or group {!r}: ignoring...'
              print(msg.format(path, mode.owner, mode.group))
          except OSError as e:
              if e.errno == errno.EINVAL:
                  msg = '{!r}: Non-existent numeric owner {!r} or group {!r}: ignoring...'
                  print(msg.format(path, mode.owner, mode.group))
  
  
  [... the rest is omitted ...]
files: mesonbuild/minstall.py
files: # Copyright 2014-2016 The Meson development team
  
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  """This is a helper script for IDE developers. It allows you to
  extract information such as list of targets, files, compiler flags,
  tests and so on. All output is in JSON for simple parsing.
  
  Currently only works for the Ninja backend. Others use generated
  project files and don't need this info."""
  
  import json
  from . import build, mtest, coredata as cdata
  from . import mesonlib
  from .backend import ninjabackend
  import sys, os
  import pathlib
  
  def add_arguments(parser):
      parser.add_argument('--targets', action='store_true', dest='list_targets', default=False,
                          help='List top level targets.')
      parser.add_argument('--installed', action='store_true', dest='list_installed', default=False,
                          help='List all installed files and directories.')
      parser.add_argument('--target-files', action='store', dest='target_files', default=None,
                          help='List source files for a given target.')
      parser.add_argument('--buildsystem-files', action='store_true', dest='buildsystem_files', default=False,
                          help='List files that make up the build system.')
      parser.add_argument('--buildoptions', action='store_true', dest='buildoptions', default=False,
                          help='List all build options.')
      parser.add_argument('--tests', action='store_true', dest='tests', default=False,
                          help='List all unit tests.')
      parser.add_argument('--benchmarks', action='store_true', dest='benchmarks', default=False,
                          help='List all benchmarks.')
      parser.add_argument('--dependencies', action='store_true', dest='dependencies', default=False,
                          help='List external dependencies.')
      parser.add_argument('--projectinfo', action='store_true', dest='projectinfo', default=False,
                          help='Information about projects.')
      parser.add_argument('builddir', nargs='?', default='.', help='The build directory')
  
  def determine_installed_path(target, installdata):
      install_target = None
      for i in installdata.targets:
          if os.path.basename(i.fname) == target.get_filename(): # FIXME, might clash due to subprojects.
              install_target = i
              break
      if install_target is None:
          raise RuntimeError('Something weird happened. File a bug.')
      outname = os.path.join(installdata.prefix, i.outdir, os.path.basename(i.fname))
      # Normalize the path by using os.path.sep consistently, etc.
      # Does not change the effective path.
      return str(pathlib.PurePath(outname))
  
  
  def list_installed(installdata):
      res = {}
      if installdata is not None:
          for t in installdata.targets:
              res[os.path.join(installdata.build_dir, t.fname)] = \
                  os.path.join(installdata.prefix, t.outdir, os.path.basename(t.fname))
          for path, installpath, unused_prefix in installdata.data:
              res[path] = os.path.join(installdata.prefix, installpath)
          for path, installdir, unused_custom_install_mode in installdata.headers:
              res[path] = os.path.join(installdata.prefix, installdir, os.path.basename(path))
          for path, installpath, unused_custom_install_mode in installdata.man:
              res[path] = os.path.join(installdata.prefix, installpath)
      print(json.dumps(res))
  
  
  def list_targets(coredata, builddata, installdata):
      tlist = []
      for (idname, target) in builddata.get_targets().items():
          t = {'name': target.get_basename(), 'id': idname}
          fname = target.get_filename()
          if isinstance(fname, list):
              fname = [os.path.join(target.subdir, x) for x in fname]
          else:
              fname = os.path.join(target.subdir, fname)
          t['filename'] = fname
          if isinstance(target, build.Executable):
              typename = 'executable'
          elif isinstance(target, build.SharedLibrary):
              typename = 'shared library'
          elif isinstance(target, build.StaticLibrary):
              typename = 'static library'
          elif isinstance(target, build.CustomTarget):
              typename = 'custom'
          elif isinstance(target, build.RunTarget):
              typename = 'run'
          else:
              typename = 'unknown'
          t['type'] = typename
          if installdata and target.should_install():
              t['installed'] = True
              t['install_filename'] = determine_installed_path(target, installdata)
          else:
              t['installed'] = False
          t['build_by_default'] = target.build_by_default
          tlist.append(t)
      print(json.dumps(tlist))
  
  def list_target_files(target_name, coredata, builddata):
      try:
          t = builddata.targets[target_name]
          sources = t.sources + t.extra_files
      except KeyError:
          print("Unknown target %s." % target_name)
          sys.exit(1)
      out = []
      for i in sources:
          if isinstance(i, mesonlib.File):
              i = os.path.join(i.subdir, i.fname)
          out.append(i)
      print(json.dumps(out))
  
  def list_buildoptions(coredata, builddata):
      optlist = []
      add_keys(optlist, coredata.user_options)
      add_keys(optlist, coredata.compiler_options)
      add_keys(optlist, coredata.base_options)
      add_keys(optlist, coredata.builtins)
      print(json.dumps(optlist))
  
  def add_keys(optlist, options):
      keys = list(options.keys())
      keys.sort()
      for key in keys:
          opt = options[key]
          optdict = {'name': key, 'value': opt.value}
          if isinstance(opt, cdata.UserString
  
  [... the rest is omitted ...]
files: mesonbuild/mintro.py
files: # Copyright 2014-2016 The Meson development team
  
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  """This is a helper script for IDE developers. It allows you to
  extract information such as list of targets, files, compiler flags,
  tests and so on. All output is in JSON for simple parsing.
  
  Currently only works for the Ninja backend. Others use generated
  project files and don't need this info."""
  
  import json
  from . import build, mtest, coredata as cdata
  from . import mesonlib
  from .backend import ninjabackend
  import argparse
  import sys, os
  import pathlib
  
  def buildparser():
      parser = argparse.ArgumentParser(prog='meson introspect')
      parser.add_argument('--targets', action='store_true', dest='list_targets', default=False,
                          help='List top level targets.')
      parser.add_argument('--installed', action='store_true', dest='list_installed', default=False,
                          help='List all installed files and directories.')
      parser.add_argument('--target-files', action='store', dest='target_files', default=None,
                          help='List source files for a given target.')
      parser.add_argument('--buildsystem-files', action='store_true', dest='buildsystem_files', default=False,
                          help='List files that make up the build system.')
      parser.add_argument('--buildoptions', action='store_true', dest='buildoptions', default=False,
                          help='List all build options.')
      parser.add_argument('--tests', action='store_true', dest='tests', default=False,
                          help='List all unit tests.')
      parser.add_argument('--benchmarks', action='store_true', dest='benchmarks', default=False,
                          help='List all benchmarks.')
      parser.add_argument('--dependencies', action='store_true', dest='dependencies', default=False,
                          help='List external dependencies.')
      parser.add_argument('--projectinfo', action='store_true', dest='projectinfo', default=False,
                          help='Information about projects.')
      parser.add_argument('builddir', nargs='?', default='.', help='The build directory')
      return parser
  
  def determine_installed_path(target, installdata):
      install_target = None
      for i in installdata.targets:
          if os.path.basename(i.fname) == target.get_filename(): # FIXME, might clash due to subprojects.
              install_target = i
              break
      if install_target is None:
          raise RuntimeError('Something weird happened. File a bug.')
      outname = os.path.join(installdata.prefix, i.outdir, os.path.basename(i.fname))
      # Normalize the path by using os.path.sep consistently, etc.
      # Does not change the effective path.
      return str(pathlib.PurePath(outname))
  
  
  def list_installed(installdata):
      res = {}
      if installdata is not None:
          for t in installdata.targets:
              res[os.path.join(installdata.build_dir, t.fname)] = \
                  os.path.join(installdata.prefix, t.outdir, os.path.basename(t.fname))
          for path, installpath, unused_prefix in installdata.data:
              res[path] = os.path.join(installdata.prefix, installpath)
          for path, installdir, unused_custom_install_mode in installdata.headers:
              res[path] = os.path.join(installdata.prefix, installdir, os.path.basename(path))
          for path, installpath, unused_custom_install_mode in installdata.man:
              res[path] = os.path.join(installdata.prefix, installpath)
      print(json.dumps(res))
  
  
  def list_targets(coredata, builddata, installdata):
      tlist = []
      for (idname, target) in builddata.get_targets().items():
          t = {'name': target.get_basename(), 'id': idname}
          fname = target.get_filename()
          if isinstance(fname, list):
              fname = [os.path.join(target.subdir, x) for x in fname]
          else:
              fname = os.path.join(target.subdir, fname)
          t['filename'] = fname
          if isinstance(target, build.Executable):
              typename = 'executable'
          elif isinstance(target, build.SharedLibrary):
              typename = 'shared library'
          elif isinstance(target, build.StaticLibrary):
              typename = 'static library'
          elif isinstance(target, build.CustomTarget):
              typename = 'custom'
          elif isinstance(target, build.RunTarget):
              typename = 'run'
          else:
              typename = 'unknown'
          t['type'] = typename
          if installdata and target.should_install():
              t['installed'] = True
              t['install_filename'] = determine_installed_path(target, installdata)
          else:
              t['installed'] = False
          t['build_by_default'] = target.build_by_default
          tlist.append(t)
      print(json.dumps(tlist))
  
  def list_target_files(target_name, coredata, builddata):
      try:
          t = builddata.targets[target_name]
          sources = t.sources + t.extra_files
      except KeyError:
          print("Unknown target %s." % target_name)
          sys.exit(1)
      out = []
      for i in sources:
          if isinstance(i, mesonlib.File):
              i = os.path.join(i.subdir, i.fname)
          out.append(i)
      print(json.dumps(out))
  
  def list_buildoptions(coredata, builddata):
      optlist = []
      add_keys(optlist, coredata.user_options)
      add_keys(optlist, coredata.compiler_options)
      add_keys(optlist, coredata.base_options)
      add_keys(optlist, coredata.builtins)
      print(json.dumps(optlist))
  
  def add_keys(optlist, options):
      keys = list(options.keys())
      keys.sort()
      for key in keys:
          opt = options[key]
          optdict = {'
  
  [... the rest is omitted ...]
files: mesonbuild/mintro.py
files: # Copyright 2016-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.
  
  import time
  import sys, stat
  import datetime
  import os.path
  import platform
  import cProfile as profile
  import argparse
  
  from . import environment, interpreter, mesonlib
  from . import build
  from . import mlog, coredata
  from .mesonlib import MesonException
  from .wrap import WrapMode
  
  def add_arguments(parser):
      coredata.register_builtin_arguments(parser)
      parser.add_argument('--cross-file', default=None,
                          help='File describing cross compilation environment.')
      parser.add_argument('-v', '--version', action='version',
                          version=coredata.version)
      # See the mesonlib.WrapMode enum for documentation
      parser.add_argument('--wrap-mode', default=None,
                          type=wrapmodetype, choices=WrapMode,
                          help='Special wrap mode to use')
      parser.add_argument('--profile-self', action='store_true', dest='profile',
                          help=argparse.SUPPRESS)
      parser.add_argument('--fatal-meson-warnings', action='store_true', dest='fatal_warnings',
                          help='Make all Meson warnings fatal')
      parser.add_argument('--reconfigure', action='store_true',
                          help='Set options and reconfigure the project. Useful when new ' +
                               'options have been added to the project and the default value ' +
                               'is not working.')
      parser.add_argument('builddir', nargs='?', default=None)
      parser.add_argument('sourcedir', nargs='?', default=None)
  
  def wrapmodetype(string):
      try:
          return getattr(WrapMode, string)
      except AttributeError:
          msg = ', '.join([t.name.lower() for t in WrapMode])
          msg = 'invalid argument {!r}, use one of {}'.format(string, msg)
          raise argparse.ArgumentTypeError(msg)
  
  class MesonApp:
      def __init__(self, options):
          (self.source_dir, self.build_dir) = self.validate_dirs(options.builddir,
                                                                 options.sourcedir,
                                                                 options.reconfigure)
          self.options = options
  
      def has_build_file(self, dirname):
          fname = os.path.join(dirname, environment.build_filename)
          return os.path.exists(fname)
  
      def validate_core_dirs(self, dir1, dir2):
          if dir1 is None:
              if dir2 is None:
                  if not os.path.exists('meson.build') and os.path.exists('../meson.build'):
                      dir2 = '..'
                  else:
                      raise MesonException('Must specify at least one directory name.')
              dir1 = os.getcwd()
          if dir2 is None:
              dir2 = os.getcwd()
          ndir1 = os.path.abspath(os.path.realpath(dir1))
          ndir2 = os.path.abspath(os.path.realpath(dir2))
          if not os.path.exists(ndir1):
              os.makedirs(ndir1)
          if not os.path.exists(ndir2):
              os.makedirs(ndir2)
          if not stat.S_ISDIR(os.stat(ndir1).st_mode):
              raise MesonException('%s is not a directory' % dir1)
          if not stat.S_ISDIR(os.stat(ndir2).st_mode):
              raise MesonException('%s is not a directory' % dir2)
          if os.path.samefile(dir1, dir2):
              raise MesonException('Source and build directories must not be the same. Create a pristine build directory.')
          if self.has_build_file(ndir1):
              if self.has_build_file(ndir2):
                  raise MesonException('Both directories contain a build file %s.' % environment.build_filename)
              return ndir1, ndir2
          if self.has_build_file(ndir2):
              return ndir2, ndir1
          raise MesonException('Neither directory contains a build file %s.' % environment.build_filename)
  
      def validate_dirs(self, dir1, dir2, reconfigure):
          (src_dir, build_dir) = self.validate_core_dirs(dir1, dir2)
          priv_dir = os.path.join(build_dir, 'meson-private/coredata.dat')
          if os.path.exists(priv_dir):
              if not reconfigure:
                  print('Directory already configured.\n'
                        '\nJust run your build command (e.g. ninja) and Meson will regenerate as necessary.\n'
                        'If ninja fails, run "ninja reconfigure" or "meson --reconfigure"\n'
                        'to force Meson to regenerate.\n'
                        '\nIf build failures persist, manually wipe your build directory to clear any\n'
                        'stored system data.\n'
                        '\nTo change option values, run "meson configure" instead.')
                  sys.exit(0)
          else:
              if reconfigure:
                  print('Directory does not contain a valid build tree:\n{}'.format(build_dir))
                  sys.exit(1)
          return src_dir, build_dir
  
      def check_pkgconfig_envvar(self, env):
          curvar = os.environ.get('PKG_CONFIG_PATH', '')
          if curvar != env.coredata.pkgconf_envvar:
              mlog.warning('PKG_CONFIG_PATH has changed between invocations from "%s" to "%s".' %
                           (env.coredata.pkgconf_envvar, curvar))
              env.coredata.pkgconf_envvar = curvar
  
      def generate(self):
          env = environment.Environment(self.source_dir, self.build_dir, self.options)
          mlog.initialize(env.get_log_dir(), self.options.fatal_warnings)
          if self.options.profile:
              mlog.set_timestamp_start(time.monotonic())
          with mesonlib.BuildDirLock(self.build_dir):
              self._generate(env)
  
      def _generate(self, env):
          mlog.debug('Build started at', datetime.datetime.now().isoformat())
          mlog.debug('Main binary:', sys.executable)
          mlog.debug('Python system:', platform.system())
          mlog.log(mlog.b
  
  [... the rest is omitted ...]
files: mesonbuild/msetup.py
files: # Copyright 2016-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.
  
  import time
  import sys, stat
  import datetime
  import os.path
  import platform
  import cProfile as profile
  import argparse
  import traceback
  
  from . import environment, interpreter, mesonlib
  from . import build
  from . import mlog, coredata
  from .mesonlib import MesonException
  from .wrap import WrapMode
  
  def buildparser():
      parser = argparse.ArgumentParser(prog='meson')
      coredata.register_builtin_arguments(parser)
      parser.add_argument('--cross-file', default=None,
                          help='File describing cross compilation environment.')
      parser.add_argument('-v', '--version', action='version',
                          version=coredata.version)
      # See the mesonlib.WrapMode enum for documentation
      parser.add_argument('--wrap-mode', default=None,
                          type=wrapmodetype, choices=WrapMode,
                          help='Special wrap mode to use')
      parser.add_argument('--profile-self', action='store_true', dest='profile',
                          help=argparse.SUPPRESS)
      parser.add_argument('--fatal-meson-warnings', action='store_true', dest='fatal_warnings',
                          help='Make all Meson warnings fatal')
      parser.add_argument('--reconfigure', action='store_true',
                          help='Set options and reconfigure the project. Useful when new ' +
                               'options have been added to the project and the default value ' +
                               'is not working.')
      parser.add_argument('builddir', nargs='?', default=None)
      parser.add_argument('sourcedir', nargs='?', default=None)
      return parser
  
  def wrapmodetype(string):
      try:
          return getattr(WrapMode, string)
      except AttributeError:
          msg = ', '.join([t.name.lower() for t in WrapMode])
          msg = 'invalid argument {!r}, use one of {}'.format(string, msg)
          raise argparse.ArgumentTypeError(msg)
  
  class MesonApp:
      def __init__(self, options):
          (self.source_dir, self.build_dir) = self.validate_dirs(options.builddir,
                                                                 options.sourcedir,
                                                                 options.reconfigure)
          self.options = options
  
      def has_build_file(self, dirname):
          fname = os.path.join(dirname, environment.build_filename)
          return os.path.exists(fname)
  
      def validate_core_dirs(self, dir1, dir2):
          if dir1 is None:
              if dir2 is None:
                  if not os.path.exists('meson.build') and os.path.exists('../meson.build'):
                      dir2 = '..'
                  else:
                      raise MesonException('Must specify at least one directory name.')
              dir1 = os.getcwd()
          if dir2 is None:
              dir2 = os.getcwd()
          ndir1 = os.path.abspath(os.path.realpath(dir1))
          ndir2 = os.path.abspath(os.path.realpath(dir2))
          if not os.path.exists(ndir1):
              os.makedirs(ndir1)
          if not os.path.exists(ndir2):
              os.makedirs(ndir2)
          if not stat.S_ISDIR(os.stat(ndir1).st_mode):
              raise MesonException('%s is not a directory' % dir1)
          if not stat.S_ISDIR(os.stat(ndir2).st_mode):
              raise MesonException('%s is not a directory' % dir2)
          if os.path.samefile(dir1, dir2):
              raise MesonException('Source and build directories must not be the same. Create a pristine build directory.')
          if self.has_build_file(ndir1):
              if self.has_build_file(ndir2):
                  raise MesonException('Both directories contain a build file %s.' % environment.build_filename)
              return ndir1, ndir2
          if self.has_build_file(ndir2):
              return ndir2, ndir1
          raise MesonException('Neither directory contains a build file %s.' % environment.build_filename)
  
      def validate_dirs(self, dir1, dir2, reconfigure):
          (src_dir, build_dir) = self.validate_core_dirs(dir1, dir2)
          priv_dir = os.path.join(build_dir, 'meson-private/coredata.dat')
          if os.path.exists(priv_dir):
              if not reconfigure:
                  print('Directory already configured.\n'
                        '\nJust run your build command (e.g. ninja) and Meson will regenerate as necessary.\n'
                        'If ninja fails, run "ninja reconfigure" or "meson --reconfigure"\n'
                        'to force Meson to regenerate.\n'
                        '\nIf build failures persist, manually wipe your build directory to clear any\n'
                        'stored system data.\n'
                        '\nTo change option values, run "meson configure" instead.')
                  sys.exit(0)
          else:
              if reconfigure:
                  print('Directory does not contain a valid build tree:\n{}'.format(build_dir))
                  sys.exit(1)
          return src_dir, build_dir
  
      def check_pkgconfig_envvar(self, env):
          curvar = os.environ.get('PKG_CONFIG_PATH', '')
          if curvar != env.coredata.pkgconf_envvar:
              mlog.warning('PKG_CONFIG_PATH has changed between invocations from "%s" to "%s".' %
                           (env.coredata.pkgconf_envvar, curvar))
              env.coredata.pkgconf_envvar = curvar
  
      def generate(self):
          env = environment.Environment(self.source_dir, self.build_dir, self.options)
          mlog.initialize(env.get_log_dir(), self.options.fatal_warnings)
          if self.options.profile:
              mlog.set_timestamp_start(time.monotonic())
          with mesonlib.BuildDirLock(self.build_dir):
              self._generate(env)
  
      def _generate(self, env):
          mlog.debug('Build started at', datetime.datetime.now().isoformat())
          mlog.debug('Main binary:', sys.executable)
  
  
  [... the rest is omitted ...]
files: mesonbuild/msetup.py
files: # Copyright 2016-2017 The Meson development team
  
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  # A tool to run tests in many different ways.
  
  import shlex
  import subprocess, sys, os, argparse
  import pickle
  from mesonbuild import build
  from mesonbuild import environment
  from mesonbuild.dependencies import ExternalProgram
  from mesonbuild.mesonlib import substring_is_in_list, MesonException
  from mesonbuild import mlog
  
  import time, datetime, multiprocessing, json
  import concurrent.futures as conc
  import platform
  import signal
  import random
  from copy import deepcopy
  import enum
  
  # GNU autotools interprets a return code of 77 from tests it executes to
  # mean that the test should be skipped.
  GNU_SKIP_RETURNCODE = 77
  
  def is_windows():
      platname = platform.system().lower()
      return platname == 'windows' or 'mingw' in platname
  
  def is_cygwin():
      platname = platform.system().lower()
      return 'cygwin' in platname
  
  def determine_worker_count():
      varname = 'MESON_TESTTHREADS'
      if varname in os.environ:
          try:
              num_workers = int(os.environ[varname])
          except ValueError:
              print('Invalid value in %s, using 1 thread.' % varname)
              num_workers = 1
      else:
          try:
              # Fails in some weird environments such as Debian
              # reproducible build.
              num_workers = multiprocessing.cpu_count()
          except Exception:
              num_workers = 1
      return num_workers
  
  def add_arguments(parser):
      parser.add_argument('--repeat', default=1, dest='repeat', type=int,
                          help='Number of times to run the tests.')
      parser.add_argument('--no-rebuild', default=False, action='store_true',
                          help='Do not rebuild before running tests.')
      parser.add_argument('--gdb', default=False, dest='gdb', action='store_true',
                          help='Run test under gdb.')
      parser.add_argument('--list', default=False, dest='list', action='store_true',
                          help='List available tests.')
      parser.add_argument('--wrapper', default=None, dest='wrapper', type=shlex.split,
                          help='wrapper to run tests with (e.g. Valgrind)')
      parser.add_argument('-C', default='.', dest='wd',
                          help='directory to cd into before running')
      parser.add_argument('--suite', default=[], dest='include_suites', action='append', metavar='SUITE',
                          help='Only run tests belonging to the given suite.')
      parser.add_argument('--no-suite', default=[], dest='exclude_suites', action='append', metavar='SUITE',
                          help='Do not run tests belonging to the given suite.')
      parser.add_argument('--no-stdsplit', default=True, dest='split', action='store_false',
                          help='Do not split stderr and stdout in test logs.')
      parser.add_argument('--print-errorlogs', default=False, action='store_true',
                          help="Whether to print failing tests' logs.")
      parser.add_argument('--benchmark', default=False, action='store_true',
                          help="Run benchmarks instead of tests.")
      parser.add_argument('--logbase', default='testlog',
                          help="Base name for log file.")
      parser.add_argument('--num-processes', default=determine_worker_count(), type=int,
                          help='How many parallel processes to use.')
      parser.add_argument('-v', '--verbose', default=False, action='store_true',
                          help='Do not redirect stdout and stderr')
      parser.add_argument('-q', '--quiet', default=False, action='store_true',
                          help='Produce less output to the terminal.')
      parser.add_argument('-t', '--timeout-multiplier', type=float, default=None,
                          help='Define a multiplier for test timeout, for example '
                          ' when running tests in particular conditions they might take'
                          ' more time to execute.')
      parser.add_argument('--setup', default=None, dest='setup',
                          help='Which test setup to use.')
      parser.add_argument('--test-args', default=[], type=shlex.split,
                          help='Arguments to pass to the specified test(s) or all tests')
      parser.add_argument('args', nargs='*',
                          help='Optional list of tests to run')
  
  
  def returncode_to_status(retcode):
      # Note: We can't use `os.WIFSIGNALED(result.returncode)` and the related
      # functions here because the status returned by subprocess is munged. It
      # returns a negative value if the process was killed by a signal rather than
      # the raw status returned by `wait()`. Also, If a shell sits between Meson
      # the the actual unit test that shell is likely to convert a termination due
      # to a signal into an exit status of 128 plus the signal number.
      if retcode < 0:
          signum = -retcode
          try:
              signame = signal.Signals(signum).name
          except ValueError:
              signame = 'SIGinvalid'
          return '(killed by signal %d %s)' % (signum, signame)
  
      if retcode <= 128:
          return '(exit status %d)' % (retcode,)
  
      signum = retcode - 128
      try:
          signame = signal.Signals(signum).name
      except ValueError:
          signame = 'SIGinvalid'
      return '(exit status %d or signal %d %s)' % (retcode, signum, signame)
  
  def env_tuple_to_str(env):
      return ''.join(["%s='%s' " % (k, v) for k, v in env])
  
  
  class TestException(MesonException):
      pass
  
  
  @enum.unique
  class TestResult(enum.Enum):
  
      OK = 'OK'
      TIMEOUT = 'TIMEOUT'
      SKIP = 'SKIP'
      FAIL = 'FAIL'
  
  
  class TestRun:
  
  
  [... the rest is omitted ...]
files: mesonbuild/mtest.py
files: # Copyright 2016-2017 The Meson development team
  
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  # A tool to run tests in many different ways.
  
  import shlex
  import subprocess, sys, os, argparse
  import pickle
  from mesonbuild import build
  from mesonbuild import environment
  from mesonbuild.dependencies import ExternalProgram
  from mesonbuild.mesonlib import substring_is_in_list, MesonException
  from mesonbuild import mlog
  
  import time, datetime, multiprocessing, json
  import concurrent.futures as conc
  import platform
  import signal
  import random
  from copy import deepcopy
  import enum
  
  # GNU autotools interprets a return code of 77 from tests it executes to
  # mean that the test should be skipped.
  GNU_SKIP_RETURNCODE = 77
  
  def is_windows():
      platname = platform.system().lower()
      return platname == 'windows' or 'mingw' in platname
  
  def is_cygwin():
      platname = platform.system().lower()
      return 'cygwin' in platname
  
  def determine_worker_count():
      varname = 'MESON_TESTTHREADS'
      if varname in os.environ:
          try:
              num_workers = int(os.environ[varname])
          except ValueError:
              print('Invalid value in %s, using 1 thread.' % varname)
              num_workers = 1
      else:
          try:
              # Fails in some weird environments such as Debian
              # reproducible build.
              num_workers = multiprocessing.cpu_count()
          except Exception:
              num_workers = 1
      return num_workers
  
  def buildparser():
      parser = argparse.ArgumentParser(prog='meson test')
      parser.add_argument('--repeat', default=1, dest='repeat', type=int,
                          help='Number of times to run the tests.')
      parser.add_argument('--no-rebuild', default=False, action='store_true',
                          help='Do not rebuild before running tests.')
      parser.add_argument('--gdb', default=False, dest='gdb', action='store_true',
                          help='Run test under gdb.')
      parser.add_argument('--list', default=False, dest='list', action='store_true',
                          help='List available tests.')
      parser.add_argument('--wrapper', default=None, dest='wrapper', type=shlex.split,
                          help='wrapper to run tests with (e.g. Valgrind)')
      parser.add_argument('-C', default='.', dest='wd',
                          help='directory to cd into before running')
      parser.add_argument('--suite', default=[], dest='include_suites', action='append', metavar='SUITE',
                          help='Only run tests belonging to the given suite.')
      parser.add_argument('--no-suite', default=[], dest='exclude_suites', action='append', metavar='SUITE',
                          help='Do not run tests belonging to the given suite.')
      parser.add_argument('--no-stdsplit', default=True, dest='split', action='store_false',
                          help='Do not split stderr and stdout in test logs.')
      parser.add_argument('--print-errorlogs', default=False, action='store_true',
                          help="Whether to print failing tests' logs.")
      parser.add_argument('--benchmark', default=False, action='store_true',
                          help="Run benchmarks instead of tests.")
      parser.add_argument('--logbase', default='testlog',
                          help="Base name for log file.")
      parser.add_argument('--num-processes', default=determine_worker_count(), type=int,
                          help='How many parallel processes to use.')
      parser.add_argument('-v', '--verbose', default=False, action='store_true',
                          help='Do not redirect stdout and stderr')
      parser.add_argument('-q', '--quiet', default=False, action='store_true',
                          help='Produce less output to the terminal.')
      parser.add_argument('-t', '--timeout-multiplier', type=float, default=None,
                          help='Define a multiplier for test timeout, for example '
                          ' when running tests in particular conditions they might take'
                          ' more time to execute.')
      parser.add_argument('--setup', default=None, dest='setup',
                          help='Which test setup to use.')
      parser.add_argument('--test-args', default=[], type=shlex.split,
                          help='Arguments to pass to the specified test(s) or all tests')
      parser.add_argument('args', nargs='*',
                          help='Optional list of tests to run')
      return parser
  
  
  def returncode_to_status(retcode):
      # Note: We can't use `os.WIFSIGNALED(result.returncode)` and the related
      # functions here because the status returned by subprocess is munged. It
      # returns a negative value if the process was killed by a signal rather than
      # the raw status returned by `wait()`. Also, If a shell sits between Meson
      # the the actual unit test that shell is likely to convert a termination due
      # to a signal into an exit status of 128 plus the signal number.
      if retcode < 0:
          signum = -retcode
          try:
              signame = signal.Signals(signum).name
          except ValueError:
              signame = 'SIGinvalid'
          return '(killed by signal %d %s)' % (signum, signame)
  
      if retcode <= 128:
          return '(exit status %d)' % (retcode,)
  
      signum = retcode - 128
      try:
          signame = signal.Signals(signum).name
      except ValueError:
          signame = 'SIGinvalid'
      return '(exit status %d or signal %d %s)' % (retcode, signum, signame)
  
  def env_tuple_to_str(env):
      return ''.join(["%s='%s' " % (k, v) for k, v in env])
  
  
  class TestException(MesonException):
      pass
  
  
  @enum.unique
  class TestResult(enum.Enum):
  
      OK = 'OK'
      TIMEOUT = 'TIMEOUT'
      SK
  
  [... the rest is omitted ...]
files: mesonbuild/mtest.py
files: #!/usr/bin/env python3
  # Copyright 2016 The Meson development team
  
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  # This class contains the basic functionality needed to run any interpreter
  # or an interpreter-based tool.
  
  # This tool is used to manipulate an existing Meson build definition.
  #
  # - add a file to a target
  # - remove files from a target
  # - move targets
  # - reindent?
  
  import mesonbuild.astinterpreter
  from mesonbuild.mesonlib import MesonException
  from mesonbuild import mlog
  import sys, traceback
  
  def add_arguments(parser):
      parser.add_argument('--sourcedir', default='.',
                          help='Path to source directory.')
      parser.add_argument('--target', default=None,
                          help='Name of target to edit.')
      parser.add_argument('--filename', default=None,
                          help='Name of source file to add or remove to target.')
      parser.add_argument('commands', nargs='+')
  
  def run(options):
      if options.target is None or options.filename is None:
          sys.exit("Must specify both target and filename.")
      print('This tool is highly experimental, use with care.')
      rewriter = mesonbuild.astinterpreter.AstInterpreter(options.sourcedir, '')
      try:
          if options.commands[0] == 'add':
              rewriter.add_source(options.target, options.filename)
          elif options.commands[0] == 'remove':
              rewriter.remove_source(options.target, options.filename)
          else:
              sys.exit('Unknown command: ' + options.commands[0])
      except Exception as e:
          if isinstance(e, MesonException):
              mlog.exception(e)
          else:
              traceback.print_exc()
          return 1
      return 0
  
  
  [... the rest is omitted ...]
files: mesonbuild/rewriter.py
files: #!/usr/bin/env python3
  # Copyright 2016 The Meson development team
  
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  # This class contains the basic functionality needed to run any interpreter
  # or an interpreter-based tool.
  
  # This tool is used to manipulate an existing Meson build definition.
  #
  # - add a file to a target
  # - remove files from a target
  # - move targets
  # - reindent?
  
  import mesonbuild.astinterpreter
  from mesonbuild.mesonlib import MesonException
  from mesonbuild import mlog
  import sys, traceback
  import argparse
  
  def buildparser():
      parser = argparse.ArgumentParser(prog='meson rewrite')
  
      parser.add_argument('--sourcedir', default='.',
                          help='Path to source directory.')
      parser.add_argument('--target', default=None,
                          help='Name of target to edit.')
      parser.add_argument('--filename', default=None,
                          help='Name of source file to add or remove to target.')
      parser.add_argument('commands', nargs='+')
      return parser
  
  def run(args):
      options = buildparser().parse_args(args)
      if options.target is None or options.filename is None:
          sys.exit("Must specify both target and filename.")
      print('This tool is highly experimental, use with care.')
      rewriter = mesonbuild.astinterpreter.AstInterpreter(options.sourcedir, '')
      try:
          if options.commands[0] == 'add':
              rewriter.add_source(options.target, options.filename)
          elif options.commands[0] == 'remove':
              rewriter.remove_source(options.target, options.filename)
          else:
              sys.exit('Unknown command: ' + options.commands[0])
      except Exception as e:
          if isinstance(e, MesonException):
              mlog.exception(e)
          else:
              traceback.print_exc()
          return 1
      return 0
  
  
  [... the rest is omitted ...]
files: mesonbuild/rewriter.py
files: # Copyright 2015-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 json
  import sys, os
  import configparser
  import shutil
  
  from glob import glob
  
  from .wrap import API_ROOT, open_wrapdburl
  
  from .. import mesonlib
  
  def add_arguments(parser):
      subparsers = parser.add_subparsers(title='Commands', dest='command')
      subparsers.required = True
  
      p = subparsers.add_parser('list', help='show all available projects')
      p.set_defaults(wrap_func=list_projects)
  
      p = subparsers.add_parser('search', help='search the db by name')
      p.add_argument('name')
      p.set_defaults(wrap_func=search)
  
      p = subparsers.add_parser('install', help='install the specified project')
      p.add_argument('name')
      p.set_defaults(wrap_func=install)
  
      p = subparsers.add_parser('update', help='update the project to its newest available release')
      p.add_argument('name')
      p.set_defaults(wrap_func=update)
  
      p = subparsers.add_parser('info', help='show available versions of a project')
      p.add_argument('name')
      p.set_defaults(wrap_func=info)
  
      p = subparsers.add_parser('status', help='show installed and available versions of your projects')
      p.set_defaults(wrap_func=status)
  
      p = subparsers.add_parser('promote', help='bring a subsubproject up to the master project')
      p.add_argument('project_path')
      p.set_defaults(wrap_func=promote)
  
  def get_result(urlstring):
      u = open_wrapdburl(urlstring)
      data = u.read().decode('utf-8')
      jd = json.loads(data)
      if jd['output'] != 'ok':
          print('Got bad output from server.')
          print(data)
          sys.exit(1)
      return jd
  
  def get_projectlist():
      jd = get_result(API_ROOT + 'projects')
      projects = jd['projects']
      return projects
  
  def list_projects(options):
      projects = get_projectlist()
      for p in projects:
          print(p)
  
  def search(options):
      name = options.name
      jd = get_result(API_ROOT + 'query/byname/' + name)
      for p in jd['projects']:
          print(p)
  
  def get_latest_version(name):
      jd = get_result(API_ROOT + 'query/get_latest/' + name)
      branch = jd['branch']
      revision = jd['revision']
      return branch, revision
  
  def install(options):
      name = options.name
      if not os.path.isdir('subprojects'):
          print('Subprojects dir not found. Run this script in your source root directory.')
          sys.exit(1)
      if os.path.isdir(os.path.join('subprojects', name)):
          print('Subproject directory for this project already exists.')
          sys.exit(1)
      wrapfile = os.path.join('subprojects', name + '.wrap')
      if os.path.exists(wrapfile):
          print('Wrap file already exists.')
          sys.exit(1)
      (branch, revision) = get_latest_version(name)
      u = open_wrapdburl(API_ROOT + 'projects/%s/%s/%s/get_wrap' % (name, branch, revision))
      data = u.read()
      with open(wrapfile, 'wb') as f:
          f.write(data)
      print('Installed', name, 'branch', branch, 'revision', revision)
  
  def get_current_version(wrapfile):
      cp = configparser.ConfigParser()
      cp.read(wrapfile)
      cp = cp['wrap-file']
      patch_url = cp['patch_url']
      arr = patch_url.split('/')
      branch = arr[-3]
      revision = int(arr[-2])
      return branch, revision, cp['directory'], cp['source_filename'], cp['patch_filename']
  
  def update(options):
      name = options.name
      if not os.path.isdir('subprojects'):
          print('Subprojects dir not found. Run this command in your source root directory.')
          sys.exit(1)
      wrapfile = os.path.join('subprojects', name + '.wrap')
      if not os.path.exists(wrapfile):
          print('Project', name, 'is not in use.')
          sys.exit(1)
      (branch, revision, subdir, src_file, patch_file) = get_current_version(wrapfile)
      (new_branch, new_revision) = get_latest_version(name)
      if new_branch == branch and new_revision == revision:
          print('Project', name, 'is already up to date.')
          sys.exit(0)
      u = open_wrapdburl(API_ROOT + 'projects/%s/%s/%d/get_wrap' % (name, new_branch, new_revision))
      data = u.read()
      shutil.rmtree(os.path.join('subprojects', subdir), ignore_errors=True)
      try:
          os.unlink(os.path.join('subprojects/packagecache', src_file))
      except FileNotFoundError:
          pass
      try:
          os.unlink(os.path.join('subprojects/packagecache', patch_file))
      except FileNotFoundError:
          pass
      with open(wrapfile, 'wb') as f:
          f.write(data)
      print('Updated', name, 'to branch', new_branch, 'revision', new_revision)
  
  def info(options):
      name = options.name
      jd = get_result(API_ROOT + 'projects/' + name)
      versions = jd['versions']
      if not versions:
          print('No available versions of', name)
          sys.exit(0)
      print('Available versions of %s:' % name)
      for v in versions:
          print(' ', v['branch'], v['revision'])
  
  def do_promotion(from_path, spdir_name):
      if os.path.isfile(from_path):
          assert(from_path.endswith('.wrap'))
          shutil.copy(from_path, spdir_name)
      elif os.path.isdir(from_path):
          sproj_name = os.path.basename(from_path
  
  [... the rest is omitted ...]
files: mesonbuild/wrap/wraptool.py
files: # Copyright 2015-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 json
  import sys, os
  import configparser
  import shutil
  import argparse
  
  from glob import glob
  
  from .wrap import API_ROOT, open_wrapdburl
  
  from .. import mesonlib
  
  def add_arguments(parser):
      subparsers = parser.add_subparsers(title='Commands', dest='command')
      subparsers.required = True
  
      p = subparsers.add_parser('list', help='show all available projects')
      p.set_defaults(wrap_func=list_projects)
  
      p = subparsers.add_parser('search', help='search the db by name')
      p.add_argument('name')
      p.set_defaults(wrap_func=search)
  
      p = subparsers.add_parser('install', help='install the specified project')
      p.add_argument('name')
      p.set_defaults(wrap_func=install)
  
      p = subparsers.add_parser('update', help='update the project to its newest available release')
      p.add_argument('name')
      p.set_defaults(wrap_func=update)
  
      p = subparsers.add_parser('info', help='show available versions of a project')
      p.add_argument('name')
      p.set_defaults(wrap_func=info)
  
      p = subparsers.add_parser('status', help='show installed and available versions of your projects')
      p.set_defaults(wrap_func=status)
  
      p = subparsers.add_parser('promote', help='bring a subsubproject up to the master project')
      p.add_argument('project_path')
      p.set_defaults(wrap_func=promote)
  
  def get_result(urlstring):
      u = open_wrapdburl(urlstring)
      data = u.read().decode('utf-8')
      jd = json.loads(data)
      if jd['output'] != 'ok':
          print('Got bad output from server.')
          print(data)
          sys.exit(1)
      return jd
  
  def get_projectlist():
      jd = get_result(API_ROOT + 'projects')
      projects = jd['projects']
      return projects
  
  def list_projects(options):
      projects = get_projectlist()
      for p in projects:
          print(p)
  
  def search(options):
      name = options.name
      jd = get_result(API_ROOT + 'query/byname/' + name)
      for p in jd['projects']:
          print(p)
  
  def get_latest_version(name):
      jd = get_result(API_ROOT + 'query/get_latest/' + name)
      branch = jd['branch']
      revision = jd['revision']
      return branch, revision
  
  def install(options):
      name = options.name
      if not os.path.isdir('subprojects'):
          print('Subprojects dir not found. Run this script in your source root directory.')
          sys.exit(1)
      if os.path.isdir(os.path.join('subprojects', name)):
          print('Subproject directory for this project already exists.')
          sys.exit(1)
      wrapfile = os.path.join('subprojects', name + '.wrap')
      if os.path.exists(wrapfile):
          print('Wrap file already exists.')
          sys.exit(1)
      (branch, revision) = get_latest_version(name)
      u = open_wrapdburl(API_ROOT + 'projects/%s/%s/%s/get_wrap' % (name, branch, revision))
      data = u.read()
      with open(wrapfile, 'wb') as f:
          f.write(data)
      print('Installed', name, 'branch', branch, 'revision', revision)
  
  def get_current_version(wrapfile):
      cp = configparser.ConfigParser()
      cp.read(wrapfile)
      cp = cp['wrap-file']
      patch_url = cp['patch_url']
      arr = patch_url.split('/')
      branch = arr[-3]
      revision = int(arr[-2])
      return branch, revision, cp['directory'], cp['source_filename'], cp['patch_filename']
  
  def update(options):
      name = options.name
      if not os.path.isdir('subprojects'):
          print('Subprojects dir not found. Run this command in your source root directory.')
          sys.exit(1)
      wrapfile = os.path.join('subprojects', name + '.wrap')
      if not os.path.exists(wrapfile):
          print('Project', name, 'is not in use.')
          sys.exit(1)
      (branch, revision, subdir, src_file, patch_file) = get_current_version(wrapfile)
      (new_branch, new_revision) = get_latest_version(name)
      if new_branch == branch and new_revision == revision:
          print('Project', name, 'is already up to date.')
          sys.exit(0)
      u = open_wrapdburl(API_ROOT + 'projects/%s/%s/%d/get_wrap' % (name, new_branch, new_revision))
      data = u.read()
      shutil.rmtree(os.path.join('subprojects', subdir), ignore_errors=True)
      try:
          os.unlink(os.path.join('subprojects/packagecache', src_file))
      except FileNotFoundError:
          pass
      try:
          os.unlink(os.path.join('subprojects/packagecache', patch_file))
      except FileNotFoundError:
          pass
      with open(wrapfile, 'wb') as f:
          f.write(data)
      print('Updated', name, 'to branch', new_branch, 'revision', new_revision)
  
  def info(options):
      name = options.name
      jd = get_result(API_ROOT + 'projects/' + name)
      versions = jd['versions']
      if not versions:
          print('No available versions of', name)
          sys.exit(0)
      print('Available versions of %s:' % name)
      for v in versions:
          print(' ', v['branch'], v['revision'])
  
  def do_promotion(from_path, spdir_name):
      if os.path.isfile(from_path):
          assert(from_path.endswith('.wrap'))
          shutil.copy(from_path, spdir_name)
      elif os.path.isdir(from_path):
          sproj_name = os.path.bas
  
  [... the rest is omitted ...]
files: mesonbuild/wrap/wraptool.py
files: #!/usr/bin/env python3
  
  # 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 itertools
  import os
  import subprocess
  import shutil
  import sys
  import signal
  from io import StringIO
  from ast import literal_eval
  from enum import Enum
  import tempfile
  from pathlib import Path, PurePath
  from mesonbuild import build
  from mesonbuild import environment
  from mesonbuild import mesonlib
  from mesonbuild import mlog
  from mesonbuild import mtest
  from mesonbuild.mesonlib import stringlistify, Popen_safe
  from mesonbuild.coredata import backendlist
  import argparse
  import xml.etree.ElementTree as ET
  import time
  import multiprocessing
  from concurrent.futures import ProcessPoolExecutor
  import re
  from run_tests import get_fake_options, run_configure, get_meson_script
  from run_tests import get_backend_commands, get_backend_args_for_dir, Backend
  from run_tests import ensure_backend_detects_changes
  
  
  class BuildStep(Enum):
      configure = 1
      build = 2
      test = 3
      install = 4
      clean = 5
      validate = 6
  
  
  class TestResult:
      def __init__(self, msg, step, stdo, stde, mlog, conftime=0, buildtime=0, testtime=0):
          self.msg = msg
          self.step = step
          self.stdo = stdo
          self.stde = stde
          self.mlog = mlog
          self.conftime = conftime
          self.buildtime = buildtime
          self.testtime = testtime
  
  
  class AutoDeletedDir:
      def __init__(self, d):
          self.dir = d
  
      def __enter__(self):
          os.makedirs(self.dir, exist_ok=True)
          return self.dir
  
      def __exit__(self, _type, value, traceback):
          # We don't use tempfile.TemporaryDirectory, but wrap the
          # deletion in the AutoDeletedDir class because
          # it fails on Windows due antivirus programs
          # holding files open.
          mesonlib.windows_proof_rmtree(self.dir)
  
  failing_logs = []
  print_debug = 'MESON_PRINT_TEST_OUTPUT' in os.environ
  under_ci = not {'TRAVIS', 'APPVEYOR'}.isdisjoint(os.environ)
  do_debug = under_ci or print_debug
  no_meson_log_msg = 'No meson-log.txt found.'
  
  system_compiler = None
  
  class StopException(Exception):
      def __init__(self):
          super().__init__('Stopped by user')
  
  stop = False
  def stop_handler(signal, frame):
      global stop
      stop = True
  signal.signal(signal.SIGINT, stop_handler)
  signal.signal(signal.SIGTERM, stop_handler)
  
  def setup_commands(optbackend):
      global do_debug, backend, backend_flags
      global compile_commands, clean_commands, test_commands, install_commands, uninstall_commands
      backend = optbackend
      msbuild_exe = shutil.which('msbuild')
      # Auto-detect backend if unspecified
      if backend is None:
          if msbuild_exe is not None:
              backend = 'vs' # Meson will auto-detect VS version to use
          else:
              backend = 'ninja'
      # Set backend arguments for Meson
      if backend.startswith('vs'):
          backend_flags = ['--backend=' + backend]
          backend = Backend.vs
      elif backend == 'xcode':
          backend_flags = ['--backend=xcode']
          backend = Backend.xcode
      elif backend == 'ninja':
          backend_flags = ['--backend=ninja']
          backend = Backend.ninja
      else:
          raise RuntimeError('Unknown backend: {!r}'.format(backend))
      compile_commands, clean_commands, test_commands, install_commands, \
          uninstall_commands = get_backend_commands(backend, do_debug)
  
  def get_relative_files_list_from_dir(fromdir):
      paths = []
      for (root, _, files) in os.walk(fromdir):
          reldir = os.path.relpath(root, start=fromdir)
          for f in files:
              path = os.path.join(reldir, f).replace('\\', '/')
              if path.startswith('./'):
                  path = path[2:]
              paths.append(path)
      return paths
  
  def platform_fix_name(fname, compiler, env):
      if '?lib' in fname:
          if mesonlib.for_cygwin(env.is_cross_build(), env):
              fname = re.sub(r'lib/\?lib(.*)\.so$', r'bin/cyg\1.dll', fname)
              fname = re.sub(r'\?lib(.*)\.dll$', r'cyg\1.dll', fname)
          else:
              fname = re.sub(r'\?lib', 'lib', fname)
  
      if fname.endswith('?exe'):
          fname = fname[:-4]
          if mesonlib.for_windows(env.is_cross_build(), env) or mesonlib.for_cygwin(env.is_cross_build(), env):
              return fname + '.exe'
  
      if fname.startswith('?msvc:'):
          fname = fname[6:]
          if compiler != 'cl':
              return None
  
      if fname.startswith('?gcc:'):
          fname = fname[5:]
          if compiler == 'cl':
              return None
  
      if fname.startswith('?cygwin:'):
          fname = fname[8:]
          if compiler == 'cl' or not mesonlib.for_cygwin(env.is_cross_build(), env):
              return None
  
      return fname
  
  def validate_install(srcdir, installdir, compiler, env):
      # List of installed files
      info
  
  [... the rest is omitted ...]
files: run_project_tests.py
files: #!/usr/bin/env python3
  
  # 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 itertools
  import os
  import subprocess
  import shutil
  import sys
  import signal
  from io import StringIO
  from ast import literal_eval
  from enum import Enum
  import tempfile
  from pathlib import Path, PurePath
  from mesonbuild import build
  from mesonbuild import environment
  from mesonbuild import mesonlib
  from mesonbuild import mlog
  from mesonbuild import mtest
  from mesonbuild.mesonlib import stringlistify, Popen_safe
  from mesonbuild.coredata import backendlist
  import argparse
  import xml.etree.ElementTree as ET
  import time
  import multiprocessing
  from concurrent.futures import ProcessPoolExecutor
  import re
  from run_tests import get_fake_options, run_configure, get_meson_script
  from run_tests import get_backend_commands, get_backend_args_for_dir, Backend
  from run_tests import ensure_backend_detects_changes
  
  
  class BuildStep(Enum):
      configure = 1
      build = 2
      test = 3
      install = 4
      clean = 5
      validate = 6
  
  
  class TestResult:
      def __init__(self, msg, step, stdo, stde, mlog, conftime=0, buildtime=0, testtime=0):
          self.msg = msg
          self.step = step
          self.stdo = stdo
          self.stde = stde
          self.mlog = mlog
          self.conftime = conftime
          self.buildtime = buildtime
          self.testtime = testtime
  
  
  class AutoDeletedDir:
      def __init__(self, d):
          self.dir = d
  
      def __enter__(self):
          os.makedirs(self.dir, exist_ok=True)
          return self.dir
  
      def __exit__(self, _type, value, traceback):
          # We don't use tempfile.TemporaryDirectory, but wrap the
          # deletion in the AutoDeletedDir class because
          # it fails on Windows due antivirus programs
          # holding files open.
          mesonlib.windows_proof_rmtree(self.dir)
  
  failing_logs = []
  print_debug = 'MESON_PRINT_TEST_OUTPUT' in os.environ
  under_ci = not {'TRAVIS', 'APPVEYOR'}.isdisjoint(os.environ)
  do_debug = under_ci or print_debug
  no_meson_log_msg = 'No meson-log.txt found.'
  
  system_compiler = None
  
  class StopException(Exception):
      def __init__(self):
          super().__init__('Stopped by user')
  
  stop = False
  def stop_handler(signal, frame):
      global stop
      stop = True
  signal.signal(signal.SIGINT, stop_handler)
  signal.signal(signal.SIGTERM, stop_handler)
  
  def setup_commands(optbackend):
      global do_debug, backend, backend_flags
      global compile_commands, clean_commands, test_commands, install_commands, uninstall_commands
      backend = optbackend
      msbuild_exe = shutil.which('msbuild')
      # Auto-detect backend if unspecified
      if backend is None:
          if msbuild_exe is not None:
              backend = 'vs' # Meson will auto-detect VS version to use
          else:
              backend = 'ninja'
      # Set backend arguments for Meson
      if backend.startswith('vs'):
          backend_flags = ['--backend=' + backend]
          backend = Backend.vs
      elif backend == 'xcode':
          backend_flags = ['--backend=xcode']
          backend = Backend.xcode
      elif backend == 'ninja':
          backend_flags = ['--backend=ninja']
          backend = Backend.ninja
      else:
          raise RuntimeError('Unknown backend: {!r}'.format(backend))
      compile_commands, clean_commands, test_commands, install_commands, \
          uninstall_commands = get_backend_commands(backend, do_debug)
  
  def get_relative_files_list_from_dir(fromdir):
      paths = []
      for (root, _, files) in os.walk(fromdir):
          reldir = os.path.relpath(root, start=fromdir)
          for f in files:
              path = os.path.join(reldir, f).replace('\\', '/')
              if path.startswith('./'):
                  path = path[2:]
              paths.append(path)
      return paths
  
  def platform_fix_name(fname, compiler, env):
      if '?lib' in fname:
          if mesonlib.for_cygwin(env.is_cross_build(), env):
              fname = re.sub(r'lib/\?lib(.*)\.so$', r'bin/cyg\1.dll', fname)
              fname = re.sub(r'\?lib(.*)\.dll$', r'cyg\1.dll', fname)
          else:
              fname = re.sub(r'\?lib', 'lib', fname)
  
      if fname.endswith('?exe'):
          fname = fname[:-4]
          if mesonlib.for_windows(env.is_cross_build(), env) or mesonlib.for_cygwin(env.is_cross_build(), env):
              return fname + '.exe'
  
      if fname.startswith('?msvc:'):
          fname = fname[6:]
          if compiler != 'cl':
              return None
  
      if fname.startswith('?gcc:'):
          fname = fname[5:]
          if compiler == 'cl':
              return None
  
      if fname.startswith('?cygwin:'):
          fname = fname[8:]
          if compiler == 'cl' or not mesonlib.for_cygwin(env.is_cross_build(), env):
              return None
  
      return fname
  
  def validate_install(srcdir, installdir, compiler, env):
      # List of installed files
      info
  
  [... the rest is omitted ...]
files: run_project_tests.py
files: #!/usr/bin/env python3
  
  # Copyright 2012-2017 The Meson development team
  
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  import os
  import sys
  import time
  import shutil
  import subprocess
  import tempfile
  import platform
  from io import StringIO
  from enum import Enum
  from glob import glob
  from pathlib import Path
  
  import mesonbuild
  from mesonbuild import mesonlib
  from mesonbuild import mesonmain
  from mesonbuild import mtest
  from mesonbuild import mlog
  from mesonbuild.environment import Environment, detect_ninja
  
  
  # Fake classes and objects for mocking
  class FakeBuild:
      def __init__(self, env):
          self.environment = env
  
  class FakeCompilerOptions:
      def __init__(self):
          self.value = []
  
  def get_fake_options(prefix):
      import argparse
      opts = argparse.Namespace()
      opts.cross_file = None
      opts.wrap_mode = None
      opts.prefix = prefix
      opts.cmd_line_options = {}
      return opts
  
  def get_fake_env(sdir, bdir, prefix):
      env = Environment(sdir, bdir, get_fake_options(prefix))
      env.coredata.compiler_options['c_args'] = FakeCompilerOptions()
      return env
  
  
  Backend = Enum('Backend', 'ninja vs xcode')
  
  if 'MESON_EXE' in os.environ:
      import shlex
      meson_exe = shlex.split(os.environ['MESON_EXE'])
  else:
      meson_exe = None
  
  if mesonlib.is_windows() or mesonlib.is_cygwin():
      exe_suffix = '.exe'
  else:
      exe_suffix = ''
  
  def get_meson_script():
      '''
      Guess the meson that corresponds to the `mesonbuild` that has been imported
      so we can run configure and other commands in-process, since mesonmain.run
      needs to know the meson_command to use.
  
      Also used by run_unittests.py to determine what meson to run when not
      running in-process (which is the default).
      '''
      # Is there a meson.py next to the mesonbuild currently in use?
      mesonbuild_dir = Path(mesonbuild.__file__).resolve().parent.parent
      meson_script = mesonbuild_dir / 'meson.py'
      if meson_script.is_file():
          return str(meson_script)
      # Then if mesonbuild is in PYTHONPATH, meson must be in PATH
      mlog.warning('Could not find meson.py next to the mesonbuild module. '
                   'Trying system meson...')
      meson_cmd = shutil.which('meson')
      if meson_cmd:
          return meson_cmd
      raise RuntimeError('Could not find {!r} or a meson in PATH'.format(meson_script))
  
  def get_backend_args_for_dir(backend, builddir):
      '''
      Visual Studio backend needs to be given the solution to build
      '''
      if backend is Backend.vs:
          sln_name = glob(os.path.join(builddir, '*.sln'))[0]
          return [os.path.split(sln_name)[-1]]
      return []
  
  def find_vcxproj_with_target(builddir, target):
      import re, fnmatch
      t, ext = os.path.splitext(target)
      if ext:
          p = '<TargetName>{}</TargetName>\s*<TargetExt>\{}</TargetExt>'.format(t, ext)
      else:
          p = '<TargetName>{}</TargetName>'.format(t)
      for root, dirs, files in os.walk(builddir):
          for f in fnmatch.filter(files, '*.vcxproj'):
              f = os.path.join(builddir, f)
              with open(f, 'r', encoding='utf-8') as o:
                  if re.search(p, o.read(), flags=re.MULTILINE):
                      return f
      raise RuntimeError('No vcxproj matching {!r} in {!r}'.format(p, builddir))
  
  def get_builddir_target_args(backend, builddir, target):
      dir_args = []
      if not target:
          dir_args = get_backend_args_for_dir(backend, builddir)
      if target is None:
          return dir_args
      if backend is Backend.vs:
          vcxproj = find_vcxproj_with_target(builddir, target)
          target_args = [vcxproj]
      elif backend is Backend.xcode:
          target_args = ['-target', target]
      elif backend is Backend.ninja:
          target_args = [target]
      else:
          raise AssertionError('Unknown backend: {!r}'.format(backend))
      return target_args + dir_args
  
  def get_backend_commands(backend, debug=False):
      install_cmd = []
      uninstall_cmd = []
      if backend is Backend.vs:
          cmd = ['msbuild']
          clean_cmd = cmd + ['/target:Clean']
          test_cmd = cmd + ['RUN_TESTS.vcxproj']
      elif backend is Backend.xcode:
          cmd = ['xcodebuild']
          # In Xcode9 new build system's clean command fails when using a custom build directory.
          # Maybe use it when CI uses Xcode10 we can remove '-UseNewBuildSystem=FALSE'
          clean_cmd = cmd + ['-alltargets', 'clean', '-UseNewBuildSystem=FALSE']
          test_cmd = cmd + ['-target', 'RUN_TESTS']
      elif backend is Backend.ninja:
          # We need at least 1.6 because of -w dupbuild=err
          cmd = [detect_ninja('1.6'), '-w', 'dupbuild=err', '-d', 'explain']
          if cmd[0] is None:
              raise RuntimeError('Could not find Ninja v1.6 or newer')
          if debug:
              cmd += ['-v']
          clean_cmd = cmd + ['clean']
  
  
  [... the rest is omitted ...]
files: run_tests.py
files: #!/usr/bin/env python3
  
  # Copyright 2012-2017 The Meson development team
  
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  import os
  import sys
  import time
  import shutil
  import subprocess
  import tempfile
  import platform
  from io import StringIO
  from enum import Enum
  from glob import glob
  from pathlib import Path
  
  import mesonbuild
  from mesonbuild import mesonlib
  from mesonbuild import mesonmain
  from mesonbuild import mtest
  from mesonbuild import mlog
  from mesonbuild.environment import Environment, detect_ninja
  
  
  # Fake classes and objects for mocking
  class FakeBuild:
      def __init__(self, env):
          self.environment = env
  
  class FakeCompilerOptions:
      def __init__(self):
          self.value = []
  
  def get_fake_options(prefix):
      import argparse
      opts = argparse.Namespace()
      opts.cross_file = None
      opts.wrap_mode = None
      opts.prefix = prefix
      opts.cmd_line_options = {}
      return opts
  
  def get_fake_env(sdir, bdir, prefix):
      env = Environment(sdir, bdir, get_fake_options(prefix))
      env.coredata.compiler_options['c_args'] = FakeCompilerOptions()
      return env
  
  
  Backend = Enum('Backend', 'ninja vs xcode')
  
  if 'MESON_EXE' in os.environ:
      import shlex
      meson_exe = shlex.split(os.environ['MESON_EXE'])
  else:
      meson_exe = None
  
  if mesonlib.is_windows() or mesonlib.is_cygwin():
      exe_suffix = '.exe'
  else:
      exe_suffix = ''
  
  def get_meson_script():
      '''
      Guess the meson that corresponds to the `mesonbuild` that has been imported
      so we can run configure and other commands in-process, since mesonmain.run
      needs to know the meson_command to use.
  
      Also used by run_unittests.py to determine what meson to run when not
      running in-process (which is the default).
      '''
      # Is there a meson.py next to the mesonbuild currently in use?
      mesonbuild_dir = Path(mesonbuild.__file__).resolve().parent.parent
      meson_script = mesonbuild_dir / 'meson.py'
      if meson_script.is_file():
          return str(meson_script)
      # Then if mesonbuild is in PYTHONPATH, meson must be in PATH
      mlog.warning('Could not find meson.py next to the mesonbuild module. '
                   'Trying system meson...')
      meson_cmd = shutil.which('meson')
      if meson_cmd:
          return meson_cmd
      raise RuntimeError('Could not find {!r} or a meson in PATH'.format(meson_script))
  
  def get_backend_args_for_dir(backend, builddir):
      '''
      Visual Studio backend needs to be given the solution to build
      '''
      if backend is Backend.vs:
          sln_name = glob(os.path.join(builddir, '*.sln'))[0]
          return [os.path.split(sln_name)[-1]]
      return []
  
  def find_vcxproj_with_target(builddir, target):
      import re, fnmatch
      t, ext = os.path.splitext(target)
      if ext:
          p = '<TargetName>{}</TargetName>\s*<TargetExt>\{}</TargetExt>'.format(t, ext)
      else:
          p = '<TargetName>{}</TargetName>'.format(t)
      for root, dirs, files in os.walk(builddir):
          for f in fnmatch.filter(files, '*.vcxproj'):
              f = os.path.join(builddir, f)
              with open(f, 'r', encoding='utf-8') as o:
                  if re.search(p, o.read(), flags=re.MULTILINE):
                      return f
      raise RuntimeError('No vcxproj matching {!r} in {!r}'.format(p, builddir))
  
  def get_builddir_target_args(backend, builddir, target):
      dir_args = []
      if not target:
          dir_args = get_backend_args_for_dir(backend, builddir)
      if target is None:
          return dir_args
      if backend is Backend.vs:
          vcxproj = find_vcxproj_with_target(builddir, target)
          target_args = [vcxproj]
      elif backend is Backend.xcode:
          target_args = ['-target', target]
      elif backend is Backend.ninja:
          target_args = [target]
      else:
          raise AssertionError('Unknown backend: {!r}'.format(backend))
      return target_args + dir_args
  
  def get_backend_commands(backend, debug=False):
      install_cmd = []
      uninstall_cmd = []
      if backend is Backend.vs:
          cmd = ['msbuild']
          clean_cmd = cmd + ['/target:Clean']
          test_cmd = cmd + ['RUN_TESTS.vcxproj']
      elif backend is Backend.xcode:
          cmd = ['xcodebuild']
          # In Xcode9 new build system's clean command fails when using a custom build directory.
          # Maybe use it when CI uses Xcode10 we can remove '-UseNewBuildSystem=FALSE'
          clean_cmd = cmd + ['-alltargets', 'clean', '-UseNewBuildSystem=FALSE']
          test_cmd = cmd + ['-target', 'RUN_TESTS']
      elif backend is Backend.ninja:
          # We need at least 1.6 because of -w dupbuild=err
          cmd = [detect_ninja('1.6'), '-w', 'dupbuild=err', '-d', 'explain']
          if cmd[0] is None:
              raise RuntimeError('Could not find Ninja v1.6 or newer')
          if debug:
              cmd += ['-v']
          clean_cmd = cmd + ['clean']
  
  
  [... the rest is omitted ...]
files: run_tests.py