aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild')
-rw-r--r--mesonbuild/backend/backends.py2
-rw-r--r--mesonbuild/backend/ninjabackend.py68
-rw-r--r--mesonbuild/backend/vs2010backend.py2
-rw-r--r--mesonbuild/backend/xcodebackend.py2
-rw-r--r--mesonbuild/build.py68
-rw-r--r--mesonbuild/compilers/c.py21
-rw-r--r--mesonbuild/compilers/compilers.py13
-rw-r--r--mesonbuild/compilers/cpp.py10
-rw-r--r--mesonbuild/compilers/fortran.py2
-rw-r--r--mesonbuild/compilers/rust.py15
-rw-r--r--mesonbuild/coredata.py36
-rw-r--r--mesonbuild/dependencies/base.py44
-rw-r--r--mesonbuild/dependencies/dev.py10
-rw-r--r--mesonbuild/dependencies/misc.py97
-rw-r--r--mesonbuild/environment.py33
-rw-r--r--mesonbuild/interpreter.py62
-rw-r--r--mesonbuild/interpreterbase.py14
-rw-r--r--mesonbuild/mesonlib.py25
-rw-r--r--mesonbuild/mesonmain.py7
-rw-r--r--mesonbuild/mintro.py6
-rw-r--r--mesonbuild/mlog.py7
-rw-r--r--mesonbuild/modules/gnome.py4
-rw-r--r--mesonbuild/modules/pkgconfig.py8
-rw-r--r--mesonbuild/modules/qt.py6
-rw-r--r--mesonbuild/modules/unstable_icestorm.py85
-rw-r--r--mesonbuild/mparser.py6
-rw-r--r--mesonbuild/mtest.py12
-rw-r--r--mesonbuild/optinterpreter.py46
-rw-r--r--mesonbuild/scripts/coverage.py28
-rw-r--r--mesonbuild/scripts/gettext.py2
-rw-r--r--mesonbuild/scripts/meson_install.py74
-rw-r--r--mesonbuild/wrap/wrap.py13
-rw-r--r--mesonbuild/wrap/wraptool.py4
33 files changed, 558 insertions, 274 deletions
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 5a5db22..292b027 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -322,7 +322,7 @@ class Backend:
continue
if os.path.splitext(libpath)[1] not in ['.dll', '.lib', '.so']:
continue
- absdir = os.path.split(libpath)[0]
+ absdir = os.path.dirname(libpath)
rel_to_src = absdir[len(self.environment.get_source_dir()) + 1:]
assert(not os.path.isabs(rel_to_src))
paths.append(os.path.join(self.build_to_src, rel_to_src))
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 77c7d50..6ab67fb 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -38,8 +38,12 @@ else:
execute_wrapper = ''
rmfile_prefix = 'rm -f {} &&'
-def ninja_quote(text):
- for char in ('$', ' ', ':'):
+def ninja_quote(text, is_build_line=False):
+ if is_build_line:
+ qcs = ('$', ' ', ':')
+ else:
+ qcs = ('$', ' ')
+ for char in qcs:
text = text.replace(char, '$' + char)
if '\n' in text:
errmsg = '''Ninja does not support newlines in rules. The content was:
@@ -87,13 +91,13 @@ class NinjaBuildElement:
def write(self, outfile):
self.check_outputs()
- line = 'build %s: %s %s' % (' '.join([ninja_quote(i) for i in self.outfilenames]),
+ line = 'build %s: %s %s' % (' '.join([ninja_quote(i, True) for i in self.outfilenames]),
self.rule,
- ' '.join([ninja_quote(i) for i in self.infilenames]))
+ ' '.join([ninja_quote(i, True) for i in self.infilenames]))
if len(self.deps) > 0:
- line += ' | ' + ' '.join([ninja_quote(x) for x in self.deps])
+ line += ' | ' + ' '.join([ninja_quote(x, True) for x in self.deps])
if len(self.orderdeps) > 0:
- line += ' || ' + ' '.join([ninja_quote(x) for x in self.orderdeps])
+ line += ' || ' + ' '.join([ninja_quote(x, True) for x in self.orderdeps])
line += '\n'
# This is the only way I could find to make this work on all
# platforms including Windows command shell. Slash is a dir separator
@@ -823,7 +827,7 @@ int dummy;
if subdir is None:
subdir = os.path.join(manroot, 'man' + num)
srcabs = f.absolute_path(self.environment.get_source_dir(), self.environment.get_build_dir())
- dstabs = os.path.join(subdir, os.path.split(f.fname)[1] + '.gz')
+ dstabs = os.path.join(subdir, os.path.basename(f.fname) + '.gz')
i = [srcabs, dstabs]
d.man.append(i)
@@ -836,24 +840,22 @@ int dummy;
subdir = de.install_dir
for f in de.sources:
assert(isinstance(f, mesonlib.File))
- plain_f = os.path.split(f.fname)[1]
+ plain_f = os.path.basename(f.fname)
dstabs = os.path.join(subdir, plain_f)
i = [f.absolute_path(srcdir, builddir), dstabs, de.install_mode]
d.data.append(i)
def generate_subdir_install(self, d):
for sd in self.build.get_install_subdirs():
- inst_subdir = sd.installable_subdir.rstrip('/')
- idir_parts = inst_subdir.split('/')
- if len(idir_parts) > 1:
- subdir = os.path.join(sd.source_subdir, '/'.join(idir_parts[:-1]))
- inst_dir = idir_parts[-1]
- else:
- subdir = sd.source_subdir
- 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, sd.install_mode, sd.exclude])
+ src_dir = os.path.join(self.environment.get_source_dir(),
+ sd.source_subdir,
+ sd.installable_subdir).rstrip('/')
+ dst_dir = os.path.join(self.environment.get_prefix(),
+ sd.install_dir)
+ if not sd.strip_directory:
+ dst_dir = os.path.join(dst_dir, os.path.basename(src_dir))
+ d.install_subdirs.append([src_dir, dst_dir, sd.install_mode,
+ sd.exclude])
def generate_tests(self, outfile):
self.serialize_tests()
@@ -1278,7 +1280,7 @@ int dummy;
# Target names really should not have slashes in them, but
# unfortunately we did not check for that and some downstream projects
# now have them. Once slashes are forbidden, remove this bit.
- target_slashname_workaround_dir = os.path.join(os.path.split(target.name)[0],
+ target_slashname_workaround_dir = os.path.join(os.path.dirname(target.name),
self.get_target_dir(target))
else:
target_slashname_workaround_dir = self.get_target_dir(target)
@@ -1291,7 +1293,11 @@ int dummy;
# installations
for rpath_arg in rpath_args:
args += ['-C', 'link-arg=' + rpath_arg + ':' + os.path.join(rustc.get_sysroot(), 'lib')]
- element = NinjaBuildElement(self.all_outputs, target_name, 'rust_COMPILER', relsrc)
+ crstr = ''
+ if target.is_cross:
+ crstr = '_CROSS'
+ compiler_name = 'rust%s_COMPILER' % crstr
+ element = NinjaBuildElement(self.all_outputs, target_name, compiler_name, relsrc)
if len(orderdeps) > 0:
element.add_orderdep(orderdeps)
element.add_item('ARGS', args)
@@ -1401,7 +1407,7 @@ int dummy;
objects = [] # Relative to swift invocation dir
rel_objects = [] # Relative to build.ninja
for i in abssrc + abs_generated:
- base = os.path.split(i)[1]
+ base = os.path.basename(i)
oname = os.path.splitext(base)[0] + '.o'
objects.append(oname)
rel_objects.append(os.path.join(self.get_target_private_dir(target), oname))
@@ -1579,8 +1585,11 @@ int dummy;
outfile.write(restat)
outfile.write('\n')
- def generate_rust_compile_rules(self, compiler, outfile):
- rule = 'rule %s_COMPILER\n' % compiler.get_language()
+ def generate_rust_compile_rules(self, compiler, outfile, is_cross):
+ crstr = ''
+ if is_cross:
+ crstr = '_CROSS'
+ rule = 'rule %s%s_COMPILER\n' % (compiler.get_language(), crstr)
invoc = ' '.join([ninja_quote(i) for i in compiler.get_exelist()])
command = ' command = %s $ARGS $in\n' % invoc
description = ' description = Compiling Rust source $in.\n'
@@ -1671,8 +1680,7 @@ rule FORTRAN_DEP_HACK
self.generate_vala_compile_rules(compiler, outfile)
return
if langname == 'rust':
- if not is_cross:
- self.generate_rust_compile_rules(compiler, outfile)
+ self.generate_rust_compile_rules(compiler, outfile, is_cross)
return
if langname == 'swift':
if not is_cross:
@@ -1928,7 +1936,7 @@ rule FORTRAN_DEP_HACK
# Check if a source uses a module it exports itself.
# Potential bug if multiple targets have a file with
# the same name.
- if mod_source_file.fname == os.path.split(src)[1]:
+ if mod_source_file.fname == os.path.basename(src):
continue
mod_name = compiler.module_name_to_filename(
usematch.group(1))
@@ -2271,7 +2279,7 @@ rule FORTRAN_DEP_HACK
commands = []
commands += self.generate_basic_compiler_args(target, compiler)
- just_name = os.path.split(header)[1]
+ just_name = os.path.basename(header)
(objname, pch_args) = compiler.gen_pch_args(just_name, source, dst)
commands += pch_args
commands += self.get_compile_debugfile_args(compiler, target, objname)
@@ -2281,7 +2289,7 @@ rule FORTRAN_DEP_HACK
def generate_gcc_pch_command(self, target, compiler, pch):
commands = self._generate_single_compile(target, compiler)
dst = os.path.join(self.get_target_private_dir(target),
- os.path.split(pch)[-1] + '.' + compiler.get_pch_suffix())
+ os.path.basename(pch) + '.' + compiler.get_pch_suffix())
dep = dst + '.' + compiler.get_depfile_suffix()
return commands, dep, dst, [] # Gcc does not create an object file during pch generation.
@@ -2476,7 +2484,7 @@ rule FORTRAN_DEP_HACK
# unfortunately we did not check for that and some downstream projects
# now have them. Once slashes are forbidden, remove this bit.
target_slashname_workaround_dir = os.path.join(
- os.path.split(target.name)[0],
+ os.path.dirname(target.name),
self.get_target_dir(target))
else:
target_slashname_workaround_dir = self.get_target_dir(target)
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index 1722db7..057e7c9 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -1034,7 +1034,7 @@ class Vs2010Backend(backends.Backend):
pch_file = ET.SubElement(inc_cl, 'PrecompiledHeaderFile')
# MSBuild searches for the header relative from the implementation, so we have to use
# just the file name instead of the relative path to the file.
- pch_file.text = os.path.split(header)[1]
+ pch_file.text = os.path.basename(header)
self.add_additional_options(lang, inc_cl, file_args)
self.add_preprocessor_defines(lang, inc_cl, file_defines)
self.add_include_dirs(lang, inc_cl, file_inc_dirs)
diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py
index aca3aea..3ae31e4 100644
--- a/mesonbuild/backend/xcodebackend.py
+++ b/mesonbuild/backend/xcodebackend.py
@@ -311,7 +311,7 @@ class XCodeBackend(backends.Backend):
for fname, idval in self.filemap.items():
fullpath = os.path.join(self.environment.get_source_dir(), fname)
xcodetype = self.get_xcodetype(fname)
- name = os.path.split(fname)[-1]
+ name = os.path.basename(fname)
path = fname
self.ofile.write(src_templ % (idval, fullpath, xcodetype, name, path))
target_templ = '%s /* %s */ = { isa = PBXFileReference; explicitFileType = "%s"; path = %s; refType = %d; sourceTree = BUILT_PRODUCTS_DIR; };\n'
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 5eab794..400b9e5 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -22,7 +22,7 @@ from . import mlog
from .mesonlib import File, MesonException, listify, extract_as_list
from .mesonlib import typeslistify, stringlistify, classify_unity_sources
from .mesonlib import get_filenames_templates_dict, substitute_values
-from .mesonlib import for_windows, for_darwin, for_cygwin
+from .mesonlib import for_windows, for_darwin, for_cygwin, for_android
from .compilers import is_object, clike_langs, sort_clike, lang_suffixes
known_basic_kwargs = {'install': True,
@@ -1065,7 +1065,7 @@ class Generator:
depfile = kwargs['depfile']
if not isinstance(depfile, str):
raise InvalidArguments('Depfile must be a string.')
- if os.path.split(depfile)[1] != depfile:
+ if os.path.basename(depfile) != depfile:
raise InvalidArguments('Depfile must be a plain filename without a subdirectory.')
self.depfile = depfile
if 'capture' in kwargs:
@@ -1075,7 +1075,7 @@ class Generator:
self.capture = capture
def get_base_outnames(self, inname):
- plainname = os.path.split(inname)[1]
+ plainname = os.path.basename(inname)
basename = os.path.splitext(plainname)[0]
bases = [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.outputs]
return bases
@@ -1083,12 +1083,12 @@ class Generator:
def get_dep_outname(self, inname):
if self.depfile is None:
raise InvalidArguments('Tried to get dep name for rule that does not have dependency file defined.')
- plainname = os.path.split(inname)[1]
+ plainname = os.path.basename(inname)
basename = os.path.splitext(plainname)[0]
return self.depfile.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname)
def get_arglist(self, inname):
- plainname = os.path.split(inname)[1]
+ plainname = os.path.basename(inname)
basename = os.path.splitext(plainname)[0]
return [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.arglist]
@@ -1130,7 +1130,7 @@ class GeneratedList:
in_abs = infile.absolute_path(state.environment.source_dir, state.environment.build_dir)
assert(os.path.isabs(self.preserve_path_from))
rel = os.path.relpath(in_abs, self.preserve_path_from)
- path_segment = os.path.split(rel)[0]
+ path_segment = os.path.dirname(rel)
for of in outfiles:
result.append(os.path.join(path_segment, of))
return result
@@ -1393,6 +1393,11 @@ class SharedLibrary(BuildTarget):
else:
# libfoo.dylib
self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
+ elif for_android(is_cross, env):
+ prefix = 'lib'
+ suffix = 'so'
+ # Android doesn't support shared_library versioning
+ self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
else:
prefix = 'lib'
suffix = 'so'
@@ -1414,25 +1419,32 @@ class SharedLibrary(BuildTarget):
def process_kwargs(self, kwargs, environment):
super().process_kwargs(kwargs, environment)
- # Shared library version
- if 'version' in kwargs:
- self.ltversion = kwargs['version']
- if not isinstance(self.ltversion, str):
- raise InvalidArguments('Shared library version needs to be a string, not ' + type(self.ltversion).__name__)
- if not re.fullmatch(r'[0-9]+(\.[0-9]+){0,2}', self.ltversion):
- raise InvalidArguments('Invalid Shared library version "{0}". Must be of the form X.Y.Z where all three are numbers. Y and Z are optional.'.format(self.ltversion))
- # Try to extract/deduce the soversion
- if 'soversion' in kwargs:
- self.soversion = kwargs['soversion']
- if isinstance(self.soversion, int):
- self.soversion = str(self.soversion)
- if not isinstance(self.soversion, str):
- raise InvalidArguments('Shared library soversion is not a string or integer.')
- elif self.ltversion:
- # library version is defined, get the soversion from that
- # We replicate what Autotools does here and take the first
- # number of the version by default.
- self.soversion = self.ltversion.split('.')[0]
+
+ if not for_android(self.is_cross, self.environment):
+ supports_versioning = True
+ else:
+ supports_versioning = False
+
+ if supports_versioning:
+ # Shared library version
+ if 'version' in kwargs:
+ self.ltversion = kwargs['version']
+ if not isinstance(self.ltversion, str):
+ raise InvalidArguments('Shared library version needs to be a string, not ' + type(self.ltversion).__name__)
+ if not re.fullmatch(r'[0-9]+(\.[0-9]+){0,2}', self.ltversion):
+ raise InvalidArguments('Invalid Shared library version "{0}". Must be of the form X.Y.Z where all three are numbers. Y and Z are optional.'.format(self.ltversion))
+ # Try to extract/deduce the soversion
+ if 'soversion' in kwargs:
+ self.soversion = kwargs['soversion']
+ if isinstance(self.soversion, int):
+ self.soversion = str(self.soversion)
+ if not isinstance(self.soversion, str):
+ raise InvalidArguments('Shared library soversion is not a string or integer.')
+ elif self.ltversion:
+ # library version is defined, get the soversion from that
+ # We replicate what Autotools does here and take the first
+ # number of the version by default.
+ self.soversion = self.ltversion.split('.')[0]
# Visual Studio module-definitions file
if 'vs_module_defs' in kwargs:
path = kwargs['vs_module_defs']
@@ -1637,6 +1649,10 @@ class CustomTarget(Target):
for i in self.outputs:
if not(isinstance(i, str)):
raise InvalidArguments('Output argument not a string.')
+ if i == '':
+ raise InvalidArguments('Output must not be empty.')
+ if i.strip() == '':
+ raise InvalidArguments('Output must not consist only of whitespace.')
if '/' in i:
raise InvalidArguments('Output must not contain a path segment.')
if '@INPUT@' in i or '@INPUT0@' in i:
@@ -1659,7 +1675,7 @@ class CustomTarget(Target):
depfile = kwargs['depfile']
if not isinstance(depfile, str):
raise InvalidArguments('Depfile must be a string.')
- if os.path.split(depfile)[1] != depfile:
+ if os.path.basename(depfile) != depfile:
raise InvalidArguments('Depfile must be a plain filename without a subdirectory.')
self.depfile = depfile
self.command = self.flatten_command(kwargs['command'])
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index 4c6e3a2..a59b7d3 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -172,10 +172,10 @@ class CCompiler(Compiler):
return ' '.join(self.exelist)
def get_pch_use_args(self, pch_dir, header):
- return ['-include', os.path.split(header)[-1]]
+ return ['-include', os.path.basename(header)]
def get_pch_name(self, header_name):
- return os.path.split(header_name)[-1] + '.' + self.get_pch_suffix()
+ return os.path.basename(header_name) + '.' + self.get_pch_suffix()
def get_linker_search_args(self, dirname):
return ['-L' + dirname]
@@ -184,7 +184,7 @@ class CCompiler(Compiler):
return []
def gen_export_dynamic_link_args(self, env):
- if for_windows(env.is_cross_build(), env):
+ if for_windows(env.is_cross_build(), env) or for_cygwin(env.is_cross_build(), env):
return ['-Wl,--export-all-symbols']
elif for_darwin(env.is_cross_build(), env):
return []
@@ -804,6 +804,13 @@ class CCompiler(Compiler):
return ['-pthread']
def has_multi_arguments(self, args, env):
+ for arg in args:
+ if arg.startswith('-Wl,'):
+ mlog.warning('''{} looks like a linker argument, but has_argument
+and other similar methods only support checking compiler arguments.
+Using them to check linker arguments are never supported, and results
+are likely to be wrong regardless of the compiler you are using.
+'''.format(arg))
return self.compiles('int i;\n', env, extra_args=args)
@@ -875,7 +882,7 @@ class GnuCCompiler(GnuCompiler, CCompiler):
return ['-shared']
def get_pch_use_args(self, pch_dir, header):
- return ['-fpch-preprocess', '-include', os.path.split(header)[-1]]
+ return ['-fpch-preprocess', '-include', os.path.basename(header)]
class IntelCCompiler(IntelCompiler, CCompiler):
@@ -954,13 +961,13 @@ class VisualStudioCCompiler(CCompiler):
return 'pch'
def get_pch_name(self, header):
- chopped = os.path.split(header)[-1].split('.')[:-1]
+ chopped = os.path.basename(header).split('.')[:-1]
chopped.append(self.get_pch_suffix())
pchname = '.'.join(chopped)
return pchname
def get_pch_use_args(self, pch_dir, header):
- base = os.path.split(header)[-1]
+ base = os.path.basename(header)
pchname = self.get_pch_name(header)
return ['/FI' + base, '/Yu' + base, '/Fp' + os.path.join(pch_dir, pchname)]
@@ -1087,7 +1094,7 @@ class VisualStudioCCompiler(CCompiler):
mlog.debug('Running VS compile:')
mlog.debug('Command line: ', ' '.join(commands))
mlog.debug('Code:\n', code)
- p, stdo, stde = Popen_safe(commands, cwd=os.path.split(srcname)[0])
+ p, stdo, stde = Popen_safe(commands, cwd=os.path.dirname(srcname))
if p.returncode != 0:
return False
return not(warning_text in stde or warning_text in stdo)
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index 2602d14..034fef4 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -137,8 +137,11 @@ gnulike_buildtype_linker_args = {'plain': [],
msvc_buildtype_linker_args = {'plain': [],
'debug': [],
'debugoptimized': [],
- 'release': [],
- 'minsize': ['/INCREMENTAL:NO'],
+ # The otherwise implicit REF and ICF linker
+ # optimisations are disabled by /DEBUG.
+ # REF implies ICF.
+ 'release': ['/OPT:REF'],
+ 'minsize': ['/INCREMENTAL:NO', '/OPT:REF'],
}
java_buildtype_args = {'plain': [],
@@ -1040,7 +1043,7 @@ class GnuCompiler:
return 'gch'
def split_shlib_to_parts(self, fname):
- return os.path.split(fname)[0], fname
+ return os.path.dirname(fname), fname
def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module):
return get_gcc_soname_args(self.gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module)
@@ -1188,10 +1191,10 @@ class IntelCompiler:
self.lang_header, '-include', header, '-x', 'none']
def get_pch_name(self, header_name):
- return os.path.split(header_name)[-1] + '.' + self.get_pch_suffix()
+ return os.path.basename(header_name) + '.' + self.get_pch_suffix()
def split_shlib_to_parts(self, fname):
- return os.path.split(fname)[0], fname
+ return os.path.dirname(fname), fname
def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module):
if self.icc_type == ICC_STANDARD:
diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py
index 5e32ace..c10f38e 100644
--- a/mesonbuild/compilers/cpp.py
+++ b/mesonbuild/compilers/cpp.py
@@ -14,6 +14,7 @@
import os.path
+from .. import mlog
from .. import coredata
from ..mesonlib import version_compare
@@ -129,7 +130,7 @@ class GnuCPPCompiler(GnuCompiler, CPPCompiler):
return []
def get_pch_use_args(self, pch_dir, header):
- return ['-fpch-preprocess', '-include', os.path.split(header)[-1]]
+ return ['-fpch-preprocess', '-include', os.path.basename(header)]
class IntelCPPCompiler(IntelCompiler, CPPCompiler):
@@ -174,6 +175,13 @@ class IntelCPPCompiler(IntelCompiler, CPPCompiler):
return []
def has_multi_arguments(self, args, env):
+ for arg in args:
+ if arg.startswith('-Wl,'):
+ mlog.warning('''{} looks like a linker argument, but has_argument
+and other similar methods only support checking compiler arguments.
+Using them to check linker arguments are never supported, and results
+are likely to be wrong regardless of the compiler you are using.
+'''.format(arg))
return super().has_multi_arguments(args + ['-diag-error', '10006'], env)
diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py
index 1b42bfa..f9fcc1c 100644
--- a/mesonbuild/compilers/fortran.py
+++ b/mesonbuild/compilers/fortran.py
@@ -91,7 +91,7 @@ end program prog
return gnulike_buildtype_linker_args[buildtype]
def split_shlib_to_parts(self, fname):
- return os.path.split(fname)[0], fname
+ return os.path.dirname(fname), fname
def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module):
return get_gcc_soname_args(self.gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module)
diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py
index b93289f..d1a05ed 100644
--- a/mesonbuild/compilers/rust.py
+++ b/mesonbuild/compilers/rust.py
@@ -19,9 +19,11 @@ from ..mesonlib import EnvironmentException, Popen_safe
from .compilers import Compiler, rust_buildtype_args
class RustCompiler(Compiler):
- def __init__(self, exelist, version):
+ def __init__(self, exelist, version, is_cross, exe_wrapper=None):
self.language = 'rust'
super().__init__(exelist, version)
+ self.is_cross = is_cross
+ self.exe_wrapper = exe_wrapper
self.id = 'rustc'
def needs_static_linker(self):
@@ -41,7 +43,16 @@ class RustCompiler(Compiler):
pc.wait()
if pc.returncode != 0:
raise EnvironmentException('Rust compiler %s can not compile programs.' % self.name_string())
- if subprocess.call(output_name) != 0:
+ if self.is_cross:
+ if self.exe_wrapper is None:
+ # Can't check if the binaries run so we have to assume they do
+ return
+ cmdlist = self.exe_wrapper + [output_name]
+ else:
+ cmdlist = [output_name]
+ pe = subprocess.Popen(cmdlist, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+ pe.wait()
+ if pe.returncode != 0:
raise EnvironmentException('Executables created by Rust compiler %s are not runnable.' % self.name_string())
def get_dependency_gen_args(self, outfile):
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 0fdac8b..f87e62c 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -1,5 +1,4 @@
-
-# Copyright 2012-2017 The Meson development team
+# Copyright 2012-2018 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.
@@ -25,12 +24,19 @@ import ast
version = '0.45.0.dev1'
backendlist = ['ninja', 'vs', 'vs2010', 'vs2015', 'vs2017', 'xcode']
+default_yielding = False
+
class UserOption:
- def __init__(self, name, description, choices):
+ def __init__(self, name, description, choices, yielding):
super().__init__()
self.name = name
self.choices = choices
self.description = description
+ if yielding is None:
+ yielding = default_yielding
+ if not isinstance(yielding, bool):
+ raise MesonException('Value of "yielding" must be a boolean.')
+ self.yielding = yielding
# Check that the input is a valid value and return the
# "cleaned" or "native" version. For example the Boolean
@@ -39,8 +45,8 @@ class UserOption:
raise RuntimeError('Derived option class did not override validate_value.')
class UserStringOption(UserOption):
- def __init__(self, name, description, value, choices=None):
- super().__init__(name, description, choices)
+ def __init__(self, name, description, value, choices=None, yielding=None):
+ super().__init__(name, description, choices, yielding)
self.set_value(value)
def validate(self, value):
@@ -56,8 +62,8 @@ class UserStringOption(UserOption):
return value
class UserBooleanOption(UserOption):
- def __init__(self, name, description, value):
- super().__init__(name, description, [True, False])
+ def __init__(self, name, description, value, yielding=None):
+ super().__init__(name, description, [True, False], yielding)
self.set_value(value)
def tobool(self, thing):
@@ -79,11 +85,17 @@ class UserBooleanOption(UserOption):
return self.tobool(value)
class UserIntegerOption(UserOption):
- def __init__(self, name, description, min_value, max_value, value):
- super().__init__(name, description, [True, False])
+ def __init__(self, name, description, min_value, max_value, value, yielding=None):
+ super().__init__(name, description, [True, False], yielding)
self.min_value = min_value
self.max_value = max_value
self.set_value(value)
+ c = []
+ if min_value is not None:
+ c.append('>=' + str(min_value))
+ if max_value is not None:
+ c.append('<=' + str(max_value))
+ self.choices = ', '.join(c)
def set_value(self, newvalue):
if isinstance(newvalue, str):
@@ -106,8 +118,8 @@ class UserIntegerOption(UserOption):
return self.toint(value)
class UserComboOption(UserOption):
- def __init__(self, name, description, choices, value):
- super().__init__(name, description, choices)
+ def __init__(self, name, description, choices, value, yielding=None):
+ super().__init__(name, description, choices, yielding)
if not isinstance(self.choices, list):
raise MesonException('Combo choices must be an array.')
for i in self.choices:
@@ -128,7 +140,7 @@ class UserComboOption(UserOption):
class UserArrayOption(UserOption):
def __init__(self, name, description, value, **kwargs):
- super().__init__(name, description, kwargs.get('choices', []))
+ super().__init__(name, description, kwargs.get('choices', []), yielding=kwargs.get('yielding', None))
self.set_value(value, user_input=False)
def validate(self, value, user_input):
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
index 04ca706..66bc3b4 100644
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -16,7 +16,7 @@
# Custom logic for several other packages are in separate files.
import os
-import sys
+import re
import stat
import shlex
import shutil
@@ -214,10 +214,12 @@ class ConfigToolDependency(ExternalDependency):
tools = None
tool_name = None
+ __strip_version = re.compile(r'^[0-9.]*')
def __init__(self, name, environment, language, kwargs):
super().__init__('config-tool', environment, language, kwargs)
self.name = name
+ self.native = kwargs.get('native', False)
self.tools = listify(kwargs.get('tools', self.tools))
req_version = kwargs.get('version', None)
@@ -229,6 +231,15 @@ class ConfigToolDependency(ExternalDependency):
return
self.version = version
+ def _sanitize_version(self, version):
+ """Remove any non-numeric, non-point version suffixes."""
+ m = self.__strip_version.match(version)
+ if m:
+ # Ensure that there isn't a trailing '.', such as an input like
+ # `1.2.3.git-1234`
+ return m.group(0).rstrip('.')
+ return version
+
@classmethod
def factory(cls, name, environment, language, kwargs, tools, tool_name):
"""Constructor for use in dependencies that can be found multiple ways.
@@ -260,8 +271,20 @@ class ConfigToolDependency(ExternalDependency):
if not isinstance(versions, list) and versions is not None:
versions = listify(versions)
+ if self.env.is_cross_build() and not self.native:
+ cross_file = self.env.cross_info.config['binaries']
+ try:
+ tools = [cross_file[self.tool_name]]
+ except KeyError:
+ mlog.warning('No entry for {0} specified in your cross file. '
+ 'Falling back to searching PATH. This may find a '
+ 'native version of {0}!'.format(self.tool_name))
+ tools = self.tools
+ else:
+ tools = self.tools
+
best_match = (None, None)
- for tool in self.tools:
+ for tool in tools:
try:
p, out = Popen_safe([tool, '--version'])[:2]
except (FileNotFoundError, PermissionError):
@@ -269,10 +292,10 @@ class ConfigToolDependency(ExternalDependency):
if p.returncode != 0:
continue
- out = out.strip()
+ out = self._sanitize_version(out.strip())
# Some tools, like pcap-config don't supply a version, but also
- # dont fail with --version, in that case just assume that there is
- # only one verison and return it.
+ # don't fail with --version, in that case just assume that there is
+ # only one version and return it.
if not out:
return (tool, 'none')
if versions:
@@ -543,6 +566,17 @@ class PkgConfigDependency(ExternalDependency):
(self.type_string, self.name))
else:
variable = out.strip()
+
+ # pkg-config doesn't distinguish between empty and non-existent variables
+ # use the variable list to check for variable existence
+ if not variable:
+ ret, out = self._call_pkgbin(['--print-variables', self.name])
+ if not re.search(r'^' + variable_name + r'$', out, re.MULTILINE):
+ if 'default' in kwargs:
+ variable = kwargs['default']
+ else:
+ mlog.warning("pkgconfig variable '%s' not defined for dependency %s." % (variable_name, self.name))
+
mlog.debug('Got pkgconfig variable %s : %s' % (variable_name, variable))
return variable
diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py
index 25316df..c254947 100644
--- a/mesonbuild/dependencies/dev.py
+++ b/mesonbuild/dependencies/dev.py
@@ -146,16 +146,6 @@ class LLVMDependency(ConfigToolDependency):
return
self.static = kwargs.get('static', False)
- # Currently meson doesn't really attempt to handle pre-release versions,
- # so strip the 'svn' off the end, since it will probably cuase problems
- # for users who want the patch version.
- #
- # If LLVM is built from svn then "svn" will be appended to the version
- # string, if it's built from a git mirror then "git-<very short sha>"
- # will be appended instead.
- self.version = self.version.rstrip('svn')
- self.version = self.version.split('git')[0]
-
self.provided_modules = self.get_config_value(['--components'], 'modules')
modules = stringlistify(extract_as_list(kwargs, 'modules'))
self.check_components(modules)
diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py
index 0570e88..af80160 100644
--- a/mesonbuild/dependencies/misc.py
+++ b/mesonbuild/dependencies/misc.py
@@ -63,9 +63,12 @@ from .base import (
# **On Unix**, official packaged versions of boost libraries follow the following schemes:
#
-# Linux / Debian: libboost_<module>.so.1.66.0 -> libboost_<module>.so
-# Linux / Red Hat: libboost_<module>.so.1.66.0 -> libboost_<module>.so
-# Linux / OpenSuse: libboost_<module>.so.1.66.0 -> libboost_<module>.so
+# Linux / Debian: libboost_<module>.so -> libboost_<module>.so.1.66.0
+# Linux / Red Hat: libboost_<module>.so -> libboost_<module>.so.1.66.0
+# Linux / OpenSuse: libboost_<module>.so -> libboost_<module>.so.1.66.0
+# Win / Cygwin: libboost_<module>.dll.a (location = /usr/lib)
+# libboost_<module>.a
+# cygboost_<module>_1_64.dll (location = /usr/bin)
# Mac / homebrew: libboost_<module>.dylib + libboost_<module>-mt.dylib (location = /usr/local/lib)
# Mac / macports: libboost_<module>.dylib + libboost_<module>-mt.dylib (location = /opt/local/lib)
#
@@ -147,6 +150,28 @@ class BoostDependency(ExternalDependency):
self.log_fail()
return
+ if self.check_invalid_modules():
+ return
+
+ mlog.debug('Boost library root dir is', mlog.bold(self.boost_root))
+ mlog.debug('Boost include directory is', mlog.bold(self.incdir))
+
+ self.lib_modules = {}
+ self.detect_version()
+ if self.is_found:
+ self.detect_lib_modules()
+ mlog.debug('Boost library directory is', mlog.bold(self.libdir))
+ for m in self.requested_modules:
+ if 'boost_' + m not in self.lib_modules:
+ mlog.debug('Requested Boost library {!r} not found'.format(m))
+ self.log_fail()
+ self.is_found = False
+ return
+ self.log_success()
+ else:
+ self.log_fail()
+
+ def check_invalid_modules(self):
invalid_modules = [c for c in self.requested_modules if 'boost_' + c not in BOOST_LIBS]
# previous versions of meson allowed include dirs as modules
@@ -163,21 +188,9 @@ class BoostDependency(ExternalDependency):
if invalid_modules:
mlog.log(mlog.red('ERROR:'), 'Invalid Boost modules: ' + ', '.join(invalid_modules))
self.log_fail()
- return
-
- mlog.debug('Boost library root dir is', mlog.bold(self.boost_root))
- mlog.debug('Boost include directory is', mlog.bold(self.incdir))
-
- self.lib_modules = {}
- self.detect_version()
- if self.is_found:
- self.detect_lib_modules()
- mlog.debug('Boost library directory is', mlog.bold(self.libdir))
- self.validate_requested()
- self.log_success()
+ return True
else:
- self.log_fail()
-
+ return False
def log_fail(self):
module_str = ', '.join(self.requested_modules)
@@ -262,12 +275,6 @@ class BoostDependency(ExternalDependency):
raise DependencyException('Boost module argument is not a string.')
return candidates
- def validate_requested(self):
- for m in self.requested_modules:
- if 'boost_' + m not in self.lib_modules:
- msg = 'Requested Boost library {!r} not found'
- raise DependencyException(msg.format(m))
-
def detect_version(self):
try:
version = self.compiler.get_define('BOOST_LIB_VERSION', '#include <boost/version.hpp>', self.env, self.get_compile_args(), [])
@@ -360,15 +367,23 @@ class BoostDependency(ExternalDependency):
fname = os.path.basename(entry)
self.lib_modules[self.modname_from_filename(fname)] = [fname]
+ # - Linux leaves off -mt but libraries are multithreading-aware.
+ # - Cygwin leaves off -mt but libraries are multithreading-aware.
+ # - Mac requires -mt for multithreading, so should not fall back
+ # to non-mt libraries.
+ def abi_tag(self):
+ if mesonlib.for_windows(self.want_cross, self.env):
+ return None
+ if self.is_multithreading and mesonlib.for_darwin(self.want_cross, self.env):
+ return '-mt'
+ else:
+ return ''
+
def detect_lib_modules_nix(self):
all_found = True
for module in self.requested_modules:
- args = None
- libname = 'boost_' + module
- if self.is_multithreading and mesonlib.for_darwin(self.want_cross, self.env):
- # - Linux leaves off -mt but libraries are multithreading-aware.
- # - Mac requires -mt for multithreading, so should not fall back to non-mt libraries.
- libname = libname + '-mt'
+ libname = 'boost_' + module + self.abi_tag()
+
args = self.compiler.find_library(libname, self.env, self.extra_lib_dirs())
if args is None:
mlog.debug('Couldn\'t find library "{}" for boost module "{}"'.format(module, libname))
@@ -417,29 +432,17 @@ class BoostDependency(ExternalDependency):
if modname not in self.lib_modules:
self.lib_modules[modname] = [entry]
- def get_win_link_args(self):
- args = []
- # TODO: should this check self.libdir?
- if self.libdir:
- args.append('-L' + self.libdir)
- for lib in self.requested_modules:
- args += self.lib_modules['boost_' + lib]
- return args
-
def extra_lib_dirs(self):
- dirs = []
- if self.boost_root:
- dirs = [os.path.join(self.boost_root, 'lib')]
- elif self.libdir:
- dirs = [self.libdir]
- return dirs
+ if self.libdir:
+ return [self.libdir]
+ elif self.boost_root:
+ return [os.path.join(self.boost_root, 'lib')]
+ return []
def get_link_args(self):
- if mesonlib.is_windows():
- return self.get_win_link_args()
args = []
for dir in self.extra_lib_dirs():
- args += ['-L' + dir]
+ args += self.compiler.get_linker_search_args(self.libdir)
for lib in self.requested_modules:
args += self.lib_modules['boost_' + lib]
return args
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index e5aa43e..52c670a 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -278,6 +278,7 @@ class Environment:
self.default_objc = ['cc']
self.default_objcpp = ['c++']
self.default_fortran = ['gfortran', 'g95', 'f95', 'f90', 'f77', 'ifort']
+ self.default_rust = ['rustc']
self.default_static_linker = ['ar']
self.vs_static_linker = ['lib']
self.gcc_static_linker = ['gcc-ar']
@@ -594,7 +595,7 @@ class Environment:
return self.scratch_dir
def get_depfixer(self):
- path = os.path.split(__file__)[0]
+ path = os.path.dirname(__file__)
return os.path.join(path, 'depfixer.py')
def detect_objc_compiler(self, want_cross):
@@ -688,16 +689,24 @@ class Environment:
return ValaCompiler(exelist, version)
raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
- def detect_rust_compiler(self):
- exelist = ['rustc']
- try:
- p, out = Popen_safe(exelist + ['--version'])[0:2]
- except OSError:
- raise EnvironmentException('Could not execute Rust compiler "%s"' % ' '.join(exelist))
- version = search_version(out)
- if 'rustc' in out:
- return RustCompiler(exelist, version)
- raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
+ def detect_rust_compiler(self, want_cross):
+ popen_exceptions = {}
+ compilers, ccache, is_cross, exe_wrap = self._get_compilers('rust', 'RUSTC', want_cross)
+ for compiler in compilers:
+ if isinstance(compiler, str):
+ compiler = [compiler]
+ try:
+ p, out = Popen_safe(compiler + ['--version'])[0:2]
+ except OSError as e:
+ popen_exceptions[compiler] = e
+ continue
+
+ version = search_version(out)
+
+ if 'rustc' in out:
+ return RustCompiler(compiler, version, is_cross, exe_wrap)
+
+ self._handle_exceptions(popen_exceptions, compilers)
def detect_d_compiler(self, want_cross):
is_cross = False
@@ -968,7 +977,7 @@ class CrossBuildInfo:
def get_properties(self):
return self.config['properties']
- # Wehn compiling a cross compiler we use the native compiler for everything.
+ # When compiling a cross compiler we use the native compiler for everything.
# But not when cross compiling a cross compiler.
def need_cross_compiler(self):
return 'host_machine' in self.config
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index c759892..31d7616 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -1,4 +1,4 @@
-# Copyright 2012-2017 The Meson development team
+# Copyright 2012-2018 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
@@ -36,8 +36,6 @@ from collections import namedtuple
import importlib
-run_depr_printed = False
-
def stringifyUserArguments(args):
if isinstance(args, list):
return '[%s]' % ', '.join([stringifyUserArguments(x) for x in args])
@@ -509,13 +507,14 @@ class DataHolder(InterpreterObject, ObjectHolder):
return self.held_object.install_dir
class InstallDir(InterpreterObject):
- def __init__(self, src_subdir, inst_subdir, install_dir, install_mode, exclude):
+ def __init__(self, src_subdir, inst_subdir, install_dir, install_mode, exclude, strip_directory):
InterpreterObject.__init__(self)
self.source_subdir = src_subdir
self.installable_subdir = inst_subdir
self.install_dir = install_dir
self.install_mode = install_mode
self.exclude = exclude
+ self.strip_directory = strip_directory
class Man(InterpreterObject):
@@ -1170,6 +1169,7 @@ class MesonMain(InterpreterObject):
'add_postconf_script': self.add_postconf_script_method,
'install_dependency_manifest': self.install_dependency_manifest_method,
'project_version': self.project_version_method,
+ 'project_license': self.project_license_method,
'version': self.version_method,
'project_name': self.project_name_method,
'get_cross_property': self.get_cross_property_method,
@@ -1283,6 +1283,9 @@ class MesonMain(InterpreterObject):
def project_version_method(self, args, kwargs):
return self.build.dep_manifest[self.interpreter.active_projectname]['version']
+ def project_license_method(self, args, kwargs):
+ return self.build.dep_manifest[self.interpreter.active_projectname]['license']
+
def version_method(self, args, kwargs):
return coredata.version
@@ -1379,7 +1382,7 @@ permitted_kwargs = {'add_global_arguments': {'language'},
'build_target': build_target_kwargs,
'configure_file': {'input', 'output', 'configuration', 'command', 'install_dir', 'capture', 'install'},
'custom_target': {'input', 'output', 'command', 'install', 'install_dir', 'build_always', 'capture', 'depends', 'depend_files', 'depfile', 'build_by_default'},
- 'dependency': {'default_options', 'fallback', 'language', 'method', 'modules', 'optional_modules', 'native', 'required', 'static', 'version'},
+ 'dependency': {'default_options', 'fallback', 'language', 'main', 'method', 'modules', 'optional_modules', 'native', 'required', 'static', 'version'},
'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'version'},
'executable': exe_kwargs,
'find_program': {'required', 'native'},
@@ -1388,7 +1391,7 @@ permitted_kwargs = {'add_global_arguments': {'language'},
'install_data': {'install_dir', 'install_mode', 'sources'},
'install_headers': {'install_dir', 'subdir'},
'install_man': {'install_dir'},
- 'install_subdir': {'exclude_files', 'exclude_directories', 'install_dir', 'install_mode'},
+ 'install_subdir': {'exclude_files', 'exclude_directories', 'install_dir', 'install_mode', 'strip_directory'},
'jar': jar_kwargs,
'project': {'version', 'meson_version', 'default_options', 'license', 'subproject_dir'},
'run_target': {'command', 'depends'},
@@ -1585,7 +1588,7 @@ class Interpreter(InterpreterBase):
modname = args[0]
if modname.startswith('unstable-'):
plainname = modname.split('-', 1)[1]
- mlog.warning('Module %s has no backwards or forwards compatibility and might not exist in future releases' % modname, location=node)
+ mlog.warning('Module %s has no backwards or forwards compatibility and might not exist in future releases.' % modname, location=node)
modname = 'unstable_' + plainname
if modname not in self.environment.coredata.modules:
try:
@@ -1765,7 +1768,7 @@ external dependencies (including libraries) must go to "dependencies".''')
def func_get_option(self, nodes, args, kwargs):
if len(args) != 1:
raise InterpreterException('Argument required for get_option.')
- optname = args[0]
+ undecorated_optname = optname = args[0]
if ':' in optname:
raise InterpreterException('''Having a colon in option name is forbidden, projects are not allowed
to directly access options of other subprojects.''')
@@ -1784,7 +1787,11 @@ to directly access options of other subprojects.''')
if not coredata.is_builtin_option(optname) and self.is_subproject():
optname = self.subproject + ':' + optname
try:
- return self.environment.coredata.user_options[optname].value
+ opt = self.environment.coredata.user_options[optname]
+ if opt.yielding and ':' in optname:
+ # If option not present in superproject, keep the original.
+ opt = self.environment.coredata.user_options.get(undecorated_optname, opt)
+ return opt.value
except KeyError:
pass
if optname.endswith('_link_args'):
@@ -1992,9 +1999,9 @@ to directly access options of other subprojects.''')
if need_cross_compiler:
cross_comp = self.environment.detect_d_compiler(True)
elif lang == 'rust':
- comp = self.environment.detect_rust_compiler()
+ comp = self.environment.detect_rust_compiler(False)
if need_cross_compiler:
- cross_comp = comp # FIXME, not correct.
+ cross_comp = self.environment.detect_rust_compiler(True)
elif lang == 'fortran':
comp = self.environment.detect_fortran_compiler(False)
if need_cross_compiler:
@@ -2138,7 +2145,7 @@ to directly access options of other subprojects.''')
return progobj
def func_find_library(self, node, args, kwargs):
- mlog.log(mlog.red('DEPRECATION:'), 'find_library() is removed, use the corresponding method in compiler object instead.')
+ raise InvalidCode('find_library() is removed, use the corresponding method in a compiler object instead.')
def _find_cached_dep(self, name, kwargs):
# Check if we want this as a cross-dep or a native-dep
@@ -2447,15 +2454,8 @@ root and issuing %s.
@permittedKwargs(permitted_kwargs['run_target'])
def func_run_target(self, node, args, kwargs):
- global run_depr_printed
if len(args) > 1:
- if not run_depr_printed:
- mlog.log(mlog.red('DEPRECATION'), 'positional version of run_target is deprecated, use the keyword version instead.')
- run_depr_printed = True
- if 'command' in kwargs:
- raise InterpreterException('Can not have command both in positional and keyword arguments.')
- all_args = args[1:]
- deps = []
+ raise InvalidCode('Run_target takes only one positional argument: the target name.')
elif len(args) == 1:
if 'command' not in kwargs:
raise InterpreterException('Missing "command" keyword argument')
@@ -2679,6 +2679,12 @@ root and issuing %s.
install_dir = kwargs['install_dir']
if not isinstance(install_dir, str):
raise InvalidArguments('Keyword argument install_dir not a string.')
+ if 'strip_directory' in kwargs:
+ if not isinstance(kwargs['strip_directory'], bool):
+ raise InterpreterException('"strip_directory" keyword must be a boolean.')
+ strip_directory = kwargs['strip_directory']
+ else:
+ strip_directory = False
if 'exclude_files' in kwargs:
exclude = extract_as_list(kwargs, 'exclude_files')
for f in exclude:
@@ -2686,7 +2692,7 @@ root and issuing %s.
raise InvalidArguments('Exclude argument not a string.')
elif os.path.isabs(f):
raise InvalidArguments('Exclude argument cannot be absolute.')
- exclude_files = {os.path.join(subdir, f) for f in exclude}
+ exclude_files = set(exclude)
else:
exclude_files = set()
if 'exclude_directories' in kwargs:
@@ -2696,12 +2702,12 @@ root and issuing %s.
raise InvalidArguments('Exclude argument not a string.')
elif os.path.isabs(d):
raise InvalidArguments('Exclude argument cannot be absolute.')
- exclude_directories = {os.path.join(subdir, f) for f in exclude}
+ exclude_directories = set(exclude)
else:
exclude_directories = set()
exclude = (exclude_files, exclude_directories)
install_mode = self._get_kwarg_install_mode(kwargs)
- idir = InstallDir(self.subdir, subdir, install_dir, install_mode, exclude)
+ idir = InstallDir(self.subdir, subdir, install_dir, install_mode, exclude, strip_directory)
self.build.install_dirs.append(idir)
return idir
@@ -2748,7 +2754,7 @@ root and issuing %s.
values = mesonlib.get_filenames_templates_dict([ifile_abs], None)
outputs = mesonlib.substitute_values([output], values)
output = outputs[0]
- if os.path.split(output)[0] != '':
+ if os.path.dirname(output) != '':
raise InterpreterException('Output file name must not contain a subdirectory.')
(ofile_path, ofile_fname) = os.path.split(os.path.join(self.subdir, output))
ofile_abs = os.path.join(self.environment.build_dir, ofile_path, ofile_fname)
@@ -2765,7 +2771,7 @@ root and issuing %s.
var_list = ", ".join(map(repr, sorted(missing_variables)))
mlog.warning(
"The variable(s) %s in the input file %s are not "
- "present in the given configuration data" % (
+ "present in the given configuration data." % (
var_list, inputfile), location=node)
else:
mesonlib.dump_conf_header(ofile_abs, conf.held_object)
@@ -2986,7 +2992,7 @@ different subdirectory.
norm = os.path.relpath(norm, self.environment.source_dir)
assert(not os.path.isabs(norm))
(num_sps, sproj_name) = self.evaluate_subproject_info(norm, self.subproject_dir)
- plain_filename = os.path.split(norm)[-1]
+ plain_filename = os.path.basename(norm)
if num_sps == 0:
if self.subproject == '':
return
@@ -2999,6 +3005,8 @@ different subdirectory.
def source_strings_to_files(self, sources):
results = []
mesonlib.check_direntry_issues(sources)
+ if not isinstance(sources, list):
+ sources = [sources]
for s in sources:
if isinstance(s, (mesonlib.File, GeneratedListHolder,
CustomTargetHolder, CustomTargetIndexHolder)):
@@ -3015,6 +3023,8 @@ different subdirectory.
def add_target(self, name, tobj):
if name == '':
raise InterpreterException('Target name must not be empty.')
+ if name.strip() == '':
+ raise InterpreterException('Target name must not consist only of whitespace.')
if name.startswith('meson-'):
raise InvalidArguments("Target names starting with 'meson-' are reserved "
"for Meson's internal use. Please rename.")
diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py
index 9dc6b0f..6618dc8 100644
--- a/mesonbuild/interpreterbase.py
+++ b/mesonbuild/interpreterbase.py
@@ -74,7 +74,7 @@ class permittedKwargs:
loc = None
for k in kwargs:
if k not in self.permitted:
- mlog.warning('''Passed invalid keyword argument "{}"'''.format(k), location=loc)
+ mlog.warning('''Passed invalid keyword argument "{}".'''.format(k), location=loc)
mlog.warning('This will become a hard error in the future.')
return f(s, node_or_state, args, kwargs)
return wrapped
@@ -265,6 +265,12 @@ class InterpreterBase:
if not isinstance(node.elseblock, mparser.EmptyNode):
self.evaluate_codeblock(node.elseblock)
+ def validate_comparison_types(self, val1, val2):
+ if type(val1) != type(val2):
+ mlog.warning('''Trying to compare values of different types ({}, {}).
+The result of this is undefined and will become a hard error
+in a future Meson release.'''.format(type(val1).__name__, type(val2).__name__))
+
def evaluate_comparison(self, node):
val1 = self.evaluate_statement(node.left)
if is_disabler(val1):
@@ -272,15 +278,11 @@ class InterpreterBase:
val2 = self.evaluate_statement(node.right)
if is_disabler(val2):
return val2
+ self.validate_comparison_types(val1, val2)
if node.ctype == '==':
return val1 == val2
elif node.ctype == '!=':
return val1 != val2
- elif not isinstance(val1, type(val2)):
- raise InterpreterException(
- 'Values of different types ({}, {}) cannot be compared using {}.'.format(type(val1).__name__,
- type(val2).__name__,
- node.ctype))
elif not self.is_elementary_type(val1):
raise InterpreterException('{} can only be compared for equality.'.format(node.left.value))
elif not self.is_elementary_type(val2):
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index 4871bf7..65b689f 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -282,6 +282,9 @@ def is_osx():
def is_linux():
return platform.system().lower() == 'linux'
+def is_android():
+ return platform.system().lower() == 'android'
+
def is_haiku():
return platform.system().lower() == 'haiku'
@@ -350,6 +353,18 @@ def for_darwin(is_cross, env):
return env.cross_info.config['host_machine']['system'] == 'darwin'
return False
+def for_android(is_cross, env):
+ """
+ Host machine is Android?
+
+ Note: 'host' is the machine on which compiled binaries will run
+ """
+ if not is_cross:
+ return is_android()
+ elif env.cross_info.has_host():
+ return env.cross_info.config['host_machine']['system'] == 'android'
+ return False
+
def for_haiku(is_cross, env):
"""
Host machine is Haiku?
@@ -576,7 +591,8 @@ def do_conf_file(src, dst, confdata):
return missing_variables
def dump_conf_header(ofilename, cdata):
- with open(ofilename, 'w', encoding='utf-8') as ofile:
+ ofilename_tmp = ofilename + '~'
+ with open(ofilename_tmp, 'w', encoding='utf-8') as ofile:
ofile.write('''/*
* Autogenerated by the Meson build system.
* Do not edit, your changes will be lost.
@@ -598,6 +614,7 @@ def dump_conf_header(ofilename, cdata):
ofile.write('#define %s %s\n\n' % (k, v))
else:
raise MesonException('Unknown data type in configuration file entry: ' + k)
+ replace_if_different(ofilename, ofilename_tmp)
def replace_if_different(dst, dst_tmp):
# If contents are identical, don't touch the file to prevent
@@ -855,7 +872,7 @@ def get_filenames_templates_dict(inputs, outputs):
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['@PLAINNAME@'] = plain = os.path.basename(inputs[0])
values['@BASENAME@'] = os.path.splitext(plain)[0]
if outputs:
# Gather values derived from the outputs, similar to above.
@@ -863,7 +880,7 @@ def get_filenames_templates_dict(inputs, 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]
+ values['@OUTDIR@'] = os.path.dirname(outputs[0])
# Many external programs fail on empty arguments.
if values['@OUTDIR@'] == '':
values['@OUTDIR@'] = '.'
@@ -893,7 +910,7 @@ def detect_subprojects(spdir_name, current_dir='', result=None):
if not os.path.exists(spdir):
return result
for trial in glob(os.path.join(spdir, '*')):
- basename = os.path.split(trial)[1]
+ basename = os.path.basename(trial)
if trial == 'packagecache':
continue
append_this = True
diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py
index e48122f..619aa39 100644
--- a/mesonbuild/mesonmain.py
+++ b/mesonbuild/mesonmain.py
@@ -296,8 +296,6 @@ def run(original_args, mainfile=None):
# FALLTHROUGH like it's 1972.
elif cmd_name == 'introspect':
return mintro.run(remaining_args)
- elif cmd_name == 'test':
- return mtest.run(remaining_args)
elif cmd_name == 'rewrite':
return rewriter.run(remaining_args)
elif cmd_name == 'configure':
@@ -368,10 +366,11 @@ def run(original_args, mainfile=None):
app.generate()
except Exception as e:
if isinstance(e, MesonException):
+ mlog.log()
if hasattr(e, 'file') and hasattr(e, 'lineno') and hasattr(e, 'colno'):
- mlog.log(mlog.red('\nMeson encountered an error in file %s, line %d, column %d:' % (e.file, e.lineno, e.colno)))
+ mlog.log('%s:%d:%d:' % (e.file, e.lineno, e.colno), mlog.red('ERROR: '), end='')
else:
- mlog.log(mlog.red('\nMeson encountered an error:'))
+ mlog.log(mlog.red('ERROR: '), end='')
# Error message
mlog.log(e)
# Path to log file
diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py
index b23869f..8cf66af 100644
--- a/mesonbuild/mintro.py
+++ b/mesonbuild/mintro.py
@@ -49,14 +49,14 @@ parser.add_argument('builddir', nargs='?', help='The build directory')
def determine_installed_path(target, installdata):
install_target = None
for i in installdata.targets:
- if os.path.split(i[0])[1] == target.get_filename(): # FIXME, might clash due to subprojects.
+ if os.path.basename(i[0]) == target.get_filename(): # FIXME, might clash due to subprojects.
install_target = i
break
if install_target is None:
raise RuntimeError('Something weird happened. File a bug.')
fname = i[0]
outdir = i[1]
- outname = os.path.join(installdata.prefix, outdir, os.path.split(fname)[-1])
+ outname = os.path.join(installdata.prefix, outdir, os.path.basename(fname))
# Normalize the path by using os.path.sep consistently, etc.
# Does not change the effective path.
return str(pathlib.PurePath(outname))
@@ -139,6 +139,8 @@ def add_keys(optlist, options):
elif isinstance(opt, coredata.UserComboOption):
optdict['choices'] = opt.choices
typestr = 'combo'
+ elif isinstance(opt, coredata.UserIntegerOption):
+ typestr = 'integer'
elif isinstance(opt, coredata.UserArrayOption):
typestr = 'array'
else:
diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py
index aa2ac20..273552d 100644
--- a/mesonbuild/mlog.py
+++ b/mesonbuild/mlog.py
@@ -105,12 +105,15 @@ def log(*args, **kwargs):
def warning(*args, **kwargs):
from . import environment
+ args = (yellow('WARNING:'),) + args
+
if kwargs.get('location'):
location = kwargs['location']
del kwargs['location']
- args += ('in file {}, line {}.'.format(os.path.join(location.subdir, environment.build_filename), location.lineno),)
+ location = '{}:{}:'.format(os.path.join(location.subdir, environment.build_filename), location.lineno)
+ args = (location,) + args
- log(yellow('WARNING:'), *args, **kwargs)
+ log(*args, **kwargs)
# Format a list for logging purposes as a string. It separates
# all but the last item with commas, and the last with 'and'.
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index db85420..218e3b3 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -362,7 +362,7 @@ class GnomeModule(ExtensionModule):
ldflags.update([lib])
if isinstance(dep, PkgConfigDependency):
- girdir = dep.get_pkgconfig_variable("girdir", {})
+ girdir = dep.get_pkgconfig_variable("girdir", {'default': ''})
if girdir:
gi_includes.update([girdir])
elif isinstance(dep, (build.StaticLibrary, build.SharedLibrary)):
@@ -553,7 +553,7 @@ class GnomeModule(ExtensionModule):
if subdir not in typelib_includes:
typelib_includes.append(subdir)
elif isinstance(dep, PkgConfigDependency):
- girdir = dep.get_pkgconfig_variable("girdir", {})
+ girdir = dep.get_pkgconfig_variable("girdir", {'default': ''})
if girdir and girdir not in typelib_includes:
typelib_includes.append(girdir)
# ldflags will be misinterpreted by gir scanner (showing
diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py
index 54c2126..5573a2e 100644
--- a/mesonbuild/modules/pkgconfig.py
+++ b/mesonbuild/modules/pkgconfig.py
@@ -66,13 +66,15 @@ class DependenciesHelper:
elif hasattr(obj, 'generated_pc'):
processed_reqs.append(obj.generated_pc)
elif isinstance(obj, dependencies.PkgConfigDependency):
- processed_reqs.append(obj.name)
+ if obj.found():
+ processed_reqs.append(obj.name)
elif isinstance(obj, dependencies.ThreadDependency):
processed_libs += obj.get_compiler().thread_link_flags(obj.env)
processed_cflags += obj.get_compiler().thread_flags(obj.env)
elif isinstance(obj, dependencies.Dependency):
- processed_libs += obj.get_link_args()
- processed_cflags += obj.get_compile_args()
+ if obj.found():
+ processed_libs += obj.get_link_args()
+ processed_cflags += obj.get_compile_args()
elif isinstance(obj, (build.SharedLibrary, build.StaticLibrary)):
processed_libs.append(obj)
if public:
diff --git a/mesonbuild/modules/qt.py b/mesonbuild/modules/qt.py
index 54e2c73..f5ce1ed 100644
--- a/mesonbuild/modules/qt.py
+++ b/mesonbuild/modules/qt.py
@@ -73,7 +73,7 @@ class QtBaseModule:
def parse_qrc(self, state, fname):
abspath = os.path.join(state.environment.source_dir, state.subdir, fname)
- relative_part = os.path.split(fname)[0]
+ relative_part = os.path.dirname(fname)
try:
tree = ET.parse(abspath)
root = tree.getroot()
@@ -83,7 +83,7 @@ class QtBaseModule:
mlog.warning("malformed rcc file: ", os.path.join(state.subdir, fname))
break
else:
- result.append(os.path.join(state.subdir, relative_part, child.text))
+ result.append(os.path.join(relative_part, child.text))
return result
except Exception:
return []
@@ -116,7 +116,7 @@ class QtBaseModule:
sources.append(res_target)
else:
for rcc_file in rcc_files:
- basename = os.path.split(rcc_file)[1]
+ basename = os.path.basename(rcc_file)
name = 'qt' + str(self.qt_version) + '-' + basename.replace('.', '_')
rcc_kwargs = {'input': rcc_file,
'output': name + '.cpp',
diff --git a/mesonbuild/modules/unstable_icestorm.py b/mesonbuild/modules/unstable_icestorm.py
new file mode 100644
index 0000000..0b7b339
--- /dev/null
+++ b/mesonbuild/modules/unstable_icestorm.py
@@ -0,0 +1,85 @@
+# Copyright 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.
+# 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.
+
+from .. import mesonlib, compilers, mlog
+
+from . import ExtensionModule
+
+class IceStormModule(ExtensionModule):
+
+ def __init__(self):
+ super().__init__()
+ self.snippets.add('project')
+ self.yosys_bin = None
+
+ def detect_binaries(self, interpreter):
+ self.yosys_bin = interpreter.func_find_program(None, ['yosys'], {})
+ self.arachne_bin = interpreter.func_find_program(None, ['arachne-pnr'], {})
+ self.icepack_bin = interpreter.func_find_program(None, ['icepack'], {})
+ self.iceprog_bin = interpreter.func_find_program(None, ['iceprog'], {})
+ self.icetime_bin = interpreter.func_find_program(None, ['icetime'], {})
+
+ def project(self, interpreter, state, args, kwargs):
+ if not self.yosys_bin:
+ self.detect_binaries(interpreter)
+ result = []
+ if not len(args):
+ raise mesonlib.MesonException('Project requires at least one argument, which is the project name.')
+ proj_name = args[0]
+ arg_sources = args[1:]
+ if not isinstance(proj_name, str):
+ raise mesonlib.MesonException('Argument must be a string.')
+ kwarg_sources = kwargs.get('sources', [])
+ if not isinstance(kwarg_sources, list):
+ kwarg_sources = [kwarg_sources]
+ all_sources = interpreter.source_strings_to_files(interpreter.flatten(arg_sources + kwarg_sources))
+ if 'constraint_file' not in kwargs:
+ raise mesonlib.MesonException('Constraint file not specified.')
+
+ constraint_file = interpreter.source_strings_to_files(kwargs['constraint_file'])
+ if len(constraint_file) != 1:
+ raise mesonlib.MesonException('Constraint file must contain one and only one entry.')
+ blif_name = proj_name + '_blif'
+ blif_fname = proj_name + '.blif'
+ asc_name = proj_name + '_asc'
+ asc_fname = proj_name + '.asc'
+ bin_name = proj_name + '_bin'
+ bin_fname = proj_name + '.bin'
+ time_name = proj_name + '-time'
+ upload_name = proj_name + '-upload'
+
+ blif_target = interpreter.func_custom_target(None, [blif_name], {
+ 'input': all_sources,
+ 'output': blif_fname,
+ 'command': [self.yosys_bin, '-q', '-p', 'synth_ice40 -blif @OUTPUT@', '@INPUT@']})
+
+ asc_target = interpreter.func_custom_target(None, [asc_name], {
+ 'input': blif_target,
+ 'output': asc_fname,
+ 'command': [self.arachne_bin, '-q', '-d', '1k', '-p', constraint_file, '@INPUT@', '-o', '@OUTPUT@']})
+
+ bin_target = interpreter.func_custom_target(None, [bin_name], {
+ 'input': asc_target,
+ 'output': bin_fname,
+ 'command': [self.icepack_bin, '@INPUT@', '@OUTPUT@'],
+ 'build_by_default' : True})
+
+ up_target = interpreter.func_run_target(None, [upload_name], {
+ 'command': [self.iceprog_bin, bin_target]})
+
+ time_target = interpreter.func_run_target(None, [time_name], {
+ 'command' : [self.icetime_bin, bin_target]})
+
+def initialize():
+ return IceStormModule()
diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py
index eb03393..94d56e5 100644
--- a/mesonbuild/mparser.py
+++ b/mesonbuild/mparser.py
@@ -71,6 +71,7 @@ class Lexer:
# Need to be sorted longest to shortest.
('ignore', re.compile(r'[ \t]')),
('id', re.compile('[_a-zA-Z][_0-9a-zA-Z]*')),
+ ('hexnumber', re.compile('0[xX][0-9a-fA-F]+')),
('number', re.compile(r'\d+')),
('eol_cont', re.compile(r'\\\n')),
('eol', re.compile(r'\n')),
@@ -152,6 +153,9 @@ class Lexer:
line_start = mo.end() - len(lines[-1])
elif tid == 'number':
value = int(match_text)
+ elif tid == 'hexnumber':
+ tid = 'number'
+ value = int(match_text, base=16)
elif tid == 'eol' or tid == 'eol_cont':
lineno += 1
line_start = loc
@@ -368,7 +372,7 @@ class ArgumentNode:
def set_kwarg(self, name, value):
if name in self.kwargs:
- mlog.warning('Keyword argument "{}" defined multiple times'.format(name), location=self)
+ mlog.warning('Keyword argument "{}" defined multiple times.'.format(name), location=self)
mlog.warning('This will be an error in future Meson releases.')
self.kwargs[name] = value
diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py
index b39f5af..95e532c 100644
--- a/mesonbuild/mtest.py
+++ b/mesonbuild/mtest.py
@@ -241,8 +241,8 @@ class TestHarness:
stdout = subprocess.PIPE
stderr = subprocess.PIPE if self.options and self.options.split else subprocess.STDOUT
- if not is_windows():
- setsid = os.setsid
+ if not is_windows():
+ setsid = os.setsid
p = subprocess.Popen(cmd,
stdout=stdout,
@@ -251,6 +251,7 @@ class TestHarness:
cwd=test.workdir,
preexec_fn=setsid)
timed_out = False
+ kill_test = False
if test.timeout is None:
timeout = None
else:
@@ -261,6 +262,11 @@ class TestHarness:
if self.options.verbose:
print("%s time out (After %d seconds)" % (test.name, timeout))
timed_out = True
+ except KeyboardInterrupt:
+ mlog.warning("CTRL-C detected while running %s" % (test.name))
+ kill_test = True
+
+ if kill_test or timed_out:
# Python does not provide multiplatform support for
# killing a process and all its children so we need
# to roll our own.
@@ -438,7 +444,7 @@ TIMEOUT: %4d
logfile_base = os.path.join(self.options.wd, 'meson-logs', self.options.logbase)
if self.options.wrapper:
- namebase = os.path.split(self.get_wrapper()[0])[1]
+ namebase = os.path.basename(self.get_wrapper()[0])
elif self.options.setup:
namebase = self.options.setup
diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py
index df945ab..d4ea06a 100644
--- a/mesonbuild/optinterpreter.py
+++ b/mesonbuild/optinterpreter.py
@@ -64,16 +64,21 @@ def permitted_kwargs(permitted):
optname_regex = re.compile('[^a-zA-Z0-9_-]')
-@permitted_kwargs({'value'})
+@permitted_kwargs({'value', 'yield'})
def StringParser(name, description, kwargs):
- return coredata.UserStringOption(name, description,
- kwargs.get('value', ''), kwargs.get('choices', []))
+ return coredata.UserStringOption(name,
+ description,
+ kwargs.get('value', ''),
+ kwargs.get('choices', []),
+ kwargs.get('yield', coredata.default_yielding))
-@permitted_kwargs({'value'})
+@permitted_kwargs({'value', 'yield'})
def BooleanParser(name, description, kwargs):
- return coredata.UserBooleanOption(name, description, kwargs.get('value', True))
+ return coredata.UserBooleanOption(name, description,
+ kwargs.get('value', True),
+ kwargs.get('yield', coredata.default_yielding))
-@permitted_kwargs({'value', 'choices'})
+@permitted_kwargs({'value', 'yiel', 'choices'})
def ComboParser(name, description, kwargs):
if 'choices' not in kwargs:
raise OptionException('Combo option missing "choices" keyword.')
@@ -83,9 +88,25 @@ def ComboParser(name, description, kwargs):
for i in choices:
if not isinstance(i, str):
raise OptionException('Combo choice elements must be strings.')
- return coredata.UserComboOption(name, description, choices, kwargs.get('value', choices[0]))
-
-@permitted_kwargs({'value', 'choices'})
+ return coredata.UserComboOption(name,
+ description,
+ choices,
+ kwargs.get('value', choices[0]),
+ kwargs.get('yield', coredata.default_yielding),)
+
+
+@permitted_kwargs({'value', 'min', 'max', 'yield'})
+def IntegerParser(name, description, kwargs):
+ if 'value' not in kwargs:
+ raise OptionException('Integer option must contain value argument.')
+ return coredata.UserIntegerOption(name,
+ description,
+ kwargs.get('min', None),
+ kwargs.get('max', None),
+ kwargs['value'],
+ kwargs.get('yield', coredata.default_yielding))
+
+@permitted_kwargs({'value', 'yield', 'choices'})
def string_array_parser(name, description, kwargs):
if 'choices' in kwargs:
choices = kwargs['choices']
@@ -100,11 +121,16 @@ def string_array_parser(name, description, kwargs):
value = kwargs.get('value', [])
if not isinstance(value, list):
raise OptionException('Array choices must be passed as an array.')
- return coredata.UserArrayOption(name, description, value, choices=choices)
+ return coredata.UserArrayOption(name,
+ description,
+ value,
+ choices=choices,
+ yielding=kwargs.get('yield', coredata.default_yielding))
option_types = {'string': StringParser,
'boolean': BooleanParser,
'combo': ComboParser,
+ 'integer': IntegerParser,
'array': string_array_parser,
}
diff --git a/mesonbuild/scripts/coverage.py b/mesonbuild/scripts/coverage.py
index 25451d4..47f4cda 100644
--- a/mesonbuild/scripts/coverage.py
+++ b/mesonbuild/scripts/coverage.py
@@ -14,12 +14,7 @@
from mesonbuild import environment
-import sys, os, subprocess
-
-def remove_dir_from_trace(lcov_command, covfile, dirname):
- tmpfile = covfile + '.tmp'
- subprocess.check_call([lcov_command, '--remove', covfile, dirname, '-o', tmpfile])
- os.replace(tmpfile, covfile)
+import sys, os, subprocess, pathlib
def coverage(source_root, build_root, log_dir):
(gcovr_exe, lcov_exe, genhtml_exe) = environment.find_coverage_tools()
@@ -38,6 +33,7 @@ def coverage(source_root, build_root, log_dir):
covinfo = os.path.join(log_dir, 'coverage.info')
initial_tracefile = covinfo + '.initial'
run_tracefile = covinfo + '.run'
+ raw_tracefile = covinfo + '.raw'
subprocess.check_call([lcov_exe,
'--directory', build_root,
'--capture',
@@ -55,11 +51,12 @@ def coverage(source_root, build_root, log_dir):
subprocess.check_call([lcov_exe,
'-a', initial_tracefile,
'-a', run_tracefile,
- '-o', covinfo])
- remove_dir_from_trace(lcov_exe, covinfo, '/usr/include/*')
- remove_dir_from_trace(lcov_exe, covinfo, '/usr/local/include/*')
- remove_dir_from_trace(lcov_exe, covinfo, '/usr/src/*')
- remove_dir_from_trace(lcov_exe, covinfo, '/usr/lib/llvm-*/include/*')
+ '-o', raw_tracefile])
+ # Remove all directories outside the source_root from the covinfo
+ subprocess.check_call([lcov_exe,
+ '--extract', raw_tracefile,
+ os.path.join(source_root, '*'),
+ '--output-file', covinfo])
subprocess.check_call([genhtml_exe,
'--prefix', build_root,
'--output-directory', htmloutdir,
@@ -68,6 +65,15 @@ def coverage(source_root, build_root, log_dir):
'--show-details',
'--branch-coverage',
covinfo])
+ if gcovr_exe:
+ print('')
+ print('XML coverage report can be found at',
+ pathlib.Path(log_dir, 'coverage.xml').as_uri())
+ print('Text coverage report can be found at',
+ pathlib.Path(log_dir, 'coverage.txt').as_uri())
+ if lcov_exe and genhtml_exe:
+ print('Html coverage report can be found at',
+ pathlib.Path(htmloutdir, 'index.html').as_uri())
return 0
def run(args):
diff --git a/mesonbuild/scripts/gettext.py b/mesonbuild/scripts/gettext.py
index 30ac54c..f308c5a 100644
--- a/mesonbuild/scripts/gettext.py
+++ b/mesonbuild/scripts/gettext.py
@@ -81,7 +81,7 @@ def do_install(src_sub, bld_sub, dest, pkgname, langs):
srcfile = os.path.join(bld_sub, l + '.gmo')
outfile = os.path.join(dest, l, 'LC_MESSAGES',
pkgname + '.mo')
- os.makedirs(os.path.split(outfile)[0], exist_ok=True)
+ os.makedirs(os.path.dirname(outfile), exist_ok=True)
shutil.copyfile(srcfile, outfile)
shutil.copystat(srcfile, outfile)
print('Installing %s to %s' % (srcfile, outfile))
diff --git a/mesonbuild/scripts/meson_install.py b/mesonbuild/scripts/meson_install.py
index fe1de1f..cbc782d 100644
--- a/mesonbuild/scripts/meson_install.py
+++ b/mesonbuild/scripts/meson_install.py
@@ -128,20 +128,42 @@ def do_copyfile(from_file, to_file):
selinux_updates.append(to_file)
append_to_log(to_file)
-def do_copydir(data, src_prefix, src_dir, dst_dir, exclude):
+def do_copydir(data, src_dir, dst_dir, exclude):
'''
- Copies the directory @src_prefix (full path) into @dst_dir
-
- @src_dir is simply the parent directory of @src_prefix
+ Copies the contents of directory @src_dir into @dst_dir.
+
+ For directory
+ /foo/
+ bar/
+ excluded
+ foobar
+ file
+ do_copydir(..., '/foo', '/dst/dir', {'bar/excluded'}) creates
+ /dst/
+ dir/
+ bar/
+ foobar
+ file
+
+ Args:
+ src_dir: str, absolute path to the source directory
+ dst_dir: str, absolute path to the destination directory
+ exclude: (set(str), set(str)), tuple of (exclude_files, exclude_dirs),
+ each element of the set is a path relative to src_dir.
'''
+ if not os.path.isabs(src_dir):
+ raise ValueError('src_dir must be absolute, got %s' % src_dir)
+ if not os.path.isabs(dst_dir):
+ raise ValueError('dst_dir must be absolute, got %s' % dst_dir)
if exclude is not None:
exclude_files, exclude_dirs = exclude
else:
exclude_files = exclude_dirs = set()
- for root, dirs, files in os.walk(src_prefix):
+ for root, dirs, files in os.walk(src_dir):
+ assert os.path.isabs(root)
for d in dirs[:]:
- abs_src = os.path.join(src_dir, root, d)
- filepart = abs_src[len(src_dir) + 1:]
+ abs_src = os.path.join(root, d)
+ filepart = os.path.relpath(abs_src, start=src_dir)
abs_dst = os.path.join(dst_dir, filepart)
# Remove these so they aren't visited by os.walk at all.
if filepart in exclude_dirs:
@@ -155,8 +177,8 @@ def do_copydir(data, src_prefix, src_dir, dst_dir, exclude):
data.dirmaker.makedirs(abs_dst)
shutil.copystat(abs_src, abs_dst)
for f in files:
- abs_src = os.path.join(src_dir, root, f)
- filepart = abs_src[len(src_dir) + 1:]
+ abs_src = os.path.join(root, f)
+ filepart = os.path.relpath(abs_src, start=src_dir)
if filepart in exclude_files:
continue
abs_dst = os.path.join(dst_dir, filepart)
@@ -164,10 +186,10 @@ def do_copydir(data, src_prefix, src_dir, dst_dir, exclude):
print('Tried to copy file %s but a directory of that name already exists.' % abs_dst)
if os.path.exists(abs_dst):
os.unlink(abs_dst)
- parent_dir = os.path.split(abs_dst)[0]
+ parent_dir = os.path.dirname(abs_dst)
if not os.path.isdir(parent_dir):
os.mkdir(parent_dir)
- shutil.copystat(os.path.split(abs_src)[0], parent_dir)
+ shutil.copystat(os.path.dirname(abs_src), parent_dir)
shutil.copy2(abs_src, abs_dst, follow_symlinks=False)
append_to_log(abs_dst)
@@ -195,23 +217,19 @@ def do_install(datafilename):
run_install_script(d)
def install_subdirs(d):
- for (src_dir, inst_dir, dst_dir, mode, exclude) in d.install_subdirs:
- if src_dir.endswith('/') or src_dir.endswith('\\'):
- src_dir = src_dir[:-1]
- src_prefix = os.path.join(src_dir, inst_dir)
- print('Installing subdir %s to %s' % (src_prefix, dst_dir))
- dst_dir = get_destdir_path(d, dst_dir)
- d.dirmaker.makedirs(dst_dir, exist_ok=True)
- do_copydir(d, src_prefix, src_dir, dst_dir, exclude)
- dst_prefix = os.path.join(dst_dir, inst_dir)
- set_mode(dst_prefix, mode)
+ for (src_dir, dst_dir, mode, exclude) in d.install_subdirs:
+ full_dst_dir = get_destdir_path(d, dst_dir)
+ print('Installing subdir %s to %s' % (src_dir, full_dst_dir))
+ d.dirmaker.makedirs(full_dst_dir, exist_ok=True)
+ do_copydir(d, src_dir, full_dst_dir, exclude)
+ set_mode(full_dst_dir, 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]
+ outdir = os.path.dirname(outfilename)
d.dirmaker.makedirs(outdir, exist_ok=True)
print('Installing %s to %s' % (fullfilename, outdir))
do_copyfile(fullfilename, outfilename)
@@ -221,7 +239,7 @@ def install_man(d):
for m in d.man:
full_source_filename = m[0]
outfilename = get_destdir_path(d, m[1])
- outdir = os.path.split(outfilename)[0]
+ outdir = os.path.dirname(outfilename)
d.dirmaker.makedirs(outdir, exist_ok=True)
print('Installing %s to %s' % (full_source_filename, outdir))
if outfilename.endswith('.gz') and not full_source_filename.endswith('.gz'):
@@ -238,7 +256,7 @@ def install_man(d):
def install_headers(d):
for t in d.headers:
fullfilename = t[0]
- fname = os.path.split(fullfilename)[1]
+ fname = os.path.basename(fullfilename)
outdir = get_destdir_path(d, t[1])
outfilename = os.path.join(outdir, fname)
print('Installing %s to %s' % (fname, outdir))
@@ -304,7 +322,7 @@ def install_targets(d):
for t in d.targets:
fname = check_for_stampfile(t[0])
outdir = get_destdir_path(d, t[1])
- outname = os.path.join(outdir, os.path.split(fname)[-1])
+ outname = os.path.join(outdir, os.path.basename(fname))
aliases = t[2]
should_strip = t[3]
install_rpath = t[4]
@@ -316,7 +334,7 @@ def install_targets(d):
do_copyfile(fname, outname)
if should_strip and d.strip_bin is not None:
if fname.endswith('.jar'):
- print('Not stripping jar target:', os.path.split(fname)[1])
+ print('Not stripping jar target:', os.path.basename(fname))
continue
print('Stripping target {!r}'.format(fname))
ps, stdo, stde = Popen_safe(d.strip_bin + [outname])
@@ -332,7 +350,7 @@ def install_targets(d):
do_copyfile(pdb_filename, pdb_outname)
elif os.path.isdir(fname):
fname = os.path.join(d.build_dir, fname.rstrip('/'))
- do_copydir(d, fname, os.path.dirname(fname), outdir, None)
+ do_copydir(d, fname, os.path.join(outdir, os.path.basename(fname)), None)
else:
raise RuntimeError('Unknown file type for {!r}'.format(fname))
printed_symlink_error = False
@@ -366,7 +384,7 @@ def run(args):
print('Installer script for Meson. Do not run on your own, mmm\'kay?')
print('meson_install.py [install info file]')
datafilename = args[0]
- private_dir = os.path.split(datafilename)[0]
+ private_dir = os.path.dirname(datafilename)
log_dir = os.path.join(private_dir, '../meson-logs')
with open(os.path.join(log_dir, 'install-log.txt'), 'w') as lf:
install_log_file = lf
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
index 26a3489..bd440a1 100644
--- a/mesonbuild/wrap/wrap.py
+++ b/mesonbuild/wrap/wrap.py
@@ -164,17 +164,20 @@ class Resolver:
if not ret:
return False
# Submodule has not been added, add it
- if out.startswith(b'-'):
+ if out.startswith(b'+'):
+ mlog.warning('submodule {} might be out of date'.format(dirname))
+ return True
+ elif out.startswith(b'U'):
+ raise RuntimeError('submodule {} has merge conflicts'.format(dirname))
+ elif out.startswith(b'-'):
if subprocess.call(['git', '-C', self.subdir_root, 'submodule', 'update', '--init', dirname]) != 0:
return False
# Submodule was added already, but it wasn't populated. Do a checkout.
elif out.startswith(b' '):
if subprocess.call(['git', 'checkout', '.'], cwd=dirname):
return True
- else:
- m = 'Unknown git submodule output: {!r}'
- raise AssertionError(m.format(out))
- return True
+ m = 'Unknown git submodule output: {!r}'
+ raise RuntimeError(m.format(out))
def get_git(self, p):
checkoutdir = os.path.join(self.subdir_root, p.get('directory'))
diff --git a/mesonbuild/wrap/wraptool.py b/mesonbuild/wrap/wraptool.py
index 0bdc417..09a0289 100644
--- a/mesonbuild/wrap/wraptool.py
+++ b/mesonbuild/wrap/wraptool.py
@@ -150,7 +150,7 @@ def do_promotion(from_path, spdir_name):
assert(from_path.endswith('.wrap'))
shutil.copy(from_path, spdir_name)
elif os.path.isdir(from_path):
- sproj_name = os.path.split(from_path)[1]
+ sproj_name = os.path.basename(from_path)
outputdir = os.path.join(spdir_name, sproj_name)
if os.path.exists(outputdir):
sys.exit('Output dir %s already exists. Will not overwrite.' % outputdir)
@@ -178,7 +178,7 @@ def promote(argument):
def status():
print('Subproject status')
for w in glob('subprojects/*.wrap'):
- name = os.path.split(w)[1][:-5]
+ name = os.path.basename(w)[:-5]
try:
(latest_branch, latest_revision) = get_latest_version(name)
except Exception: