aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mesonbuild/backend/backends.py18
-rw-r--r--mesonbuild/backend/ninjabackend.py8
-rw-r--r--mesonbuild/backend/vs2010backend.py3
-rw-r--r--mesonbuild/compilers/c.py11
-rw-r--r--mesonbuild/mtest.py15
-rw-r--r--mesonbuild/scripts/meson_exe.py6
-rwxr-xr-xrun_tests.py34
-rwxr-xr-xrun_unittests.py65
-rw-r--r--test cases/unit/35 exe_wrapper behaviour/broken-cross.txt20
-rw-r--r--test cases/unit/35 exe_wrapper behaviour/meson.build19
-rw-r--r--test cases/unit/35 exe_wrapper behaviour/meson_options.txt2
-rw-r--r--test cases/unit/35 exe_wrapper behaviour/prog.c17
12 files changed, 186 insertions, 32 deletions
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index b13aa10..b55d3e0 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -276,8 +276,11 @@ class Backend:
raise MesonException('Unknown data type in object list.')
return obj_list
- def serialize_executable(self, exe, cmd_args, workdir, env={},
+ def serialize_executable(self, tname, exe, cmd_args, workdir, env={},
extra_paths=None, capture=None):
+ '''
+ Serialize an executable for running with a generator or a custom target
+ '''
import hashlib
if extra_paths is None:
# The callee didn't check if we needed extra paths, so check it here
@@ -302,19 +305,24 @@ class Backend:
with open(exe_data, 'wb') as f:
if isinstance(exe, dependencies.ExternalProgram):
exe_cmd = exe.get_command()
- exe_needs_wrapper = False
+ exe_is_native = True
elif isinstance(exe, (build.BuildTarget, build.CustomTarget)):
exe_cmd = [self.get_target_filename_abs(exe)]
- exe_needs_wrapper = exe.is_cross
+ exe_is_native = not exe.is_cross
else:
exe_cmd = [exe]
- exe_needs_wrapper = False
- is_cross_built = exe_needs_wrapper and \
+ exe_is_native = True
+ is_cross_built = (not exe_is_native) and \
self.environment.is_cross_build() and \
self.environment.cross_info.need_cross_compiler() and \
self.environment.cross_info.need_exe_wrapper()
if is_cross_built:
exe_wrapper = self.environment.get_exe_wrapper()
+ if not exe_wrapper.found():
+ msg = 'The exe_wrapper {!r} defined in the cross file is ' \
+ 'needed by target {!r}, but was not found. Please ' \
+ 'check the command and/or add it to PATH.'
+ raise MesonException(msg.format(exe_wrapper.name, tname))
else:
exe_wrapper = None
es = ExecutableSerialisation(basename, exe_cmd, cmd_args, env,
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index f62bc67..eed40bc 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -541,7 +541,7 @@ int dummy;
if extra_paths:
serialize = True
if serialize:
- exe_data = self.serialize_executable(target.command[0], cmd[1:],
+ exe_data = self.serialize_executable(target.name, target.command[0], cmd[1:],
# All targets are built from the build dir
self.environment.get_build_dir(),
extra_paths=extra_paths,
@@ -598,6 +598,11 @@ int dummy;
if self.environment.is_cross_build():
exe_wrap = self.environment.get_exe_wrapper()
if exe_wrap:
+ if not exe_wrap.found():
+ msg = 'The exe_wrapper {!r} defined in the cross file is ' \
+ 'needed by run target {!r}, but was not found. ' \
+ 'Please check the command and/or add it to PATH.'
+ raise MesonException(msg.format(exe_wrap.name, target.name))
cmd += exe_wrap.get_command()
cmd.append(abs_exe)
elif isinstance(texe, dependencies.ExternalProgram):
@@ -1932,6 +1937,7 @@ rule FORTRAN_DEP_HACK%s
cmdlist = exe_arr + self.replace_extra_args(args, genlist)
if generator.capture:
exe_data = self.serialize_executable(
+ 'generator ' + cmdlist[0],
cmdlist[0],
cmdlist[1:],
self.environment.get_build_dir(),
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index d42e91d..cb4f706 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -133,6 +133,7 @@ class Vs2010Backend(backends.Backend):
cmd = exe_arr + self.replace_extra_args(args, genlist)
if generator.capture:
exe_data = self.serialize_executable(
+ 'generator ' + cmd[0],
cmd[0],
cmd[1:],
self.environment.get_build_dir(),
@@ -489,7 +490,7 @@ class Vs2010Backend(backends.Backend):
tdir_abs = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target))
extra_bdeps = target.get_transitive_build_target_deps()
extra_paths = self.determine_windows_extra_paths(target.command[0], extra_bdeps)
- exe_data = self.serialize_executable(target.command[0], cmd[1:],
+ exe_data = self.serialize_executable(target.name, target.command[0], cmd[1:],
# All targets run from the target dir
tdir_abs,
extra_paths=extra_paths,
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index fa6fd89..1530ea0 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -57,9 +57,12 @@ class CCompiler(Compiler):
self.id = 'unknown'
self.is_cross = is_cross
self.can_compile_suffixes.add('h')
- self.exe_wrapper = exe_wrapper
- if self.exe_wrapper:
- self.exe_wrapper = self.exe_wrapper.get_command()
+ # If the exe wrapper was not found, pretend it wasn't set so that the
+ # sanity check is skipped and compiler checks use fallbacks.
+ if not exe_wrapper or not exe_wrapper.found():
+ self.exe_wrapper = None
+ else:
+ self.exe_wrapper = exe_wrapper.get_command()
# Set to None until we actually need to check this
self.has_fatal_warnings_link_arg = None
@@ -277,7 +280,7 @@ class CCompiler(Compiler):
if self.exe_wrapper is None:
# Can't check if the binaries run so we have to assume they do
return
- cmdlist = self.exe_wrapper.get_command() + [binary_name]
+ cmdlist = self.exe_wrapper + [binary_name]
else:
cmdlist = [binary_name]
mlog.debug('Running test binary command: ' + ' '.join(cmdlist))
diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py
index 3c4073b..8ebef04 100644
--- a/mesonbuild/mtest.py
+++ b/mesonbuild/mtest.py
@@ -240,7 +240,10 @@ class SingleTestRunner:
# because there is no execute wrapper.
return None
else:
- return [self.test.exe_runner] + self.test.fname
+ if not self.test.exe_runner.found():
+ msg = 'The exe_wrapper defined in the cross file {!r} was not ' \
+ 'found. Please check the command and/or add it to PATH.'
+ raise TestException(msg.format(self.test.exe_runner.name))
return self.test.exe_runner.get_command() + self.test.fname
else:
return self.test.fname
@@ -738,12 +741,13 @@ def run(args):
if check_bin is not None:
exe = ExternalProgram(check_bin, silent=True)
if not exe.found():
- sys.exit('Could not find requested program: %s' % check_bin)
+ print('Could not find requested program: {!r}'.format(check_bin))
+ return 1
options.wd = os.path.abspath(options.wd)
if not options.list and not options.no_rebuild:
if not rebuild_all(options.wd):
- sys.exit(-1)
+ return 1
try:
th = TestHarness(options)
@@ -755,5 +759,8 @@ def run(args):
return th.run_special()
except TestException as e:
print('Meson test encountered an error:\n')
- print(e)
+ if os.environ.get('MESON_FORCE_BACKTRACE'):
+ raise e
+ else:
+ print(e)
return 1
diff --git a/mesonbuild/scripts/meson_exe.py b/mesonbuild/scripts/meson_exe.py
index ee5906b..84abfc3 100644
--- a/mesonbuild/scripts/meson_exe.py
+++ b/mesonbuild/scripts/meson_exe.py
@@ -49,7 +49,11 @@ def run_exe(exe):
else:
if exe.is_cross:
if exe.exe_runner is None:
- raise AssertionError('BUG: Trying to run cross-compiled exes with no wrapper')
+ raise AssertionError('BUG: Can\'t run cross-compiled exe {!r}'
+ 'with no wrapper'.format(exe.name))
+ elif not exe.exe_runner.found():
+ raise AssertionError('BUG: Can\'t run cross-compiled exe {!r} with not-found'
+ 'wrapper {!r}'.format(exe.name, exe.exe_runner.get_path()))
else:
cmd = exe.exe_runner.get_command() + exe.fname
else:
diff --git a/run_tests.py b/run_tests.py
index af20ba2..6e441d3 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -29,6 +29,7 @@ from pathlib import Path
import mesonbuild
from mesonbuild import mesonlib
from mesonbuild import mesonmain
+from mesonbuild import mtest
from mesonbuild import mlog
from mesonbuild.environment import detect_ninja
@@ -156,8 +157,17 @@ def get_fake_options(prefix):
opts.cmd_line_options = {}
return opts
-def should_run_linux_cross_tests():
- return shutil.which('arm-linux-gnueabihf-gcc') and not platform.machine().lower().startswith('arm')
+def run_mtest_inprocess(commandlist):
+ old_stdout = sys.stdout
+ sys.stdout = mystdout = StringIO()
+ old_stderr = sys.stderr
+ sys.stderr = mystderr = StringIO()
+ try:
+ returncode = mtest.run(commandlist)
+ finally:
+ sys.stdout = old_stdout
+ sys.stderr = old_stderr
+ return returncode, mystdout.getvalue(), mystderr.getvalue()
def run_configure_inprocess(commandlist):
old_stdout = sys.stdout
@@ -203,7 +213,7 @@ if __name__ == '__main__':
# Iterate over list in reverse order to find the last --backend arg
backend = Backend.ninja
cross = False
- # FIXME: Convert to argparse
+ # FIXME: PLEASE convert to argparse
for arg in reversed(sys.argv[1:]):
if arg.startswith('--backend'):
if arg.startswith('--backend=vs'):
@@ -212,6 +222,10 @@ if __name__ == '__main__':
backend = Backend.xcode
if arg.startswith('--cross'):
cross = True
+ if arg == '--cross=mingw':
+ cross = 'mingw'
+ elif arg == '--cross=arm':
+ cross = 'arm'
# Running on a developer machine? Be nice!
if not mesonlib.is_windows() and not mesonlib.is_haiku() and 'TRAVIS' not in os.environ:
os.nice(20)
@@ -249,10 +263,12 @@ if __name__ == '__main__':
returncode += subprocess.call(mesonlib.python_command + ['run_project_tests.py'] + sys.argv[1:], env=env)
else:
cross_test_args = mesonlib.python_command + ['run_cross_test.py']
- print(mlog.bold('Running armhf cross tests.').get_text(mlog.colorize_console))
- print()
- returncode += subprocess.call(cross_test_args + ['cross/ubuntu-armhf.txt'], env=env)
- print(mlog.bold('Running mingw-w64 64-bit cross tests.').get_text(mlog.colorize_console))
- print()
- returncode += subprocess.call(cross_test_args + ['cross/linux-mingw-w64-64bit.txt'], env=env)
+ if cross is True or cross == 'arm':
+ print(mlog.bold('Running armhf cross tests.').get_text(mlog.colorize_console))
+ print()
+ returncode += subprocess.call(cross_test_args + ['cross/ubuntu-armhf.txt'], env=env)
+ if cross is True or cross == 'mingw':
+ print(mlog.bold('Running mingw-w64 64-bit cross tests.').get_text(mlog.colorize_console))
+ print()
+ returncode += subprocess.call(cross_test_args + ['cross/linux-mingw-w64-64bit.txt'], env=env)
sys.exit(returncode)
diff --git a/run_unittests.py b/run_unittests.py
index b3bc271..02afe8e 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -22,6 +22,7 @@ import textwrap
import os
import shutil
import unittest
+import platform
from unittest import mock
from configparser import ConfigParser
from glob import glob
@@ -47,7 +48,7 @@ import mesonbuild.modules.pkgconfig
from run_tests import exe_suffix, get_fake_options, get_meson_script
from run_tests import get_builddir_target_args, get_backend_commands, Backend
from run_tests import ensure_backend_detects_changes, run_configure_inprocess
-from run_tests import should_run_linux_cross_tests
+from run_tests import run_mtest_inprocess
def get_dynamic_section_entry(fname, entry):
@@ -641,8 +642,11 @@ class BasePlatformTests(unittest.TestCase):
dir_args = get_builddir_target_args(self.backend, self.builddir, None)
self._run(self.clean_command + dir_args, workdir=self.builddir)
- def run_tests(self):
- self._run(self.test_command, workdir=self.builddir)
+ def run_tests(self, inprocess=False):
+ if not inprocess:
+ self._run(self.test_command, workdir=self.builddir)
+ else:
+ run_mtest_inprocess(['-C', self.builddir])
def install(self, *, use_destdir=True):
if self.backend is not Backend.ninja:
@@ -3500,9 +3504,9 @@ endian = 'little'
self.assertNotRegex(out, self.installdir + '.*dylib ')
-class LinuxArmCrossCompileTests(BasePlatformTests):
+class LinuxCrossArmTests(BasePlatformTests):
'''
- Tests that verify cross-compilation to Linux/ARM
+ Tests that cross-compilation to Linux/ARM works
'''
def setUp(self):
super().setUp()
@@ -3536,6 +3540,45 @@ class LinuxArmCrossCompileTests(BasePlatformTests):
self.assertRegex(compdb[0]['command'], '-D_FILE_OFFSET_BITS=64.*-U_FILE_OFFSET_BITS')
self.build()
+class LinuxCrossMingwTests(BasePlatformTests):
+ '''
+ Tests that cross-compilation to Windows/MinGW works
+ '''
+ def setUp(self):
+ super().setUp()
+ src_root = os.path.dirname(__file__)
+ self.meson_cross_file = os.path.join(src_root, 'cross', 'linux-mingw-w64-64bit.txt')
+
+ def test_exe_wrapper_behaviour(self):
+ '''
+ Test that an exe wrapper that isn't found doesn't cause compiler sanity
+ checks and compiler checks to fail, but causes configure to fail if it
+ requires running a cross-built executable (custom_target or run_target)
+ and causes the tests to be skipped if they are run.
+ '''
+ testdir = os.path.join(self.unit_test_dir, '35 exe_wrapper behaviour')
+ # Configures, builds, and tests fine by default
+ self.init(testdir)
+ self.build()
+ self.run_tests()
+ self.wipe()
+ os.mkdir(self.builddir)
+ # Change cross file to use a non-existing exe_wrapper and it should fail
+ self.meson_cross_file = os.path.join(testdir, 'broken-cross.txt')
+ # Force tracebacks so we can detect them properly
+ os.environ['MESON_FORCE_BACKTRACE'] = '1'
+ with self.assertRaisesRegex(MesonException, 'exe_wrapper.*target.*use-exe-wrapper'):
+ # Must run in-process or we'll get a generic CalledProcessError
+ self.init(testdir, extra_args='-Drun-target=false', inprocess=True)
+ with self.assertRaisesRegex(MesonException, 'exe_wrapper.*run target.*run-prog'):
+ # Must run in-process or we'll get a generic CalledProcessError
+ self.init(testdir, extra_args='-Dcustom-target=false', inprocess=True)
+ self.init(testdir, extra_args=['-Dcustom-target=false', '-Drun-target=false'])
+ self.build()
+ with self.assertRaisesRegex(MesonException, 'exe_wrapper.*PATH'):
+ # Must run in-process or we'll get a generic CalledProcessError
+ self.run_tests(inprocess=True)
+
class PythonTests(BasePlatformTests):
'''
@@ -3669,13 +3712,21 @@ def unset_envs():
if v in os.environ:
del os.environ[v]
+def should_run_cross_arm_tests():
+ return shutil.which('arm-linux-gnueabihf-gcc') and not platform.machine().lower().startswith('arm')
+
+def should_run_cross_mingw_tests():
+ return shutil.which('x86_64-w64-mingw32-gcc') and not (is_windows() or is_cygwin())
+
if __name__ == '__main__':
unset_envs()
cases = ['InternalTests', 'AllPlatformTests', 'FailureTests', 'PythonTests']
if not is_windows():
cases += ['LinuxlikeTests']
- if should_run_linux_cross_tests():
- cases += ['LinuxArmCrossCompileTests']
+ if should_run_cross_arm_tests():
+ cases += ['LinuxCrossArmTests']
+ if should_run_cross_mingw_tests():
+ cases += ['LinuxCrossMingwTests']
if is_windows() or is_cygwin():
cases += ['WindowsTests']
diff --git a/test cases/unit/35 exe_wrapper behaviour/broken-cross.txt b/test cases/unit/35 exe_wrapper behaviour/broken-cross.txt
new file mode 100644
index 0000000..a5a3931
--- /dev/null
+++ b/test cases/unit/35 exe_wrapper behaviour/broken-cross.txt
@@ -0,0 +1,20 @@
+[binaries]
+c = '/usr/bin/x86_64-w64-mingw32-gcc'
+cpp = '/usr/bin/x86_64-w64-mingw32-g++'
+ar = '/usr/bin/x86_64-w64-mingw32-ar'
+strip = '/usr/bin/x86_64-w64-mingw32-strip'
+pkgconfig = '/usr/bin/x86_64-w64-mingw32-pkg-config'
+windres = '/usr/bin/x86_64-w64-mingw32-windres'
+exe_wrapper = 'broken'
+
+[properties]
+# Directory that contains 'bin', 'lib', etc
+root = '/usr/x86_64-w64-mingw32'
+# Directory that contains 'bin', 'lib', etc for the toolchain and system libraries
+sys_root = '/usr/x86_64-w64-mingw32/sys-root/mingw'
+
+[host_machine]
+system = 'windows'
+cpu_family = 'x86_64'
+cpu = 'x86_64'
+endian = 'little'
diff --git a/test cases/unit/35 exe_wrapper behaviour/meson.build b/test cases/unit/35 exe_wrapper behaviour/meson.build
new file mode 100644
index 0000000..16a44d5
--- /dev/null
+++ b/test cases/unit/35 exe_wrapper behaviour/meson.build
@@ -0,0 +1,19 @@
+project('exe wrapper behaviour', 'c')
+
+assert(meson.is_cross_build(), 'not setup as cross build')
+assert(meson.has_exe_wrapper(), 'exe wrapper not defined?')
+
+exe = executable('prog', 'prog.c')
+
+if get_option('custom-target')
+ custom_target('use-exe-wrapper',
+ build_by_default: true,
+ output: 'out.txt',
+ command: [exe, '@OUTPUT@'])
+endif
+
+test('test-prog', exe)
+
+if get_option('run-target')
+ run_target('run-prog', command : exe)
+endif
diff --git a/test cases/unit/35 exe_wrapper behaviour/meson_options.txt b/test cases/unit/35 exe_wrapper behaviour/meson_options.txt
new file mode 100644
index 0000000..e5645a0
--- /dev/null
+++ b/test cases/unit/35 exe_wrapper behaviour/meson_options.txt
@@ -0,0 +1,2 @@
+option('custom-target', type: 'boolean', value: true)
+option('run-target', type: 'boolean', value: true)
diff --git a/test cases/unit/35 exe_wrapper behaviour/prog.c b/test cases/unit/35 exe_wrapper behaviour/prog.c
new file mode 100644
index 0000000..3213780
--- /dev/null
+++ b/test cases/unit/35 exe_wrapper behaviour/prog.c
@@ -0,0 +1,17 @@
+#include <stdio.h>
+
+int main (int argc, char * argv[])
+{
+ const char *out = "SUCCESS!";
+
+ if (argc != 2) {
+ printf ("%s\n", out);
+ } else {
+ int ret;
+ FILE *f = fopen (argv[1], "w");
+ ret = fwrite (out, sizeof (out), 1, f);
+ if (ret != 1)
+ return -1;
+ }
+ return 0;
+}