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. 46
      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. 106
      mesonbuild/scripts/meson_test.py

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

@ -18,10 +18,9 @@ from .. import build
from .. import mlog from .. import mlog
from .. import dependencies from .. import dependencies
from .. import compilers from .. import compilers
from ..mesonlib import File from ..mesonlib import File, MesonException
from .backends import InstallData from .backends import InstallData
from ..build import InvalidArguments from ..build import InvalidArguments
from ..coredata import MesonException
import os, sys, pickle, re import os, sys, pickle, re
import subprocess, shutil import subprocess, shutil
@ -586,7 +585,12 @@ int dummy;
valgrind = environment.find_valgrind() valgrind = environment.find_valgrind()
script_root = self.environment.get_script_dir() script_root = self.environment.get_script_dir()
test_data = os.path.join(self.environment.get_scratch_dir(), 'meson_test_setup.dat') 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 = NinjaBuildElement(self.all_outputs, 'test', 'CUSTOM_COMMAND', ['all', 'PHONY'])
elem.add_item('COMMAND', cmd) elem.add_item('COMMAND', cmd)
elem.add_item('DESC', 'Running all tests.') elem.add_item('DESC', 'Running all tests.')

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

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

@ -17,7 +17,7 @@ from . import environment
from . import dependencies from . import dependencies
from . import mlog from . import mlog
import copy, os import copy, os
from .mesonlib import File, flatten from .mesonlib import File, flatten, MesonException
known_basic_kwargs = {'install' : True, known_basic_kwargs = {'install' : True,
'c_pch' : 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. this but it's the best we can do given the way shell quoting works.
''' '''
class InvalidArguments(coredata.MesonException): class InvalidArguments(MesonException):
pass pass
class Build: class Build:
@ -299,10 +299,10 @@ class BuildTarget():
srclist = [srclist] srclist = [srclist]
for src in srclist: for src in srclist:
if not isinstance(src, str): 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) src = File(False, self.subdir, src)
if src not in self.sources: 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) obj_src.append(src)
return ExtractedObjects(self, obj_src) return ExtractedObjects(self, obj_src)

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

@ -13,36 +13,10 @@
# limitations under the License. # limitations under the License.
import pickle, os, uuid import pickle, os, uuid
from .mesonlib import MesonException, default_libdir, default_libexecdir, default_prefix
version = '0.31.0.dev1' 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: class UserOption:
def __init__(self, name, description, choices): def __init__(self, name, description, choices):
super().__init__() super().__init__()
@ -73,7 +47,7 @@ class UserStringOption(UserOption):
class UserBooleanOption(UserOption): class UserBooleanOption(UserOption):
def __init__(self, name, description, value): def __init__(self, name, description, value):
super().__init__(name, description, '[true, false]') super().__init__(name, description, [ True, False ])
self.set_value(value) self.set_value(value)
def tobool(self, thing): def tobool(self, thing):
@ -140,7 +114,6 @@ class CoreData():
self.regen_guid = str(uuid.uuid4()).upper() self.regen_guid = str(uuid.uuid4()).upper()
self.target_guids = {} self.target_guids = {}
self.version = version self.version = version
self.builtin_options = {}
self.init_builtins(options) self.init_builtins(options)
self.user_options = {} self.user_options = {}
self.compiler_options = {} self.compiler_options = {}
@ -158,36 +131,21 @@ class CoreData():
self.modules = {} self.modules = {}
def init_builtins(self, options): def init_builtins(self, options):
self.builtin_options['prefix'] = UserStringOption('prefix', 'Installation prefix', options.prefix) self.builtins = {}
self.builtin_options['libdir'] = UserStringOption('libdir', 'Library dir', options.libdir) for key in get_builtin_options():
self.builtin_options['libexecdir'] = UserStringOption('libexecdir', 'Library executables dir', options.libexecdir) args = [key] + builtin_options[key][1:-1] + [ getattr(options, key, get_builtin_option_default(key)) ]
self.builtin_options['bindir'] = UserStringOption('bindir', 'Executable dir', options.bindir) self.builtins[key] = builtin_options[key][0](*args)
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)
def get_builtin_option(self, optname): def get_builtin_option(self, optname):
if optname in self.builtin_options: if optname in self.builtins:
return self.builtin_options[optname].value return self.builtins[optname].value
raise RuntimeError('Tried to get unknown builtin option %s' % optname) raise RuntimeError('Tried to get unknown builtin option %s.' % optname)
def set_builtin_option(self, optname, value): def set_builtin_option(self, optname, value):
if optname in self.builtin_options: if optname in self.builtins:
self.builtin_options[optname].set_value(value) self.builtins[optname].set_value(value)
else: else:
raise RuntimeError('Tried to set unknown builtin option %s' % optname) raise RuntimeError('Tried to set unknown builtin option %s.' % optname)
def is_builtin_option(self, optname):
return optname in self.builtin_options
def load(filename): def load(filename):
obj = pickle.load(open(filename, 'rb')) obj = pickle.load(open(filename, 'rb'))
@ -203,6 +161,59 @@ def save(obj, filename):
raise RuntimeError('Fatal version mismatch corruption.') raise RuntimeError('Fatal version mismatch corruption.')
pickle.dump(obj, open(filename, 'wb')) 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, forbidden_target_names = {'clean': None,
'clean-gcno': None, 'clean-gcno': None,
'clean-gcda': None, 'clean-gcda': None,

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

@ -19,7 +19,7 @@ import configparser
build_filename = 'meson.build' build_filename = 'meson.build'
class EnvironmentException(coredata.MesonException): class EnvironmentException(mesonlib.MesonException):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -657,11 +657,11 @@ class CrossBuildInfo():
if 'target_machine' in self.config: if 'target_machine' in self.config:
return return
if not 'host_machine' in self.config: 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: 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: 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): def ok_type(self, i):
return isinstance(i, str) or isinstance(i, int) or isinstance(i, bool) return isinstance(i, str) or isinstance(i, int) or isinstance(i, bool)

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

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

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

@ -21,8 +21,7 @@ from . import environment, interpreter, mesonlib
from . import build from . import build
import platform import platform
from . import mlog, coredata from . import mlog, coredata
from .mesonlib import MesonException
from .coredata import MesonException, build_types, layouts, warning_levels, libtypelist
backendlist = ['ninja', 'vs2010', 'xcode'] backendlist = ['ninja', 'vs2010', 'xcode']
@ -30,49 +29,40 @@ parser = argparse.ArgumentParser()
default_warning = '1' default_warning = '1'
if mesonlib.is_windows(): def add_builtin_argument(name, **kwargs):
def_prefix = 'c:/' k = kwargs.get('dest', name.replace('-', '_'))
else: c = coredata.get_builtin_option_choices(k)
def_prefix = '/usr/local' 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', parser.add_argument('--cross-file', default=None,
help='the installation prefix (default: %(default)s)') help='File describing cross compilation environment.')
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('-D', action='append', dest='projectoptions', default=[], parser.add_argument('-D', action='append', dest='projectoptions', default=[],
help='Set project options.') help='Set project options.')
parser.add_argument('-v', '--version', action='store_true', dest='print_version', default=False, 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='*') parser.add_argument('directories', nargs='*')
class MesonApp(): class MesonApp():

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

@ -55,7 +55,7 @@ class PkgConfigModule:
def generate(self, state, args, kwargs): def generate(self, state, args, kwargs):
if len(args) > 0: 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', []) libs = kwargs.get('libraries', [])
if not isinstance(libs, list): if not isinstance(libs, list):
libs = [libs] libs = [libs]
@ -64,22 +64,22 @@ class PkgConfigModule:
if hasattr(l, 'held_object'): if hasattr(l, 'held_object'):
l = l.held_object l = l.held_object
if not (isinstance(l, build.SharedLibrary) or isinstance(l, build.StaticLibrary)): 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) processed_libs.append(l)
libs = processed_libs libs = processed_libs
subdirs = mesonlib.stringlistify(kwargs.get('subdirs', ['.'])) subdirs = mesonlib.stringlistify(kwargs.get('subdirs', ['.']))
version = kwargs.get('version', '') version = kwargs.get('version', '')
if not isinstance(version, str): 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) name = kwargs.get('name', None)
if not isinstance(name, str): if not isinstance(name, str):
raise coredata.MesonException('Name not specified.') raise mesonlib.MesonException('Name not specified.')
filebase = kwargs.get('filebase', name) filebase = kwargs.get('filebase', name)
if not isinstance(filebase, str): 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) description = kwargs.get('description', None)
if not isinstance(description, str): 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', [])) pub_reqs = mesonlib.stringlistify(kwargs.get('requires', []))
priv_reqs = mesonlib.stringlistify(kwargs.get('requires_private', [])) priv_reqs = mesonlib.stringlistify(kwargs.get('requires_private', []))
priv_libs = mesonlib.stringlistify(kwargs.get('libraries_private', [])) priv_libs = mesonlib.stringlistify(kwargs.get('libraries_private', []))

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

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

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

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

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

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

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

Loading…
Cancel
Save