aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mesonbuild/backend/backends.py52
-rw-r--r--mesonbuild/backend/ninjabackend.py47
-rw-r--r--mesonbuild/backend/vs2010backend.py32
-rw-r--r--mesonbuild/backend/xcodebackend.py7
-rw-r--r--mesonbuild/build.py11
-rw-r--r--mesonbuild/compilers.py46
-rw-r--r--mesonbuild/environment.py41
-rw-r--r--mesonbuild/mesonlib.py4
-rw-r--r--mesonbuild/mesonmain.py5
-rw-r--r--mesonbuild/scripts/delwithsuffix.py2
-rw-r--r--mesonbuild/scripts/meson_exe.py74
11 files changed, 251 insertions, 70 deletions
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index b7a2c30..7c6caa6 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -34,6 +34,18 @@ class InstallData():
self.install_scripts = []
self.install_subdirs = []
+class ExecutableSerialisation():
+ def __init__(self, name, fname, cmd_args, env, is_cross, exe_wrapper,
+ workdir, extra_paths):
+ self.name = name
+ self.fname = fname
+ self.cmd_args = cmd_args
+ self.env = env
+ self.is_cross = is_cross
+ self.exe_runner = exe_wrapper
+ self.workdir = workdir
+ self.extra_paths = extra_paths
+
class TestSerialisation:
def __init__(self, name, suite, fname, is_cross, exe_wrapper, is_parallel, cmd_args, env,
should_fail, valgrind_args, timeout, workdir, extra_paths):
@@ -154,6 +166,35 @@ class Backend():
raise MesonException('Unknown data type in object list.')
return obj_list
+ def serialise_executable(self, exe, cmd_args, workdir, env={}):
+ import uuid
+ # Can't just use exe.name here; it will likely be run more than once
+ scratch_file = 'meson_exe_{0}_{1}.dat'.format(exe.name,
+ str(uuid.uuid4())[:8])
+ exe_data = os.path.join(self.environment.get_scratch_dir(), scratch_file)
+ with open(exe_data, 'wb') as f:
+ if isinstance(exe, dependencies.ExternalProgram):
+ exe_fullpath = exe.fullpath
+ else:
+ exe_fullpath = [os.path.join(self.environment.get_build_dir(),
+ self.get_target_filename(exe))]
+ is_cross = self.environment.is_cross_build() and \
+ self.environment.cross_info.need_cross_compiler() and \
+ self.environment.cross_info.need_exe_wrapper()
+ if is_cross:
+ exe_wrapper = self.environment.cross_info.config['binaries'].get('exe_wrapper', None)
+ else:
+ exe_wrapper = None
+ if mesonlib.is_windows():
+ extra_paths = self.determine_windows_extra_paths(exe)
+ else:
+ extra_paths = []
+ es = ExecutableSerialisation(exe.name, exe_fullpath, cmd_args, env,
+ is_cross, exe_wrapper, workdir,
+ extra_paths)
+ pickle.dump(es, f)
+ return exe_data
+
def serialise_tests(self):
test_data = os.path.join(self.environment.get_scratch_dir(), 'meson_test_setup.dat')
datafile = open(test_data, 'wb')
@@ -163,6 +204,7 @@ class Backend():
datafile = open(benchmark_data, 'wb')
self.write_benchmark_file(datafile)
datafile.close()
+ return (test_data, benchmark_data)
def has_source_suffix(self, target, suffix):
for s in target.get_sources():
@@ -390,6 +432,16 @@ class Backend():
final_args.append(a)
return final_args
+ def get_custom_target_provided_libraries(self, target):
+ libs = []
+ for t in target.get_generated_sources():
+ if not isinstance(t, build.CustomTarget):
+ continue
+ for f in t.output:
+ if self.environment.is_library(f):
+ libs.append(os.path.join(self.get_target_dir(t), f))
+ return libs
+
def eval_custom_target_command(self, target, absolute_paths=False):
if not absolute_paths:
ofilenames = [os.path.join(self.get_target_dir(target), i) for i in target.output]
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 4415a68..ce8968c 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -341,6 +341,7 @@ int dummy;
def generate_custom_target(self, target, outfile):
(srcs, ofilenames, cmd) = self.eval_custom_target_command(target)
deps = []
+ desc = 'Generating {0} with a {1} command.'
for i in target.get_dependencies():
# FIXME, should not grab element at zero but rather expand all.
if isinstance(i, list):
@@ -364,9 +365,23 @@ int dummy;
tmp = [tmp]
for fname in tmp:
elem.add_dep(os.path.join(self.get_target_dir(d), fname))
+ # Windows doesn't have -rpath, so for EXEs that need DLLs built within
+ # the project, we need to set PATH so the DLLs are found. We use
+ # a serialized executable wrapper for that and check if the
+ # CustomTarget command needs extra paths first.
+ if mesonlib.is_windows() and \
+ self.determine_windows_extra_paths(target.command[0]):
+ exe_data = self.serialise_executable(target.command[0], cmd[1:],
+ # All targets are built from the build dir
+ self.environment.get_build_dir())
+ cmd = [sys.executable, self.environment.get_build_command(),
+ '--internal', 'exe', exe_data]
+ cmd_type = 'meson_exe.py custom'
+ else:
+ cmd_type = 'custom'
elem.add_item('COMMAND', cmd)
- elem.add_item('description', 'Generating %s with a custom command.' % target.name)
+ elem.add_item('description', desc.format(target.name, cmd_type))
elem.write(outfile)
self.processed_targets[target.name + target.type_suffix()] = True
@@ -433,27 +448,27 @@ int dummy;
if gcovr_exe:
added_rule = True
elem = NinjaBuildElement(self.all_outputs, 'coverage-xml', 'CUSTOM_COMMAND', '')
- elem.add_item('COMMAND', [gcovr_exe, '-x', '-r', self.environment.get_build_dir(),\
+ elem.add_item('COMMAND', [gcovr_exe, '-x', '-r', self.environment.get_source_dir(),\
'-o', os.path.join(self.environment.get_log_dir(), 'coverage.xml')])
elem.add_item('DESC', 'Generating XML coverage report.')
elem.write(outfile)
elem = NinjaBuildElement(self.all_outputs, 'coverage-text', 'CUSTOM_COMMAND', '')
- elem.add_item('COMMAND', [gcovr_exe, '-r', self.environment.get_build_dir(),\
+ elem.add_item('COMMAND', [gcovr_exe, '-r', self.environment.get_source_dir(),\
'-o', os.path.join(self.environment.get_log_dir(), 'coverage.txt')])
elem.add_item('DESC', 'Generating text coverage report.')
elem.write(outfile)
if lcov_exe and genhtml_exe:
added_rule = True
- phony_elem = NinjaBuildElement(self.all_outputs, 'coverage-html', 'phony', 'coveragereport/index.html')
- phony_elem.write(outfile)
-
- elem = NinjaBuildElement(self.all_outputs, 'coveragereport/index.html', 'CUSTOM_COMMAND', '')
htmloutdir = os.path.join(self.environment.get_log_dir(), 'coveragereport')
+ covinfo = os.path.join(self.environment.get_log_dir(), 'coverage.info')
+ phony_elem = NinjaBuildElement(self.all_outputs, 'coverage-html', 'phony', os.path.join(htmloutdir, 'index.html'))
+ phony_elem.write(outfile)
+ elem = NinjaBuildElement(self.all_outputs, os.path.join(htmloutdir, 'index.html'), 'CUSTOM_COMMAND', '')
command = [lcov_exe, '--directory', self.environment.get_build_dir(),\
- '--capture', '--output-file', 'coverage.info', '--no-checksum',\
+ '--capture', '--output-file', covinfo, '--no-checksum',\
'&&', genhtml_exe, '--prefix', self.environment.get_build_dir(),\
'--output-directory', htmloutdir, '--title', 'Code coverage',\
- '--legend', '--show-details', 'coverage.info']
+ '--legend', '--show-details', covinfo]
elem.add_item('COMMAND', command)
elem.add_item('DESC', 'Generating HTML coverage report.')
elem.write(outfile)
@@ -584,10 +599,9 @@ int dummy;
elem.write(outfile)
def generate_tests(self, outfile):
- self.serialise_tests()
+ (test_data, benchmark_data) = self.serialise_tests()
valgrind = environment.find_valgrind()
script_root = self.environment.get_script_dir()
- test_data = os.path.join(self.environment.get_scratch_dir(), 'meson_test_setup.dat')
cmd = [ sys.executable, self.environment.get_build_command(), '--internal', 'test' ]
if not self.environment.coredata.get_builtin_option('stdsplit'):
cmd += ['--no-stdsplit']
@@ -610,7 +624,6 @@ int dummy;
# And then benchmarks.
benchmark_script = os.path.join(script_root, 'meson_benchmark.py')
- benchmark_data = os.path.join(self.environment.get_scratch_dir(), 'meson_benchmark_setup.dat')
cmd = [sys.executable, self.environment.get_build_command(), '--internal', 'benchmark', benchmark_data]
elem = NinjaBuildElement(self.all_outputs, 'benchmark', 'CUSTOM_COMMAND', ['all', 'PHONY'])
elem.add_item('COMMAND', cmd)
@@ -1706,16 +1719,6 @@ rule FORTRAN_DEP_HACK
elem.add_item('LINK_ARGS', commands)
return elem
- def get_custom_target_provided_libraries(self, target):
- libs = []
- for t in target.get_generated_sources():
- if not isinstance(t, build.CustomTarget):
- continue
- for f in t.output:
- if self.environment.is_library(f):
- libs.append(os.path.join(self.get_target_dir(t), f))
- return libs
-
def determine_rpath_dirs(self, target):
link_deps = target.get_all_link_deps()
result = []
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index 725b8ed..82d0dc9 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -69,13 +69,18 @@ class Vs2010Backend(backends.Backend):
if len(src_conflicts) > 1}
def generate_custom_generator_commands(self, target, parent_node):
- all_output_files = []
+ generator_output_files = []
commands = []
inputs = []
outputs = []
+ custom_target_include_dirs = []
+ custom_target_output_files = []
for genlist in target.get_generated_sources():
if isinstance(genlist, build.CustomTarget):
- all_output_files += [os.path.join(self.get_target_dir(genlist), i) for i in genlist.output]
+ custom_target_output_files += [os.path.join(self.get_target_dir(genlist), i) for i in genlist.output]
+ idir = self.relpath(self.get_target_dir(genlist), self.get_target_dir(target))
+ if idir not in custom_target_include_dirs:
+ custom_target_include_dirs.append(idir)
else:
generator = genlist.get_generator()
exe = generator.get_exe()
@@ -93,7 +98,7 @@ class Vs2010Backend(backends.Backend):
infilename = os.path.join(self.environment.get_source_dir(), curfile)
outfiles = genlist.get_outputs_for(curfile)
outfiles = [os.path.join(target_private_dir, of) for of in outfiles]
- all_output_files += outfiles
+ generator_output_files += outfiles
args = [x.replace("@INPUT@", infilename).replace('@OUTPUT@', sole_output)\
for x in base_args]
args = [x.replace("@SOURCE_DIR@", self.environment.get_source_dir()).replace("@BUILD_DIR@", target_private_dir)
@@ -111,7 +116,7 @@ class Vs2010Backend(backends.Backend):
ET.SubElement(cbs, 'Message').text = 'Generating custom sources.'
pg = ET.SubElement(parent_node, 'PropertyGroup')
ET.SubElement(pg, 'CustomBuildBeforeTargets').text = 'ClCompile'
- return all_output_files
+ return generator_output_files, custom_target_output_files, custom_target_include_dirs
def generate(self, interp):
self.resolve_source_conflicts()
@@ -260,6 +265,8 @@ class Vs2010Backend(backends.Backend):
lang = self.lang_from_source_file(i)
if lang not in languages:
languages.append(lang)
+ elif self.environment.is_library(i):
+ pass
else:
# Everything that is not an object or source file is considered a header.
headers.append(i)
@@ -436,8 +443,12 @@ class Vs2010Backend(backends.Backend):
ET.SubElement(type_config, 'WholeProgramOptimization').text = 'false'
ET.SubElement(type_config, 'UseDebugLibraries').text = 'true'
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.props')
- generated_files = self.generate_custom_generator_commands(target, root)
+ generated_files, custom_target_output_files, generated_files_include_dirs = self.generate_custom_generator_commands(target, root)
(gen_src, gen_hdrs, gen_objs, gen_langs) = self.split_sources(generated_files)
+ (custom_src, custom_hdrs, custom_objs, custom_langs) = self.split_sources(custom_target_output_files)
+ gen_src += custom_src
+ gen_hdrs += custom_hdrs
+ gen_langs += custom_langs
direlem = ET.SubElement(root, 'PropertyGroup')
fver = ET.SubElement(direlem, '_ProjectFileVersion')
fver.text = self.project_file_version
@@ -455,7 +466,7 @@ class Vs2010Backend(backends.Backend):
opt = ET.SubElement(clconf, 'Optimization')
opt.text = 'disabled'
inc_dirs = ['.', self.relpath(self.get_target_private_dir(target), self.get_target_dir(target)),
- proj_to_src_dir]
+ proj_to_src_dir] + generated_files_include_dirs
extra_args = {'c': [], 'cpp': []}
for l, args in self.environment.coredata.external_args.items():
@@ -561,10 +572,14 @@ class Vs2010Backend(backends.Backend):
rel_path = self.relpath(lobj.subdir, target.subdir)
linkname = os.path.join(rel_path, lobj.get_import_filename())
additional_links.append(linkname)
+ for lib in self.get_custom_target_provided_libraries(target):
+ additional_links.append(self.relpath(lib, self.get_target_dir(target)))
additional_objects = []
for o in self.flatten_object_list(target, down):
assert(isinstance(o, str))
additional_objects.append(o)
+ for o in custom_objs:
+ additional_objects.append(self.relpath(o, self.get_target_dir(target)))
if len(additional_links) > 0:
additional_links.append('%(AdditionalDependencies)')
ET.SubElement(link, 'AdditionalDependencies').text = ';'.join(additional_links)
@@ -773,7 +788,6 @@ if %%errorlevel%% neq 0 goto :VCEnd'''
ET.SubElement(midl, 'ProxyFileName').text = '%(Filename)_p.c'
postbuild = ET.SubElement(action, 'PostBuildEvent')
ET.SubElement(postbuild, 'Message')
- test_data = os.path.join(self.environment.get_scratch_dir(), 'meson_test_setup.dat')
test_command = [sys.executable,
self.environment.get_build_command(),
'--internal',
@@ -787,14 +801,12 @@ endlocal & call :cmErrorLevel %%errorlevel%% & goto :cmDone
exit /b %%1
:cmDone
if %%errorlevel%% neq 0 goto :VCEnd'''
+ test_data = self.serialise_tests()[0]
ET.SubElement(postbuild, 'Command').text =\
cmd_templ % ('" "'.join(test_command), test_data)
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets')
tree = ET.ElementTree(root)
tree.write(ofname, encoding='utf-8', xml_declaration=True)
- datafile = open(test_data, 'wb')
- self.serialise_tests()
- datafile.close()
# ElementTree can not do prettyprinting so do it manually
#doc = xml.dom.minidom.parse(ofname)
#open(ofname, 'w').write(doc.toprettyxml())
diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py
index 6bda826..0ce90ce 100644
--- a/mesonbuild/backend/xcodebackend.py
+++ b/mesonbuild/backend/xcodebackend.py
@@ -64,7 +64,7 @@ class XCodeBackend(backends.Backend):
def generate(self, interp):
self.interpreter = interp
- self.serialise_tests()
+ test_data = self.serialise_tests()[0]
self.generate_filemap()
self.generate_buildmap()
self.generate_buildstylemap()
@@ -92,7 +92,7 @@ class XCodeBackend(backends.Backend):
self.generate_pbx_group()
self.generate_pbx_native_target()
self.generate_pbx_project()
- self.generate_pbx_shell_build_phase()
+ self.generate_pbx_shell_build_phase(test_data)
self.generate_pbx_sources_build_phase()
self.generate_pbx_target_dependency()
self.generate_xc_build_configuration()
@@ -480,7 +480,7 @@ class XCodeBackend(backends.Backend):
self.write_line('};')
self.ofile.write('/* End PBXProject section */\n')
- def generate_pbx_shell_build_phase(self):
+ def generate_pbx_shell_build_phase(self, test_data):
self.ofile.write('\n/* Begin PBXShellScriptBuildPhase section */\n')
self.write_line('%s = {' % self.test_command_id)
self.indent_level += 1
@@ -496,7 +496,6 @@ class XCodeBackend(backends.Backend):
self.write_line('shellPath = /bin/sh;')
script_root = self.environment.get_script_dir()
test_script = os.path.join(script_root, 'meson_test.py')
- test_data = os.path.join(self.environment.get_scratch_dir(), 'meson_test_setup.dat')
cmd = [sys.executable, test_script, test_data, '--wd', self.environment.get_build_dir()]
cmdstr = ' '.join(["'%s'" % i for i in cmd])
self.write_line('shellScript = "%s";' % cmdstr)
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 63cdcf3..105fb0b 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -871,11 +871,12 @@ class CustomTarget:
self.install = kwargs['install']
if not isinstance(self.install, bool):
raise InvalidArguments('"install" must be boolean.')
- if 'install_dir' not in kwargs:
- raise InvalidArguments('"install_dir" not specified.')
- self.install_dir = kwargs['install_dir']
- if not(isinstance(self.install_dir, str)):
- raise InvalidArguments('"install_dir" must be a string.')
+ if self.install:
+ if 'install_dir' not in kwargs:
+ raise InvalidArguments('"install_dir" not specified.')
+ self.install_dir = kwargs['install_dir']
+ if not(isinstance(self.install_dir, str)):
+ raise InvalidArguments('"install_dir" must be a string.')
else:
self.install = False
self.build_always = kwargs.get('build_always', False)
diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py
index 0535653..a320e7d 100644
--- a/mesonbuild/compilers.py
+++ b/mesonbuild/compilers.py
@@ -520,7 +520,8 @@ int main () {{ {1}; }}'''
ofile = open(srcname, 'w')
ofile.write(code)
ofile.close()
- extra_args = extra_args + self.get_output_args(dstname)
+ extra_args = self.unix_link_flags_to_native(extra_args) + \
+ self.get_output_args(dstname)
p = self.compile(code, srcname, extra_args)
try:
os.remove(dstname)
@@ -539,7 +540,7 @@ int main () {{ {1}; }}'''
ofile.close()
exename = srcname + '.exe' # Is guaranteed to be executable on every platform.
commands = self.get_exelist()
- commands += extra_args
+ commands += self.unix_link_flags_to_native(extra_args)
commands.append(srcname)
commands += self.get_output_args(exename)
p = subprocess.Popen(commands, cwd=os.path.split(srcname)[0], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@@ -576,16 +577,30 @@ int main () {{ {1}; }}'''
return RunResult(True, pe.returncode, so, se)
def cross_sizeof(self, element, prefix, env, extra_args=[]):
- templ = '''%s
+ element_exists_templ = '''#include <stdio.h>
+{0}
+int main(int argc, char **argv) {{
+ {1} something;
+}}
+'''
+ templ = '''#include <stdio.h>
+%s
int temparray[%d-sizeof(%s)];
'''
try:
extra_args += env.cross_info.config['properties'][self.language + '_args']
except KeyError:
pass
+ extra_args += self.get_no_optimization_args()
+ if not self.compiles(element_exists_templ.format(prefix, element)):
+ return -1
for i in range(1, 1024):
code = templ % (prefix, i, element)
if self.compiles(code, extra_args):
+ if self.id == 'msvc':
+ # MSVC refuses to construct an array of zero size, so
+ # the test only succeeds when i is sizeof(element) + 1
+ return i - 1
return i
raise EnvironmentException('Cross checking sizeof overflowed.')
@@ -608,6 +623,11 @@ int main(int argc, char **argv) {
return int(res.stdout)
def cross_alignment(self, typename, env, extra_args=[]):
+ type_exists_templ = '''#include <stdio.h>
+int main(int argc, char **argv) {{
+ {0} something;
+}}
+'''
templ = '''#include<stddef.h>
struct tmp {
char c;
@@ -620,9 +640,16 @@ int testarray[%d-offsetof(struct tmp, target)];
extra_args += env.cross_info.config['properties'][self.language + '_args']
except KeyError:
pass
+ extra_args += self.get_no_optimization_args()
+ if not self.compiles(type_exists_templ.format(typename)):
+ return -1
for i in range(1, 1024):
code = templ % (typename, i)
if self.compiles(code, extra_args):
+ if self.id == 'msvc':
+ # MSVC refuses to construct an array of zero size, so
+ # the test only succeeds when i is sizeof(element) + 1
+ return i - 1
return i
raise EnvironmentException('Cross checking offsetof overflowed.')
@@ -1366,19 +1393,6 @@ class VisualStudioCCompiler(CCompiler):
def build_rpath_args(self, build_dir, rpath_paths, install_rpath):
return []
- def find_library(self, libname, extra_dirs):
- code = '''int main(int argc, char **argv) {
- return 0;
-}
- '''
- args = []
- for i in extra_dirs:
- args += self.get_linker_search_args(i)
- args.append(libname + '.lib')
- if self.links(code, extra_args=args):
- return args
- return None
-
# FIXME, no idea what these should be.
def thread_flags(self):
return []
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index a7d6d97..41e8531 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -158,7 +158,8 @@ class Environment():
coredata.save(self.coredata, cdf)
def get_script_dir(self):
- return os.path.join(os.path.dirname(self.meson_script_file), '../scripts')
+ import mesonbuild.scripts
+ return os.path.dirname(mesonbuild.scripts.__file__)
def get_log_dir(self):
return self.log_dir
@@ -222,6 +223,7 @@ class Environment():
ccache = self.detect_ccache()
is_cross = False
exe_wrap = None
+ popen_exceptions = {}
for compiler in compilers:
try:
basename = os.path.basename(compiler).lower()
@@ -229,9 +231,10 @@ class Environment():
arg = '/?'
else:
arg = '--version'
- p = subprocess.Popen([compiler] + [arg], stdout=subprocess.PIPE,
+ p = subprocess.Popen([compiler, arg], stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
- except OSError:
+ except OSError as e:
+ popen_exceptions[' '.join([compiler, arg])] = e
continue
(out, err) = p.communicate()
out = out.decode(errors='ignore')
@@ -262,7 +265,12 @@ class Environment():
# everything else to stdout. Why? Lord only knows.
version = re.search(Environment.version_regex, err).group()
return VisualStudioCCompiler([compiler], version, is_cross, exe_wrap)
- raise EnvironmentException('Unknown compiler(s): "' + ', '.join(compilers) + '"')
+ errmsg = 'Unknown compiler(s): "' + ', '.join(compilers) + '"'
+ if popen_exceptions:
+ errmsg += '\nThe follow exceptions were encountered:'
+ for (c, e) in popen_exceptions.items():
+ errmsg += '\nRunning "{0}" gave "{1}"'.format(c, e)
+ raise EnvironmentException(errmsg)
def detect_fortran_compiler(self, want_cross):
evar = 'FC'
@@ -281,13 +289,15 @@ class Environment():
compilers = self.default_fortran
is_cross = False
exe_wrap = None
+ popen_exceptions = {}
for compiler in compilers:
for arg in ['--version', '-V']:
try:
- p = subprocess.Popen([compiler] + [arg],
+ p = subprocess.Popen([compiler, arg],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
- except OSError:
+ except OSError as e:
+ popen_exceptions[' '.join([compiler, arg])] = e
continue
(out, err) = p.communicate()
out = out.decode(errors='ignore')
@@ -325,8 +335,12 @@ class Environment():
if 'NAG Fortran' in err:
return NAGFortranCompiler([compiler], version, is_cross, exe_wrap)
-
- raise EnvironmentException('Unknown compiler(s): "' + ', '.join(compilers) + '"')
+ errmsg = 'Unknown compiler(s): "' + ', '.join(compilers) + '"'
+ if popen_exceptions:
+ errmsg += '\nThe follow exceptions were encountered:'
+ for (c, e) in popen_exceptions.items():
+ errmsg += '\nRunning "{0}" gave "{1}"'.format(c, e)
+ raise EnvironmentException(errmsg)
def get_scratch_dir(self):
return self.scratch_dir
@@ -355,6 +369,7 @@ class Environment():
ccache = self.detect_ccache()
is_cross = False
exe_wrap = None
+ popen_exceptions = {}
for compiler in compilers:
basename = os.path.basename(compiler).lower()
if basename == 'cl' or basename == 'cl.exe':
@@ -365,7 +380,8 @@ class Environment():
p = subprocess.Popen([compiler, arg],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
- except OSError:
+ except OSError as e:
+ popen_exceptions[' '.join([compiler, arg])] = e
continue
(out, err) = p.communicate()
out = out.decode(errors='ignore')
@@ -394,7 +410,12 @@ class Environment():
if 'Microsoft' in out or 'Microsoft' in err:
version = re.search(Environment.version_regex, err).group()
return VisualStudioCPPCompiler([compiler], version, is_cross, exe_wrap)
- raise EnvironmentException('Unknown compiler(s) "' + ', '.join(compilers) + '"')
+ errmsg = 'Unknown compiler(s): "' + ', '.join(compilers) + '"'
+ if popen_exceptions:
+ errmsg += '\nThe follow exceptions were encountered:'
+ for (c, e) in popen_exceptions.items():
+ errmsg += '\nRunning "{0}" gave "{1}"'.format(c, e)
+ raise EnvironmentException(errmsg)
def detect_objc_compiler(self, want_cross):
if self.is_cross_build() and want_cross:
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index 2ac0932..fe831bd 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -252,7 +252,9 @@ def do_mesondefine(line, confdata):
def do_conf_file(src, dst, confdata):
data = open(src).readlines()
- regex = re.compile('@(.*?)@')
+ # Only allow (a-z, A-Z, 0-9, _, -) as valid characters for a define
+ # Also allow escaping '@' with '\@'
+ regex = re.compile(r'[^\\]?@([-a-zA-Z0-9_]+)@')
result = []
for line in data:
if line.startswith('#mesondefine'):
diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py
index 3b05afb..052c178 100644
--- a/mesonbuild/mesonmain.py
+++ b/mesonbuild/mesonmain.py
@@ -163,7 +163,10 @@ itself as required.'''
def run_script_command(args):
cmdname = args[0]
cmdargs = args[1:]
- if cmdname == 'test':
+ if cmdname == 'exe':
+ import mesonbuild.scripts.meson_exe as abc
+ cmdfunc = abc.run
+ elif cmdname == 'test':
import mesonbuild.scripts.meson_test as abc
cmdfunc = abc.run
elif cmdname == 'benchmark':
diff --git a/mesonbuild/scripts/delwithsuffix.py b/mesonbuild/scripts/delwithsuffix.py
index 38ab406..e112101 100644
--- a/mesonbuild/scripts/delwithsuffix.py
+++ b/mesonbuild/scripts/delwithsuffix.py
@@ -17,7 +17,7 @@
import os, sys
def run(args):
- if len(sys.argv) != 2:
+ if len(sys.argv) != 3:
print('delwithsuffix.py <root of subdir to process> <suffix to delete>')
sys.exit(1)
diff --git a/mesonbuild/scripts/meson_exe.py b/mesonbuild/scripts/meson_exe.py
new file mode 100644
index 0000000..f075fa0
--- /dev/null
+++ b/mesonbuild/scripts/meson_exe.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+
+# Copyright 2013-2016 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.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import sys
+import argparse
+import pickle
+import platform
+import subprocess
+
+import mesonbuild
+
+options = None
+
+parser = argparse.ArgumentParser()
+parser.add_argument('args', nargs='+')
+
+def is_windows():
+ platname = platform.system().lower()
+ return platname == 'windows' or 'mingw' in platname
+
+def run_with_mono(fname):
+ if fname.endswith('.exe') and not is_windows():
+ return True
+ return False
+
+def run_exe(exe):
+ if exe.fname[0].endswith('.jar'):
+ cmd = ['java', '-jar'] + exe.fname
+ elif not exe.is_cross and run_with_mono(exe.fname[0]):
+ cmd = ['mono'] + exe.fname
+ else:
+ if exe.is_cross:
+ if exe.exe_runner is None:
+ raise Exception('BUG: Trying to run cross-compiled exes with no wrapper')
+ else:
+ cmd = [exe.exe_runner] + exe.fname
+ else:
+ cmd = exe.fname
+ child_env = os.environ.copy()
+ child_env.update(exe.env)
+ if len(exe.extra_paths) > 0:
+ child_env['PATH'] = ';'.join(exe.extra_paths + ['']) + child_env['PATH']
+ p = subprocess.Popen(cmd + exe.cmd_args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ env=child_env,
+ cwd=exe.workdir)
+
+def run(args):
+ global options
+ options = parser.parse_args(args)
+ if len(options.args) != 1:
+ print('Test runner for Meson. Do not run on your own, mmm\'kay?')
+ print(sys.argv[0] + ' [data file]')
+ exe_data_file = options.args[0]
+ exe = pickle.load(open(exe_data_file, 'rb'))
+ run_exe(exe)
+
+if __name__ == '__main__':
+ sys.exit(run(sys.argv[1:]))