diff options
-rw-r--r-- | .appveyor.yml | 2 | ||||
-rw-r--r-- | man/meson.1 | 2 | ||||
-rw-r--r-- | man/mesonconf.1 | 2 | ||||
-rw-r--r-- | man/mesonintrospect.1 | 2 | ||||
-rw-r--r-- | man/mesontest.1 | 2 | ||||
-rw-r--r-- | man/wraptool.1 | 2 | ||||
-rw-r--r-- | mesonbuild/backend/ninjabackend.py | 13 | ||||
-rw-r--r-- | mesonbuild/backend/vs2010backend.py | 42 | ||||
-rw-r--r-- | mesonbuild/backend/vs2015backend.py | 11 | ||||
-rw-r--r-- | mesonbuild/build.py | 13 | ||||
-rw-r--r-- | mesonbuild/compilers.py | 51 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 43 | ||||
-rw-r--r-- | mesonbuild/modules/gnome.py | 20 | ||||
-rw-r--r-- | mesonbuild/mparser.py | 9 | ||||
-rwxr-xr-x | run_project_tests.py | 50 | ||||
-rwxr-xr-x | run_unittests.py | 37 | ||||
-rw-r--r-- | test cases/common/113 generatorcustom/meson.build | 27 | ||||
-rw-r--r-- | test cases/common/37 has header/meson.build | 74 | ||||
-rw-r--r-- | test cases/common/37 has header/ouagadougou.h | 1 | ||||
-rw-r--r-- | test cases/common/40 logic ops/meson.build | 6 | ||||
-rw-r--r-- | test cases/linuxlike/10 large file support/meson.build | 12 | ||||
-rw-r--r-- | test cases/windows/8 msvc dll versioning/copyfile.py | 6 | ||||
-rw-r--r-- | test cases/windows/8 msvc dll versioning/meson.build | 3 |
23 files changed, 281 insertions, 149 deletions
diff --git a/.appveyor.yml b/.appveyor.yml index ce56a12..289758d 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -38,7 +38,7 @@ branches: install: # Use the x86 python only when building for x86 for the cpython tests. # For all other archs (including, say, arm), use the x64 python. - - ps: (new-object net.webclient).DownloadFile('https://dl.dropboxusercontent.com/u/37517477/ninja.exe', 'C:\projects\meson\ninja.exe') + - ps: (new-object net.webclient).DownloadFile('https://www.dropbox.com/s/bbzvepq85hv47x1/ninja.exe?dl=1', 'C:\projects\meson\ninja.exe') - cmd: if %arch%==x86 (set MESON_PYTHON_PATH=C:\python34) else (set MESON_PYTHON_PATH=C:\python34-x64) - cmd: echo Using Python at %MESON_PYTHON_PATH% - cmd: if %compiler%==msvc2010 ( call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" %arch% ) diff --git a/man/meson.1 b/man/meson.1 index 6311103..6defcaa 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "March 2017" "meson 0.39.0" "User Commands" +.TH MESON "1" "March 2017" "meson 0.39.1" "User Commands" .SH NAME meson - a high productivity build system .SH DESCRIPTION diff --git a/man/mesonconf.1 b/man/mesonconf.1 index e334d07..6ef8a1d 100644 --- a/man/mesonconf.1 +++ b/man/mesonconf.1 @@ -1,4 +1,4 @@ -.TH MESONCONF "1" "March 2017" "mesonconf 0.39.0" "User Commands" +.TH MESONCONF "1" "March 2017" "mesonconf 0.39.1" "User Commands" .SH NAME mesonconf - a tool to configure Meson builds .SH DESCRIPTION diff --git a/man/mesonintrospect.1 b/man/mesonintrospect.1 index 2346dce..c9c11db 100644 --- a/man/mesonintrospect.1 +++ b/man/mesonintrospect.1 @@ -1,4 +1,4 @@ -.TH MESONCONF "1" "March 2017" "mesonintrospect 0.39.0" "User Commands" +.TH MESONCONF "1" "March 2017" "mesonintrospect 0.39.1" "User Commands" .SH NAME mesonintrospect - a tool to extract information about a Meson build .SH DESCRIPTION diff --git a/man/mesontest.1 b/man/mesontest.1 index b98cff6..4e70149 100644 --- a/man/mesontest.1 +++ b/man/mesontest.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "March 2017" "meson 0.39.0" "User Commands" +.TH MESON "1" "March 2017" "meson 0.39.1" "User Commands" .SH NAME mesontest - test tool for the Meson build system .SH DESCRIPTION diff --git a/man/wraptool.1 b/man/wraptool.1 index 3b0766b..0a6b9f1 100644 --- a/man/wraptool.1 +++ b/man/wraptool.1 @@ -1,4 +1,4 @@ -.TH WRAPTOOL "1" "March 2017" "meson 0.39.0" "User Commands" +.TH WRAPTOOL "1" "March 2017" "meson 0.39.1" "User Commands" .SH NAME wraptool - source dependency downloader .SH DESCRIPTION diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 4d54acb..1acfe78 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2280,12 +2280,7 @@ rule FORTRAN_DEP_HACK def generate_ending(self, outfile): targetlist = [] - ctlist = [] for t in self.get_build_by_default_targets().values(): - if isinstance(t, build.CustomTarget): - # Create a list of all custom target outputs - for o in t.get_outputs(): - ctlist.append(os.path.join(self.get_target_dir(t), o)) # Add the first output of each target to the 'all' target so that # they are all built targetlist.append(os.path.join(self.get_target_dir(t), t.get_outputs()[0])) @@ -2302,14 +2297,22 @@ rule FORTRAN_DEP_HACK elem = NinjaBuildElement(self.all_outputs, 'clean', 'CUSTOM_COMMAND', 'PHONY') elem.add_item('COMMAND', [ninja_command, '-t', 'clean']) elem.add_item('description', 'Cleaning') + # If we have custom targets in this project, add all their outputs to # the list that is passed to the `cleantrees.py` script. The script # will manually delete all custom_target outputs that are directories # instead of files. This is needed because on platforms other than # Windows, Ninja only deletes directories while cleaning if they are # empty. https://github.com/mesonbuild/meson/issues/1220 + ctlist = [] + for t in self.build.get_targets().values(): + if isinstance(t, build.CustomTarget): + # Create a list of all custom target outputs + for o in t.get_outputs(): + ctlist.append(os.path.join(self.get_target_dir(t), o)) if ctlist: elem.add_dep(self.generate_custom_target_clean(outfile, ctlist)) + if 'b_coverage' in self.environment.coredata.base_options and \ self.environment.coredata.base_options['b_coverage'].value: self.generate_gcov_clean(outfile) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 547889c..e1f7325 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -93,9 +93,6 @@ class Vs2010Backend(backends.Backend): def generate_custom_generator_commands(self, target, parent_node): generator_output_files = [] - commands = [] - inputs = [] - outputs = [] custom_target_include_dirs = [] custom_target_output_files = [] target_private_dir = self.relpath(self.get_target_private_dir(target), self.get_target_dir(target)) @@ -116,6 +113,7 @@ class Vs2010Backend(backends.Backend): outfilelist = genlist.get_outputs() exe_arr = self.exe_object_to_cmd_array(exe) base_args = generator.get_arglist() + idgroup = ET.SubElement(parent_node, 'ItemGroup') for i in range(len(infilelist)): if len(infilelist) == len(outfilelist): sole_output = os.path.join(target_private_dir, outfilelist[i]) @@ -131,19 +129,10 @@ class Vs2010Backend(backends.Backend): args = self.replace_outputs(args, target_private_dir, outfiles_rel) args = [x.replace("@SOURCE_DIR@", self.environment.get_source_dir()).replace("@BUILD_DIR@", target_private_dir) for x in args] - fullcmd = exe_arr + self.replace_extra_args(args, genlist) - commands.append(' '.join(self.special_quote(fullcmd))) - inputs.append(infilename) - outputs.extend(outfiles) - if len(commands) > 0: - idgroup = ET.SubElement(parent_node, 'ItemDefinitionGroup') - cbs = ET.SubElement(idgroup, 'CustomBuildStep') - ET.SubElement(cbs, 'Command').text = '\r\n'.join(commands) - ET.SubElement(cbs, 'Inputs').text = ";".join(inputs) - ET.SubElement(cbs, 'Outputs').text = ';'.join(outputs) - ET.SubElement(cbs, 'Message').text = 'Generating custom sources.' - pg = ET.SubElement(parent_node, 'PropertyGroup') - ET.SubElement(pg, 'CustomBuildBeforeTargets').text = 'ClCompile' + cmd = exe_arr + self.replace_extra_args(args, genlist) + cbs = ET.SubElement(idgroup, 'CustomBuild', Include=infilename) + ET.SubElement(cbs, 'Command').text = ' '.join(self.quote_arguments(cmd)) + ET.SubElement(cbs, 'Outputs').text = ';'.join(outfiles) return generator_output_files, custom_target_output_files, custom_target_include_dirs def generate(self, interp): @@ -205,8 +194,7 @@ class Vs2010Backend(backends.Backend): for d in [target.command] + target.args: if isinstance(d, (build.BuildTarget, build.CustomTarget)): all_deps[d.get_id()] = d - # BuildTarget - else: + elif isinstance(target, build.BuildTarget): for ldep in target.link_targets: all_deps[ldep.get_id()] = ldep for obj_id, objdep in self.get_obj_target_deps(target.objects): @@ -218,6 +206,8 @@ class Vs2010Backend(backends.Backend): gen_exe = gendep.generator.get_exe() if isinstance(gen_exe, build.Executable): all_deps[gen_exe.get_id()] = gen_exe + else: + raise MesonException('Unknown target type for target %s' % target) if not t or not recursive: return all_deps ret = self.get_target_deps(all_deps, recursive) @@ -340,8 +330,8 @@ class Vs2010Backend(backends.Backend): directories = os.path.normpath(target.subdir).split(os.sep) return os.sep.join(['..'] * len(directories)) - def special_quote(self, arr): - return ['"%s"' % i for i in arr] + def quote_arguments(self, arr): + return ['"%s"' % i for i in arr] def create_basic_crap(self, target): project_name = target.name @@ -413,11 +403,11 @@ class Vs2010Backend(backends.Backend): # from the target dir, not the build root. target.absolute_paths = True (srcs, ofilenames, cmd) = self.eval_custom_target_command(target, True) - cmd_templ = '''"%s" ''' * len(cmd) - ET.SubElement(customstep, 'Command').text = cmd_templ % tuple(cmd) + ET.SubElement(customstep, 'Command').text = ' '.join(self.quote_arguments(cmd)) ET.SubElement(customstep, 'Outputs').text = ';'.join(ofilenames) ET.SubElement(customstep, 'Inputs').text = ';'.join(srcs) ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets') + self.generate_custom_generator_commands(target, root) tree = ET.ElementTree(root) tree.write(ofname, encoding='utf-8', xml_declaration=True) @@ -467,14 +457,14 @@ class Vs2010Backend(backends.Backend): @staticmethod def has_objects(objects, additional_objects, generated_objects): - # Ignore generated objects, those are automatically used by MSBuild for VS2010, because they are part of - # the CustomBuildStep Outputs. + # Ignore generated objects, those are automatically used by MSBuild because they are part of + # the CustomBuild Outputs. return len(objects) + len(additional_objects) > 0 @staticmethod def add_generated_objects(node, generated_objects): - # Do not add generated objects to project file. Those are automatically used by MSBuild for VS2010, because - # they are part of the CustomBuildStep Outputs. + # Do not add generated objects to project file. Those are automatically used by MSBuild, because + # they are part of the CustomBuild Outputs. return @staticmethod diff --git a/mesonbuild/backend/vs2015backend.py b/mesonbuild/backend/vs2015backend.py index 2c0efa7..b8e3504 100644 --- a/mesonbuild/backend/vs2015backend.py +++ b/mesonbuild/backend/vs2015backend.py @@ -23,14 +23,3 @@ class Vs2015Backend(Vs2010Backend): self.platform_toolset = 'v140' self.vs_version = '2015' - @staticmethod - def has_objects(objects, additional_objects, generated_objects): - # VS2015 requires generated objects to be added explicitly to the project file. - return len(objects) + len(additional_objects) + len(generated_objects) > 0 - - @staticmethod - def add_generated_objects(node, generated_objects): - # VS2015 requires generated objects to be added explicitly to the project file. - for s in generated_objects: - ET.SubElement(node, 'Object', Include=s) - return diff --git a/mesonbuild/build.py b/mesonbuild/build.py index bf692e1..c7e8f8e 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1277,7 +1277,7 @@ class CustomTarget(Target): for c in self.sources: if hasattr(c, 'held_object'): c = c.held_object - if isinstance(c, (BuildTarget, CustomTarget, GeneratedList)): + if isinstance(c, (BuildTarget, CustomTarget)): deps.append(c) return deps @@ -1402,8 +1402,17 @@ class CustomTarget(Target): def get_sources(self): return self.sources + def get_generated_lists(self): + genlists = [] + for c in self.sources: + if hasattr(c, 'held_object'): + c = c.held_object + if isinstance(c, GeneratedList): + genlists.append(c) + return genlists + def get_generated_sources(self): - return [] + return self.get_generated_lists() def type_suffix(self): return "@cus" diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py index 1fb9c5a..519a7d5 100644 --- a/mesonbuild/compilers.py +++ b/mesonbuild/compilers.py @@ -709,7 +709,10 @@ class CCompiler(Compiler): return True # When compiling static libraries, so yes. def get_always_args(self): - return [] + ''' + Args that are always-on for all C compilers other than MSVC + ''' + return ['-pipe'] + get_largefile_args(self) def get_linker_debug_crt_args(self): """ @@ -892,11 +895,11 @@ class CCompiler(Compiler): fargs = {'prefix': prefix, 'header': hname} code = '''{prefix} #ifdef __has_include - #if !__has_include(<{header}>) + #if !__has_include("{header}") #error "Header '{header}' could not be found" #endif #else - #include<{header}> + #include <{header}> #endif''' return self.compiles(code.format(**fargs), env, extra_args, dependencies, 'preprocess') @@ -1891,6 +1894,16 @@ class DCompiler(Compiler): # translate library link flag dcargs.append('-L' + arg) continue + elif arg.startswith('-L/') or arg.startswith('-L./'): + # we need to handle cases where -L is set by e.g. a pkg-config + # setting to select a linker search path. We can however not + # unconditionally prefix '-L' with '-L' because the user might + # have set this flag too to do what it is intended to for this + # compiler (pass flag through to the linker) + # Hence, we guess here whether the flag was intended to pass + # a linker search path. + dcargs.append('-L' + arg) + continue dcargs.append(arg) return dcargs @@ -2058,6 +2071,7 @@ class VisualStudioCCompiler(CCompiler): '3': ['/W4']} self.base_options = ['b_pch'] # FIXME add lto, pgo and the like + # Override CCompiler.get_always_args def get_always_args(self): return self.always_args @@ -2296,6 +2310,34 @@ def get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, i else: raise RuntimeError('Not implemented yet.') +def get_compiler_is_linuxlike(compiler): + if (getattr(compiler, 'gcc_type', None) == GCC_STANDARD) or \ + (getattr(compiler, 'clang_type', None) == CLANG_STANDARD) or \ + (getattr(compiler, 'icc_type', None) == ICC_STANDARD): + return True + return False + +def get_largefile_args(compiler): + ''' + Enable transparent large-file-support for 32-bit UNIX systems + ''' + if get_compiler_is_linuxlike(compiler): + # Enable large-file support unconditionally on all platforms other + # than macOS and Windows. macOS is now 64-bit-only so it doesn't + # need anything special, and Windows doesn't have automatic LFS. + # You must use the 64-bit counterparts explicitly. + # glibc, musl, and uclibc, and all BSD libcs support this. On Android, + # support for transparent LFS is available depending on the version of + # Bionic: https://github.com/android/platform_bionic#32-bit-abi-bugs + # https://code.google.com/p/android/issues/detail?id=64613 + # + # If this breaks your code, fix it! It's been 20+ years! + return ['-D_FILE_OFFSET_BITS=64'] + # We don't enable -D_LARGEFILE64_SOURCE since that enables + # transitionary features and must be enabled by programs that use + # those features explicitly. + return [] + class GnuCompiler: # Functionality that is common to all GNU family compilers. @@ -2344,9 +2386,6 @@ class GnuCompiler: return apple_buildtype_linker_args[buildtype] return gnulike_buildtype_linker_args[buildtype] - def get_always_args(self): - return ['-pipe'] - def get_pch_suffix(self): return 'gch' diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 9e8b136..79a531d 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -32,6 +32,7 @@ from .modules import ModuleReturnValue import os, sys, shutil, uuid import re +from collections import namedtuple import importlib @@ -1008,8 +1009,11 @@ class CompilerHolder(InterpreterObject): mlog.log('First supported argument:', mlog.red('None')) return [] -class ModuleState: - pass +ModuleState = namedtuple('ModuleState', [ + 'build_to_src', 'subdir', 'environment', 'project_name', + 'project_version', 'compilers', 'targets', 'data', 'headers', + 'man', 'global_args', 'project_args', 'build_machine', + 'host_machine', 'target_machine']) class ModuleHolder(InterpreterObject): def __init__(self, modname, module, interpreter): @@ -1028,23 +1032,24 @@ class ModuleHolder(InterpreterObject): # This is not 100% reliable but we can't use hash() # because the Build object contains dicts and lists. num_targets = len(self.interpreter.build.targets) - state = ModuleState() - state.build_to_src = os.path.relpath(self.interpreter.environment.get_source_dir(), - self.interpreter.environment.get_build_dir()) - state.subdir = self.interpreter.subdir - state.environment = self.interpreter.environment - state.project_name = self.interpreter.build.project_name - state.project_version = self.interpreter.build.dep_manifest[self.interpreter.active_projectname] - state.compilers = self.interpreter.build.compilers - state.targets = self.interpreter.build.targets - state.data = self.interpreter.build.data - state.headers = self.interpreter.build.get_headers() - state.man = self.interpreter.build.get_man() - state.global_args = self.interpreter.build.global_args - state.project_args = self.interpreter.build.projects_args.get(self.interpreter.subproject, {}) - state.build_machine = self.interpreter.builtin['build_machine'].held_object - state.host_machine = self.interpreter.builtin['host_machine'].held_object - state.target_machine = self.interpreter.builtin['target_machine'].held_object + state = ModuleState( + build_to_src=os.path.relpath(self.interpreter.environment.get_source_dir(), + self.interpreter.environment.get_build_dir()), + subdir=self.interpreter.subdir, + environment=self.interpreter.environment, + project_name=self.interpreter.build.project_name, + project_version=self.interpreter.build.dep_manifest[self.interpreter.active_projectname], + compilers=self.interpreter.build.compilers, + targets=self.interpreter.build.targets, + data=self.interpreter.build.data, + headers=self.interpreter.build.get_headers(), + man=self.interpreter.build.get_man(), + global_args=self.interpreter.build.global_args, + project_args=self.interpreter.build.projects_args.get(self.interpreter.subproject, {}), + build_machine=self.interpreter.builtin['build_machine'].held_object, + host_machine=self.interpreter.builtin['host_machine'].held_object, + target_machine=self.interpreter.builtin['target_machine'].held_object, + ) if self.held_object.is_snippet(method_name): value = fn(self.interpreter, state, args, kwargs) return self.interpreter.holderify(value) diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 13ffabc..423031d 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -41,6 +41,7 @@ gresource_dep_needed_version = '>= 2.51.1' native_glib_version = None girwarning_printed = False +gdbuswarning_printed = False gresource_warning_printed = False _gir_has_extra_lib_arg = None @@ -80,6 +81,15 @@ class GnomeModule(ExtensionModule): gresource_warning_printed = True return [] + @staticmethod + def _print_gdbus_warning(): + global gdbuswarning_printed + if not gdbuswarning_printed: + mlog.warning('Code generated with gdbus_codegen() requires the root directory be added to\n' + ' include_directories of targets with GLib < 2.51.3:', + mlog.bold('https://github.com/mesonbuild/meson/issues/1387')) + gdbuswarning_printed = True + def compile_resources(self, state, args, kwargs): self.__print_gresources_warning(state) glib_version = self._get_native_glib_version(state) @@ -758,7 +768,13 @@ class GnomeModule(ExtensionModule): cmd += ['--interface-prefix', kwargs.pop('interface_prefix')] if 'namespace' in kwargs: cmd += ['--c-namespace', kwargs.pop('namespace')] - cmd += ['--generate-c-code', '@OUTDIR@/' + namebase, '@INPUT@'] + + # https://git.gnome.org/browse/glib/commit/?id=ee09bb704fe9ccb24d92dd86696a0e6bb8f0dc1a + if mesonlib.version_compare(self._get_native_glib_version(state), '>= 2.51.3'): + cmd += ['--output-directory', '@OUTDIR@', '--generate-c-code', namebase, '@INPUT@'] + else: + self._print_gdbus_warning() + cmd += ['--generate-c-code', '@OUTDIR@/' + namebase, '@INPUT@'] outputs = [namebase + '.c', namebase + '.h'] custom_kwargs = {'input': xml_file, 'output': outputs, @@ -927,7 +943,7 @@ class GnomeModule(ExtensionModule): if arg in kwargs: custom_kwargs[arg] = kwargs[arg] - custom_kwargs['command'] = cmd + ['--header', '--body', '@INPUT@'] + custom_kwargs['command'] = cmd + ['--body', '@INPUT@'] custom_kwargs['output'] = output + '.c' body = build.CustomTarget(output + '_c', state.subdir, custom_kwargs) diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py index 6e1e398..fe5ccc5 100644 --- a/mesonbuild/mparser.py +++ b/mesonbuild/mparser.py @@ -193,9 +193,10 @@ class OrNode: self.right = right class AndNode: - def __init__(self, lineno, colno, left, right): - self.lineno = lineno - self.colno = colno + def __init__(self, left, right): + self.subdir = left.subdir + self.lineno = left.lineno + self.colno = left.colno self.left = left self.right = right @@ -436,7 +437,7 @@ class Parser: def e3(self): left = self.e4() while self.accept('and'): - left = AndNode(left.lineno, left.colno, left, self.e4()) + left = AndNode(left, self.e4()) return left def e4(self): diff --git a/run_project_tests.py b/run_project_tests.py index 1457432..3684de5 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -18,6 +18,7 @@ from glob import glob import os, subprocess, shutil, sys, signal from io import StringIO from ast import literal_eval +from enum import Enum import tempfile import mesontest from mesonbuild import environment @@ -33,9 +34,17 @@ import concurrent.futures as conc from mesonbuild.coredata import backendlist +class BuildStep(Enum): + configure = 1 + build = 2 + test = 3 + install = 4 + clean = 5 + class TestResult: - def __init__(self, msg, stdo, stde, mlog, conftime=0, buildtime=0, testtime=0): + def __init__(self, msg, step, stdo, stde, mlog, conftime=0, buildtime=0, testtime=0): self.msg = msg + self.step = step self.stdo = stdo self.stde = stde self.mlog = mlog @@ -74,6 +83,7 @@ class AutoDeletedDir: failing_logs = [] print_debug = 'MESON_PRINT_TEST_OUTPUT' in os.environ do_debug = not {'MESON_PRINT_TEST_OUTPUT', 'TRAVIS', 'APPVEYOR'}.isdisjoint(os.environ) +no_meson_log_msg = 'No meson-log.txt found.' meson_command = os.path.join(os.getcwd(), 'meson') if not os.path.exists(meson_command): @@ -270,14 +280,14 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c with open(logfile, errors='ignore') as f: mesonlog = f.read() except Exception: - mesonlog = 'No meson-log.txt found.' + mesonlog = no_meson_log_msg gen_time = time.time() - gen_start if should_fail == 'meson': if returncode != 0: - return TestResult('', stdo, stde, mesonlog, gen_time) - return TestResult('Test that should have failed succeeded', stdo, stde, mesonlog, gen_time) + return TestResult('', BuildStep.configure, stdo, stde, mesonlog, gen_time) + return TestResult('Test that should have failed succeeded', BuildStep.configure, stdo, stde, mesonlog, gen_time) if returncode != 0: - return TestResult('Generating the build system failed.', stdo, stde, mesonlog, gen_time) + return TestResult('Generating the build system failed.', BuildStep.configure, stdo, stde, mesonlog, gen_time) # Build with subprocess comp = get_compile_commands_for_dir(compile_commands, test_build_dir) build_start = time.time() @@ -287,10 +297,10 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c stde += e if should_fail == 'build': if pc.returncode != 0: - return TestResult('', stdo, stde, mesonlog, gen_time) - return TestResult('Test that should have failed to build succeeded', stdo, stde, mesonlog, gen_time) + return TestResult('', BuildStep.build, stdo, stde, mesonlog, gen_time) + return TestResult('Test that should have failed to build succeeded', BuildStep.build, stdo, stde, mesonlog, gen_time) if pc.returncode != 0: - return TestResult('Compiling source code failed.', stdo, stde, mesonlog, gen_time, build_time) + return TestResult('Compiling source code failed.', BuildStep.build, stdo, stde, mesonlog, gen_time, build_time) # Touch the meson.build file to force a regenerate so we can test that # regeneration works. We need to sleep for 0.2s because Ninja tracks mtimes # at a low resolution: https://github.com/ninja-build/ninja/issues/371 @@ -304,12 +314,12 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c stde += tstde if should_fail == 'test': if returncode != 0: - return TestResult('', stdo, stde, mesonlog, gen_time) - return TestResult('Test that should have failed to run unit tests succeeded', stdo, stde, mesonlog, gen_time) + return TestResult('', BuildStep.test, stdo, stde, mesonlog, gen_time) + return TestResult('Test that should have failed to run unit tests succeeded', BuildStep.test, stdo, stde, mesonlog, gen_time) if returncode != 0: - return TestResult('Running unit tests failed.', stdo, stde, mesonlog, gen_time, build_time, test_time) + return TestResult('Running unit tests failed.', BuildStep.test, stdo, stde, mesonlog, gen_time, build_time, test_time) if len(install_commands) == 0: - return TestResult('', '', '', gen_time, build_time, test_time) + return TestResult('', BuildStep.install, '', '', mesonlog, gen_time, build_time, test_time) env = os.environ.copy() env['DESTDIR'] = install_dir # Install with subprocess @@ -317,7 +327,7 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c stdo += o stde += e if pi.returncode != 0: - return TestResult('Running install failed.', stdo, stde, mesonlog, gen_time, build_time, test_time) + return TestResult('Running install failed.', BuildStep.install, stdo, stde, mesonlog, gen_time, build_time, test_time) if len(clean_commands) != 0: env = os.environ.copy() # Clean with subprocess @@ -325,8 +335,8 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c stdo += o stde += e if pi.returncode != 0: - return TestResult('Running clean failed.', stdo, stde, mesonlog, gen_time, build_time, test_time) - return TestResult(validate_install(testdir, install_dir), stdo, stde, mesonlog, gen_time, build_time, test_time) + return TestResult('Running clean failed.', BuildStep.clean, stdo, stde, mesonlog, gen_time, build_time, test_time) + return TestResult(validate_install(testdir, install_dir), BuildStep.clean, stdo, stde, mesonlog, gen_time, build_time, test_time) def gather_tests(testdir): tests = [t.replace('\\', '/').split('/', 2)[2] for t in glob(os.path.join(testdir, '*'))] @@ -441,10 +451,16 @@ def run_tests(all_tests, log_name_base, extra_args): else: without_install = "" if len(install_commands) > 0 else " (without install)" if result.msg != '': - print('Failed test%s: %s' % (without_install, t)) + print('Failed test{} during {}: {!r}'.format(without_install, result.step.name, t)) print('Reason:', result.msg) failing_tests += 1 - failing_logs.append(result.stdo) + if result.step == BuildStep.configure and result.mlog != no_meson_log_msg: + # For configure failures, instead of printing stdout, + # print the meson log if available since it's a superset + # of stdout and often has very useful information. + failing_logs.append(result.mlog) + else: + failing_logs.append(result.stdo) failing_logs.append(result.stde) else: print('Succeeded test%s: %s' % (without_install, t)) diff --git a/run_unittests.py b/run_unittests.py index 82c1b80..9945057 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -356,16 +356,31 @@ class BasePlatformTests(unittest.TestCase): self.unit_test_dir = os.path.join(src_root, 'test cases/unit') self.orig_env = os.environ.copy() + def _print_meson_log(self): + log = os.path.join(self.logdir, 'meson-log.txt') + if not os.path.isfile(log): + print("{!r} doesn't exist".format(log)) + return + with open(log, 'r', encoding='utf-8') as f: + print(f.read()) + def tearDown(self): shutil.rmtree(self.builddir) os.environ = self.orig_env super().tearDown() def _run(self, command): - output = subprocess.check_output(command, stderr=subprocess.STDOUT, - env=os.environ.copy(), - universal_newlines=True) + ''' + Run a command while printing the stdout and stderr to stdout, + and also return a copy of it + ''' + p = subprocess.Popen(command, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, env=os.environ.copy(), + universal_newlines=True) + output = p.communicate()[0] print(output) + if p.returncode != 0: + raise subprocess.CalledProcessError(p.returncode, command) return output def init(self, srcdir, extra_args=None, default_args=True): @@ -375,7 +390,11 @@ class BasePlatformTests(unittest.TestCase): if default_args: args += ['--prefix', self.prefix, '--libdir', self.libdir] - self._run(self.meson_command + args + extra_args) + try: + self._run(self.meson_command + args + extra_args) + except: + self._print_meson_log() + raise self.privatedir = os.path.join(self.builddir, 'meson-private') def build(self, extra_args=None): @@ -394,11 +413,11 @@ class BasePlatformTests(unittest.TestCase): self._run(self.ninja_command + ['uninstall']) def run_target(self, target): - output = subprocess.check_output(self.ninja_command + [target], - stderr=subprocess.STDOUT, - universal_newlines=True) - print(output) - return output + ''' + Run a Ninja target while printing the stdout and stderr to stdout, + and also return a copy of it + ''' + return self._run(self.ninja_command + [target]) def setconf(self, arg, will_build=True): # This is needed to increase the difference between build.ninja's diff --git a/test cases/common/113 generatorcustom/meson.build b/test cases/common/113 generatorcustom/meson.build index 472d565..17d27e5 100644 --- a/test cases/common/113 generatorcustom/meson.build +++ b/test cases/common/113 generatorcustom/meson.build @@ -1,21 +1,18 @@ project('generatorcustom', 'c') -if meson.get_compiler('c').get_id() != 'msvc' - creator = find_program('gen.py') - catter = find_program('catter.py') +creator = find_program('gen.py') +catter = find_program('catter.py') - gen = generator(creator, - output: '@BASENAME@.h', - arguments : ['@INPUT@', '@OUTPUT@']) +gen = generator(creator, + output: '@BASENAME@.h', + arguments : ['@INPUT@', '@OUTPUT@']) - hs = gen.process('res1.txt', 'res2.txt') +hs = gen.process('res1.txt', 'res2.txt') - allinone = custom_target('alltogether', - input : hs, - output : 'alltogether.h', - command : [catter, '@INPUT@', '@OUTPUT@']) +allinone = custom_target('alltogether', + input : hs, + output : 'alltogether.h', + command : [catter, '@INPUT@', '@OUTPUT@']) + +executable('proggie', 'main.c', allinone) - executable('proggie', 'main.c', allinone) -else - error('MESON_SKIP_TEST: Skipping test on VS backend; see: https://github.com/mesonbuild/meson/issues/1004') -endif diff --git a/test cases/common/37 has header/meson.build b/test cases/common/37 has header/meson.build index 2f763ae..4299ce5 100644 --- a/test cases/common/37 has header/meson.build +++ b/test cases/common/37 has header/meson.build @@ -1,32 +1,54 @@ project('has header', 'c', 'cpp') -foreach comp : [meson.get_compiler('c'), meson.get_compiler('cpp')] - if not comp.has_header('stdio.h') - error('Stdio missing.') - endif - - # stdio.h doesn't actually need stdlib.h, but just test that setting the - # prefix does not result in an error. - if not comp.has_header('stdio.h', prefix : '#include <stdlib.h>') - error('Stdio missing.') - endif - - # XInput.h should not require type definitions from windows.h, but it does - # require macro definitions. Specifically, it requires an arch setting for - # VS2015 at least. - # We only do this check on MSVC because MinGW often defines its own wrappers - # that pre-include windows.h - if comp.get_id() == 'msvc' - if not comp.has_header('XInput.h', prefix : '#include <windows.h>') - error('XInput.h should not be missing on Windows') - endif - if not comp.has_header('XInput.h', prefix : '#define _X86_') - error('XInput.h should not need windows.h') +host_system = host_machine.system() + +non_existant_header = 'ouagadougou.h' + +# Copy it into the builddir to ensure that it isn't found even if it's there +configure_file(input : non_existant_header, + output : non_existant_header, + configuration : configuration_data()) + +# Test that the fallback to __has_include also works on all compilers +if host_system != 'darwin' + args = [[], ['-U__has_include']] +else + # On Darwin's clang you can't redefine builtin macros so the above doesn't work + args = [[]] +endif + +foreach arg : args + foreach comp : [meson.get_compiler('c'), meson.get_compiler('cpp')] + assert(comp.has_header('stdio.h', args : arg), 'Stdio missing.') + + # stdio.h doesn't actually need stdlib.h, but just test that setting the + # prefix does not result in an error. + assert(comp.has_header('stdio.h', prefix : '#include <stdlib.h>', args : arg), + 'Stdio missing.') + + # XInput.h should not require type definitions from windows.h, but it does + # require macro definitions. Specifically, it requires an arch setting for + # VS2015 at least. + # We only do this check on MSVC because MinGW often defines its own wrappers + # that pre-include windows.h + if comp.get_id() == 'msvc' + assert(comp.has_header('XInput.h', prefix : '#include <windows.h>', args : arg), + 'XInput.h should not be missing on Windows') + assert(comp.has_header('XInput.h', prefix : '#define _X86_', args : arg), + 'XInput.h should not need windows.h') endif - endif + # Test that the following GCC bug doesn't happen: + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80005 + # https://github.com/mesonbuild/meson/issues/1458 + if host_system == 'linux' + assert(comp.has_header('linux/if.h', args : arg), + 'Could not find <linux/if.h>') + endif - if comp.has_header('ouagadougou.h') - error('Found non-existant header.') - endif + # This header exists in the source and the builddir, but we still must not + # find it since we are looking in the system directories. + assert(not comp.has_header(non_existant_header, args : arg), + 'Found non-existant header.') + endforeach endforeach diff --git a/test cases/common/37 has header/ouagadougou.h b/test cases/common/37 has header/ouagadougou.h new file mode 100644 index 0000000..2f76c49 --- /dev/null +++ b/test cases/common/37 has header/ouagadougou.h @@ -0,0 +1 @@ +#define OMG_THIS_SHOULDNT_BE_FOUND diff --git a/test cases/common/40 logic ops/meson.build b/test cases/common/40 logic ops/meson.build index e975c7e..897054e 100644 --- a/test cases/common/40 logic ops/meson.build +++ b/test cases/common/40 logic ops/meson.build @@ -87,3 +87,9 @@ if t and t and t and t and t and t and t and t and f else message('Ok.') endif + +if t and t or t + message('Ok.') +else + error('Combination of and-or failed.') +endif diff --git a/test cases/linuxlike/10 large file support/meson.build b/test cases/linuxlike/10 large file support/meson.build new file mode 100644 index 0000000..aa4eccf --- /dev/null +++ b/test cases/linuxlike/10 large file support/meson.build @@ -0,0 +1,12 @@ +project('trivial test', 'c') + +cc = meson.get_compiler('c') + +size = cc.sizeof('off_t') +assert(size == 8, 'off_t size is @0@ bytes instead of 8'.format(size)) + +code = '''#if !defined(_FILE_OFFSET_BITS) || (_FILE_OFFSET_BITS != 64) +#error "Large-file support was not enabled" +#endif''' + +assert(cc.compiles(code, name : 'checking for LFS'), 'Large file support was not enabled') diff --git a/test cases/windows/8 msvc dll versioning/copyfile.py b/test cases/windows/8 msvc dll versioning/copyfile.py new file mode 100644 index 0000000..ff42ac3 --- /dev/null +++ b/test cases/windows/8 msvc dll versioning/copyfile.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 + +import sys +import shutil + +shutil.copyfile(sys.argv[1], sys.argv[2]) diff --git a/test cases/windows/8 msvc dll versioning/meson.build b/test cases/windows/8 msvc dll versioning/meson.build index 3f15e76..b72c5ec 100644 --- a/test cases/windows/8 msvc dll versioning/meson.build +++ b/test cases/windows/8 msvc dll versioning/meson.build @@ -28,11 +28,12 @@ onlysoversion = shared_library('onlysoversion', 'lib.c', # Hack to make the executables below depend on the shared libraries above # without actually adding them as `link_with` dependencies since we want to try # linking to them with -lfoo linker arguments. +cp = find_program('copyfile.py') out = custom_target('library-dependency-hack', input : 'exe.orig.c', output : 'exe.c', depends : [some, noversion, onlyversion, onlysoversion], - command : ['cp', '@INPUT@', '@OUTPUT@']) + command : [cp, '@INPUT@', '@OUTPUT@']) # Manually test if the linker can find the above libraries # i.e., whether they were generated with the right naming scheme |