aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/macros.meson12
-rw-r--r--docs/markdown/Feature-autodetection.md23
-rw-r--r--docs/markdown/Unit-tests.md2
-rw-r--r--mesonbuild/ast/interpreter.py111
-rw-r--r--mesonbuild/ast/introspection.py7
-rw-r--r--mesonbuild/ast/printer.py2
-rw-r--r--mesonbuild/backend/xcodebackend.py4
-rw-r--r--mesonbuild/compilers/__init__.py4
-rw-r--r--mesonbuild/compilers/c.py12
-rw-r--r--mesonbuild/compilers/compilers.py17
-rw-r--r--mesonbuild/compilers/cpp.py13
-rw-r--r--mesonbuild/compilers/fortran.py4
-rw-r--r--mesonbuild/dependencies/boost.py4
-rw-r--r--mesonbuild/envconfig.py122
-rw-r--r--mesonbuild/environment.py70
-rw-r--r--mesonbuild/interpreter.py18
-rw-r--r--mesonbuild/interpreterbase.py18
-rw-r--r--mesonbuild/mesonlib.py94
-rw-r--r--mesonbuild/mesonmain.py2
-rw-r--r--mesonbuild/minit.py2
-rw-r--r--mesonbuild/mintro.py4
-rw-r--r--mesonbuild/mlog.py124
-rw-r--r--mesonbuild/modules/cmake.py14
-rw-r--r--mesonbuild/modules/hotdoc.py6
-rw-r--r--mesonbuild/modules/qt.py12
-rw-r--r--mesonbuild/modules/rpm.py6
-rw-r--r--mesonbuild/modules/unstable_icestorm.py2
-rw-r--r--mesonbuild/modules/unstable_simd.py2
-rw-r--r--mesonbuild/rewriter.py13
-rw-r--r--mesonbuild/scripts/coverage.py29
-rw-r--r--mesonbuild/scripts/dist.py2
-rw-r--r--mesonbuild/scripts/gtkdochelper.py14
-rw-r--r--mesonbuild/scripts/meson_exe.py2
-rw-r--r--mesonbuild/scripts/symbolextractor.py4
-rwxr-xr-xrun_unittests.py202
-rw-r--r--test cases/unit/55 introspection/meson.build16
36 files changed, 597 insertions, 396 deletions
diff --git a/data/macros.meson b/data/macros.meson
index 05d21e5..c5b90de 100644
--- a/data/macros.meson
+++ b/data/macros.meson
@@ -2,6 +2,12 @@
%__meson_wrap_mode nodownload
%__meson_auto_features enabled
+%_smp_mesonflags %([ -z "$MESON_BUILD_NCPUS" ] \\\
+ && MESON_BUILD_NCPUS="`/usr/bin/getconf _NPROCESSORS_ONLN`"; \\\
+ ncpus_max=%{?_smp_ncpus_max}; \\\
+ if [ -n "$ncpus_max" ] && [ "$ncpus_max" -gt 0 ] && [ "$MESON_BUILD_NCPUS" -gt "$ncpus_max" ]; then MESON_BUILD_NCPUS="$ncpus_max"; fi; \\\
+ if [ "$MESON_BUILD_NCPUS" -gt 1 ]; then echo "--num-processes $MESON_BUILD_NCPUS"; fi)
+
%meson \
%set_build_flags \
%{shrink:%{__meson} \
@@ -31,4 +37,8 @@
%ninja_install -C %{_vpath_builddir}
%meson_test \
- %ninja_test -C %{_vpath_builddir}
+ %{shrink: %{__meson} test \
+ -C %{_vpath_builddir} \
+ %{?_smp_mesonflags} \
+ --print-errorlogs \
+ %{nil}}
diff --git a/docs/markdown/Feature-autodetection.md b/docs/markdown/Feature-autodetection.md
index f865174..f50309e 100644
--- a/docs/markdown/Feature-autodetection.md
+++ b/docs/markdown/Feature-autodetection.md
@@ -4,16 +4,31 @@ short-description: Auto-detection of features like ccache and code coverage
# Feature autodetection
-Meson is designed for high productivity. It tries to do as many things automatically as it possibly can.
+Meson is designed for high productivity. It tries to do as many things
+automatically as it possibly can.
CCache
--
-[CCache](https://ccache.samba.org/) is a cache system designed to make compiling faster. When you run Meson for the first time for a given project, it checks if CCache is installed. If it is, Meson will use it automatically.
+[CCache](https://ccache.samba.org/) is a cache system designed to make
+compiling faster. When you run Meson for the first time for a given
+project, it checks if CCache is installed. If it is, Meson will use it
+automatically.
-If you do not wish to use CCache for some reason, just specify your compiler with environment variables `CC` and/or `CXX` when first running Meson (remember that once specified the compiler can not be changed). Meson will then use the specified compiler without CCache.
+If you do not wish to use CCache for some reason, just specify your
+compiler with environment variables `CC` and/or `CXX` when first
+running Meson (remember that once specified the compiler can not be
+changed). Meson will then use the specified compiler without CCache.
Coverage
--
-When doing a code coverage build, Meson will check the existence of binaries `gcovr`, `lcov` and `genhtml`. If the first one is found, it will create targets called *coverage-text* and *coverage-xml*. If the latter two or a new enough `gcovr` is found, it generates the target *coverage-html*. You can then generate coverage reports just by calling e.g. `ninja coverage-xml`.
+When doing a code coverage build, Meson will check for existence of
+the binaries `gcovr`, `lcov` and `genhtml`. If version 3.3 or higher
+of the first is found, targets called *coverage-text*, *coverage-xml*
+and *coverage-html* are generated. Alternatively, if the latter two
+are found, only the target *coverage-html* is generated. Coverage
+reports can then be produced simply by calling e.g. `ninja
+coverage-xml`. As a convenience, a high-level *coverage* target is
+also generated which will produce all 3 coverage report types, if
+possible.
diff --git a/docs/markdown/Unit-tests.md b/docs/markdown/Unit-tests.md
index 9e61739..694c190 100644
--- a/docs/markdown/Unit-tests.md
+++ b/docs/markdown/Unit-tests.md
@@ -30,7 +30,7 @@ Note how you need to specify multiple values as an array.
Coverage
--
-If you enable coverage measurements by giving Meson the command line flag `-Db_coverage=true`, you can generate coverage reports. Meson will autodetect what coverage generator tools you have installed and will generate the corresponding targets. These targets are `coverage-xml` and `coverage-text` which are both provided by [Gcovr](http://gcovr.com) and `coverage-html`, which requires [Lcov](https://ltp.sourceforge.io/coverage/lcov.php) and [GenHTML](https://linux.die.net/man/1/genhtml) or [Gcovr](http://gcovr.com) with html support.
+If you enable coverage measurements by giving Meson the command line flag `-Db_coverage=true`, you can generate coverage reports. Meson will autodetect what coverage generator tools you have installed and will generate the corresponding targets. These targets are `coverage-xml` and `coverage-text` which are both provided by [Gcovr](http://gcovr.com) (version 3.3 or higher) and `coverage-html`, which requires [Lcov](https://ltp.sourceforge.io/coverage/lcov.php) and [GenHTML](https://linux.die.net/man/1/genhtml) or [Gcovr](http://gcovr.com). As a convenience, a high-level `coverage` target is also generated which will produce all 3 coverage report types, if possible.
The output of these commands is written to the log directory `meson-logs` in your build directory.
diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py
index a75c0b7..eb9cb9f 100644
--- a/mesonbuild/ast/interpreter.py
+++ b/mesonbuild/ast/interpreter.py
@@ -20,9 +20,22 @@ from .. import interpreterbase, mparser, mesonlib
from .. import environment
from ..interpreterbase import InvalidArguments, BreakRequest, ContinueRequest
+from ..mparser import (
+ ArgumentNode,
+ ArithmeticNode,
+ ArrayNode,
+ AssignmentNode,
+ BaseNode,
+ ElementaryNode,
+ EmptyNode,
+ IdNode,
+ MethodNode,
+ PlusAssignmentNode,
+ TernaryNode,
+)
import os, sys
-from typing import List, Optional
+from typing import List, Any, Optional
class DontCareObject(interpreterbase.InterpreterObject):
pass
@@ -162,13 +175,13 @@ class AstInterpreter(interpreterbase.InterpreterBase):
return 0
def evaluate_ternary(self, node):
- assert(isinstance(node, mparser.TernaryNode))
+ assert(isinstance(node, TernaryNode))
self.evaluate_statement(node.condition)
self.evaluate_statement(node.trueblock)
self.evaluate_statement(node.falseblock)
def evaluate_plusassign(self, node):
- assert(isinstance(node, mparser.PlusAssignmentNode))
+ assert(isinstance(node, PlusAssignmentNode))
if node.var_name not in self.assignments:
self.assignments[node.var_name] = []
self.assign_vals[node.var_name] = []
@@ -184,10 +197,12 @@ class AstInterpreter(interpreterbase.InterpreterBase):
pass
def reduce_arguments(self, args):
- assert(isinstance(args, mparser.ArgumentNode))
- if args.incorrect_order():
- raise InvalidArguments('All keyword arguments must be after positional arguments.')
- return args.arguments, args.kwargs
+ if isinstance(args, ArgumentNode):
+ if args.incorrect_order():
+ raise InvalidArguments('All keyword arguments must be after positional arguments.')
+ return self.flatten_args(args.arguments), args.kwargs
+ else:
+ return self.flatten_args(args), {}
def evaluate_comparison(self, node):
self.evaluate_statement(node.left)
@@ -215,45 +230,95 @@ class AstInterpreter(interpreterbase.InterpreterBase):
def evaluate_if(self, node):
for i in node.ifs:
self.evaluate_codeblock(i.block)
- if not isinstance(node.elseblock, mparser.EmptyNode):
+ if not isinstance(node.elseblock, EmptyNode):
self.evaluate_codeblock(node.elseblock)
def get_variable(self, varname):
return 0
def assignment(self, node):
- assert(isinstance(node, mparser.AssignmentNode))
+ assert(isinstance(node, AssignmentNode))
self.assignments[node.var_name] = [node.value] # Save a reference to the value node
if hasattr(node.value, 'ast_id'):
self.reverse_assignment[node.value.ast_id] = node
self.assign_vals[node.var_name] = [self.evaluate_statement(node.value)] # Evaluate the value just in case
- def flatten_args(self, args, include_unknown_args: bool = False):
- # Resolve mparser.ArrayNode if needed
+ def flatten_args(self, args: Any, include_unknown_args: bool = False, id_loop_detect: Optional[List[str]] = None) -> List[str]:
+ def quick_resolve(n: BaseNode, loop_detect: Optional[List[str]] = None) -> Any:
+ if loop_detect is None:
+ loop_detect = []
+ if isinstance(n, IdNode):
+ if n.value in loop_detect or n.value not in self.assignments:
+ return []
+ return quick_resolve(self.assignments[n.value][0], loop_detect = loop_detect + [n.value])
+ elif isinstance(n, ElementaryNode):
+ return n.value
+ else:
+ return n
+
+ if id_loop_detect is None:
+ id_loop_detect = []
flattend_args = []
- temp_args = []
- if isinstance(args, mparser.ArrayNode):
+
+ if isinstance(args, ArrayNode):
args = [x for x in args.args.arguments]
- elif isinstance(args, mparser.ArgumentNode):
+
+ elif isinstance(args, ArgumentNode):
args = [x for x in args.arguments]
- for i in args:
- if isinstance(i, mparser.ArrayNode):
- temp_args += [x for x in i.args.arguments]
+
+ elif isinstance(args, ArithmeticNode):
+ if args.operation != 'add':
+ return [] # Only handle string and array concats
+ l = quick_resolve(args.left)
+ r = quick_resolve(args.right)
+ if isinstance(l, str) and isinstance(r, str):
+ args = [l + r] # String concatination detected
else:
- temp_args += [i]
- for i in temp_args:
- if isinstance(i, mparser.ElementaryNode) and not isinstance(i, mparser.IdNode):
+ args = self.flatten_args(l, include_unknown_args, id_loop_detect) + self.flatten_args(r, include_unknown_args, id_loop_detect)
+
+ elif isinstance(args, MethodNode):
+ src = quick_resolve(args.source_object)
+ margs = self.flatten_args(args.args, include_unknown_args, id_loop_detect)
+ try:
+ if isinstance(src, str):
+ args = [self.string_method_call(src, args.name, margs)]
+ elif isinstance(src, bool):
+ args = [self.bool_method_call(src, args.name, margs)]
+ elif isinstance(src, int):
+ args = [self.int_method_call(src, args.name, margs)]
+ elif isinstance(src, list):
+ args = [self.array_method_call(src, args.name, margs)]
+ elif isinstance(src, dict):
+ args = [self.dict_method_call(src, args.name, margs)]
+ else:
+ return []
+ except mesonlib.MesonException:
+ return []
+
+ # Make sure we are always dealing with lists
+ if not isinstance(args, list):
+ args = [args]
+
+ # Resolve the contents of args
+ for i in args:
+ if isinstance(i, IdNode) and i.value not in id_loop_detect:
+ flattend_args += self.flatten_args(quick_resolve(i), include_unknown_args, id_loop_detect + [i.value])
+ elif isinstance(i, (ArrayNode, ArgumentNode, ArithmeticNode, MethodNode)):
+ flattend_args += self.flatten_args(i, include_unknown_args, id_loop_detect)
+ elif isinstance(i, mparser.ElementaryNode):
flattend_args += [i.value]
- elif isinstance(i, (str, bool, int, float)) or include_unknown_args:
+ elif isinstance(i, (str, bool, int, float)):
+ flattend_args += [i]
+ elif include_unknown_args:
flattend_args += [i]
return flattend_args
def flatten_kwargs(self, kwargs: object, include_unknown_args: bool = False):
flattend_kwargs = {}
for key, val in kwargs.items():
- if isinstance(val, mparser.ElementaryNode):
+ if isinstance(val, ElementaryNode):
flattend_kwargs[key] = val.value
- elif isinstance(val, (mparser.ArrayNode, mparser.ArgumentNode)):
+ elif isinstance(val, (ArrayNode, ArgumentNode)):
flattend_kwargs[key] = self.flatten_args(val, include_unknown_args)
elif isinstance(val, (str, bool, int, float)) or include_unknown_args:
flattend_kwargs[key] = val
diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py
index f8fb2e8..49d531f 100644
--- a/mesonbuild/ast/introspection.py
+++ b/mesonbuild/ast/introspection.py
@@ -182,11 +182,12 @@ class IntrospectionInterpreter(AstInterpreter):
srcqueue += [curr.left, curr.right]
if arg_node is None:
continue
- elemetary_nodes = list(filter(lambda x: isinstance(x, (str, StringNode)), arg_node.arguments))
- srcqueue += list(filter(lambda x: isinstance(x, (FunctionNode, ArrayNode, IdNode, ArithmeticNode)), arg_node.arguments))
+ arg_nodes = arg_node.arguments.copy()
# Pop the first element if the function is a build target function
if isinstance(curr, FunctionNode) and curr.func_name in build_target_functions:
- elemetary_nodes.pop(0)
+ arg_nodes.pop(0)
+ elemetary_nodes = [x for x in arg_nodes if isinstance(x, (str, StringNode))]
+ srcqueue += [x for x in arg_nodes if isinstance(x, (FunctionNode, ArrayNode, IdNode, ArithmeticNode))]
if elemetary_nodes:
source_nodes += [curr]
diff --git a/mesonbuild/ast/printer.py b/mesonbuild/ast/printer.py
index 60e0b0d..2de1d0c 100644
--- a/mesonbuild/ast/printer.py
+++ b/mesonbuild/ast/printer.py
@@ -179,7 +179,7 @@ class AstPrinter(AstVisitor):
node.falseblock.accept(self)
def visit_ArgumentNode(self, node: mparser.ArgumentNode):
- break_args = True if (len(node.arguments) + len(node.kwargs)) > self.arg_newline_cutoff else False
+ break_args = (len(node.arguments) + len(node.kwargs)) > self.arg_newline_cutoff
for i in node.arguments + list(node.kwargs.values()):
if not isinstance(i, mparser.ElementaryNode):
break_args = True
diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py
index 990b824..7dd3674 100644
--- a/mesonbuild/backend/xcodebackend.py
+++ b/mesonbuild/backend/xcodebackend.py
@@ -748,7 +748,7 @@ class XCodeBackend(backends.Backend):
gargs = self.build.global_args.get(lang, [])
targs = target.get_extra_args(lang)
args = pargs + gargs + targs
- if len(args) > 0:
+ if args:
langargs[langnamemap[lang]] = args
symroot = os.path.join(self.environment.get_build_dir(), target.subdir)
self.write_line('%s /* %s */ = {' % (valid, buildtype))
@@ -783,7 +783,7 @@ class XCodeBackend(backends.Backend):
self.write_line('GCC_PREFIX_HEADER = "$(PROJECT_DIR)/%s";' % relative_pch_path)
self.write_line('GCC_PREPROCESSOR_DEFINITIONS = "";')
self.write_line('GCC_SYMBOLS_PRIVATE_EXTERN = NO;')
- if len(headerdirs) > 0:
+ if headerdirs:
quotedh = ','.join(['"\\"%s\\""' % i for i in headerdirs])
self.write_line('HEADER_SEARCH_PATHS=(%s);' % quotedh)
self.write_line('INSTALL_PATH = "%s";' % install_path)
diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py
index 5de0e59..4cb7ebf 100644
--- a/mesonbuild/compilers/__init__.py
+++ b/mesonbuild/compilers/__init__.py
@@ -69,6 +69,8 @@ __all__ = [
'IntelCCompiler',
'IntelCPPCompiler',
'IntelFortranCompiler',
+ 'IntelClCCompiler',
+ 'IntelClCPPCompiler',
'JavaCompiler',
'LLVMDCompiler',
'MonoCompiler',
@@ -130,6 +132,7 @@ from .c import (
GnuCCompiler,
ElbrusCCompiler,
IntelCCompiler,
+ IntelClCCompiler,
PGICCompiler,
CcrxCCompiler,
VisualStudioCCompiler,
@@ -143,6 +146,7 @@ from .cpp import (
GnuCPPCompiler,
ElbrusCPPCompiler,
IntelCPPCompiler,
+ IntelClCPPCompiler,
PGICPPCompiler,
CcrxCPPCompiler,
VisualStudioCPPCompiler,
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index da9a5dc..54ca894 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -248,7 +248,7 @@ class CCompiler(Compiler):
for d in dirs:
files = [f for f in os.listdir(d) if f.endswith('.so') and os.path.isfile(os.path.join(d, f))]
# if no files, accept directory and move on
- if len(files) == 0:
+ if not files:
retval.append(d)
continue
file_to_check = os.path.join(d, files[0])
@@ -1324,9 +1324,9 @@ class GnuCCompiler(GnuCompiler, CCompiler):
class PGICCompiler(PGICompiler, CCompiler):
- def __init__(self, exelist, version, is_cross, exe_wrapper=None, **kwargs):
+ def __init__(self, exelist, version, compiler_type, is_cross, exe_wrapper=None, **kwargs):
CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper, **kwargs)
- PGICompiler.__init__(self, CompilerType.PGI_STANDARD)
+ PGICompiler.__init__(self, compiler_type)
class ElbrusCCompiler(GnuCCompiler, ElbrusCompiler):
@@ -1722,6 +1722,12 @@ class ClangClCCompiler(VisualStudioCCompiler):
self.id = 'clang-cl'
+class IntelClCCompiler(VisualStudioCCompiler):
+ def __init__(self, exelist, version, is_cross, exe_wrap, target):
+ super().__init__(exelist, version, is_cross, exe_wrap, target)
+ self.id = 'intel'
+
+
class ArmCCompiler(ArmCompiler, CCompiler):
def __init__(self, exelist, version, compiler_type, is_cross, exe_wrapper=None, **kwargs):
CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper, **kwargs)
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index 79eefd1..04cc31a 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -1397,18 +1397,20 @@ class CompilerType(enum.Enum):
CCRX_WIN = 40
PGI_STANDARD = 50
+ PGI_OSX = 51
+ PGI_WIN = 52
@property
def is_standard_compiler(self):
- return self.name in ('GCC_STANDARD', 'CLANG_STANDARD', 'ICC_STANDARD')
+ return self.name in ('GCC_STANDARD', 'CLANG_STANDARD', 'ICC_STANDARD', 'PGI_STANDARD')
@property
def is_osx_compiler(self):
- return self.name in ('GCC_OSX', 'CLANG_OSX', 'ICC_OSX')
+ return self.name in ('GCC_OSX', 'CLANG_OSX', 'ICC_OSX', 'PGI_OSX')
@property
def is_windows_compiler(self):
- return self.name in ('GCC_MINGW', 'GCC_CYGWIN', 'CLANG_MINGW', 'ICC_WIN', 'ARM_WIN', 'CCRX_WIN')
+ return self.name in ('GCC_MINGW', 'GCC_CYGWIN', 'CLANG_MINGW', 'ICC_WIN', 'ARM_WIN', 'CCRX_WIN', 'PGI_WIN')
def get_macos_dylib_install_name(prefix, shlib_name, suffix, soversion):
@@ -1506,7 +1508,7 @@ def gnulike_default_include_dirs(compiler, lang):
break
else:
paths.append(line[1:])
- if len(paths) == 0:
+ if not paths:
mlog.warning('No include directory found parsing "{cmd}" output'.format(cmd=" ".join(cmd)))
return paths
@@ -1690,7 +1692,7 @@ class GnuCompiler(GnuLikeCompiler):
class PGICompiler:
- def __init__(self, compiler_type=None):
+ def __init__(self, compiler_type):
self.id = 'pgi'
self.compiler_type = compiler_type
@@ -1706,6 +1708,11 @@ class PGICompiler:
def get_no_warn_args(self) -> List[str]:
return ['-silent']
+ def get_pic_args(self) -> List[str]:
+ if self.compiler_type.is_osx_compiler or self.compiler_type.is_windows_compiler:
+ return [] # PGI -fPIC is Linux only.
+ return ['-fPIC']
+
def openmp_flags(self) -> List[str]:
return ['-mp']
diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py
index 87e6ffc..7c2253d 100644
--- a/mesonbuild/compilers/cpp.py
+++ b/mesonbuild/compilers/cpp.py
@@ -19,7 +19,7 @@ from .. import coredata
from .. import mlog
from ..mesonlib import MesonException, version_compare
-from .c import CCompiler, VisualStudioCCompiler, ClangClCCompiler
+from .c import CCompiler, VisualStudioCCompiler, ClangClCCompiler, IntelClCCompiler
from .compilers import (
gnu_winlibs,
msvc_winlibs,
@@ -238,9 +238,9 @@ class GnuCPPCompiler(GnuCompiler, CPPCompiler):
class PGICPPCompiler(PGICompiler, CPPCompiler):
- def __init__(self, exelist, version, is_cross, exe_wrapper=None, **kwargs):
+ def __init__(self, exelist, version, compiler_type, is_cross, exe_wrapper=None, **kwargs):
CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrapper, **kwargs)
- PGICompiler.__init__(self, CompilerType.PGI_STANDARD)
+ PGICompiler.__init__(self, compiler_type)
class ElbrusCPPCompiler(GnuCPPCompiler, ElbrusCompiler):
@@ -406,6 +406,13 @@ class ClangClCPPCompiler(VisualStudioCPPCompiler, ClangClCCompiler):
VisualStudioCPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap, target)
self.id = 'clang-cl'
+
+class IntelClCPPCompiler(VisualStudioCPPCompiler, IntelClCCompiler):
+ def __init__(self, exelist, version, is_cross, exe_wrap, target):
+ VisualStudioCPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap, target)
+ self.id = 'intel'
+
+
class ArmCPPCompiler(ArmCompiler, CPPCompiler):
def __init__(self, exelist, version, compiler_type, is_cross, exe_wrap=None, **kwargs):
CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap, **kwargs)
diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py
index 0f7c38e..dd54fd0 100644
--- a/mesonbuild/compilers/fortran.py
+++ b/mesonbuild/compilers/fortran.py
@@ -423,9 +423,9 @@ class PathScaleFortranCompiler(FortranCompiler):
class PGIFortranCompiler(PGICompiler, FortranCompiler):
- def __init__(self, exelist, version, is_cross, exe_wrapper=None, **kwags):
+ def __init__(self, exelist, version, compiler_type, is_cross, exe_wrapper=None, **kwags):
FortranCompiler.__init__(self, exelist, version, is_cross, exe_wrapper, **kwags)
- PGICompiler.__init__(self, CompilerType.PGI_STANDARD)
+ PGICompiler.__init__(self, compiler_type)
class FlangFortranCompiler(ClangCompiler, FortranCompiler):
diff --git a/mesonbuild/dependencies/boost.py b/mesonbuild/dependencies/boost.py
index 62a652a..381824c 100644
--- a/mesonbuild/dependencies/boost.py
+++ b/mesonbuild/dependencies/boost.py
@@ -209,7 +209,7 @@ class BoostDependency(ExternalDependency):
for root in self.boost_roots:
globtext = os.path.join(root, 'include', 'boost-*')
incdirs = glob.glob(globtext)
- if len(incdirs) > 0:
+ if incdirs:
return incdirs[0]
incboostdir = os.path.join(root, 'include', 'boost')
if os.path.isdir(incboostdir):
@@ -427,7 +427,7 @@ class BoostDependency(ExternalDependency):
for entry in globber2_matches:
fname = os.path.basename(entry)
self.lib_modules[self.modname_from_filename(fname)] = [fname]
- if len(globber2_matches) == 0:
+ if not globber2_matches:
# FIXME - why are we only looking for *.lib? Mingw provides *.dll.a and *.a
for entry in glob.glob(os.path.join(self.libdir, globber1 + '.lib')):
if self.static:
diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py
index e211945..f4c371f 100644
--- a/mesonbuild/envconfig.py
+++ b/mesonbuild/envconfig.py
@@ -19,6 +19,8 @@ from . import mesonlib
from .mesonlib import EnvironmentException, MachineChoice, PerMachine
from . import mlog
+_T = typing.TypeVar('_T')
+
# These classes contains all the data pulled from configuration files (native
# and cross file currently), and also assists with the reading environment
@@ -69,7 +71,7 @@ CPU_FAMILES_64_BIT = [
class MesonConfigFile:
@classmethod
- def from_config_parser(cls, parser: configparser.ConfigParser):
+ def from_config_parser(cls, parser: configparser.ConfigParser) -> typing.Dict[str, typing.Dict[str, typing.Dict[str, str]]]:
out = {}
# This is a bit hackish at the moment.
for s in parser.sections():
@@ -106,55 +108,58 @@ class HasEnvVarFallback:
that we deal with environment variables will become more structured, and
this can be starting point.
"""
- def __init__(self, fallback = True):
+ def __init__(self, fallback: bool = True):
self.fallback = fallback
class Properties(HasEnvVarFallback):
def __init__(
self,
properties: typing.Optional[typing.Dict[str, typing.Union[str, typing.List[str]]]] = None,
- fallback = True):
+ fallback: bool = True):
super().__init__(fallback)
- self.properties = properties or {}
+ self.properties = properties or {} # type: typing.Dict[str, typing.Union[str, typing.List[str]]]
- def has_stdlib(self, language):
+ def has_stdlib(self, language: str) -> bool:
return language + '_stdlib' in self.properties
- def get_stdlib(self, language):
+ # Some of get_stdlib, get_root, get_sys_root are wider than is actually
+ # true, but without heterogenious dict annotations it's not practical to
+ # narrow them
+ def get_stdlib(self, language: str) -> typing.Union[str, typing.List[str]]:
return self.properties[language + '_stdlib']
- def get_root(self):
+ def get_root(self) -> typing.Optional[typing.Union[str, typing.List[str]]]:
return self.properties.get('root', None)
- def get_sys_root(self):
+ def get_sys_root(self) -> typing.Optional[typing.Union[str, typing.List[str]]]:
return self.properties.get('sys_root', None)
- def __eq__(self, other):
+ def __eq__(self, other: typing.Any) -> typing.Union[bool, 'NotImplemented']:
if isinstance(other, type(self)):
return self.properties == other.properties
return NotImplemented
# TODO consider removing so Properties is less freeform
- def __getitem__(self, key):
+ def __getitem__(self, key: str) -> typing.Any:
return self.properties[key]
# TODO consider removing so Properties is less freeform
- def __contains__(self, item):
+ def __contains__(self, item: typing.Any) -> bool:
return item in self.properties
# TODO consider removing, for same reasons as above
- def get(self, key, default=None):
+ def get(self, key: str, default: typing.Any = None) -> typing.Any:
return self.properties.get(key, default)
class MachineInfo:
- def __init__(self, system, cpu_family, cpu, endian):
+ def __init__(self, system: str, cpu_family: str, cpu: str, endian: str):
self.system = system
self.cpu_family = cpu_family
self.cpu = cpu
self.endian = endian
- self.is_64_bit = cpu_family in CPU_FAMILES_64_BIT
+ self.is_64_bit = cpu_family in CPU_FAMILES_64_BIT # type: bool
- def __eq__(self, other):
+ def __eq__(self, other: typing.Any) -> typing.Union[bool, 'NotImplemented']:
if self.__class__ is not other.__class__:
return NotImplemented
return \
@@ -163,16 +168,16 @@ class MachineInfo:
self.cpu == other.cpu and \
self.endian == other.endian
- def __ne__(self, other):
+ def __ne__(self, other: typing.Any) -> typing.Union[bool, 'NotImplemented']:
if self.__class__ is not other.__class__:
return NotImplemented
return not self.__eq__(other)
- def __repr__(self):
+ def __repr__(self) -> str:
return '<MachineInfo: {} {} ({})>'.format(self.system, self.cpu_family, self.cpu)
- @staticmethod
- def from_literal(literal):
+ @classmethod
+ def from_literal(cls, literal: typing.Dict[str, str]) -> 'MachineInfo':
minimum_literal = {'cpu', 'cpu_family', 'endian', 'system'}
if set(literal) < minimum_literal:
raise EnvironmentException(
@@ -187,49 +192,45 @@ class MachineInfo:
if endian not in ('little', 'big'):
mlog.warning('Unknown endian %s' % endian)
- return MachineInfo(
- literal['system'],
- cpu_family,
- literal['cpu'],
- endian)
+ return cls(literal['system'], cpu_family, literal['cpu'], endian)
- def is_windows(self):
+ def is_windows(self) -> bool:
"""
Machine is windows?
"""
return self.system == 'windows'
- def is_cygwin(self):
+ def is_cygwin(self) -> bool:
"""
Machine is cygwin?
"""
return self.system == 'cygwin'
- def is_linux(self):
+ def is_linux(self) -> bool:
"""
Machine is linux?
"""
return self.system == 'linux'
- def is_darwin(self):
+ def is_darwin(self) -> bool:
"""
Machine is Darwin (iOS/OS X)?
"""
return self.system in ('darwin', 'ios')
- def is_android(self):
+ def is_android(self) -> bool:
"""
Machine is Android?
"""
return self.system == 'android'
- def is_haiku(self):
+ def is_haiku(self) -> bool:
"""
Machine is Haiku?
"""
return self.system == 'haiku'
- def is_openbsd(self):
+ def is_openbsd(self) -> bool:
"""
Machine is OpenBSD?
"""
@@ -239,29 +240,28 @@ class MachineInfo:
# static libraries, and executables.
# Versioning is added to these names in the backends as-needed.
- def get_exe_suffix(self):
+ def get_exe_suffix(self) -> str:
if self.is_windows() or self.is_cygwin():
return 'exe'
else:
return ''
- def get_object_suffix(self):
+ def get_object_suffix(self) -> str:
if self.is_windows():
return 'obj'
else:
return 'o'
- def libdir_layout_is_win(self):
- return self.is_windows() \
- or self.is_cygwin()
+ def libdir_layout_is_win(self) -> bool:
+ return self.is_windows() or self.is_cygwin()
-class PerMachineDefaultable(PerMachine):
+class PerMachineDefaultable(PerMachine[_T]):
"""Extends `PerMachine` with the ability to default from `None`s.
"""
- def __init__(self):
+ def __init__(self) -> None:
super().__init__(None, None, None)
- def default_missing(self):
+ def default_missing(self) -> None:
"""Default host to buid and target to host.
This allows just specifying nothing in the native case, just host in the
@@ -273,7 +273,7 @@ class PerMachineDefaultable(PerMachine):
if self.target is None:
self.target = self.host
- def miss_defaulting(self):
+ def miss_defaulting(self) -> None:
"""Unset definition duplicated from their previous to None
This is the inverse of ''default_missing''. By removing defaulted
@@ -285,18 +285,17 @@ class PerMachineDefaultable(PerMachine):
if self.host == self.build:
self.host = None
-class MachineInfos(PerMachineDefaultable):
- def matches_build_machine(self, machine: MachineChoice):
+class MachineInfos(PerMachineDefaultable[typing.Optional[MachineInfo]]):
+ def matches_build_machine(self, machine: MachineChoice) -> bool:
return self.build == self[machine]
class BinaryTable(HasEnvVarFallback):
def __init__(
self,
binaries: typing.Optional[typing.Dict[str, typing.Union[str, typing.List[str]]]] = None,
-
- fallback = True):
+ fallback: bool = True):
super().__init__(fallback)
- self.binaries = binaries or {}
+ self.binaries = binaries or {} # type: typing.Dict[str, typing.Union[str, typing.List[str]]]
for name, command in self.binaries.items():
if not isinstance(command, (list, str)):
# TODO generalize message
@@ -325,29 +324,25 @@ class BinaryTable(HasEnvVarFallback):
'cmake': 'CMAKE',
'qmake': 'QMAKE',
'pkgconfig': 'PKG_CONFIG',
- }
+ } # type: typing.Dict[str, str]
- @classmethod
- def detect_ccache(cls):
+ @staticmethod
+ def detect_ccache() -> typing.List[str]:
try:
- has_ccache = subprocess.call(['ccache', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- except OSError:
- has_ccache = 1
- if has_ccache == 0:
- cmdlist = ['ccache']
- else:
- cmdlist = []
- return cmdlist
+ subprocess.check_call(['ccache', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except (OSError, subprocess.CalledProcessError):
+ return []
+ return ['ccache']
@classmethod
- def _warn_about_lang_pointing_to_cross(cls, compiler_exe, evar):
+ def _warn_about_lang_pointing_to_cross(cls, compiler_exe: str, evar: str) -> None:
evar_str = os.environ.get(evar, 'WHO_WOULD_CALL_THEIR_COMPILER_WITH_THIS_NAME')
if evar_str == compiler_exe:
mlog.warning('''Env var %s seems to point to the cross compiler.
This is probably wrong, it should always point to the native compiler.''' % evar)
@classmethod
- def parse_entry(cls, entry):
+ def parse_entry(cls, entry: typing.Union[str, typing.List[str]]) -> typing.Tuple[typing.List[str], typing.List[str]]:
compiler = mesonlib.stringlistify(entry)
# Ensure ccache exists and remove it if it doesn't
if compiler[0] == 'ccache':
@@ -358,8 +353,8 @@ This is probably wrong, it should always point to the native compiler.''' % evar
# Return value has to be a list of compiler 'choices'
return compiler, ccache
- def lookup_entry(self, name):
- """Lookup binary
+ def lookup_entry(self, name: str) -> typing.Optional[typing.List[str]]:
+ """Lookup binaryk
Returns command with args as list if found, Returns `None` if nothing is
found.
@@ -408,11 +403,12 @@ class Directories:
self.sharedstatedir = sharedstatedir
self.sysconfdir = sysconfdir
- def __contains__(self, key: str) -> str:
+ def __contains__(self, key: str) -> bool:
return hasattr(self, key)
- def __getitem__(self, key: str) -> str:
- return getattr(self, key)
+ def __getitem__(self, key: str) -> typing.Optional[str]:
+ # Mypy can't figure out what to do with getattr here, so we'll case for it
+ return typing.cast(typing.Optional[str], getattr(self, key))
def __setitem__(self, key: str, value: typing.Optional[str]) -> None:
setattr(self, key, value)
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index c296573..bf2cce9 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -12,14 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import configparser, os, platform, re, sys, shlex, shutil, subprocess
-import typing
+import os, platform, re, sys, shlex, shutil, subprocess
from . import coredata
from .linkers import ArLinker, ArmarLinker, VisualStudioLinker, DLinker, CcrxLinker
from . import mesonlib
from .mesonlib import (
- MesonException, EnvironmentException, MachineChoice, PerMachine, Popen_safe,
+ MesonException, EnvironmentException, MachineChoice, Popen_safe,
)
from . import mlog
@@ -38,7 +37,6 @@ from .compilers import (
is_source,
)
from .compilers import (
- Compiler,
ArmCCompiler,
ArmCPPCompiler,
ArmclangCCompiler,
@@ -62,6 +60,8 @@ from .compilers import (
IntelCCompiler,
IntelCPPCompiler,
IntelFortranCompiler,
+ IntelClCCompiler,
+ IntelClCPPCompiler,
JavaCompiler,
MonoCompiler,
CudaCompiler,
@@ -83,7 +83,7 @@ from .compilers import (
build_filename = 'meson.build'
-def detect_gcovr(version='3.1', log=False):
+def detect_gcovr(min_version='3.3', new_rootdir_version='4.2', log=False):
gcovr_exe = 'gcovr'
try:
p, found = Popen_safe([gcovr_exe, '--version'])[0:2]
@@ -91,10 +91,10 @@ def detect_gcovr(version='3.1', log=False):
# Doesn't exist in PATH or isn't executable
return None, None
found = search_version(found)
- if p.returncode == 0:
+ if p.returncode == 0 and mesonlib.version_compare(found, '>=' + min_version):
if log:
mlog.log('Found gcovr-{} at {}'.format(found, shlex.quote(shutil.which(gcovr_exe))))
- return gcovr_exe, mesonlib.version_compare(found, '>=' + version)
+ return gcovr_exe, mesonlib.version_compare(found, '>=' + new_rootdir_version)
return None, None
def find_coverage_tools():
@@ -182,7 +182,7 @@ def detect_windows_arch(compilers):
# 32-bit and pretend like we're running under WOW64. Else, return the
# actual Windows architecture that we deduced above.
for compiler in compilers.values():
- if compiler.id == 'msvc' and compiler.target == 'x86':
+ if compiler.id == 'msvc' and (compiler.target == 'x86' or compiler.target == '80x86'):
return 'x86'
if compiler.id == 'clang-cl' and compiler.target == 'x86':
return 'x86'
@@ -455,11 +455,13 @@ class Environment:
# List of potential compilers.
if mesonlib.is_windows():
- self.default_c = ['cl', 'cc', 'gcc', 'clang', 'clang-cl', 'pgcc']
- self.default_cpp = ['cl', 'c++', 'g++', 'clang++', 'clang-cl', 'pgc++']
+ # Intel C and C++ compiler is icl on Windows, but icc and icpc elsewhere.
+ self.default_c = ['cl', 'cc', 'gcc', 'clang', 'clang-cl', 'pgcc', 'icl']
+ # There is currently no pgc++ for Windows, only for Mac and Linux.
+ self.default_cpp = ['cl', 'c++', 'g++', 'clang++', 'clang-cl', 'icl']
else:
- self.default_c = ['cc', 'gcc', 'clang', 'pgcc']
- self.default_cpp = ['c++', 'g++', 'clang++', 'pgc++']
+ self.default_c = ['cc', 'gcc', 'clang', 'pgcc', 'icc']
+ self.default_cpp = ['c++', 'g++', 'clang++', 'pgc++', 'icpc']
if mesonlib.is_windows():
self.default_cs = ['csc', 'mcs']
else:
@@ -467,7 +469,7 @@ class Environment:
self.default_objc = ['cc']
self.default_objcpp = ['c++']
self.default_d = ['ldc2', 'ldc', 'gdc', 'dmd']
- self.default_fortran = ['gfortran', 'g95', 'f95', 'f90', 'f77', 'ifort', 'pgfortran']
+ self.default_fortran = ['gfortran', 'flang', 'pgfortran', 'ifort', 'g95']
self.default_java = ['javac']
self.default_cuda = ['nvcc']
self.default_rust = ['rustc']
@@ -676,6 +678,7 @@ class Environment:
arg = '-v'
else:
arg = '--version'
+
try:
p, out, err = Popen_safe(compiler + [arg])
except OSError as e:
@@ -684,6 +687,11 @@ class Environment:
if 'ccrx' in compiler[0]:
out = err
+ if 'icl' in compiler[0]:
+ # https://software.intel.com/en-us/cpp-compiler-developer-guide-and-reference-alphabetical-list-of-compiler-options
+ # https://software.intel.com/en-us/fortran-compiler-developer-guide-and-reference-logo
+ # most consistent way for ICL is to just let compiler error and tell version
+ out = err
full_version = out.split('\n', 1)[0]
version = search_version(out)
@@ -769,19 +777,29 @@ class Environment:
target = 'x86'
cls = VisualStudioCCompiler if lang == 'c' else VisualStudioCPPCompiler
return cls(compiler, version, is_cross, exe_wrap, target)
+
if 'PGI Compilers' in out:
+ if mesonlib.for_darwin(is_cross, self):
+ compiler_type = CompilerType.PGI_OSX
+ elif mesonlib.for_windows(is_cross, self):
+ compiler_type = CompilerType.PGI_WIN
+ else:
+ compiler_type = CompilerType.PGI_STANDARD
cls = PGICCompiler if lang == 'c' else PGICPPCompiler
- return cls(ccache + compiler, version, is_cross, exe_wrap)
+ return cls(ccache + compiler, version, compiler_type, is_cross, exe_wrap)
if '(ICC)' in out:
if mesonlib.for_darwin(want_cross, self):
compiler_type = CompilerType.ICC_OSX
elif mesonlib.for_windows(want_cross, self):
- # TODO: fix ICC on Windows
- compiler_type = CompilerType.ICC_WIN
+ raise EnvironmentException('At the time of authoring, there was no ICC for Windows')
else:
compiler_type = CompilerType.ICC_STANDARD
cls = IntelCCompiler if lang == 'c' else IntelCPPCompiler
return cls(ccache + compiler, version, compiler_type, is_cross, exe_wrap, full_version=full_version)
+ if out.startswith('Intel(R) C++') and mesonlib.for_windows(want_cross, self):
+ cls = IntelClCCompiler if lang == 'c' else IntelClCPPCompiler
+ target = 'x64' if 'Intel(R) 64 Compiler' in out else 'x86'
+ return cls(compiler, version, is_cross, exe_wrap, target)
if 'ARM' in out:
compiler_type = CompilerType.ARM_WIN
cls = ArmCCompiler if lang == 'c' else ArmCPPCompiler
@@ -846,6 +864,13 @@ class Environment:
popen_exceptions[' '.join(compiler + [arg])] = e
continue
+ if mesonlib.for_windows(is_cross, self):
+ if 'ifort' in compiler[0]:
+ # https://software.intel.com/en-us/cpp-compiler-developer-guide-and-reference-alphabetical-list-of-compiler-options
+ # https://software.intel.com/en-us/fortran-compiler-developer-guide-and-reference-logo
+ # most consistent way for ICL is to just let compiler error and tell version
+ out = err
+
version = search_version(out)
full_version = out.split('\n', 1)[0]
@@ -876,14 +901,20 @@ class Environment:
version = search_version(err)
return SunFortranCompiler(compiler, version, is_cross, exe_wrap, full_version=full_version)
- if 'ifort (IFORT)' in out:
+ if 'ifort (IFORT)' in out or out.startswith('Intel(R) Visual Fortran'):
return IntelFortranCompiler(compiler, version, is_cross, exe_wrap, full_version=full_version)
if 'PathScale EKOPath(tm)' in err:
return PathScaleFortranCompiler(compiler, version, is_cross, exe_wrap, full_version=full_version)
if 'PGI Compilers' in out:
- return PGIFortranCompiler(compiler, version, is_cross, exe_wrap, full_version=full_version)
+ if mesonlib.for_darwin(is_cross, self):
+ compiler_type = CompilerType.PGI_OSX
+ elif mesonlib.for_windows(is_cross, self):
+ compiler_type = CompilerType.PGI_WIN
+ else:
+ compiler_type = CompilerType.PGI_STANDARD
+ return PGIFortranCompiler(compiler, version, compiler_type, is_cross, exe_wrap, full_version=full_version)
if 'flang' in out or 'clang' in out:
return FlangFortranCompiler(compiler, version, is_cross, exe_wrap, full_version=full_version)
@@ -1039,7 +1070,8 @@ class Environment:
# up to date language version at time (2016).
if exelist is not None:
if os.path.basename(exelist[-1]).startswith(('ldmd', 'gdmd')):
- raise EnvironmentException('Meson doesn\'t support %s as it\'s only a DMD frontend for another compiler. Please provide a valid value for DC or unset it so that Meson can resolve the compiler by itself.' % exelist[-1])
+ raise EnvironmentException('Meson does not support {} as it is only a DMD frontend for another compiler.'
+ 'Please provide a valid value for DC or unset it so that Meson can resolve the compiler by itself.'.format(exelist[-1]))
else:
for d in self.default_d:
if shutil.which(d):
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index b107a25..8ca7758 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -4076,24 +4076,6 @@ This will become a hard error in the future.''', location=self.current_node)
if not os.path.isfile(fname):
raise InterpreterException('Tried to add non-existing source file %s.' % s)
- def format_string(self, templ, args):
- if isinstance(args, mparser.ArgumentNode):
- args = args.arguments
- arg_strings = []
- for arg in args:
- arg = self.evaluate_statement(arg)
- if isinstance(arg, bool): # Python boolean is upper case.
- arg = str(arg).lower()
- arg_strings.append(str(arg))
-
- def arg_replace(match):
- idx = int(match.group(1))
- if idx >= len(arg_strings):
- raise InterpreterException('Format placeholder @{}@ out of range.'.format(idx))
- return arg_strings[idx]
-
- return re.sub(r'@(\d+)@', arg_replace, templ)
-
# Only permit object extraction from the same subproject
def validate_extraction(self, buildtarget):
if not self.subdir.startswith(self.subproject_dir):
diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py
index 650d1e0..c148cbd 100644
--- a/mesonbuild/interpreterbase.py
+++ b/mesonbuild/interpreterbase.py
@@ -920,6 +920,24 @@ The result of this is undefined and will become a hard error in a future Meson r
return mesonlib.version_compare(obj, cmpr)
raise InterpreterException('Unknown method "%s" for a string.' % method_name)
+ def format_string(self, templ, args):
+ if isinstance(args, mparser.ArgumentNode):
+ args = args.arguments
+ arg_strings = []
+ for arg in args:
+ arg = self.evaluate_statement(arg)
+ if isinstance(arg, bool): # Python boolean is upper case.
+ arg = str(arg).lower()
+ arg_strings.append(str(arg))
+
+ def arg_replace(match):
+ idx = int(match.group(1))
+ if idx >= len(arg_strings):
+ raise InterpreterException('Format placeholder @{}@ out of range.'.format(idx))
+ return arg_strings[idx]
+
+ return re.sub(r'@(\d+)@', arg_replace, templ)
+
def unknown_function_called(self, func_name):
raise InvalidCode('Unknown function "%s".' % func_name)
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index 25e15e4..7219946 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -15,7 +15,6 @@
"""A library of random helper functionality."""
from pathlib import Path
from typing import List
-import functools
import sys
import stat
import time
@@ -23,9 +22,13 @@ import platform, subprocess, operator, os, shutil, re
import collections
from enum import Enum
from functools import lru_cache
+import typing
from mesonbuild import mlog
+_T = typing.TypeVar('_T')
+_U = typing.TypeVar('_U')
+
have_fcntl = False
have_msvcrt = False
# {subproject: project_meson_version}
@@ -320,20 +323,22 @@ class MachineChoice(OrderedEnum):
HOST = 1
TARGET = 2
-class PerMachine:
- def __init__(self, build, host, target):
+_T = typing.TypeVar('_T')
+
+class PerMachine(typing.Generic[_T]):
+ def __init__(self, build: typing.Optional[_T], host: typing.Optional[_T], target: typing.Optional[_T]):
self.build = build
self.host = host
self.target = target
- def __getitem__(self, machine: MachineChoice):
+ def __getitem__(self, machine: MachineChoice) -> typing.Optional[_T]:
return {
MachineChoice.BUILD: self.build,
MachineChoice.HOST: self.host,
MachineChoice.TARGET: self.target
}[machine]
- def __setitem__(self, machine: MachineChoice, val):
+ def __setitem__(self, machine: MachineChoice, val: typing.Optional[_T]) -> None:
key = {
MachineChoice.BUILD: 'build',
MachineChoice.HOST: 'host',
@@ -506,7 +511,6 @@ def detect_vcs(source_dir):
return None
# a helper class which implements the same version ordering as RPM
-@functools.total_ordering
class Version:
def __init__(self, s):
self._s = s
@@ -515,8 +519,8 @@ class Version:
sequences = re.finditer(r'(\d+|[a-zA-Z]+|[^a-zA-Z\d]+)', s)
# non-alphanumeric separators are discarded
sequences = [m for m in sequences if not re.match(r'[^a-zA-Z\d]+', m.group(1))]
- # numeric sequences have leading zeroes discarded
- sequences = [re.sub(r'^0+(\d)', r'\1', m.group(1), 1) for m in sequences]
+ # numeric sequences are converted from strings to ints
+ sequences = [int(m.group(1)) if m.group(1).isdigit() else m.group(1) for m in sequences]
self._v = sequences
@@ -527,38 +531,50 @@ class Version:
return '<Version: {}>'.format(self._s)
def __lt__(self, other):
- return self.__cmp__(other) == -1
+ if isinstance(other, Version):
+ return self.__cmp(other, operator.lt)
+ return NotImplemented
+
+ def __gt__(self, other):
+ if isinstance(other, Version):
+ return self.__cmp(other, operator.gt)
+ return NotImplemented
+
+ def __le__(self, other):
+ if isinstance(other, Version):
+ return self.__cmp(other, operator.le)
+ return NotImplemented
+
+ def __ge__(self, other):
+ if isinstance(other, Version):
+ return self.__cmp(other, operator.ge)
+ return NotImplemented
def __eq__(self, other):
- return self.__cmp__(other) == 0
+ if isinstance(other, Version):
+ return self._v == other._v
+ return NotImplemented
- def __cmp__(self, other):
- def cmp(a, b):
- return (a > b) - (a < b)
+ def __ne__(self, other):
+ if isinstance(other, Version):
+ return self._v != other._v
+ return NotImplemented
+ def __cmp(self, other, comparator):
# compare each sequence in order
- for i in range(0, min(len(self._v), len(other._v))):
+ for ours, theirs in zip(self._v, other._v):
# sort a non-digit sequence before a digit sequence
- if self._v[i].isdigit() != other._v[i].isdigit():
- return 1 if self._v[i].isdigit() else -1
-
- # compare as numbers
- if self._v[i].isdigit():
- # because leading zeros have already been removed, if one number
- # has more digits, it is greater
- c = cmp(len(self._v[i]), len(other._v[i]))
- if c != 0:
- return c
- # fallthrough
-
- # compare lexicographically
- c = cmp(self._v[i], other._v[i])
- if c != 0:
- return c
+ ours_is_int = isinstance(ours, int)
+ theirs_is_int = isinstance(theirs, int)
+ if ours_is_int != theirs_is_int:
+ return comparator(ours_is_int, theirs_is_int)
+
+ if ours != theirs:
+ return comparator(ours, theirs)
# if equal length, all components have matched, so equal
# otherwise, the version with a suffix remaining is greater
- return cmp(len(self._v), len(other._v))
+ return comparator(len(self._v), len(other._v))
def _version_extract_cmpop(vstr2):
if vstr2.startswith('>='):
@@ -906,14 +922,13 @@ def extract_as_list(dict_object, *keys, pop=False, **kwargs):
result.append(listify(fetch(key, []), **kwargs))
return result
-
-def typeslistify(item, types):
+def typeslistify(item: typing.Union[_T, typing.List[_T]], types: typing.Union[typing.Type[_T], typing.Tuple[typing.Type[_T]]]) -> typing.List[_T]:
'''
Ensure that type(@item) is one of @types or a
list of items all of which are of type @types
'''
if isinstance(item, types):
- item = [item]
+ item = typing.cast(typing.List[_T], [item])
if not isinstance(item, list):
raise MesonException('Item must be a list or one of {!r}'.format(types))
for i in item:
@@ -921,7 +936,7 @@ def typeslistify(item, types):
raise MesonException('List item must be one of {!r}'.format(types))
return item
-def stringlistify(item):
+def stringlistify(item: typing.Union[str, typing.List[str]]) -> typing.List[str]:
return typeslistify(item, str)
def expand_arguments(args):
@@ -1192,7 +1207,14 @@ def detect_subprojects(spdir_name, current_dir='', result=None):
result[basename] = [trial]
return result
-def get_error_location_string(fname, lineno):
+# This isn't strictly correct. What we really want here is something like:
+# class StringProtocol(typing_extensions.Protocol):
+#
+# def __str__(self) -> str: ...
+#
+# This would more accurately embody what this funcitonc an handle, but we
+# don't have that yet, so instead we'll do some casting to work around it
+def get_error_location_string(fname: str, lineno: str) -> str:
return '{}:{}:'.format(fname, lineno)
def substring_is_in_list(substr, strlist):
diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py
index f80dfdd..4326c20 100644
--- a/mesonbuild/mesonmain.py
+++ b/mesonbuild/mesonmain.py
@@ -110,7 +110,7 @@ class CommandLineParser:
# If first arg is not a known command, assume user wants to run the setup
# command.
known_commands = list(self.commands.keys()) + ['-h', '--help']
- if len(args) == 0 or args[0] not in known_commands:
+ if not args or args[0] not in known_commands:
args = ['setup'] + args
# Hidden commands have their own parser instead of using the global one
diff --git a/mesonbuild/minit.py b/mesonbuild/minit.py
index 394fe40..4ae0ae3 100644
--- a/mesonbuild/minit.py
+++ b/mesonbuild/minit.py
@@ -442,7 +442,7 @@ def add_arguments(parser):
parser.add_argument('--version', default='0.1')
def run(options):
- if len(glob('*')) == 0:
+ if not glob('*'):
autodetect_options(options, sample=True)
if not options.language:
print('Defaulting to generating a C language project.')
diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py
index 32931b6..a4a6978 100644
--- a/mesonbuild/mintro.py
+++ b/mesonbuild/mintro.py
@@ -343,7 +343,7 @@ def list_projinfo_from_source(sourcedir: str, intr: IntrospectionInterpreter):
return intr.project_data
def print_results(options, results, indent):
- if len(results) == 0 and not options.force_dict:
+ if not results and not options.force_dict:
print('No command specified')
return 1
elif len(results) == 1 and not options.force_dict:
@@ -487,7 +487,7 @@ def write_meson_info_file(builddata: build.Build, errors: list, build_files_upda
'build_files_updated': build_files_updated,
}
- if len(errors) > 0:
+ if errors:
info_data['error'] = True
info_data['error_list'] = [x if isinstance(x, str) else str(x) for x in errors]
else:
diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py
index 0434274..e8ee6c8 100644
--- a/mesonbuild/mlog.py
+++ b/mesonbuild/mlog.py
@@ -18,13 +18,15 @@ import sys
import time
import platform
from contextlib import contextmanager
+import typing
"""This is (mostly) a standalone module used to write logging
information about Meson runs. Some output goes to screen,
some to logging dir and some goes to both."""
-def _windows_ansi():
- from ctypes import windll, byref
+def _windows_ansi() -> bool:
+ # windll only exists on windows, so mypy will get mad
+ from ctypes import windll, byref # type: ignore
from ctypes.wintypes import DWORD
kernel = windll.kernel32
@@ -35,48 +37,48 @@ def _windows_ansi():
# ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0x4
# If the call to enable VT processing fails (returns 0), we fallback to
# original behavior
- return kernel.SetConsoleMode(stdout, mode.value | 0x4) or os.environ.get('ANSICON')
+ return bool(kernel.SetConsoleMode(stdout, mode.value | 0x4) or os.environ.get('ANSICON'))
if platform.system().lower() == 'windows':
- colorize_console = os.isatty(sys.stdout.fileno()) and _windows_ansi()
+ colorize_console = os.isatty(sys.stdout.fileno()) and _windows_ansi() # type: bool
else:
colorize_console = os.isatty(sys.stdout.fileno()) and os.environ.get('TERM') != 'dumb'
-log_dir = None
-log_file = None
-log_fname = 'meson-log.txt'
-log_depth = 0
-log_timestamp_start = None
-log_fatal_warnings = False
-log_disable_stdout = False
-log_errors_only = False
-
-def disable():
+log_dir = None # type: typing.Optional[str]
+log_file = None # type: typing.Optional[typing.TextIO]
+log_fname = 'meson-log.txt' # type: str
+log_depth = 0 # type: int
+log_timestamp_start = None # type: typing.Optional[float]
+log_fatal_warnings = False # type: bool
+log_disable_stdout = False # type: bool
+log_errors_only = False # type: bool
+
+def disable() -> None:
global log_disable_stdout
log_disable_stdout = True
-def enable():
+def enable() -> None:
global log_disable_stdout
log_disable_stdout = False
-def set_quiet():
+def set_quiet() -> None:
global log_errors_only
log_errors_only = True
-def set_verbose():
+def set_verbose() -> None:
global log_errors_only
log_errors_only = False
-def initialize(logdir, fatal_warnings=False):
+def initialize(logdir: str, fatal_warnings: bool = False) -> None:
global log_dir, log_file, log_fatal_warnings
log_dir = logdir
log_file = open(os.path.join(logdir, log_fname), 'w', encoding='utf8')
log_fatal_warnings = fatal_warnings
-def set_timestamp_start(start):
+def set_timestamp_start(start: float) -> None:
global log_timestamp_start
log_timestamp_start = start
-def shutdown():
+def shutdown() -> typing.Optional[str]:
global log_file
if log_file is not None:
path = log_file.name
@@ -89,12 +91,12 @@ def shutdown():
class AnsiDecorator:
plain_code = "\033[0m"
- def __init__(self, text, code, quoted=False):
+ def __init__(self, text: str, code: str, quoted: bool = False):
self.text = text
self.code = code
self.quoted = quoted
- def get_text(self, with_codes):
+ def get_text(self, with_codes: bool) -> str:
text = self.text
if with_codes:
text = self.code + self.text + AnsiDecorator.plain_code
@@ -102,26 +104,28 @@ class AnsiDecorator:
text = '"{}"'.format(text)
return text
-def bold(text, quoted=False):
+def bold(text: str, quoted: bool = False) -> AnsiDecorator:
return AnsiDecorator(text, "\033[1m", quoted=quoted)
-def red(text):
+def red(text: str) -> AnsiDecorator:
return AnsiDecorator(text, "\033[1;31m")
-def green(text):
+def green(text: str) -> AnsiDecorator:
return AnsiDecorator(text, "\033[1;32m")
-def yellow(text):
+def yellow(text: str) -> AnsiDecorator:
return AnsiDecorator(text, "\033[1;33m")
-def blue(text):
+def blue(text: str) -> AnsiDecorator:
return AnsiDecorator(text, "\033[1;34m")
-def cyan(text):
+def cyan(text: str) -> AnsiDecorator:
return AnsiDecorator(text, "\033[1;36m")
-def process_markup(args, keep):
- arr = []
+# This really should be AnsiDecorator or anything that implements
+# __str__(), but that requires protocols from typing_extensions
+def process_markup(args: typing.Sequence[typing.Union[AnsiDecorator, str]], keep: bool) -> typing.List[str]:
+ arr = [] # type: typing.List[str]
if log_timestamp_start is not None:
arr = ['[{:.3f}]'.format(time.monotonic() - log_timestamp_start)]
for arg in args:
@@ -135,7 +139,7 @@ def process_markup(args, keep):
arr.append(str(arg))
return arr
-def force_print(*args, **kwargs):
+def force_print(*args: str, **kwargs: typing.Any) -> None:
global log_disable_stdout
if log_disable_stdout:
return
@@ -155,41 +159,51 @@ def force_print(*args, **kwargs):
cleaned = raw.encode('ascii', 'replace').decode('ascii')
print(cleaned, end='')
-def debug(*args, **kwargs):
+# We really want a heterogenous dict for this, but that's in typing_extensions
+def debug(*args: typing.Union[str, AnsiDecorator], **kwargs: typing.Any) -> None:
arr = process_markup(args, False)
if log_file is not None:
- print(*arr, file=log_file, **kwargs) # Log file never gets ANSI codes.
+ print(*arr, file=log_file, **kwargs)
log_file.flush()
-def log(*args, is_error=False, **kwargs):
+def log(*args: typing.Union[str, AnsiDecorator], is_error: bool = False,
+ **kwargs: typing.Any) -> None:
global log_errors_only
arr = process_markup(args, False)
if log_file is not None:
- print(*arr, file=log_file, **kwargs) # Log file never gets ANSI codes.
+ print(*arr, file=log_file, **kwargs)
log_file.flush()
if colorize_console:
arr = process_markup(args, True)
if not log_errors_only or is_error:
force_print(*arr, **kwargs)
-def _log_error(severity, *args, **kwargs):
+def _log_error(severity: str, *rargs: typing.Union[str, AnsiDecorator], **kwargs: typing.Any) -> None:
from .mesonlib import get_error_location_string
from .environment import build_filename
from .mesonlib import MesonException
+
+ # The tping requirements here are non-obvious. Lists are invariant,
+ # therefore List[A] and List[Union[A, B]] are not able to be joined
if severity == 'warning':
- args = (yellow('WARNING:'),) + args
+ label = [yellow('WARNING:')] # type: typing.List[typing.Union[str, AnsiDecorator]]
elif severity == 'error':
- args = (red('ERROR:'),) + args
+ label = [red('ERROR:')]
elif severity == 'deprecation':
- args = (red('DEPRECATION:'),) + args
+ label = [red('DEPRECATION:')]
else:
- assert False, 'Invalid severity ' + severity
+ raise MesonException('Invalid severity ' + severity)
+ # rargs is a tuple, not a list
+ args = label + list(rargs)
location = kwargs.pop('location', None)
if location is not None:
location_file = os.path.join(location.subdir, build_filename)
location_str = get_error_location_string(location_file, location.lineno)
- args = (location_str,) + args
+ # Unions are frankly awful, and we have to cast here to get mypy
+ # to understand that the list concatenation is safe
+ location_list = typing.cast(typing.List[typing.Union[str, AnsiDecorator]], [location_str])
+ args = location_list + args
log(*args, **kwargs)
@@ -197,40 +211,42 @@ def _log_error(severity, *args, **kwargs):
if log_fatal_warnings:
raise MesonException("Fatal warnings enabled, aborting")
-def error(*args, **kwargs):
+def error(*args: typing.Union[str, AnsiDecorator], **kwargs: typing.Any) -> None:
return _log_error('error', *args, **kwargs, is_error=True)
-def warning(*args, **kwargs):
+def warning(*args: typing.Union[str, AnsiDecorator], **kwargs: typing.Any) -> None:
return _log_error('warning', *args, **kwargs, is_error=True)
-def deprecation(*args, **kwargs):
+def deprecation(*args: typing.Union[str, AnsiDecorator], **kwargs: typing.Any) -> None:
return _log_error('deprecation', *args, **kwargs, is_error=True)
-def exception(e, prefix=red('ERROR:')):
+def exception(e: Exception, prefix: AnsiDecorator = red('ERROR:')) -> None:
log()
- args = []
+ args = [] # type: typing.List[typing.Union[AnsiDecorator, str]]
if hasattr(e, 'file') and hasattr(e, 'lineno') and hasattr(e, 'colno'):
- args.append('%s:%d:%d:' % (e.file, e.lineno, e.colno))
+ # Mypy can't figure this out, and it's pretty easy to vidual inspect
+ # that this is correct, so we'll just ignore it.
+ args.append('%s:%d:%d:' % (e.file, e.lineno, e.colno)) # type: ignore
if prefix:
args.append(prefix)
- args.append(e)
+ args.append(str(e))
log(*args)
# Format a list for logging purposes as a string. It separates
# all but the last item with commas, and the last with 'and'.
-def format_list(list):
- l = len(list)
+def format_list(list_: typing.List[str]) -> str:
+ l = len(list_)
if l > 2:
- return ' and '.join([', '.join(list[:-1]), list[-1]])
+ return ' and '.join([', '.join(list_[:-1]), list_[-1]])
elif l == 2:
- return ' and '.join(list)
+ return ' and '.join(list_)
elif l == 1:
- return list[0]
+ return list_[0]
else:
return ''
@contextmanager
-def nested():
+def nested() -> typing.Generator[None, None, None]:
global log_depth
log_depth += 1
try:
diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py
index d98213d..6af4adb 100644
--- a/mesonbuild/modules/cmake.py
+++ b/mesonbuild/modules/cmake.py
@@ -72,20 +72,12 @@ class CmakeModule(ExtensionModule):
mlog.log('error retrieving cmake informations: returnCode={0} stdout={1} stderr={2}'.format(p.returncode, stdout, stderr))
return False
- match = re.search('\n_INCLUDED_FILE \\"([^"]+)"\n', stdout.strip())
+ match = re.search('\nCMAKE_ROOT \\"([^"]+)"\n', stdout.strip())
if not match:
mlog.log('unable to determine cmake root')
return False
- # compilerpath is something like '/usr/share/cmake-3.5/Modules/Platform/Linux-GNU-CXX.cmake'
- # or 'C:/Program Files (x86)/CMake 2.8/share/cmake-2.8/Modules/Platform/Windows-MSVC-CXX.cmake' under windows
- compilerpath = match.group(1)
- pos = compilerpath.find('/Modules/Platform/')
- if pos < 0:
- mlog.log('unknown _INCLUDED_FILE path scheme')
- return False
-
- cmakePath = pathlib.PurePath(compilerpath[0:pos])
+ cmakePath = pathlib.PurePath(match.group(1))
self.cmake_root = os.path.join(*cmakePath.parts)
self.cmake_detected = True
return True
@@ -158,7 +150,7 @@ class CmakeModule(ExtensionModule):
@permittedKwargs({'input', 'name', 'install_dir', 'configuration'})
def configure_package_config_file(self, interpreter, state, args, kwargs):
- if len(args) > 0:
+ if args:
raise mesonlib.MesonException('configure_package_config_file takes only keyword arguments.')
if 'input' not in kwargs:
diff --git a/mesonbuild/modules/hotdoc.py b/mesonbuild/modules/hotdoc.py
index 5064803..02e07a9 100644
--- a/mesonbuild/modules/hotdoc.py
+++ b/mesonbuild/modules/hotdoc.py
@@ -76,7 +76,8 @@ class HotdocTargetBuilder:
return
if isinstance(value, bool):
- self.cmd.append(option)
+ if value:
+ self.cmd.append(option)
elif isinstance(value, list):
# Do not do anything on empty lists
if value:
@@ -308,6 +309,9 @@ class HotdocTargetBuilder:
for path in self.include_paths.keys():
self.cmd.extend(['--include-path', path])
+
+ if self.state.environment.coredata.get_builtin_option('werror'):
+ self.cmd.append('--fatal-warning')
self.generate_hotdoc_config()
target_cmd = self.build_command + ["--internal", "hotdoc"] + \
diff --git a/mesonbuild/modules/qt.py b/mesonbuild/modules/qt.py
index 0b252ac..11289c4 100644
--- a/mesonbuild/modules/qt.py
+++ b/mesonbuild/modules/qt.py
@@ -129,13 +129,13 @@ class QtBaseModule(ExtensionModule):
self._detect_tools(state.environment, method)
err_msg = "{0} sources specified and couldn't find {1}, " \
"please check your qt{2} installation"
- if len(moc_headers) + len(moc_sources) > 0 and not self.moc.found():
+ if (moc_headers or moc_sources) and not self.moc.found():
raise MesonException(err_msg.format('MOC', 'moc-qt{}'.format(self.qt_version), self.qt_version))
- if len(rcc_files) > 0:
+ if rcc_files:
if not self.rcc.found():
raise MesonException(err_msg.format('RCC', 'rcc-qt{}'.format(self.qt_version), self.qt_version))
# custom output name set? -> one output file, multiple otherwise
- if len(args) > 0:
+ if args:
qrc_deps = []
for i in rcc_files:
qrc_deps += self.parse_qrc(state, i)
@@ -160,7 +160,7 @@ class QtBaseModule(ExtensionModule):
'depend_files': qrc_deps}
res_target = build.CustomTarget(name, state.subdir, state.subproject, rcc_kwargs)
sources.append(res_target)
- if len(ui_files) > 0:
+ if ui_files:
if not self.uic.found():
raise MesonException(err_msg.format('UIC', 'uic-qt{}'.format(self.qt_version), self.qt_version))
arguments = uic_extra_arguments + ['-o', '@OUTPUT@', '@INPUT@']
@@ -183,14 +183,14 @@ class QtBaseModule(ExtensionModule):
'either an external dependency (returned by find_library() or '
'dependency()) or an internal dependency (returned by '
'declare_dependency()).'.format(type(dep).__name__))
- if len(moc_headers) > 0:
+ if moc_headers:
arguments = moc_extra_arguments + inc + compile_args + ['@INPUT@', '-o', '@OUTPUT@']
moc_kwargs = {'output': 'moc_@BASENAME@.cpp',
'arguments': arguments}
moc_gen = build.Generator([self.moc], moc_kwargs)
moc_output = moc_gen.process_files('Qt{} moc header'.format(self.qt_version), moc_headers, state)
sources.append(moc_output)
- if len(moc_sources) > 0:
+ if moc_sources:
arguments = moc_extra_arguments + inc + compile_args + ['@INPUT@', '-o', '@OUTPUT@']
moc_kwargs = {'output': '@BASENAME@.moc',
'arguments': arguments}
diff --git a/mesonbuild/modules/rpm.py b/mesonbuild/modules/rpm.py
index 9774286..7c1cefb 100644
--- a/mesonbuild/modules/rpm.py
+++ b/mesonbuild/modules/rpm.py
@@ -58,7 +58,7 @@ class RPMModule(ExtensionModule):
elif isinstance(target, TypelibTarget) and target.should_install():
files.add('%%{_libdir}/girepository-1.0/%s' % target.get_filename()[0])
for header in coredata.headers:
- if len(header.get_install_subdir()) > 0:
+ if header.get_install_subdir():
files_devel.add('%%{_includedir}/%s/' % header.get_install_subdir())
else:
for hdr_src in header.get_sources():
@@ -66,7 +66,7 @@ class RPMModule(ExtensionModule):
for man in coredata.man:
for man_file in man.get_sources():
files.add('%%{_mandir}/man%u/%s.*' % (int(man_file.split('.')[-1]), man_file))
- if len(files_devel) > 0:
+ if files_devel:
devel_subpkg = True
filename = os.path.join(coredata.environment.get_build_dir(),
@@ -122,7 +122,7 @@ class RPMModule(ExtensionModule):
fn.write('\n')
fn.write('%install\n')
fn.write('%meson_install\n')
- if len(to_delete) > 0:
+ if to_delete:
fn.write('rm -vf %s\n' % ' '.join(to_delete))
fn.write('\n')
fn.write('%check\n')
diff --git a/mesonbuild/modules/unstable_icestorm.py b/mesonbuild/modules/unstable_icestorm.py
index 051dc5f..268c394 100644
--- a/mesonbuild/modules/unstable_icestorm.py
+++ b/mesonbuild/modules/unstable_icestorm.py
@@ -36,7 +36,7 @@ class IceStormModule(ExtensionModule):
def project(self, interpreter, state, args, kwargs):
if not self.yosys_bin:
self.detect_binaries(interpreter)
- if not len(args):
+ if not args:
raise mesonlib.MesonException('Project requires at least one argument, which is the project name.')
proj_name = args[0]
arg_sources = args[1:]
diff --git a/mesonbuild/modules/unstable_simd.py b/mesonbuild/modules/unstable_simd.py
index b64242a..18a1099 100644
--- a/mesonbuild/modules/unstable_simd.py
+++ b/mesonbuild/modules/unstable_simd.py
@@ -65,7 +65,7 @@ class SimdModule(ExtensionModule):
if args is None:
mlog.log('Compiler supports %s:' % iset, mlog.red('NO'))
continue
- if len(args) > 0:
+ if args:
if not compiler.has_multi_arguments(args, state.environment):
mlog.log('Compiler supports %s:' % iset, mlog.red('NO'))
continue
diff --git a/mesonbuild/rewriter.py b/mesonbuild/rewriter.py
index 6205eae..1558cdd 100644
--- a/mesonbuild/rewriter.py
+++ b/mesonbuild/rewriter.py
@@ -874,11 +874,16 @@ target_operation_map = {
}
def list_to_dict(in_list: List[str]) -> Dict[str, str]:
- if len(in_list) % 2 != 0:
- raise TypeError('An even ammount of arguments are required')
result = {}
- for i in range(0, len(in_list), 2):
- result[in_list[i]] = in_list[i + 1]
+ it = iter(in_list)
+ try:
+ for i in it:
+ # calling next(it) is not a mistake, we're taking the next element from
+ # the iterator, avoiding te need to preprocess it into a sequence of
+ # key value pairs.
+ result[i] = next(it)
+ except StopIteration:
+ raise TypeError('in_list parameter of list_to_dict must have an even length.')
return result
def generate_target(options) -> List[dict]:
diff --git a/mesonbuild/scripts/coverage.py b/mesonbuild/scripts/coverage.py
index 0e203f9..4bd41fe 100644
--- a/mesonbuild/scripts/coverage.py
+++ b/mesonbuild/scripts/coverage.py
@@ -22,35 +22,33 @@ def coverage(outputs, source_root, subproject_root, build_root, log_dir):
(gcovr_exe, gcovr_new_rootdir, lcov_exe, genhtml_exe) = environment.find_coverage_tools()
- # gcovr >= 3.1 interprets rootdir differently
+ # gcovr >= 4.2 requires a different syntax for out of source builds
if gcovr_new_rootdir:
- gcovr_rootdir = build_root
+ gcovr_base_cmd = [gcovr_exe, '-r', source_root, build_root]
else:
- gcovr_rootdir = source_root
+ gcovr_base_cmd = [gcovr_exe, '-r', build_root]
if not outputs or 'xml' in outputs:
if gcovr_exe:
- subprocess.check_call([gcovr_exe,
- '-x',
- '-r', gcovr_rootdir,
+ subprocess.check_call(gcovr_base_cmd +
+ ['-x',
'-e', subproject_root,
'-o', os.path.join(log_dir, 'coverage.xml'),
])
outfiles.append(('Xml', pathlib.Path(log_dir, 'coverage.xml')))
elif outputs:
- print('gcovr needed to generate Xml coverage report')
+ print('gcovr >= 3.3 needed to generate Xml coverage report')
exitcode = 1
if not outputs or 'text' in outputs:
if gcovr_exe:
- subprocess.check_call([gcovr_exe,
- '-r', gcovr_rootdir,
- '-e', subproject_root,
+ subprocess.check_call(gcovr_base_cmd +
+ ['-e', subproject_root,
'-o', os.path.join(log_dir, 'coverage.txt'),
])
outfiles.append(('Text', pathlib.Path(log_dir, 'coverage.txt')))
elif outputs:
- print('gcovr needed to generate text coverage report')
+ print('gcovr >= 3.3 needed to generate text coverage report')
exitcode = 1
if not outputs or 'html' in outputs:
@@ -101,21 +99,20 @@ def coverage(outputs, source_root, subproject_root, build_root, log_dir):
'--branch-coverage',
covinfo])
outfiles.append(('Html', pathlib.Path(htmloutdir, 'index.html')))
- elif gcovr_exe and gcovr_new_rootdir:
+ elif gcovr_exe:
htmloutdir = os.path.join(log_dir, 'coveragereport')
if not os.path.isdir(htmloutdir):
os.mkdir(htmloutdir)
- subprocess.check_call([gcovr_exe,
- '--html',
+ subprocess.check_call(gcovr_base_cmd +
+ ['--html',
'--html-details',
'--print-summary',
- '-r', build_root,
'-e', subproject_root,
'-o', os.path.join(htmloutdir, 'index.html'),
])
outfiles.append(('Html', pathlib.Path(htmloutdir, 'index.html')))
elif outputs:
- print('lcov/genhtml or gcovr >= 3.2 needed to generate Html coverage report')
+ print('lcov/genhtml or gcovr >= 3.3 needed to generate Html coverage report')
exitcode = 1
if not outputs and not outfiles:
diff --git a/mesonbuild/scripts/dist.py b/mesonbuild/scripts/dist.py
index 309a032..47fde23 100644
--- a/mesonbuild/scripts/dist.py
+++ b/mesonbuild/scripts/dist.py
@@ -133,7 +133,7 @@ def create_dist_hg(dist_name, src_root, bld_root, dist_sub, dist_scripts):
tarname = os.path.join(dist_sub, dist_name + '.tar')
xzname = tarname + '.xz'
subprocess.check_call(['hg', 'archive', '-R', src_root, '-S', '-t', 'tar', tarname])
- if len(dist_scripts) > 0:
+ if dist_scripts:
mlog.warning('dist scripts are not supported in Mercurial projects')
with lzma.open(xzname, 'wb') as xf, open(tarname, 'rb') as tf:
shutil.copyfileobj(tf, xf)
diff --git a/mesonbuild/scripts/gtkdochelper.py b/mesonbuild/scripts/gtkdochelper.py
index 01ced5b..11d31b6 100644
--- a/mesonbuild/scripts/gtkdochelper.py
+++ b/mesonbuild/scripts/gtkdochelper.py
@@ -175,7 +175,7 @@ def build_gtkdoc(source_root, build_root, doc_subdir, src_subdirs,
mkdb_cmd.append('--name-space=' + namespace)
if modeflag:
mkdb_cmd.append(modeflag)
- if len(main_file) > 0:
+ if main_file:
# Yes, this is the flag even if the file is in xml.
mkdb_cmd.append('--main-sgml-file=' + main_file)
# Add user-specified arguments
@@ -187,7 +187,7 @@ def build_gtkdoc(source_root, build_root, doc_subdir, src_subdirs,
'--path=' + ':'.join((doc_src, abs_out)),
module,
] + html_args
- if len(main_file) > 0:
+ if main_file:
mkhtml_cmd.append('../' + main_file)
else:
mkhtml_cmd.append('%s-docs.xml' % module)
@@ -212,23 +212,23 @@ def install_gtkdoc(build_root, doc_subdir, install_prefix, datadir, module):
def run(args):
options = parser.parse_args(args)
- if len(options.htmlargs) > 0:
+ if options.htmlargs:
htmlargs = options.htmlargs.split('@@')
else:
htmlargs = []
- if len(options.scanargs) > 0:
+ if options.scanargs:
scanargs = options.scanargs.split('@@')
else:
scanargs = []
- if len(options.scanobjsargs) > 0:
+ if options.scanobjsargs:
scanobjsargs = options.scanobjsargs.split('@@')
else:
scanobjsargs = []
- if len(options.fixxrefargs) > 0:
+ if options.fixxrefargs:
fixxrefargs = options.fixxrefargs.split('@@')
else:
fixxrefargs = []
- if len(options.mkdbargs) > 0:
+ if options.mkdbargs:
mkdbargs = options.mkdbargs.split('@@')
else:
mkdbargs = []
diff --git a/mesonbuild/scripts/meson_exe.py b/mesonbuild/scripts/meson_exe.py
index 23c7334..a862ec5 100644
--- a/mesonbuild/scripts/meson_exe.py
+++ b/mesonbuild/scripts/meson_exe.py
@@ -60,7 +60,7 @@ def run_exe(exe):
cmd = exe.fname
child_env = os.environ.copy()
child_env.update(exe.env)
- if len(exe.extra_paths) > 0:
+ if exe.extra_paths:
child_env['PATH'] = (os.pathsep.join(exe.extra_paths + ['']) +
child_env['PATH'])
if exe.exe_runner and mesonlib.substring_is_in_list('wine', exe.exe_runner.get_command()):
diff --git a/mesonbuild/scripts/symbolextractor.py b/mesonbuild/scripts/symbolextractor.py
index 95ea0ec..410cb33 100644
--- a/mesonbuild/scripts/symbolextractor.py
+++ b/mesonbuild/scripts/symbolextractor.py
@@ -69,7 +69,7 @@ def linux_syms(libfilename, outfilename):
if pnm.returncode != 0:
raise RuntimeError('nm does not work.')
for line in output.split('\n'):
- if len(line) == 0:
+ if not line:
continue
line_split = line.split()
entry = line_split[0:2]
@@ -91,7 +91,7 @@ def osx_syms(libfilename, outfilename):
pnm, output = Popen_safe(['nm', '-g', '-P', libfilename])[0:2]
if pnm.returncode != 0:
raise RuntimeError('nm does not work.')
- result += [' '.join(x.split()[0:2]) for x in output.split('\n') if len(x) > 0 and not x.endswith('U')]
+ result += [' '.join(x.split()[0:2]) for x in output.split('\n') if x and not x.endswith('U')]
write_if_changed('\n'.join(result) + '\n', outfilename)
def gen_symbols(libfilename, outfilename, cross_host):
diff --git a/run_unittests.py b/run_unittests.py
index d37e805..2457a50 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -28,6 +28,7 @@ import platform
import pickle
import functools
import io
+import operator
from itertools import chain
from unittest import mock
from configparser import ConfigParser
@@ -856,107 +857,118 @@ class InternalTests(unittest.TestCase):
]:
self.assertEqual(comparefunc(a, b)[0], result)
- for (a, b, result) in [
+ for (a, b, op) in [
# examples from https://fedoraproject.org/wiki/Archive:Tools/RPM/VersionComparison
- ("1.0010", "1.9", 1),
- ("1.05", "1.5", 0),
- ("1.0", "1", 1),
- ("2.50", "2.5", 1),
- ("fc4", "fc.4", 0),
- ("FC5", "fc4", -1),
- ("2a", "2.0", -1),
- ("1.0", "1.fc4", 1),
- ("3.0.0_fc", "3.0.0.fc", 0),
+ ("1.0010", "1.9", operator.gt),
+ ("1.05", "1.5", operator.eq),
+ ("1.0", "1", operator.gt),
+ ("2.50", "2.5", operator.gt),
+ ("fc4", "fc.4", operator.eq),
+ ("FC5", "fc4", operator.lt),
+ ("2a", "2.0", operator.lt),
+ ("1.0", "1.fc4", operator.gt),
+ ("3.0.0_fc", "3.0.0.fc", operator.eq),
# from RPM tests
- ("1.0", "1.0", 0),
- ("1.0", "2.0", -1),
- ("2.0", "1.0", 1),
- ("2.0.1", "2.0.1", 0),
- ("2.0", "2.0.1", -1),
- ("2.0.1", "2.0", 1),
- ("2.0.1a", "2.0.1a", 0),
- ("2.0.1a", "2.0.1", 1),
- ("2.0.1", "2.0.1a", -1),
- ("5.5p1", "5.5p1", 0),
- ("5.5p1", "5.5p2", -1),
- ("5.5p2", "5.5p1", 1),
- ("5.5p10", "5.5p10", 0),
- ("5.5p1", "5.5p10", -1),
- ("5.5p10", "5.5p1", 1),
- ("10xyz", "10.1xyz", -1),
- ("10.1xyz", "10xyz", 1),
- ("xyz10", "xyz10", 0),
- ("xyz10", "xyz10.1", -1),
- ("xyz10.1", "xyz10", 1),
- ("xyz.4", "xyz.4", 0),
- ("xyz.4", "8", -1),
- ("8", "xyz.4", 1),
- ("xyz.4", "2", -1),
- ("2", "xyz.4", 1),
- ("5.5p2", "5.6p1", -1),
- ("5.6p1", "5.5p2", 1),
- ("5.6p1", "6.5p1", -1),
- ("6.5p1", "5.6p1", 1),
- ("6.0.rc1", "6.0", 1),
- ("6.0", "6.0.rc1", -1),
- ("10b2", "10a1", 1),
- ("10a2", "10b2", -1),
- ("1.0aa", "1.0aa", 0),
- ("1.0a", "1.0aa", -1),
- ("1.0aa", "1.0a", 1),
- ("10.0001", "10.0001", 0),
- ("10.0001", "10.1", 0),
- ("10.1", "10.0001", 0),
- ("10.0001", "10.0039", -1),
- ("10.0039", "10.0001", 1),
- ("4.999.9", "5.0", -1),
- ("5.0", "4.999.9", 1),
- ("20101121", "20101121", 0),
- ("20101121", "20101122", -1),
- ("20101122", "20101121", 1),
- ("2_0", "2_0", 0),
- ("2.0", "2_0", 0),
- ("2_0", "2.0", 0),
- ("a", "a", 0),
- ("a+", "a+", 0),
- ("a+", "a_", 0),
- ("a_", "a+", 0),
- ("+a", "+a", 0),
- ("+a", "_a", 0),
- ("_a", "+a", 0),
- ("+_", "+_", 0),
- ("_+", "+_", 0),
- ("_+", "_+", 0),
- ("+", "_", 0),
- ("_", "+", 0),
+ ("1.0", "1.0", operator.eq),
+ ("1.0", "2.0", operator.lt),
+ ("2.0", "1.0", operator.gt),
+ ("2.0.1", "2.0.1", operator.eq),
+ ("2.0", "2.0.1", operator.lt),
+ ("2.0.1", "2.0", operator.gt),
+ ("2.0.1a", "2.0.1a", operator.eq),
+ ("2.0.1a", "2.0.1", operator.gt),
+ ("2.0.1", "2.0.1a", operator.lt),
+ ("5.5p1", "5.5p1", operator.eq),
+ ("5.5p1", "5.5p2", operator.lt),
+ ("5.5p2", "5.5p1", operator.gt),
+ ("5.5p10", "5.5p10", operator.eq),
+ ("5.5p1", "5.5p10", operator.lt),
+ ("5.5p10", "5.5p1", operator.gt),
+ ("10xyz", "10.1xyz", operator.lt),
+ ("10.1xyz", "10xyz", operator.gt),
+ ("xyz10", "xyz10", operator.eq),
+ ("xyz10", "xyz10.1", operator.lt),
+ ("xyz10.1", "xyz10", operator.gt),
+ ("xyz.4", "xyz.4", operator.eq),
+ ("xyz.4", "8", operator.lt),
+ ("8", "xyz.4", operator.gt),
+ ("xyz.4", "2", operator.lt),
+ ("2", "xyz.4", operator.gt),
+ ("5.5p2", "5.6p1", operator.lt),
+ ("5.6p1", "5.5p2", operator.gt),
+ ("5.6p1", "6.5p1", operator.lt),
+ ("6.5p1", "5.6p1", operator.gt),
+ ("6.0.rc1", "6.0", operator.gt),
+ ("6.0", "6.0.rc1", operator.lt),
+ ("10b2", "10a1", operator.gt),
+ ("10a2", "10b2", operator.lt),
+ ("1.0aa", "1.0aa", operator.eq),
+ ("1.0a", "1.0aa", operator.lt),
+ ("1.0aa", "1.0a", operator.gt),
+ ("10.0001", "10.0001", operator.eq),
+ ("10.0001", "10.1", operator.eq),
+ ("10.1", "10.0001", operator.eq),
+ ("10.0001", "10.0039", operator.lt),
+ ("10.0039", "10.0001", operator.gt),
+ ("4.999.9", "5.0", operator.lt),
+ ("5.0", "4.999.9", operator.gt),
+ ("20101121", "20101121", operator.eq),
+ ("20101121", "20101122", operator.lt),
+ ("20101122", "20101121", operator.gt),
+ ("2_0", "2_0", operator.eq),
+ ("2.0", "2_0", operator.eq),
+ ("2_0", "2.0", operator.eq),
+ ("a", "a", operator.eq),
+ ("a+", "a+", operator.eq),
+ ("a+", "a_", operator.eq),
+ ("a_", "a+", operator.eq),
+ ("+a", "+a", operator.eq),
+ ("+a", "_a", operator.eq),
+ ("_a", "+a", operator.eq),
+ ("+_", "+_", operator.eq),
+ ("_+", "+_", operator.eq),
+ ("_+", "_+", operator.eq),
+ ("+", "_", operator.eq),
+ ("_", "+", operator.eq),
# other tests
- ('0.99.beta19', '0.99.beta14', 1),
- ("1.0.0", "2.0.0", -1),
- (".0.0", "2.0.0", -1),
- ("alpha", "beta", -1),
- ("1.0", "1.0.0", -1),
- ("2.456", "2.1000", -1),
- ("2.1000", "3.111", -1),
- ("2.001", "2.1", 0),
- ("2.34", "2.34", 0),
- ("6.1.2", "6.3.8", -1),
- ("1.7.3.0", "2.0.0", -1),
- ("2.24.51", "2.25", -1),
- ("2.1.5+20120813+gitdcbe778", "2.1.5", 1),
- ("3.4.1", "3.4b1", 1),
- ("041206", "200090325", -1),
- ("0.6.2+git20130413", "0.6.2", 1),
- ("2.6.0+bzr6602", "2.6.0", 1),
- ("2.6.0", "2.6b2", 1),
- ("2.6.0+bzr6602", "2.6b2x", 1),
- ("0.6.7+20150214+git3a710f9", "0.6.7", 1),
- ("15.8b", "15.8.0.1", -1),
- ("1.2rc1", "1.2.0", -1),
+ ('0.99.beta19', '0.99.beta14', operator.gt),
+ ("1.0.0", "2.0.0", operator.lt),
+ (".0.0", "2.0.0", operator.lt),
+ ("alpha", "beta", operator.lt),
+ ("1.0", "1.0.0", operator.lt),
+ ("2.456", "2.1000", operator.lt),
+ ("2.1000", "3.111", operator.lt),
+ ("2.001", "2.1", operator.eq),
+ ("2.34", "2.34", operator.eq),
+ ("6.1.2", "6.3.8", operator.lt),
+ ("1.7.3.0", "2.0.0", operator.lt),
+ ("2.24.51", "2.25", operator.lt),
+ ("2.1.5+20120813+gitdcbe778", "2.1.5", operator.gt),
+ ("3.4.1", "3.4b1", operator.gt),
+ ("041206", "200090325", operator.lt),
+ ("0.6.2+git20130413", "0.6.2", operator.gt),
+ ("2.6.0+bzr6602", "2.6.0", operator.gt),
+ ("2.6.0", "2.6b2", operator.gt),
+ ("2.6.0+bzr6602", "2.6b2x", operator.gt),
+ ("0.6.7+20150214+git3a710f9", "0.6.7", operator.gt),
+ ("15.8b", "15.8.0.1", operator.lt),
+ ("1.2rc1", "1.2.0", operator.lt),
]:
ver_a = Version(a)
ver_b = Version(b)
- self.assertEqual(ver_a.__cmp__(ver_b), result)
- self.assertEqual(ver_b.__cmp__(ver_a), -result)
+ if op is operator.eq:
+ for o, name in [(op, 'eq'), (operator.ge, 'ge'), (operator.le, 'le')]:
+ self.assertTrue(o(ver_a, ver_b), '{} {} {}'.format(ver_a, name, ver_b))
+ if op is operator.lt:
+ for o, name in [(op, 'lt'), (operator.le, 'le'), (operator.ne, 'ne')]:
+ self.assertTrue(o(ver_a, ver_b), '{} {} {}'.format(ver_a, name, ver_b))
+ for o, name in [(operator.gt, 'gt'), (operator.ge, 'ge'), (operator.eq, 'eq')]:
+ self.assertFalse(o(ver_a, ver_b), '{} {} {}'.format(ver_a, name, ver_b))
+ if op is operator.gt:
+ for o, name in [(op, 'gt'), (operator.ge, 'ge'), (operator.ne, 'ne')]:
+ self.assertTrue(o(ver_a, ver_b), '{} {} {}'.format(ver_a, name, ver_b))
+ for o, name in [(operator.lt, 'lt'), (operator.le, 'le'), (operator.eq, 'eq')]:
+ self.assertFalse(o(ver_a, ver_b), '{} {} {}'.format(ver_a, name, ver_b))
def test_msvc_toolset_version(self):
'''
diff --git a/test cases/unit/55 introspection/meson.build b/test cases/unit/55 introspection/meson.build
index 95a7c5f..f11d64d 100644
--- a/test cases/unit/55 introspection/meson.build
+++ b/test cases/unit/55 introspection/meson.build
@@ -20,9 +20,19 @@ endif
subdir('sharedlib')
subdir('staticlib')
-t1 = executable('test1', 't1.cpp', link_with: [sharedlib], install: true, build_by_default: get_option('test_opt2'))
-t2 = executable('test2', 't2.cpp', link_with: [staticlib])
-t3 = executable('test3', 't3.cpp', link_with: [sharedlib, staticlib], dependencies: [dep1])
+var1 = '1'
+var2 = 2.to_string()
+var3 = 'test3'
+
+t1 = executable('test' + var1, ['t1.cpp'], link_with: [sharedlib], install: true, build_by_default: get_option('test_opt2'))
+t2 = executable('test@0@'.format('@0@'.format(var2)), 't2.cpp', link_with: [staticlib])
+t3 = executable(var3, 't3.cpp', link_with: [sharedlib, staticlib], dependencies: [dep1])
+
+### BEGIN: Test inspired by taisei: https://github.com/taisei-project/taisei/blob/master/meson.build#L293
+systype = '@0@'.format(host_machine.system())
+systype = '@0@, @1@, @2@'.format(systype, host_machine.cpu_family(), host_machine.cpu())
+message(systype)
+### END: Test inspired by taisei
test('test case 1', t1)
test('test case 2', t2)