diff options
50 files changed, 788 insertions, 269 deletions
diff --git a/authors.txt b/authors.txt index 2c2a4df..18863eb 100644 --- a/authors.txt +++ b/authors.txt @@ -60,3 +60,4 @@ Daniel Stone Marc-Antoine Perennou Matthieu Gautier Kseniia Vasilchuk +Philipp Geier diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index ac184f0..7e3f936 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -197,15 +197,20 @@ class Backend(): def serialise_executable(self, exe, cmd_args, workdir, env={}, capture=None): - import uuid + import hashlib # Can't just use exe.name here; it will likely be run more than once if isinstance(exe, (dependencies.ExternalProgram, build.BuildTarget, build.CustomTarget)): basename = exe.name else: basename = os.path.basename(exe) - scratch_file = 'meson_exe_{0}_{1}.dat'.format(basename, - str(uuid.uuid4())[:8]) + # Take a digest of the cmd args, env, workdir, and capture. This avoids + # collisions and also makes the name deterministic over regenerations + # which avoids a rebuild by Ninja because the cmdline stays the same. + data = bytes(str(sorted(env.items())) + str(cmd_args) + str(workdir) + str(capture), + encoding='utf-8') + digest = hashlib.sha1(data).hexdigest() + scratch_file = 'meson_exe_{0}_{1}.dat'.format(basename, digest) exe_data = os.path.join(self.environment.get_scratch_dir(), scratch_file) with open(exe_data, 'wb') as f: if isinstance(exe, dependencies.ExternalProgram): diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 77d8b1c..429bb64 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1837,6 +1837,8 @@ rule FORTRAN_DEP_HACK pchlist = [] if len(pchlist) == 0: pch_dep = [] + elif compiler.id == 'intel': + pch_dep = [] else: arr = [] i = os.path.join(self.get_target_private_dir(target), compiler.get_pch_name(pchlist[0])) @@ -1956,6 +1958,9 @@ rule FORTRAN_DEP_HACK src = os.path.join(self.build_to_src, target.get_source_subdir(), pch[-1]) (commands, dep, dst, objs) = self.generate_msvc_pch_command(target, compiler, pch) extradep = os.path.join(self.build_to_src, target.get_source_subdir(), pch[0]) + elif compiler.id == 'intel': + # Intel generates on target generation + continue else: src = os.path.join(self.build_to_src, target.get_source_subdir(), pch[0]) (commands, dep, dst, objs) = self.generate_gcc_pch_command(target, compiler, pch[0]) @@ -2104,7 +2109,6 @@ rule FORTRAN_DEP_HACK e = NinjaBuildElement(self.all_outputs, 'clean-ctlist', 'CUSTOM_COMMAND', 'PHONY') d = CleanTrees(self.environment.get_build_dir(), trees) d_file = os.path.join(self.environment.get_scratch_dir(), 'cleantrees.dat') - script_root = self.environment.get_script_dir() e.add_item('COMMAND', [sys.executable, self.environment.get_build_command(), '--internal', 'cleantrees', d_file]) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 449afe7..f895531 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -98,6 +98,7 @@ class Build: self.dep_manifest_name = None self.dep_manifest = {} self.cross_stdlibs = {} + self.test_setups = {} def add_compiler(self, compiler): if self.static_linker is None and compiler.needs_static_linker(): @@ -1507,3 +1508,10 @@ class RunScript(dict): assert(isinstance(args, list)) self['exe'] = script self['args'] = args + +class TestSetup: + def __init__(self, *, exe_wrapper=None, gdb=None, timeout_multiplier=None, env=None): + self.exe_wrapper = exe_wrapper + self.gdb = gdb + self.timeout_multiplier = timeout_multiplier + self.env = env diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py index 06f76e4..bd8d0a1 100644 --- a/mesonbuild/compilers.py +++ b/mesonbuild/compilers.py @@ -33,7 +33,9 @@ lib_suffixes = ('a', 'lib', 'dll', 'dylib', 'so') lang_suffixes = { 'c': ('c',), 'cpp': ('cpp', 'cc', 'cxx', 'c++', 'hh', 'hpp', 'ipp', 'hxx'), - 'fortran': ('f', 'f90', 'f95'), + # f90, f95, f03, f08 are for free-form fortran ('f90' recommended) + # f, for, ftn, fpp are for fixed-form fortran ('f' or 'for' recommended) + 'fortran': ('f90', 'f95', 'f03', 'f08', 'f', 'for', 'ftn', 'fpp'), 'd': ('d', 'di'), 'objc': ('m',), 'objcpp': ('mm',), @@ -1683,9 +1685,10 @@ class GnuDCompiler(DCompiler): def __init__(self, exelist, version, is_cross): DCompiler.__init__(self, exelist, version, is_cross) self.id = 'gcc' - self.warn_args = {'1': ['-Wall', '-Wdeprecated'], - '2': ['-Wall', '-Wextra', '-Wdeprecated'], - '3': ['-Wall', '-Wextra', '-Wdeprecated', '-Wpedantic']} + default_warn_args = ['-Wall', '-Wdeprecated'] + self.warn_args = {'1': default_warn_args, + '2': default_warn_args + ['-Wextra'], + '3': default_warn_args + ['-Wextra', '-Wpedantic']} self.base_options = ['b_colorout', 'b_sanitize', 'b_staticpic'] def get_colorout_args(self, colortype): @@ -2064,6 +2067,10 @@ CLANG_OSX = 1 CLANG_WIN = 2 # Possibly clang-cl? +ICC_STANDARD = 0 +ICC_OSX = 1 +ICC_WIN = 2 + def get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module): if soversion is None: sostr = '' @@ -2072,7 +2079,6 @@ def get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, i if gcc_type == GCC_STANDARD or gcc_type == GCC_MINGW: # Might not be correct for mingw but seems to work. return ['-Wl,-soname,%s%s.%s%s' % (prefix, shlib_name, suffix, sostr)] - return ['-Wl,-soname,%s%s' % (shlib_name, sostr)] elif gcc_type == GCC_OSX: if is_shared_module: return [] @@ -2149,9 +2155,10 @@ class GnuCCompiler(GnuCompiler, CCompiler): def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None): CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) GnuCompiler.__init__(self, gcc_type, defines) - self.warn_args = {'1': ['-Wall', '-Winvalid-pch'], - '2': ['-Wall', '-Wextra', '-Winvalid-pch'], - '3': ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch']} + default_warn_args = ['-Wall', '-Winvalid-pch'] + self.warn_args = {'1': default_warn_args, + '2': default_warn_args + ['-Wextra'], + '3': default_warn_args + ['-Wextra', '-Wpedantic']} def get_options(self): opts = {'c_std': coredata.UserComboOption('c_std', 'C language standard to use', @@ -2185,9 +2192,10 @@ class GnuCPPCompiler(GnuCompiler, CPPCompiler): def __init__(self, exelist, version, gcc_type, is_cross, exe_wrap, defines): CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap) GnuCompiler.__init__(self, gcc_type, defines) - self.warn_args = {'1': ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'], - '2': ['-Wall', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor'], - '3': ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor']} + default_warn_args = ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'] + self.warn_args = {'1': default_warn_args, + '2': default_warn_args + ['-Wextra'], + '3': default_warn_args + ['-Wextra', '-Wpedantic']} def get_options(self): opts = {'cpp_std': coredata.UserComboOption('cpp_std', 'C++ language standard to use', @@ -2224,6 +2232,7 @@ class GnuCPPCompiler(GnuCompiler, CPPCompiler): # too strict without this and always fails. return self.get_no_optimization_args() + ['-fpermissive'] + class GnuObjCCompiler(GnuCompiler, ObjCCompiler): def __init__(self, exelist, version, is_cross, exe_wrapper=None, defines=None): @@ -2231,9 +2240,10 @@ class GnuObjCCompiler(GnuCompiler, ObjCCompiler): # Not really correct, but GNU objc is only used on non-OSX non-win. File a bug # if this breaks your use case. GnuCompiler.__init__(self, GCC_STANDARD, defines) - self.warn_args = {'1': ['-Wall', '-Winvalid-pch'], - '2': ['-Wall', '-Wextra', '-Winvalid-pch'], - '3': ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch']} + default_warn_args = ['-Wall', '-Winvalid-pch'] + self.warn_args = {'1': default_warn_args, + '2': default_warn_args + ['-Wextra'], + '3': default_warn_args + ['-Wextra', '-Wpedantic']} class GnuObjCPPCompiler(GnuCompiler, ObjCPPCompiler): @@ -2242,9 +2252,10 @@ class GnuObjCPPCompiler(GnuCompiler, ObjCPPCompiler): # Not really correct, but GNU objc is only used on non-OSX non-win. File a bug # if this breaks your use case. GnuCompiler.__init__(self, GCC_STANDARD, defines) - self.warn_args = {'1': ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'], - '2': ['-Wall', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor'], - '3': ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor']} + default_warn_args = ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'] + self.warn_args = {'1': default_warn_args, + '2': default_warn_args + ['-Wextra'], + '3': default_warn_args + ['-Wextra', '-Wpedantic']} def get_compiler_check_args(self): # -fpermissive allows non-conforming code to compile which is necessary @@ -2284,7 +2295,7 @@ class ClangCompiler(): # Workaround for Clang bug http://llvm.org/bugs/show_bug.cgi?id=15136 # This flag is internal to Clang (or at least not documented on the man page) # so it might change semantics at any time. - return ['-include-pch', os.path.join (pch_dir, self.get_pch_name (header))] + return ['-include-pch', os.path.join(pch_dir, self.get_pch_name(header))] def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): if self.clang_type == CLANG_STANDARD: @@ -2322,9 +2333,10 @@ class ClangCCompiler(ClangCompiler, CCompiler): def __init__(self, exelist, version, clang_type, is_cross, exe_wrapper=None): CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) ClangCompiler.__init__(self, clang_type) - self.warn_args = {'1': ['-Wall', '-Winvalid-pch'], - '2': ['-Wall', '-Wextra', '-Winvalid-pch'], - '3': ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch']} + default_warn_args = ['-Wall', '-Winvalid-pch'] + self.warn_args = {'1': default_warn_args, + '2': default_warn_args + ['-Wextra'], + '3': default_warn_args + ['-Wextra', '-Wpedantic']} def get_options(self): return {'c_std': coredata.UserComboOption('c_std', 'C language standard to use', @@ -2347,14 +2359,15 @@ class ClangCPPCompiler(ClangCompiler, CPPCompiler): def __init__(self, exelist, version, cltype, is_cross, exe_wrapper=None): CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) ClangCompiler.__init__(self, cltype) - self.warn_args = {'1': ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'], - '2': ['-Wall', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor'], - '3': ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor']} + default_warn_args = ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'] + self.warn_args = {'1': default_warn_args, + '2': default_warn_args + ['-Wextra'], + '3': default_warn_args + ['-Wextra', '-Wpedantic']} def get_options(self): return {'cpp_std': coredata.UserComboOption('cpp_std', 'C++ language standard to use', ['none', 'c++03', 'c++11', 'c++14', 'c++1z', - 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++1z'], + 'gnu++11', 'gnu++14', 'gnu++1z'], 'none')} def get_option_compile_args(self, options): @@ -2379,6 +2392,140 @@ class ClangObjCPPCompiler(ClangCompiler, GnuObjCPPCompiler): ClangCompiler.__init__(self, cltype) self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage'] + +# Tested on linux for ICC 14.0.3, 15.0.6, 16.0.4, 17.0.1 +class IntelCompiler: + def __init__(self, icc_type): + self.id = 'intel' + self.icc_type = icc_type + self.lang_header = 'none' + self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage', + 'b_colorout', 'b_ndebug', 'b_staticpic', 'b_lundef', 'b_asneeded'] + # Assembly + self.can_compile_suffixes.add('s') + + def get_pic_args(self): + return ['-fPIC'] + + def get_buildtype_args(self, buildtype): + return gnulike_buildtype_args[buildtype] + + def get_buildtype_linker_args(self, buildtype): + return gnulike_buildtype_linker_args[buildtype] + + def get_pch_suffix(self): + return 'pchi' + + def get_pch_use_args(self, pch_dir, header): + return ['-pch', '-pch_dir', os.path.join(pch_dir), '-x', + self.lang_header, '-include', header, '-x', 'none'] + + def get_pch_name(self, header_name): + return os.path.split(header_name)[-1] + '.' + self.get_pch_suffix() + + def split_shlib_to_parts(self, fname): + return (os.path.split(fname)[0], fname) + + def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): + if self.icc_type == ICC_STANDARD: + gcc_type = GCC_STANDARD + elif self.icc_type == ICC_OSX: + gcc_type = GCC_OSX + elif self.icc_type == ICC_WIN: + gcc_type = GCC_MINGW + else: + raise MesonException('Unreachable code when converting icc type to gcc type.') + return get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module) + + def get_std_shared_lib_link_args(self): + # FIXME: Don't know how icc works on OSX + # if self.icc_type == ICC_OSX: + # return ['-bundle'] + return ['-shared'] + + +class IntelCCompiler(IntelCompiler, CCompiler): + def __init__(self, exelist, version, icc_type, is_cross, exe_wrapper=None): + CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) + IntelCompiler.__init__(self, icc_type) + self.lang_header = 'c-header' + default_warn_args = ['-Wall', '-w3', '-diag-disable:remark', '-Wpch-messages'] + self.warn_args = {'1': default_warn_args, + '2': default_warn_args + ['-Wextra'], + '3': default_warn_args + ['-Wextra', '-Wpedantic']} + + def get_options(self): + c_stds = ['c89', 'c99'] + g_stds = ['gnu89', 'gnu99'] + if mesonlib.version_compare(self.version, '>=16.0.0'): + c_stds += ['c11'] + opts = {'c_std': coredata.UserComboOption('c_std', 'C language standard to use', + ['none'] + c_stds + g_stds, + 'none')} + return opts + + def get_option_compile_args(self, options): + args = [] + std = options['c_std'] + if std.value != 'none': + args.append('-std=' + std.value) + return args + + def get_std_shared_lib_link_args(self): + return ['-shared'] + + def has_multi_arguments(self, args, env): + return super(IntelCCompiler, self).has_multi_arguments(args + ['-diag-error', '10006'], env) + + +class IntelCPPCompiler(IntelCompiler, CPPCompiler): + def __init__(self, exelist, version, icc_type, is_cross, exe_wrap): + CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap) + IntelCompiler.__init__(self, icc_type) + self.lang_header = 'c++-header' + default_warn_args = ['-Wall', '-w3', '-diag-disable:remark', + '-Wpch-messages', '-Wnon-virtual-dtor'] + self.warn_args = {'1': default_warn_args, + '2': default_warn_args + ['-Wextra'], + '3': default_warn_args + ['-Wextra', '-Wpedantic']} + + def get_options(self): + c_stds = [] + g_stds = ['gnu++98'] + if mesonlib.version_compare(self.version, '>=15.0.0'): + c_stds += ['c++11', 'c++14'] + g_stds += ['gnu++11'] + if mesonlib.version_compare(self.version, '>=16.0.0'): + c_stds += ['c++17'] + if mesonlib.version_compare(self.version, '>=17.0.0'): + g_stds += ['gnu++14'] + opts = {'cpp_std': coredata.UserComboOption('cpp_std', 'C++ language standard to use', + ['none'] + c_stds + g_stds, + 'none'), + 'cpp_debugstl': coredata.UserBooleanOption('cpp_debugstl', + 'STL debug mode', + False)} + return opts + + def get_option_compile_args(self, options): + args = [] + std = options['cpp_std'] + if std.value != 'none': + args.append('-std=' + std.value) + if options['cpp_debugstl'].value: + args.append('-D_GLIBCXX_DEBUG=1') + return args + + def get_option_link_args(self, options): + return [] + + def get_compiler_check_args(self): + return self.get_no_optimization_args() + + def has_multi_arguments(self, args, env): + return super(IntelCPPCompiler, self).has_multi_arguments(args + ['-diag-error', '10006'], env) + + class FortranCompiler(Compiler): def __init__(self, exelist, version, is_cross, exe_wrapper=None): self.language = 'fortran' @@ -2553,18 +2700,21 @@ class SunFortranCompiler(FortranCompiler): def get_always_args(self): return [] - def get_warn_args(self): + def get_warn_args(self, level): return [] def get_module_outdir_args(self, path): return ['-moddir=' + path] -class IntelFortranCompiler(FortranCompiler): +class IntelFortranCompiler(IntelCompiler, FortranCompiler): std_warn_args = ['-warn', 'all'] def __init__(self, exelist, version, is_cross, exe_wrapper=None): - self.file_suffixes = ('f', 'f90') - super().__init__(exelist, version, is_cross, exe_wrapper=None) + self.file_suffixes = ('f90', 'f', 'for', 'ftn', 'fpp') + FortranCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) + # FIXME: Add support for OS X and Windows in detect_fortran_compiler so + # we are sent the type of compiler + IntelCompiler.__init__(self, ICC_STANDARD) self.id = 'intel' def get_module_outdir_args(self, path): @@ -2626,15 +2776,13 @@ class NAGFortranCompiler(FortranCompiler): def get_module_outdir_args(self, path): return ['-mdir', path] - def get_always_args(self): - return [] - def get_warn_args(self, level): return NAGFortranCompiler.std_warn_args class VisualStudioLinker(): always_args = ['/NOLOGO'] + def __init__(self, exelist): self.exelist = exelist diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 2e5387d..48f5865 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -396,6 +396,10 @@ class Environment(): # everything else to stdout. Why? Lord only knows. version = search_version(err) return VisualStudioCCompiler([compiler], version, is_cross, exe_wrap) + if '(ICC)' in out: + # TODO: add microsoft add check OSX + inteltype = ICC_STANDARD + return IntelCCompiler(ccache + [compiler], version, inteltype, is_cross, exe_wrap) errmsg = 'Unknown compiler(s): "' + ', '.join(compilers) + '"' if popen_exceptions: errmsg += '\nThe follow exceptions were encountered:' @@ -525,6 +529,10 @@ class Environment(): if 'Microsoft' in out or 'Microsoft' in err: version = search_version(err) return VisualStudioCPPCompiler([compiler], version, is_cross, exe_wrap) + if '(ICC)' in out: + # TODO: add microsoft add check OSX + inteltype = ICC_STANDARD + return IntelCPPCompiler(ccache + [compiler], version, inteltype, is_cross, exe_wrap) errmsg = 'Unknown compiler(s): "' + ', '.join(compilers) + '"' if popen_exceptions: errmsg += '\nThe follow exceptions were encountered:' diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index ac15401..ec82ec9 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -30,6 +30,7 @@ from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode from .interpreterbase import InterpreterObject, MutableInterpreterObject import os, sys, shutil, uuid +import re import importlib @@ -1139,7 +1140,8 @@ class MesonMain(InterpreterObject): class Interpreter(InterpreterBase): - def __init__(self, build, backend, subproject='', subdir='', subproject_dir='subprojects'): + def __init__(self, build, backend, subproject='', subdir='', subproject_dir='subprojects', + default_project_options=[]): super().__init__(build.environment.get_source_dir(), subdir) self.build = build self.environment = build.environment @@ -1147,12 +1149,7 @@ class Interpreter(InterpreterBase): self.backend = backend self.subproject = subproject self.subproject_dir = subproject_dir - option_file = os.path.join(self.source_root, self.subdir, 'meson_options.txt') - if os.path.exists(option_file): - oi = optinterpreter.OptionInterpreter(self.subproject, \ - self.build.environment.cmd_line_options.projectoptions) - oi.process(option_file) - self.build.environment.merge_options(oi.options) + self.option_file = os.path.join(self.source_root, self.subdir, 'meson_options.txt') self.load_root_meson_file() self.sanity_check_ast() self.builtin.update({'meson': MesonMain(build, self)}) @@ -1161,6 +1158,7 @@ class Interpreter(InterpreterBase): self.args_frozen = False self.subprojects = {} self.subproject_stack = [] + self.default_project_options = default_project_options[:] # Passed from the outside, only used in subprojects. self.build_func_dict() self.parse_project() self.builtin['build_machine'] = BuildMachine(self.coredata.compilers) @@ -1207,6 +1205,7 @@ class Interpreter(InterpreterBase): 'add_project_arguments': self.func_add_project_arguments, 'add_global_link_arguments': self.func_add_global_link_arguments, 'add_project_link_arguments': self.func_add_project_link_arguments, + 'add_test_setup': self.func_add_test_setup, 'add_languages': self.func_add_languages, 'find_program': self.func_find_program, 'find_library': self.func_find_library, @@ -1418,7 +1417,8 @@ class Interpreter(InterpreterBase): os.makedirs(os.path.join(self.build.environment.get_build_dir(), subdir), exist_ok=True) self.args_frozen = True mlog.log('\nExecuting subproject ', mlog.bold(dirname), '.\n', sep='') - subi = Interpreter(self.build, self.backend, dirname, subdir, self.subproject_dir) + subi = Interpreter(self.build, self.backend, dirname, subdir, self.subproject_dir, + mesonlib.stringlistify(kwargs.get('default_options', []))) subi.subprojects = self.subprojects subi.subproject_stack = self.subproject_stack + [dirname] @@ -1488,19 +1488,53 @@ class Interpreter(InterpreterBase): raise InterpreterException('All default options must be of type key=value.') key, value = option.split('=', 1) if coredata.is_builtin_option(key): + if self.subproject != '': + continue # Only the master project is allowed to set global options. 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. else: + # Option values set with subproject() default_options override those + # set in project() default_options. + pref = key + '=' + for i in self.default_project_options: + if i.startswith(pref): + option = i + break + # If we are in a subproject, add the subproject prefix to option + # name. + if self.subproject != '': + option = self.subproject + ':' + option newoptions = [option] + self.environment.cmd_line_options.projectoptions self.environment.cmd_line_options.projectoptions = newoptions + # Add options that are only in default_options. + for defopt in self.default_project_options: + key, value = defopt.split('=') + pref = key + '=' + was_found = False + for i in default_options: + if i.startswith(pref): + was_found = True + break + if was_found: + break + defopt = self.subproject + ':' + defopt + newoptions = [defopt] + self.environment.cmd_line_options.projectoptions + self.environment.cmd_line_options.projectoptions = newoptions @stringArgs def func_project(self, node, args, kwargs): + if self.environment.first_invocation and ('default_options' in kwargs or \ + len(self.default_project_options) > 0): + self.parse_default_options(kwargs['default_options']) if not self.is_subproject(): self.build.project_name = args[0] - if self.environment.first_invocation and 'default_options' in kwargs: - self.parse_default_options(kwargs['default_options']) + if os.path.exists(self.option_file): + oi = optinterpreter.OptionInterpreter(self.subproject, \ + self.build.environment.cmd_line_options.projectoptions, + ) + oi.process(self.option_file) + self.build.environment.merge_options(oi.options) if len(args) < 2: raise InvalidArguments('Not enough arguments to project(). Needs at least the project name and one language') self.active_projectname = args[0] @@ -1766,7 +1800,12 @@ requirements use the version keyword argument instead.''') dirname, varname = self.get_subproject_infos(kwargs) # Try to execute the subproject try: - self.do_subproject(dirname, {}) + sp_kwargs = {} + try: + sp_kwargs['default_options'] = kwargs['default_options'] + except KeyError: + pass + self.do_subproject(dirname, sp_kwargs) # Invalid code is always an error except InvalidCode: raise @@ -1785,7 +1824,6 @@ requirements use the version keyword argument instead.''') if not isinstance(dep, DependencyHolder): raise InvalidCode('Fallback variable {!r} in the subproject {!r} is ' 'not a dependency object.'.format(varname, dirname)) - return None # Check if the version of the declared dependency matches what we want if 'version' in kwargs: wanted = kwargs['version'] @@ -1942,22 +1980,7 @@ requirements use the version keyword argument instead.''') def func_test(self, node, args, kwargs): self.add_test(node, args, kwargs, True) - def add_test(self, node, args, kwargs, is_base_test): - if len(args) != 2: - raise InterpreterException('Incorrect number of arguments') - if not isinstance(args[0], str): - raise InterpreterException('First argument of test must be a string.') - if not isinstance(args[1], (ExecutableHolder, JarHolder, ExternalProgramHolder)): - raise InterpreterException('Second argument must be executable.') - par = kwargs.get('is_parallel', True) - if not isinstance(par, bool): - raise InterpreterException('Keyword argument is_parallel must be a boolean.') - cmd_args = kwargs.get('args', []) - if not isinstance(cmd_args, list): - cmd_args = [cmd_args] - for i in cmd_args: - if not isinstance(i, (str, mesonlib.File)): - raise InterpreterException('Command line arguments must be strings') + def unpack_env_kwarg(self, kwargs): envlist = kwargs.get('env', []) if isinstance(envlist, EnvironmentVariablesHolder): env = envlist.held_object @@ -1974,8 +1997,25 @@ requirements use the version keyword argument instead.''') if ' ' in k: raise InterpreterException('Env var key must not have spaces in it.') env[k] = val - if not isinstance(envlist, list): - envlist = [envlist] + return env + + def add_test(self, node, args, kwargs, is_base_test): + if len(args) != 2: + raise InterpreterException('Incorrect number of arguments') + if not isinstance(args[0], str): + raise InterpreterException('First argument of test must be a string.') + if not isinstance(args[1], (ExecutableHolder, JarHolder, ExternalProgramHolder)): + raise InterpreterException('Second argument must be executable.') + par = kwargs.get('is_parallel', True) + if not isinstance(par, bool): + raise InterpreterException('Keyword argument is_parallel must be a boolean.') + cmd_args = kwargs.get('args', []) + if not isinstance(cmd_args, list): + cmd_args = [cmd_args] + for i in cmd_args: + if not isinstance(i, (str, mesonlib.File)): + raise InterpreterException('Command line arguments must be strings') + env = self.unpack_env_kwarg(kwargs) should_fail = kwargs.get('should_fail', False) if not isinstance(should_fail, bool): raise InterpreterException('Keyword argument should_fail must be a boolean.') @@ -2139,6 +2179,47 @@ requirements use the version keyword argument instead.''') return i @stringArgs + def func_add_test_setup(self, node, args, kwargs): + if len(args) != 1: + raise InterpreterException('Add_test_setup needs one argument for the setup name.') + setup_name = args[0] + if re.fullmatch('[_a-zA-Z][_0-9a-zA-Z]*', setup_name) is None: + raise InterpreterException('Setup name may only contain alphanumeric characters.') + try: + inp = kwargs.get('exe_wrapper', []) + if not isinstance(inp, list): + inp = [inp] + exe_wrapper = [] + for i in inp: + if hasattr(i, 'held_object'): + i = i.held_object + if isinstance(i, str): + exe_wrapper.append(i) + elif isinstance(i, dependencies.ExternalProgram): + if not i.found(): + raise InterpreterException('Tried to use non-found external executable.') + exe_wrapper += i.get_command() + else: + raise InterpreterException('Exe wrapper can only contain strings or external binaries.') + except KeyError: + exe_wrapper = None + gdb = kwargs.get('gdb', False) + if not isinstance(gdb, bool): + raise InterpreterException('Gdb option must be a boolean') + timeout_multiplier = kwargs.get('timeout_multiplier', 1) + if not isinstance(timeout_multiplier, int): + raise InterpreterException('Timeout multiplier must be a number.') + env = self.unpack_env_kwarg(kwargs) + setupobj = build.TestSetup(exe_wrapper=exe_wrapper, + gdb=gdb, + timeout_multiplier=timeout_multiplier, + env=env) + if self.subproject == '': + # Dunno what we should do with subprojects really. Let's start simple + # and just use the master project ones. + self.build.test_setups[setup_name] = setupobj + + @stringArgs def func_add_global_arguments(self, node, args, kwargs): if self.subproject != '': msg = 'Global arguments can not be set in subprojects because ' \ diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py index 5ff14ff..4b4ae3d 100644 --- a/mesonbuild/modules/i18n.py +++ b/mesonbuild/modules/i18n.py @@ -16,6 +16,7 @@ from os import path from .. import coredata, mesonlib, build from ..mesonlib import MesonException import sys +import shutil PRESET_ARGS = { 'glib': [ @@ -63,6 +64,8 @@ class I18nModule: def gettext(self, state, args, kwargs): if len(args) != 1: raise coredata.MesonException('Gettext requires one positional argument (package name).') + if not shutil.which('xgettext'): + raise coredata.MesonException('Can not do gettext because xgettext is not installed.') packagename = args[0] languages = mesonlib.stringlistify(kwargs.get('languages', [])) datadirs = mesonlib.stringlistify(kwargs.get('data_dirs', [])) diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py index ac23fe8..089eb2c 100644 --- a/mesonbuild/optinterpreter.py +++ b/mesonbuild/optinterpreter.py @@ -71,8 +71,15 @@ class OptionInterpreter: def __init__(self, subproject, command_line_options): self.options = {} self.subproject = subproject + self.sbprefix = subproject + ':' self.cmd_line_options = {} for o in command_line_options: + if self.subproject != '': # Strip the beginning. + if not o.startswith(self.sbprefix): + continue + else: + if ':' in o: + continue try: (key, value) = o.split('=', 1) except ValueError: diff --git a/mesonbuild/scripts/depfixer.py b/mesonbuild/scripts/depfixer.py index c9ee36c..193d5b6 100644 --- a/mesonbuild/scripts/depfixer.py +++ b/mesonbuild/scripts/depfixer.py @@ -220,7 +220,7 @@ class Elf(DataSizes): soname = i if i.d_tag == DT_STRTAB: strtab = i - else: + if soname is None or strtab is None: print("This file does not have a soname") return self.bf.seek(strtab.val + soname.val) diff --git a/mesontest.py b/mesontest.py index ecf1b41..af4ae2a 100755 --- a/mesontest.py +++ b/mesontest.py @@ -69,7 +69,7 @@ parser.add_argument('--suite', default=None, dest='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.") + 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', @@ -82,6 +82,8 @@ parser.add_argument('-t', '--timeout-multiplier', type=float, default=1.0, 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('args', nargs='*') class TestRun(): @@ -101,9 +103,7 @@ class TestRun(): if self.cmd is None: res += 'NONE\n' else: - res += "\n%s %s\n" % (' '.join( - ["%s='%s'" % (k, v) for k, v in self.env.items()]), - ' ' .join(self.cmd)) + res += "%s%s\n" % (''.join(["%s='%s' " % (k, v) for k, v in self.env.items()]), ' ' .join(self.cmd)) if self.stdo: res += '--- stdout ---\n' res += self.stdo @@ -149,8 +149,10 @@ class TestHarness: def __init__(self, options): self.options = options self.collected_logs = [] - self.failed_tests = [] - self.error_count = 0 + self.fail_count = 0 + self.success_count = 0 + self.skip_count = 0 + self.timeout_count = 0 self.is_run = False self.cant_rebuild = False if self.options.benchmark: @@ -180,7 +182,6 @@ class TestHarness: return True def run_single_test(self, wrap, test): - failling = False if test.fname[0].endswith('.jar'): cmd = ['java', '-jar'] + test.fname elif not test.is_cross and run_with_mono(test.fname[0]): @@ -206,6 +207,7 @@ class TestHarness: cmd = wrap + cmd + test.cmd_args starttime = time.time() child_env = os.environ.copy() + child_env.update(self.options.global_env.get_env(child_env)) if isinstance(test.env, build.EnvironmentVariables): test.env = test.env.get_env(child_env) @@ -255,21 +257,19 @@ class TestHarness: stde = decode(stde) if timed_out: res = 'TIMEOUT' - failling = True + self.timeout_count += 1 if p.returncode == GNU_SKIP_RETURNCODE: res = 'SKIP' - elif (not test.should_fail and p.returncode == 0) or \ - (test.should_fail and p.returncode != 0): + self.skip_count += 1 + elif test.should_fail == bool(p.returncode): res = 'OK' + self.success_count += 1 else: res = 'FAIL' - failling = True + self.fail_count += 1 returncode = p.returncode result = TestRun(res, returncode, test.should_fail, duration, stdo, stde, cmd, test.env) - if failling: - self.failed_tests.append(result) - return result def print_stats(self, numlen, tests, name, result, i, logfile, jsonlogfile): @@ -283,20 +283,21 @@ class TestHarness: result_str += "\n\n" + result.get_log() if (result.returncode != GNU_SKIP_RETURNCODE) and \ (result.returncode != 0) != result.should_fail: - self.error_count += 1 if self.options.print_errorlogs: self.collected_logs.append(result_str) - logfile.write(result_str) - write_json_log(jsonlogfile, name, result) - - def doit(self): - if self.is_run: - raise RuntimeError('Test harness object can only be used once.') - if not os.path.isfile(self.datafile): - print('Test data file. Probably this means that you did not run this in the build directory.') - return 1 - self.is_run = True - logfilename = self.run_tests(self.options.logbase) + if logfile: + logfile.write(result_str) + if jsonlogfile: + write_json_log(jsonlogfile, name, result) + + def print_summary(self, logfile, jsonlogfile): + msg = 'Test summary: %d OK, %d FAIL, %d SKIP, %d TIMEOUT' \ + % (self.success_count, self.fail_count, self.skip_count, self.timeout_count) + print(msg) + if logfile: + logfile.write(msg) + + def print_collected_logs(self): if len(self.collected_logs) > 0: if len(self.collected_logs) > 10: print('\nThe output from 10 first failed tests:\n') @@ -304,54 +305,100 @@ class TestHarness: print('\nThe output from the failed tests:\n') for log in self.collected_logs[:10]: lines = log.splitlines() - if len(lines) > 100: - print(lines[0]) + if len(lines) > 104: + print('\n'.join(lines[0:4])) print('--- Listing only the last 100 lines from a long log. ---') - lines = lines[-99:] + lines = lines[-100:] for line in lines: print(line) - print('Full log written to %s.' % logfilename) - return self.error_count + + def doit(self): + if self.is_run: + raise RuntimeError('Test harness object can only be used once.') + if not os.path.isfile(self.datafile): + print('Test data file. Probably this means that you did not run this in the build directory.') + return 1 + self.is_run = True + tests = self.get_tests() + if not tests: + return 0 + self.run_tests(tests) + return self.fail_count def get_tests(self): with open(self.datafile, 'rb') as f: tests = pickle.load(f) + + if not tests: + print('No tests defined.') + return [] + + if self.options.suite: + tests = [t for t in tests if self.options.suite in t.suite] + + if self.options.args: + tests = [t for t in tests if t.name in self.options.args] + + if not tests: + print('No suitable tests defined.') + return [] + for test in tests: test.rebuilt = False return tests - def run_tests(self, log_base): - logfile_base = os.path.join(self.options.wd, 'meson-logs', log_base) + def open_log_files(self): + if not self.options.logbase or self.options.verbose: + return (None, None, None, None) + + logfile_base = os.path.join(self.options.wd, 'meson-logs', self.options.logbase) + if self.options.wrapper is None: - wrap = [] logfilename = logfile_base + '.txt' jsonlogfilename = logfile_base + '.json' else: - wrap = self.options.wrapper.split() - namebase = wrap[0] + namebase = os.path.split(self.get_wrapper()[0])[1] logfilename = logfile_base + '-' + namebase.replace(' ', '_') + '.txt' jsonlogfilename = logfile_base + '-' + namebase.replace(' ', '_') + '.json' - tests = self.get_tests() - if len(tests) == 0: - print('No tests defined.') - return - numlen = len('%d' % len(tests)) - executor = conc.ThreadPoolExecutor(max_workers=self.options.num_processes) - futures = [] - filtered_tests = filter_tests(self.options.suite, tests) - - jsonlogfile = None - logfile = None + + jsonlogfile = open(jsonlogfilename, 'w') + logfile = open(logfilename, 'w') + + logfile.write('Log of Meson test suite run on %s.\n\n' + % datetime.datetime.now().isoformat()) + + return (logfile, logfilename, jsonlogfile, jsonlogfilename) + + def get_wrapper(self): + wrap = [] + if self.options.gdb: + wrap = ['gdb', '--quiet', '--nh'] + if self.options.repeat > 1: + wrap += ['-ex', 'run', '-ex', 'quit'] + elif self.options.wrapper: + if isinstance(self.options.wrapper, str): + wrap = self.options.wrapper.split() + else: + wrap = self.options.wrapper + assert(isinstance(wrap, list)) + return wrap + + def get_suites(self, tests): + return set([test.suite[0] for test in tests]) + + def run_tests(self, tests): try: - if not self.options.verbose: - jsonlogfile = open(jsonlogfilename, 'w') - logfile = open(logfilename, 'w') - logfile.write('Log of Meson test suite run on %s.\n\n' % - datetime.datetime.now().isoformat()) + executor = None + logfile = None + jsonlogfile = None + futures = [] + numlen = len('%d' % len(tests)) + (logfile, logfilename, jsonlogfile, jsonlogfilename) = self.open_log_files() + wrap = self.get_wrapper() for i in range(self.options.repeat): - for i, test in enumerate(filtered_tests): + for i, test in enumerate(tests): if test.suite[0] == '': visible_name = test.name else: @@ -360,112 +407,113 @@ class TestHarness: else: visible_name = test.suite[0] + ' / ' + test.name - if not test.is_parallel: - self.drain_futures(futures) + if self.options.gdb: + test.timeout = None + if len(test.cmd_args): + wrap.append('--args') + + if not test.is_parallel or self.options.gdb: + self.drain_futures(futures, logfile, jsonlogfile) futures = [] res = self.run_single_test(wrap, test) - if not self.options.verbose: - self.print_stats(numlen, filtered_tests, visible_name, res, i, - logfile, jsonlogfile) + self.print_stats(numlen, tests, visible_name, res, i, logfile, jsonlogfile) else: + if not executor: + executor = conc.ThreadPoolExecutor(max_workers=self.options.num_processes) f = executor.submit(self.run_single_test, wrap, test) - if not self.options.verbose: - futures.append((f, numlen, filtered_tests, visible_name, i, - logfile, jsonlogfile)) + futures.append((f, numlen, tests, visible_name, i, logfile, jsonlogfile)) + if self.options.repeat > 1 and self.fail_count: + break + if self.options.repeat > 1 and self.fail_count: + break + self.drain_futures(futures, logfile, jsonlogfile) + self.print_summary(logfile, jsonlogfile) + self.print_collected_logs() + + if logfilename: + print('Full log written to %s.' % logfilename) finally: if jsonlogfile: jsonlogfile.close() if logfile: logfile.close() - return logfilename - - def drain_futures(self, futures, logfile, jsonlogfile): for i in futures: (result, numlen, tests, name, i, logfile, jsonlogfile) = i - if self.options.repeat > 1 and self.failed_tests: + if self.options.repeat > 1 and self.fail_count: result.cancel() - elif not self.options.verbose: - self.print_stats(numlen, tests, name, result.result(), i, logfile, jsonlogfile) - else: + if self.options.verbose: result.result() - - if self.options.repeat > 1 and self.failed_tests: - if not self.options.verbose: - for res in self.failed_tests: - print('Test failed:\n\n-- stdout --\n') - print(res.stdo) - print('\n-- stderr --\n') - print(res.stde) - return 1 - - return + self.print_stats(numlen, tests, name, result.result(), i, logfile, jsonlogfile) def run_special(self): 'Tests run by the user, usually something like "under gdb 1000 times".' if self.is_run: raise RuntimeError('Can not use run_special after a full run.') - if self.options.wrapper is not None: - wrap = self.options.wrapper.split(' ') - else: - wrap = [] - if self.options.gdb and len(wrap) > 0: - print('Can not specify both a wrapper and gdb.') - return 1 if os.path.isfile('build.ninja'): subprocess.check_call([environment.detect_ninja(), 'all']) tests = self.get_tests() - if self.options.list: - for i in tests: - print(i.name) - return 0 - for t in tests: - if t.name in self.options.args: - for i in range(self.options.repeat): - print('Running: %s %d/%d' % (t.name, i + 1, self.options.repeat)) - if self.options.gdb: - wrap = ['gdb', '--quiet'] - if len(t.cmd_args) > 0: - wrap.append('--args') - if self.options.repeat > 1: - # The user wants to debug interactively, so no timeout. - t.timeout = None - wrap += ['-ex', 'run', '-ex', 'quit'] - - res = self.run_single_test(wrap, t) - else: - res = self.run_single_test(wrap, t) - if (res.returncode == 0 and res.should_fail) or \ - (res.returncode != 0 and not res.should_fail): - if not self.options.verbose: - print('Test failed:\n\n-- stdout --\n') - print(res.stdo) - print('\n-- stderr --\n') - print(res.stde) - return 1 - return 0 + self.run_tests(tests) + return self.fail_count -def filter_tests(suite, tests): - if suite is None: - return tests - return [x for x in tests if suite in x.suite] +def list_tests(th): + tests = th.get_tests() + print_suites = True if len(th.get_suites(tests)) != 1 else False + for i in tests: + if print_suites: + print("%s / %s" % (i.suite[0], i.name)) + else: + print("%s" % i.name) + + +def merge_suite_options(options): + buildfile = os.path.join(options.wd, 'meson-private/build.dat') + with open(buildfile, 'rb') as f: + build = pickle.load(f) + setups = build.test_setups + if options.setup not in setups: + sys.exit('Unknown test setup: %s' % options.setup) + current = setups[options.setup] + if not options.gdb: + options.gdb = current.gdb + if options.timeout_multiplier is None: + options.timeout_multiplier = current.timeout_multiplier +# if options.env is None: +# options.env = current.env # FIXME, should probably merge options here. + if options.wrapper is not None and current.exe_wrapper is not None: + sys.exit('Conflict: both test setup and command line specify an exe wrapper.') + if options.wrapper is None: + options.wrapper = current.exe_wrapper + return current.env def run(args): options = parser.parse_args(args) + if options.benchmark: options.num_processes = 1 + if options.setup is not None: + global_env = merge_suite_options(options) + else: + global_env = build.EnvironmentVariables() + + setattr(options, 'global_env', global_env) + if options.gdb: options.verbose = True + if options.wrapper: + print('Must not specify both a wrapper and gdb at the same time.') + return 1 options.wd = os.path.abspath(options.wd) th = TestHarness(options) if options.list: - return th.run_special() + list_tests(th) + return 0 if not options.no_rebuild: if not th.rebuild_all(): sys.exit(-1) diff --git a/run_project_tests.py b/run_project_tests.py index 07e7ddb..18988fb 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -58,9 +58,15 @@ class AutoDeletedDir(): try: shutil.rmtree(self.dir) return - except OSError: + # Sometimes we get: ValueError: I/O operation on closed file. + except ValueError: + return + # Deleting can raise OSError or PermissionError on Windows + # (most likely because of anti-virus locking the file) + except (OSError, PermissionError): if i == retries - 1: - raise + mlog.warning('Could not delete temporary directory.') + return time.sleep(0.1 * (2**i)) failing_logs = [] @@ -554,7 +560,7 @@ if __name__ == '__main__': print('\nTotal passed tests:', passing_tests) print('Total failed tests:', failing_tests) print('Total skipped tests:', skipped_tests) - if failing_tests > 0 and ('TRAVIS' in os.environ or 'APPVEYOR' in os.environ): + if failing_tests > 0: print('\nMesonlogs of failing tests\n') for l in failing_logs: print(l, '\n') diff --git a/run_unittests.py b/run_unittests.py index 8f1f155..d1c192f 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -19,7 +19,7 @@ import re, json import tempfile from glob import glob import mesonbuild.environment -from mesonbuild.environment import detect_ninja +from mesonbuild.environment import detect_ninja, Environment from mesonbuild.dependencies import PkgConfigDependency def get_soname(fname): @@ -33,6 +33,12 @@ def get_soname(fname): return m.group(1) raise RuntimeError('Could not determine soname:\n\n' + raw_out) +def get_fake_options(): + import argparse + opts = argparse.Namespace() + opts.cross_file = None + return opts + class FakeEnvironment(object): def __init__(self): self.cross_info = None @@ -57,12 +63,14 @@ class LinuxlikeTests(unittest.TestCase): src_root = os.path.dirname(__file__) src_root = os.path.join(os.getcwd(), src_root) self.builddir = tempfile.mkdtemp() + self.logdir = os.path.join(self.builddir, 'meson-logs') self.prefix = '/usr' self.libdir = os.path.join(self.prefix, 'lib') self.installdir = os.path.join(self.builddir, 'install') self.meson_command = [sys.executable, os.path.join(src_root, 'meson.py')] self.mconf_command = [sys.executable, os.path.join(src_root, 'mesonconf.py')] self.mintro_command = [sys.executable, os.path.join(src_root, 'mesonintrospect.py')] + self.mtest_command = [sys.executable, os.path.join(src_root, 'mesontest.py'), '-C', self.builddir] self.ninja_command = [detect_ninja(), '-C', self.builddir] self.common_test_dir = os.path.join(src_root, 'test cases/common') self.vala_test_dir = os.path.join(src_root, 'test cases/vala') @@ -79,15 +87,21 @@ class LinuxlikeTests(unittest.TestCase): def _run(self, command): self.output += subprocess.check_output(command, env=os.environ.copy()) - def init(self, srcdir): + def init(self, srcdir, extra_args=None): + if extra_args is None: + extra_args = [] args = [srcdir, self.builddir, '--prefix', self.prefix, '--libdir', self.libdir] - self._run(self.meson_command + args) + self._run(self.meson_command + args + extra_args) + self.privatedir = os.path.join(self.builddir, 'meson-private') def build(self): self._run(self.ninja_command) + def run_tests(self): + self._run(self.ninja_command + ['test']) + def install(self): os.environ['DESTDIR'] = self.installdir self._run(self.ninja_command + ['install']) @@ -101,6 +115,9 @@ class LinuxlikeTests(unittest.TestCase): def setconf(self, arg): self._run(self.mconf_command + [arg, self.builddir]) + def wipe(self): + shutil.rmtree(self.builddir) + def get_compdb(self): with open(os.path.join(self.builddir, 'compile_commands.json')) as ifile: return json.load(ifile) @@ -161,7 +178,7 @@ class LinuxlikeTests(unittest.TestCase): testdir = os.path.join(self.common_test_dir, '3 static') self.init(testdir) compdb = self.get_compdb() - self.assertTrue('-fPIC' in compdb[0]['command']) + self.assertIn('-fPIC', compdb[0]['command']) # This is needed to increase the difference between build.ninja's # timestamp and coredata.dat's timestamp due to a Ninja bug. # https://github.com/ninja-build/ninja/issues/371 @@ -170,7 +187,7 @@ class LinuxlikeTests(unittest.TestCase): # Regenerate build self.build() compdb = self.get_compdb() - self.assertTrue('-fPIC' not in compdb[0]['command']) + self.assertNotIn('-fPIC', compdb[0]['command']) def test_pkgconfig_gen(self): ''' @@ -183,11 +200,11 @@ class LinuxlikeTests(unittest.TestCase): self.init(testdir) env = FakeEnvironment() kwargs = {'required': True, 'silent': True} - os.environ['PKG_CONFIG_LIBDIR'] = os.path.join(self.builddir, 'meson-private') + os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir simple_dep = PkgConfigDependency('libfoo', env, kwargs) self.assertTrue(simple_dep.found()) self.assertEqual(simple_dep.get_version(), '1.0') - self.assertTrue('-lfoo' in simple_dep.get_link_args()) + self.assertIn('-lfoo', simple_dep.get_link_args()) def test_vala_c_warnings(self): ''' @@ -212,15 +229,15 @@ class LinuxlikeTests(unittest.TestCase): self.assertIsNotNone(vala_command) self.assertIsNotNone(c_command) # -w suppresses all warnings, should be there in Vala but not in C - self.assertTrue('-w' in vala_command) - self.assertFalse('-w' in c_command) + self.assertIn("'-w'", vala_command) + self.assertNotIn("'-w'", c_command) # -Wall enables all warnings, should be there in C but not in Vala - self.assertFalse('-Wall' in vala_command) - self.assertTrue('-Wall' in c_command) + self.assertNotIn("'-Wall'", vala_command) + self.assertIn("'-Wall'", c_command) # -Werror converts warnings to errors, should always be there since it's # injected by an unrelated piece of code and the project has werror=true - self.assertTrue('-Werror' in vala_command) - self.assertTrue('-Werror' in c_command) + self.assertIn("'-Werror'", vala_command) + self.assertIn("'-Werror'", c_command) def test_static_compile_order(self): ''' @@ -374,6 +391,86 @@ class LinuxlikeTests(unittest.TestCase): self.uninstall() self.assertFalse(os.path.exists(exename)) + def test_custom_target_exe_data_deterministic(self): + testdir = os.path.join(self.common_test_dir, '117 custom target capture') + self.init(testdir) + meson_exe_dat1 = glob(os.path.join(self.privatedir, 'meson_exe*.dat')) + self.wipe() + self.init(testdir) + meson_exe_dat2 = glob(os.path.join(self.privatedir, 'meson_exe*.dat')) + self.assertListEqual(meson_exe_dat1, meson_exe_dat2) + + def test_testsetups(self): + if not shutil.which('valgrind'): + raise unittest.SkipTest('Valgrind not installed.') + testdir = os.path.join(self.unit_test_dir, '2 testsetups') + self.init(testdir) + self.build() + self.run_tests() + with open(os.path.join(self.logdir, 'testlog.txt')) as f: + basic_log = f.read() + self.assertRaises(subprocess.CalledProcessError, + self._run, self.mtest_command + ['--setup=valgrind']) + with open(os.path.join(self.logdir, 'testlog-valgrind.txt')) as f: + vg_log = f.read() + self.assertFalse('TEST_ENV is set' in basic_log) + self.assertFalse('Memcheck' in basic_log) + self.assertTrue('TEST_ENV is set' in vg_log) + self.assertTrue('Memcheck' in vg_log) + + def _test_stds_impl(self, testdir, compiler, p): + lang_std = p + '_std' + # Check that all the listed -std=xxx options for this compiler work + # just fine when used + for v in compiler.get_options()[lang_std].choices: + std_opt = '{}={}'.format(lang_std, v) + self.init(testdir, ['-D' + std_opt]) + cmd = self.get_compdb()[0]['command'] + if v != 'none': + cmd_std = "'-std={}'".format(v) + self.assertIn(cmd_std, cmd) + try: + self.build() + except: + print('{} was {!r}'.format(lang_std, v)) + raise + self.wipe() + # Check that an invalid std option in CFLAGS/CPPFLAGS fails + # Needed because by default ICC ignores invalid options + cmd_std = '-std=FAIL' + env_flags = p.upper() + 'FLAGS' + os.environ[env_flags] = cmd_std + self.init(testdir) + cmd = self.get_compdb()[0]['command'] + qcmd_std = "'{}'".format(cmd_std) + self.assertIn(qcmd_std, cmd) + with self.assertRaises(subprocess.CalledProcessError, + msg='{} should have failed'.format(qcmd_std)): + self.build() + + def test_compiler_c_stds(self): + ''' + Test that C stds specified for this compiler can all be used. Can't be + an ordinary test because it requires passing options to meson. + ''' + testdir = os.path.join(self.common_test_dir, '1 trivial') + env = Environment(testdir, self.builddir, self.meson_command, + get_fake_options(), []) + cc = env.detect_c_compiler(False) + self._test_stds_impl(testdir, cc, 'c') + + def test_compiler_cpp_stds(self): + ''' + Test that C++ stds specified for this compiler can all be used. Can't + be an ordinary test because it requires passing options to meson. + ''' + testdir = os.path.join(self.common_test_dir, '2 cpp') + env = Environment(testdir, self.builddir, self.meson_command, + get_fake_options(), []) + cpp = env.detect_cpp_compiler(False) + self._test_stds_impl(testdir, cpp, 'cpp') + + class RewriterTests(unittest.TestCase): def setUp(self): @@ -432,5 +529,6 @@ class RewriterTests(unittest.TestCase): self.assertEqual(top, self.read_contents('meson.build')) self.assertEqual(s2, self.read_contents('sub2/meson.build')) + if __name__ == '__main__': unittest.main() diff --git a/test cases/common/1 trivial/meson.build b/test cases/common/1 trivial/meson.build index 1f7b375..a93de75 100644 --- a/test cases/common/1 trivial/meson.build +++ b/test cases/common/1 trivial/meson.build @@ -6,6 +6,11 @@ project('trivial test', #this is a comment sources = 'trivial.c' +if meson.get_compiler('c').get_id() == 'intel' + # Error out if the -std=xxx option is incorrect + add_project_arguments('-diag-error', '10159', language : 'c') +endif + exe = executable('trivialprog', sources : sources) test('runtest', exe) # This is a comment diff --git a/test cases/common/132 dependency file generation/main .c b/test cases/common/132 dependency file generation/main .c new file mode 100644 index 0000000..0fb4389 --- /dev/null +++ b/test cases/common/132 dependency file generation/main .c @@ -0,0 +1,3 @@ +int main(int argc, char *argv[]) { + return 0; +} diff --git a/test cases/common/132 dependency file generation/meson.build b/test cases/common/132 dependency file generation/meson.build new file mode 100644 index 0000000..dcfdcd9 --- /dev/null +++ b/test cases/common/132 dependency file generation/meson.build @@ -0,0 +1,12 @@ +project('dep file gen', 'c') + +cc_id = meson.get_compiler('c').get_id() +if cc_id == 'intel' + # ICC does not escape spaces in paths in the dependency file, so Ninja + # (correctly) thinks that the rule has multiple outputs and errors out: + # 'depfile has multiple output paths' + error('MESON_SKIP_TEST: Skipping test with Intel compiler because it generates broken dependency files') +endif + +e = executable('main file', 'main .c') +test('test it', e) diff --git a/test cases/common/2 cpp/meson.build b/test cases/common/2 cpp/meson.build index 9c6f71a..6398382 100644 --- a/test cases/common/2 cpp/meson.build +++ b/test cases/common/2 cpp/meson.build @@ -1,3 +1,9 @@ project('c++ test', 'cpp') + +if meson.get_compiler('cpp').get_id() == 'intel' + # Error out if the -std=xxx option is incorrect + add_project_arguments('-diag-error', '10159', language : 'cpp') +endif + exe = executable('trivialprog', 'trivial.cc', extra_files : 'something.txt') test('runtest', exe) diff --git a/test cases/common/25 object extraction/meson.build b/test cases/common/25 object extraction/meson.build index c76b0db..d99ec84 100644 --- a/test cases/common/25 object extraction/meson.build +++ b/test cases/common/25 object extraction/meson.build @@ -9,8 +9,8 @@ else obj1 = lib1.extract_objects('src/lib.c') obj2 = lib2.extract_objects(['lib.c']) - e1 = executable('main 1', 'main.c', objects : obj1) - e2 = executable('main 2', 'main.c', objects : obj2) + e1 = executable('main1', 'main.c', objects : obj1) + e2 = executable('main2', 'main.c', objects : obj2) test('extraction test 1', e1) test('extraction test 2', e2) diff --git a/test cases/common/94 default options/meson.build b/test cases/common/94 default options/meson.build index a718bcc..a9176e0 100644 --- a/test cases/common/94 default options/meson.build +++ b/test cases/common/94 default options/meson.build @@ -1,20 +1,20 @@ project('default options', 'cpp', 'c', default_options : [ 'buildtype=debugoptimized', - 'cpp_std=c++03', + 'cpp_std=c++11', 'cpp_eh=none', 'warning_level=3', ]) -cpp = meson.get_compiler('cpp') +cpp_id = meson.get_compiler('cpp').get_id() assert(get_option('buildtype') == 'debugoptimized', 'Build type default value wrong.') -if cpp.get_id() == 'msvc' +if cpp_id == 'msvc' cpp_eh = get_option('cpp_eh') assert(cpp_eh == 'none', 'MSVC eh value is "' + cpp_eh + '" instead of "none"') else cpp_std = get_option('cpp_std') - assert(cpp_std == 'c++03', 'C++ std value is "' + cpp_std + '" instead of c++03.') + assert(cpp_std == 'c++11', 'C++ std value is "' + cpp_std + '" instead of c++11.') endif w_level = get_option('warning_level') diff --git a/test cases/d/3 shared library/meson.build b/test cases/d/3 shared library/meson.build index 5dae66b..1f45109 100644 --- a/test cases/d/3 shared library/meson.build +++ b/test cases/d/3 shared library/meson.build @@ -1,12 +1,9 @@ project('D Shared Library', 'd') -if meson.get_compiler('d').get_id() != 'gcc' - - ldyn = shared_library('stuff', 'libstuff.d', install : true) - ed = executable('app_d', 'app.d', link_with : ldyn, install : true) - test('linktest_dyn', ed) - -else - message('GDC can not build shared libraries. Test skipped.') - install_data('no-installed-files', install_dir : '') +if meson.get_compiler('d').get_id() == 'gcc' + error('MESON_SKIP_TEST: GDC can not build shared libraries') endif + +ldyn = shared_library('stuff', 'libstuff.d', install : true) +ed = executable('app_d', 'app.d', link_with : ldyn, install : true) +test('linktest_dyn', ed) diff --git a/test cases/d/3 shared library/no-installed-files b/test cases/d/3 shared library/no-installed-files deleted file mode 100644 index e69de29..0000000 --- a/test cases/d/3 shared library/no-installed-files +++ /dev/null diff --git a/test cases/d/4 library versions/meson.build b/test cases/d/4 library versions/meson.build index 26cc38a..f680651 100644 --- a/test cases/d/4 library versions/meson.build +++ b/test cases/d/4 library versions/meson.build @@ -1,25 +1,22 @@ project('D library versions', 'd') if meson.get_compiler('d').get_id() == 'gcc' - message('GDC can not build shared libraries. Test skipped.') - install_data('no-installed-files', install_dir : '') -else - - shared_library('some', 'lib.d', - version : '1.2.3', - soversion : '0', - install : true) + error('MESON_SKIP_TEST: GDC can not build shared libraries') +endif - shared_library('noversion', 'lib.d', - install : true) +shared_library('some', 'lib.d', + version : '1.2.3', + soversion : '0', + install : true) - shared_library('onlyversion', 'lib.d', - version : '1.4.5', - install : true) +shared_library('noversion', 'lib.d', + install : true) - shared_library('onlysoversion', 'lib.d', - # Also test that int soversion is acceptable - soversion : 5, - install : true) +shared_library('onlyversion', 'lib.d', + version : '1.4.5', + install : true) -endif +shared_library('onlysoversion', 'lib.d', + # Also test that int soversion is acceptable + soversion : 5, + install : true) diff --git a/test cases/d/4 library versions/no-installed-files b/test cases/d/4 library versions/no-installed-files deleted file mode 100644 index e69de29..0000000 --- a/test cases/d/4 library versions/no-installed-files +++ /dev/null diff --git a/test cases/failing build/1 vala c werror/unused-var.c b/test cases/failing build/1 vala c werror/unused-var.c index e11d64c..6b85078 100644 --- a/test cases/failing build/1 vala c werror/unused-var.c +++ b/test cases/failing build/1 vala c werror/unused-var.c @@ -1,3 +1,5 @@ +#warning "something" + int somelib(void) { diff --git a/test cases/fortran/1 basic/meson.build b/test cases/fortran/1 basic/meson.build index 9c40951..833a177 100644 --- a/test cases/fortran/1 basic/meson.build +++ b/test cases/fortran/1 basic/meson.build @@ -2,6 +2,6 @@ project('simple fortran', 'fortran') add_global_arguments('-fbounds-check', language : 'fortran') -e = executable('simple', 'simple.f95', +e = executable('simple', 'simple.f90', fortran_args : '-ffree-form') test('Simple Fortran', e) diff --git a/test cases/fortran/1 basic/simple.f95 b/test cases/fortran/1 basic/simple.f90 index e0fb1d8..e0fb1d8 100644 --- a/test cases/fortran/1 basic/simple.f95 +++ b/test cases/fortran/1 basic/simple.f90 diff --git a/test cases/fortran/2 modules/meson.build b/test cases/fortran/2 modules/meson.build index 0087c26..030f255 100644 --- a/test cases/fortran/2 modules/meson.build +++ b/test cases/fortran/2 modules/meson.build @@ -1,4 +1,4 @@ project('modules', 'fortran') -e = executable('modprog', 'stuff.f95', 'prog.f95') +e = executable('modprog', 'stuff.f90', 'prog.f90') test('moduletest', e) diff --git a/test cases/fortran/2 modules/prog.f95 b/test cases/fortran/2 modules/prog.f90 index c3998cc..c3998cc 100644 --- a/test cases/fortran/2 modules/prog.f95 +++ b/test cases/fortran/2 modules/prog.f90 diff --git a/test cases/fortran/2 modules/stuff.f95 b/test cases/fortran/2 modules/stuff.f90 index 4a6399b..4a6399b 100644 --- a/test cases/fortran/2 modules/stuff.f95 +++ b/test cases/fortran/2 modules/stuff.f90 diff --git a/test cases/fortran/5 static/main.f95 b/test cases/fortran/5 static/main.f90 index dc6454c..dc6454c 100644 --- a/test cases/fortran/5 static/main.f95 +++ b/test cases/fortran/5 static/main.f90 diff --git a/test cases/fortran/5 static/meson.build b/test cases/fortran/5 static/meson.build index d6f922b..bd74a29 100644 --- a/test cases/fortran/5 static/meson.build +++ b/test cases/fortran/5 static/meson.build @@ -1,5 +1,5 @@ project('try-static-library', 'fortran') -static_hello = static_library('static_hello', 'static_hello.f95') +static_hello = static_library('static_hello', 'static_hello.f90') -executable('test_exe', 'main.f95', link_with : static_hello) +executable('test_exe', 'main.f90', link_with : static_hello) diff --git a/test cases/fortran/5 static/static_hello.f95 b/test cases/fortran/5 static/static_hello.f90 index 63415b0..63415b0 100644 --- a/test cases/fortran/5 static/static_hello.f95 +++ b/test cases/fortran/5 static/static_hello.f90 diff --git a/test cases/fortran/6 dynamic/dynamic.f95 b/test cases/fortran/6 dynamic/dynamic.f90 index e78a406..e78a406 100644 --- a/test cases/fortran/6 dynamic/dynamic.f95 +++ b/test cases/fortran/6 dynamic/dynamic.f90 diff --git a/test cases/fortran/6 dynamic/main.f95 b/test cases/fortran/6 dynamic/main.f90 index cb3a53f..cb3a53f 100644 --- a/test cases/fortran/6 dynamic/main.f95 +++ b/test cases/fortran/6 dynamic/main.f90 diff --git a/test cases/fortran/6 dynamic/meson.build b/test cases/fortran/6 dynamic/meson.build index 53edaf6..c791dac 100644 --- a/test cases/fortran/6 dynamic/meson.build +++ b/test cases/fortran/6 dynamic/meson.build @@ -1,4 +1,4 @@ project('dynamic_fortran', 'fortran') -dynamic = shared_library('dynamic', 'dynamic.f95') -executable('test_exe', 'main.f95', link_with : dynamic) +dynamic = shared_library('dynamic', 'dynamic.f90') +executable('test_exe', 'main.f90', link_with : dynamic) diff --git a/test cases/frameworks/11 gir subproject/gir/meson.build b/test cases/frameworks/11 gir subproject/gir/meson.build index e92c641..48e0a47 100644 --- a/test cases/frameworks/11 gir subproject/gir/meson.build +++ b/test cases/frameworks/11 gir subproject/gir/meson.build @@ -28,8 +28,10 @@ gnome.generate_gir( message('TEST: ' + girsubproject.outdir()) +envdata = environment() +envdata.append('GI_TYPELIB_PATH', girsubproject.outdir(), 'subprojects/mesongir', separator : ':') +envdata.append('LD_LIBRARY_PATH', girsubproject.outdir(), 'subprojects/mesongir') + test('gobject introspection/subproject/c', girexe) test('gobject introspection/subproject/py', find_program('prog.py'), - env : ['GI_TYPELIB_PATH=' + girsubproject.outdir() + ':subprojects/mesongir', - 'LD_LIBRARY_PATH=' + girsubproject.outdir() + ':subprojects/mesongir', - ]) + env : envdata) diff --git a/test cases/frameworks/7 gnome/gir/meson.build b/test cases/frameworks/7 gnome/gir/meson.build index beddc81..f3a4534 100644 --- a/test cases/frameworks/7 gnome/gir/meson.build +++ b/test cases/frameworks/7 gnome/gir/meson.build @@ -33,7 +33,8 @@ gnome.generate_gir( test('gobject introspection/c', girexe) gir_paths = ':'.join([girlib.outdir(), dep1lib.outdir(), dep2lib.outdir()]) +envdata = environment() +envdata.append('GI_TYPELIB_PATH', gir_paths, separator : ':') +envdata.append('LD_LIBRARY_PATH', gir_paths) test('gobject introspection/py', find_program('prog.py'), - env : ['GI_TYPELIB_PATH=' + gir_paths, - 'LD_LIBRARY_PATH=' + gir_paths, - ]) + env : envdata) diff --git a/test cases/frameworks/7 gnome/meson.build b/test cases/frameworks/7 gnome/meson.build index a771e71..c75c049 100644 --- a/test cases/frameworks/7 gnome/meson.build +++ b/test cases/frameworks/7 gnome/meson.build @@ -1,5 +1,14 @@ project('gobject-introspection', 'c') +cc = meson.get_compiler('c') + +add_global_arguments('-DMESON_TEST', language : 'c') +if cc.get_id() == 'intel' + # Ignore invalid GCC pragma warnings from glib + # https://bugzilla.gnome.org/show_bug.cgi?id=776562 + add_global_arguments('-wd2282', language : 'c') +endif + gnome = import('gnome') gio = dependency('gio-2.0') giounix = dependency('gio-unix-2.0') @@ -7,7 +16,6 @@ glib = dependency('glib-2.0') gobj = dependency('gobject-2.0') gir = dependency('gobject-introspection-1.0') gmod = dependency('gmodule-2.0') -add_global_arguments('-DMESON_TEST', language : 'c') subdir('resources-data') subdir('resources') diff --git a/test cases/unit/2 testsetups/buggy.c b/test cases/unit/2 testsetups/buggy.c new file mode 100644 index 0000000..5d20a24 --- /dev/null +++ b/test cases/unit/2 testsetups/buggy.c @@ -0,0 +1,14 @@ +#include<stdio.h> +#include<stdlib.h> + +#include<impl.h> + +int main(int argc, char **argv) { + char *ten = malloc(10); + do_nasty(ten); + free(ten); + if(getenv("TEST_ENV")) { + printf("TEST_ENV is set.\n"); + } + return 0; +} diff --git a/test cases/unit/2 testsetups/impl.c b/test cases/unit/2 testsetups/impl.c new file mode 100644 index 0000000..d87f3de --- /dev/null +++ b/test cases/unit/2 testsetups/impl.c @@ -0,0 +1,5 @@ +/* Write past the end. */ + +void do_nasty(char *ptr) { + ptr[10] = 'n'; +} diff --git a/test cases/unit/2 testsetups/impl.h b/test cases/unit/2 testsetups/impl.h new file mode 100644 index 0000000..7a08cb3 --- /dev/null +++ b/test cases/unit/2 testsetups/impl.h @@ -0,0 +1,3 @@ +#pragma once + +void do_nasty(char *ptr); diff --git a/test cases/unit/2 testsetups/meson.build b/test cases/unit/2 testsetups/meson.build new file mode 100644 index 0000000..a65548e --- /dev/null +++ b/test cases/unit/2 testsetups/meson.build @@ -0,0 +1,16 @@ +project('testsetups', 'c') + +vg = find_program('valgrind') + +# This is only set when running under Valgrind test setup. +env = environment() +env.set('TEST_ENV', '1') + +add_test_setup('valgrind', + exe_wrapper : [vg, '--error-exitcode=1', '--leak-check=full'], + timeout_multiplier : 100, + env : env) + +buggy = executable('buggy', 'buggy.c', 'impl.c') +test('Test buggy', buggy) + diff --git a/test cases/unit/3 subproject defaults/meson.build b/test cases/unit/3 subproject defaults/meson.build new file mode 100644 index 0000000..3bf05d0 --- /dev/null +++ b/test cases/unit/3 subproject defaults/meson.build @@ -0,0 +1,11 @@ +project('subproject defaults', 'c', + default_options : ['defopoverride=defopt', # This should be overridden. + 'fromcmdline=defopt'] # This should get the value set in command line. + ) + +subproject('foob', default_options : ['fromspfunc=spfunc', 'fromspfunconly=spfunc']) + +assert(get_option('fromcmdline') == 'cmdline', 'Default option defined in cmd line is incorrect: ' + get_option('fromcmdline')) +assert(get_option('defopoverride') == 'defopt', 'Default option without cmd line override is incorrect: ' + get_option('defopoverride')) +assert(get_option('fromoptfile') == 'optfile', 'Default value from option file is incorrect: ' + get_option('fromoptfile')) + diff --git a/test cases/unit/3 subproject defaults/meson_options.txt b/test cases/unit/3 subproject defaults/meson_options.txt new file mode 100644 index 0000000..b63f512 --- /dev/null +++ b/test cases/unit/3 subproject defaults/meson_options.txt @@ -0,0 +1,3 @@ +option('defopoverride', type : 'string', value : 'optfile', description : 'A value for overriding.') +option('fromcmdline', type : 'string', value : 'optfile', description : 'A value for overriding.') +option('fromoptfile', type : 'string', value : 'optfile', description : 'A value for not overriding.') diff --git a/test cases/unit/3 subproject defaults/subprojects/foob/meson.build b/test cases/unit/3 subproject defaults/subprojects/foob/meson.build new file mode 100644 index 0000000..69f01d1 --- /dev/null +++ b/test cases/unit/3 subproject defaults/subprojects/foob/meson.build @@ -0,0 +1,12 @@ +project('foob', 'c', + default_options : ['defopoverride=s_defopt', # This should be overridden. + 'fromspfunc=s_defopt', # This is specified with a default_options kwarg to subproject() + 'fromcmdline=s_defopt'] # This should get the value set in command line. + ) + +assert(get_option('fromcmdline') == 's_cmdline', 'Default option defined in cmd line is incorrect: ' + get_option('fromcmdline')) +assert(get_option('fromspfunc') == 'spfunc', 'Default option set with subproject() incorrect: ' + get_option('fromspfunc')) +assert(get_option('fromspfunconly') == 'spfunc', 'Default option set with subproject() incorrect: ' + get_option('fromspfunc')) +assert(get_option('defopoverride') == 's_defopt', 'Default option without cmd line override is incorrect: ' + get_option('defopoverride')) +assert(get_option('fromoptfile') == 's_optfile', 'Default value from option file is incorrect: ' + get_option('fromoptfile')) + diff --git a/test cases/unit/3 subproject defaults/subprojects/foob/meson_options.txt b/test cases/unit/3 subproject defaults/subprojects/foob/meson_options.txt new file mode 100644 index 0000000..a9a615e --- /dev/null +++ b/test cases/unit/3 subproject defaults/subprojects/foob/meson_options.txt @@ -0,0 +1,5 @@ +option('defopoverride', type : 'string', value : 's_optfile', description : 'A value for overriding.') +option('fromcmdline', type : 'string', value : 's_optfile', description : 'A value for overriding.') +option('fromspfunc', type : 'string', value : 's_optfile', description : 'A value for overriding.') +option('fromspfunconly', type : 'string', value : 's_optfile', description : 'A value for overriding.') +option('fromoptfile', type : 'string', value : 's_optfile', description : 'A value for not overriding.') diff --git a/test cases/windows/7 mingw dll versioning/meson.build b/test cases/windows/7 mingw dll versioning/meson.build index 23a3343..2f6035e 100644 --- a/test cases/windows/7 mingw dll versioning/meson.build +++ b/test cases/windows/7 mingw dll versioning/meson.build @@ -2,16 +2,16 @@ project('mingw dll versioning', 'c') cc = meson.get_compiler('c') +if cc.get_id() == 'msvc' + error('MESON_SKIP_TEST: test is only for MinGW') +endif + # Test that MinGW/GCC creates correctly-named dll files and dll.a files, # and also installs them in the right place -if cc.get_id() != 'msvc' - shared_library('some', 'lib.c', - version : '1.2.3', - soversion : '0', - install : true) +shared_library('some', 'lib.c', + version : '1.2.3', + soversion : '0', + install : true) - shared_library('noversion', 'lib.c', - install : true) -else - install_data('no-installed-files', install_dir : '') -endif +shared_library('noversion', 'lib.c', + install : true) diff --git a/test cases/windows/7 mingw dll versioning/no-installed-files b/test cases/windows/7 mingw dll versioning/no-installed-files deleted file mode 100644 index e69de29..0000000 --- a/test cases/windows/7 mingw dll versioning/no-installed-files +++ /dev/null diff --git a/test cases/windows/8 msvc dll versioning/meson.build b/test cases/windows/8 msvc dll versioning/meson.build index 0c36173..d6aecb6 100644 --- a/test cases/windows/8 msvc dll versioning/meson.build +++ b/test cases/windows/8 msvc dll versioning/meson.build @@ -2,15 +2,15 @@ project('msvc dll versioning', 'c') cc = meson.get_compiler('c') +if cc.get_id() != 'msvc' + error('MESON_SKIP_TEST: test is only for msvc') +endif + # Test that MSVC creates foo-0.dll and bar.dll -if cc.get_id() == 'msvc' - shared_library('some', 'lib.c', - version : '1.2.3', - soversion : '0', - install : true) +shared_library('some', 'lib.c', + version : '1.2.3', + soversion : '0', + install : true) - shared_library('noversion', 'lib.c', - install : true) -else - install_data('no-installed-files', install_dir : '') -endif +shared_library('noversion', 'lib.c', + install : true) diff --git a/test cases/windows/8 msvc dll versioning/no-installed-files b/test cases/windows/8 msvc dll versioning/no-installed-files deleted file mode 100644 index e69de29..0000000 --- a/test cases/windows/8 msvc dll versioning/no-installed-files +++ /dev/null |