aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml3
-rw-r--r--man/mesontest.157
-rw-r--r--mesonbuild/backend/backends.py3
-rw-r--r--mesonbuild/backend/ninjabackend.py4
-rw-r--r--mesonbuild/build.py4
-rw-r--r--mesonbuild/compilers.py2
-rw-r--r--mesonbuild/dependencies.py7
-rw-r--r--mesonbuild/interpreter.py36
-rw-r--r--mesonbuild/interpreterbase.py10
-rw-r--r--mesonbuild/mconf.py17
-rw-r--r--mesonbuild/mesonlib.py104
-rw-r--r--mesonbuild/mintro.py2
-rw-r--r--mesonbuild/mparser.py2
-rw-r--r--mesonbuild/scripts/meson_install.py32
-rwxr-xr-xmesontest.py7
-rwxr-xr-xrun_project_tests.py2
-rwxr-xr-xrun_unittests.py89
-rw-r--r--setup.py1
-rwxr-xr-xtest cases/common/103 manygen/subdir/manygen.py4
-rw-r--r--test cases/common/103 manygen/subdir/meson.build5
-rw-r--r--test cases/common/105 find program path/meson.build7
-rw-r--r--test cases/common/105 find program path/program.py2
-rw-r--r--test cases/common/107 postconf/postconf.py2
-rw-r--r--test cases/common/108 postconf with args/postconf.py2
-rwxr-xr-xtest cases/common/113 generatorcustom/catter.py2
-rwxr-xr-xtest cases/common/113 generatorcustom/gen.py2
-rw-r--r--test cases/common/117 custom target capture/meson.build7
-rwxr-xr-xtest cases/common/118 allgenerate/converter.py2
-rw-r--r--test cases/common/12 data/installed_files.txt1
-rw-r--r--test cases/common/12 data/meson.build11
-rw-r--r--test cases/common/12 data/runscript.sh3
-rw-r--r--test cases/common/131 custom target directory install/docgen.py2
-rwxr-xr-xtest cases/common/133 configure file in generator/src/gen.py2
-rwxr-xr-xtest cases/common/16 configure file/generator.py6
-rw-r--r--test cases/common/16 configure file/meson.build5
-rwxr-xr-xtest cases/common/48 test args/tester.py2
-rwxr-xr-xtest cases/common/56 custom target/depfile/dep.py2
-rw-r--r--test cases/common/56 custom target/meson.build5
-rw-r--r--test cases/common/57 custom target chain/meson.build7
-rwxr-xr-xtest cases/common/57 custom target chain/my_compiler.py2
-rwxr-xr-xtest cases/common/57 custom target chain/my_compiler2.py2
-rwxr-xr-xtest cases/common/57 custom target chain/usetarget/subcomp.py2
-rw-r--r--test cases/common/58 run target/converter.py2
-rwxr-xr-xtest cases/common/58 run target/fakeburner.py4
-rw-r--r--test cases/common/58 run target/meson.build6
-rw-r--r--test cases/common/59 object generator/meson.build5
-rwxr-xr-xtest cases/common/61 custom target source output/generator.py2
-rw-r--r--test cases/common/64 custom header generator/makeheader.py2
-rwxr-xr-xtest cases/common/65 multiple generators/mygen.py2
-rw-r--r--test cases/common/66 install subdir/meson.build4
-rw-r--r--test cases/common/66 install subdir/subdir/meson.build4
-rwxr-xr-xtest cases/common/72 build always/version_gen.py2
-rw-r--r--test cases/common/76 configure file in custom target/src/mycompiler.py2
-rwxr-xr-xtest cases/common/77 external test program/mytest.py4
-rwxr-xr-xtest cases/common/78 ctarget dependency/gen1.py2
-rwxr-xr-xtest cases/common/78 ctarget dependency/gen2.py2
-rwxr-xr-xtest cases/common/93 private include/stlib/compiler.py2
-rwxr-xr-xtest cases/common/98 gen extra/srcgen.py2
-rw-r--r--test cases/failing/41 kwarg assign/dummy.c3
-rw-r--r--test cases/failing/41 kwarg assign/meson.build4
-rw-r--r--test cases/failing/41 kwarg assign/prog.c3
61 files changed, 455 insertions, 71 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index adc13b8..ce56a12 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -41,7 +41,6 @@ install:
- ps: (new-object net.webclient).DownloadFile('https://dl.dropboxusercontent.com/u/37517477/ninja.exe', 'C:\projects\meson\ninja.exe')
- cmd: if %arch%==x86 (set MESON_PYTHON_PATH=C:\python34) else (set MESON_PYTHON_PATH=C:\python34-x64)
- cmd: echo Using Python at %MESON_PYTHON_PATH%
- - cmd: copy %MESON_PYTHON_PATH%\python.exe %MESON_PYTHON_PATH%\python3.exe
- cmd: if %compiler%==msvc2010 ( call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" %arch% )
- cmd: if %compiler%==msvc2015 ( call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %arch% )
@@ -51,7 +50,7 @@ build_script:
test_script:
- cmd: echo Running tests for %arch% and %compiler% with the %backend% backend
- - cmd: PATH=%cd%;%MESON_PYTHON_PATH%;%PATH%; && python3 run_tests.py --backend=%backend%
+ - cmd: PATH=%cd%;%MESON_PYTHON_PATH%;%PATH%; && python run_tests.py --backend=%backend%
on_finish:
- appveyor PushArtifact meson-test-run.txt -DeploymentName "Text test logs"
diff --git a/man/mesontest.1 b/man/mesontest.1
new file mode 100644
index 0000000..47fdd15
--- /dev/null
+++ b/man/mesontest.1
@@ -0,0 +1,57 @@
+.TH MESON "1" "December 2016" "meson 0.37.1" "User Commands"
+.SH NAME
+mesontest - test tool for the Meson build system
+.SH DESCRIPTION
+
+Mesontest is a helper tool for running test suites of projects using Meson.
+The default way of running tests is to invoke the default build command:
+
+\fBninja [\fR \fItest\fR \fB]\fR
+
+Mesontest provides a much richer set of tools for invoking tests.
+
+.SS "options:"
+.TP
+\fB\-\-repeat\fR
+run tests as many times as specified
+.TP
+\fB\-\-gdb\fR
+run tests under gdb
+.TP
+\fB\-\-list\fR
+list all available tests
+.TP
+\fB\-\-wrapper\fR
+invoke all tests via the given wrapper (e.g. valgrind)
+.TP
+\fB\-C\fR
+Change into the given directory before running tests (must be root of build directory).
+.TP
+\fB\-\-suite\fR
+run tests in this suite
+.TP
+\fB\-\-no\-suite\fR
+do not run tests in this suite
+.TP
+\fB\-\-no\-stdsplit\fR
+do not split stderr and stdout in test logs
+.TP
+\fB\-\-benchmark\fR
+run benchmarks instead of tests
+.TP
+\fB\-\-logbase\fR
+base of file name to use for writing test logs
+.TP
+\fB\-\-num-processes\fR
+how many parallel processes to use to run tests
+.TP
+\fB\-\-verbose\fR
+do not redirect stdout and stderr
+.TP
+\fB\-t\fR
+a multiplier to use for test timeout values (usually something like 100 for Valgrind)
+.TP
+\fB\-\-setup\fR
+use the specified test setup
+.SH SEE ALSO
+http://mesonbuild.com/
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index e46c2c5..6f8a50e 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -459,7 +459,8 @@ class Backend:
mfobj['projects'] = self.build.dep_manifest
with open(ifilename, 'w') as f:
f.write(json.dumps(mfobj))
- d.data.append([ifilename, ofilename])
+ # Copy file from, to, and with mode unchanged
+ d.data.append([ifilename, ofilename, None])
def get_regen_filelist(self):
'''List of all files whose alteration means that the build
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index e1a478c..628718f 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -700,7 +700,7 @@ int dummy;
assert(isinstance(f, mesonlib.File))
plain_f = os.path.split(f.fname)[1]
dstabs = os.path.join(subdir, plain_f)
- i = [f.absolute_path(srcdir, builddir), dstabs]
+ i = [f.absolute_path(srcdir, builddir), dstabs, de.install_mode]
d.data.append(i)
def generate_subdir_install(self, d):
@@ -715,7 +715,7 @@ int dummy;
inst_dir = sd.installable_subdir
src_dir = os.path.join(self.environment.get_source_dir(), subdir)
dst_dir = os.path.join(self.environment.get_prefix(), sd.install_dir)
- d.install_subdirs.append([src_dir, inst_dir, dst_dir])
+ d.install_subdirs.append([src_dir, inst_dir, dst_dir, sd.install_mode])
def generate_tests(self, outfile):
self.serialise_tests()
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index ceae49b..a9f08c3 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -1254,6 +1254,7 @@ class CustomTarget(Target):
'depends': True,
'depend_files': True,
'depfile': True,
+ 'build_by_default': True,
}
def __init__(self, name, subdir, kwargs, absolute_paths=False):
@@ -1502,9 +1503,10 @@ class ConfigurationData:
# A bit poorly named, but this represents plain data files to copy
# during install.
class Data:
- def __init__(self, sources, install_dir):
+ def __init__(self, sources, install_dir, install_mode=None):
self.sources = sources
self.install_dir = install_dir
+ self.install_mode = install_mode
if not isinstance(self.sources, list):
self.sources = [self.sources]
for s in self.sources:
diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py
index de14d4a..0a88d6b 100644
--- a/mesonbuild/compilers.py
+++ b/mesonbuild/compilers.py
@@ -610,7 +610,7 @@ class CCompiler(Compiler):
return ['--coverage']
def get_coverage_link_args(self):
- return ['-lgcov']
+ return ['--coverage']
def get_werror_args(self):
return ['-Werror']
diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py
index 97aec7e..6ae91d4 100644
--- a/mesonbuild/dependencies.py
+++ b/mesonbuild/dependencies.py
@@ -119,8 +119,10 @@ class PkgConfigDependency(Dependency):
if self.required:
raise DependencyException('Pkg-config binary missing from cross file')
else:
- self.pkgbin = environment.cross_info.config['binaries']['pkgconfig']
- PkgConfigDependency.class_pkgbin = self.pkgbin
+ potential_pkgbin = environment.cross_info.config['binaries'].get('pkgconfig', 'non_existing_binary')
+ if shutil.which(potential_pkgbin):
+ self.pkgbin = potential_pkgbin
+ PkgConfigDependency.class_pkgbin = self.pkgbin
# Only search for the native pkg-config the first time and
# store the result in the class definition
elif PkgConfigDependency.class_pkgbin is None:
@@ -316,6 +318,7 @@ class WxDependency(Dependency):
def __init__(self, environment, kwargs):
Dependency.__init__(self, 'wx')
self.is_found = False
+ self.modversion = 'none'
if WxDependency.wx_found is None:
self.check_wxconfig()
if not WxDependency.wx_found:
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index b5cd0b6..cb5b617 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -22,7 +22,7 @@ from . import optinterpreter
from . import compilers
from .wrap import wrap
from . import mesonlib
-from .mesonlib import Popen_safe
+from .mesonlib import FileMode, Popen_safe
from .dependencies import InternalDependency, Dependency
from .interpreterbase import InterpreterBase
from .interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs
@@ -453,11 +453,12 @@ class DataHolder(InterpreterObject):
return self.held_object.install_dir
class InstallDir(InterpreterObject):
- def __init__(self, source_subdir, installable_subdir, install_dir):
+ def __init__(self, src_subdir, inst_subdir, install_dir, install_mode):
InterpreterObject.__init__(self)
- self.source_subdir = source_subdir
- self.installable_subdir = installable_subdir
+ self.source_subdir = src_subdir
+ self.installable_subdir = inst_subdir
self.install_dir = install_dir
+ self.install_mode = install_mode
class Man(InterpreterObject):
@@ -2141,6 +2142,25 @@ requirements use the version keyword argument instead.''')
self.evaluate_codeblock(codeblock)
self.subdir = prev_subdir
+ def _get_kwarg_install_mode(self, kwargs):
+ if 'install_mode' not in kwargs:
+ return None
+ install_mode = []
+ mode = mesonlib.stringintlistify(kwargs.get('install_mode', []))
+ for m in mode:
+ # We skip any arguments that are set to `false`
+ if m is False:
+ m = None
+ install_mode.append(m)
+ if len(install_mode) > 3:
+ raise InvalidArguments('Keyword argument install_mode takes at '
+ 'most 3 arguments.')
+ if len(install_mode) > 0 and install_mode[0] is not None and \
+ not isinstance(install_mode[0], str):
+ raise InvalidArguments('Keyword argument install_mode requires the '
+ 'permissions arg to be a string or false')
+ return FileMode(*install_mode)
+
def func_install_data(self, node, args, kwargs):
kwsource = mesonlib.stringlistify(kwargs.get('sources', []))
raw_sources = args + kwsource
@@ -2153,7 +2173,10 @@ requirements use the version keyword argument instead.''')
source_strings.append(s)
sources += self.source_strings_to_files(source_strings)
install_dir = kwargs.get('install_dir', None)
- data = DataHolder(build.Data(sources, install_dir))
+ if not isinstance(install_dir, (str, type(None))):
+ raise InvalidArguments('Keyword argument install_dir not a string.')
+ install_mode = self._get_kwarg_install_mode(kwargs)
+ data = DataHolder(build.Data(sources, install_dir, install_mode))
self.build.data.append(data.held_object)
return data
@@ -2166,7 +2189,8 @@ requirements use the version keyword argument instead.''')
install_dir = kwargs['install_dir']
if not isinstance(install_dir, str):
raise InvalidArguments('Keyword argument install_dir not a string.')
- idir = InstallDir(self.subdir, args[0], install_dir)
+ install_mode = self._get_kwarg_install_mode(kwargs)
+ idir = InstallDir(self.subdir, args[0], install_dir, install_mode)
self.build.install_dirs.append(idir)
return idir
diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py
index e4dd58b..837a4f8 100644
--- a/mesonbuild/interpreterbase.py
+++ b/mesonbuild/interpreterbase.py
@@ -1,4 +1,4 @@
-# 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.
@@ -86,6 +86,7 @@ class InterpreterBase:
self.builtin = {}
self.subdir = subdir
self.variables = {}
+ self.argument_depth = 0
def load_root_meson_file(self):
mesonfile = os.path.join(self.source_root, self.subdir, environment.build_filename)
@@ -511,6 +512,7 @@ class InterpreterBase:
assert(isinstance(args, mparser.ArgumentNode))
if args.incorrect_order():
raise InvalidArguments('All keyword arguments must be after positional arguments.')
+ self.argument_depth += 1
reduced_pos = [self.evaluate_statement(arg) for arg in args.arguments]
reduced_kw = {}
for key in args.kwargs.keys():
@@ -520,6 +522,7 @@ class InterpreterBase:
reduced_kw[key] = self.evaluate_statement(a)
if not isinstance(reduced_pos, list):
reduced_pos = [reduced_pos]
+ self.argument_depth -= 1
return reduced_pos, reduced_kw
def flatten(self, args):
@@ -540,6 +543,9 @@ class InterpreterBase:
def assignment(self, node):
assert(isinstance(node, mparser.AssignmentNode))
+ if self.argument_depth != 0:
+ raise InvalidArguments('''Tried to assign values inside an argument list.
+To specify a keyword argument, use : instead of =.''')
var_name = node.var_name
if not isinstance(var_name, str):
raise InvalidArguments('Tried to assign value to a non-variable.')
@@ -551,7 +557,7 @@ class InterpreterBase:
if isinstance(value, MutableInterpreterObject):
value = copy.deepcopy(value)
self.set_variable(var_name, value)
- return value
+ return None
def set_variable(self, varname, variable):
if variable is None:
diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py
index c921409..2ab5f92 100644
--- a/mesonbuild/mconf.py
+++ b/mesonbuild/mconf.py
@@ -22,6 +22,8 @@ parser = argparse.ArgumentParser()
parser.add_argument('-D', action='append', default=[], dest='sets',
help='Set an option to the given value.')
parser.add_argument('directory', nargs='*')
+parser.add_argument('--clearcache', action='store_true', default=False,
+ help='Clear cached state (e.g. found dependencies)')
class ConfException(mesonlib.MesonException):
def __init__(self, *args, **kwargs):
@@ -42,13 +44,16 @@ class Conf:
raise ConfException('Version mismatch (%s vs %s)' %
(coredata.version, self.coredata.version))
+ def clear_cache(self):
+ self.coredata.deps = {}
+
def save(self):
# Only called if something has changed so overwrite unconditionally.
with open(self.coredata_file, 'wb') as f:
pickle.dump(self.coredata, f)
# We don't write the build file because any changes to it
- # are erased when Meson is executed the nex time, i.e. the next
- # time Ninja is run.
+ # are erased when Meson is executed the next time, i.e. whne
+ # Ninja is run.
def print_aligned(self, arr):
if len(arr) == 0:
@@ -223,11 +228,17 @@ def run(args):
builddir = options.directory[0]
try:
c = Conf(builddir)
+ save = False
if len(options.sets) > 0:
c.set_options(options.sets)
- c.save()
+ save = True
+ elif options.clearcache:
+ c.clear_cache()
+ save = True
else:
c.print_conf()
+ if save:
+ c.save()
except ConfException as e:
print('Meson configurator encountered an error:\n')
print(e)
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index 2587d6f..2ad43c8 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -14,6 +14,7 @@
"""A library of random helper functionality."""
+import stat
import platform, subprocess, operator, os, shutil, re
from glob import glob
@@ -24,6 +25,97 @@ class MesonException(Exception):
class EnvironmentException(MesonException):
'''Exceptions thrown while processing and creating the build environment'''
+class FileMode:
+ # The first triad is for owner permissions, the second for group permissions,
+ # and the third for others (everyone else).
+ # For the 1st character:
+ # 'r' means can read
+ # '-' means not allowed
+ # For the 2nd character:
+ # 'w' means can write
+ # '-' means not allowed
+ # For the 3rd character:
+ # 'x' means can execute
+ # 's' means can execute and setuid/setgid is set (owner/group triads only)
+ # 'S' means cannot execute and setuid/setgid is set (owner/group triads only)
+ # 't' means can execute and sticky bit is set ("others" triads only)
+ # 'T' means cannot execute and sticky bit is set ("others" triads only)
+ # '-' means none of these are allowed
+ #
+ # The meanings of 'rwx' perms is not obvious for directories; see:
+ # https://www.hackinglinuxexposed.com/articles/20030424.html
+ #
+ # For information on this notation such as setuid/setgid/sticky bits, see:
+ # https://en.wikipedia.org/wiki/File_system_permissions#Symbolic_notation
+ symbolic_perms_regex = re.compile('[r-][w-][xsS-]' # Owner perms
+ '[r-][w-][xsS-]' # Group perms
+ '[r-][w-][xtT-]') # Others perms
+
+ def __init__(self, perms=None, owner=None, group=None):
+ self.perms_s = perms
+ self.perms = self.perms_s_to_bits(perms)
+ self.owner = owner
+ self.group = group
+
+ def __repr__(self):
+ ret = '<FileMode: {!r} owner={} group={}'
+ return ret.format(self.perms_s, self.owner, self.group)
+
+ @classmethod
+ def perms_s_to_bits(cls, perms_s):
+ '''
+ Does the opposite of stat.filemode(), converts strings of the form
+ 'rwxr-xr-x' to st_mode enums which can be passed to os.chmod()
+ '''
+ if perms_s is None:
+ # No perms specified, we will not touch the permissions
+ return -1
+ eg = 'rwxr-xr-x'
+ if not isinstance(perms_s, str):
+ msg = 'Install perms must be a string. For example, {!r}'
+ raise MesonException(msg.format(eg))
+ if len(perms_s) != 9 or not cls.symbolic_perms_regex.match(perms_s):
+ msg = 'File perms {!r} must be exactly 9 chars. For example, {!r}'
+ raise MesonException(msg.format(perms_s, eg))
+ perms = 0
+ # Owner perms
+ if perms_s[0] == 'r':
+ perms |= stat.S_IRUSR
+ if perms_s[1] == 'w':
+ perms |= stat.S_IWUSR
+ if perms_s[2] == 'x':
+ perms |= stat.S_IXUSR
+ elif perms_s[2] == 'S':
+ perms |= stat.S_ISUID
+ elif perms_s[2] == 's':
+ perms |= stat.S_IXUSR
+ perms |= stat.S_ISUID
+ # Group perms
+ if perms_s[3] == 'r':
+ perms |= stat.S_IRGRP
+ if perms_s[4] == 'w':
+ perms |= stat.S_IWGRP
+ if perms_s[5] == 'x':
+ perms |= stat.S_IXGRP
+ elif perms_s[5] == 'S':
+ perms |= stat.S_ISGID
+ elif perms_s[5] == 's':
+ perms |= stat.S_IXGRP
+ perms |= stat.S_ISGID
+ # Others perms
+ if perms_s[6] == 'r':
+ perms |= stat.S_IROTH
+ if perms_s[7] == 'w':
+ perms |= stat.S_IWOTH
+ if perms_s[8] == 'x':
+ perms |= stat.S_IXOTH
+ elif perms_s[8] == 'T':
+ perms |= stat.S_ISVTX
+ elif perms_s[8] == 't':
+ perms |= stat.S_IXOTH
+ perms |= stat.S_ISVTX
+ return perms
+
class File:
def __init__(self, is_built, subdir, fname):
self.is_built = is_built
@@ -360,11 +452,21 @@ def replace_if_different(dst, dst_tmp):
else:
os.unlink(dst_tmp)
+def stringintlistify(item):
+ if isinstance(item, (str, int)):
+ item = [item]
+ if not isinstance(item, list):
+ raise MesonException('Item must be a list, a string, or an int')
+ for i in item:
+ if not isinstance(i, (str, int, type(None))):
+ raise MesonException('List item must be a string or an int')
+ return item
+
def stringlistify(item):
if isinstance(item, str):
item = [item]
if not isinstance(item, list):
- raise MesonException('Item is not an array')
+ raise MesonException('Item is not a list')
for i in item:
if not isinstance(i, str):
raise MesonException('List item not a string.')
diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py
index 5a381c1..e30500f 100644
--- a/mesonbuild/mintro.py
+++ b/mesonbuild/mintro.py
@@ -62,7 +62,7 @@ def determine_installed_path(target, installdata):
def list_installed(installdata):
res = {}
if installdata is not None:
- for path, installpath in installdata.data:
+ for path, installpath, unused_prefix in installdata.data:
res[path] = os.path.join(installdata.prefix, installpath)
print(json.dumps(res))
diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py
index 5871f65..6e1e398 100644
--- a/mesonbuild/mparser.py
+++ b/mesonbuild/mparser.py
@@ -1,4 +1,4 @@
-# Copyright 2014-2016 The Meson development team
+# Copyright 2014-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.
diff --git a/mesonbuild/scripts/meson_install.py b/mesonbuild/scripts/meson_install.py
index 676a1e5..a74573e 100644
--- a/mesonbuild/scripts/meson_install.py
+++ b/mesonbuild/scripts/meson_install.py
@@ -16,10 +16,34 @@ import sys, pickle, os, shutil, subprocess, gzip, platform
from glob import glob
from . import depfixer
from . import destdir_join
-from ..mesonlib import Popen_safe
+from ..mesonlib import is_windows, Popen_safe
install_log_file = None
+def set_mode(path, mode):
+ if mode is None:
+ # Keep mode unchanged
+ return
+ if (mode.perms_s or mode.owner or mode.group) is None:
+ # Nothing to set
+ return
+ # No chown() on Windows, and must set one of owner/group
+ if not is_windows() and (mode.owner or mode.group) is not None:
+ try:
+ shutil.chown(path, mode.owner, mode.group)
+ except PermissionError as e:
+ msg = '{!r}: Unable to set owner {!r} and group {!r}: {}, ignoring...'
+ print(msg.format(path, mode.owner, mode.group, e.strerror))
+ # Must set permissions *after* setting owner/group otherwise the
+ # setuid/setgid bits will get wiped by chmod
+ # NOTE: On Windows you can set read/write perms; the rest are ignored
+ if mode.perms_s is not None:
+ try:
+ os.chmod(path, mode.perms)
+ except PermissionError as e:
+ msg = '{!r}: Unable to set permissions {!r}: {}, ignoring...'
+ print(msg.format(path, mode.perms_s, e.strerror))
+
def append_to_log(line):
install_log_file.write(line)
if not line.endswith('\n'):
@@ -96,7 +120,7 @@ def do_install(datafilename):
run_install_script(d)
def install_subdirs(data):
- for (src_dir, inst_dir, dst_dir) in data.install_subdirs:
+ for (src_dir, inst_dir, dst_dir, mode) in data.install_subdirs:
if src_dir.endswith('/') or src_dir.endswith('\\'):
src_dir = src_dir[:-1]
src_prefix = os.path.join(src_dir, inst_dir)
@@ -105,15 +129,19 @@ def install_subdirs(data):
if not os.path.exists(dst_dir):
os.makedirs(dst_dir)
do_copydir(src_prefix, src_dir, dst_dir)
+ dst_prefix = os.path.join(dst_dir, inst_dir)
+ set_mode(dst_prefix, mode)
def install_data(d):
for i in d.data:
fullfilename = i[0]
outfilename = get_destdir_path(d, i[1])
+ mode = i[2]
outdir = os.path.split(outfilename)[0]
os.makedirs(outdir, exist_ok=True)
print('Installing %s to %s.' % (fullfilename, outdir))
do_copyfile(fullfilename, outfilename)
+ set_mode(outfilename, mode)
def install_man(d):
for m in d.man:
diff --git a/mesontest.py b/mesontest.py
index bce4100..1a0c9b1 100755
--- a/mesontest.py
+++ b/mesontest.py
@@ -262,7 +262,8 @@ class TestHarness:
if timed_out:
res = 'TIMEOUT'
self.timeout_count += 1
- if p.returncode == GNU_SKIP_RETURNCODE:
+ self.fail_count += 1
+ elif p.returncode == GNU_SKIP_RETURNCODE:
res = 'SKIP'
self.skip_count += 1
elif test.should_fail == bool(p.returncode):
@@ -415,7 +416,7 @@ TIMEOUT: %4d
jsonlogfile = open(jsonlogfilename, 'w')
logfile = open(logfilename, 'w')
- logfile.write('Log of Meson test suite run on %s.\n\n'
+ logfile.write('Log of Meson test suite run on %s\n\n'
% datetime.datetime.now().isoformat())
return logfile, logfilename, jsonlogfile, jsonlogfilename
@@ -483,7 +484,7 @@ TIMEOUT: %4d
self.print_collected_logs()
if logfilename:
- print('Full log written to %s.' % logfilename)
+ print('Full log written to %s' % logfilename)
finally:
if jsonlogfile:
jsonlogfile.close()
diff --git a/run_project_tests.py b/run_project_tests.py
index 4715dbb..e04fed1 100755
--- a/run_project_tests.py
+++ b/run_project_tests.py
@@ -105,6 +105,8 @@ def setup_commands(backend):
global backend_flags, compile_commands, test_commands, install_commands, clean_commands
msbuild_exe = shutil.which('msbuild')
if (backend and backend.startswith('vs')) or (backend is None and msbuild_exe is not None):
+ if backend is None:
+ backend = 'vs2010'
backend_flags = ['--backend=' + backend]
compile_commands = ['msbuild']
test_commands = ['msbuild', 'RUN_TESTS.vcxproj']
diff --git a/run_unittests.py b/run_unittests.py
index 6aa5b2b..5eba222 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -13,12 +13,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import stat
import unittest, os, sys, shutil, time
import subprocess
import re, json
import tempfile
from glob import glob
import mesonbuild.environment
+import mesonbuild.mesonlib
from mesonbuild.environment import detect_ninja, Environment
from mesonbuild.dependencies import PkgConfigDependency
@@ -58,6 +60,42 @@ class InternalTests(unittest.TestCase):
self.assertEqual(searchfunc('foobar 2016.10.128'), 'unknown version')
self.assertEqual(searchfunc('2016.10.128'), 'unknown version')
+ def test_mode_symbolic_to_bits(self):
+ modefunc = mesonbuild.mesonlib.FileMode.perms_s_to_bits
+ self.assertEqual(modefunc('---------'), 0)
+ self.assertEqual(modefunc('r--------'), stat.S_IRUSR)
+ self.assertEqual(modefunc('---r-----'), stat.S_IRGRP)
+ self.assertEqual(modefunc('------r--'), stat.S_IROTH)
+ self.assertEqual(modefunc('-w-------'), stat.S_IWUSR)
+ self.assertEqual(modefunc('----w----'), stat.S_IWGRP)
+ self.assertEqual(modefunc('-------w-'), stat.S_IWOTH)
+ self.assertEqual(modefunc('--x------'), stat.S_IXUSR)
+ self.assertEqual(modefunc('-----x---'), stat.S_IXGRP)
+ self.assertEqual(modefunc('--------x'), stat.S_IXOTH)
+ self.assertEqual(modefunc('--S------'), stat.S_ISUID)
+ self.assertEqual(modefunc('-----S---'), stat.S_ISGID)
+ self.assertEqual(modefunc('--------T'), stat.S_ISVTX)
+ self.assertEqual(modefunc('--s------'), stat.S_ISUID | stat.S_IXUSR)
+ self.assertEqual(modefunc('-----s---'), stat.S_ISGID | stat.S_IXGRP)
+ self.assertEqual(modefunc('--------t'), stat.S_ISVTX | stat.S_IXOTH)
+ self.assertEqual(modefunc('rwx------'), stat.S_IRWXU)
+ self.assertEqual(modefunc('---rwx---'), stat.S_IRWXG)
+ self.assertEqual(modefunc('------rwx'), stat.S_IRWXO)
+ # We could keep listing combinations exhaustively but that seems
+ # tedious and pointless. Just test a few more.
+ self.assertEqual(modefunc('rwxr-xr-x'),
+ stat.S_IRWXU |
+ stat.S_IRGRP | stat.S_IXGRP |
+ stat.S_IROTH | stat.S_IXOTH)
+ self.assertEqual(modefunc('rw-r--r--'),
+ stat.S_IRUSR | stat.S_IWUSR |
+ stat.S_IRGRP |
+ stat.S_IROTH)
+ self.assertEqual(modefunc('rwsr-x---'),
+ stat.S_IRWXU | stat.S_ISUID |
+ stat.S_IRGRP | stat.S_IXGRP)
+
+
class LinuxlikeTests(unittest.TestCase):
def setUp(self):
super().setUp()
@@ -552,6 +590,57 @@ class LinuxlikeTests(unittest.TestCase):
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.
+ Can't be an ordinary test because our installed_files.txt is very basic.
+ '''
+ # Test file modes
+ testdir = os.path.join(self.common_test_dir, '12 data')
+ self.init(testdir)
+ self.install()
+
+ f = os.path.join(self.installdir, 'etc', 'etcfile.dat')
+ found_mode = stat.filemode(os.stat(f).st_mode)
+ want_mode = 'rw------T'
+ self.assertEqual(want_mode, found_mode[1:])
+
+ f = os.path.join(self.installdir, 'usr', 'bin', 'runscript.sh')
+ statf = os.stat(f)
+ found_mode = stat.filemode(statf.st_mode)
+ want_mode = 'rwxr-sr-x'
+ self.assertEqual(want_mode, found_mode[1:])
+ if os.getuid() == 0:
+ # The chown failed nonfatally if we're not root
+ self.assertEqual(0, statf.st_uid)
+ self.assertEqual(0, statf.st_gid)
+
+ f = os.path.join(self.installdir, 'usr', 'share', 'progname',
+ 'fileobject_datafile.dat')
+ statf = os.stat(f)
+ found_mode = stat.filemode(statf.st_mode)
+ want_mode = 'rw-rw-r--'
+ self.assertEqual(want_mode, found_mode[1:])
+ self.assertEqual(os.getuid(), statf.st_uid)
+ if os.getuid() == 0:
+ # The chown failed nonfatally if we're not root
+ self.assertEqual(0, statf.st_gid)
+
+ self.wipe()
+ # Test directory modes
+ testdir = os.path.join(self.common_test_dir, '66 install subdir')
+ self.init(testdir)
+ self.install()
+
+ f = os.path.join(self.installdir, 'usr', 'share', 'sub1')
+ statf = os.stat(f)
+ found_mode = stat.filemode(statf.st_mode)
+ want_mode = 'rwxr-x--t'
+ self.assertEqual(want_mode, found_mode[1:])
+ if os.getuid() == 0:
+ # The chown failed nonfatally if we're not root
+ self.assertEqual(0, statf.st_uid)
+
class RewriterTests(unittest.TestCase):
diff --git a/setup.py b/setup.py
index 5fed15a..e9bbdb4 100644
--- a/setup.py
+++ b/setup.py
@@ -74,6 +74,7 @@ setup(name='meson',
data_files=[('share/man/man1', ['man/meson.1',
'man/mesonconf.1',
'man/mesonintrospect.1',
+ 'main/mesontest.1',
'man/wraptool.1'])],
classifiers=['Development Status :: 5 - Production/Stable',
'Environment :: Console',
diff --git a/test cases/common/103 manygen/subdir/manygen.py b/test cases/common/103 manygen/subdir/manygen.py
index 8e74bcd..7ffd435 100755
--- a/test cases/common/103 manygen/subdir/manygen.py
+++ b/test cases/common/103 manygen/subdir/manygen.py
@@ -1,4 +1,6 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
+
+from __future__ import print_function
# Generates a static library, object file, source
# file and a header file.
diff --git a/test cases/common/103 manygen/subdir/meson.build b/test cases/common/103 manygen/subdir/meson.build
index 3036899..73b4ff7 100644
--- a/test cases/common/103 manygen/subdir/meson.build
+++ b/test cases/common/103 manygen/subdir/meson.build
@@ -1,4 +1,5 @@
-gen = find_program('manygen.py')
+gen = files('manygen.py')
+py3_bin = import('python3').find_python()
buildtype = get_option('buildtype')
buildtype_args = '-Dfooxxx' # a useless compiler argument
@@ -20,5 +21,5 @@ endif
generated = custom_target('manygen',
output : outfiles,
input : ['funcinfo.def'],
- command : [gen, '@INPUT@', '@OUTDIR@', buildtype_args],
+ command : [py3_bin, gen[0], '@INPUT@', '@OUTDIR@', buildtype_args],
)
diff --git a/test cases/common/105 find program path/meson.build b/test cases/common/105 find program path/meson.build
index 2ece2bc..ba6030b 100644
--- a/test cases/common/105 find program path/meson.build
+++ b/test cases/common/105 find program path/meson.build
@@ -2,8 +2,9 @@ project('find program', 'c')
prog = find_program('program.py')
-# Python 3 is guaranteed to be available because Meson
-# is implemented in it.
-python = find_program('python3')
+python = find_program('python3', required : false)
+if not python.found()
+ python = find_program('python')
+endif
run_command(python, prog.path())
diff --git a/test cases/common/105 find program path/program.py b/test cases/common/105 find program path/program.py
index 2ebc564..b910718 100644
--- a/test cases/common/105 find program path/program.py
+++ b/test cases/common/105 find program path/program.py
@@ -1,3 +1,3 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
print("Found")
diff --git a/test cases/common/107 postconf/postconf.py b/test cases/common/107 postconf/postconf.py
index 950c706..9a23cfa 100644
--- a/test cases/common/107 postconf/postconf.py
+++ b/test cases/common/107 postconf/postconf.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import os
diff --git a/test cases/common/108 postconf with args/postconf.py b/test cases/common/108 postconf with args/postconf.py
index cef7f79..3ed0450 100644
--- a/test cases/common/108 postconf with args/postconf.py
+++ b/test cases/common/108 postconf with args/postconf.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import sys, os
diff --git a/test cases/common/113 generatorcustom/catter.py b/test cases/common/113 generatorcustom/catter.py
index 198fa98..a79b739 100755
--- a/test cases/common/113 generatorcustom/catter.py
+++ b/test cases/common/113 generatorcustom/catter.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import sys
diff --git a/test cases/common/113 generatorcustom/gen.py b/test cases/common/113 generatorcustom/gen.py
index c1e34ed..f9efb47 100755
--- a/test cases/common/113 generatorcustom/gen.py
+++ b/test cases/common/113 generatorcustom/gen.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import sys
diff --git a/test cases/common/117 custom target capture/meson.build b/test cases/common/117 custom target capture/meson.build
index fa59d51..d3214e8 100644
--- a/test cases/common/117 custom target capture/meson.build
+++ b/test cases/common/117 custom target capture/meson.build
@@ -1,6 +1,9 @@
project('custom target', 'c')
-python = find_program('python3')
+python3 = find_program('python3', required : false)
+if not python3.found()
+ python3 = find_program('python')
+endif
# Note that this will not add a dependency to the compiler executable.
# Code will not be rebuilt if it changes.
@@ -10,7 +13,7 @@ mytarget = custom_target('bindat',
output : 'data.dat',
input : 'data_source.txt',
capture : true,
- command : [python, comp, '@INPUT@'],
+ command : [python3, comp, '@INPUT@'],
install : true,
install_dir : 'subdir'
)
diff --git a/test cases/common/118 allgenerate/converter.py b/test cases/common/118 allgenerate/converter.py
index f8e2ca0..cc2c574 100755
--- a/test cases/common/118 allgenerate/converter.py
+++ b/test cases/common/118 allgenerate/converter.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import sys
diff --git a/test cases/common/12 data/installed_files.txt b/test cases/common/12 data/installed_files.txt
index 8651e3a..af1a735 100644
--- a/test cases/common/12 data/installed_files.txt
+++ b/test cases/common/12 data/installed_files.txt
@@ -3,3 +3,4 @@ usr/share/progname/fileobject_datafile.dat
usr/share/progname/vanishing.dat
usr/share/progname/vanishing2.dat
etc/etcfile.dat
+usr/bin/runscript.sh
diff --git a/test cases/common/12 data/meson.build b/test cases/common/12 data/meson.build
index 7494abc..d3407d1 100644
--- a/test cases/common/12 data/meson.build
+++ b/test cases/common/12 data/meson.build
@@ -1,7 +1,14 @@
project('data install test', 'c')
install_data(sources : 'datafile.dat', install_dir : 'share/progname')
-install_data(sources : 'etcfile.dat', install_dir : '/etc')
-install_data(files('fileobject_datafile.dat'), install_dir : 'share/progname')
+# Some file in /etc that is only read-write by root; add a sticky bit for testing
+install_data(sources : 'etcfile.dat', install_dir : '/etc', install_mode : 'rw------T')
+# Some script that needs to be executable by the group
+install_data('runscript.sh',
+ install_dir : get_option('bindir'),
+ install_mode : ['rwxr-sr-x', 'root', 0])
+install_data(files('fileobject_datafile.dat'),
+ install_dir : 'share/progname',
+ install_mode : [false, false, 0])
subdir('vanishing')
diff --git a/test cases/common/12 data/runscript.sh b/test cases/common/12 data/runscript.sh
new file mode 100644
index 0000000..8bc5ca6
--- /dev/null
+++ b/test cases/common/12 data/runscript.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo "Runscript"
diff --git a/test cases/common/131 custom target directory install/docgen.py b/test cases/common/131 custom target directory install/docgen.py
index 245f370..4d80124 100644
--- a/test cases/common/131 custom target directory install/docgen.py
+++ b/test cases/common/131 custom target directory install/docgen.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import os
import sys
diff --git a/test cases/common/133 configure file in generator/src/gen.py b/test cases/common/133 configure file in generator/src/gen.py
index 99b7cdd..5bccece 100755
--- a/test cases/common/133 configure file in generator/src/gen.py
+++ b/test cases/common/133 configure file in generator/src/gen.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import sys
diff --git a/test cases/common/16 configure file/generator.py b/test cases/common/16 configure file/generator.py
index 31a6944..2c7f2f8 100755
--- a/test cases/common/16 configure file/generator.py
+++ b/test cases/common/16 configure file/generator.py
@@ -1,5 +1,9 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
+# On some platforms "python" points to Python 2
+# on others to Python 3. Work with both.
+
+from __future__ import print_function
import sys, os
if len(sys.argv) != 3:
diff --git a/test cases/common/16 configure file/meson.build b/test cases/common/16 configure file/meson.build
index 0c5b981..b764c5a 100644
--- a/test cases/common/16 configure file/meson.build
+++ b/test cases/common/16 configure file/meson.build
@@ -23,7 +23,10 @@ cfile)
test('inctest', e)
# Now generate a header file with an external script.
-genprog = find_program('python3')
+genprog = find_program('python3', required : false)
+if not genprog.found()
+ genprog = find_program('python')
+endif
scriptfile = '@0@/generator.py'.format(meson.current_source_dir())
ifile = '@0@/dummy.dat'.format(meson.current_source_dir())
ofile = '@0@/config2.h'.format(meson.current_build_dir())
diff --git a/test cases/common/48 test args/tester.py b/test cases/common/48 test args/tester.py
index 0b4010a..c3c1edc 100755
--- a/test cases/common/48 test args/tester.py
+++ b/test cases/common/48 test args/tester.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import sys
diff --git a/test cases/common/56 custom target/depfile/dep.py b/test cases/common/56 custom target/depfile/dep.py
index 585e192..aff325b 100755
--- a/test cases/common/56 custom target/depfile/dep.py
+++ b/test cases/common/56 custom target/depfile/dep.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import sys, os
from glob import glob
diff --git a/test cases/common/56 custom target/meson.build b/test cases/common/56 custom target/meson.build
index feaa762..fd59fbd 100644
--- a/test cases/common/56 custom target/meson.build
+++ b/test cases/common/56 custom target/meson.build
@@ -1,6 +1,9 @@
project('custom target', 'c')
-python = find_program('python3')
+python = find_program('python3', required : false)
+if not python.found()
+ python = find_program('python')
+endif
# Note that this will not add a dependency to the compiler executable.
# Code will not be rebuilt if it changes.
diff --git a/test cases/common/57 custom target chain/meson.build b/test cases/common/57 custom target chain/meson.build
index 1af0425..138f795 100644
--- a/test cases/common/57 custom target chain/meson.build
+++ b/test cases/common/57 custom target chain/meson.build
@@ -1,7 +1,12 @@
project('custom target', 'c')
-python = find_program('python3')
+python = find_program('python3', required : false)
+if not python.found()
+ python = find_program('python')
+endif
+# files() is the correct way to do this, but some people
+# do this so test that it works.
comp = '@0@/@1@'.format(meson.current_source_dir(), 'my_compiler.py')
comp2 = '@0@/@1@'.format(meson.current_source_dir(), 'my_compiler2.py')
infile = files('data_source.txt')[0]
diff --git a/test cases/common/57 custom target chain/my_compiler.py b/test cases/common/57 custom target chain/my_compiler.py
index d99029b..9cf4425 100755
--- a/test cases/common/57 custom target chain/my_compiler.py
+++ b/test cases/common/57 custom target chain/my_compiler.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import sys
diff --git a/test cases/common/57 custom target chain/my_compiler2.py b/test cases/common/57 custom target chain/my_compiler2.py
index 22ec789..0191f3f 100755
--- a/test cases/common/57 custom target chain/my_compiler2.py
+++ b/test cases/common/57 custom target chain/my_compiler2.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import sys
diff --git a/test cases/common/57 custom target chain/usetarget/subcomp.py b/test cases/common/57 custom target chain/usetarget/subcomp.py
index 52dc0bb..b5f6eb0 100755
--- a/test cases/common/57 custom target chain/usetarget/subcomp.py
+++ b/test cases/common/57 custom target chain/usetarget/subcomp.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import sys
diff --git a/test cases/common/58 run target/converter.py b/test cases/common/58 run target/converter.py
index 8dd31fe..9f47ba5 100644
--- a/test cases/common/58 run target/converter.py
+++ b/test cases/common/58 run target/converter.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import sys
diff --git a/test cases/common/58 run target/fakeburner.py b/test cases/common/58 run target/fakeburner.py
index 5728002..7f505d6 100755
--- a/test cases/common/58 run target/fakeburner.py
+++ b/test cases/common/58 run target/fakeburner.py
@@ -1,4 +1,6 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
+
+from __future__ import print_function
import sys
diff --git a/test cases/common/58 run target/meson.build b/test cases/common/58 run target/meson.build
index 0540b80..8a06490 100644
--- a/test cases/common/58 run target/meson.build
+++ b/test cases/common/58 run target/meson.build
@@ -31,7 +31,11 @@ run_target('upload2',
depends : hex,
)
-python3 = find_program('python3')
+python3 = find_program('python3', required : false)
+if not python3.found()
+ python3 = find_program('python')
+endif
+
run_target('py3hi',
command : [python3, '-c', 'print("I am Python3.")'])
diff --git a/test cases/common/59 object generator/meson.build b/test cases/common/59 object generator/meson.build
index 0bdefb8..e20da6f 100644
--- a/test cases/common/59 object generator/meson.build
+++ b/test cases/common/59 object generator/meson.build
@@ -1,6 +1,9 @@
project('object generator', 'c')
-python = find_program('python3')
+python = find_program('python3', required : false)
+if not python.found()
+ python = find_program('python')
+endif
# Note that this will not add a dependency to the compiler executable.
# Code will not be rebuilt if it changes.
diff --git a/test cases/common/61 custom target source output/generator.py b/test cases/common/61 custom target source output/generator.py
index 3464b0a..42532ca 100755
--- a/test cases/common/61 custom target source output/generator.py
+++ b/test cases/common/61 custom target source output/generator.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import sys, os
diff --git a/test cases/common/64 custom header generator/makeheader.py b/test cases/common/64 custom header generator/makeheader.py
index f156834..0c5a228 100644
--- a/test cases/common/64 custom header generator/makeheader.py
+++ b/test cases/common/64 custom header generator/makeheader.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
# NOTE: this file does not have the executable bit set. This tests that
# Meson can automatically parse shebang lines.
diff --git a/test cases/common/65 multiple generators/mygen.py b/test cases/common/65 multiple generators/mygen.py
index 99dc331..020a389 100755
--- a/test cases/common/65 multiple generators/mygen.py
+++ b/test cases/common/65 multiple generators/mygen.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import sys, os
diff --git a/test cases/common/66 install subdir/meson.build b/test cases/common/66 install subdir/meson.build
index 669cf09..fed2f89 100644
--- a/test cases/common/66 install subdir/meson.build
+++ b/test cases/common/66 install subdir/meson.build
@@ -1,5 +1,7 @@
project('install a whole subdir', 'c')
subdir('subdir')
-install_subdir('sub1', install_dir : 'share')
+# A subdir with write perms only for the owner
+# and read-list perms for owner and group
+install_subdir('sub1', install_dir : 'share', install_mode : ['rwxr-x--t', 'root'])
install_subdir('sub/sub1', install_dir : 'share')
diff --git a/test cases/common/66 install subdir/subdir/meson.build b/test cases/common/66 install subdir/subdir/meson.build
index 08b417d..37d2da4 100644
--- a/test cases/common/66 install subdir/subdir/meson.build
+++ b/test cases/common/66 install subdir/subdir/meson.build
@@ -1 +1,3 @@
-install_subdir('sub1', install_dir : 'share')
+install_subdir('sub1', install_dir : 'share',
+ # This mode will be overriden by the mode set in the outer install_subdir
+ install_mode : 'rwxr-x---')
diff --git a/test cases/common/72 build always/version_gen.py b/test cases/common/72 build always/version_gen.py
index d7b01ca..3973e61 100755
--- a/test cases/common/72 build always/version_gen.py
+++ b/test cases/common/72 build always/version_gen.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import sys, os, subprocess
diff --git a/test cases/common/76 configure file in custom target/src/mycompiler.py b/test cases/common/76 configure file in custom target/src/mycompiler.py
index b00c862..e1750f8 100644
--- a/test cases/common/76 configure file in custom target/src/mycompiler.py
+++ b/test cases/common/76 configure file in custom target/src/mycompiler.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import sys
diff --git a/test cases/common/77 external test program/mytest.py b/test cases/common/77 external test program/mytest.py
index 03d88b8..7cdaf09 100755
--- a/test cases/common/77 external test program/mytest.py
+++ b/test cases/common/77 external test program/mytest.py
@@ -1,4 +1,6 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
+
+from __future__ import print_function
import sys
diff --git a/test cases/common/78 ctarget dependency/gen1.py b/test cases/common/78 ctarget dependency/gen1.py
index 0fa6ea1..f920e53 100755
--- a/test cases/common/78 ctarget dependency/gen1.py
+++ b/test cases/common/78 ctarget dependency/gen1.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import time, sys
diff --git a/test cases/common/78 ctarget dependency/gen2.py b/test cases/common/78 ctarget dependency/gen2.py
index b087b02..fc60e1e 100755
--- a/test cases/common/78 ctarget dependency/gen2.py
+++ b/test cases/common/78 ctarget dependency/gen2.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import sys, os
from glob import glob
diff --git a/test cases/common/93 private include/stlib/compiler.py b/test cases/common/93 private include/stlib/compiler.py
index 98dbe46..0555c16 100755
--- a/test cases/common/93 private include/stlib/compiler.py
+++ b/test cases/common/93 private include/stlib/compiler.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import sys, os
diff --git a/test cases/common/98 gen extra/srcgen.py b/test cases/common/98 gen extra/srcgen.py
index 8988cd9..86fd698 100755
--- a/test cases/common/98 gen extra/srcgen.py
+++ b/test cases/common/98 gen extra/srcgen.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import sys
import argparse
diff --git a/test cases/failing/41 kwarg assign/dummy.c b/test cases/failing/41 kwarg assign/dummy.c
new file mode 100644
index 0000000..16fcdd9
--- /dev/null
+++ b/test cases/failing/41 kwarg assign/dummy.c
@@ -0,0 +1,3 @@
+const char* dummy() {
+ return "I do nothing.";
+}
diff --git a/test cases/failing/41 kwarg assign/meson.build b/test cases/failing/41 kwarg assign/meson.build
new file mode 100644
index 0000000..c86786f
--- /dev/null
+++ b/test cases/failing/41 kwarg assign/meson.build
@@ -0,0 +1,4 @@
+project('assign in kwarg', 'c')
+
+executable('prog', 'dummy.c', args = 'prog.c')
+
diff --git a/test cases/failing/41 kwarg assign/prog.c b/test cases/failing/41 kwarg assign/prog.c
new file mode 100644
index 0000000..11b7fad
--- /dev/null
+++ b/test cases/failing/41 kwarg assign/prog.c
@@ -0,0 +1,3 @@
+int main(int argc, char **argv) {
+ return 0;
+}