Merge pull request #438 from trhd/testing_options

New options for controlling test output.
pull/510/head
Jussi Pakkanen 9 years ago
commit cab5ce4fc0
  1. 2
      mesonbuild/backend/backends.py
  2. 10
      mesonbuild/backend/ninjabackend.py
  3. 2
      mesonbuild/backend/vs2010backend.py
  4. 2
      mesonbuild/backend/xcodebackend.py
  5. 8
      mesonbuild/build.py
  6. 2
      mesonbuild/compilers.py
  7. 119
      mesonbuild/coredata.py
  8. 2
      mesonbuild/dependencies.py
  9. 8
      mesonbuild/environment.py
  10. 11
      mesonbuild/interpreter.py
  11. 48
      mesonbuild/mconf.py
  12. 7
      mesonbuild/mesonlib.py
  13. 72
      mesonbuild/mesonmain.py
  14. 2
      mesonbuild/modules/gnome.py
  15. 12
      mesonbuild/modules/pkgconfig.py
  16. 2
      mesonbuild/modules/qt4.py
  17. 2
      mesonbuild/modules/qt5.py
  18. 2
      mesonbuild/modules/windows.py
  19. 2
      mesonbuild/mparser.py
  20. 7
      mesonbuild/optinterpreter.py
  21. 3
      mesonbuild/scripts/meson_benchmark.py
  22. 108
      mesonbuild/scripts/meson_test.py

@ -18,7 +18,7 @@ from .. import dependencies
from .. import mesonlib
import json
import subprocess
from ..coredata import MesonException
from ..mesonlib import MesonException
class InstallData():
def __init__(self, source_dir, build_dir, prefix):

@ -18,10 +18,9 @@ from .. import build
from .. import mlog
from .. import dependencies
from .. import compilers
from ..mesonlib import File
from ..mesonlib import File, MesonException
from .backends import InstallData
from ..build import InvalidArguments
from ..coredata import MesonException
import os, sys, pickle, re
import subprocess, shutil
@ -586,7 +585,12 @@ int dummy;
valgrind = environment.find_valgrind()
script_root = self.environment.get_script_dir()
test_data = os.path.join(self.environment.get_scratch_dir(), 'meson_test_setup.dat')
cmd = [sys.executable, self.environment.get_build_command(), '--internal', 'test', test_data]
cmd = [ sys.executable, self.environment.get_build_command(), '--internal', 'test' ]
if not self.environment.coredata.get_builtin_option('stdsplit'):
cmd += ['--no-stdsplit']
if self.environment.coredata.get_builtin_option('errorlogs'):
cmd += ['--print-errorlogs']
cmd += [ test_data ]
elem = NinjaBuildElement(self.all_outputs, 'test', 'CUSTOM_COMMAND', ['all', 'PHONY'])
elem.add_item('COMMAND', cmd)
elem.add_item('DESC', 'Running all tests.')

@ -25,7 +25,7 @@ from .. import dependencies
from .. import mlog
import xml.etree.ElementTree as ET
import xml.dom.minidom
from ..coredata import MesonException
from ..mesonlib import MesonException
from ..environment import Environment
class RegenInfo():

@ -17,7 +17,7 @@ from .. import build
from .. import mesonlib
import uuid, os, sys
from ..coredata import MesonException
from ..mesonlib import MesonException
class XCodeBackend(backends.Backend):
def __init__(self, build):

