aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2017-02-19 12:37:56 -0500
committerGitHub <noreply@github.com>2017-02-19 12:37:56 -0500
commit0cf18eb3bc6f872324971610f92d54d63049b9c9 (patch)
tree002840679e8cb2d179adb156c3c92f69bca3ae00
parentaba099a4910c2e578c0aefbd20bda666b6a6856e (diff)
parentf23a4a8b27b2a2e46185869bb736b1ab843cdcf3 (diff)
downloadmeson-0cf18eb3bc6f872324971610f92d54d63049b9c9.zip
meson-0cf18eb3bc6f872324971610f92d54d63049b9c9.tar.gz
meson-0cf18eb3bc6f872324971610f92d54d63049b9c9.tar.bz2
Merge pull request #1356 from centricular/cross-platform-unit-tests
Run unit tests on more platforms, and more
-rw-r--r--mesonbuild/backend/backends.py14
-rw-r--r--mesonbuild/backend/ninjabackend.py14
-rw-r--r--mesonbuild/backend/vs2010backend.py2
-rw-r--r--mesonbuild/dependencies.py92
-rw-r--r--mesonbuild/interpreter.py28
-rw-r--r--mesonbuild/mintro.py21
-rw-r--r--mesonbuild/modules/qt4.py6
-rw-r--r--mesonbuild/modules/qt5.py6
-rw-r--r--mesonbuild/modules/rpm.py9
-rwxr-xr-xrun_tests.py10
-rwxr-xr-xrun_unittests.py612
-rw-r--r--test cases/common/105 find program path/meson.build21
-rw-r--r--test cases/common/119 pathjoin/meson.build9
-rw-r--r--test cases/common/3 static/libfile2.c3
-rw-r--r--test cases/common/3 static/meson.build3
-rw-r--r--test cases/common/3 static/meson_options.txt1
-rw-r--r--test cases/common/94 default options/meson.build1
-rw-r--r--test cases/windows/9 find program/meson.build8
-rw-r--r--test cases/windows/9 find program/test-script-ext.py3
19 files changed, 563 insertions, 300 deletions
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 46f8563..7372c4c 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -215,13 +215,13 @@ class Backend:
exe_data = os.path.join(self.environment.get_scratch_dir(), scratch_file)
with open(exe_data, 'wb') as f:
if isinstance(exe, dependencies.ExternalProgram):
- exe_fullpath = exe.fullpath
+ exe_cmd = exe.get_command()
exe_needs_wrapper = False
elif isinstance(exe, (build.BuildTarget, build.CustomTarget)):
- exe_fullpath = [self.get_target_filename_abs(exe)]
+ exe_cmd = [self.get_target_filename_abs(exe)]
exe_needs_wrapper = exe.is_cross
else:
- exe_fullpath = [exe]
+ exe_cmd = [exe]
exe_needs_wrapper = False
is_cross = exe_needs_wrapper and \
self.environment.is_cross_build() and \
@@ -235,7 +235,7 @@ class Backend:
extra_paths = self.determine_windows_extra_paths(exe)
else:
extra_paths = []
- es = ExecutableSerialisation(basename, exe_fullpath, cmd_args, env,
+ es = ExecutableSerialisation(basename, exe_cmd, cmd_args, env,
is_cross, exe_wrapper, workdir,
extra_paths, capture)
pickle.dump(es, f)
@@ -444,9 +444,9 @@ class Backend:
for t in tests:
exe = t.get_exe()
if isinstance(exe, dependencies.ExternalProgram):
- fname = exe.fullpath
+ cmd = exe.get_command()
else:
- fname = [os.path.join(self.environment.get_build_dir(), self.get_target_filename(t.get_exe()))]
+ cmd = [os.path.join(self.environment.get_build_dir(), self.get_target_filename(t.get_exe()))]
is_cross = self.environment.is_cross_build() and \
self.environment.cross_info.need_cross_compiler() and \
self.environment.cross_info.need_exe_wrapper()
@@ -471,7 +471,7 @@ class Backend:
cmd_args.append(self.get_target_filename(a))
else:
raise MesonException('Bad object in test command.')
- ts = TestSerialisation(t.get_name(), t.suite, fname, is_cross, exe_wrapper,
+ ts = TestSerialisation(t.get_name(), t.suite, cmd, is_cross, exe_wrapper,
t.is_parallel, cmd_args, t.env, t.should_fail,
t.timeout, t.workdir, extra_paths)
arr.append(ts)
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index a22e0ab..27e1e9a 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -30,9 +30,11 @@ from collections import OrderedDict
if mesonlib.is_windows():
quote_char = '"'
execute_wrapper = 'cmd /c'
+ rmfile_prefix = 'del /f /s /q {} &&'
else:
quote_char = "'"
execute_wrapper = ''
+ rmfile_prefix = 'rm -f {} &&'
def ninja_quote(text):
return text.replace(' ', '$ ').replace(':', '$:')
@@ -1238,10 +1240,16 @@ int dummy;
'''
else:
command_template = ' command = {executable} $LINK_ARGS {output_args} $in\n'
+ cmdlist = []
+ if isinstance(static_linker, compilers.ArLinker):
+ # `ar` has no options to overwrite archives. It always appends,
+ # which is never what we want. Delete an existing library first if
+ # it exists. https://github.com/mesonbuild/meson/issues/1355
+ cmdlist = [execute_wrapper, rmfile_prefix.format('$out')]
+ cmdlist += static_linker.get_exelist()
command = command_template.format(
- executable=' '.join(static_linker.get_exelist()),
- output_args=' '.join(static_linker.get_output_args('$out'))
- )
+ executable=' '.join(cmdlist),
+ output_args=' '.join(static_linker.get_output_args('$out')))
description = ' description = Static linking library $out\n\n'
outfile.write(rule)
outfile.write(command)
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index 666da7d..547889c 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -395,7 +395,7 @@ class Vs2010Backend(backends.Backend):
if isinstance(i, build.BuildTarget):
cmd.append(os.path.join(self.environment.get_build_dir(), self.get_target_filename(i)))
elif isinstance(i, dependencies.ExternalProgram):
- cmd += i.fullpath
+ cmd += i.get_command()
else:
cmd.append(i)
cmd_templ = '''"%s" ''' * len(cmd)
diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py
index 9525ffa..e4317f1 100644
--- a/mesonbuild/dependencies.py
+++ b/mesonbuild/dependencies.py
@@ -120,8 +120,8 @@ class PkgConfigDependency(Dependency):
if self.required:
raise DependencyException('Pkg-config binary missing from cross file')
else:
- potential_pkgbin = ExternalProgram(environment.cross_info.config['binaries'].get('pkgconfig', 'non_existing_binary'),
- silent=True)
+ pkgname = environment.cross_info.config['binaries']['pkgconfig']
+ potential_pkgbin = ExternalProgram(pkgname, silent=True)
if potential_pkgbin.found():
# FIXME, we should store all pkg-configs in ExternalPrograms.
# However that is too destabilizing a change to do just before release.
@@ -402,24 +402,28 @@ class WxDependency(Dependency):
return self.is_found
class ExternalProgram:
- windows_exts = ('exe', 'com', 'bat')
+ windows_exts = ('exe', 'msc', 'com', 'bat')
- def __init__(self, name, fullpath=None, silent=False, search_dir=None):
+ def __init__(self, name, command=None, silent=False, search_dir=None):
self.name = name
- if fullpath is not None:
- if not isinstance(fullpath, list):
- self.fullpath = [fullpath]
+ if command is not None:
+ if not isinstance(command, list):
+ self.command = [command]
else:
- self.fullpath = fullpath
+ self.command = command
else:
- self.fullpath = self._search(name, search_dir)
+ self.command = self._search(name, search_dir)
if not silent:
if self.found():
mlog.log('Program', mlog.bold(name), 'found:', mlog.green('YES'),
- '(%s)' % ' '.join(self.fullpath))
+ '(%s)' % ' '.join(self.command))
else:
mlog.log('Program', mlog.bold(name), 'found:', mlog.red('NO'))
+ def __repr__(self):
+ r = '<{} {!r} -> {!r}>'
+ return r.format(self.__class__.__name__, self.name, self.command)
+
@staticmethod
def _shebang_to_cmd(script):
"""
@@ -473,34 +477,63 @@ class ExternalProgram:
return self._shebang_to_cmd(trial)
def _search(self, name, search_dir):
+ '''
+ Search in the specified dir for the specified executable by name
+ and if not found search in PATH
+ '''
commands = self._search_dir(name, search_dir)
if commands:
return commands
# Do a standard search in PATH
- fullpath = shutil.which(name)
- if fullpath or not mesonlib.is_windows():
+ command = shutil.which(name)
+ if not mesonlib.is_windows():
# On UNIX-like platforms, the standard PATH search is enough
- return [fullpath]
- # On Windows, if name is an absolute path, we need the extension too
- for ext in self.windows_exts:
- fullpath = '{}.{}'.format(name, ext)
- if os.path.exists(fullpath):
- return [fullpath]
- # On Windows, interpreted scripts must have an extension otherwise they
- # cannot be found by a standard PATH search. So we do a custom search
- # where we manually search for a script with a shebang in PATH.
- search_dirs = os.environ.get('PATH', '').split(';')
- for search_dir in search_dirs:
- commands = self._search_dir(name, search_dir)
+ return [command]
+ # HERE BEGINS THE TERROR OF WINDOWS
+ if command:
+ # On Windows, even if the PATH search returned a full path, we can't be
+ # sure that it can be run directly if it's not a native executable.
+ # For instance, interpreted scripts sometimes need to be run explicitly
+ # with an interpreter if the file association is not done properly.
+ name_ext = os.path.splitext(command)[1]
+ if name_ext[1:].lower() in self.windows_exts:
+ # Good, it can be directly executed
+ return [command]
+ # Try to extract the interpreter from the shebang
+ commands = self._shebang_to_cmd(command)
if commands:
return commands
+ else:
+ # Maybe the name is an absolute path to a native Windows
+ # executable, but without the extension. This is technically wrong,
+ # but many people do it because it works in the MinGW shell.
+ if os.path.isabs(name):
+ for ext in self.windows_exts:
+ command = '{}.{}'.format(name, ext)
+ if os.path.exists(command):
+ return [command]
+ # On Windows, interpreted scripts must have an extension otherwise they
+ # cannot be found by a standard PATH search. So we do a custom search
+ # where we manually search for a script with a shebang in PATH.
+ search_dirs = os.environ.get('PATH', '').split(';')
+ for search_dir in search_dirs:
+ commands = self._search_dir(name, search_dir)
+ if commands:
+ return commands
return [None]
def found(self):
- return self.fullpath[0] is not None
+ return self.command[0] is not None
def get_command(self):
- return self.fullpath[:]
+ return self.command[:]
+
+ def get_path(self):
+ # Assume that the last element is the full path to the script
+ # If it's not a script, this will be an array of length 1
+ if self.found():
+ return self.command[-1]
+ return None
def get_name(self):
return self.name
@@ -531,6 +564,9 @@ class ExternalLibrary(Dependency):
def found(self):
return self.is_found
+ def get_name(self):
+ return self.name
+
def get_link_args(self):
return self.link_args
@@ -994,7 +1030,7 @@ class QtBaseDependency(Dependency):
if not self.qmake.found():
continue
# Check that the qmake is for qt5
- pc, stdo = Popen_safe(self.qmake.fullpath + ['-v'])[0:2]
+ pc, stdo = Popen_safe(self.qmake.get_command() + ['-v'])[0:2]
if pc.returncode != 0:
continue
if not 'Qt version ' + self.qtver in stdo:
@@ -1007,7 +1043,7 @@ class QtBaseDependency(Dependency):
return
self.version = re.search(self.qtver + '(\.\d+)+', stdo).group(0)
# Query library path, header path, and binary path
- stdo = Popen_safe(self.qmake.fullpath + ['-query'])[1]
+ stdo = Popen_safe(self.qmake.get_command() + ['-query'])[1]
qvars = {}
for line in stdo.split('\n'):
line = line.strip()
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 224f98e..f6065d5 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -285,16 +285,16 @@ class ExternalProgramHolder(InterpreterObject):
return self.found()
def path_method(self, args, kwargs):
- return self.get_command()
+ return self.held_object.get_path()
def found(self):
return self.held_object.found()
def get_command(self):
- return self.held_object.fullpath
+ return self.held_object.get_command()
def get_name(self):
- return self.held_object.name
+ return self.held_object.get_name()
class ExternalLibraryHolder(InterpreterObject):
def __init__(self, el):
@@ -308,9 +308,6 @@ class ExternalLibraryHolder(InterpreterObject):
def found_method(self, args, kwargs):
return self.found()
- def get_filename(self):
- return self.held_object.fullpath
-
def get_name(self):
return self.held_object.name
@@ -1424,7 +1421,8 @@ class Interpreter(InterpreterBase):
elif isinstance(cmd, str):
cmd = [cmd]
else:
- raise InterpreterException('First argument is of incorrect type.')
+ raise InterpreterException('First argument should be find_program() '
+ 'or string, not {!r}'.format(cmd))
expanded_args = []
for a in mesonlib.flatten(cargs):
if isinstance(a, str):
@@ -1759,7 +1757,6 @@ class Interpreter(InterpreterBase):
break
self.coredata.base_options[optname] = oobj
- @stringArgs
def func_find_program(self, node, args, kwargs):
if len(args) == 0:
raise InterpreterException('No program name specified.')
@@ -1769,8 +1766,21 @@ class Interpreter(InterpreterBase):
# 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)
+ source_dir = os.path.join(self.environment.get_source_dir(), self.subdir)
for exename in args:
+ if isinstance(exename, mesonlib.File):
+ if exename.is_built:
+ search_dir = os.path.join(self.environment.get_build_dir(),
+ exename.subdir)
+ else:
+ search_dir = os.path.join(self.environment.get_source_dir(),
+ exename.subdir)
+ exename = exename.fname
+ elif isinstance(exename, str):
+ search_dir = source_dir
+ else:
+ raise InvalidArguments('find_program only accepts strings and '
+ 'files, not {!r}'.format(exename))
extprog = dependencies.ExternalProgram(exename, search_dir=search_dir)
progobj = ExternalProgramHolder(extprog)
if progobj.found():
diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py
index e30500f..6eab76e 100644
--- a/mesonbuild/mintro.py
+++ b/mesonbuild/mintro.py
@@ -23,6 +23,7 @@ import json, pickle
from . import coredata, build
import argparse
import sys, os
+import pathlib
parser = argparse.ArgumentParser()
parser.add_argument('--targets', action='store_true', dest='list_targets', default=False,
@@ -56,7 +57,9 @@ def determine_installed_path(target, installdata):
fname = i[0]
outdir = i[1]
outname = os.path.join(installdata.prefix, outdir, os.path.split(fname)[-1])
- return outname
+ # Normalize the path by using os.path.sep consistently, etc.
+ # Does not change the effective path.
+ return str(pathlib.PurePath(outname))
def list_installed(installdata):
@@ -111,23 +114,11 @@ def list_target_files(target_name, coredata, builddata):
print(json.dumps(sources))
def list_buildoptions(coredata, builddata):
- buildtype = {'choices': ['plain', 'debug', 'debugoptimized', 'release', 'minsize'],
- 'type': 'combo',
- 'value': coredata.get_builtin_option('buildtype'),
- 'description': 'Build type',
- 'name': 'type'}
- strip = {'value': coredata.get_builtin_option('strip'),
- 'type': 'boolean',
- 'description': 'Strip on install',
- 'name': 'strip'}
- unity = {'value': coredata.get_builtin_option('unity'),
- 'type': 'boolean',
- 'description': 'Unity build',
- 'name': 'unity'}
- optlist = [buildtype, strip, unity]
+ optlist = []
add_keys(optlist, coredata.user_options)
add_keys(optlist, coredata.compiler_options)
add_keys(optlist, coredata.base_options)
+ add_keys(optlist, coredata.builtins)
print(json.dumps(optlist))
def add_keys(optlist, options):
diff --git a/mesonbuild/modules/qt4.py b/mesonbuild/modules/qt4.py
index 33c9f80..7146739 100644
--- a/mesonbuild/modules/qt4.py
+++ b/mesonbuild/modules/qt4.py
@@ -48,7 +48,7 @@ class Qt4Module(ExtensionModule):
raise MesonException('Moc preprocessor is not for Qt 4. Output:\n%s\n%s' %
(stdout, stderr))
mlog.log(' moc:', mlog.green('YES'), '(%s, %s)' %
- (' '.join(self.moc.fullpath), moc_ver.split()[-1]))
+ (self.moc.get_path(), moc_ver.split()[-1]))
else:
mlog.log(' moc:', mlog.red('NO'))
if self.uic.found():
@@ -61,7 +61,7 @@ class Qt4Module(ExtensionModule):
raise MesonException('Uic compiler is not for Qt4. Output:\n%s\n%s' %
(stdout, stderr))
mlog.log(' uic:', mlog.green('YES'), '(%s, %s)' %
- (' '.join(self.uic.fullpath), uic_ver.split()[-1]))
+ (self.uic.get_path(), uic_ver.split()[-1]))
else:
mlog.log(' uic:', mlog.red('NO'))
if self.rcc.found():
@@ -74,7 +74,7 @@ class Qt4Module(ExtensionModule):
raise MesonException('Rcc compiler is not for Qt 4. Output:\n%s\n%s' %
(stdout, stderr))
mlog.log(' rcc:', mlog.green('YES'), '(%s, %s)'
- % (' '.join(self.rcc.fullpath), rcc_ver.split()[-1]))
+ % (self.rcc.get_path(), rcc_ver.split()[-1]))
else:
mlog.log(' rcc:', mlog.red('NO'))
self.tools_detected = True
diff --git a/mesonbuild/modules/qt5.py b/mesonbuild/modules/qt5.py
index b4f1475..2a87a80 100644
--- a/mesonbuild/modules/qt5.py
+++ b/mesonbuild/modules/qt5.py
@@ -50,7 +50,7 @@ class Qt5Module(ExtensionModule):
raise MesonException('Moc preprocessor is not for Qt 5. Output:\n%s\n%s' %
(stdout, stderr))
mlog.log(' moc:', mlog.green('YES'), '(%s, %s)' %
- (' '.join(self.moc.fullpath), moc_ver.split()[-1]))
+ (self.moc.get_path(), moc_ver.split()[-1]))
else:
mlog.log(' moc:', mlog.red('NO'))
if self.uic.found():
@@ -65,7 +65,7 @@ class Qt5Module(ExtensionModule):
raise MesonException('Uic compiler is not for Qt 5. Output:\n%s\n%s' %
(stdout, stderr))
mlog.log(' uic:', mlog.green('YES'), '(%s, %s)' %
- (' '.join(self.uic.fullpath), uic_ver.split()[-1]))
+ (self.uic.get_path(), uic_ver.split()[-1]))
else:
mlog.log(' uic:', mlog.red('NO'))
if self.rcc.found():
@@ -80,7 +80,7 @@ class Qt5Module(ExtensionModule):
raise MesonException('Rcc compiler is not for Qt 5. Output:\n%s\n%s' %
(stdout, stderr))
mlog.log(' rcc:', mlog.green('YES'), '(%s, %s)'
- % (' '.join(self.rcc.fullpath), rcc_ver.split()[-1]))
+ % (self.rcc.get_path(), rcc_ver.split()[-1]))
else:
mlog.log(' rcc:', mlog.red('NO'))
self.tools_detected = True
diff --git a/mesonbuild/modules/rpm.py b/mesonbuild/modules/rpm.py
index bd8a3c4..bbfeaa0 100644
--- a/mesonbuild/modules/rpm.py
+++ b/mesonbuild/modules/rpm.py
@@ -98,17 +98,18 @@ class RPMModule(ExtensionModule):
for dep in state.environment.coredata.deps:
fn.write('BuildRequires: pkgconfig(%s)\n' % dep[0])
for lib in state.environment.coredata.ext_libs.values():
- fn.write('BuildRequires: %s # FIXME\n' % lib.fullpath)
- mlog.warning('replace', mlog.bold(lib.fullpath), 'with real package.',
+ name = lib.get_name()
+ fn.write('BuildRequires: {} # FIXME\n'.format(name))
+ mlog.warning('replace', mlog.bold(name), 'with the real package.',
'You can use following command to find package which '
'contains this lib:',
- mlog.bold('dnf provides %s' % lib.fullpath))
+ mlog.bold("dnf provides '*/lib{}.so".format(name))
for prog in state.environment.coredata.ext_progs.values():
if not prog.found():
fn.write('BuildRequires: %%{_bindir}/%s # FIXME\n' %
prog.get_name())
else:
- fn.write('BuildRequires: %s\n' % ' '.join(prog.fullpath))
+ fn.write('BuildRequires: {}\n'.format(prog.get_path()))
fn.write('BuildRequires: meson\n')
fn.write('\n')
fn.write('%description\n')
diff --git a/run_tests.py b/run_tests.py
index 005717e..f2038e4 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright 2012-2016 The Meson development team
+# Copyright 2012-2017 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.
@@ -21,10 +21,12 @@ from mesonbuild import mesonlib
if __name__ == '__main__':
returncode = 0
print('Running unittests.\n')
+ units = ['InternalTests', 'AllPlatformTests']
if mesonlib.is_linux():
- returncode += subprocess.call([sys.executable, 'run_unittests.py', '-v'])
- else:
- returncode += subprocess.call([sys.executable, 'run_unittests.py', '-v', 'InternalTests'])
+ units += ['LinuxlikeTests']
+ elif mesonlib.is_windows():
+ units += ['WindowsTests']
+ returncode += subprocess.call([sys.executable, 'run_unittests.py', '-v'] + units)
# Ubuntu packages do not have a binary without -6 suffix.
if shutil.which('arm-linux-gnueabihf-gcc-6') and not platform.machine().startswith('arm'):
print('Running cross compilation tests.\n')
diff --git a/run_unittests.py b/run_unittests.py
index aed1412..95e52e3 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright 2016 The Meson development team
+# Copyright 2016-2017 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.
@@ -18,14 +18,20 @@ import shlex
import subprocess
import re, json
import tempfile
-import pathlib
import unittest, os, sys, shutil, time
from glob import glob
+from pathlib import PurePath
import mesonbuild.compilers
import mesonbuild.environment
import mesonbuild.mesonlib
+from mesonbuild.mesonlib import is_windows
from mesonbuild.environment import detect_ninja, Environment
-from mesonbuild.dependencies import PkgConfigDependency
+from mesonbuild.dependencies import PkgConfigDependency, ExternalProgram
+
+if is_windows():
+ exe_suffix = '.exe'
+else:
+ exe_suffix = ''
def get_soname(fname):
# HACK, fix to not use shell.
@@ -172,15 +178,18 @@ class InternalTests(unittest.TestCase):
self.assertEqual(commonpath(['blam', 'bin']), '')
prefix = '/some/path/to/prefix'
libdir = '/some/path/to/prefix/libdir'
- self.assertEqual(commonpath([prefix, libdir]), str(pathlib.PurePath(prefix)))
+ self.assertEqual(commonpath([prefix, libdir]), str(PurePath(prefix)))
-class LinuxlikeTests(unittest.TestCase):
+class BasePlatformTests(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.src_root = src_root
+ # In case the directory is inside a symlinked directory, find the real
+ # path otherwise we might not find the srcdir from inside the builddir.
+ self.builddir = os.path.realpath(tempfile.mkdtemp())
self.logdir = os.path.join(self.builddir, 'meson-logs')
self.prefix = '/usr'
self.libdir = os.path.join(self.prefix, 'lib')
@@ -194,7 +203,6 @@ class LinuxlikeTests(unittest.TestCase):
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()
def tearDown(self):
@@ -203,20 +211,26 @@ class LinuxlikeTests(unittest.TestCase):
super().tearDown()
def _run(self, command):
- self.output += subprocess.check_output(command, stderr=subprocess.STDOUT,
- env=os.environ.copy())
+ output = subprocess.check_output(command, stderr=subprocess.STDOUT,
+ env=os.environ.copy(),
+ universal_newlines=True)
+ print(output)
+ return output
- def init(self, srcdir, extra_args=None):
+ def init(self, srcdir, extra_args=None, default_args=True):
if extra_args is None:
extra_args = []
- args = [srcdir, self.builddir,
- '--prefix', self.prefix,
- '--libdir', self.libdir]
+ args = [srcdir, self.builddir]
+ if default_args:
+ args += ['--prefix', self.prefix,
+ '--libdir', self.libdir]
self._run(self.meson_command + args + extra_args)
self.privatedir = os.path.join(self.builddir, 'meson-private')
- def build(self):
- self._run(self.ninja_command)
+ def build(self, extra_args=None):
+ if extra_args is None:
+ extra_args = []
+ self._run(self.ninja_command + extra_args)
def run_tests(self):
self._run(self.ninja_command + ['test'])
@@ -229,9 +243,18 @@ class LinuxlikeTests(unittest.TestCase):
self._run(self.ninja_command + ['uninstall'])
def run_target(self, target):
- self.output += subprocess.check_output(self.ninja_command + [target])
+ output = subprocess.check_output(self.ninja_command + [target],
+ stderr=subprocess.STDOUT,
+ universal_newlines=True)
+ print(output)
+ return output
- def setconf(self, arg):
+ def setconf(self, arg, will_build=True):
+ # This is needed to increase the difference between build.ninja's
+ # timestamp and coredata.dat's timestamp due to a Ninja bug.
+ # https://github.com/ninja-build/ninja/issues/371
+ if will_build:
+ time.sleep(1)
self._run(self.mconf_command + [arg, self.builddir])
def wipe(self):
@@ -260,6 +283,336 @@ class LinuxlikeTests(unittest.TestCase):
universal_newlines=True)
return json.loads(out)
+ def assertPathEqual(self, path1, path2):
+ '''
+ Handles a lot of platform-specific quirks related to paths such as
+ separator, case-sensitivity, etc.
+ '''
+ self.assertEqual(PurePath(path1), PurePath(path2))
+
+ def assertPathBasenameEqual(self, path, basename):
+ msg = '{!r} does not end with {!r}'.format(path, basename)
+ # We cannot use os.path.basename because it returns '' when the path
+ # ends with '/' for some silly reason. This is not how the UNIX utility
+ # `basename` works.
+ path_basename = PurePath(path).parts[-1]
+ self.assertEqual(PurePath(path_basename), PurePath(basename), msg)
+
+
+class AllPlatformTests(BasePlatformTests):
+ '''
+ Tests that should run on all platforms
+ '''
+ def test_default_options_prefix(self):
+ '''
+ Tests that setting a prefix in default_options in project() works.
+ Can't be an ordinary test because we pass --prefix to meson there.
+ https://github.com/mesonbuild/meson/issues/1349
+ '''
+ testdir = os.path.join(self.common_test_dir, '94 default options')
+ self.init(testdir, default_args=False)
+ opts = self.introspect('--buildoptions')
+ for opt in opts:
+ if opt['name'] == 'prefix':
+ prefix = opt['value']
+ self.assertEqual(prefix, '/absoluteprefix')
+
+ def test_absolute_prefix_libdir(self):
+ '''
+ Tests that setting absolute paths for --prefix and --libdir work. Can't
+ be an ordinary test because these are set via the command-line.
+ https://github.com/mesonbuild/meson/issues/1341
+ https://github.com/mesonbuild/meson/issues/1345
+ '''
+ testdir = os.path.join(self.common_test_dir, '94 default options')
+ prefix = '/someabs'
+ libdir = 'libdir'
+ extra_args = ['--prefix=' + prefix,
+ # This can just be a relative path, but we want to test
+ # that passing this as an absolute path also works
+ '--libdir=' + prefix + '/' + libdir]
+ self.init(testdir, extra_args, default_args=False)
+ opts = self.introspect('--buildoptions')
+ for opt in opts:
+ if opt['name'] == 'prefix':
+ self.assertEqual(prefix, opt['value'])
+ elif opt['name'] == 'libdir':
+ self.assertEqual(libdir, opt['value'])
+
+ def test_libdir_must_be_inside_prefix(self):
+ '''
+ Tests that libdir is forced to be inside prefix no matter how it is set.
+ Must be a unit test for obvious reasons.
+ '''
+ testdir = os.path.join(self.common_test_dir, '1 trivial')
+ # libdir being inside prefix is ok
+ args = ['--prefix', '/opt', '--libdir', '/opt/lib32']
+ self.init(testdir, args)
+ self.wipe()
+ # libdir not being inside prefix is not ok
+ args = ['--prefix', '/usr', '--libdir', '/opt/lib32']
+ self.assertRaises(subprocess.CalledProcessError, self.init, testdir, args)
+ self.wipe()
+ # libdir must be inside prefix even when set via mesonconf
+ self.init(testdir)
+ self.assertRaises(subprocess.CalledProcessError, self.setconf, '-Dlibdir=/opt', False)
+
+ def test_static_library_overwrite(self):
+ '''
+ Tests that static libraries are never appended to, always overwritten.
+ Has to be a unit test because this involves building a project,
+ reconfiguring, and building it again so that `ar` is run twice on the
+ same static library.
+ https://github.com/mesonbuild/meson/issues/1355
+ '''
+ testdir = os.path.join(self.common_test_dir, '3 static')
+ env = Environment(testdir, self.builddir, self.meson_command,
+ get_fake_options(self.prefix), [])
+ cc = env.detect_c_compiler(False)
+ static_linker = env.detect_static_linker(cc)
+ if not isinstance(static_linker, mesonbuild.compilers.ArLinker):
+ raise unittest.SkipTest('static linker is not `ar`')
+ # Configure
+ self.init(testdir)
+ # Get name of static library
+ targets = self.introspect('--targets')
+ self.assertEqual(len(targets), 1)
+ libname = targets[0]['filename']
+ # Build and get contents of static library
+ self.build()
+ before = self._run(['ar', 't', os.path.join(self.builddir, libname)]).split()
+ # Filter out non-object-file contents
+ before = [f for f in before if f.endswith(('.o', '.obj'))]
+ # Static library should contain only one object
+ self.assertEqual(len(before), 1, msg=before)
+ # Change the source to be built into the static library
+ self.setconf('-Dsource=libfile2.c')
+ self.build()
+ after = self._run(['ar', 't', os.path.join(self.builddir, libname)]).split()
+ # Filter out non-object-file contents
+ after = [f for f in after if f.endswith(('.o', '.obj'))]
+ # Static library should contain only one object
+ self.assertEqual(len(after), 1, msg=after)
+ # and the object must have changed
+ self.assertNotEqual(before, after)
+
+ 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()
+ # Rules will get written out in this order
+ self.assertTrue(compdb[0]['file'].endswith("libfile.c"))
+ self.assertTrue(compdb[1]['file'].endswith("libfile2.c"))
+ self.assertTrue(compdb[2]['file'].endswith("libfile3.c"))
+ self.assertTrue(compdb[3]['file'].endswith("libfile4.c"))
+ # FIXME: We don't have access to the linker command
+
+ 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_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')
+ if intro[0]['type'] == 'executable':
+ intro = intro[::-1]
+ self.assertPathEqual(intro[0]['install_filename'], '/usr/lib/libstat.a')
+ self.assertPathEqual(intro[1]['install_filename'], '/usr/bin/prog' + exe_suffix)
+
+ def test_uninstall(self):
+ exename = os.path.join(self.installdir, 'usr/bin/prog' + exe_suffix)
+ testdir = os.path.join(self.common_test_dir, '8 install')
+ self.init(testdir)
+ self.assertFalse(os.path.exists(exename))
+ self.install()
+ self.assertTrue(os.path.exists(exename))
+ self.uninstall()
+ self.assertFalse(os.path.exists(exename))
+
+ def test_testsetups(self):
+ if not shutil.which('valgrind'):
+ raise unittest.SkipTest('Valgrind not installed.')
+ testdir = os.path.join(self.unit_test_dir, '2 testsetups')
+ self.init(testdir)
+ self.build()
+ self.run_tests()
+ with open(os.path.join(self.logdir, 'testlog.txt')) as f:
+ basic_log = f.read()
+ self.assertRaises(subprocess.CalledProcessError,
+ self._run, self.mtest_command + ['--setup=valgrind'])
+ with open(os.path.join(self.logdir, 'testlog-valgrind.txt')) as f:
+ vg_log = f.read()
+ self.assertFalse('TEST_ENV is set' in basic_log)
+ self.assertFalse('Memcheck' in basic_log)
+ self.assertTrue('TEST_ENV is set' in vg_log)
+ self.assertTrue('Memcheck' in vg_log)
+
+ def assertFailedTestCount(self, failure_count, command):
+ try:
+ self._run(command)
+ self.assertEqual(0, failure_count, 'Expected %d tests to fail.' % failure_count)
+ except subprocess.CalledProcessError as e:
+ self.assertEqual(e.returncode, failure_count)
+
+ def test_suite_selection(self):
+ testdir = os.path.join(self.unit_test_dir, '4 suite selection')
+ self.init(testdir)
+ self.build()
+
+ self.assertFailedTestCount(3, self.mtest_command)
+
+ self.assertFailedTestCount(0, self.mtest_command + ['--suite', ':success'])
+ self.assertFailedTestCount(3, self.mtest_command + ['--suite', ':fail'])
+ self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', ':success'])
+ self.assertFailedTestCount(0, self.mtest_command + ['--no-suite', ':fail'])
+
+ self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'mainprj'])
+ self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjsucc'])
+ self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjfail'])
+ self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjmix'])
+ self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'mainprj'])
+ self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjsucc'])
+ self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjfail'])
+ self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjmix'])
+
+ self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'mainprj:fail'])
+ self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'mainprj:success'])
+ self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'mainprj:fail'])
+ self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'mainprj:success'])
+
+ self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjfail:fail'])
+ self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjfail:success'])
+ self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjfail:fail'])
+ self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjfail:success'])
+
+ self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjsucc:fail'])
+ self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjsucc:success'])
+ self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjsucc:fail'])
+ self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjsucc:success'])
+
+ self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjmix:fail'])
+ self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjmix:success'])
+ self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjmix:fail'])
+ self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjmix:success'])
+
+ self.assertFailedTestCount(2, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix:fail'])
+ self.assertFailedTestCount(3, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj'])
+ self.assertFailedTestCount(2, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj', '--no-suite', 'subprjmix:fail'])
+ self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj', '--no-suite', 'subprjmix:fail', 'mainprj-failing_test'])
+
+ self.assertFailedTestCount(1, self.mtest_command + ['--no-suite', 'subprjfail:fail', '--no-suite', 'subprjmix:fail'])
+
+ def test_build_by_default(self):
+ testdir = os.path.join(self.common_test_dir, '137 build by default')
+ self.init(testdir)
+ self.build()
+ genfile = os.path.join(self.builddir, 'generated.dat')
+ exe = os.path.join(self.builddir, 'fooprog' + exe_suffix)
+ self.assertTrue(os.path.exists(genfile))
+ self.assertFalse(os.path.exists(exe))
+ self._run(self.ninja_command + ['fooprog' + exe_suffix])
+ self.assertTrue(os.path.exists(exe))
+
+ def test_internal_include_order(self):
+ testdir = os.path.join(self.common_test_dir, '138 include order')
+ self.init(testdir)
+ for cmd in self.get_compdb():
+ if cmd['file'].endswith('/main.c'):
+ cmd = cmd['command']
+ break
+ else:
+ raise Exception('Could not find main.c command')
+ if cmd.endswith('.rsp'):
+ # Pretend to build so that the rsp files are generated
+ self.build(['-d', 'keeprsp', '-n'])
+ # Extract the actual command from the rsp file
+ rsp = os.path.join(self.builddir, cmd.split('cl @')[1])
+ with open(rsp, 'r', encoding='utf-8') as f:
+ cmd = f.read()
+ incs = [a for a in shlex.split(cmd) if a.startswith("-I")]
+ self.assertEqual(len(incs), 8)
+ # target private dir
+ self.assertPathEqual(incs[0], "-Isub4/someexe@exe")
+ # target build subdir
+ self.assertPathEqual(incs[1], "-Isub4")
+ # target source subdir
+ self.assertPathBasenameEqual(incs[2], 'sub4')
+ # include paths added via per-target c_args: ['-I'...]
+ self.assertPathBasenameEqual(incs[3], 'sub3')
+ # target include_directories: build dir
+ self.assertPathEqual(incs[4], "-Isub2")
+ # target include_directories: source dir
+ self.assertPathBasenameEqual(incs[5], 'sub2')
+ # target internal dependency include_directories: build dir
+ self.assertPathEqual(incs[6], "-Isub1")
+ # target internal dependency include_directories: source dir
+ self.assertPathBasenameEqual(incs[7], 'sub1')
+
+
+class WindowsTests(BasePlatformTests):
+ '''
+ Tests that should run on Cygwin, MinGW, and MSVC
+ '''
+ def setUp(self):
+ super().setUp()
+ self.platform_test_dir = os.path.join(self.src_root, 'test cases/windows')
+
+ def test_find_program(self):
+ '''
+ Test that Windows-specific edge-cases in find_program are functioning
+ correctly. Cannot be an ordinary test because it involves manipulating
+ PATH to point to a directory with Python scripts.
+ '''
+ testdir = os.path.join(self.platform_test_dir, '9 find program')
+ # Find `cmd` and `cmd.exe`
+ prog1 = ExternalProgram('cmd')
+ self.assertTrue(prog1.found(), msg='cmd not found')
+ prog2 = ExternalProgram('cmd.exe')
+ self.assertTrue(prog2.found(), msg='cmd.exe not found')
+ self.assertPathEqual(prog1.get_path(), prog2.get_path())
+ # Find cmd with an absolute path that's missing the extension
+ cmd_path = prog2.get_path()[:-4]
+ prog = ExternalProgram(cmd_path)
+ self.assertTrue(prog.found(), msg='{!r} not found'.format(cmd_path))
+ # Finding a script with no extension inside a directory works
+ prog = ExternalProgram(os.path.join(testdir, 'test-script'))
+ self.assertTrue(prog.found(), msg='test-script not found')
+ # Finding a script with an extension inside a directory works
+ prog = ExternalProgram(os.path.join(testdir, 'test-script-ext.py'))
+ self.assertTrue(prog.found(), msg='test-script-ext.py not found')
+ # Finding a script in PATH w/o extension works and adds the interpreter
+ os.environ['PATH'] += os.pathsep + testdir
+ prog = ExternalProgram('test-script-ext')
+ self.assertTrue(prog.found(), msg='test-script-ext not found in PATH')
+ self.assertPathEqual(prog.get_command()[0], sys.executable)
+ self.assertPathBasenameEqual(prog.get_path(), 'test-script-ext.py')
+ # Finding a script in PATH with extension works and adds the interpreter
+ prog = ExternalProgram('test-script-ext.py')
+ self.assertTrue(prog.found(), msg='test-script-ext.py not found in PATH')
+ self.assertPathEqual(prog.get_command()[0], sys.executable)
+ self.assertPathBasenameEqual(prog.get_path(), 'test-script-ext.py')
+
+
+class LinuxlikeTests(BasePlatformTests):
+ '''
+ Tests that should run on Linux and *BSD
+ '''
def test_basic_soname(self):
'''
Test that the soname is set correctly for shared libraries. This can't
@@ -298,10 +651,6 @@ class LinuxlikeTests(unittest.TestCase):
self.init(testdir)
compdb = self.get_compdb()
self.assertIn('-fPIC', compdb[0]['command'])
- # This is needed to increase the difference between build.ninja's
- # timestamp and coredata.dat's timestamp due to a Ninja bug.
- # https://github.com/ninja-build/ninja/issues/371
- time.sleep(1)
self.setconf('-Db_staticpic=false')
# Regenerate build
self.build()
@@ -358,45 +707,6 @@ class LinuxlikeTests(unittest.TestCase):
self.assertIn("'-Werror'", vala_command)
self.assertIn("'-Werror'", 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()
- # Rules will get written out in this order
- self.assertTrue(compdb[0]['file'].endswith("libfile.c"))
- self.assertTrue(compdb[1]['file'].endswith("libfile2.c"))
- self.assertTrue(compdb[2]['file'].endswith("libfile3.c"))
- self.assertTrue(compdb[3]['file'].endswith("libfile4.c"))
- # 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')
- if intro[0]['type'] == 'executable':
- intro = intro[::-1]
- self.assertEqual(intro[0]['install_filename'], '/usr/lib/libstat.a')
- self.assertEqual(intro[1]['install_filename'], '/usr/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):
'''
Test that qt5 detection with qmake works. This can't be an ordinary
@@ -500,16 +810,6 @@ class LinuxlikeTests(unittest.TestCase):
Oargs = [arg for arg in cmd if arg.startswith('-O')]
self.assertEqual(Oargs, [Oflag, '-O0'])
- def test_uninstall(self):
- exename = os.path.join(self.installdir, 'usr/bin/prog')
- testdir = os.path.join(self.common_test_dir, '8 install')
- self.init(testdir)
- self.assertFalse(os.path.exists(exename))
- self.install()
- self.assertTrue(os.path.exists(exename))
- self.uninstall()
- self.assertFalse(os.path.exists(exename))
-
def test_custom_target_exe_data_deterministic(self):
testdir = os.path.join(self.common_test_dir, '117 custom target capture')
self.init(testdir)
@@ -519,79 +819,6 @@ class LinuxlikeTests(unittest.TestCase):
meson_exe_dat2 = glob(os.path.join(self.privatedir, 'meson_exe*.dat'))
self.assertListEqual(meson_exe_dat1, meson_exe_dat2)
- def test_testsetups(self):
- if not shutil.which('valgrind'):
- raise unittest.SkipTest('Valgrind not installed.')
- testdir = os.path.join(self.unit_test_dir, '2 testsetups')
- self.init(testdir)
- self.build()
- self.run_tests()
- with open(os.path.join(self.logdir, 'testlog.txt')) as f:
- basic_log = f.read()
- self.assertRaises(subprocess.CalledProcessError,
- self._run, self.mtest_command + ['--setup=valgrind'])
- with open(os.path.join(self.logdir, 'testlog-valgrind.txt')) as f:
- vg_log = f.read()
- self.assertFalse('TEST_ENV is set' in basic_log)
- self.assertFalse('Memcheck' in basic_log)
- self.assertTrue('TEST_ENV is set' in vg_log)
- self.assertTrue('Memcheck' in vg_log)
-
- def assertFailedTestCount(self, failure_count, command):
- try:
- self._run(command)
- self.assertEqual(0, failure_count, 'Expected %d tests to fail.' % failure_count)
- except subprocess.CalledProcessError as e:
- self.assertEqual(e.returncode, failure_count)
-
- def test_suite_selection(self):
- testdir = os.path.join(self.unit_test_dir, '4 suite selection')
- self.init(testdir)
- self.build()
-
- self.assertFailedTestCount(3, self.mtest_command)
-
- self.assertFailedTestCount(0, self.mtest_command + ['--suite', ':success'])
- self.assertFailedTestCount(3, self.mtest_command + ['--suite', ':fail'])
- self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', ':success'])
- self.assertFailedTestCount(0, self.mtest_command + ['--no-suite', ':fail'])
-
- self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'mainprj'])
- self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjsucc'])
- self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjfail'])
- self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjmix'])
- self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'mainprj'])
- self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjsucc'])
- self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjfail'])
- self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjmix'])
-
- self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'mainprj:fail'])
- self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'mainprj:success'])
- self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'mainprj:fail'])
- self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'mainprj:success'])
-
- self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjfail:fail'])
- self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjfail:success'])
- self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjfail:fail'])
- self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjfail:success'])
-
- self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjsucc:fail'])
- self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjsucc:success'])
- self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjsucc:fail'])
- self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjsucc:success'])
-
- self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjmix:fail'])
- self.assertFailedTestCount(0, self.mtest_command + ['--suite', 'subprjmix:success'])
- self.assertFailedTestCount(2, self.mtest_command + ['--no-suite', 'subprjmix:fail'])
- self.assertFailedTestCount(3, self.mtest_command + ['--no-suite', 'subprjmix:success'])
-
- self.assertFailedTestCount(2, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix:fail'])
- self.assertFailedTestCount(3, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj'])
- self.assertFailedTestCount(2, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj', '--no-suite', 'subprjmix:fail'])
- self.assertFailedTestCount(1, self.mtest_command + ['--suite', 'subprjfail', '--suite', 'subprjmix', '--suite', 'mainprj', '--no-suite', 'subprjmix:fail', 'mainprj-failing_test'])
-
- self.assertFailedTestCount(1, self.mtest_command + ['--no-suite', 'subprjfail:fail', '--no-suite', 'subprjmix:fail'])
-
def _test_stds_impl(self, testdir, compiler, p):
lang_std = p + '_std'
# Check that all the listed -std=xxx options for this compiler work
@@ -644,31 +871,6 @@ class LinuxlikeTests(unittest.TestCase):
cpp = env.detect_cpp_compiler(False)
self._test_stds_impl(testdir, cpp, 'cpp')
- def test_build_by_default(self):
- testdir = os.path.join(self.common_test_dir, '137 build by default')
- self.init(testdir)
- self.build()
- genfile = os.path.join(self.builddir, 'generated.dat')
- exe = os.path.join(self.builddir, 'fooprog')
- self.assertTrue(os.path.exists(genfile))
- self.assertFalse(os.path.exists(exe))
- self._run(self.ninja_command + ['fooprog'])
- self.assertTrue(os.path.exists(exe))
-
- def test_libdir_must_be_inside_prefix(self):
- testdir = os.path.join(self.common_test_dir, '1 trivial')
- # libdir being inside prefix is ok
- args = ['--prefix', '/opt', '--libdir', '/opt/lib32']
- self.init(testdir, args)
- self.wipe()
- # libdir not being inside prefix is not ok
- args = ['--prefix', '/usr', '--libdir', '/opt/lib32']
- self.assertRaises(subprocess.CalledProcessError, self.init, testdir, args)
- self.wipe()
- # libdir must be inside prefix even when set via mesonconf
- self.init(testdir)
- self.assertRaises(subprocess.CalledProcessError, self.setconf, '-Dlibdir=/opt')
-
def test_installed_modes(self):
'''
Test that files installed by these tests have the correct permissions.
@@ -722,47 +924,15 @@ class LinuxlikeTests(unittest.TestCase):
# The chown failed nonfatally if we're not root
self.assertEqual(0, statf.st_uid)
- def test_internal_include_order(self):
- testdir = os.path.join(self.common_test_dir, '138 include order')
- self.init(testdir)
- for cmd in self.get_compdb():
- if cmd['file'].endswith('/main.c'):
- cmd = cmd['command']
- break
- else:
- raise Exception('Could not find main.c command')
- incs = [a for a in shlex.split(cmd) if a.startswith("-I")]
- self.assertEqual(len(incs), 8)
- # target private dir
- self.assertEqual(incs[0], "-Isub4/someexe@exe")
- # target build subdir
- self.assertEqual(incs[1], "-Isub4")
- # target source subdir
- msg = "{!r} does not end with '/sub4'".format(incs[2])
- self.assertTrue(incs[2].endswith("/sub4"), msg)
- # include paths added via per-target c_args: ['-I'...]
- msg = "{!r} does not end with '/sub3'".format(incs[3])
- self.assertTrue(incs[3].endswith("/sub3"), msg)
- # target include_directories: build dir
- self.assertEqual(incs[4], "-Isub2")
- # target include_directories: source dir
- msg = "{!r} does not end with '/sub2'".format(incs[5])
- self.assertTrue(incs[5].endswith("/sub2"), msg)
- # target internal dependency include_directories: build dir
- self.assertEqual(incs[6], "-Isub1")
- # target internal dependency include_directories: source dir
- msg = "{!r} does not end with '/sub1'".format(incs[7])
- self.assertTrue(incs[7].endswith("/sub1"), msg)
-
class RewriterTests(unittest.TestCase):
def setUp(self):
super().setUp()
src_root = os.path.dirname(__file__)
- self.testroot = tempfile.mkdtemp()
+ self.testroot = os.path.realpath(tempfile.mkdtemp())
self.rewrite_command = [sys.executable, os.path.join(src_root, 'mesonrewriter.py')]
- self.tmpdir = tempfile.mkdtemp()
+ self.tmpdir = os.path.realpath(tempfile.mkdtemp())
self.workdir = os.path.join(self.tmpdir, 'foo')
self.test_dir = os.path.join(src_root, 'test cases/rewrite')
@@ -785,34 +955,38 @@ class RewriterTests(unittest.TestCase):
def test_basic(self):
self.prime('1 basic')
- subprocess.check_output(self.rewrite_command + ['remove',
- '--target=trivialprog',
- '--filename=notthere.c',
- '--sourcedir', self.workdir])
+ subprocess.check_call(self.rewrite_command + ['remove',
+ '--target=trivialprog',
+ '--filename=notthere.c',
+ '--sourcedir', self.workdir],
+ universal_newlines=True)
self.check_effectively_same('meson.build', 'removed.txt')
- subprocess.check_output(self.rewrite_command + ['add',
- '--target=trivialprog',
- '--filename=notthere.c',
- '--sourcedir', self.workdir])
+ subprocess.check_call(self.rewrite_command + ['add',
+ '--target=trivialprog',
+ '--filename=notthere.c',
+ '--sourcedir', self.workdir],
+ universal_newlines=True)
self.check_effectively_same('meson.build', 'added.txt')
- subprocess.check_output(self.rewrite_command + ['remove',
- '--target=trivialprog',
- '--filename=notthere.c',
- '--sourcedir', self.workdir])
+ subprocess.check_call(self.rewrite_command + ['remove',
+ '--target=trivialprog',
+ '--filename=notthere.c',
+ '--sourcedir', self.workdir],
+ universal_newlines=True)
self.check_effectively_same('meson.build', 'removed.txt')
def test_subdir(self):
self.prime('2 subdirs')
top = self.read_contents('meson.build')
s2 = self.read_contents('sub2/meson.build')
- subprocess.check_output(self.rewrite_command + ['remove',
- '--target=something',
- '--filename=second.c',
- '--sourcedir', self.workdir])
+ subprocess.check_call(self.rewrite_command + ['remove',
+ '--target=something',
+ '--filename=second.c',
+ '--sourcedir', self.workdir],
+ universal_newlines=True)
self.check_effectively_same('sub1/meson.build', 'sub1/after.txt')
self.assertEqual(top, self.read_contents('meson.build'))
self.assertEqual(s2, self.read_contents('sub2/meson.build'))
if __name__ == '__main__':
- unittest.main()
+ unittest.main(buffer=True)
diff --git a/test cases/common/105 find program path/meson.build b/test cases/common/105 find program path/meson.build
index ba6030b..e1e6d2e 100644
--- a/test cases/common/105 find program path/meson.build
+++ b/test cases/common/105 find program path/meson.build
@@ -1,10 +1,25 @@
project('find program', 'c')
-prog = find_program('program.py')
-
python = find_program('python3', required : false)
if not python.found()
python = find_program('python')
endif
-run_command(python, prog.path())
+# Source file via string
+prog = find_program('program.py')
+# Source file via files()
+progf = files('program.py')
+# Built file
+py = configure_file(input : 'program.py',
+ output : 'builtprogram.py',
+ configuration : configuration_data())
+
+foreach f : [prog, find_program(py), find_program(progf)]
+ ret = run_command(python, f.path())
+ assert(ret.returncode() == 0, 'can\'t manually run @0@'.format(prog.path()))
+ assert(ret.stdout().strip() == 'Found', 'wrong output from manually-run @0@'.format(prog.path()))
+
+ ret = run_command(f)
+ assert(ret.returncode() == 0, 'can\'t run @0@'.format(prog.path()))
+ assert(ret.stdout().strip() == 'Found', 'wrong output from @0@'.format(prog.path()))
+endforeach
diff --git a/test cases/common/119 pathjoin/meson.build b/test cases/common/119 pathjoin/meson.build
index 7f33791..751ca68 100644
--- a/test cases/common/119 pathjoin/meson.build
+++ b/test cases/common/119 pathjoin/meson.build
@@ -1,8 +1,17 @@
project('pathjoin', 'c')
+# Test string-args form since that is the canonical way
assert(join_paths('foo') == 'foo', 'Single argument join is broken')
assert(join_paths('foo', 'bar') == 'foo/bar', 'Path joining is broken')
assert(join_paths('foo', 'bar', 'baz') == 'foo/bar/baz', 'Path joining is broken')
assert(join_paths('/foo', 'bar') == '/foo/bar', 'Path joining is broken')
assert(join_paths('foo', '/bar') == '/bar', 'Absolute path joining is broken')
assert(join_paths('/foo', '/bar') == '/bar', 'Absolute path joining is broken')
+
+# Test array form since people are using that too
+assert(join_paths(['foo']) == 'foo', 'Single argument join is broken')
+assert(join_paths(['foo', 'bar']) == 'foo/bar', 'Path joining is broken')
+assert(join_paths(['foo', 'bar', 'baz']) == 'foo/bar/baz', 'Path joining is broken')
+assert(join_paths(['/foo', 'bar']) == '/foo/bar', 'Path joining is broken')
+assert(join_paths(['foo', '/bar']) == '/bar', 'Absolute path joining is broken')
+assert(join_paths(['/foo', '/bar']) == '/bar', 'Absolute path joining is broken')
diff --git a/test cases/common/3 static/libfile2.c b/test cases/common/3 static/libfile2.c
new file mode 100644
index 0000000..86bbb2c
--- /dev/null
+++ b/test cases/common/3 static/libfile2.c
@@ -0,0 +1,3 @@
+int libfunc2() {
+ return 4;
+}
diff --git a/test cases/common/3 static/meson.build b/test cases/common/3 static/meson.build
index 3dee93b..e539956 100644
--- a/test cases/common/3 static/meson.build
+++ b/test cases/common/3 static/meson.build
@@ -1,3 +1,4 @@
project('static library test', 'c')
-lib = static_library('mylib', 'libfile.c',
+
+lib = static_library('mylib', get_option('source'),
link_args : '-THISMUSTNOBEUSED') # Static linker needs to ignore all link args.
diff --git a/test cases/common/3 static/meson_options.txt b/test cases/common/3 static/meson_options.txt
new file mode 100644
index 0000000..7261a19
--- /dev/null
+++ b/test cases/common/3 static/meson_options.txt
@@ -0,0 +1 @@
+option('source', type : 'combo', choices : ['libfile.c', 'libfile2.c'], value : 'libfile.c')
diff --git a/test cases/common/94 default options/meson.build b/test cases/common/94 default options/meson.build
index a9176e0..9f45df0 100644
--- a/test cases/common/94 default options/meson.build
+++ b/test cases/common/94 default options/meson.build
@@ -1,4 +1,5 @@
project('default options', 'cpp', 'c', default_options : [
+ 'prefix=/absoluteprefix',
'buildtype=debugoptimized',
'cpp_std=c++11',
'cpp_eh=none',
diff --git a/test cases/windows/9 find program/meson.build b/test cases/windows/9 find program/meson.build
index ef34586..565fb62 100644
--- a/test cases/windows/9 find program/meson.build
+++ b/test cases/windows/9 find program/meson.build
@@ -1,4 +1,12 @@
project('find program', 'c')
+# Test that we can find native windows executables
+find_program('cmd')
+find_program('cmd.exe')
+
+# Test that a script file with an extension can be found
+ext = find_program('test-script-ext.py')
+test('ext', ext)
+# Test that a script file without an extension can be found
prog = find_program('test-script')
test('script', prog)
diff --git a/test cases/windows/9 find program/test-script-ext.py b/test cases/windows/9 find program/test-script-ext.py
new file mode 100644
index 0000000..ae9adfb
--- /dev/null
+++ b/test cases/windows/9 find program/test-script-ext.py
@@ -0,0 +1,3 @@
+#!/usr/bin/env python3
+
+print('ext/noext')