aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--authors.txt1
-rw-r--r--mesonbuild/backend/backends.py56
-rw-r--r--mesonbuild/build.py37
-rw-r--r--mesonbuild/compilers.py10
-rw-r--r--mesonbuild/environment.py82
-rw-r--r--mesonbuild/interpreter.py37
-rw-r--r--mesonbuild/mesonlib.py151
-rw-r--r--mesonbuild/modules/gnome.py62
-rw-r--r--mesonbuild/modules/rpm.py2
-rwxr-xr-xmesontest.py48
-rwxr-xr-xrun_tests.py8
-rwxr-xr-xrun_unittests.py151
-rw-r--r--syntax-highlighting/vim/syntax/meson.vim3
-rw-r--r--test cases/common/118 allgenerate/meson.build2
-rw-r--r--test cases/common/129 object only target/meson.build2
-rw-r--r--test cases/common/16 configure file/check_file.py6
-rwxr-xr-xtest cases/common/16 configure file/generator.py16
-rw-r--r--test cases/common/16 configure file/installed_files.txt2
-rw-r--r--test cases/common/16 configure file/meson.build18
-rw-r--r--test cases/common/16 configure file/subdir/meson.build19
-rw-r--r--test cases/failing/42 custom target plainname many inputs/1.txt1
-rw-r--r--test cases/failing/42 custom target plainname many inputs/2.txt1
-rw-r--r--test cases/failing/42 custom target plainname many inputs/catfiles.py9
-rw-r--r--test cases/failing/42 custom target plainname many inputs/meson.build8
-rw-r--r--test cases/frameworks/7 gnome/resources/copyfile.py6
-rw-r--r--test cases/frameworks/7 gnome/resources/meson.build11
26 files changed, 588 insertions, 161 deletions
diff --git a/authors.txt b/authors.txt
index a5f3d46..c75f84b 100644
--- a/authors.txt
+++ b/authors.txt
@@ -64,3 +64,4 @@ Philipp Geier
Mike Sinkovsky
Dima Krasner
Fabio Porcedda
+Rodrigo Lourenço
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 7372c4c..d6f1c38 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -603,19 +603,15 @@ class Backend:
return srcs
def eval_custom_target_command(self, target, absolute_outputs=False):
- # We only want the outputs to be absolute when using the VS backend
- if not absolute_outputs:
- ofilenames = [os.path.join(self.get_target_dir(target), i) for i in target.output]
- else:
- ofilenames = [os.path.join(self.environment.get_build_dir(), self.get_target_dir(target), i)
- for i in target.output]
- srcs = self.get_custom_target_sources(target)
+ # We want the outputs to be absolute only when using the VS backend
outdir = self.get_target_dir(target)
- # Many external programs fail on empty arguments.
- if outdir == '':
- outdir = '.'
- if target.absolute_paths:
+ if absolute_outputs:
outdir = os.path.join(self.environment.get_build_dir(), outdir)
+ outputs = []
+ for i in target.output:
+ outputs.append(os.path.join(outdir, i))
+ inputs = self.get_custom_target_sources(target)
+ # Evaluate the command list
cmd = []
for i in target.command:
if isinstance(i, build.Executable):
@@ -631,37 +627,10 @@ class Backend:
if target.absolute_paths:
i = os.path.join(self.environment.get_build_dir(), i)
# FIXME: str types are blindly added ignoring 'target.absolute_paths'
+ # because we can't know if they refer to a file or just a string
elif not isinstance(i, str):
err_msg = 'Argument {0} is of unknown type {1}'
raise RuntimeError(err_msg.format(str(i), str(type(i))))
- for (j, src) in enumerate(srcs):
- i = i.replace('@INPUT%d@' % j, src)
- for (j, res) in enumerate(ofilenames):
- i = i.replace('@OUTPUT%d@' % j, res)
- if '@INPUT@' in i:
- msg = 'Custom target {} has @INPUT@ in the command, but'.format(target.name)
- if len(srcs) == 0:
- raise MesonException(msg + ' no input files')
- if i == '@INPUT@':
- cmd += srcs
- continue
- else:
- if len(srcs) > 1:
- raise MesonException(msg + ' more than one input file')
- i = i.replace('@INPUT@', srcs[0])
- elif '@OUTPUT@' in i:
- msg = 'Custom target {} has @OUTPUT@ in the command, but'.format(target.name)
- if len(ofilenames) == 0:
- raise MesonException(msg + ' no output files')
- if i == '@OUTPUT@':
- cmd += ofilenames
- continue
- else:
- if len(ofilenames) > 1:
- raise MesonException(msg + ' more than one output file')
- i = i.replace('@OUTPUT@', ofilenames[0])
- elif '@OUTDIR@' in i:
- i = i.replace('@OUTDIR@', outdir)
elif '@DEPFILE@' in i:
if target.depfile is None:
msg = 'Custom target {!r} has @DEPFILE@ but no depfile ' \
@@ -680,10 +649,11 @@ class Backend:
lead_dir = ''
else:
lead_dir = self.environment.get_build_dir()
- i = i.replace(source,
- os.path.join(lead_dir,
- outdir))
+ i = i.replace(source, os.path.join(lead_dir, outdir))
cmd.append(i)
+ # Substitute the rest of the template strings
+ values = mesonlib.get_filenames_templates_dict(inputs, outputs)
+ cmd = mesonlib.substitute_values(cmd, values)
# This should not be necessary but removing it breaks
# building GStreamer on Windows. The underlying issue
# is problems with quoting backslashes on Windows
@@ -703,7 +673,7 @@ class Backend:
#
# https://github.com/mesonbuild/meson/pull/737
cmd = [i.replace('\\', '/') for i in cmd]
- return srcs, ofilenames, cmd
+ return inputs, outputs, cmd
def run_postconf_scripts(self):
env = {'MESON_SOURCE_ROOT': self.environment.get_source_dir(),
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 7ab656e..28456f7 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -16,7 +16,9 @@ from . import environment
from . import dependencies
from . import mlog
import copy, os, re
-from .mesonlib import File, flatten, MesonException, stringlistify, classify_unity_sources
+from .mesonlib import File, MesonException
+from .mesonlib import flatten, stringlistify, classify_unity_sources
+from .mesonlib import get_filenames_templates_dict, substitute_values
from .environment import for_windows, for_darwin
from .compilers import is_object, clike_langs, lang_suffixes
@@ -1335,11 +1337,25 @@ class CustomTarget(Target):
self.output = kwargs['output']
if not isinstance(self.output, list):
self.output = [self.output]
+ # This will substitute values from the input into output and return it.
+ inputs = get_sources_string_names(self.sources)
+ values = get_filenames_templates_dict(inputs, [])
for i in self.output:
if not(isinstance(i, str)):
raise InvalidArguments('Output argument not a string.')
if '/' in i:
raise InvalidArguments('Output must not contain a path segment.')
+ if '@INPUT@' in i or '@INPUT0@' in i:
+ m = 'Output cannot contain @INPUT@ or @INPUT0@, did you ' \
+ 'mean @PLAINNAME@ or @BASENAME@?'
+ raise InvalidArguments(m)
+ # We already check this during substitution, but the error message
+ # will be unclear/confusing, so check it here.
+ if len(inputs) != 1 and ('@PLAINNAME@' in i or '@BASENAME@' in i):
+ m = "Output cannot contain @PLAINNAME@ or @BASENAME@ when " \
+ "there is more than one input (we can't know which to use)"
+ raise InvalidArguments(m)
+ self.output = substitute_values(self.output, values)
self.capture = kwargs.get('capture', False)
if self.capture and len(self.output) != 1:
raise InvalidArguments('Capturing can only output to a single file.')
@@ -1534,3 +1550,22 @@ class TestSetup:
self.gdb = gdb
self.timeout_multiplier = timeout_multiplier
self.env = env
+
+def get_sources_string_names(sources):
+ '''
+ For the specified list of @sources which can be strings, Files, or targets,
+ get all the output basenames.
+ '''
+ names = []
+ for s in sources:
+ if hasattr(s, 'held_object'):
+ s = s.held_object
+ if isinstance(s, str):
+ names.append(s)
+ elif isinstance(s, (BuildTarget, CustomTarget, GeneratedList)):
+ names += s.get_outputs()
+ elif isinstance(s, File):
+ names.append(s.fname)
+ else:
+ raise AssertionError('Unknown source type: {!r}'.format(s))
+ return names
diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py
index 5351111..30f2608 100644
--- a/mesonbuild/compilers.py
+++ b/mesonbuild/compilers.py
@@ -188,6 +188,11 @@ gnu_color_args = {'auto': ['-fdiagnostics-color=auto'],
'never': ['-fdiagnostics-color=never'],
}
+clang_color_args = {'auto': ['-Xclang', '-fcolor-diagnostics'],
+ 'always': ['-Xclang', '-fcolor-diagnostics'],
+ 'never': ['-Xclang', '-fno-color-diagnostics'],
+ }
+
base_options = {'b_pch': coredata.UserBooleanOption('b_pch', 'Use precompiled headers', True),
'b_lto': coredata.UserBooleanOption('b_lto', 'Use link time optimization', False),
'b_sanitize': coredata.UserComboOption('b_sanitize',
@@ -2427,7 +2432,7 @@ class ClangCompiler:
self.id = 'clang'
self.clang_type = clang_type
self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage',
- 'b_ndebug', 'b_staticpic']
+ 'b_ndebug', 'b_staticpic', 'b_colorout']
if self.clang_type != CLANG_OSX:
self.base_options.append('b_lundef')
self.base_options.append('b_asneeded')
@@ -2439,6 +2444,9 @@ class ClangCompiler:
return [] # On Window and OS X, pic is always on.
return ['-fPIC']
+ def get_colorout_args(self, colortype):
+ return clang_color_args[colortype][:]
+
def get_buildtype_args(self, buildtype):
return gnulike_buildtype_args[buildtype]
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 13b38d5..cbfa3ec 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -370,9 +370,7 @@ 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']
- if not isinstance(compilers, list):
- compilers = [compilers]
+ compilers = mesonlib.stringintlistify(self.cross_info.config['binaries']['c'])
ccache = []
is_cross = True
if self.cross_info.need_exe_wrapper():
@@ -391,41 +389,43 @@ class Environment:
exe_wrap = None
popen_exceptions = {}
for compiler in compilers:
+ if isinstance(compiler, str):
+ compiler = [compiler]
try:
- basename = os.path.basename(compiler).lower()
+ basename = os.path.basename(compiler[-1]).lower()
if basename == 'cl' or basename == 'cl.exe':
arg = '/?'
else:
arg = '--version'
- p, out, err = Popen_safe([compiler, arg])
+ p, out, err = Popen_safe(compiler + [arg])
except OSError as e:
- popen_exceptions[' '.join([compiler, arg])] = e
+ popen_exceptions[' '.join(compiler + [arg])] = e
continue
version = search_version(out)
if 'Free Software Foundation' in out:
- defines = self.get_gnu_compiler_defines([compiler])
+ defines = self.get_gnu_compiler_defines(compiler)
if not defines:
popen_exceptions[compiler] = 'no pre-processor defines'
continue
gtype = self.get_gnu_compiler_type(defines)
version = self.get_gnu_version_from_defines(defines)
- return GnuCCompiler(ccache + compilers, version, gtype, is_cross, exe_wrap, defines)
+ return GnuCCompiler(ccache + compiler, 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 + compilers, version, cltype, is_cross, exe_wrap)
+ return ClangCCompiler(ccache + compiler, 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.
version = search_version(err)
- return VisualStudioCCompiler([compiler], version, is_cross, exe_wrap)
+ return VisualStudioCCompiler(compiler, version, is_cross, exe_wrap)
if '(ICC)' in out:
# TODO: add microsoft add check OSX
inteltype = ICC_STANDARD
- return IntelCCompiler(ccache + compilers, version, inteltype, is_cross, exe_wrap)
- errmsg = 'Unknown compiler(s): "' + ', '.join(compilers) + '"'
+ return IntelCCompiler(ccache + compiler, version, inteltype, is_cross, exe_wrap)
+ errmsg = 'Unknown compiler(s): ' + str(compilers)
if popen_exceptions:
errmsg += '\nThe follow exceptions were encountered:'
for (c, e) in popen_exceptions.items():
@@ -435,7 +435,7 @@ class Environment:
def detect_fortran_compiler(self, want_cross):
evar = 'FC'
if self.is_cross_build() and want_cross:
- compilers = [self.cross_info['fortran']]
+ compilers = meson.stringlistify(self.cross_info['fortran'])
is_cross = True
if self.cross_info.need_exe_wrapper():
exe_wrap = self.cross_info.get('exe_wrapper', None)
@@ -451,46 +451,48 @@ class Environment:
exe_wrap = None
popen_exceptions = {}
for compiler in compilers:
+ if not isinstance(compiler, list):
+ compiler = [compiler]
for arg in ['--version', '-V']:
try:
- p, out, err = Popen_safe([compiler, arg])
+ p, out, err = Popen_safe(compiler + [arg])
except OSError as e:
- popen_exceptions[' '.join([compiler, arg])] = e
+ popen_exceptions[' '.join(compiler + [arg])] = e
continue
version = search_version(out)
if 'GNU Fortran' in out:
- defines = self.get_gnu_compiler_defines([compiler])
+ defines = self.get_gnu_compiler_defines(compiler)
if not defines:
popen_exceptions[compiler] = 'no pre-processor defines'
continue
gtype = self.get_gnu_compiler_type(defines)
version = self.get_gnu_version_from_defines(defines)
- return GnuFortranCompiler([compiler], version, gtype, is_cross, exe_wrap, defines)
+ return GnuFortranCompiler(compiler, version, gtype, is_cross, exe_wrap, defines)
if 'G95' in out:
- return G95FortranCompiler([compiler], version, is_cross, exe_wrap)
+ return G95FortranCompiler(compiler, version, is_cross, exe_wrap)
if 'Sun Fortran' in err:
version = search_version(err)
- return SunFortranCompiler([compiler], version, is_cross, exe_wrap)
+ return SunFortranCompiler(compiler, version, is_cross, exe_wrap)
if 'ifort (IFORT)' in out:
- return IntelFortranCompiler([compiler], version, is_cross, exe_wrap)
+ return IntelFortranCompiler(compiler, version, is_cross, exe_wrap)
if 'PathScale EKOPath(tm)' in err:
- return PathScaleFortranCompiler([compiler], version, is_cross, exe_wrap)
+ return PathScaleFortranCompiler(compiler, version, is_cross, exe_wrap)
if 'PGI Compilers' in out:
- return PGIFortranCompiler([compiler], version, is_cross, exe_wrap)
+ return PGIFortranCompiler(compiler, version, is_cross, exe_wrap)
if 'Open64 Compiler Suite' in err:
- return Open64FortranCompiler([compiler], version, is_cross, exe_wrap)
+ return Open64FortranCompiler(compiler, version, is_cross, exe_wrap)
if 'NAG Fortran' in err:
- return NAGFortranCompiler([compiler], version, is_cross, exe_wrap)
- errmsg = 'Unknown compiler(s): "' + ', '.join(compilers) + '"'
+ return NAGFortranCompiler(compiler, version, is_cross, exe_wrap)
+ errmsg = 'Unknown compiler(s): ' + str(compilers)
if popen_exceptions:
errmsg += '\nThe follow exceptions were encountered:'
for (c, e) in popen_exceptions.items():
@@ -507,7 +509,7 @@ class Environment:
def detect_cpp_compiler(self, want_cross):
evar = 'CXX'
if self.is_cross_build() and want_cross:
- compilers = [self.cross_info.config['binaries']['cpp']]
+ compilers = mesonlib.stringlistify(self.cross_info.config['binaries']['cpp'])
ccache = []
is_cross = True
if self.cross_info.need_exe_wrapper():
@@ -515,7 +517,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
@@ -526,38 +528,40 @@ class Environment:
exe_wrap = None
popen_exceptions = {}
for compiler in compilers:
- basename = os.path.basename(compiler).lower()
+ if isinstance(compiler, str):
+ compiler = [compiler]
+ basename = os.path.basename(compiler[-1]).lower()
if basename == 'cl' or basename == 'cl.exe':
arg = '/?'
else:
arg = '--version'
try:
- p, out, err = Popen_safe([compiler, arg])
+ p, out, err = Popen_safe(compiler + [arg])
except OSError as e:
- popen_exceptions[' '.join([compiler, arg])] = e
+ popen_exceptions[' '.join(compiler + [arg])] = e
continue
version = search_version(out)
if 'Free Software Foundation' in out:
- defines = self.get_gnu_compiler_defines([compiler])
+ defines = self.get_gnu_compiler_defines(compiler)
if not defines:
popen_exceptions[compiler] = 'no pre-processor defines'
continue
gtype = self.get_gnu_compiler_type(defines)
version = self.get_gnu_version_from_defines(defines)
- return GnuCPPCompiler(ccache + [compiler], version, gtype, is_cross, exe_wrap, defines)
+ return GnuCPPCompiler(ccache + compiler, version, gtype, is_cross, exe_wrap, defines)
if 'clang' in out:
if 'Apple' in out:
cltype = CLANG_OSX
else:
cltype = CLANG_STANDARD
- return ClangCPPCompiler(ccache + [compiler], version, cltype, is_cross, exe_wrap)
+ return ClangCPPCompiler(ccache + compiler, version, cltype, is_cross, exe_wrap)
if 'Microsoft' in out or 'Microsoft' in err:
version = search_version(err)
- return VisualStudioCPPCompiler([compiler], version, is_cross, exe_wrap)
+ return VisualStudioCPPCompiler(compiler, version, is_cross, exe_wrap)
if '(ICC)' in out:
# TODO: add microsoft add check OSX
inteltype = ICC_STANDARD
- return IntelCPPCompiler(ccache + [compiler], version, inteltype, is_cross, exe_wrap)
+ return IntelCPPCompiler(ccache + compiler, version, inteltype, is_cross, exe_wrap)
errmsg = 'Unknown compiler(s): "' + ', '.join(compilers) + '"'
if popen_exceptions:
errmsg += '\nThe follow exceptions were encountered:'
@@ -567,7 +571,7 @@ class Environment:
def detect_objc_compiler(self, want_cross):
if self.is_cross_build() and want_cross:
- exelist = [self.cross_info['objc']]
+ exelist = mesonlib.stringlistify(self.cross_info['objc'])
is_cross = True
if self.cross_info.need_exe_wrapper():
exe_wrap = self.cross_info.get('exe_wrapper', None)
@@ -592,7 +596,7 @@ class Environment:
def detect_objcpp_compiler(self, want_cross):
if self.is_cross_build() and want_cross:
- exelist = [self.cross_info['objcpp']]
+ exelist = mesonlib.stringlistify(self.cross_info['objcpp'])
is_cross = True
if self.cross_info.need_exe_wrapper():
exe_wrap = self.cross_info.get('exe_wrapper', None)
@@ -665,9 +669,9 @@ class Environment:
# environment variable because LDC has a much more
# up to date language version at time (2016).
if 'DC' in os.environ:
- exelist = os.environ['DC'].split()
+ exelist = shlex.split(os.environ['DC'])
elif self.is_cross_build() and want_cross:
- exelist = [self.cross_info.config['binaries']['d']]
+ exelist = mesonlib.stringlistify(self.cross_info.config['binaries']['d'])
is_cross = True
elif shutil.which("ldc2"):
exelist = ['ldc2']
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index ba1ebf3..07b5c40 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -2228,12 +2228,28 @@ requirements use the version keyword argument instead.''')
raise InterpreterException("configure_file takes only keyword arguments.")
if 'output' not in kwargs:
raise InterpreterException('Required keyword argument "output" not defined.')
- inputfile = kwargs.get('input', None)
+ if 'configuration' in kwargs and 'command' in kwargs:
+ raise InterpreterException('Must not specify both "configuration" '
+ 'and "command" keyword arguments since '
+ 'they are mutually exclusive.')
+ # Validate input
+ inputfile = None
+ if 'input' in kwargs:
+ inputfile = kwargs['input']
+ if isinstance(inputfile, list):
+ if len(inputfile) != 1:
+ m = "Keyword argument 'input' requires exactly one file"
+ raise InterpreterException(m)
+ inputfile = inputfile[0]
+ if not isinstance(inputfile, (str, mesonlib.File)):
+ raise InterpreterException('Input must be a string or a file')
+ ifile_abs = os.path.join(self.environment.source_dir, self.subdir, inputfile)
+ elif 'command' in kwargs:
+ raise InterpreterException('Required keyword argument \'input\' missing')
+ # Validate output
output = kwargs['output']
- if not isinstance(inputfile, (str, type(None))):
- raise InterpreterException('Input must be a string.')
if not isinstance(output, str):
- raise InterpreterException('Output must be a string.')
+ raise InterpreterException('Output file name must be a string')
if os.path.split(output)[0] != '':
raise InterpreterException('Output file name must not contain a subdirectory.')
(ofile_path, ofile_fname) = os.path.split(os.path.join(self.subdir, output))
@@ -2242,6 +2258,7 @@ requirements use the version keyword argument instead.''')
conf = kwargs['configuration']
if not isinstance(conf, ConfigurationDataHolder):
raise InterpreterException('Argument "configuration" is not of type configuration_data')
+ mlog.log('Configuring', mlog.bold(output), 'using configuration')
if inputfile is not None:
# Normalize the path of the conffile to avoid duplicates
# This is especially important to convert '/' to '\' on Windows
@@ -2249,15 +2266,19 @@ requirements use the version keyword argument instead.''')
if conffile not in self.build_def_files:
self.build_def_files.append(conffile)
os.makedirs(os.path.join(self.environment.build_dir, self.subdir), exist_ok=True)
- ifile_abs = os.path.join(self.environment.source_dir, self.subdir, inputfile)
mesonlib.do_conf_file(ifile_abs, ofile_abs, conf.held_object)
else:
mesonlib.dump_conf_header(ofile_abs, conf.held_object)
conf.mark_used()
elif 'command' in kwargs:
- if 'input' not in kwargs:
- raise InterpreterException('Required keyword input missing.')
- res = self.func_run_command(node, kwargs['command'], {})
+ # We use absolute paths for input and output here because the cwd
+ # that the command is run from is 'unspecified', so it could change.
+ # Currently it's builddir/subdir for in_builddir else srcdir/subdir.
+ values = mesonlib.get_filenames_templates_dict([ifile_abs], [ofile_abs])
+ # Substitute @INPUT@, @OUTPUT@, etc here.
+ cmd = mesonlib.substitute_values(kwargs['command'], values)
+ mlog.log('Configuring', mlog.bold(output), 'with command')
+ res = self.func_run_command(node, cmd, {'in_builddir': True})
if res.returncode != 0:
raise InterpreterException('Running configure command failed.\n%s\n%s' %
(res.stdout, res.stderr))
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index f0b20e1..c7368d5 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -521,3 +521,154 @@ def commonpath(paths):
new = os.path.join(*new)
common = pathlib.PurePath(new)
return str(common)
+
+def iter_regexin_iter(regexiter, initer):
+ '''
+ Takes each regular expression in @regexiter and tries to search for it in
+ every item in @initer. If there is a match, returns that match.
+ Else returns False.
+ '''
+ for regex in regexiter:
+ for ii in initer:
+ if not isinstance(ii, str):
+ continue
+ match = re.search(regex, ii)
+ if match:
+ return match.group()
+ return False
+
+def _substitute_values_check_errors(command, values):
+ # Error checking
+ inregex = ('@INPUT([0-9]+)?@', '@PLAINNAME@', '@BASENAME@')
+ outregex = ('@OUTPUT([0-9]+)?@', '@OUTDIR@')
+ if '@INPUT@' not in values:
+ # Error out if any input-derived templates are present in the command
+ match = iter_regexin_iter(inregex, command)
+ if match:
+ m = 'Command cannot have {!r}, since no input files were specified'
+ raise MesonException(m.format(match))
+ else:
+ if len(values['@INPUT@']) > 1:
+ # Error out if @PLAINNAME@ or @BASENAME@ is present in the command
+ match = iter_regexin_iter(inregex[1:], command)
+ if match:
+ raise MesonException('Command cannot have {!r} when there is '
+ 'more than one input file'.format(match))
+ # Error out if an invalid @INPUTnn@ template was specified
+ for each in command:
+ if not isinstance(each, str):
+ continue
+ match = re.search(inregex[0], each)
+ if match and match.group() not in values:
+ m = 'Command cannot have {!r} since there are only {!r} inputs'
+ raise MesonException(m.format(match.group(), len(values['@INPUT@'])))
+ if '@OUTPUT@' not in values:
+ # Error out if any output-derived templates are present in the command
+ match = iter_regexin_iter(outregex, command)
+ if match:
+ m = 'Command cannot have {!r} since there are no outputs'
+ raise MesonException(m.format(match))
+ else:
+ # Error out if an invalid @OUTPUTnn@ template was specified
+ for each in command:
+ if not isinstance(each, str):
+ continue
+ match = re.search(outregex[0], each)
+ if match and match.group() not in values:
+ m = 'Command cannot have {!r} since there are only {!r} outputs'
+ raise MesonException(m.format(match.group(), len(values['@OUTPUT@'])))
+
+def substitute_values(command, values):
+ '''
+ Substitute the template strings in the @values dict into the list of
+ strings @command and return a new list. For a full list of the templates,
+ see get_filenames_templates_dict()
+
+ If multiple inputs/outputs are given in the @values dictionary, we
+ substitute @INPUT@ and @OUTPUT@ only if they are the entire string, not
+ just a part of it, and in that case we substitute *all* of them.
+ '''
+ # Error checking
+ _substitute_values_check_errors(command, values)
+ # Substitution
+ outcmd = []
+ for vv in command:
+ if not isinstance(vv, str):
+ outcmd.append(vv)
+ elif '@INPUT@' in vv:
+ inputs = values['@INPUT@']
+ if vv == '@INPUT@':
+ outcmd += inputs
+ elif len(inputs) == 1:
+ outcmd.append(vv.replace('@INPUT@', inputs[0]))
+ else:
+ raise MesonException("Command has '@INPUT@' as part of a "
+ "string and more than one input file")
+ elif '@OUTPUT@' in vv:
+ outputs = values['@OUTPUT@']
+ if vv == '@OUTPUT@':
+ outcmd += outputs
+ elif len(outputs) == 1:
+ outcmd.append(vv.replace('@OUTPUT@', outputs[0]))
+ else:
+ raise MesonException("Command has '@OUTPUT@' as part of a "
+ "string and more than one output file")
+ # Append values that are exactly a template string.
+ # This is faster than a string replace.
+ elif vv in values:
+ outcmd.append(values[vv])
+ # Substitute everything else with replacement
+ else:
+ for key, value in values.items():
+ if key in ('@INPUT@', '@OUTPUT@'):
+ # Already done above
+ continue
+ vv = vv.replace(key, value)
+ outcmd.append(vv)
+ return outcmd
+
+def get_filenames_templates_dict(inputs, outputs):
+ '''
+ Create a dictionary with template strings as keys and values as values for
+ the following templates:
+
+ @INPUT@ - the full path to one or more input files, from @inputs
+ @OUTPUT@ - the full path to one or more output files, from @outputs
+ @OUTDIR@ - the full path to the directory containing the output files
+
+ If there is only one input file, the following keys are also created:
+
+ @PLAINNAME@ - the filename of the input file
+ @BASENAME@ - the filename of the input file with the extension removed
+
+ If there is more than one input file, the following keys are also created:
+
+ @INPUT0@, @INPUT1@, ... one for each input file
+
+ If there is more than one output file, the following keys are also created:
+
+ @OUTPUT0@, @OUTPUT1@, ... one for each output file
+ '''
+ values = {}
+ # Gather values derived from the input
+ if inputs:
+ # We want to substitute all the inputs.
+ values['@INPUT@'] = inputs
+ for (ii, vv) in enumerate(inputs):
+ # Write out @INPUT0@, @INPUT1@, ...
+ values['@INPUT{}@'.format(ii)] = vv
+ if len(inputs) == 1:
+ # Just one value, substitute @PLAINNAME@ and @BASENAME@
+ values['@PLAINNAME@'] = plain = os.path.split(inputs[0])[1]
+ values['@BASENAME@'] = os.path.splitext(plain)[0]
+ if outputs:
+ # Gather values derived from the outputs, similar to above.
+ values['@OUTPUT@'] = outputs
+ for (ii, vv) in enumerate(outputs):
+ values['@OUTPUT{}@'.format(ii)] = vv
+ # Outdir should be the same for all outputs
+ values['@OUTDIR@'] = os.path.split(outputs[0])[0]
+ # Many external programs fail on empty arguments.
+ if values['@OUTDIR@'] == '':
+ values['@OUTDIR@'] = '.'
+ return values
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index 2a54f3a..bf4b8be 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -37,7 +37,7 @@ from . import ExtensionModule
#
# https://github.com/ninja-build/ninja/issues/1184
# https://bugzilla.gnome.org/show_bug.cgi?id=774368
-gresource_dep_needed_version = '>= 2.52.0'
+gresource_dep_needed_version = '>= 2.51.1'
native_glib_version = None
girwarning_printed = False
@@ -74,14 +74,15 @@ class GnomeModule(ExtensionModule):
global gresource_warning_printed
if not gresource_warning_printed:
if not mesonlib.version_compare(self._get_native_glib_version(state), gresource_dep_needed_version):
- mlog.warning('''GLib compiled dependencies do not work reliably with
-the current version of GLib. See the following upstream issue:''',
+ mlog.warning('GLib compiled dependencies do not work reliably with \n'
+ 'the current version of GLib. See the following upstream issue:',
mlog.bold('https://bugzilla.gnome.org/show_bug.cgi?id=774368'))
gresource_warning_printed = True
return []
def compile_resources(self, state, args, kwargs):
self.__print_gresources_warning(state)
+ glib_version = self._get_native_glib_version(state)
cmd = ['glib-compile-resources', '@INPUT@']
@@ -90,26 +91,48 @@ the current version of GLib. See the following upstream issue:''',
source_dirs = [source_dirs]
if len(args) < 2:
- raise MesonException('Not enough arguments; The name of the resource and the path to the XML file are required')
+ raise MesonException('Not enough arguments; the name of the resource '
+ 'and the path to the XML file are required')
dependencies = kwargs.pop('dependencies', [])
if not isinstance(dependencies, list):
dependencies = [dependencies]
-
- glib_version = self._get_native_glib_version(state)
- if not mesonlib.version_compare(glib_version, gresource_dep_needed_version):
- if len(dependencies) > 0:
- raise MesonException('''The "dependencies" argument of gnome.compile_resources()
-can not be used with the current version of glib-compiled-resources, due to
-<https://bugzilla.gnome.org/show_bug.cgi?id=774368>''')
+ # Validate dependencies
+ for (ii, dep) in enumerate(dependencies):
+ if hasattr(dep, 'held_object'):
+ dependencies[ii] = dep = dep.held_object
+ if not isinstance(dep, (mesonlib.File, build.CustomTarget)):
+ m = 'Unexpected dependency type {!r} for gnome.compile_resources() ' \
+ '"dependencies" argument.\nPlease pass the return value of ' \
+ 'custom_target() or configure_file()'
+ raise MesonException(m.format(dep))
+ if isinstance(dep, build.CustomTarget):
+ if not mesonlib.version_compare(glib_version, gresource_dep_needed_version):
+ m = 'The "dependencies" argument of gnome.compile_resources() can not\n' \
+ 'be used with the current version of glib-compile-resources due to\n' \
+ '<https://bugzilla.gnome.org/show_bug.cgi?id=774368>'
+ raise MesonException(m)
ifile = args[1]
if isinstance(ifile, mesonlib.File):
- ifile = os.path.join(ifile.subdir, ifile.fname)
+ # glib-compile-resources will be run inside the source dir,
+ # so we need either 'src_to_build' or the absolute path.
+ # Absolute path is the easiest choice.
+ if ifile.is_built:
+ ifile = os.path.join(state.environment.get_build_dir(), ifile.subdir, ifile.fname)
+ else:
+ ifile = os.path.join(ifile.subdir, ifile.fname)
elif isinstance(ifile, str):
ifile = os.path.join(state.subdir, ifile)
+ elif isinstance(ifile, (interpreter.CustomTargetHolder,
+ interpreter.GeneratedObjectsHolder)):
+ m = 'Resource xml files generated at build-time cannot be used ' \
+ 'with gnome.compile_resources() because we need to scan ' \
+ 'the xml for dependencies. Use configure_file() instead ' \
+ 'to generate it at configure-time.'
+ raise MesonException(m)
else:
- raise RuntimeError('Unreachable code.')
+ raise MesonException('Invalid file argument: {!r}'.format(ifile))
depend_files, depends, subdirs = self._get_gresource_dependencies(
state, ifile, source_dirs, dependencies)
@@ -183,13 +206,6 @@ can not be used with the current version of glib-compiled-resources, due to
return ModuleReturnValue(rv, rv)
def _get_gresource_dependencies(self, state, input_file, source_dirs, dependencies):
- for dep in dependencies:
- if not isinstance(dep, interpreter.CustomTargetHolder) and not \
- isinstance(dep, mesonlib.File):
- raise MesonException(
- 'Unexpected dependency type for gnome.compile_resources() '
- '"dependencies" argument. Please pass the output of '
- 'custom_target() or configure_file().')
cmd = ['glib-compile-resources',
input_file,
@@ -199,9 +215,10 @@ can not be used with the current version of glib-compiled-resources, due to
cmd += ['--sourcedir', os.path.join(state.subdir, source_dir)]
cmd += ['--sourcedir', state.subdir] # Current dir
- pc, stdout = Popen_safe(cmd, cwd=state.environment.get_source_dir())[0:2]
+ pc, stdout, stderr = Popen_safe(cmd, cwd=state.environment.get_source_dir())
if pc.returncode != 0:
- mlog.warning('glib-compile-resources has failed to get the dependencies for {}'.format(cmd[1]))
+ m = 'glib-compile-resources failed to get dependencies for {}:\n{}'
+ mlog.warning(m.format(cmd[1], stderr))
raise subprocess.CalledProcessError(pc.returncode, cmd)
dep_files = stdout.split('\n')[:-1]
@@ -863,6 +880,7 @@ can not be used with the current version of glib-compiled-resources, due to
}
custom_kwargs.update(kwargs)
return build.CustomTarget(output, state.subdir, custom_kwargs,
+ # https://github.com/mesonbuild/meson/issues/973
absolute_paths=True)
def genmarshal(self, state, args, kwargs):
diff --git a/mesonbuild/modules/rpm.py b/mesonbuild/modules/rpm.py
index bbfeaa0..17396ae 100644
--- a/mesonbuild/modules/rpm.py
+++ b/mesonbuild/modules/rpm.py
@@ -103,7 +103,7 @@ class RPMModule(ExtensionModule):
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 '*/lib{}.so".format(name))
+ 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' %
diff --git a/mesontest.py b/mesontest.py
index 57c364b..980fbee 100755
--- a/mesontest.py
+++ b/mesontest.py
@@ -159,7 +159,6 @@ class TestHarness:
self.skip_count = 0
self.timeout_count = 0
self.is_run = False
- self.cant_rebuild = False
self.tests = None
self.suites = None
if self.options.benchmark:
@@ -167,27 +166,6 @@ class TestHarness:
else:
self.load_datafile(os.path.join(options.wd, 'meson-private', 'meson_test_setup.dat'))
- def rebuild_all(self):
- if not os.path.isfile(os.path.join(self.options.wd, 'build.ninja')):
- print("Only ninja backend is supported to rebuilt tests before running them.")
- self.cant_rebuild = True
- return True
-
- ninja = environment.detect_ninja()
- if not ninja:
- print("Can't find ninja, can't rebuild test.")
- self.cant_rebuild = True
- return False
-
- p = subprocess.Popen([ninja, '-C', self.options.wd])
- (stdo, stde) = p.communicate()
-
- if p.returncode != 0:
- print("Could not rebuild")
- return False
-
- return True
-
def run_single_test(self, wrap, test):
if test.fname[0].endswith('.jar'):
cmd = ['java', '-jar'] + test.fname
@@ -553,6 +531,25 @@ def merge_suite_options(options):
options.wrapper = current.exe_wrapper
return current.env
+def rebuild_all(wd):
+ if not os.path.isfile(os.path.join(wd, 'build.ninja')):
+ print("Only ninja backend is supported to rebuild tests before running them.")
+ return True
+
+ ninja = environment.detect_ninja()
+ if not ninja:
+ print("Can't find ninja, can't rebuild test.")
+ return False
+
+ p = subprocess.Popen([ninja, '-C', wd])
+ (stdo, stde) = p.communicate()
+
+ if p.returncode != 0:
+ print("Could not rebuild")
+ return False
+
+ return True
+
def run(args):
options = parser.parse_args(args)
@@ -580,13 +577,14 @@ def run(args):
options.wd = os.path.abspath(options.wd)
+ if not options.no_rebuild:
+ if not rebuild_all(options.wd):
+ sys.exit(-1)
+
th = TestHarness(options)
if options.list:
list_tests(th)
return 0
- if not options.no_rebuild:
- if not th.rebuild_all():
- sys.exit(-1)
if len(options.args) == 0:
return th.doit()
return th.run_special()
diff --git a/run_tests.py b/run_tests.py
index f2038e4..5025057 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -14,12 +14,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import subprocess, sys, shutil
+import os
+import sys
+import shutil
+import subprocess
import platform
from mesonbuild import mesonlib
if __name__ == '__main__':
returncode = 0
+ # Running on a developer machine? Be nice!
+ if not mesonlib.is_windows() and 'TRAVIS' not in os.environ:
+ os.nice(20)
print('Running unittests.\n')
units = ['InternalTests', 'AllPlatformTests']
if mesonlib.is_linux():
diff --git a/run_unittests.py b/run_unittests.py
index 46be657..16ff354 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -180,6 +180,157 @@ class InternalTests(unittest.TestCase):
libdir = '/some/path/to/prefix/libdir'
self.assertEqual(commonpath([prefix, libdir]), str(PurePath(prefix)))
+ def test_string_templates_substitution(self):
+ dictfunc = mesonbuild.mesonlib.get_filenames_templates_dict
+ substfunc = mesonbuild.mesonlib.substitute_values
+ ME = mesonbuild.mesonlib.MesonException
+
+ # Identity
+ self.assertEqual(dictfunc([], []), {})
+
+ # One input, no outputs
+ inputs = ['bar/foo.c.in']
+ outputs = []
+ ret = dictfunc(inputs, outputs)
+ d = {'@INPUT@': inputs, '@INPUT0@': inputs[0],
+ '@PLAINNAME@': 'foo.c.in', '@BASENAME@': 'foo.c'}
+ # Check dictionary
+ self.assertEqual(ret, d)
+ # Check substitutions
+ cmd = ['some', 'ordinary', 'strings']
+ self.assertEqual(substfunc(cmd, d), cmd)
+ cmd = ['@INPUT@.out', 'ordinary', 'strings']
+ self.assertEqual(substfunc(cmd, d), [inputs[0] + '.out'] + cmd[1:])
+ cmd = ['@INPUT0@.out', '@PLAINNAME@.ok', 'strings']
+ self.assertEqual(substfunc(cmd, d),
+ [inputs[0] + '.out'] + [d['@PLAINNAME@'] + '.ok'] + cmd[2:])
+ cmd = ['@INPUT@', '@BASENAME@.hah', 'strings']
+ self.assertEqual(substfunc(cmd, d),
+ inputs + [d['@BASENAME@'] + '.hah'] + cmd[2:])
+ cmd = ['@OUTPUT@']
+ self.assertRaises(ME, substfunc, cmd, d)
+
+ # One input, one output
+ inputs = ['bar/foo.c.in']
+ outputs = ['out.c']
+ ret = dictfunc(inputs, outputs)
+ d = {'@INPUT@': inputs, '@INPUT0@': inputs[0],
+ '@PLAINNAME@': 'foo.c.in', '@BASENAME@': 'foo.c',
+ '@OUTPUT@': outputs, '@OUTPUT0@': outputs[0], '@OUTDIR@': '.'}
+ # Check dictionary
+ self.assertEqual(ret, d)
+ # Check substitutions
+ cmd = ['some', 'ordinary', 'strings']
+ self.assertEqual(substfunc(cmd, d), cmd)
+ cmd = ['@INPUT@.out', '@OUTPUT@', 'strings']
+ self.assertEqual(substfunc(cmd, d),
+ [inputs[0] + '.out'] + outputs + cmd[2:])
+ cmd = ['@INPUT0@.out', '@PLAINNAME@.ok', '@OUTPUT0@']
+ self.assertEqual(substfunc(cmd, d),
+ [inputs[0] + '.out', d['@PLAINNAME@'] + '.ok'] + outputs)
+ cmd = ['@INPUT@', '@BASENAME@.hah', 'strings']
+ self.assertEqual(substfunc(cmd, d),
+ inputs + [d['@BASENAME@'] + '.hah'] + cmd[2:])
+
+ # One input, one output with a subdir
+ outputs = ['dir/out.c']
+ ret = dictfunc(inputs, outputs)
+ d = {'@INPUT@': inputs, '@INPUT0@': inputs[0],
+ '@PLAINNAME@': 'foo.c.in', '@BASENAME@': 'foo.c',
+ '@OUTPUT@': outputs, '@OUTPUT0@': outputs[0], '@OUTDIR@': 'dir'}
+ # Check dictionary
+ self.assertEqual(ret, d)
+
+ # Two inputs, no outputs
+ inputs = ['bar/foo.c.in', 'baz/foo.c.in']
+ outputs = []
+ ret = dictfunc(inputs, outputs)
+ d = {'@INPUT@': inputs, '@INPUT0@': inputs[0], '@INPUT1@': inputs[1]}
+ # Check dictionary
+ self.assertEqual(ret, d)
+ # Check substitutions
+ cmd = ['some', 'ordinary', 'strings']
+ self.assertEqual(substfunc(cmd, d), cmd)
+ cmd = ['@INPUT@', 'ordinary', 'strings']
+ self.assertEqual(substfunc(cmd, d), inputs + cmd[1:])
+ cmd = ['@INPUT0@.out', 'ordinary', 'strings']
+ self.assertEqual(substfunc(cmd, d), [inputs[0] + '.out'] + cmd[1:])
+ cmd = ['@INPUT0@.out', '@INPUT1@.ok', 'strings']
+ self.assertEqual(substfunc(cmd, d), [inputs[0] + '.out', inputs[1] + '.ok'] + cmd[2:])
+ cmd = ['@INPUT0@', '@INPUT1@', 'strings']
+ self.assertEqual(substfunc(cmd, d), inputs + cmd[2:])
+ # Many inputs, can't use @INPUT@ like this
+ cmd = ['@INPUT@.out', 'ordinary', 'strings']
+ # Not enough inputs
+ cmd = ['@INPUT2@.out', 'ordinary', 'strings']
+ self.assertRaises(ME, substfunc, cmd, d)
+ # Too many inputs
+ cmd = ['@PLAINNAME@']
+ self.assertRaises(ME, substfunc, cmd, d)
+ cmd = ['@BASENAME@']
+ self.assertRaises(ME, substfunc, cmd, d)
+ # No outputs
+ cmd = ['@OUTPUT@']
+ self.assertRaises(ME, substfunc, cmd, d)
+ cmd = ['@OUTPUT0@']
+ self.assertRaises(ME, substfunc, cmd, d)
+ cmd = ['@OUTDIR@']
+ self.assertRaises(ME, substfunc, cmd, d)
+
+ # Two inputs, one output
+ outputs = ['dir/out.c']
+ ret = dictfunc(inputs, outputs)
+ d = {'@INPUT@': inputs, '@INPUT0@': inputs[0], '@INPUT1@': inputs[1],
+ '@OUTPUT@': outputs, '@OUTPUT0@': outputs[0], '@OUTDIR@': 'dir'}
+ # Check dictionary
+ self.assertEqual(ret, d)
+ # Check substitutions
+ cmd = ['some', 'ordinary', 'strings']
+ self.assertEqual(substfunc(cmd, d), cmd)
+ cmd = ['@OUTPUT@', 'ordinary', 'strings']
+ self.assertEqual(substfunc(cmd, d), outputs + cmd[1:])
+ cmd = ['@OUTPUT@.out', 'ordinary', 'strings']
+ self.assertEqual(substfunc(cmd, d), [outputs[0] + '.out'] + cmd[1:])
+ cmd = ['@OUTPUT0@.out', '@INPUT1@.ok', 'strings']
+ self.assertEqual(substfunc(cmd, d), [outputs[0] + '.out', inputs[1] + '.ok'] + cmd[2:])
+ # Many inputs, can't use @INPUT@ like this
+ cmd = ['@INPUT@.out', 'ordinary', 'strings']
+ # Not enough inputs
+ cmd = ['@INPUT2@.out', 'ordinary', 'strings']
+ self.assertRaises(ME, substfunc, cmd, d)
+ # Not enough outputs
+ cmd = ['@OUTPUT2@.out', 'ordinary', 'strings']
+ self.assertRaises(ME, substfunc, cmd, d)
+
+ # Two inputs, two outputs
+ outputs = ['dir/out.c', 'dir/out2.c']
+ ret = dictfunc(inputs, outputs)
+ d = {'@INPUT@': inputs, '@INPUT0@': inputs[0], '@INPUT1@': inputs[1],
+ '@OUTPUT@': outputs, '@OUTPUT0@': outputs[0], '@OUTPUT1@': outputs[1],
+ '@OUTDIR@': 'dir'}
+ # Check dictionary
+ self.assertEqual(ret, d)
+ # Check substitutions
+ cmd = ['some', 'ordinary', 'strings']
+ self.assertEqual(substfunc(cmd, d), cmd)
+ cmd = ['@OUTPUT@', 'ordinary', 'strings']
+ self.assertEqual(substfunc(cmd, d), outputs + cmd[1:])
+ cmd = ['@OUTPUT0@', '@OUTPUT1@', 'strings']
+ self.assertEqual(substfunc(cmd, d), outputs + cmd[2:])
+ cmd = ['@OUTPUT0@.out', '@INPUT1@.ok', '@OUTDIR@']
+ self.assertEqual(substfunc(cmd, d), [outputs[0] + '.out', inputs[1] + '.ok', 'dir'])
+ # Many inputs, can't use @INPUT@ like this
+ cmd = ['@INPUT@.out', 'ordinary', 'strings']
+ # Not enough inputs
+ cmd = ['@INPUT2@.out', 'ordinary', 'strings']
+ self.assertRaises(ME, substfunc, cmd, d)
+ # Not enough outputs
+ cmd = ['@OUTPUT2@.out', 'ordinary', 'strings']
+ self.assertRaises(ME, substfunc, cmd, d)
+ # Many outputs, can't use @OUTPUT@ like this
+ cmd = ['@OUTPUT@.out', 'ordinary', 'strings']
+ self.assertRaises(ME, substfunc, cmd, d)
+
class BasePlatformTests(unittest.TestCase):
def setUp(self):
diff --git a/syntax-highlighting/vim/syntax/meson.vim b/syntax-highlighting/vim/syntax/meson.vim
index 0799237..49921c1 100644
--- a/syntax-highlighting/vim/syntax/meson.vim
+++ b/syntax-highlighting/vim/syntax/meson.vim
@@ -67,8 +67,8 @@ syn keyword mesonBuiltin
\ add_global_link_arguments
\ add_languages
\ add_project_arguments
- \ add_project_arguments
\ add_project_link_arguments
+ \ add_test_setup
\ benchmark
\ build_machine
\ build_target
@@ -94,7 +94,6 @@ syn keyword mesonBuiltin
\ install_headers
\ install_man
\ install_subdir
- \ is_subproject
\ is_variable
\ jar
\ join_paths
diff --git a/test cases/common/118 allgenerate/meson.build b/test cases/common/118 allgenerate/meson.build
index 36abbe9..049e849 100644
--- a/test cases/common/118 allgenerate/meson.build
+++ b/test cases/common/118 allgenerate/meson.build
@@ -13,7 +13,7 @@ c = g.process('foobar.cpp.in')
prog = executable('genexe', c)
c2 = custom_target('c2gen',
- output : 'c2gen.cpp',
+ output : '@BASENAME@',
input : 'foobar.cpp.in',
command : [comp, '@INPUT@', '@OUTPUT@'])
diff --git a/test cases/common/129 object only target/meson.build b/test cases/common/129 object only target/meson.build
index 58d01d9..d83a658 100644
--- a/test cases/common/129 object only target/meson.build
+++ b/test cases/common/129 object only target/meson.build
@@ -16,7 +16,7 @@ cc = meson.get_compiler('c').cmd_array().get(-1)
# provided by the source tree
source1 = configure_file(input : 'source.c',
output : 'source' + ext,
- command : [comp, cc, 'source.c',
+ command : [comp, cc, files('source.c'),
join_paths(meson.current_build_dir(), 'source' + ext)])
obj = static_library('obj', objects : source1)
diff --git a/test cases/common/16 configure file/check_file.py b/test cases/common/16 configure file/check_file.py
new file mode 100644
index 0000000..449b77a
--- /dev/null
+++ b/test cases/common/16 configure file/check_file.py
@@ -0,0 +1,6 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+
+assert(os.path.exists(sys.argv[1]))
diff --git a/test cases/common/16 configure file/generator.py b/test cases/common/16 configure file/generator.py
index 2c7f2f8..e3cc881 100755
--- a/test cases/common/16 configure file/generator.py
+++ b/test cases/common/16 configure file/generator.py
@@ -1,15 +1,17 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
-# On some platforms "python" points to Python 2
-# on others to Python 3. Work with both.
-
-from __future__ import print_function
import sys, os
+from pathlib import Path
if len(sys.argv) != 3:
print("Wrong amount of parameters.")
-assert(os.path.exists(sys.argv[1]))
+build_dir = Path(os.environ['MESON_BUILD_ROOT'])
+subdir = Path(os.environ['MESON_SUBDIR'])
+inputf = Path(sys.argv[1])
+outputf = Path(sys.argv[2])
+
+assert(inputf.exists())
-with open(sys.argv[2], 'w') as ofile:
+with outputf.open('w') as ofile:
ofile.write("#define ZERO_RESULT 0\n")
diff --git a/test cases/common/16 configure file/installed_files.txt b/test cases/common/16 configure file/installed_files.txt
index 219b4c0..d9fee12 100644
--- a/test cases/common/16 configure file/installed_files.txt
+++ b/test cases/common/16 configure file/installed_files.txt
@@ -1 +1,3 @@
usr/share/appdir/config2.h
+usr/share/appdireh/config2-1.h
+usr/share/appdirok/config2-2.h
diff --git a/test cases/common/16 configure file/meson.build b/test cases/common/16 configure file/meson.build
index b764c5a..bff041b 100644
--- a/test cases/common/16 configure file/meson.build
+++ b/test cases/common/16 configure file/meson.build
@@ -23,18 +23,22 @@ cfile)
test('inctest', e)
# Now generate a header file with an external script.
-genprog = find_program('python3', required : false)
-if not genprog.found()
- genprog = find_program('python')
-endif
+genprog = import('python3').find_python()
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())
+check_file = find_program('check_file.py')
+# Configure in source root with command and absolute paths
configure_file(input : 'dummy.dat',
-output : 'config2.h',
-command : [genprog, scriptfile, ifile, ofile],
-install_dir : 'share/appdir')
+ output : 'config2.h',
+ command : [genprog, scriptfile, ifile, ofile],
+ install_dir : 'share/appdir')
+run_command(check_file, join_paths(meson.current_build_dir(), 'config2.h'))
+
+found_script = find_program('generator.py')
+# More configure_file tests in here
+subdir('subdir')
test('inctest2', executable('prog2', 'prog2.c'))
diff --git a/test cases/common/16 configure file/subdir/meson.build b/test cases/common/16 configure file/subdir/meson.build
new file mode 100644
index 0000000..d802c1d
--- /dev/null
+++ b/test cases/common/16 configure file/subdir/meson.build
@@ -0,0 +1,19 @@
+# Configure in subdir with absolute paths for input and relative for output
+configure_file(input : '../dummy.dat',
+ output : 'config2-1.h',
+ command : [genprog, scriptfile, ifile, 'config2-1.h'],
+ install_dir : 'share/appdireh')
+run_command(check_file, join_paths(meson.current_build_dir(), 'config2-1.h'))
+
+# Configure in subdir with files() for input and relative for output
+configure_file(input : '../dummy.dat',
+ output : 'config2-2.h',
+ command : [genprog, scriptfile, files('../dummy.dat'), 'config2-2.h'],
+ install_dir : 'share/appdirok')
+run_command(check_file, join_paths(meson.current_build_dir(), 'config2-2.h'))
+
+# Configure in subdir with string templates for input and output
+configure_file(input : '../dummy.dat',
+ output : 'config2-3.h',
+ command : [found_script, '@INPUT@', '@OUTPUT@'])
+run_command(check_file, join_paths(meson.current_build_dir(), 'config2-3.h'))
diff --git a/test cases/failing/42 custom target plainname many inputs/1.txt b/test cases/failing/42 custom target plainname many inputs/1.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/test cases/failing/42 custom target plainname many inputs/1.txt
@@ -0,0 +1 @@
+1
diff --git a/test cases/failing/42 custom target plainname many inputs/2.txt b/test cases/failing/42 custom target plainname many inputs/2.txt
new file mode 100644
index 0000000..0cfbf08
--- /dev/null
+++ b/test cases/failing/42 custom target plainname many inputs/2.txt
@@ -0,0 +1 @@
+2
diff --git a/test cases/failing/42 custom target plainname many inputs/catfiles.py b/test cases/failing/42 custom target plainname many inputs/catfiles.py
new file mode 100644
index 0000000..1c53e24
--- /dev/null
+++ b/test cases/failing/42 custom target plainname many inputs/catfiles.py
@@ -0,0 +1,9 @@
+#!/usr/bin/env python3
+
+import sys
+
+out = sys.argv[-1]
+with open(out, 'wb') as o:
+ for infile in sys.argv[1:-1]:
+ with open(infile, 'rb') as f:
+ o.write(f.read())
diff --git a/test cases/failing/42 custom target plainname many inputs/meson.build b/test cases/failing/42 custom target plainname many inputs/meson.build
new file mode 100644
index 0000000..1bcfc06
--- /dev/null
+++ b/test cases/failing/42 custom target plainname many inputs/meson.build
@@ -0,0 +1,8 @@
+project('plain name many inputs', 'c')
+
+catfiles = find_program('catfiles.py')
+
+custom_target('plainname-inputs',
+ input : ['1.txt', '2.txt'],
+ output : '@PLAINNAME@.dat',
+ command : [catfiles, '@INPUT@', '@OUTPUT@'])
diff --git a/test cases/frameworks/7 gnome/resources/copyfile.py b/test cases/frameworks/7 gnome/resources/copyfile.py
new file mode 100644
index 0000000..7e44c48
--- /dev/null
+++ b/test cases/frameworks/7 gnome/resources/copyfile.py
@@ -0,0 +1,6 @@
+#!/usr/bin/env python3
+
+import sys
+import shutil
+
+shutil.copy(sys.argv[1], sys.argv[2])
diff --git a/test cases/frameworks/7 gnome/resources/meson.build b/test cases/frameworks/7 gnome/resources/meson.build
index 2e72501..fdf6f63 100644
--- a/test cases/frameworks/7 gnome/resources/meson.build
+++ b/test cases/frameworks/7 gnome/resources/meson.build
@@ -1,8 +1,15 @@
# There are two tests here, because the 2nd one depends on a version of
-# GLib (2.48.2) that is very recent at the time of writing.
+# GLib (2.51.1) that is very recent at the time of writing.
+
+copyfile = find_program('copyfile.py')
+
+simple_gresource = configure_file(
+ input : 'simple.gresource.xml',
+ output : 'simple-gen.gresource.xml',
+ command : [copyfile, '@INPUT@', '@OUTPUT@'])
simple_resources = gnome.compile_resources('simple-resources',
- 'simple.gresource.xml',
+ simple_gresource,
install_header : true,
export : true,
source_dir : '../resources-data',