@ -17,7 +17,7 @@ from . import environment
from . import dependencies
from . import mlog
import copy, os
from .mesonlib import File, flatten
from .mesonlib import File, flatten, MesonException
known_basic_kwargs = {'install' : True,
'c_pch' : True,
@ -71,7 +71,7 @@ We are fully aware that these are not really usable or pleasant ways to do
this but it's the best we can do given the way shell quoting works.
'''
class InvalidArguments(coredata.MesonException):
class InvalidArguments(MesonException):
pass
class Build:
@ -299,10 +299,10 @@ class BuildTarget():
srclist = [srclist]
for src in srclist:
if not isinstance(src, str):
raise coredata.MesonException('Extraction arguments must be strings.')
raise MesonException('Extraction arguments must be strings.')
src = File(False, self.subdir, src)
if src not in self.sources:
raise coredata.MesonException('Tried to extract unknown source %s.' % src)
raise MesonException('Tried to extract unknown source %s.' % src)
obj_src.append(src)
return ExtractedObjects(self, obj_src)

@ -16,7 +16,7 @@ import subprocess, os.path
import tempfile
from .import mesonlib
from . import mlog
from .coredata import MesonException
from .mesonlib import MesonException
from . import coredata
"""This file contains the data files of all compilers Meson knows

@ -13,36 +13,10 @@
# limitations under the License.
import pickle, os, uuid
from .mesonlib import MesonException, default_libdir, default_libexecdir, default_prefix
version = '0.31.0.dev1'
build_types = ['plain', 'debug', 'debugoptimized', 'release']
layouts = ['mirror', 'flat']
warning_levels = ['1', '2', '3']
libtypelist = ['shared', 'static']
builtin_options = {'buildtype': True,
'strip': True,
'coverage': True,
'unity': True,
'prefix': True,
'libdir' : True,
'libexecdir' : True,
'bindir' : True,
'includedir' : True,
'datadir' : True,
'mandir' : True,
'localedir' : True,
'werror' : True,
'warning_level': True,
'layout' : True,
'default_library': True,
}
class MesonException(Exception):
def __init__(self, *args, **kwargs):
Exception.__init__(self, *args, **kwargs)
class UserOption:
def __init__(self, name, description, choices):
super().__init__()
@ -73,7 +47,7 @@ class UserStringOption(UserOption):
class UserBooleanOption(UserOption):
def __init__(self, name, description, value):
super().__init__(name, description, '[true, false]')
super().__init__(name, description, [ True, False ])
self.set_value(value)
def tobool(self, thing):
@ -140,7 +114,6 @@ class CoreData():
self.regen_guid = str(uuid.uuid4()).upper()
self.target_guids = {}
self.version = version
self.builtin_options = {}
self.init_builtins(options)
self.user_options = {}
self.compiler_options = {}
@ -158,36 +131,21 @@ class CoreData():
self.modules = {}
def init_builtins(self, options):
self.builtin_options['prefix'] = UserStringOption('prefix', 'Installation prefix', options.prefix)
self.builtin_options['libdir'] = UserStringOption('libdir', 'Library dir', options.libdir)
self.builtin_options['libexecdir'] = UserStringOption('libexecdir', 'Library executables dir', options.libexecdir)
self.builtin_options['bindir'] = UserStringOption('bindir', 'Executable dir', options.bindir)
self.builtin_options['includedir'] = UserStringOption('includedir', 'Include dir', options.includedir)
self.builtin_options['datadir'] = UserStringOption('datadir', 'Data directory', options.datadir)
self.builtin_options['mandir'] = UserStringOption('mandir', 'Man page dir', options.mandir)
self.builtin_options['localedir'] = UserStringOption('localedir', 'Locale dir', options.localedir)
self.builtin_options['backend'] = UserStringOption('backend', 'Backend to use', options.backend)
self.builtin_options['buildtype'] = UserComboOption('buildtype', 'Build type', build_types, options.buildtype)
self.builtin_options['strip'] = UserBooleanOption('strip', 'Strip on install', options.strip)
self.builtin_options['unity'] = UserBooleanOption('unity', 'Unity build', options.unity)
self.builtin_options['warning_level'] = UserComboOption('warning_level', 'Warning level', warning_levels, options.warning_level)
self.builtin_options['werror'] = UserBooleanOption('werror', 'Warnings are errors', options.werror)
self.builtin_options['layout'] = UserComboOption('layout', 'Build dir layout', layouts, options.layout)
self.builtin_options['default_library'] = UserComboOption('default_library', 'Default_library type', libtypelist, options.default_library)
self.builtins = {}
for key in get_builtin_options():
args = [key] + builtin_options[key][1:-1] + [ getattr(options, key, get_builtin_option_default(key)) ]
self.builtins[key] = builtin_options[key][0](*args)
def get_builtin_option(self, optname):
if optname in self.builtin_options:
return self.builtin_options[optname].value
raise RuntimeError('Tried to get unknown builtin option %s' % optname)
if optname in self.builtins:
return self.builtins[optname].value
raise RuntimeError('Tried to get unknown builtin option %s.' % optname)
def set_builtin_option(self, optname, value):
if optname in self.builtin_options:
self.builtin_options[optname].set_value(value)
if optname in self.builtins:
self.builtins[optname].set_value(value)
else:
raise RuntimeError('Tried to set unknown builtin option %s' % optname)
def is_builtin_option(self, optname):
return optname in self.builtin_options
raise RuntimeError('Tried to set unknown builtin option %s.' % optname)
def load(filename):
obj = pickle.load(open(filename, 'rb'))
@ -203,6 +161,59 @@ def save(obj, filename):
raise RuntimeError('Fatal version mismatch corruption.')
pickle.dump(obj, open(filename, 'wb'))
def get_builtin_options():
return list(builtin_options.keys())
def is_builtin_option(optname):
return optname in get_builtin_options()
def get_builtin_option_choices(optname):
if is_builtin_option(optname):
if builtin_options[optname][0] == UserStringOption:
return None
elif builtin_options[optname][0] == UserBooleanOption:
return [ True, False ]
else:
return builtin_options[optname][2]
else:
raise RuntimeError('Tried to get the supported values for an unknown builtin option \'%s\'.' % optname)
def get_builtin_option_description(optname):
if is_builtin_option(optname):
return builtin_options[optname][1]
else:
raise RuntimeError('Tried to get the description for an unknown builtin option \'%s\'.' % optname)
def get_builtin_option_default(optname):
if is_builtin_option(optname):
o = builtin_options[optname]
if o[0] == UserComboOption:
return o[3]
return o[2]
else:
raise RuntimeError('Tried to get the default value for an unknown builtin option \'%s\'.' % optname)
builtin_options = {
'buildtype' : [ UserComboOption, 'Build type to use.', [ 'plain', 'debug', 'debugoptimized', 'release' ], 'debug' ],
'strip' : [ UserBooleanOption, 'Strip targets on install.', False ],
'unity' : [ UserBooleanOption, 'Unity build.', False ],
'prefix' : [ UserStringOption, 'Installation prefix.', default_prefix() ],
'libdir' : [ UserStringOption, 'Library directory.', default_libdir() ],
'libexecdir' : [ UserStringOption, 'Library executable directory.', default_libexecdir() ],
'bindir' : [ UserStringOption, 'Executable directory.', 'bin' ],
'includedir' : [ UserStringOption, 'Header file directory.', 'include' ],
'datadir' : [ UserStringOption, 'Data file directory.', 'share' ],
'mandir' : [ UserStringOption, 'Manual page directory.', 'share/man' ],
'localedir' : [ UserStringOption, 'Locale data directory.', 'share/locale' ],
'werror' : [ UserBooleanOption, 'Treat warnings as errors.', False ],
'warning_level' : [ UserComboOption, 'Compiler warning level to use.', [ '1', '2', '3' ], '1'],
'layout' : [ UserComboOption, 'Build directory layout.', ['mirror', 'flat' ], 'mirror' ],
'default_library' : [ UserComboOption, 'Default library type.', [ 'shared', 'static' ], 'shared' ],
'backend' : [ UserComboOption, 'Backend to use.', [ 'ninja', 'vs2010', 'xcode' ], 'ninja' ],
'stdsplit' : [ UserBooleanOption, 'Split stdout and stderr in test logs.', True ],
'errorlogs' : [ UserBooleanOption, "Whether to print the logs from failing tests.", False ],
}
forbidden_target_names = {'clean': None,
'clean-gcno': None,
'clean-gcda': None,

@ -22,7 +22,7 @@
import re
import os, stat, glob, subprocess, shutil
import sysconfig
from . coredata import MesonException
from . mesonlib import MesonException
from . import mlog
from . import mesonlib

@ -19,7 +19,7 @@ import configparser
build_filename = 'meson.build'
class EnvironmentException(coredata.MesonException):
class EnvironmentException(mesonlib.MesonException):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -657,11 +657,11 @@ class CrossBuildInfo():
if 'target_machine' in self.config:
return
if not 'host_machine' in self.config:
raise coredata.MesonException('Cross info file must have either host or a target machine.')
raise mesonlib.MesonException('Cross info file must have either host or a target machine.')
if not 'properties' in self.config:
raise coredata.MesonException('Cross file is missing "properties".')
raise mesonlib.MesonException('Cross file is missing "properties".')
if not 'binaries' in self.config:
raise coredata.MesonException('Cross file is missing "binaries".')
raise mesonlib.MesonException('Cross file is missing "binaries".')
def ok_type(self, i):
return isinstance(i, str) or isinstance(i, int) or isinstance(i, bool)

@ -28,7 +28,7 @@ from functools import wraps
import importlib
class InterpreterException(coredata.MesonException):
class InterpreterException(mesonlib.MesonException):
pass
class InvalidCode(InterpreterException):
@ -932,7 +932,7 @@ class Interpreter():
assert(isinstance(code, str))
try:
self.ast = mparser.Parser(code).parse()
except coredata.MesonException as me:
except mesonlib.MesonException as me:
me.file = environment.build_filename
raise me
self.sanity_check_ast()
@ -1341,7 +1341,7 @@ class Interpreter():
return self.environment.coredata.compiler_options[optname].value
except KeyError:
pass
if optname not in coredata.builtin_options and self.is_subproject():
if not coredata.is_builtin_option(optname) and self.is_subproject():
optname = self.subproject + ':' + optname
try:
return self.environment.coredata.user_options[optname].value
@ -1364,8 +1364,7 @@ class Interpreter():
if '=' not in option:
raise InterpreterException('All default options must be of type key=value.')
key, value = option.split('=', 1)
builtin_options = self.coredata.builtin_options
if key in builtin_options:
if coredata.is_builtin_option(key):
if not self.environment.had_argument_for(key):
self.coredata.set_builtin_option(key, value)
# If this was set on the command line, do not override.
@ -1801,7 +1800,7 @@ class Interpreter():
assert(isinstance(code, str))
try:
codeblock = mparser.Parser(code).parse()
except coredata.MesonException as me:
except mesonlib.MesonException as me:
me.file = buildfilename
raise me
self.evaluate_codeblock(codeblock)

@ -18,7 +18,6 @@ import sys, os
import pickle
import argparse
from . import coredata, mesonlib
from .coredata import build_types, warning_levels, libtypelist
parser = argparse.ArgumentParser()
@ -26,7 +25,7 @@ parser.add_argument('-D', action='append', default=[], dest='sets',
help='Set an option to the given value.')
parser.add_argument('directory', nargs='*')
class ConfException(coredata.MesonException):
class ConfException(mesonlib.MesonException):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -62,7 +61,8 @@ class Conf:
longest_name = max(longest_name, len(x[0]))
longest_descr = max(longest_descr, len(x[1]))
longest_value = max(longest_value, len(str(x[2])))
longest_possible_value = max(longest_possible_value, len(x[3]))
if x[3]:
longest_possible_value = max(longest_possible_value, len(x[3]))
if longest_possible_value > 0:
titles[3] = 'Possible Values'
@ -71,10 +71,14 @@ class Conf:
for i in arr:
name = i[0]
descr = i[1]
value = i[2]
if isinstance(value, bool):
value = 'true' if value else 'false'
possible_values = i[3]
value = i[2] if isinstance(i[2], str) else str(i[2]).lower()
possible_values = ''
if isinstance(i[3], list):
if len(i[3]) > 0:
i[3] = [s if isinstance(s, str) else str(s).lower() for s in i[3]]
possible_values = '[%s]' % ', '.join(map(str, i[3]))
elif i[3]:
possible_values = i[3] if isinstance(i[3], str) else str(i[3]).lower()
namepad = ' '*(longest_name - len(name))
descrpad = ' '*(longest_descr - len(descr))
valuepad = ' '*(longest_value - len(str(value)))
@ -86,7 +90,7 @@ class Conf:
if '=' not in o:
raise ConfException('Value "%s" not of type "a=b".' % o)
(k, v) = o.split('=', 1)
if self.coredata.is_builtin_option(k):
if coredata.is_builtin_option(k):
self.coredata.set_builtin_option(k, v)
elif k in self.coredata.user_options:
tgt = self.coredata.user_options[k]
@ -123,13 +127,9 @@ class Conf:
print('')
print('Core options:')
carr = []
booleans = '[true, false]'
carr.append(['buildtype', 'Build type', self.coredata.get_builtin_option('buildtype'), build_types])
carr.append(['warning_level', 'Warning level', self.coredata.get_builtin_option('warning_level'), warning_levels])
carr.append(['werror', 'Treat warnings as errors', self.coredata.get_builtin_option('werror'), booleans])
carr.append(['strip', 'Strip on install', self.coredata.get_builtin_option('strip'), booleans])
carr.append(['unity', 'Unity build', self.coredata.get_builtin_option('unity'), booleans])
carr.append(['default_library', 'Default library type', self.coredata.get_builtin_option('default_library'), libtypelist])
for key in [ 'buildtype', 'warning_level', 'werror', 'strip', 'unity', 'default_library' ]:
carr.append([key, coredata.get_builtin_option_description(key),
self.coredata.get_builtin_option(key), coredata.get_builtin_option_choices(key)])
self.print_aligned(carr)
print('')
print('Base options:')
@ -164,14 +164,9 @@ class Conf:
print('')
print('Directories:')
parr = []
parr.append(['prefix', 'Install prefix', self.coredata.get_builtin_option('prefix'), ''])
parr.append(['libdir', 'Library directory', self.coredata.get_builtin_option('libdir'), ''])
parr.append(['libexecdir', 'Library executables directory', self.coredata.get_builtin_option('libexecdir'), ''])
parr.append(['bindir', 'Binary directory', self.coredata.get_builtin_option('bindir'), ''])
parr.append(['includedir', 'Header directory', self.coredata.get_builtin_option('includedir'), ''])
parr.append(['datadir', 'Data directory', self.coredata.get_builtin_option('datadir'), ''])
parr.append(['mandir', 'Man page directory', self.coredata.get_builtin_option('mandir'), ''])
parr.append(['localedir', 'Locale file directory', self.coredata.get_builtin_option('localedir'), ''])
for key in [ 'prefix', 'libdir', 'libexecdir', 'bindir', 'includedir', 'datadir', 'mandir', 'localedir' ]:
parr.append([key, coredata.get_builtin_option_description(key),
self.coredata.get_builtin_option(key), coredata.get_builtin_option_choices(key)])
self.print_aligned(parr)
print('')
print('Project options:')
@ -192,6 +187,13 @@ class Conf:
choices = str(opt.choices);
optarr.append([key, opt.description, opt.value, choices])
self.print_aligned(optarr)
print('')
print('Testing options:')
tarr = []
for key in [ 'stdsplit', 'errorlogs' ]:
tarr.append([key, coredata.get_builtin_option_description(key),
self.coredata.get_builtin_option(key), coredata.get_builtin_option_choices(key)])
self.print_aligned(tarr)
def run(args):
args = mesonlib.expand_arguments(args)

@ -18,7 +18,9 @@ import platform, subprocess, operator, os, shutil, re, sys
from glob import glob
from .coredata import MesonException
class MesonException(Exception):
def __init__(self, *args, **kwargs):
Exception.__init__(self, *args, **kwargs)
class File:
def __init__(self, is_built, subdir, fname):
@ -177,6 +179,9 @@ def default_libexecdir():
# There is no way to auto-detect this, so it must be set at build time
return 'libexec'
def default_prefix():
return 'c:/' if is_windows() else '/usr/local'
def get_library_dirs():
if is_windows():
return ['C:/mingw/lib'] # Fixme

@ -21,8 +21,7 @@ from . import environment, interpreter, mesonlib
from . import build
import platform
from . import mlog, coredata
from .coredata import MesonException, build_types, layouts, warning_levels, libtypelist
from .mesonlib import MesonException
backendlist = ['ninja', 'vs2010', 'xcode']
@ -30,49 +29,40 @@ parser = argparse.ArgumentParser()
default_warning = '1'
if mesonlib.is_windows():
def_prefix = 'c:/'
else:
def_prefix = '/usr/local'
def add_builtin_argument(name, **kwargs):
k = kwargs.get('dest', name.replace('-', '_'))
c = coredata.get_builtin_option_choices(k)
b = True if kwargs.get('action', None) in [ 'store_true', 'store_false' ] else False
h = coredata.get_builtin_option_description(k)
if not b:
h = h.rstrip('.') + ' (default: %s).' % coredata.get_builtin_option_default(k)
if c and not b:
kwargs['choices'] = c
parser.add_argument('--' + name, default=coredata.get_builtin_option_default(k), help=h, **kwargs)
add_builtin_argument('prefix')
add_builtin_argument('libdir')
add_builtin_argument('libexecdir')
add_builtin_argument('bindir')
add_builtin_argument('includedir')
add_builtin_argument('datadir')
add_builtin_argument('mandir')
add_builtin_argument('localedir')
add_builtin_argument('backend')
add_builtin_argument('buildtype')
add_builtin_argument('strip', action='store_true')
add_builtin_argument('unity', action='store_true')
add_builtin_argument('werror', action='store_true')
add_builtin_argument('layout')
add_builtin_argument('default-library')
add_builtin_argument('warnlevel', dest='warning_level')
parser.add_argument('--prefix', default=def_prefix, dest='prefix',
help='the installation prefix (default: %(default)s)')
parser.add_argument('--libdir', default=mesonlib.default_libdir(), dest='libdir',
help='the installation subdir of libraries (default: %(default)s)')
parser.add_argument('--libexecdir', default=mesonlib.default_libexecdir(), dest='libexecdir',
help='the installation subdir of library executables (default: %(default)s)')
parser.add_argument('--bindir', default='bin', dest='bindir',
help='the installation subdir of executables (default: %(default)s)')
parser.add_argument('--includedir', default='include', dest='includedir',
help='relative path of installed headers (default: %(default)s)')
parser.add_argument('--datadir', default='share', dest='datadir',
help='relative path to the top of data file subdirectory (default: %(default)s)')
parser.add_argument('--mandir', default='share/man', dest='mandir',
help='relative path of man files (default: %(default)s)')
parser.add_argument('--localedir', default='share/locale', dest='localedir',
help='relative path of locale data (default: %(default)s)')
parser.add_argument('--backend', default='ninja', dest='backend', choices=backendlist,
help='backend to use (default: %(default)s)')
parser.add_argument('--buildtype', default='debug', choices=build_types, dest='buildtype',
help='build type go use (default: %(default)s)')
parser.add_argument('--strip', action='store_true', dest='strip', default=False,\
help='strip targets on install (default: %(default)s)')
parser.add_argument('--unity', action='store_true', dest='unity', default=False,\
help='unity build')
parser.add_argument('--werror', action='store_true', dest='werror', default=False,\
help='Treat warnings as errors')
parser.add_argument('--layout', choices=layouts, dest='layout', default='mirror',\
help='Build directory layout.')
parser.add_argument('--default-library', choices=libtypelist, dest='default_library',
default='shared', help='Default library type.')
parser.add_argument('--warnlevel', default=default_warning, dest='warning_level', choices=warning_levels,\
help='Level of compiler warnings to use (larger is more, default is %(default)s)')
parser.add_argument('--cross-file', default=None, dest='cross_file',
help='file describing cross compilation environment')
parser.add_argument('--cross-file', default=None,
help='File describing cross compilation environment.')
parser.add_argument('-D', action='append', dest='projectoptions', default=[],
help='Set project options.')
parser.add_argument('-v', '--version', action='store_true', dest='print_version', default=False,
help='Print version.')
help='Print version information.')
parser.add_argument('directories', nargs='*')
class MesonApp():

@ -18,7 +18,7 @@ functionality such as gobject-introspection and gresources.'''
from .. import build
import os, sys
import subprocess
from ..coredata import MesonException
from ..mesonlib import MesonException
from .. import mlog
from .. import mesonlib

@ -55,7 +55,7 @@ class PkgConfigModule:
def generate(self, state, args, kwargs):
if len(args) > 0:
raise coredata.MesonException('Pkgconfig_gen takes no positional arguments.')
raise mesonlib.MesonException('Pkgconfig_gen takes no positional arguments.')
libs = kwargs.get('libraries', [])
if not isinstance(libs, list):
libs = [libs]
@ -64,22 +64,22 @@ class PkgConfigModule:
if hasattr(l, 'held_object'):
l = l.held_object
if not (isinstance(l, build.SharedLibrary) or isinstance(l, build.StaticLibrary)):
raise coredata.MesonException('Library argument not a library object.')
raise mesonlib.MesonException('Library argument not a library object.')
processed_libs.append(l)
libs = processed_libs
subdirs = mesonlib.stringlistify(kwargs.get('subdirs', ['.']))
version = kwargs.get('version', '')
if not isinstance(version, str):
raise coredata.MesonException('Version must be a string.')
raise mesonlib.MesonException('Version must be a string.')
name = kwargs.get('name', None)
if not isinstance(name, str):
raise coredata.MesonException('Name not specified.')
raise mesonlib.MesonException('Name not specified.')
filebase = kwargs.get('filebase', name)
if not isinstance(filebase, str):
raise coredata.MesonException('Filebase must be a string.')
raise mesonlib.MesonException('Filebase must be a string.')
description = kwargs.get('description', None)
if not isinstance(description, str):
raise coredata.MesonException('Description is not a string.')
raise mesonlib.MesonException('Description is not a string.')
pub_reqs = mesonlib.stringlistify(kwargs.get('requires', []))
priv_reqs = mesonlib.stringlistify(kwargs.get('requires_private', []))
priv_libs = mesonlib.stringlistify(kwargs.get('libraries_private', []))

@ -15,7 +15,7 @@
from .. import dependencies, mlog
import os, subprocess
from .. import build
from ..coredata import MesonException
from ..mesonlib import MesonException
import xml.etree.ElementTree as ET
class Qt4Module():

@ -15,7 +15,7 @@
from .. import dependencies, mlog
import os, subprocess
from .. import build
from ..coredata import MesonException
from ..mesonlib import MesonException
import xml.etree.ElementTree as ET
class Qt5Module():

@ -13,7 +13,7 @@
# limitations under the License.
from .. import mesonlib, dependencies, build
from ..coredata import MesonException
from ..mesonlib import MesonException
import os
class WindowsModule:

@ -13,7 +13,7 @@
# limitations under the License.
import re
from .coredata import MesonException
from .mesonlib import MesonException
class ParseException(MesonException):
def __init__(self, text, lineno, colno):

@ -14,9 +14,10 @@
from . import mparser
from . import coredata
from . import mesonlib
import os, re
forbidden_option_names = coredata.builtin_options
forbidden_option_names = coredata.get_builtin_options()
forbidden_prefixes = {'c_': True,
'cpp_': True,
'rust_': True,
@ -37,7 +38,7 @@ def is_invalid_name(name):
return True
return False
class OptionException(coredata.MesonException):
class OptionException(mesonlib.MesonException):
pass
optname_regex = re.compile('[^a-zA-Z0-9_-]')
@ -77,7 +78,7 @@ class OptionInterpreter:
def process(self, option_file):
try:
ast = mparser.Parser(open(option_file, 'r', encoding='utf8').read()).parse()
except coredata.MesonException as me:
except mesonlib.MesonException as me:
me.file = option_file
raise me
if not isinstance(ast, mparser.CodeBlockNode):

@ -39,9 +39,10 @@ def print_json_log(jsonlogfile, rawruns, test_name, i):
for r in rawruns:
runobj = {'duration': r.duration,
'stdout': r.stdo,
'stderr': r.stde,
'returncode' : r.returncode,
'duration' : r.duration}
if r.stde:
runobj['stderr'] = r.stde
runs.append(runobj)
jsonobj['runs'] = runs
jsonlogfile.write(json.dumps(jsonobj) + '\n')

@ -25,7 +25,9 @@ def is_windows():
platname = platform.system().lower()
return platname == 'windows' or 'mingw' in platname
tests_failed = []
collected_logs = []
error_count = 0
options = None
parser = argparse.ArgumentParser()
parser.add_argument('--wrapper', default=None, dest='wrapper',
@ -34,17 +36,41 @@ parser.add_argument('--wd', default=None, dest='wd',
help='directory to cd into before running')
parser.add_argument('--suite', default=None, dest='suite',
help='Only run tests belonging to this 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 faling tests' logs.")
parser.add_argument('args', nargs='+')
class TestRun():
def __init__(self, res, returncode, duration, stdo, stde, cmd):
def __init__(self, res, returncode, should_fail, duration, stdo, stde, cmd):
self.res = res
self.returncode = returncode
self.duration = duration
self.stdo = stdo
self.stde = stde
self.cmd = cmd
self.should_fail = should_fail
def get_log(self):
res = '--- command ---\n'
if self.cmd is None:
res += 'NONE\n'
else:
res += ' '.join(self.cmd) + '\n'
if self.stdo:
res += '--- stdout ---\n'
res += self.stdo
if self.stde:
if res[-1:] != '\n':
res += '\n'
res += '--- stderr ---\n'
res += self.stde
if res[-1:] != '\n':
res += '\n'
res += '-------\n\n'
return res
def decode(stream):
try:
@ -52,28 +78,16 @@ def decode(stream):
except UnicodeDecodeError:
return stream.decode('iso-8859-1', errors='ignore')
def write_log(logfile, test_name, result_str, result):
logfile.write(result_str + '\n\n')
logfile.write('--- command ---\n')
if result.cmd is None:
logfile.write('NONE')
else:
logfile.write(' '.join(result.cmd))
logfile.write('\n--- "%s" stdout ---\n' % test_name)
logfile.write(result.stdo)
logfile.write('\n--- "%s" stderr ---\n' % test_name)
logfile.write(result.stde)
logfile.write('\n-------\n\n')
def write_json_log(jsonlogfile, test_name, result):
result = {'name' : test_name,
jresult = {'name' : test_name,
'stdout' : result.stdo,
'stderr' : result.stde,
'result' : result.res,
'duration' : result.duration,
'returncode' : result.returncode,
'command' : result.cmd}
jsonlogfile.write(json.dumps(result) + '\n')
if result.stde:
jresult['stderr'] = result.stde
jsonlogfile.write(json.dumps(jresult) + '\n')
def run_with_mono(fname):
if fname.endswith('.exe') and not is_windows():
@ -81,7 +95,7 @@ def run_with_mono(fname):
return False
def run_single_test(wrap, test):
global tests_failed
global options
if test.fname[0].endswith('.jar'):
cmd = ['java', '-jar'] + test.fname
elif not test.is_cross and run_with_mono(test.fname[0]):
@ -102,7 +116,7 @@ def run_single_test(wrap, test):
res = 'SKIP'
duration = 0.0
stdo = 'Not run because can not execute cross compiled binaries.'
stde = ''
stde = None
returncode = -1
else:
cmd = wrap + cmd + test.cmd_args
@ -117,7 +131,7 @@ def run_single_test(wrap, test):
setsid = os.setsid
p = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stderr=subprocess.PIPE if options and options.split else subprocess.STDOUT,
env=child_env,
cwd=test.workdir,
preexec_fn=setsid)
@ -137,20 +151,20 @@ def run_single_test(wrap, test):
endtime = time.time()
duration = endtime - starttime
stdo = decode(stdo)
stde = decode(stde)
if stde:
stde = decode(stde)
if timed_out:
res = 'TIMEOUT'
tests_failed.append((test.name, stdo, stde))
elif (not test.should_fail and p.returncode == 0) or \
(test.should_fail and p.returncode != 0):
res = 'OK'
else:
res = 'FAIL'
tests_failed.append((test.name, stdo, stde))
returncode = p.returncode
return TestRun(res, returncode, duration, stdo, stde, cmd)
return TestRun(res, returncode, test.should_fail, duration, stdo, stde, cmd)
def print_stats(numlen, tests, name, result, i, logfile, jsonlogfile):
global collected_logs, error_count, options
startpad = ' '*(numlen - len('%d' % (i+1)))
num = '%s%d/%d' % (startpad, i+1, len(tests))
padding1 = ' '*(38-len(name))
@ -158,7 +172,12 @@ def print_stats(numlen, tests, name, result, i, logfile, jsonlogfile):
result_str = '%s %s %s%s%s%5.2f s' % \
(num, name, padding1, result.res, padding2, result.duration)
print(result_str)
write_log(logfile, name, result_str, result)
result_str += "\n\n" + result.get_log()
if (result.returncode != 0) != result.should_fail:
error_count += 1
if options.print_errorlogs:
collected_logs.append(result_str)
logfile.write(result_str)
write_json_log(jsonlogfile, name, result)
def drain_futures(futures):
@ -171,7 +190,8 @@ def filter_tests(suite, tests):
return tests
return [x for x in tests if suite in x.suite]
def run_tests(options, datafilename):
def run_tests(datafilename):
global options
logfile_base = 'meson-logs/testlog'
if options.wrapper is None:
wrap = []
@ -222,8 +242,9 @@ def run_tests(options, datafilename):
return logfilename
def run(args):
global tests_failed
tests_failed = [] # To avoid state leaks when invoked multiple times (running tests in-process)
global collected_logs, error_count, options
collected_logs = [] # To avoid state leaks when invoked multiple times (running tests in-process)
error_count = 0
options = parser.parse_args(args)
if len(options.args) != 1:
print('Test runner for Meson. Do not run on your own, mmm\'kay?')
@ -231,19 +252,22 @@ def run(args):
if options.wd is not None:
os.chdir(options.wd)
datafile = options.args[0]
logfilename = run_tests(options, datafile)
returncode = 0
if len(tests_failed) > 0:
print('\nOutput of failed tests (max 10):')
for (name, stdo, stde) in tests_failed[:10]:
print("{} stdout:\n".format(name))
print(stdo)
print('\n{} stderr:\n'.format(name))
print(stde)
print('\n')
returncode = 1
print('\nFull log written to %s.' % logfilename)
return returncode
logfilename = run_tests(datafile)
if len(collected_logs) > 0:
if len(collected_logs) > 10:
print('\nThe output from 10 first failed tests:\n')
else:
print('\nThe output from the failed tests:\n')
for log in collected_logs[:10]:
lines = log.splitlines()
if len(lines) > 100:
print(line[0])
print('--- Listing only the last 100 lines from a long log. ---')
lines = lines[-99:]
for line in lines:
print(line)
print('Full log written to %s.' % logfilename)
return error_count
if __name__ == '__main__':
sys.exit(run(sys.argv[1:]))

Loading…
Cancel
Save