diff options
78 files changed, 1495 insertions, 622 deletions
@@ -4,9 +4,11 @@ MesonĀ® is a project to create the best possible next-generation build system. -####Build status +#### Status -[](https://travis-ci.org/mesonbuild/meson) [](https://ci.appveyor.com/project/jpakkane/meson) +[](https://pypi.python.org/pypi/meson) +[](https://travis-ci.org/mesonbuild/meson) +[](https://ci.appveyor.com/project/jpakkane/meson) ####Dependencies @@ -38,6 +40,10 @@ executable run the following command: Note that the source checkout may not be `meson` because it would clash with the generated binary name. +This will zip all files inside the source checkout into the script +which includes hundreds of tests, so you might want to temporarily +remove those before running it. + ####Running Meson requires that you have a source directory and a build directory diff --git a/authors.txt b/authors.txt index 9bf3e33..03e8478 100644 --- a/authors.txt +++ b/authors.txt @@ -56,3 +56,4 @@ Aurelien Jarno Mark Schulte Paulo Antonio Alvarez Olexa Bilaniuk +Daniel Stone diff --git a/manual tests/10 hg wrap/meson.build b/manual tests/6 hg wrap/meson.build index c7ac004..c7ac004 100644 --- a/manual tests/10 hg wrap/meson.build +++ b/manual tests/6 hg wrap/meson.build diff --git a/manual tests/10 hg wrap/prog.c b/manual tests/6 hg wrap/prog.c index df38000..df38000 100644 --- a/manual tests/10 hg wrap/prog.c +++ b/manual tests/6 hg wrap/prog.c diff --git a/manual tests/10 hg wrap/subprojects/samplesubproject.wrap b/manual tests/6 hg wrap/subprojects/samplesubproject.wrap index 6d3b3f2..6d3b3f2 100644 --- a/manual tests/10 hg wrap/subprojects/samplesubproject.wrap +++ b/manual tests/6 hg wrap/subprojects/samplesubproject.wrap @@ -14,10 +14,21 @@ # See the License for the specific language governing permissions and # limitations under the License. -from mesonbuild import mesonmain -import sys, os +from mesonbuild import mlog, mesonmain +import sys, os, locale def main(): + # Warn if the locale is not UTF-8. This can cause various unfixable issues + # such as os.stat not being able to decode filenames with unicode in them. + # There is no way to reset both the preferred encoding and the filesystem + # encoding, so we can just warn about it. + e = locale.getpreferredencoding() + if e.upper() != 'UTF-8': + mlog.warning('You are using {!r} which is not a a Unicode-compatible ' + 'locale.'.format(e)) + mlog.warning('You might see errors if you use UTF-8 strings as ' + 'filenames, as strings, or as file contents.') + mlog.warning('Please switch to a UTF-8 locale for your platform.') # Always resolve the command path so Ninja can find it for regen, tests, etc. launcher = os.path.realpath(sys.argv[0]) return mesonmain.run(launcher, sys.argv[1:]) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index b82227f..49b6008 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -50,7 +50,7 @@ class ExecutableSerialisation(): class TestSerialisation: def __init__(self, name, suite, fname, is_cross, exe_wrapper, is_parallel, cmd_args, env, - should_fail, valgrind_args, timeout, workdir, extra_paths): + should_fail, timeout, workdir, extra_paths): self.name = name self.suite = suite self.fname = fname @@ -60,7 +60,6 @@ class TestSerialisation: self.cmd_args = cmd_args self.env = env self.should_fail = should_fail - self.valgrind_args = valgrind_args self.timeout = timeout self.workdir = workdir self.extra_paths = extra_paths @@ -280,13 +279,9 @@ class Backend(): result = [] targetdir = self.get_target_private_dir(extobj.target) # With unity builds, there's just one object that contains all the - # sources, so if we want all the objects, just return that. + # sources, and we only support extracting all the objects in this mode, + # so just return that. if self.environment.coredata.get_builtin_option('unity'): - if not extobj.unity_compatible: - # This should never happen - msg = 'BUG: Meson must not allow extracting single objects ' \ - 'in Unity builds' - raise AssertionError(msg) comp = get_compiler_for_source(extobj.target.compilers.values(), extobj.srclist[0]) # The unity object name uses the full absolute path of the source file @@ -443,7 +438,7 @@ class Backend(): a = os.path.join(self.environment.get_build_dir(), a.rel_to_builddir(self.build_to_src)) cmd_args.append(a) ts = TestSerialisation(t.get_name(), t.suite, fname, is_cross, exe_wrapper, - t.is_parallel, cmd_args, t.env, t.should_fail, t.valgrind_args, + t.is_parallel, cmd_args, t.env, t.should_fail, t.timeout, t.workdir, extra_paths) arr.append(ts) pickle.dump(arr, datafile) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 2cd3724..71797ed 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -142,7 +142,6 @@ class NinjaBackend(backends.Backend): self.ninja_filename = 'build.ninja' self.fortran_deps = {} self.all_outputs = {} - self.valgrind = environment.find_valgrind() def detect_vs_dep_prefix(self, tempfilename): '''VS writes its dependency in a locale dependent format. @@ -657,12 +656,15 @@ int dummy; incroot = self.environment.get_includedir() headers = self.build.get_headers() + srcdir = self.environment.get_source_dir() + builddir = self.environment.get_build_dir() for h in headers: outdir = h.get_custom_install_dir() if outdir is None: outdir = os.path.join(incroot, h.get_install_subdir()) for f in h.get_sources(): - abspath = os.path.join(self.environment.get_source_dir(), h.get_source_subdir(), f) + assert(isinstance(f, File)) + abspath = f.absolute_path(srcdir, builddir) i = [abspath, outdir] d.headers.append(i) @@ -708,56 +710,25 @@ int dummy; dst_dir = os.path.join(self.environment.get_prefix(), sd.install_dir) d.install_subdirs.append([src_dir, inst_dir, dst_dir]) - def write_test_suite_targets(self, cmd, outfile): - suites = {} - for t in self.build.get_tests(): - for s in t.suite: - suites[s] = True - suites = list(suites.keys()) - suites.sort() - for s in suites: - if s == '': - visible_name = 'for top level tests' - else: - visible_name = s - elem = NinjaBuildElement(self.all_outputs, 'test:' + s, 'CUSTOM_COMMAND', ['all', 'PHONY']) - elem.add_item('COMMAND', cmd + ['--suite=' + s]) - elem.add_item('DESC', 'Running test suite %s.' % visible_name) - elem.add_item('pool', 'console') - elem.write(outfile) - - if self.valgrind: - velem = NinjaBuildElement(self.all_outputs, 'test-valgrind:' + s, 'CUSTOM_COMMAND', ['all', 'PHONY']) - velem.add_item('COMMAND', cmd + ['--wrapper=' + self.valgrind, '--suite=' + s]) - velem.add_item('DESC', 'Running test suite %s under Valgrind.' % visible_name) - velem.add_item('pool', 'console') - velem.write(outfile) - def generate_tests(self, outfile): - (test_data, benchmark_data) = self.serialise_tests() - script_root = self.environment.get_script_dir() - cmd = [ sys.executable, self.environment.get_build_command(), '--internal', 'test' ] + self.serialise_tests() + meson_exe = self.environment.get_build_command() + (base, ext) = os.path.splitext(meson_exe) + test_exe = base + 'test' + ext + cmd = [sys.executable, test_exe, '--no-rebuild'] if not self.environment.coredata.get_builtin_option('stdsplit'): cmd += ['--no-stdsplit'] if self.environment.coredata.get_builtin_option('errorlogs'): cmd += ['--print-errorlogs'] - cmd += [ test_data ] elem = NinjaBuildElement(self.all_outputs, 'test', 'CUSTOM_COMMAND', ['all', 'PHONY']) elem.add_item('COMMAND', cmd) elem.add_item('DESC', 'Running all tests.') elem.add_item('pool', 'console') elem.write(outfile) - self.write_test_suite_targets(cmd, outfile) - - if self.valgrind: - velem = NinjaBuildElement(self.all_outputs, 'test-valgrind', 'CUSTOM_COMMAND', ['all', 'PHONY']) - velem.add_item('COMMAND', cmd + ['--wrapper=' + self.valgrind]) - velem.add_item('DESC', 'Running test suite under Valgrind.') - velem.add_item('pool', 'console') - velem.write(outfile) # And then benchmarks. - cmd = [sys.executable, self.environment.get_build_command(), '--internal', 'benchmark', benchmark_data] + cmd = [sys.executable, test_exe, '--benchmark','--logbase', + 'benchmarklog', '--num-processes=1', '--no-rebuild'] elem = NinjaBuildElement(self.all_outputs, 'benchmark', 'CUSTOM_COMMAND', ['all', 'PHONY']) elem.add_item('COMMAND', cmd) elem.add_item('DESC', 'Running benchmark suite.') @@ -804,7 +775,6 @@ int dummy; def generate_jar_target(self, target, outfile): fname = target.get_filename() - subdir = target.get_subdir() outname_rel = os.path.join(self.get_target_dir(target), fname) src_list = target.get_sources() class_list = [] @@ -889,6 +859,9 @@ int dummy; def generate_single_java_compile(self, src, target, compiler, outfile): args = [] args += compiler.get_buildtype_args(self.environment.coredata.get_builtin_option('buildtype')) + args += self.build.get_global_args(compiler) + args += self.build.get_project_args(compiler, target.subproject) + args += target.get_java_args() args += compiler.get_output_args(self.get_target_private_dir(target)) for i in target.include_dirs: for idir in i.get_incdirs(): @@ -1044,9 +1017,11 @@ int dummy; args += valac.get_werror_args() for d in target.get_external_deps(): if isinstance(d, dependencies.PkgConfigDependency): - if d.name == 'glib-2.0' and d.version_requirement is not None \ - and d.version_requirement.startswith(('>=', '==')): - args += ['--target-glib', d.version_requirement[2:]] + if d.name == 'glib-2.0' and d.version_reqs is not None: + for req in d.version_reqs: + if req.startswith(('>=', '==')): + args += ['--target-glib', req[2:]] + break args += ['--pkg', d.name] elif isinstance(d, dependencies.ExternalLibrary): args += d.get_lang_args('vala') @@ -2031,14 +2006,21 @@ rule FORTRAN_DEP_HACK def generate_shlib_aliases(self, target, outdir): basename = target.get_filename() aliases = target.get_aliaslist() - for alias in aliases: + for i, alias in enumerate(aliases): aliasfile = os.path.join(self.environment.get_build_dir(), outdir, alias) try: os.remove(aliasfile) except Exception: pass + # If both soversion and version are set and to different values, + # the .so symlink must point to the soversion symlink rather than the + # original file. + if i == 0 and len(aliases) > 1: + pointed_to_filename = aliases[1] + else: + pointed_to_filename = basename try: - os.symlink(basename, aliasfile) + os.symlink(pointed_to_filename, aliasfile) except NotImplementedError: mlog.debug("Library versioning disabled because symlinks are not supported.") except OSError: diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 0e23777..462a55b 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -28,6 +28,7 @@ known_basic_kwargs = {'install' : True, 'cs_args' : True, 'vala_args' : True, 'd_args' : True, + 'java_args' : True, 'link_args' : True, 'link_depends': True, 'link_with' : True, @@ -197,10 +198,11 @@ class ExtractedObjects(): ''' Holds a list of sources for which the objects must be extracted ''' - def __init__(self, target, srclist): + def __init__(self, target, srclist, is_unity): self.target = target self.srclist = srclist - self.check_unity_compatible() + if is_unity: + self.check_unity_compatible() def check_unity_compatible(self): # Figure out if the extracted object list is compatible with a Unity @@ -210,11 +212,9 @@ class ExtractedObjects(): # from each unified source file. # If the list of sources for which we want objects is the same as the # list of sources that go into each unified build, we're good. - self.unity_compatible = False srclist_set = set(self.srclist) # Objects for all the sources are required, so we're compatible if srclist_set == set(self.target.sources): - self.unity_compatible = True return # Check if the srclist is a subset (of the target's sources) that is # going to form a unified source file and a single object @@ -222,7 +222,6 @@ class ExtractedObjects(): self.target.sources) for srcs in compsrcs.values(): if srclist_set == set(srcs): - self.unity_compatible = True return msg = 'Single object files can not be extracted in Unity builds. ' \ 'You can only extract all the object files at once.' @@ -272,6 +271,7 @@ class BuildTarget(): self.subdir = subdir self.subproject = subproject # Can not be calculated from subdir as subproject dirname can be changed per project. self.is_cross = is_cross + self.is_unity = environment.coredata.get_builtin_option('unity') self.environment = environment self.sources = [] self.compilers = {} @@ -457,10 +457,10 @@ class BuildTarget(): if src not in self.sources: raise MesonException('Tried to extract unknown source %s.' % src) obj_src.append(src) - return ExtractedObjects(self, obj_src) + return ExtractedObjects(self, obj_src, self.is_unity) def extract_all_objects(self): - return ExtractedObjects(self, self.sources) + return ExtractedObjects(self, self.sources, self.is_unity) def get_all_link_deps(self): return self.get_transitive_link_deps() @@ -1069,12 +1069,10 @@ class SharedLibrary(BuildTarget): self.soversion = str(self.soversion) if not isinstance(self.soversion, str): raise InvalidArguments('Shared library soversion is not a string or integer.') - try: - int(self.soversion) - except ValueError: - raise InvalidArguments('Shared library soversion must be a valid integer') elif self.ltversion: # library version is defined, get the soversion from that + # We replicate what Autotools does here and take the first + # number of the version by default. self.soversion = self.ltversion.split('.')[0] # Visual Studio module-definitions file if 'vs_module_defs' in kwargs: @@ -1342,7 +1340,7 @@ class Jar(BuildTarget): if not s.endswith('.java'): raise InvalidArguments('Jar source %s is not a java file.' % s) self.filename = self.name + '.jar' - incdirs = kwargs.get('include_directories', []) + self.java_args = kwargs.get('java_args', []) def get_main_class(self): return self.main_class @@ -1350,6 +1348,9 @@ class Jar(BuildTarget): def type_suffix(self): return "@jar" + def get_java_args(self): + return self.java_args + class ConfigureFile(): def __init__(self, subdir, sourcename, targetname, configuration_data): diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py index fef4c1d..2534a47 100644 --- a/mesonbuild/compilers.py +++ b/mesonbuild/compilers.py @@ -701,8 +701,28 @@ int main () {{ #endif return 0; }}''' - args = extra_args + self.get_compiler_check_args() - return self.compiles(templ.format(hname, symbol, prefix), env, args, dependencies) + return self.compiles(templ.format(hname, symbol, prefix), env, + extra_args, dependencies) + + @staticmethod + def _override_args(args, override): + ''' + Add @override to @args in such a way that arguments are overriden + correctly. + + We want the include directories to be added first (since they are + chosen left-to-right) and all other arguments later (since they + override previous arguments or add to a list that's chosen + right-to-left). + ''' + before_args = [] + after_args = [] + for arg in override: + if arg.startswith(('-I', '/I')): + before_args.append(arg) + else: + after_args.append(arg) + return before_args + args + after_args def compiles(self, code, env, extra_args=None, dependencies=None): if extra_args is None: @@ -713,9 +733,10 @@ int main () {{ dependencies = [] elif not isinstance(dependencies, list): dependencies = [dependencies] + # Add compile flags needed by dependencies after converting to the + # native type of the selected compiler cargs = [a for d in dependencies for a in d.get_compile_args()] - # Convert flags to the native type of the selected compiler - args = self.unix_link_flags_to_native(cargs + extra_args) + args = self.unix_link_flags_to_native(cargs) # Read c_args/cpp_args/etc from the cross-info file (if needed) args += self.get_cross_extra_flags(env, compile=True, link=False) # Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS from the env @@ -723,6 +744,11 @@ int main () {{ args += env.coredata.external_args[self.language] # We only want to compile; not link args += self.get_compile_only_args() + # Append extra_args to the compiler check args such that it overrides + extra_args = self._override_args(self.get_compiler_check_args(), extra_args) + extra_args = self.unix_link_flags_to_native(extra_args) + # Append both to the compiler args such that they override them + args = self._override_args(args, extra_args) with self.compile(code, args) as p: return p.returncode == 0 @@ -736,17 +762,24 @@ int main () {{ dependencies = [] elif not isinstance(dependencies, list): dependencies = [dependencies] + # Add compile and link flags needed by dependencies after converting to + # the native type of the selected compiler cargs = [a for d in dependencies for a in d.get_compile_args()] link_args = [a for d in dependencies for a in d.get_link_args()] - # Convert flags to the native type of the selected compiler - args = self.unix_link_flags_to_native(cargs + link_args + extra_args) + args = self.unix_link_flags_to_native(cargs + link_args) # Select a CRT if needed since we're linking args += self.get_linker_debug_crt_args() - # Read c_args/c_link_args/cpp_args/cpp_link_args/etc from the cross-info file (if needed) + # Read c_args/c_link_args/cpp_args/cpp_link_args/etc from the + # cross-info file (if needed) args += self.get_cross_extra_flags(env, compile=True, link=True) # Add LDFLAGS from the env. We assume that the user has ensured these # are compiler-specific args += env.coredata.external_link_args[self.language] + # Append extra_args to the compiler check args such that it overrides + extra_args = self._override_args(self.get_compiler_check_args(), extra_args) + extra_args = self.unix_link_flags_to_native(extra_args) + # Append both to the compiler args such that they override them + args = self._override_args(args, extra_args) return self.compile(code, args) def links(self, code, env, extra_args=None, dependencies=None): @@ -795,7 +828,6 @@ int main(int argc, char **argv) {{ %s int temparray[%d-sizeof(%s)]; ''' - args = extra_args + self.get_compiler_check_args() if not self.compiles(element_exists_templ.format(prefix, element), env, args, dependencies): return -1 for i in range(1, 1024): @@ -844,7 +876,6 @@ struct tmp { int testarray[%d-offsetof(struct tmp, target)]; ''' - args = extra_args + self.get_compiler_check_args() if not self.compiles(type_exists_templ.format(typename), env, args, dependencies): return -1 for i in range(1, 1024): @@ -927,9 +958,11 @@ int main(int argc, char **argv) { """ # Add the 'prefix', aka defines, includes, etc that the user provides head = '#include <limits.h>\n{0}\n' - # We don't know what the function takes or returns, so try to use it as - # a function pointer - main = '\nint main() {{ void *a = (void*) &{1}; }}' + # We don't know what the function takes or returns, so return it as an int. + # Just taking the address or comparing it to void is not enough because + # compilers are smart enough to optimize it away. The resulting binary + # is not run so we don't care what the return value is. + main = '\nint main() {{ void *a = (void*) &{1}; long b = (long) a; return (int) b; }}' return head, main def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None): @@ -978,14 +1011,14 @@ int main(int argc, char **argv) { head, main = self._no_prototype_templ() templ = head + stubs_fail + main - args = extra_args + self.get_compiler_check_args() - if self.links(templ.format(prefix, funcname), env, args, dependencies): + if self.links(templ.format(prefix, funcname), env, extra_args, dependencies): return True # Some functions like alloca() are defined as compiler built-ins which # are inlined by the compiler, so test for that instead. Built-ins are # special functions that ignore all includes and defines, so we just # directly try to link via main(). - return self.links('int main() {{ {0}; }}'.format('__builtin_' + funcname), env, args, dependencies) + return self.links('int main() {{ {0}; }}'.format('__builtin_' + funcname), + env, extra_args, dependencies) def has_members(self, typename, membernames, prefix, env, extra_args=None, dependencies=None): if extra_args is None: @@ -1069,8 +1102,8 @@ class CPPCompiler(CCompiler): #include <{0}> using {1}; int main () {{ return 0; }}''' - args = extra_args + self.get_compiler_check_args() - return self.compiles(templ.format(hname, symbol, prefix), env, args, dependencies) + return self.compiles(templ.format(hname, symbol, prefix), env, + extra_args, dependencies) class ObjCCompiler(CCompiler): def __init__(self, exelist, version, is_cross, exe_wrap): @@ -1844,11 +1877,7 @@ class VisualStudioCCompiler(CCompiler): } def get_option_link_args(self, options): - # FIXME: See GnuCCompiler.get_option_link_args - if 'c_winlibs' in options: - return options['c_winlibs'].value[:] - else: - return msvc_winlibs[:] + return options['c_winlibs'].value[:] def unix_link_flags_to_native(self, args): result = [] @@ -1953,11 +1982,7 @@ class VisualStudioCPPCompiler(VisualStudioCCompiler, CPPCompiler): return args def get_option_link_args(self, options): - # FIXME: See GnuCCompiler.get_option_link_args - if 'cpp_winlibs' in options: - return options['cpp_winlibs'].value[:] - else: - return msvc_winlibs[:] + return options['cpp_winlibs'].value[:] GCC_STANDARD = 0 GCC_OSX = 1 @@ -2069,17 +2094,7 @@ class GnuCCompiler(GnuCompiler, CCompiler): def get_option_link_args(self, options): if self.gcc_type == GCC_MINGW: - # FIXME: This check is needed because we currently pass - # cross-compiler options to the native compiler too and when - # cross-compiling from Windows to Linux, `options` will contain - # Linux-specific options which doesn't include `c_winlibs`. The - # proper fix is to allow cross-info files to specify compiler - # options and to maintain both cross and native compiler options in - # coredata: https://github.com/mesonbuild/meson/issues/1029 - if 'c_winlibs' in options: - return options['c_winlibs'].value[:] - else: - return gnu_winlibs[:] + return options['c_winlibs'].value[:] return [] class GnuCPPCompiler(GnuCompiler, CPPCompiler): @@ -2117,11 +2132,7 @@ class GnuCPPCompiler(GnuCompiler, CPPCompiler): def get_option_link_args(self, options): if self.gcc_type == GCC_MINGW: - # FIXME: See GnuCCompiler.get_option_link_args - if 'cpp_winlibs' in options: - return options['cpp_winlibs'].value[:] - else: - return gnu_winlibs[:] + return options['cpp_winlibs'].value[:] return [] def get_compiler_check_args(self): diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 0894f9a..29ea1bf 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -241,8 +241,6 @@ forbidden_target_names = {'clean': None, 'all': None, 'test': None, 'test:': None, - 'test-valgrind': None, - 'test-valgrind:': None, 'benchmark': None, 'install': None, 'build.ninja': None, diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py index 38945b4..a092732 100644 --- a/mesonbuild/dependencies.py +++ b/mesonbuild/dependencies.py @@ -23,7 +23,7 @@ import re import os, stat, glob, subprocess, shutil import sysconfig from collections import OrderedDict -from . mesonlib import MesonException +from . mesonlib import MesonException, version_compare, version_compare_many from . import mlog from . import mesonlib from .environment import detect_cpu_family, for_windows @@ -135,22 +135,27 @@ class PkgConfigDependency(Dependency): self.modversion = 'none' return found_msg = ['%s dependency' % self.type_string, mlog.bold(name), 'found:'] - self.version_requirement = kwargs.get('version', None) - if self.version_requirement is None: + self.version_reqs = kwargs.get('version', None) + if self.version_reqs is None: self.is_found = True else: - if not isinstance(self.version_requirement, str): - raise DependencyException('Version argument must be string.') - self.is_found = mesonlib.version_compare(self.modversion, self.version_requirement) + if not isinstance(self.version_reqs, (str, list)): + raise DependencyException('Version argument must be string or list.') + (self.is_found, not_found, found) = \ + version_compare_many(self.modversion, self.version_reqs) if not self.is_found: - found_msg += [mlog.red('NO'), 'found {!r}'.format(self.modversion), - 'but need {!r}'.format(self.version_requirement)] + found_msg += [mlog.red('NO'), + 'found {!r} but need:'.format(self.modversion), + ', '.join(["'{}'".format(e) for e in not_found])] + if found: + found_msg += ['; matched:', + ', '.join(["'{}'".format(e) for e in found])] if not self.silent: mlog.log(*found_msg) if self.required: raise DependencyException( 'Invalid version of a dependency, needed %s %s found %s.' % - (name, self.version_requirement, self.modversion)) + (name, not_found, self.modversion)) return found_msg += [mlog.green('YES'), self.modversion] if not self.silent: @@ -233,7 +238,7 @@ class PkgConfigDependency(Dependency): '(%s)' % out.decode().strip()) PkgConfigDependency.pkgconfig_found = True return - except Exception: + except (FileNotFoundError, PermissionError): pass PkgConfigDependency.pkgconfig_found = False if not self.silent: @@ -301,7 +306,7 @@ class WxDependency(Dependency): self.modversion = out.decode().strip() version_req = kwargs.get('version', None) if version_req is not None: - if not mesonlib.version_compare(self.modversion, version_req): + if not version_compare(self.modversion, version_req, strict=True): mlog.log('Wxwidgets version %s does not fullfill requirement %s' %\ (self.modversion, version_req)) return @@ -358,7 +363,7 @@ class WxDependency(Dependency): self.wxc = wxc WxDependency.wx_found = True return - except Exception: + except (FileNotFoundError, PermissionError): pass WxDependency.wxconfig_found = False mlog.log('Found wx-config:', mlog.red('NO')) @@ -1040,7 +1045,7 @@ class GnuStepDependency(Dependency): gp = subprocess.Popen([confprog, '--help'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) gp.communicate() - except FileNotFoundError: + except (FileNotFoundError, PermissionError): self.args = None mlog.log('Dependency GnuStep found:', mlog.red('NO'), '(no gnustep-config)') return diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 405685c..098f8ca 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -39,17 +39,12 @@ def find_coverage_tools(): genhtml_exe = None return (gcovr_exe, lcov_exe, genhtml_exe) -def find_valgrind(): - valgrind_exe = 'valgrind' - if not mesonlib.exe_exists([valgrind_exe, '--version']): - valgrind_exe = None - return valgrind_exe - def detect_ninja(): for n in ['ninja', 'ninja-build']: try: p = subprocess.Popen([n, '--version'], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) - except FileNotFoundError: + except (FileNotFoundError, PermissionError): + # Doesn't exist in PATH or isn't executable continue version = p.communicate()[0].decode(errors='ignore') # Perhaps we should add a way for the caller to know the failure mode diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index cb093ae..2167b81 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -404,10 +404,9 @@ class IncludeDirsHolder(InterpreterObject): class Headers(InterpreterObject): - def __init__(self, src_subdir, sources, kwargs): + def __init__(self, sources, kwargs): InterpreterObject.__init__(self) self.sources = sources - self.source_subdir = src_subdir self.install_subdir = kwargs.get('subdir', '') self.custom_install_dir = kwargs.get('install_dir', None) if self.custom_install_dir is not None: @@ -420,9 +419,6 @@ class Headers(InterpreterObject): def get_install_subdir(self): return self.install_subdir - def get_source_subdir(self): - return self.source_subdir - def get_sources(self): return self.sources @@ -557,7 +553,7 @@ class RunTargetHolder(InterpreterObject): self.held_object = build.RunTarget(name, command, args, dependencies, subdir) class Test(InterpreterObject): - def __init__(self, name, suite, exe, is_parallel, cmd_args, env, should_fail, valgrind_args, timeout, workdir): + def __init__(self, name, suite, exe, is_parallel, cmd_args, env, should_fail, timeout, workdir): InterpreterObject.__init__(self) self.name = name self.suite = suite @@ -566,7 +562,6 @@ class Test(InterpreterObject): self.cmd_args = cmd_args self.env = env self.should_fail = should_fail - self.valgrind_args = valgrind_args self.timeout = timeout self.workdir = workdir @@ -1561,12 +1556,12 @@ class Interpreter(InterpreterBase): raise InvalidCode('Tried to use unknown language "%s".' % lang) comp.sanity_check(self.environment.get_scratch_dir(), self.environment) self.coredata.compilers[lang] = comp + # Native compiler always exist so always add its options. + new_options = comp.get_options() if cross_comp is not None: cross_comp.sanity_check(self.environment.get_scratch_dir(), self.environment) self.coredata.cross_compilers[lang] = cross_comp - new_options = cross_comp.get_options() - else: - new_options = comp.get_options() + new_options.update(cross_comp.get_options()) optprefix = lang + '_' for i in new_options: if not i.startswith(optprefix): @@ -1624,18 +1619,22 @@ class Interpreter(InterpreterBase): break self.coredata.base_options[optname] = oobj + @stringArgs def func_find_program(self, node, args, kwargs): - self.validate_arguments(args, 1, [str]) + if len(args) == 0: + raise InterpreterException('No program name specified.') required = kwargs.get('required', True) if not isinstance(required, bool): raise InvalidArguments('"required" argument must be a boolean.') - exename = args[0] # Search for scripts relative to current subdir. # Do not cache found programs because find_program('foobar') # might give different results when run from different source dirs. search_dir = os.path.join(self.environment.get_source_dir(), self.subdir) - extprog = dependencies.ExternalProgram(exename, search_dir=search_dir) - progobj = ExternalProgramHolder(extprog) + for exename in args: + extprog = dependencies.ExternalProgram(exename, search_dir=search_dir) + progobj = ExternalProgramHolder(extprog) + if progobj.found(): + return progobj if required and not progobj.found(): raise InvalidArguments('Program "%s" not found.' % exename) return progobj @@ -1657,7 +1656,8 @@ requirements use the version keyword argument instead.''') if 'version' in kwargs: wanted = kwargs['version'] found = cached_dep.get_version() - if not cached_dep.found() or not mesonlib.version_compare(found, wanted): + if not cached_dep.found() or \ + not mesonlib.version_compare_many(found, wanted)[0]: # Cached dep has the wrong version. Check if an external # dependency or a fallback dependency provides it. cached_dep = None @@ -1918,12 +1918,8 @@ requirements use the version keyword argument instead.''') if ' ' in k: raise InterpreterException('Env var key must not have spaces in it.') env[k] = val - valgrind_args = kwargs.get('valgrind_args', []) - if not isinstance(valgrind_args, list): - valgrind_args = [valgrind_args] - for a in valgrind_args: - if not isinstance(a, str): - raise InterpreterException('Valgrind_arg not a string.') + if not isinstance(envlist, list): + envlist = [envlist] should_fail = kwargs.get('should_fail', False) if not isinstance(should_fail, bool): raise InterpreterException('Keyword argument should_fail must be a boolean.') @@ -1946,7 +1942,7 @@ requirements use the version keyword argument instead.''') s = '.' + s newsuite.append(self.subproject.replace(' ', '_').replace('.', '_') + s) suite = newsuite - t = Test(args[0], suite, args[1].held_object, par, cmd_args, env, should_fail, valgrind_args, timeout, workdir) + t = Test(args[0], suite, args[1].held_object, par, cmd_args, env, should_fail, timeout, workdir) if is_base_test: self.build.tests.append(t) mlog.debug('Adding test "', mlog.bold(args[0]), '".', sep='') @@ -1954,9 +1950,9 @@ requirements use the version keyword argument instead.''') self.build.benchmarks.append(t) mlog.debug('Adding benchmark "', mlog.bold(args[0]), '".', sep='') - @stringArgs def func_install_headers(self, node, args, kwargs): - h = Headers(self.subdir, args, kwargs) + source_files = self.source_strings_to_files(args) + h = Headers(source_files, kwargs) self.build.headers.append(h) return h @@ -2172,10 +2168,6 @@ requirements use the version keyword argument instead.''') @stringArgs @noKwargs def func_join_paths(self, node, args, kwargs): - if isinstance(args, str): - st = (args,) - else: - st = tuple(args) return os.path.join(*args).replace('\\', '/') def run(self): diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index b92be5f..4670685 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -146,21 +146,26 @@ def detect_vcs(source_dir): return vcs return None -def grab_leading_numbers(vstr): +def grab_leading_numbers(vstr, strict=False): result = [] for x in vstr.split('.'): try: result.append(int(x)) - except ValueError: + except ValueError as e: + if strict: + msg = 'Invalid version to compare against: {!r}; only ' \ + 'numeric digits separated by "." are allowed: ' + str(e) + raise MesonException(msg.format(vstr)) break return result numpart = re.compile('[0-9.]+') -def version_compare(vstr1, vstr2): +def version_compare(vstr1, vstr2, strict=False): match = numpart.match(vstr1.strip()) if match is None: - raise MesonException('Uncomparable version string %s.' % vstr1) + msg = 'Uncomparable version string {!r}.' + raise MesonException(msg.format(vstr1)) vstr1 = match.group(0) if vstr2.startswith('>='): cmpop = operator.ge @@ -185,10 +190,22 @@ def version_compare(vstr1, vstr2): vstr2 = vstr2[1:] else: cmpop = operator.eq - varr1 = grab_leading_numbers(vstr1) - varr2 = grab_leading_numbers(vstr2) + varr1 = grab_leading_numbers(vstr1, strict) + varr2 = grab_leading_numbers(vstr2, strict) return cmpop(varr1, varr2) +def version_compare_many(vstr1, conditions): + if not isinstance(conditions, (list, tuple)): + conditions = [conditions] + found = [] + not_found = [] + for req in conditions: + if not version_compare(vstr1, req, strict=True): + not_found.append(req) + else: + found.append(req) + return (not_found == [], not_found, found) + def default_libdir(): if is_debianlike(): try: @@ -303,7 +320,7 @@ def do_conf_file(src, dst, confdata): replace_if_different(dst, dst_tmp) def dump_conf_header(ofilename, cdata): - with open(ofilename, 'w') as ofile: + with open(ofilename, 'w', encoding='utf-8') as ofile: ofile.write('''/* * Autogenerated by the Meson build system. * Do not edit, your changes will be lost. diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 88826f8..71d42e3 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -115,7 +115,9 @@ class MesonApp(): msg = '''Trying to run Meson on a build directory that has already been configured. If you want to build it, just run your build command (e.g. ninja) inside the build directory. Meson will autodetect any changes in your setup and regenerate -itself as required.''' +itself as required. + +If you want to change option values, use the mesonconf tool instead.''' raise RuntimeError(msg) else: if handshake: @@ -192,12 +194,6 @@ def run_script_command(args): if cmdname == 'exe': import mesonbuild.scripts.meson_exe as abc cmdfunc = abc.run - elif cmdname == 'test': - import mesonbuild.scripts.meson_test as abc - cmdfunc = abc.run - elif cmdname == 'benchmark': - import mesonbuild.scripts.meson_benchmark as abc - cmdfunc = abc.run elif cmdname == 'install': import mesonbuild.scripts.meson_install as abc cmdfunc = abc.run @@ -246,7 +242,13 @@ def run(mainfile, args): return 1 if len(args) >= 2 and args[0] == '--internal': if args[1] != 'regenerate': - sys.exit(run_script_command(args[1:])) + script = args[1] + try: + sys.exit(run_script_command(args[1:])) + except MesonException as e: + mlog.log(mlog.red('\nError in {} helper script:'.format(script))) + mlog.log(e) + sys.exit(1) args = args[2:] handshake = True else: diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 492bf3f..c1af818 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -168,7 +168,10 @@ def list_tests(testdata): else: fname = t.fname to['cmd'] = fname + t.cmd_args - to['env'] = t.env + if isinstance(t.env, build.EnvironmentVariables): + to['env'] = t.env.get_env(os.environ) + else: + to['env'] = t.env to['name'] = t.name to['workdir'] = t.workdir to['timeout'] = t.timeout diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 114114d..241a531 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -31,11 +31,32 @@ from .. import interpreter # # https://github.com/ninja-build/ninja/issues/1184 # https://bugzilla.gnome.org/show_bug.cgi?id=774368 -gresource_dep_needed_version = '>9.99.99' +gresource_dep_needed_version = '>= 2.52.0' native_glib_version = None girwarning_printed = False gresource_warning_printed = False +_gir_has_extra_lib_arg = None + +def gir_has_extra_lib_arg(): + global _gir_has_extra_lib_arg + if _gir_has_extra_lib_arg is not None: + return _gir_has_extra_lib_arg + + _gir_has_extra_lib_arg = False + try: + scanner_options = subprocess.check_output(['g-ir-scanner', '--help']).decode() + _gir_has_extra_lib_arg = '--extra-library' in scanner_options + except (FileNotFound, subprocess.CalledProcessError): + pass + return _gir_has_extra_lib_arg + +def find_program(program_name, target_name): + program = dependencies.ExternalProgram(program_name) + if not program.found(): + raise MesonException('%s can\'t be generated as %s could not be found' % ( + target_name, program_name)) + return program class GnomeModule: @@ -104,12 +125,33 @@ can not be used with the current version of glib-compiled-resources, due to if 'c_name' in kwargs: cmd += ['--c-name', kwargs.pop('c_name')] + export = kwargs.pop('export', False) + if not export: + cmd += ['--internal'] + cmd += ['--generate', '--target', '@OUTPUT@'] cmd += mesonlib.stringlistify(kwargs.pop('extra_args', [])) + gresource = kwargs.pop('gresource_bundle', False) + if gresource: + output = args[0] + '.gresource' + name = args[0] + '_gresource' + else: + output = args[0] + '.c' + name = args[0] + '_c' + + if kwargs.get('install', False) and not gresource: + raise MesonException('The install kwarg only applies to gresource bundles, see install_header') + + install_header = kwargs.pop('install_header', False) + if install_header and gresource: + raise MesonException('The install_header kwarg does not apply to gresource bundles') + if install_header and not export: + raise MesonException('GResource header is installed yet export is not enabled') + kwargs['input'] = args[1] - kwargs['output'] = args[0] + '.c' + kwargs['output'] = output kwargs['depends'] = depends if not mesonlib.version_compare(glib_version, gresource_dep_needed_version): # This will eventually go out of sync if dependencies are added @@ -119,7 +161,10 @@ can not be used with the current version of glib-compiled-resources, due to depfile = kwargs['output'] + '.d' kwargs['depfile'] = depfile kwargs['command'] = copy.copy(cmd) + ['--dependency-file', '@DEPFILE@'] - target_c = build.CustomTarget(args[0] + '_c', state.subdir, kwargs) + target_c = build.CustomTarget(name, state.subdir, kwargs) + + if gresource: # Only one target for .gresource files + return [target_c] h_kwargs = { 'command': cmd, @@ -128,6 +173,10 @@ can not be used with the current version of glib-compiled-resources, due to # The header doesn't actually care about the files yet it errors if missing 'depends': depends } + if install_header: + h_kwargs['install'] = install_header + h_kwargs['install_dir'] = kwargs.get('install_dir', + state.environment.coredata.get_builtin_option('includedir')) target_h = build.CustomTarget(args[0] + '_h', state.subdir, h_kwargs) return [target_c, target_h] @@ -206,9 +255,13 @@ can not be used with the current version of glib-compiled-resources, due to return dep_files, depends, subdirs - @staticmethod - def _get_link_args(state, lib, depends=None): - link_command = ['-l%s' % lib.name] + + def _get_link_args(self, state, lib, depends=None): + if gir_has_extra_lib_arg(): + link_command = ['--extra-library=%s' % lib.name] + else: + link_command = ['-l%s' % lib.name] + print('lib: %s - %s' % (lib.name, link_command)) if isinstance(lib, build.SharedLibrary): link_command += ['-L%s' % os.path.join(state.environment.get_build_dir(), @@ -288,6 +341,8 @@ can not be used with the current version of glib-compiled-resources, due to # Hack to avoid passing some compiler options in if lib.startswith("-W"): continue + if gir_has_extra_lib_arg(): + lib = lib.replace('-l', '--extra-library=') ldflags.update([lib]) if isinstance(dep, dependencies.PkgConfigDependency): @@ -308,6 +363,8 @@ can not be used with the current version of glib-compiled-resources, due to raise MesonException('Gir takes one argument') if kwargs.get('install_dir'): raise MesonException('install_dir is not supported with generate_gir(), see "install_dir_gir" and "install_dir_typelib"') + giscanner = find_program('g-ir-scanner', 'Gir') + gicompiler = find_program('g-ir-compiler', 'Gir') girtarget = args[0] while hasattr(girtarget, 'held_object'): girtarget = girtarget.held_object @@ -329,7 +386,7 @@ can not be used with the current version of glib-compiled-resources, due to depends = [girtarget] gir_inc_dirs = [] - scan_command = ['g-ir-scanner', '@INPUT@'] + scan_command = giscanner.get_command() + ['@INPUT@'] scan_command += pkgargs scan_command += ['--no-libtool', '--namespace='+ns, '--nsversion=' + nsversion, '--warn-all', '--output', '@OUTPUT@'] @@ -484,7 +541,7 @@ can not be used with the current version of glib-compiled-resources, due to scan_target = GirTarget(girfile, state.subdir, scankwargs) typelib_output = '%s-%s.typelib' % (ns, nsversion) - typelib_cmd = ['g-ir-compiler', scan_target, '--output', '@OUTPUT@'] + typelib_cmd = gicompiler.get_command() + [scan_target, '--output', '@OUTPUT@'] typelib_cmd += self._get_include_args(state, gir_inc_dirs, prefix='--includedir=') for incdir in typelib_includes: @@ -506,7 +563,9 @@ can not be used with the current version of glib-compiled-resources, due to raise MesonException('Compile_schemas does not take positional arguments.') srcdir = os.path.join(state.build_to_src, state.subdir) outdir = state.subdir - cmd = ['glib-compile-schemas', '--targetdir', outdir, srcdir] + + cmd = find_program('glib-compile-schemas', 'gsettings-compile').get_command() + cmd += ['--targetdir', outdir, srcdir] kwargs['command'] = cmd kwargs['input'] = [] kwargs['output'] = 'gschemas.compiled' @@ -684,7 +743,8 @@ can not be used with the current version of glib-compiled-resources, due to raise MesonException('Gdbus_codegen takes two arguments, name and xml file.') namebase = args[0] xml_file = args[1] - cmd = ['gdbus-codegen'] + target_name = namebase + '-gdbus' + cmd = find_program('gdbus-codegen', target_name).get_command() if 'interface_prefix' in kwargs: cmd += ['--interface-prefix', kwargs.pop('interface_prefix')] if 'namespace' in kwargs: @@ -695,7 +755,7 @@ can not be used with the current version of glib-compiled-resources, due to 'output' : outputs, 'command' : cmd } - return build.CustomTarget(namebase + '-gdbus', state.subdir, custom_kwargs) + return build.CustomTarget(target_name, state.subdir, custom_kwargs) def mkenums(self, state, args, kwargs): if len(args) != 1: @@ -741,7 +801,7 @@ can not be used with the current version of glib-compiled-resources, due to elif arg not in known_custom_target_kwargs: raise MesonException( 'Mkenums does not take a %s keyword argument.' % (arg, )) - cmd = ['glib-mkenums'] + cmd + cmd = find_program('glib-mkenums', 'mkenums').get_command() + cmd custom_kwargs = {} for arg in known_custom_target_kwargs: if arg in kwargs: @@ -822,7 +882,7 @@ can not be used with the current version of glib-compiled-resources, due to raise MesonException( 'Sources keyword argument must be a string or array.') - cmd = ['glib-genmarshal'] + cmd = find_program('glib-genmarshal', output + '_genmarshal').get_command() known_kwargs = ['internal', 'nostdinc', 'skip_source', 'stdinc', 'valist_marshallers'] known_custom_target_kwargs = ['build_always', 'depends', @@ -949,7 +1009,8 @@ can not be used with the current version of glib-compiled-resources, due to build_dir = os.path.join(state.environment.get_build_dir(), state.subdir) source_dir = os.path.join(state.environment.get_source_dir(), state.subdir) pkg_cmd, vapi_depends, vapi_packages, vapi_includes = self._extract_vapi_packages(state, kwargs) - cmd = ['vapigen', '--quiet', '--library=' + library, '--directory=' + build_dir] + cmd = find_program('vapigen', 'Vaapi').get_command() + cmd += ['--quiet', '--library=' + library, '--directory=' + build_dir] cmd += self._vapi_args_to_command('--vapidir=', 'vapi_dirs', kwargs) cmd += self._vapi_args_to_command('--metadatadir=', 'metadata_dirs', kwargs) cmd += self._vapi_args_to_command('--girdir=', 'gir_dirs', kwargs) diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py index 1ddb2fc..13394c1 100644 --- a/mesonbuild/modules/i18n.py +++ b/mesonbuild/modules/i18n.py @@ -12,21 +12,81 @@ # See the License for the specific language governing permissions and # limitations under the License. +from os import path from .. import coredata, mesonlib, build +from ..mesonlib import MesonException import sys +PRESET_ARGS = { + 'glib': [ + '--from-code=UTF-8', + '--add-comments', + + # https://developer.gnome.org/glib/stable/glib-I18N.html + '--keyword=_', + '--keyword=N_', + '--keyword=C_:1c,2', + '--keyword=NC_:1c,2', + '--keyword=g_dcgettext:2', + '--keyword=g_dngettext:2,3', + '--keyword=g_dpgettext2:2c,3', + + '--flag=N_:1:pass-c-format', + '--flag=C_:2:pass-c-format', + '--flag=NC_:2:pass-c-format', + '--flag=g_dngettext:2:pass-c-format', + '--flag=g_strdup_printf:1:c-format', + '--flag=g_string_printf:2:c-format', + '--flag=g_string_append_printf:2:c-format', + '--flag=g_error_new:3:c-format', + '--flag=g_set_error:4:c-format', + ] +} + class I18nModule: + def merge_file(self, state, args, kwargs): + podir = kwargs.pop('po_dir', None) + if not podir: + raise MesonException('i18n: po_dir is a required kwarg') + podir = path.join(state.build_to_src, state.subdir, podir) + + file_type = kwargs.pop('type', 'xml') + VALID_TYPES = ('xml', 'desktop') + if not file_type in VALID_TYPES: + raise MesonException('i18n: "{}" is not a valid type {}'.format(file_type, VALID_TYPES)) + + kwargs['command'] = ['msgfmt', '--' + file_type, + '--template', '@INPUT@', '-d', podir, '-o', '@OUTPUT@'] + return build.CustomTarget(kwargs['output'] + '_merge', state.subdir, kwargs) + + @staticmethod + def _read_linguas(state): + linguas = path.join(state.environment.get_source_dir(), state.subdir, 'LINGUAS') + try: + with open(linguas) as f: + return [line.strip() for line in f if not line.strip().startswith('#')] + except (FileNotFoundError, PermissionError): + return [] + def gettext(self, state, args, kwargs): if len(args) != 1: raise coredata.MesonException('Gettext requires one positional argument (package name).') packagename = args[0] - languages = mesonlib.stringlistify(kwargs.get('languages', [])) + languages = mesonlib.stringlistify(kwargs.get('languages', self._read_linguas(state))) if len(languages) == 0: raise coredata.MesonException('List of languages empty.') datadirs = mesonlib.stringlistify(kwargs.get('data_dirs', [])) extra_args = mesonlib.stringlistify(kwargs.get('args', [])) + preset = kwargs.pop('preset', None) + if preset: + preset_args = PRESET_ARGS.get(preset) + if not preset_args: + raise coredata.MesonException('i18n: Preset "{}" is not one of the valid options: {}'.format( + preset, list(PRESET_ARGS.keys()))) + extra_args = set(preset_args + extra_args) + pkg_arg = '--pkgname=' + packagename lang_arg = '--langs=' + '@@'.join(languages) datadirs = '--datadirs=' + ':'.join(datadirs) if datadirs else None diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index 9f50b0e..73b29ae 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -49,6 +49,7 @@ class PkgConfigModule: # 'os.path.join' for details) ofile.write('libdir=%s\n' % os.path.join('${prefix}', coredata.get_builtin_option('libdir'))) ofile.write('includedir=%s\n' % os.path.join('${prefix}', coredata.get_builtin_option('includedir'))) + ofile.write('\n') ofile.write('Name: %s\n' % name) if len(description) > 0: ofile.write('Description: %s\n' % description) diff --git a/mesonbuild/scripts/meson_benchmark.py b/mesonbuild/scripts/meson_benchmark.py deleted file mode 100755 index 6d138b0..0000000 --- a/mesonbuild/scripts/meson_benchmark.py +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2015 The Meson development team - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import subprocess, sys, os, argparse -import pickle, statistics, json -from . import meson_test - -parser = argparse.ArgumentParser() -parser.add_argument('--wd', default=None, dest='wd', - help='directory to cd into before running') -parser.add_argument('args', nargs='+') - -def print_stats(numlen, num_tests, name, res, i, duration, stdev): - startpad = ' '*(numlen - len('%d' % (i+1))) - num = '%s%d/%d' % (startpad, i+1, num_tests) - padding1 = ' '*(38-len(name)) - padding2 = ' '*(8-len(res)) - result_str = '%s %s %s%s%s%5.5f s +- %5.5f s' % \ - (num, name, padding1, res, padding2, duration, stdev) - print(result_str) -# write_json_log(jsonlogfile, name, result) - -def print_json_log(jsonlogfile, rawruns, test_name, i): - jsonobj = {'name' : test_name} - runs = [] - for r in rawruns: - runobj = {'duration': r.duration, - 'stdout': r.stdo, - 'returncode' : r.returncode, - 'duration' : r.duration} - if r.stde: - runobj['stderr'] = r.stde - runs.append(runobj) - jsonobj['runs'] = runs - jsonlogfile.write(json.dumps(jsonobj) + '\n') - jsonlogfile.flush() - -def run_benchmarks(options, datafile): - failed_tests = 0 - logfile_base = 'meson-logs/benchmarklog' - jsonlogfilename = logfile_base+ '.json' - with open(datafile, 'rb') as f: - tests = pickle.load(f) - num_tests = len(tests) - if num_tests == 0: - print('No benchmarks defined.') - return 0 - iteration_count = 5 - wrap = [] # Benchmarks on cross builds are pointless so don't support them. - with open(jsonlogfilename, 'w') as jsonlogfile: - for i, test in enumerate(tests): - runs = [] - durations = [] - failed = False - for _ in range(iteration_count): - res = meson_test.run_single_test(wrap, test) - runs.append(res) - durations.append(res.duration) - if res.returncode != 0: - failed = True - mean = statistics.mean(durations) - stddev = statistics.stdev(durations) - if failed: - resultstr = 'FAIL' - failed_tests += 1 - else: - resultstr = 'OK' - print_stats(3, num_tests, test.name, resultstr, i, mean, stddev) - print_json_log(jsonlogfile, runs, test.name, i) - print('\nFull log written to meson-logs/benchmarklog.json.') - return failed_tests - -def run(args): - global failed_tests - options = parser.parse_args(args) - if len(options.args) != 1: - print('Benchmark runner for Meson. Do not run on your own, mmm\'kay?') - print('%s [data file]' % sys.argv[0]) - if options.wd is not None: - os.chdir(options.wd) - datafile = options.args[0] - returncode = run_benchmarks(options, datafile) - return returncode - -if __name__ == '__main__': - sys.exit(run(sys.argv[1:])) diff --git a/mesonbuild/scripts/meson_test.py b/mesonbuild/scripts/meson_test.py deleted file mode 100755 index 8034815..0000000 --- a/mesonbuild/scripts/meson_test.py +++ /dev/null @@ -1,290 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2013-2016 The Meson development team - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import mesonbuild -from .. import build -import sys, os, subprocess, time, datetime, pickle, multiprocessing, json -import concurrent.futures as conc -import argparse -import platform -import signal - -def is_windows(): - platname = platform.system().lower() - return platname == 'windows' or 'mingw' in platname - -collected_logs = [] -error_count = 0 -options = None - -parser = argparse.ArgumentParser() -parser.add_argument('--wrapper', default=None, dest='wrapper', - help='wrapper to run tests with (e.g. valgrind)') -parser.add_argument('--wd', default=None, dest='wd', - help='directory to cd into before running') -parser.add_argument('--suite', default=None, dest='suite', - help='Only run tests belonging to this suite.') -parser.add_argument('--no-stdsplit', default=True, dest='split', action='store_false', - help='Do not split stderr and stdout in test logs.') -parser.add_argument('--print-errorlogs', default=False, action='store_true', - help="Whether to print faling tests' logs.") -parser.add_argument('args', nargs='+') - - -class TestRun(): - def __init__(self, res, returncode, should_fail, duration, stdo, stde, cmd, - env): - self.res = res - self.returncode = returncode - self.duration = duration - self.stdo = stdo - self.stde = stde - self.cmd = cmd - self.env = env - self.should_fail = should_fail - - def get_log(self): - res = '--- command ---\n' - 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)) - if self.stdo: - res += '--- stdout ---\n' - res += self.stdo - if self.stde: - if res[-1:] != '\n': - res += '\n' - res += '--- stderr ---\n' - res += self.stde - if res[-1:] != '\n': - res += '\n' - res += '-------\n\n' - return res - -def decode(stream): - try: - return stream.decode('utf-8') - except UnicodeDecodeError: - return stream.decode('iso-8859-1', errors='ignore') - -def write_json_log(jsonlogfile, test_name, result): - jresult = {'name' : test_name, - 'stdout' : result.stdo, - 'result' : result.res, - 'duration' : result.duration, - 'returncode' : result.returncode, - 'command' : result.cmd, - 'env' : result.env} - if result.stde: - jresult['stderr'] = result.stde - jsonlogfile.write(json.dumps(jresult) + '\n') - -def run_with_mono(fname): - if fname.endswith('.exe') and not is_windows(): - return True - return False - -def run_single_test(wrap, test): - global options - if test.fname[0].endswith('.jar'): - cmd = ['java', '-jar'] + test.fname - elif not test.is_cross and run_with_mono(test.fname[0]): - cmd = ['mono'] + test.fname - else: - if test.is_cross: - if test.exe_runner is None: - # Can not run test on cross compiled executable - # because there is no execute wrapper. - cmd = None - else: - cmd = [test.exe_runner] + test.fname - else: - cmd = test.fname - if cmd is None: - res = 'SKIP' - duration = 0.0 - stdo = 'Not run because can not execute cross compiled binaries.' - stde = None - returncode = -1 - else: - if len(wrap) > 0 and 'valgrind' in wrap[0]: - cmd = wrap + test.valgrind_args + cmd + test.cmd_args - else: - cmd = wrap + cmd + test.cmd_args - starttime = time.time() - child_env = os.environ.copy() - if isinstance(test.env, build.EnvironmentVariables): - test.env = test.env.get_env(child_env) - - child_env.update(test.env) - if len(test.extra_paths) > 0: - child_env['PATH'] = (child_env['PATH'] + - os.pathsep.join([''] + test.extra_paths)) - if is_windows(): - setsid = None - else: - setsid = os.setsid - p = subprocess.Popen(cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE if options and options.split else subprocess.STDOUT, - env=child_env, - cwd=test.workdir, - preexec_fn=setsid) - timed_out = False - try: - (stdo, stde) = p.communicate(timeout=test.timeout) - except subprocess.TimeoutExpired: - timed_out = True - # Python does not provide multiplatform support for - # killing a process and all its children so we need - # to roll our own. - if is_windows(): - subprocess.call(['taskkill', '/F', '/T', '/PID', str(p.pid)]) - else: - os.killpg(os.getpgid(p.pid), signal.SIGKILL) - (stdo, stde) = p.communicate() - endtime = time.time() - duration = endtime - starttime - stdo = decode(stdo) - if stde: - stde = decode(stde) - if timed_out: - res = 'TIMEOUT' - elif (not test.should_fail and p.returncode == 0) or \ - (test.should_fail and p.returncode != 0): - res = 'OK' - else: - res = 'FAIL' - returncode = p.returncode - return TestRun(res, returncode, test.should_fail, duration, stdo, stde, cmd, test.env) - -def print_stats(numlen, tests, name, result, i, logfile, jsonlogfile): - global collected_logs, error_count, options - startpad = ' '*(numlen - len('%d' % (i+1))) - num = '%s%d/%d' % (startpad, i+1, len(tests)) - padding1 = ' '*(38-len(name)) - padding2 = ' '*(8-len(result.res)) - result_str = '%s %s %s%s%s%5.2f s' % \ - (num, name, padding1, result.res, padding2, result.duration) - print(result_str) - result_str += "\n\n" + result.get_log() - if (result.returncode != 0) != result.should_fail: - error_count += 1 - if options.print_errorlogs: - collected_logs.append(result_str) - logfile.write(result_str) - write_json_log(jsonlogfile, name, result) - -def drain_futures(futures): - for i in futures: - (result, numlen, tests, name, i, logfile, jsonlogfile) = i - print_stats(numlen, tests, name, result.result(), i, logfile, jsonlogfile) - -def filter_tests(suite, tests): - if suite is None: - return tests - return [x for x in tests if suite in x.suite] - -def run_tests(datafilename): - global options - logfile_base = 'meson-logs/testlog' - if options.wrapper is None: - wrap = [] - logfilename = logfile_base + '.txt' - jsonlogfilename = logfile_base+ '.json' - else: - wrap = [options.wrapper] - logfilename = logfile_base + '-' + options.wrapper.replace(' ', '_') + '.txt' - jsonlogfilename = logfile_base + '-' + options.wrapper.replace(' ', '_') + '.json' - with open(datafilename, 'rb') as f: - tests = pickle.load(f) - if len(tests) == 0: - print('No tests defined.') - return - numlen = len('%d' % len(tests)) - varname = 'MESON_TESTTHREADS' - if varname in os.environ: - try: - num_workers = int(os.environ[varname]) - except ValueError: - print('Invalid value in %s, using 1 thread.' % varname) - num_workers = 1 - else: - num_workers = multiprocessing.cpu_count() - executor = conc.ThreadPoolExecutor(max_workers=num_workers) - futures = [] - filtered_tests = filter_tests(options.suite, tests) - - with open(jsonlogfilename, 'w') as jsonlogfile, \ - open(logfilename, 'w') as logfile: - logfile.write('Log of Meson test suite run on %s.\n\n' % - datetime.datetime.now().isoformat()) - for i, test in enumerate(filtered_tests): - if test.suite[0] == '': - visible_name = test.name - else: - if options.suite is not None: - visible_name = options.suite + ' / ' + test.name - else: - visible_name = test.suite[0] + ' / ' + test.name - - if not test.is_parallel: - drain_futures(futures) - futures = [] - res = run_single_test(wrap, test) - print_stats(numlen, filtered_tests, visible_name, res, i, - logfile, jsonlogfile) - else: - f = executor.submit(run_single_test, wrap, test) - futures.append((f, numlen, filtered_tests, visible_name, i, - logfile, jsonlogfile)) - drain_futures(futures) - return logfilename - -def run(args): - global collected_logs, error_count, options - collected_logs = [] # To avoid state leaks when invoked multiple times (running tests in-process) - error_count = 0 - options = parser.parse_args(args) - if len(options.args) != 1: - print('Test runner for Meson. Do not run on your own, mmm\'kay?') - print('%s [data file]' % sys.argv[0]) - if options.wd is not None: - os.chdir(options.wd) - datafile = options.args[0] - logfilename = run_tests(datafile) - if len(collected_logs) > 0: - if len(collected_logs) > 10: - print('\nThe output from 10 first failed tests:\n') - else: - print('\nThe output from the failed tests:\n') - for log in collected_logs[:10]: - lines = log.splitlines() - if len(lines) > 100: - print(lines[0]) - print('--- Listing only the last 100 lines from a long log. ---') - lines = lines[-99:] - for line in lines: - print(line) - if logfilename: - print('Full log written to %s.' % logfilename) - return error_count - -if __name__ == '__main__': - sys.exit(run(sys.argv[1:])) diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index e05c641..37d6df7 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -93,9 +93,19 @@ class Resolver: def resolve(self, packagename): fname = os.path.join(self.subdir_root, packagename + '.wrap') dirname = os.path.join(self.subdir_root, packagename) - if os.path.isdir(dirname): - # The directory is there? Great, use it. - return packagename + try: + if os.listdir(dirname): + # The directory is there and not empty? Great, use it. + return packagename + else: + mlog.warning('Subproject directory %s is empty, possibly because of an unfinished' + 'checkout, removing to reclone' % dirname) + os.rmdir(checkoutdir) + except NotADirectoryError: + raise RuntimeError('%s is not a directory, can not use as subproject.' % dirname) + except FileNotFoundError: + pass + if not os.path.isfile(fname): # No wrap file with this name? Give up. return None @@ -118,6 +128,15 @@ class Resolver: revno = p.get('revision') is_there = os.path.isdir(checkoutdir) if is_there: + try: + subprocess.check_call(['git', 'rev-parse']) + is_there = True + except subprocess.CalledProcessError: + raise RuntimeError('%s is not empty but is not a valid ' + 'git repository, we can not work with it' + ' as a subproject directory.' % ( + checkoutdir)) + if revno.lower() == 'head': # Failure to do pull is not a fatal error, # because otherwise you can't develop without @@ -134,6 +153,11 @@ class Resolver: if revno.lower() != 'head': subprocess.check_call(['git', 'checkout', revno], cwd=checkoutdir) + push_url = p.values.get('push-url') + if push_url: + subprocess.check_call(['git', 'remote', 'set-url', + '--push', 'origin', push_url], + cwd=checkoutdir) def get_hg(self, p): checkoutdir = os.path.join(self.subdir_root, p.get('directory')) revno = p.get('revision') diff --git a/mesontest.py b/mesontest.py new file mode 100755 index 0000000..2d834b1 --- /dev/null +++ b/mesontest.py @@ -0,0 +1,468 @@ +#!/usr/bin/env python3 + +# Copyright 2016 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# A tool to run tests in many different ways. + +import subprocess, sys, os, argparse +import pickle +from mesonbuild import build +from mesonbuild import environment + +import time, datetime, multiprocessing, json +import concurrent.futures as conc +import platform +import signal + +# GNU autotools interprets a return code of 77 from tests it executes to +# mean that the test should be skipped. +GNU_SKIP_RETURNCODE = 77 + +def is_windows(): + platname = platform.system().lower() + return platname == 'windows' or 'mingw' in platname + +def determine_worker_count(): + varname = 'MESON_TESTTHREADS' + if varname in os.environ: + try: + num_workers = int(os.environ[varname]) + except ValueError: + print('Invalid value in %s, using 1 thread.' % varname) + num_workers = 1 + else: + try: + # Fails in some weird environments such as Debian + # reproducible build. + num_workers = multiprocessing.cpu_count() + except Exception: + num_workers = 1 + return num_workers + +parser = argparse.ArgumentParser() +parser.add_argument('--repeat', default=1, dest='repeat', type=int, + help='Number of times to run the tests.') +parser.add_argument('--no-rebuild', default=False, action='store_true', + help='Do not rebuild before running tests.') +parser.add_argument('--gdb', default=False, dest='gdb', action='store_true', + help='Run test under gdb.') +parser.add_argument('--list', default=False, dest='list', action='store_true', + help='List available tests.') +parser.add_argument('--wrapper', default=None, dest='wrapper', + help='wrapper to run tests with (e.g. Valgrind)') +parser.add_argument('-C', default='.', dest='wd', + help='directory to cd into before running') +parser.add_argument('--suite', default=None, dest='suite', + help='Only run tests belonging to the given suite.') +parser.add_argument('--no-stdsplit', default=True, dest='split', action='store_false', + help='Do not split stderr and stdout in test logs.') +parser.add_argument('--print-errorlogs', default=False, action='store_true', + help="Whether to print faling tests' logs.") +parser.add_argument('--benchmark', default=False, action='store_true', + help="Run benchmarks instead of tests.") +parser.add_argument('--logbase', default='testlog', + help="Base name for log file.") +parser.add_argument('--num-processes', default=determine_worker_count(), type=int, + help='How many parallel processes to use.') +parser.add_argument('-v', '--verbose', default=False, action='store_true', + help='Do not redirect stdout and stderr') +parser.add_argument('-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('args', nargs='*') + +class TestRun(): + def __init__(self, res, returncode, should_fail, duration, stdo, stde, cmd, + env): + self.res = res + self.returncode = returncode + self.duration = duration + self.stdo = stdo + self.stde = stde + self.cmd = cmd + self.env = env + self.should_fail = should_fail + + def get_log(self): + res = '--- command ---\n' + 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)) + 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): + if stream is None: + return '' + try: + return stream.decode('utf-8') + except UnicodeDecodeError: + return stream.decode('iso-8859-1', errors='ignore') + +def write_json_log(jsonlogfile, test_name, result): + jresult = {'name' : test_name, + 'stdout' : result.stdo, + 'result' : result.res, + 'duration' : result.duration, + 'returncode' : result.returncode, + 'command' : result.cmd, + 'env' : result.env} + if result.stde: + jresult['stderr'] = result.stde + jsonlogfile.write(json.dumps(jresult) + '\n') + +def run_with_mono(fname): + if fname.endswith('.exe') and not is_windows(): + return True + return False + +class TestHarness: + def __init__(self, options): + self.options = options + self.collected_logs = [] + self.error_count = 0 + self.is_run = False + self.cant_rebuild = False + if self.options.benchmark: + self.datafile = os.path.join(options.wd, 'meson-private/meson_benchmark_setup.dat') + else: + self.datafile = os.path.join(options.wd, 'meson-private/meson_test_setup.dat') + + def rebuild_all(self): + if not os.path.isfile(os.path.join(self.options.wd, 'build.ninja')): + print("Only ninja backend is supported to rebuilt tests before running them.") + self.cant_rebuild = True + return True + + ninja = environment.detect_ninja() + if not ninja: + print("Can't find ninja, can't rebuild test.") + self.cant_rebuild = True + return False + + p = subprocess.Popen([ninja, '-C', self.options.wd]) + (stdo, stde) = p.communicate() + + if p.returncode != 0: + print("Could not rebuild") + return False + + 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]): + cmd = ['mono'] + test.fname + else: + if test.is_cross: + if test.exe_runner is None: + # Can not run test on cross compiled executable + # because there is no execute wrapper. + cmd = None + else: + cmd = [test.exe_runner] + test.fname + else: + cmd = test.fname + + if cmd is None: + res = 'SKIP' + duration = 0.0 + stdo = 'Not run because can not execute cross compiled binaries.' + stde = None + returncode = -1 + else: + cmd = wrap + cmd + test.cmd_args + starttime = time.time() + child_env = os.environ.copy() + if isinstance(test.env, build.EnvironmentVariables): + test.env = test.env.get_env(child_env) + + child_env.update(test.env) + if len(test.extra_paths) > 0: + child_env['PATH'] = child_env['PATH'] + ';'.join([''] + test.extra_paths) + + setsid = None + stdout = None + stderr = None + if not self.options.verbose: + stdout = subprocess.PIPE + stderr = subprocess.PIPE if self.options and self.options.split else subprocess.STDOUT + + if not is_windows(): + setsid = os.setsid + + p = subprocess.Popen(cmd, + stdout=stdout, + stderr=stderr, + env=child_env, + cwd=test.workdir, + preexec_fn=setsid) + timed_out = False + timeout = test.timeout * self.options.timeout_multiplier + try: + (stdo, stde) = p.communicate(timeout=timeout) + except subprocess.TimeoutExpired: + if self.options.verbose: + print("%s time out (After %d seconds)" % (test.name, timeout)) + timed_out = True + # Python does not provide multiplatform support for + # killing a process and all its children so we need + # to roll our own. + if is_windows(): + subprocess.call(['taskkill', '/F', '/T', '/PID', str(p.pid)]) + else: + os.killpg(os.getpgid(p.pid), signal.SIGKILL) + (stdo, stde) = p.communicate() + endtime = time.time() + duration = endtime - starttime + stdo = decode(stdo) + if stde: + stde = decode(stde) + if timed_out: + res = 'TIMEOUT' + failling = True + 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): + res = 'OK' + else: + res = 'FAIL' + failling = True + 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): + startpad = ' '*(numlen - len('%d' % (i+1))) + num = '%s%d/%d' % (startpad, i+1, len(tests)) + padding1 = ' '*(38-len(name)) + padding2 = ' '*(8-len(result.res)) + result_str = '%s %s %s%s%s%5.2f s' % \ + (num, name, padding1, result.res, padding2, result.duration) + print(result_str) + 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 len(self.collected_logs) > 0: + if len(self.collected_logs) > 10: + print('\nThe output from 10 first failed tests:\n') + else: + 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]) + print('--- Listing only the last 100 lines from a long log. ---') + lines = lines[-99:] + for line in lines: + print(line) + print('Full log written to %s.' % logfilename) + return self.error_count + + def get_tests(self): + with open(self.datafile, 'rb') as f: + tests = pickle.load(f) + 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) + if self.options.wrapper is None: + wrap = [] + logfilename = logfile_base + '.txt' + jsonlogfilename = logfile_base+ '.json' + else: + wrap = self.options.wrapper.split() + namebase = wrap[0] + 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 + 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()) + + for i in range(self.options.repeat): + for i, test in enumerate(filtered_tests): + if test.suite[0] == '': + visible_name = test.name + else: + if self.options.suite is not None: + visible_name = self.options.suite + ' / ' + test.name + else: + visible_name = test.suite[0] + ' / ' + test.name + + if not test.is_parallel: + self.drain_futures(futures) + 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) + else: + 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)) + self.drain_futures(futures, logfile, jsonlogfile) + 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: + result.cancel() + elif not self.options.verbose: + self.print_stats(numlen, tests, name, result.result(), i, logfile, jsonlogfile) + else: + 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 + + 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: + wrap.append('-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 + +def filter_tests(suite, tests): + if suite is None: + return tests + return [x for x in tests if suite in x.suite] + + +def run(args): + options = parser.parse_args(args) + if options.benchmark: + options.num_processes = 1 + + if options.gdb: + options.verbose = True + + options.wd = os.path.abspath(options.wd) + + th = TestHarness(options) + if options.list: + return th.run_special() + if not options.no_rebuild: + if not th.rebuild_all(): + return -1 + elif len(options.args) == 0: + return th.doit() + return th.run_special() + +if __name__ == '__main__': + sys.exit(run(sys.argv[1:])) diff --git a/run_project_tests.py b/run_project_tests.py index 15d656b..da70bcb 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -19,12 +19,12 @@ import os, subprocess, shutil, sys, signal from io import StringIO from ast import literal_eval import sys, tempfile +import mesontest from mesonbuild import environment from mesonbuild import mesonlib from mesonbuild import mlog from mesonbuild import mesonmain from mesonbuild.mesonlib import stringlistify -from mesonbuild.scripts import meson_test, meson_benchmark import argparse import xml.etree.ElementTree as ET import time @@ -211,8 +211,8 @@ def run_test_inprocess(testdir): old_cwd = os.getcwd() os.chdir(testdir) try: - returncode_test = meson_test.run(['meson-private/meson_test_setup.dat']) - returncode_benchmark = meson_benchmark.run(['meson-private/meson_benchmark_setup.dat']) + returncode_test = mesontest.run(['--no-rebuild']) + returncode_benchmark = mesontest.run(['--no-rebuild', '--benchmark', '--logbase', 'benchmarklog']) finally: sys.stdout = old_stdout sys.stderr = old_stderr @@ -380,7 +380,14 @@ def run_tests(extra_args): build_time = 0 test_time = 0 - executor = conc.ProcessPoolExecutor(max_workers=multiprocessing.cpu_count()) + try: + # This fails in some CI environments for unknown reasons. + num_workers = multiprocessing.cpu_count() + except Exception as e: + print('Could not determine number of CPUs due to the following reason:' + str(e)) + print('Defaulting to using only one process') + num_workers = 1 + executor = conc.ProcessPoolExecutor(max_workers=num_workers) for name, test_cases, skipped in all_tests: current_suite = ET.SubElement(junit_root, 'testsuite', {'name' : name, 'tests' : str(len(test_cases))}) diff --git a/run_unittests.py b/run_unittests.py index f495c97..36d899a 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -24,12 +24,14 @@ from mesonbuild.dependencies import PkgConfigDependency, Qt5Dependency def get_soname(fname): # HACK, fix to not use shell. - raw_out = subprocess.check_output(['readelf', '-a', fname]) - pattern = re.compile(b'soname: \[(.*?)\]') - for line in raw_out.split(b'\n'): + raw_out = subprocess.check_output(['readelf', '-a', fname], + universal_newlines=True) + pattern = re.compile('soname: \[(.*?)\]') + for line in raw_out.split('\n'): m = pattern.search(line) if m is not None: return m.group(1) + raise RuntimeError('Could not determine soname:\n\n' + raw_out) class FakeEnvironment(object): def __init__(self): @@ -53,6 +55,7 @@ class LinuxlikeTests(unittest.TestCase): def setUp(self): super().setUp() src_root = os.path.dirname(__file__) + src_root = os.path.join(os.getcwd(), src_root) self.builddir = tempfile.mkdtemp() self.meson_command = [sys.executable, os.path.join(src_root, 'meson.py')] self.mconf_command = [sys.executable, os.path.join(src_root, 'mesonconf.py')] @@ -61,6 +64,7 @@ class LinuxlikeTests(unittest.TestCase): self.common_test_dir = os.path.join(src_root, 'test cases/common') self.vala_test_dir = os.path.join(src_root, 'test cases/vala') self.framework_test_dir = os.path.join(src_root, 'test cases/frameworks') + self.unit_test_dir = os.path.join(src_root, 'test cases/unit') self.output = b'' self.orig_env = os.environ.copy() @@ -92,27 +96,55 @@ class LinuxlikeTests(unittest.TestCase): with open(os.path.join(self.builddir, 'meson-logs', 'meson-log.txt')) as f: return f.readlines() + def get_meson_log_compiler_checks(self): + ''' + Fetch a list command-lines run by meson for compiler checks. + Each command-line is returned as a list of arguments. + ''' + log = self.get_meson_log() + prefix = 'Command line:' + cmds = [l[len(prefix):].split() for l in log if l.startswith(prefix)] + return cmds + def introspect(self, arg): - out = subprocess.check_output(self.mintro_command + [arg, self.builddir]) - return json.loads(out.decode('utf-8')) + out = subprocess.check_output(self.mintro_command + [arg, self.builddir], + universal_newlines=True) + return json.loads(out) def test_basic_soname(self): + ''' + Test that the soname is set correctly for shared libraries. This can't + be an ordinary test case because we need to run `readelf` and actually + check the soname. + https://github.com/mesonbuild/meson/issues/785 + ''' testdir = os.path.join(self.common_test_dir, '4 shared') self.init(testdir) self.build() lib1 = os.path.join(self.builddir, 'libmylib.so') soname = get_soname(lib1) - self.assertEqual(soname, b'libmylib.so') + self.assertEqual(soname, 'libmylib.so') def test_custom_soname(self): + ''' + Test that the soname is set correctly for shared libraries when + a custom prefix and/or suffix is used. This can't be an ordinary test + case because we need to run `readelf` and actually check the soname. + https://github.com/mesonbuild/meson/issues/785 + ''' testdir = os.path.join(self.common_test_dir, '27 library versions') self.init(testdir) self.build() lib1 = os.path.join(self.builddir, 'prefixsomelib.suffix') soname = get_soname(lib1) - self.assertEqual(soname, b'prefixsomelib.suffix') + self.assertEqual(soname, 'prefixsomelib.suffix') def test_pic(self): + ''' + Test that -fPIC is correctly added to static libraries when b_staticpic + is true and not when it is false. This can't be an ordinary test case + because we need to inspect the compiler database. + ''' testdir = os.path.join(self.common_test_dir, '3 static') self.init(testdir) compdb = self.get_compdb() @@ -128,6 +160,12 @@ class LinuxlikeTests(unittest.TestCase): self.assertTrue('-fPIC' not in compdb[0]['command']) def test_pkgconfig_gen(self): + ''' + Test that generated pkg-config files can be found and have the correct + version and link args. This can't be an ordinary test case because we + need to run pkg-config outside of a Meson build file. + https://github.com/mesonbuild/meson/issues/889 + ''' testdir = os.path.join(self.common_test_dir, '51 pkgconfig-gen') self.init(testdir) env = FakeEnvironment() @@ -139,6 +177,12 @@ class LinuxlikeTests(unittest.TestCase): self.assertTrue('-lfoo' in simple_dep.get_link_args()) def test_vala_c_warnings(self): + ''' + Test that no warnings are emitted for C code generated by Vala. This + can't be an ordinary test case because we need to inspect the compiler + database. + https://github.com/mesonbuild/meson/issues/864 + ''' testdir = os.path.join(self.vala_test_dir, '5 target glib') self.init(testdir) compdb = self.get_compdb() @@ -166,6 +210,12 @@ class LinuxlikeTests(unittest.TestCase): self.assertTrue('-Werror' in c_command) def test_static_compile_order(self): + ''' + Test that the order of files in a compiler command-line while compiling + and linking statically is deterministic. This can't be an ordinary test + case because we need to inspect the compiler database. + https://github.com/mesonbuild/meson/pull/951 + ''' testdir = os.path.join(self.common_test_dir, '5 linkstatic') self.init(testdir) compdb = self.get_compdb() @@ -177,6 +227,10 @@ class LinuxlikeTests(unittest.TestCase): # FIXME: We don't have access to the linker command def test_install_introspection(self): + ''' + Tests that the Meson introspection API exposes install filenames correctly + https://github.com/mesonbuild/meson/issues/829 + ''' testdir = os.path.join(self.common_test_dir, '8 install') self.init(testdir) intro = self.introspect('--targets') @@ -186,14 +240,29 @@ class LinuxlikeTests(unittest.TestCase): self.assertEqual(intro[1]['install_filename'], '/usr/local/bin/prog') def test_run_target_files_path(self): + ''' + Test that run_targets are run from the correct directory + https://github.com/mesonbuild/meson/issues/957 + ''' testdir = os.path.join(self.common_test_dir, '58 run target') self.init(testdir) self.run_target('check_exists') def test_qt5dependency_qmake_detection(self): - # Can't be sure that `qmake` is Qt5, so just try qmake-qt5. + ''' + Test that qt5 detection with qmake works. This can't be an ordinary + test case because it involves setting the environment. + ''' + # Verify that qmake is for Qt5 if not shutil.which('qmake-qt5'): - raise unittest.SkipTest('qt5 not found') + if not shutil.which('qmake'): + raise unittest.SkipTest('QMake not found') + # For some inexplicable reason qmake --version gives different + # results when run from the command line vs invoked by Python. + # Check for both cases in case this behaviour changes in the future. + output = subprocess.getoutput(['qmake', '--version']) + if 'Qt version 5' not in output and 'qt5' not in output: + raise unittest.SkipTest('Qmake found, but it is not for Qt 5.') # Disable pkg-config codepath and force searching with qmake/qmake-qt5 os.environ['PKG_CONFIG_LIBDIR'] = self.builddir os.environ['PKG_CONFIG_PATH'] = self.builddir @@ -201,8 +270,76 @@ class LinuxlikeTests(unittest.TestCase): self.init(testdir) # Confirm that the dependency was found with qmake msg = 'Qt5 native `qmake-qt5` dependency (modules: Core) found: YES\n' + msg2 = 'Qt5 native `qmake` dependency (modules: Core) found: YES\n' mesonlog = self.get_meson_log() - self.assertTrue(msg in mesonlog) + self.assertTrue(msg in mesonlog or msg2 in mesonlog) + + def get_soname(self, fname): + output = subprocess.check_output(['readelf', '-a', fname], + universal_newlines=True) + for line in output.split('\n'): + if 'SONAME' in line: + return line.split('[')[1].split(']')[0] + raise RuntimeError('Readelf gave no SONAME.') + + def test_soname(self): + testdir = os.path.join(self.unit_test_dir, '1 soname') + self.init(testdir) + self.build() + + # File without aliases set. + nover = os.path.join(self.builddir, 'libnover.so') + self.assertTrue(os.path.exists(nover)) + self.assertFalse(os.path.islink(nover)) + self.assertEqual(self.get_soname(nover), 'libnover.so') + self.assertEqual(len(glob(nover[:-3] + '*')), 1) + + # File with version set + verset = os.path.join(self.builddir, 'libverset.so') + self.assertTrue(os.path.exists(verset + '.4.5.6')) + self.assertEqual(os.readlink(verset), 'libverset.so.4') + self.assertEqual(self.get_soname(verset), 'libverset.so.4') + self.assertEqual(len(glob(verset[:-3] + '*')), 3) + + # File with soversion set + soverset = os.path.join(self.builddir, 'libsoverset.so') + self.assertTrue(os.path.exists(soverset + '.1.2.3')) + self.assertEqual(os.readlink(soverset), 'libsoverset.so.1.2.3') + self.assertEqual(self.get_soname(soverset), 'libsoverset.so.1.2.3') + self.assertEqual(len(glob(soverset[:-3] + '*')), 2) + + # File with version and soversion set to same values + settosame = os.path.join(self.builddir, 'libsettosame.so') + self.assertTrue(os.path.exists(settosame + '.7.8.9')) + self.assertEqual(os.readlink(settosame), 'libsettosame.so.7.8.9') + self.assertEqual(self.get_soname(settosame), 'libsettosame.so.7.8.9') + self.assertEqual(len(glob(settosame[:-3] + '*')), 2) + + # File with version and soversion set to different values + bothset = os.path.join(self.builddir, 'libbothset.so') + self.assertTrue(os.path.exists(bothset + '.1.2.3')) + self.assertEqual(os.readlink(bothset), 'libbothset.so.1.2.3') + self.assertEqual(os.readlink(bothset + '.1.2.3'), 'libbothset.so.4.5.6') + self.assertEqual(self.get_soname(bothset), 'libbothset.so.1.2.3') + self.assertEqual(len(glob(bothset[:-3] + '*')), 3) + + def test_compiler_check_flags_order(self): + ''' + Test that compiler check flags override all other flags. This can't be + an ordinary test case because it needs the environment to be set. + ''' + Oflag = '-O3' + os.environ['CFLAGS'] = os.environ['CXXFLAGS'] = Oflag + testdir = os.path.join(self.common_test_dir, '43 has function') + self.init(testdir) + cmds = self.get_meson_log_compiler_checks() + for cmd in cmds: + # Verify that -I flags from the `args` kwarg are first + # This is set in the '43 has function' test case + self.assertEqual(cmd[2], '-I/tmp') + # Verify that -O3 set via the environment is overriden by -O0 + Oargs = [arg for arg in cmd if arg.startswith('-O')] + self.assertEqual(Oargs, [Oflag, '-O0']) class RewriterTests(unittest.TestCase): @@ -70,6 +70,7 @@ setup(name='meson', 'mesonbuild.wrap'], scripts=['meson.py', 'mesonconf.py', + 'mesontest.py', 'mesonintrospect.py', 'wraptool.py'], cmdclass={'install_scripts': install_scripts}, diff --git a/syntax-highlighting/vim/README b/syntax-highlighting/vim/README new file mode 100644 index 0000000..1afa243 --- /dev/null +++ b/syntax-highlighting/vim/README @@ -0,0 +1,3 @@ +ftdetect sets the filetype +syntax does Meson syntax highlighting +plugin does Meson indentation diff --git a/syntax-highlighting/vim/ftdetect/meson.vim b/syntax-highlighting/vim/ftdetect/meson.vim new file mode 100644 index 0000000..84db70c --- /dev/null +++ b/syntax-highlighting/vim/ftdetect/meson.vim @@ -0,0 +1,2 @@ +au BufNewFile,BufRead meson.build set filetype=meson +au BufNewFile,BufRead meson_options.txt set filetype=meson diff --git a/syntax-highlighting/vim/plugin/meson.vim b/syntax-highlighting/vim/plugin/meson.vim new file mode 100644 index 0000000..b219bdc --- /dev/null +++ b/syntax-highlighting/vim/plugin/meson.vim @@ -0,0 +1,183 @@ +" Vim indent file +" Language: Meson +" Maintainer: Nirbheek Chauhan <nirbheek.chauhan@gmail.com> +" Original Authors: David Bustos <bustos@caltech.edu> +" Bram Moolenaar <Bram@vim.org> +" Last Change: 2015 Feb 23 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" Some preliminary settings +setlocal nolisp " Make sure lisp indenting doesn't supersede us +setlocal autoindent " indentexpr isn't much help otherwise + +setlocal indentexpr=GetMesonIndent(v:lnum) +setlocal indentkeys+==elif,=else,=endforeach,=endif,0) + +" Only define the function once. +if exists("*GetMesonIndent") + finish +endif +let s:keepcpo= &cpo +set cpo&vim + +" Come here when loading the script the first time. + +let s:maxoff = 50 " maximum number of lines to look backwards for () + +" Force sw=2 sts=2 because that's required by convention +set shiftwidth=2 +set softtabstop=2 + +function GetMesonIndent(lnum) + echom getline(line(".")) + + " If this line is explicitly joined: If the previous line was also joined, + " line it up with that one, otherwise add two 'shiftwidth' + if getline(a:lnum - 1) =~ '\\$' + if a:lnum > 1 && getline(a:lnum - 2) =~ '\\$' + return indent(a:lnum - 1) + endif + return indent(a:lnum - 1) + (exists("g:mesonindent_continue") ? eval(g:mesonindent_continue) : (shiftwidth() * 2)) + endif + + " If the start of the line is in a string don't change the indent. + if has('syntax_items') + \ && synIDattr(synID(a:lnum, 1, 1), "name") =~ "String$" + return -1 + endif + + " Search backwards for the previous non-empty line. + let plnum = prevnonblank(v:lnum - 1) + + if plnum == 0 + " This is the first non-empty line, use zero indent. + return 0 + endif + + " If the previous line is inside parenthesis, use the indent of the starting + " line. + " Trick: use the non-existing "dummy" variable to break out of the loop when + " going too far back. + call cursor(plnum, 1) + let parlnum = searchpair('(\|{\|\[', '', ')\|}\|\]', 'nbW', + \ "line('.') < " . (plnum - s:maxoff) . " ? dummy :" + \ . " synIDattr(synID(line('.'), col('.'), 1), 'name')" + \ . " =~ '\\(Comment\\|Todo\\|String\\)$'") + if parlnum > 0 + let plindent = indent(parlnum) + let plnumstart = parlnum + else + let plindent = indent(plnum) + let plnumstart = plnum + endif + + + " When inside parenthesis: If at the first line below the parenthesis add + " two 'shiftwidth', otherwise same as previous line. + " i = (a + " + b + " + c) + call cursor(a:lnum, 1) + let p = searchpair('(\|{\|\[', '', ')\|}\|\]', 'bW', + \ "line('.') < " . (a:lnum - s:maxoff) . " ? dummy :" + \ . " synIDattr(synID(line('.'), col('.'), 1), 'name')" + \ . " =~ '\\(Comment\\|Todo\\|String\\)$'") + if p > 0 + if p == plnum + " When the start is inside parenthesis, only indent one 'shiftwidth'. + let pp = searchpair('(\|{\|\[', '', ')\|}\|\]', 'bW', + \ "line('.') < " . (a:lnum - s:maxoff) . " ? dummy :" + \ . " synIDattr(synID(line('.'), col('.'), 1), 'name')" + \ . " =~ '\\(Comment\\|Todo\\|String\\)$'") + if pp > 0 + return indent(plnum) + (exists("g:pyindent_nested_paren") ? eval(g:pyindent_nested_paren) : shiftwidth()) + endif + return indent(plnum) + (exists("g:pyindent_open_paren") ? eval(g:pyindent_open_paren) : (shiftwidth() * 2)) + endif + if plnumstart == p + return indent(plnum) + endif + return plindent + endif + + + " Get the line and remove a trailing comment. + " Use syntax highlighting attributes when possible. + let pline = getline(plnum) + let pline_len = strlen(pline) + if has('syntax_items') + " If the last character in the line is a comment, do a binary search for + " the start of the comment. synID() is slow, a linear search would take + " too long on a long line. + if synIDattr(synID(plnum, pline_len, 1), "name") =~ "\\(Comment\\|Todo\\)$" + let min = 1 + let max = pline_len + while min < max + let col = (min + max) / 2 + if synIDattr(synID(plnum, col, 1), "name") =~ "\\(Comment\\|Todo\\)$" + let max = col + else + let min = col + 1 + endif + endwhile + let pline = strpart(pline, 0, min - 1) + endif + else + let col = 0 + while col < pline_len + if pline[col] == '#' + let pline = strpart(pline, 0, col) + break + endif + let col = col + 1 + endwhile + endif + + " If the previous line ended the conditional/loop + if getline(plnum) =~ '^\s*\(endif\|endforeach\)\>\s*' + " Maintain indent + return -1 + endif + + " If the previous line ended with a builtin, indent this line + if pline =~ '^\s*\(foreach\|if\|else\|elif\)\>\s*' + return plindent + shiftwidth() + endif + + " If the current line begins with a header keyword, deindent + if getline(a:lnum) =~ '^\s*\(else\|elif\|endif\|endforeach\)' + + " Unless the previous line was a one-liner + if getline(plnumstart) =~ '^\s*\(foreach\|if\)\>\s*' + return plindent + endif + + " Or the user has already dedented + if indent(a:lnum) <= plindent - shiftwidth() + return -1 + endif + + return plindent - shiftwidth() + endif + + " When after a () construct we probably want to go back to the start line. + " a = (b + " + c) + " here + if parlnum > 0 + return plindent + endif + + return -1 + +endfunction + +let &cpo = s:keepcpo +unlet s:keepcpo + +" vim:sw=2 diff --git a/syntax-highlighting/vim/syntax/meson.vim b/syntax-highlighting/vim/syntax/meson.vim new file mode 100644 index 0000000..c2653ab --- /dev/null +++ b/syntax-highlighting/vim/syntax/meson.vim @@ -0,0 +1,117 @@ +" Vim syntax file +" Language: Meson +" Maintainer: Nirbheek Chauhan <nirbheek.chauhan@gmail.com> +" Last Change: 2015 Feb 23 +" Credits: Zvezdan Petkovic <zpetkovic@acm.org> +" Neil Schemenauer <nas@meson.ca> +" Dmitry Vasiliev +" +" This version is copied and edited from python.vim +" It's very basic, and doesn't do many things I'd like it to +" For instance, it should show errors for syntax that is valid in +" Python but not in Meson. +" +" Optional highlighting can be controlled using these variables. +" +" let meson_space_error_highlight = 1 +" + +" For version 5.x: Clear all syntax items. +" For version 6.x: Quit when a syntax file was already loaded. +if version < 600 + syntax clear +elseif exists("b:current_syntax") + finish +endif + +" We need nocompatible mode in order to continue lines with backslashes. +" Original setting will be restored. +let s:cpo_save = &cpo +set cpo&vim + +" https://github.com/mesonbuild/meson/wiki/Syntax +syn keyword mesonConditional elif else if endif +syn keyword mesonRepeat foreach endforeach +syn keyword mesonOperator and not or + +syn match mesonComment "#.*$" contains=mesonTodo,@Spell +syn keyword mesonTodo FIXME NOTE NOTES TODO XXX contained + +" Strings can either be single quoted or triple counted across multiple lines, +" but always with a ' +syn region mesonString + \ start="\z('\)" end="\z1" skip="\\\\\|\\\z1" + \ contains=mesonEscape,@Spell +syn region mesonString + \ start="\z('''\)" end="\z1" keepend + \ contains=mesonEscape,mesonSpaceError,@Spell + +syn match mesonEscape "\\[abfnrtv'\\]" contained +syn match mesonEscape "\\\o\{1,3}" contained +syn match mesonEscape "\\x\x\{2}" contained +syn match mesonEscape "\%(\\u\x\{4}\|\\U\x\{8}\)" contained +" Meson allows case-insensitive Unicode IDs: http://www.unicode.org/charts/ +syn match mesonEscape "\\N{\a\+\%(\s\a\+\)*}" contained +syn match mesonEscape "\\$" + +" Meson only supports integer numbers +" https://github.com/mesonbuild/meson/wiki/Syntax#numbers +syn match mesonNumber "\<\d\+\>" + +" booleans +syn keyword mesonConstant false true + +" Built-in functions +syn keyword mesonBuiltin add_global_arguments add_languages benchmark +syn keyword mesonBuiltin build_target configuration_data configure_file +syn keyword mesonBuiltin custom_target declare_dependency dependency +syn keyword mesonBuiltin error executable find_program find_library +syn keyword mesonBuiltin files generator get_option get_variable +syn keyword mesonBuiltin gettext import include_directories install_data +syn keyword mesonBuiltin install_headers install_man install_subdir +syn keyword mesonBuiltin is_subproject is_variable jar library message +syn keyword mesonBuiltin project run_command run_target set_variable +syn keyword mesonBuiltin shared_library static_library subdir subproject +syn keyword mesonBuiltin test vcs_tag + +if exists("meson_space_error_highlight") + " trailing whitespace + syn match mesonSpaceError display excludenl "\s\+$" + " mixed tabs and spaces + syn match mesonSpaceError display " \+\t" + syn match mesonSpaceError display "\t\+ " +endif + +if version >= 508 || !exists("did_meson_syn_inits") + if version <= 508 + let did_meson_syn_inits = 1 + command -nargs=+ HiLink hi link <args> + else + command -nargs=+ HiLink hi def link <args> + endif + + " The default highlight links. Can be overridden later. + HiLink mesonStatement Statement + HiLink mesonConditional Conditional + HiLink mesonRepeat Repeat + HiLink mesonOperator Operator + HiLink mesonComment Comment + HiLink mesonTodo Todo + HiLink mesonString String + HiLink mesonEscape Special + HiLink mesonNumber Number + HiLink mesonBuiltin Function + HiLink mesonConstant Number + if exists("meson_space_error_highlight") + HiLink mesonSpaceError Error + endif + + delcommand HiLink +endif + +let b:current_syntax = "meson" + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim:set sw=2 sts=2 ts=8 noet: diff --git a/test cases/common/124 test skip/meson.build b/test cases/common/124 test skip/meson.build new file mode 100644 index 0000000..568527f --- /dev/null +++ b/test cases/common/124 test skip/meson.build @@ -0,0 +1,4 @@ +project('test skip', 'c') + +exe_test_skip = executable('test_skip', 'test_skip.c') +test('test_skip', exe_test_skip) diff --git a/test cases/common/124 test skip/test_skip.c b/test cases/common/124 test skip/test_skip.c new file mode 100644 index 0000000..d050a61 --- /dev/null +++ b/test cases/common/124 test skip/test_skip.c @@ -0,0 +1,4 @@ +int main(int argc, char *argv[]) +{ + return 77; +} diff --git a/test cases/common/25 object extraction/lib2.c b/test cases/common/25 object extraction/lib2.c new file mode 100644 index 0000000..c30dde2 --- /dev/null +++ b/test cases/common/25 object extraction/lib2.c @@ -0,0 +1,3 @@ +int retval() { + return 43; +} diff --git a/test cases/common/25 object extraction/meson.build b/test cases/common/25 object extraction/meson.build index 7c5ab90..c76b0db 100644 --- a/test cases/common/25 object extraction/meson.build +++ b/test cases/common/25 object extraction/meson.build @@ -4,7 +4,7 @@ if meson.is_unity() message('Skipping extraction test because this is a Unity build.') else lib1 = shared_library('somelib', 'src/lib.c') - lib2 = shared_library('somelib2', 'lib.c') + lib2 = shared_library('somelib2', 'lib.c', 'lib2.c') obj1 = lib1.extract_objects('src/lib.c') obj2 = lib2.extract_objects(['lib.c']) diff --git a/test cases/common/31 find program/meson.build b/test cases/common/31 find program/meson.build index ae71703..ba5386d 100644 --- a/test cases/common/31 find program/meson.build +++ b/test cases/common/31 find program/meson.build @@ -9,7 +9,7 @@ if build_machine.system() == 'windows' # the program can be found. cp = find_program('xcopy') else - cp = find_program('cp') + cp = find_program('donotfindme', 'cp') gen = generator(cp, \ output : '@BASENAME@.c', \ arguments : ['@INPUT@', '@OUTPUT@']) diff --git a/test cases/common/43 has function/meson.build b/test cases/common/43 has function/meson.build index 61f96e1..e0d3344 100644 --- a/test cases/common/43 has function/meson.build +++ b/test cases/common/43 has function/meson.build @@ -1,9 +1,12 @@ project('has function', 'c', 'cpp') +# This is used in the `test_compiler_check_flags_order` unit test +unit_test_args = '-I/tmp' compilers = [meson.get_compiler('c'), meson.get_compiler('cpp')] foreach cc : compilers - if not cc.has_function('printf', prefix : '#include<stdio.h>') + if not cc.has_function('printf', prefix : '#include<stdio.h>', + args : unit_test_args) error('"printf" function not found (should always exist).') endif @@ -13,12 +16,16 @@ foreach cc : compilers # On MSVC fprintf is defined as an inline function in the header, so it cannot # be found without the include. if cc.get_id() != 'msvc' - assert(cc.has_function('fprintf'), '"fprintf" function not found without include (on !msvc).') + assert(cc.has_function('fprintf', args : unit_test_args), + '"fprintf" function not found without include (on !msvc).') else - assert(cc.has_function('fprintf', prefix : '#include <stdio.h>'), '"fprintf" function not found with include (on msvc).') + assert(cc.has_function('fprintf', prefix : '#include <stdio.h>', + args : unit_test_args), + '"fprintf" function not found with include (on msvc).') endif - if cc.has_function('hfkerhisadf', prefix : '#include<stdio.h>') + if cc.has_function('hfkerhisadf', prefix : '#include<stdio.h>', + args : unit_test_args) error('Found non-existent function "hfkerhisadf".') endif @@ -28,16 +35,23 @@ foreach cc : compilers # implemented in glibc it's probably not implemented in any other 'slimmer' # C library variants either, so the check should be safe either way hopefully. if host_machine.system() == 'linux' and cc.get_id() == 'gcc' - assert (cc.has_function('poll', prefix : '#include <poll.h>'), 'couldn\'t detect "poll" when defined by a header') - assert (not cc.has_function('lchmod', prefix : '''#include <sys/stat.h> - #include <unistd.h>'''), '"lchmod" check should have failed') + assert (cc.has_function('poll', prefix : '#include <poll.h>', + args : unit_test_args), + 'couldn\'t detect "poll" when defined by a header') + lchmod_prefix = '#include <sys/stat.h>\n#include <unistd.h>' + assert (not cc.has_function('lchmod', prefix : lchmod_prefix, + args : unit_test_args), + '"lchmod" check should have failed') endif # For some functions one needs to define _GNU_SOURCE before including the # right headers to get them picked up. Make sure we can detect these functions # as well without any prefix - if cc.has_header_symbol('sys/socket.h', 'recvmmsg', prefix : '#define _GNU_SOURCE') + if cc.has_header_symbol('sys/socket.h', 'recvmmsg', + prefix : '#define _GNU_SOURCE', + args : unit_test_args) # We assume that if recvmmsg exists sendmmsg does too - assert (cc.has_function('sendmmsg'), 'Failed to detect function "sendmmsg" (should always exist).') + assert (cc.has_function('sendmmsg', args : unit_test_args), + 'Failed to detect function "sendmmsg" (should always exist).') endif endforeach diff --git a/test cases/common/51 pkgconfig-gen/meson.build b/test cases/common/51 pkgconfig-gen/meson.build index 0933238..e1e41d9 100644 --- a/test cases/common/51 pkgconfig-gen/meson.build +++ b/test cases/common/51 pkgconfig-gen/meson.build @@ -19,11 +19,17 @@ pkgg.generate( ) pkgconfig = find_program('pkg-config', required: false) -if pkgconfig.found() and build_machine.system() != 'windows' - test('pkgconfig-validation', pkgconfig, - args: ['--validate', 'simple'], - env: ['PKG_CONFIG_PATH=' + meson.current_build_dir() + '/meson-private' ], - ) +if pkgconfig.found() + v = run_command(pkgconfig, '--version').stdout().strip() + if v.version_compare('>=0.29') + test('pkgconfig-validation', pkgconfig, + args: ['--validate', 'simple'], + env: ['PKG_CONFIG_PATH=' + meson.current_build_dir() + '/meson-private' ]) + else + message('pkg-config version \'' + v + '\' too old, skipping validate test') + endif +else + message('pkg-config not found, skipping validate test') endif # Test that name_prefix='' and name='libfoo' results in '-lfoo' diff --git a/test cases/common/9 header install/installed_files.txt b/test cases/common/9 header install/installed_files.txt index b9e91a2..8af6c1f 100644 --- a/test cases/common/9 header install/installed_files.txt +++ b/test cases/common/9 header install/installed_files.txt @@ -1,3 +1,4 @@ usr/include/rootdir.h usr/include/subdir/subdir.h usr/include/vanished.h +usr/include/fileheader.h diff --git a/test cases/common/9 header install/meson.build b/test cases/common/9 header install/meson.build index 8c8ca73..7f3ce51 100644 --- a/test cases/common/9 header install/meson.build +++ b/test cases/common/9 header install/meson.build @@ -2,7 +2,10 @@ project('header install', 'c') as_array = ['subdir.h'] +subdir('vanishing_subdir') +subdir('sub') + h1 = install_headers('rootdir.h') h2 = install_headers(as_array, subdir : 'subdir') +h3 = install_headers(subheader) -subdir('vanishing_subdir') diff --git a/test cases/common/9 header install/sub/fileheader.h b/test cases/common/9 header install/sub/fileheader.h new file mode 100644 index 0000000..28e5c8d --- /dev/null +++ b/test cases/common/9 header install/sub/fileheader.h @@ -0,0 +1,3 @@ +#pragma once + +#define LIFE "Is life! Na naa, naa-na na." diff --git a/test cases/common/9 header install/sub/meson.build b/test cases/common/9 header install/sub/meson.build new file mode 100644 index 0000000..1ee0d1d --- /dev/null +++ b/test cases/common/9 header install/sub/meson.build @@ -0,0 +1,2 @@ +subheader = files('fileheader.h') + diff --git a/test cases/failing/37 pkgconfig dependency impossible conditions/meson.build b/test cases/failing/37 pkgconfig dependency impossible conditions/meson.build new file mode 100644 index 0000000..54d434c --- /dev/null +++ b/test cases/failing/37 pkgconfig dependency impossible conditions/meson.build @@ -0,0 +1,3 @@ +project('impossible-dep-test', 'c', version : '1.0') + +dependency('zlib', version : ['>=1.0', '<1.0']) diff --git a/test cases/frameworks/6 gettext/data/meson.build b/test cases/frameworks/6 gettext/data/meson.build new file mode 100644 index 0000000..d927ba3 --- /dev/null +++ b/test cases/frameworks/6 gettext/data/meson.build @@ -0,0 +1,8 @@ +i18n.merge_file( + input: 'test.desktop.in', + output: 'test.desktop', + type: 'desktop', + po_dir: '../po', + install: true, + install_dir: join_paths(get_option('datadir'), 'applications') +) diff --git a/test cases/frameworks/6 gettext/data/test.desktop.in b/test cases/frameworks/6 gettext/data/test.desktop.in new file mode 100644 index 0000000..33b9a9f --- /dev/null +++ b/test cases/frameworks/6 gettext/data/test.desktop.in @@ -0,0 +1,6 @@ +[Desktop Entry] +Name=Test +GenericName=Application +Comment=Test Application +Type=Application + diff --git a/test cases/frameworks/6 gettext/installed_files.txt b/test cases/frameworks/6 gettext/installed_files.txt index c95b9fd..ffe543f 100644 --- a/test cases/frameworks/6 gettext/installed_files.txt +++ b/test cases/frameworks/6 gettext/installed_files.txt @@ -1,3 +1,4 @@ usr/bin/intlprog usr/share/locale/de/LC_MESSAGES/intltest.mo usr/share/locale/fi/LC_MESSAGES/intltest.mo +usr/share/applications/test.desktop diff --git a/test cases/frameworks/6 gettext/meson.build b/test cases/frameworks/6 gettext/meson.build index 6bba7e0..6b517a4 100644 --- a/test cases/frameworks/6 gettext/meson.build +++ b/test cases/frameworks/6 gettext/meson.build @@ -4,3 +4,4 @@ i18n = import('i18n') subdir('po') subdir('src') +subdir('data') diff --git a/test cases/frameworks/6 gettext/po/LINGUAS b/test cases/frameworks/6 gettext/po/LINGUAS new file mode 100644 index 0000000..d319e48 --- /dev/null +++ b/test cases/frameworks/6 gettext/po/LINGUAS @@ -0,0 +1,2 @@ +de +fi diff --git a/test cases/frameworks/6 gettext/po/POTFILES b/test cases/frameworks/6 gettext/po/POTFILES index 5fd4b84..f49cecd 100644 --- a/test cases/frameworks/6 gettext/po/POTFILES +++ b/test cases/frameworks/6 gettext/po/POTFILES @@ -1 +1,2 @@ src/intlmain.c +data/test.desktop diff --git a/test cases/frameworks/7 gnome/installed_files.txt b/test cases/frameworks/7 gnome/installed_files.txt index 06f4163..d0d51d5 100644 --- a/test cases/frameworks/7 gnome/installed_files.txt +++ b/test cases/frameworks/7 gnome/installed_files.txt @@ -12,3 +12,5 @@ usr/share/gir-1.0/Meson-1.0.gir usr/share/gir-1.0/MesonDep1-1.0.gir usr/share/gir-1.0/MesonDep2-1.0.gir usr/share/glib-2.0/schemas/com.github.meson.gschema.xml +usr/share/simple-resources.gresource +usr/include/simple-resources.h diff --git a/test cases/frameworks/7 gnome/resources/meson.build b/test cases/frameworks/7 gnome/resources/meson.build index f8c3d58..2e72501 100644 --- a/test cases/frameworks/7 gnome/resources/meson.build +++ b/test cases/frameworks/7 gnome/resources/meson.build @@ -3,6 +3,8 @@ simple_resources = gnome.compile_resources('simple-resources', 'simple.gresource.xml', + install_header : true, + export : true, source_dir : '../resources-data', c_name : 'simple_resources') @@ -11,7 +13,16 @@ simple_res_exe = executable('simple-resources-test', dependencies: gio) test('simple resource test', simple_res_exe) -if glib.version() >= '9.99.9' +gnome.compile_resources('simple-resources', + 'simple.gresource.xml', + gresource_bundle: true, + install: true, + install_dir: get_option('datadir'), + source_dir : '../resources-data', +) +test('simple resource test (gresource)', find_program('resources.py')) + +if glib.version() >= '2.52.0' # This test cannot pass if GLib version is older than 9.99.9. # Meson will raise an error if the user tries to use the 'dependencies' # argument and the version of GLib is too old for generated resource diff --git a/test cases/frameworks/7 gnome/resources/resources.py b/test cases/frameworks/7 gnome/resources/resources.py new file mode 100644 index 0000000..b351b04 --- /dev/null +++ b/test cases/frameworks/7 gnome/resources/resources.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 +import os +from gi.repository import Gio + +if __name__ == '__main__': + res = Gio.resource_load(os.path.join('resources', 'simple-resources.gresource')) + Gio.Resource._register(res) + + data = Gio.resources_lookup_data('/com/example/myprog/res1.txt', Gio.ResourceLookupFlags.NONE) + assert(data.get_data() == b'This is a resource.\n') diff --git a/test cases/java/3 args/com/mesonbuild/Simple.java b/test cases/java/3 args/com/mesonbuild/Simple.java new file mode 100644 index 0000000..325a49a --- /dev/null +++ b/test cases/java/3 args/com/mesonbuild/Simple.java @@ -0,0 +1,7 @@ +package com.mesonbuild; + +class Simple { + public static void main(String [] args) { + System.out.println("Java is working.\n"); + } +} diff --git a/test cases/java/3 args/meson.build b/test cases/java/3 args/meson.build new file mode 100644 index 0000000..7a73cf8 --- /dev/null +++ b/test cases/java/3 args/meson.build @@ -0,0 +1,9 @@ +project('simplejava', 'java') + +add_project_arguments('-target', '1.6', language : 'java') + +javaprog = jar('myprog', 'com/mesonbuild/Simple.java', + main_class : 'com.mesonbuild.Simple', + java_args : ['-source', '1.6']) +test('mytest', javaprog) + diff --git a/test cases/linuxlike/1 pkg-config/meson.build b/test cases/linuxlike/1 pkg-config/meson.build index 13361a7..5a8366c 100644 --- a/test cases/linuxlike/1 pkg-config/meson.build +++ b/test cases/linuxlike/1 pkg-config/meson.build @@ -2,11 +2,16 @@ project('external dependency', 'c') # Zlib is probably on all dev machines. -dep = dependency('zlib', version : '>=1.2.8') -exe = executable('zlibprog', 'prog.c', dependencies : dep) +dep = dependency('zlib', version : '>=1.2') +exe = executable('zlibprog', 'prog-checkver.c', + dependencies : dep, + c_args : '-DFOUND_ZLIB="' + dep.version() + '"') -assert(dep.version().version_compare('>=1.2.8'), 'Pkg-config version numbers exposed incorrectly.') +assert(dep.version().version_compare('>=1.2'), 'Pkg-config version numbers exposed incorrectly.') +# Check that the version exposed by zlib internally is the same as the one we +# retrieve from the pkg-config file. This assumes that the packager didn't mess +# up, but we can be reasonably sure of that. test('zlibtest', exe) zprefix = dep.get_pkgconfig_variable('prefix') # Always set but we can't be sure what the value is. diff --git a/test cases/linuxlike/1 pkg-config/prog-checkver.c b/test cases/linuxlike/1 pkg-config/prog-checkver.c new file mode 100644 index 0000000..16b7170 --- /dev/null +++ b/test cases/linuxlike/1 pkg-config/prog-checkver.c @@ -0,0 +1,15 @@ +#include <zlib.h> +#include <stdio.h> +#include <string.h> + +int main(int argc, char **argv) { + void * something = deflate; + if(strcmp(ZLIB_VERSION, FOUND_ZLIB) != 0) { + printf("Meson found '%s' but zlib is '%s'\n", FOUND_ZLIB, ZLIB_VERSION); + return 2; + } + if(something != 0) + return 0; + printf("Couldn't find 'deflate'\n"); + return 1; +} diff --git a/test cases/linuxlike/5 dependency versions/meson.build b/test cases/linuxlike/5 dependency versions/meson.build index 1de87c8..7f29564 100644 --- a/test cases/linuxlike/5 dependency versions/meson.build +++ b/test cases/linuxlike/5 dependency versions/meson.build @@ -10,6 +10,17 @@ assert(zlib.type_name() == 'pkgconfig', 'zlib should be of type "pkgconfig" not zlibver = dependency('zlib', version : '<1.0', required : false) assert(zlibver.found() == false, 'zlib <1.0 should not be found!') +# Find external dependencies with various version restrictions +dependency('zlib', version : '>=1.0') +dependency('zlib', version : '<=9999') +dependency('zlib', version : '=' + zlib.version()) + +# Find external dependencies with multiple version restrictions +dependency('zlib', version : ['>=1.0', '<=9999']) +if dependency('zlib', version : ['<=1.0', '>=9999', '=' + zlib.version()], required : false).found() + error('zlib <=1.0 >=9999 should not have been found') +endif + # Test https://github.com/mesonbuild/meson/pull/610 dependency('somebrokenlib', version : '>=2.0', required : false) dependency('somebrokenlib', version : '>=1.0', required : false) diff --git a/test cases/unit/1 soname/CMakeLists.txt b/test cases/unit/1 soname/CMakeLists.txt new file mode 100644 index 0000000..c4f2e3e --- /dev/null +++ b/test cases/unit/1 soname/CMakeLists.txt @@ -0,0 +1,26 @@ +# This is a CMake version of this test. It behaves slightly differently +# so in case you ever need to debug this, here it is. +# +# The biggest difference is that if SOVERSION is not set, it +# is set to VERSION. Autotools sets it to the first number +# of VERSION. That is, for version number 1.2.3 CMake sets +# soname to 1.2.3 but Autotools sets it to 1. + +project(vertest C) +cmake_minimum_required(VERSION 3.5) + +add_library(nover SHARED versioned.c) + +add_library(verset SHARED versioned.c) +set_target_properties(verset PROPERTIES VERSION 4.5.6) + +add_library(soverset SHARED versioned.c) +set_target_properties(soverset PROPERTIES SOVERSION 1.2.3) + +add_library(bothset SHARED versioned.c) +set_target_properties(bothset PROPERTIES SOVERSION 1.2.3) +set_target_properties(bothset PROPERTIES VERSION 4.5.6) + +add_library(settosame SHARED versioned.c) +set_target_properties(settosame PROPERTIES SOVERSION 7.8.9) +set_target_properties(settosame PROPERTIES VERSION 7.8.9) diff --git a/test cases/unit/1 soname/meson.build b/test cases/unit/1 soname/meson.build new file mode 100644 index 0000000..d956afe --- /dev/null +++ b/test cases/unit/1 soname/meson.build @@ -0,0 +1,18 @@ +project('vertest', 'c') + +shared_library('nover', 'versioned.c') + +shared_library('verset', 'versioned.c', + version : '4.5.6') + +shared_library('soverset', 'versioned.c', + soversion : '1.2.3') + +shared_library('bothset', 'versioned.c', + soversion : '1.2.3', + version : '4.5.6') + +shared_library('settosame', 'versioned.c', + soversion : '7.8.9', + version : '7.8.9') + diff --git a/test cases/unit/1 soname/versioned.c b/test cases/unit/1 soname/versioned.c new file mode 100644 index 0000000..f48d2b0 --- /dev/null +++ b/test cases/unit/1 soname/versioned.c @@ -0,0 +1,3 @@ +int versioned_func() { + return 0; +} diff --git a/test cases/vala/11 mixed sources/c/foo.c b/test cases/vala/10 mixed sources/c/foo.c index f3c6fb8..f3c6fb8 100644 --- a/test cases/vala/11 mixed sources/c/foo.c +++ b/test cases/vala/10 mixed sources/c/foo.c diff --git a/test cases/vala/11 mixed sources/c/meson.build b/test cases/vala/10 mixed sources/c/meson.build index ead0575..ead0575 100644 --- a/test cases/vala/11 mixed sources/c/meson.build +++ b/test cases/vala/10 mixed sources/c/meson.build diff --git a/test cases/vala/11 mixed sources/c/writec.py b/test cases/vala/10 mixed sources/c/writec.py index 2cc822b..2cc822b 100644 --- a/test cases/vala/11 mixed sources/c/writec.py +++ b/test cases/vala/10 mixed sources/c/writec.py diff --git a/test cases/vala/11 mixed sources/meson.build b/test cases/vala/10 mixed sources/meson.build index 75b8ecd..75b8ecd 100644 --- a/test cases/vala/11 mixed sources/meson.build +++ b/test cases/vala/10 mixed sources/meson.build diff --git a/test cases/vala/11 mixed sources/vala/bar.vala b/test cases/vala/10 mixed sources/vala/bar.vala index 10dce1e..10dce1e 100644 --- a/test cases/vala/11 mixed sources/vala/bar.vala +++ b/test cases/vala/10 mixed sources/vala/bar.vala diff --git a/test cases/vala/12 generated vapi/installed_files.txt b/test cases/vala/11 generated vapi/installed_files.txt index 5993d01..5993d01 100644 --- a/test cases/vala/12 generated vapi/installed_files.txt +++ b/test cases/vala/11 generated vapi/installed_files.txt diff --git a/test cases/vala/12 generated vapi/libbar/bar.c b/test cases/vala/11 generated vapi/libbar/bar.c index f0f5cb8..f0f5cb8 100644 --- a/test cases/vala/12 generated vapi/libbar/bar.c +++ b/test cases/vala/11 generated vapi/libbar/bar.c diff --git a/test cases/vala/12 generated vapi/libbar/bar.h b/test cases/vala/11 generated vapi/libbar/bar.h index 165b104..165b104 100644 --- a/test cases/vala/12 generated vapi/libbar/bar.h +++ b/test cases/vala/11 generated vapi/libbar/bar.h diff --git a/test cases/vala/12 generated vapi/libbar/meson.build b/test cases/vala/11 generated vapi/libbar/meson.build index 6482504..6482504 100644 --- a/test cases/vala/12 generated vapi/libbar/meson.build +++ b/test cases/vala/11 generated vapi/libbar/meson.build diff --git a/test cases/vala/12 generated vapi/libfoo/foo.c b/test cases/vala/11 generated vapi/libfoo/foo.c index 0413ac5..0413ac5 100644 --- a/test cases/vala/12 generated vapi/libfoo/foo.c +++ b/test cases/vala/11 generated vapi/libfoo/foo.c diff --git a/test cases/vala/12 generated vapi/libfoo/foo.h b/test cases/vala/11 generated vapi/libfoo/foo.h index f09256d..f09256d 100644 --- a/test cases/vala/12 generated vapi/libfoo/foo.h +++ b/test cases/vala/11 generated vapi/libfoo/foo.h diff --git a/test cases/vala/12 generated vapi/libfoo/meson.build b/test cases/vala/11 generated vapi/libfoo/meson.build index 482c8fe..482c8fe 100644 --- a/test cases/vala/12 generated vapi/libfoo/meson.build +++ b/test cases/vala/11 generated vapi/libfoo/meson.build diff --git a/test cases/vala/12 generated vapi/main.vala b/test cases/vala/11 generated vapi/main.vala index 303ab33..303ab33 100644 --- a/test cases/vala/12 generated vapi/main.vala +++ b/test cases/vala/11 generated vapi/main.vala diff --git a/test cases/vala/12 generated vapi/meson.build b/test cases/vala/11 generated vapi/meson.build index 82f0c44..82f0c44 100644 --- a/test cases/vala/12 generated vapi/meson.build +++ b/test cases/vala/11 generated vapi/meson.build diff --git a/test cases/vala/13 custom output/foo.vala b/test cases/vala/12 custom output/foo.vala index e69de29..e69de29 100644 --- a/test cases/vala/13 custom output/foo.vala +++ b/test cases/vala/12 custom output/foo.vala diff --git a/test cases/vala/13 custom output/meson.build b/test cases/vala/12 custom output/meson.build index ef6dbb5..ef6dbb5 100644 --- a/test cases/vala/13 custom output/meson.build +++ b/test cases/vala/12 custom output/meson.build diff --git a/test cases/vala/14 find library/meson.build b/test cases/vala/13 find library/meson.build index 03054d2..03054d2 100644 --- a/test cases/vala/14 find library/meson.build +++ b/test cases/vala/13 find library/meson.build diff --git a/test cases/vala/14 find library/test.vala b/test cases/vala/13 find library/test.vala index b087cfb..b087cfb 100644 --- a/test cases/vala/14 find library/test.vala +++ b/test cases/vala/13 find library/test.vala |