aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml2
-rw-r--r--man/meson.12
-rw-r--r--man/mesonconf.12
-rw-r--r--man/mesonintrospect.12
-rw-r--r--man/mesontest.12
-rw-r--r--man/wraptool.12
-rw-r--r--mesonbuild/backend/ninjabackend.py13
-rw-r--r--mesonbuild/backend/vs2010backend.py42
-rw-r--r--mesonbuild/backend/vs2015backend.py11
-rw-r--r--mesonbuild/build.py13
-rw-r--r--mesonbuild/compilers.py51
-rw-r--r--mesonbuild/interpreter.py43
-rw-r--r--mesonbuild/modules/gnome.py20
-rw-r--r--mesonbuild/mparser.py9
-rwxr-xr-xrun_project_tests.py50
-rwxr-xr-xrun_unittests.py37
-rw-r--r--test cases/common/113 generatorcustom/meson.build27
-rw-r--r--test cases/common/37 has header/meson.build74
-rw-r--r--test cases/common/37 has header/ouagadougou.h1
-rw-r--r--test cases/common/40 logic ops/meson.build6
-rw-r--r--test cases/linuxlike/10 large file support/meson.build12
-rw-r--r--test cases/windows/8 msvc dll versioning/copyfile.py6
-rw-r--r--test cases/windows/8 msvc dll versioning/meson.build3
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