aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--authors.txt1
-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/build.py50
-rw-r--r--mesonbuild/coredata.py1
-rw-r--r--mesonbuild/dependencies.py112
-rw-r--r--mesonbuild/environment.py34
-rw-r--r--mesonbuild/interpreter.py51
-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-xmesontest.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/56 custom target/meson.build4
-rwxr-xr-xtest cases/common/56 custom target/my_compiler.py15
-rw-r--r--test cases/common/94 default options/meson.build1
-rw-r--r--test cases/failing/42 abs subdir/bob/meson.build2
-rw-r--r--test cases/failing/42 abs subdir/meson.build6
-rw-r--r--test cases/failing/42 abspath to srcdir/meson.build3
-rw-r--r--test cases/linuxlike/5 dependency versions/meson.build2
-rw-r--r--test cases/windows/9 find program/meson.build8
-rw-r--r--test cases/windows/9 find program/test-script-ext.py3
30 files changed, 690 insertions, 343 deletions
diff --git a/authors.txt b/authors.txt
index 2f36256..a5f3d46 100644
--- a/authors.txt
+++ b/authors.txt
@@ -63,3 +63,4 @@ Kseniia Vasilchuk
Philipp Geier
Mike Sinkovsky
Dima Krasner
+Fabio Porcedda
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/build.py b/mesonbuild/build.py
index 5466431..5f2de3b 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -1298,6 +1298,29 @@ class CustomTarget(Target):
deps.append(c)
return deps
+ def flatten_command(self, cmd):
+ if not isinstance(cmd, list):
+ cmd = [cmd]
+ final_cmd = []
+ for c in cmd:
+ if hasattr(c, 'held_object'):
+ c = c.held_object
+ if isinstance(c, (str, File)):
+ final_cmd.append(c)
+ elif isinstance(c, dependencies.ExternalProgram):
+ if not c.found():
+ m = 'Tried to use not-found external program {!r} in "command"'
+ raise InvalidArguments(m.format(c.name))
+ final_cmd += c.get_command()
+ elif isinstance(c, (BuildTarget, CustomTarget)):
+ self.dependencies.append(c)
+ final_cmd.append(c)
+ elif isinstance(c, list):
+ final_cmd += self.flatten_command(c)
+ else:
+ raise InvalidArguments('Argument {!r} in "command" is invalid'.format(c))
+ return final_cmd
+
def process_kwargs(self, kwargs):
super().process_kwargs(kwargs)
self.sources = kwargs.get('input', [])
@@ -1325,32 +1348,7 @@ class CustomTarget(Target):
if os.path.split(depfile)[1] != depfile:
raise InvalidArguments('Depfile must be a plain filename without a subdirectory.')
self.depfile = depfile
- cmd = kwargs['command']
- if not(isinstance(cmd, list)):
- cmd = [cmd]
- final_cmd = []
- for i, c in enumerate(cmd):
- if hasattr(c, 'held_object'):
- c = c.held_object
- if isinstance(c, (str, File)):
- final_cmd.append(c)
- elif isinstance(c, dependencies.ExternalProgram):
- if not c.found():
- raise InvalidArguments('Tried to use not found external program {!r} in a build rule.'.format(c.name))
- final_cmd += c.get_command()
- elif isinstance(c, (BuildTarget, CustomTarget)):
- self.dependencies.append(c)
- final_cmd.append(c)
- elif isinstance(c, list):
- # Hackety hack, only supports one level of flattening. Should really
- # work to arbtrary depth.
- for s in c:
- if not isinstance(s, str):
- raise InvalidArguments('Array as argument %d contains a non-string.' % i)
- final_cmd.append(s)
- else:
- raise InvalidArguments('Argument %s in "command" is invalid.' % i)
- self.command = final_cmd
+ self.command = self.flatten_command(kwargs['command'])
if self.capture:
for c in self.command:
if isinstance(c, str) and '@OUTPUT@' in c:
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index d39f161..0e6685c 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -304,7 +304,6 @@ forbidden_target_names = {'clean': None,
'PHONY': None,
'all': None,
'test': None,
- 'test:': None,
'benchmark': None,
'install': None,
'uninstall': None,
diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py
index c894b0e..e4317f1 100644
--- a/mesonbuild/dependencies.py
+++ b/mesonbuild/dependencies.py
@@ -1,4 +1,4 @@
-# Copyright 2013-2015 The Meson development team
+# Copyright 2013-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.
@@ -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
@@ -589,6 +625,9 @@ class BoostDependency(Dependency):
mlog.log('Dependency Boost (%s) found:' % module_str, mlog.green('YES'), info)
else:
mlog.log("Dependency Boost (%s) found:" % module_str, mlog.red('NO'))
+ if 'cpp' not in self.environment.coredata.compilers:
+ raise DependencyException('Tried to use Boost but a C++ compiler is not defined.')
+ self.cpp_compiler = self.environment.coredata.compilers['cpp']
def detect_win_root(self):
globtext = 'c:\\local\\boost_*'
@@ -721,8 +760,19 @@ class BoostDependency(Dependency):
args.append('-L' + os.path.join(self.boost_root, 'lib'))
for module in self.requested_modules:
module = BoostDependency.name2lib.get(module, module)
- if module in self.lib_modules or module in self.lib_modules_mt:
- linkcmd = '-lboost_' + module
+ libname = 'boost_' + module
+ # The compiler's library detector is the most reliable so use that first.
+ default_detect = self.cpp_compiler.find_library(libname, self.environment, [])
+ if default_detect is not None:
+ if module == 'unit_testing_framework':
+ emon_args = self.cpp_compiler.find_library('boost_test_exec_monitor')
+ else:
+ emon_args = None
+ args += default_detect
+ if emon_args is not None:
+ args += emon_args
+ elif module in self.lib_modules or module in self.lib_modules_mt:
+ linkcmd = '-l' + libname
args.append(linkcmd)
# FIXME a hack, but Boost's testing framework has a lot of
# different options and it's hard to determine what to do
@@ -980,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:
@@ -993,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/environment.py b/mesonbuild/environment.py
index e143b0b..13b38d5 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -19,6 +19,7 @@ from . import mlog
from .compilers import *
from .mesonlib import EnvironmentException, Popen_safe
import configparser
+import shlex
import shutil
build_filename = 'meson.build'
@@ -121,6 +122,18 @@ def detect_cpu_family(compilers):
if trial.startswith('arm'):
return 'arm'
if trial in ('amd64', 'x64'):
+ trial = 'x86_64'
+ if trial == 'x86_64':
+ # On Linux (and maybe others) there can be any mixture of 32/64 bit
+ # code in the kernel, Python, system etc. The only reliable way
+ # to know is to check the compiler defines.
+ for c in compilers.values():
+ try:
+ if c.has_define('__i386__'):
+ return 'x86'
+ except mesonlib.MesonException:
+ # Ignore compilers that do not support has_define.
+ pass
return 'x86_64'
# Add fixes here as bugs are reported.
return trial
@@ -131,6 +144,15 @@ def detect_cpu(compilers):
else:
trial = platform.machine().lower()
if trial in ('amd64', 'x64'):
+ trial = 'x86_64'
+ if trial == 'x86_64':
+ # Same check as above for cpu_family
+ for c in compilers.values():
+ try:
+ if c.has_define('__i386__'):
+ return 'i686' # All 64 bit cpus have at least this level of x86 support.
+ except mesonlib.MesonException:
+ pass
return 'x86_64'
# Add fixes here as bugs are reported.
return trial
@@ -348,7 +370,9 @@ class Environment:
def detect_c_compiler(self, want_cross):
evar = 'CC'
if self.is_cross_build() and want_cross:
- compilers = [self.cross_info.config['binaries']['c']]
+ compilers = self.cross_info.config['binaries']['c']
+ if not isinstance(compilers, list):
+ compilers = [compilers]
ccache = []
is_cross = True
if self.cross_info.need_exe_wrapper():
@@ -356,7 +380,7 @@ class Environment:
else:
exe_wrap = []
elif evar in os.environ:
- compilers = os.environ[evar].split()
+ compilers = shlex.split(os.environ[evar])
ccache = []
is_cross = False
exe_wrap = None
@@ -385,13 +409,13 @@ class Environment:
continue
gtype = self.get_gnu_compiler_type(defines)
version = self.get_gnu_version_from_defines(defines)
- return GnuCCompiler(ccache + [compiler], version, gtype, is_cross, exe_wrap, defines)
+ return GnuCCompiler(ccache + compilers, version, gtype, is_cross, exe_wrap, defines)
if 'clang' in out:
if 'Apple' in out or for_darwin(want_cross, self):
cltype = CLANG_OSX
else:
cltype = CLANG_STANDARD
- return ClangCCompiler(ccache + [compiler], version, cltype, is_cross, exe_wrap)
+ return ClangCCompiler(ccache + compilers, version, cltype, is_cross, exe_wrap)
if 'Microsoft' in out or 'Microsoft' in err:
# Visual Studio prints version number to stderr but
# everything else to stdout. Why? Lord only knows.
@@ -400,7 +424,7 @@ class Environment:
if '(ICC)' in out:
# TODO: add microsoft add check OSX
inteltype = ICC_STANDARD
- return IntelCCompiler(ccache + [compiler], version, inteltype, is_cross, exe_wrap)
+ return IntelCCompiler(ccache + compilers, version, inteltype, is_cross, exe_wrap)
errmsg = 'Unknown compiler(s): "' + ', '.join(compilers) + '"'
if popen_exceptions:
errmsg += '\nThe follow exceptions were encountered:'
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 4466f22..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():
@@ -2132,6 +2142,8 @@ requirements use the version keyword argument instead.''')
raise InvalidArguments('Must not go into subprojects dir with subdir(), use subproject() instead.')
prev_subdir = self.subdir
subdir = os.path.join(prev_subdir, args[0])
+ if os.path.isabs(subdir):
+ raise InvalidArguments('Subdir argument must be a relative path.')
if subdir in self.visited_subdirs:
raise InvalidArguments('Tried to enter directory "%s", which has already been visited.'
% subdir)
@@ -2255,8 +2267,27 @@ requirements use the version keyword argument instead.''')
@stringArgs
def func_include_directories(self, node, args, kwargs):
- absbase = os.path.join(self.environment.get_source_dir(), self.subdir)
+ src_root = self.environment.get_source_dir()
+ absbase = os.path.join(src_root, self.subdir)
for a in args:
+ if a.startswith(src_root):
+ raise InvalidArguments('''Tried to form an absolute path to a source dir. You should not do that but use
+relative paths instead.
+
+To get include path to any directory relative to the current dir do
+
+incdir = include_directories(dirname)
+
+After this incdir will contain both the current source dir as well as the
+corresponding build dir. It can then be used in any subdirectory and
+Meson will take care of all the busywork to make paths work.
+
+Dirname can even be '.' to mark the current directory. Though you should
+remember that the current source and build directories are always
+put in the include directories by default so you only need to do
+include_directories('.') if you intend to use the result in a
+different subdirectory.
+''')
absdir = os.path.join(absbase, a)
if not os.path.isdir(absdir):
raise InvalidArguments('Include dir %s does not exist.' % a)
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/mesontest.py b/mesontest.py
index f5da103..3545ed8 100755
--- a/mesontest.py
+++ b/mesontest.py
@@ -25,6 +25,7 @@ import time, datetime, multiprocessing, json
import concurrent.futures as conc
import platform
import signal
+import random
# GNU autotools interprets a return code of 77 from tests it executes to
# mean that the test should be skipped.
@@ -221,6 +222,14 @@ class TestHarness:
if len(test.extra_paths) > 0:
child_env['PATH'] += ';'.join([''] + test.extra_paths)
+ # If MALLOC_PERTURB_ is not set, or if it is set to an empty value,
+ # (i.e., the test or the environment don't explicitly set it), set
+ # it ourselves. We do this unconditionally because it is extremely
+ # useful to have in tests.
+ # Setting MALLOC_PERTURB_="0" will completely disable this feature.
+ if 'MALLOC_PERTURB_' not in child_env or not child_env['MALLOC_PERTURB_']:
+ child_env['MALLOC_PERTURB_'] = str(random.randint(1, 255))
+
setsid = None
stdout = None
stderr = None
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/56 custom target/meson.build b/test cases/common/56 custom target/meson.build
index fd59fbd..2e6f69c 100644
--- a/test cases/common/56 custom target/meson.build
+++ b/test cases/common/56 custom target/meson.build
@@ -8,11 +8,13 @@ endif
# Note that this will not add a dependency to the compiler executable.
# Code will not be rebuilt if it changes.
comp = '@0@/@1@'.format(meson.current_source_dir(), 'my_compiler.py')
+# Test that files() in command: works. The compiler just discards it.
+useless = files('installed_files.txt')
mytarget = custom_target('bindat',
output : 'data.dat',
input : 'data_source.txt',
-command : [python, comp, '--input=@INPUT@', '--output=@OUTPUT@'],
+command : [python, comp, '--input=@INPUT@', '--output=@OUTPUT@', useless],
install : true,
install_dir : 'subdir'
)
diff --git a/test cases/common/56 custom target/my_compiler.py b/test cases/common/56 custom target/my_compiler.py
index 4ba2da6..f46d23a 100755
--- a/test cases/common/56 custom target/my_compiler.py
+++ b/test cases/common/56 custom target/my_compiler.py
@@ -1,16 +1,21 @@
#!/usr/bin/env python3
+import os
import sys
+assert(os.path.exists(sys.argv[3]))
+
+args = sys.argv[:-1]
+
if __name__ == '__main__':
- if len(sys.argv) != 3 or not sys.argv[1].startswith('--input') or \
- not sys.argv[2].startswith('--output'):
- print(sys.argv[0], '--input=input_file --output=output_file')
+ if len(args) != 3 or not args[1].startswith('--input') or \
+ not args[2].startswith('--output'):
+ print(args[0], '--input=input_file --output=output_file')
sys.exit(1)
- with open(sys.argv[1].split('=')[1]) as f:
+ with open(args[1].split('=')[1]) as f:
ifile = f.read()
if ifile != 'This is a text only input file.\n':
print('Malformed input')
sys.exit(1)
- with open(sys.argv[2].split('=')[1], 'w') as ofile:
+ with open(args[2].split('=')[1], 'w') as ofile:
ofile.write('This is a binary output file.\n')
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/failing/42 abs subdir/bob/meson.build b/test cases/failing/42 abs subdir/bob/meson.build
new file mode 100644
index 0000000..7bbf4b2
--- /dev/null
+++ b/test cases/failing/42 abs subdir/bob/meson.build
@@ -0,0 +1,2 @@
+# This file is never reached.
+x = 3
diff --git a/test cases/failing/42 abs subdir/meson.build b/test cases/failing/42 abs subdir/meson.build
new file mode 100644
index 0000000..8c23224
--- /dev/null
+++ b/test cases/failing/42 abs subdir/meson.build
@@ -0,0 +1,6 @@
+project('abs subdir', 'c')
+
+# For some reason people insist on doing this, probably
+# because Make has taught them to never rely on anything.
+subdir(join_paths(meson.source_root(), 'bob'))
+
diff --git a/test cases/failing/42 abspath to srcdir/meson.build b/test cases/failing/42 abspath to srcdir/meson.build
new file mode 100644
index 0000000..964a19b
--- /dev/null
+++ b/test cases/failing/42 abspath to srcdir/meson.build
@@ -0,0 +1,3 @@
+project('meson', 'c')
+
+include_directories(meson.current_source_dir())
diff --git a/test cases/linuxlike/5 dependency versions/meson.build b/test cases/linuxlike/5 dependency versions/meson.build
index 20b3df5..1b01cd6 100644
--- a/test cases/linuxlike/5 dependency versions/meson.build
+++ b/test cases/linuxlike/5 dependency versions/meson.build
@@ -1,4 +1,4 @@
-project('dep versions', 'c')
+project('dep versions', 'c', 'cpp')
# Find external dependency without version
zlib = dependency('zlib')
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')