diff options
47 files changed, 388 insertions, 269 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 056612d..d86d825 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -518,6 +518,8 @@ be passed to [shared and static libraries](#library). depends on such as a symbol visibility map. The purpose is to automatically trigger a re-link (but not a re-compile) of the target when this file changes. +- `link_language` since 0.51.0 makes the linker for this target + be for the specified language. This is helpful for multi-language targets. - `link_whole` links all contents of the given static libraries whether they are used by not, equivalent to the `-Wl,--whole-archive` argument flag of GCC, available since 0.40.0. @@ -568,7 +570,7 @@ be passed to [shared and static libraries](#library). the keyword argument for the default behaviour. - `override_options` takes an array of strings in the same format as `project`'s `default_options` overriding the values of these options - for this target only, since 0.40.0 + for this target only, since 0.40.0. - `gnu_symbol_visibility` specifies how symbols should be exported, see e.g [the GCC Wiki](https://gcc.gnu.org/wiki/Visibility) for more information. This value can either be an empty string or one of diff --git a/docs/markdown/snippets/link_language.md b/docs/markdown/snippets/link_language.md new file mode 100644 index 0000000..28ebe8b --- /dev/null +++ b/docs/markdown/snippets/link_language.md @@ -0,0 +1,10 @@ +## New target keyword argument: `link_language` +There may be situations for which the user wishes to manually specify the linking language. +For example, a C++ target may link C, Fortran, etc. and perhaps the automatic detection in Meson does not pick the desired compiler. +The user can manually choose the linker by language per-target like this example of a target where one wishes to link with the Fortran compiler: +```meson +executable(..., link_language : 'fortran') +``` + +A specific case this option fixes is where for example the main program is Fortran that calls C and/or C++ code. +The automatic language detection of Meson prioritizes C/C++, and so an compile-time error results like `undefined reference to main`, because the linker is C or C++ instead of Fortran, which is fixed by this per-target override. diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index 49d531f..b6ec450 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -122,7 +122,7 @@ class IntrospectionInterpreter(AstInterpreter): subi.analyze() subi.project_data['name'] = dirname self.project_data['subprojects'] += [subi.project_data] - except: + except (mesonlib.MesonException, RuntimeError): return def func_add_languages(self, node, args, kwargs): @@ -173,9 +173,9 @@ class IntrospectionInterpreter(AstInterpreter): arg_node = curr.args elif isinstance(curr, IdNode): # Try to resolve the ID and append the node to the queue - id = curr.value - if id in self.assignments and self.assignments[id]: - tmp_node = self.assignments[id][0] + var_name = curr.value + if var_name in self.assignments and self.assignments[var_name]: + tmp_node = self.assignments[var_name][0] if isinstance(tmp_node, (ArrayNode, IdNode, FunctionNode)): srcqueue += [tmp_node] elif isinstance(curr, ArithmeticNode): diff --git a/mesonbuild/ast/visitor.py b/mesonbuild/ast/visitor.py index c8769d4..fab4ed2 100644 --- a/mesonbuild/ast/visitor.py +++ b/mesonbuild/ast/visitor.py @@ -134,7 +134,5 @@ class AstVisitor: self.visit_default_func(node) for i in node.arguments: i.accept(self) - for i in node.commas: - pass for val in node.kwargs.values(): val.accept(self) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index e40bcbc..d0b4bb5 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -322,12 +322,14 @@ class Backend: raise MesonException('Unknown data type in object list.') return obj_list - def serialize_executable(self, tname, exe, cmd_args, workdir, env={}, + def serialize_executable(self, tname, exe, cmd_args, workdir, env=None, extra_paths=None, capture=None): ''' Serialize an executable for running with a generator or a custom target ''' import hashlib + if env is None: + env = {} if extra_paths is None: # The callee didn't check if we needed extra paths, so check it here if mesonlib.is_windows() or mesonlib.is_cygwin(): diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 9a6fdad..7eafcad 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -406,9 +406,9 @@ int dummy; } } ''' - id = target.get_id() + tid = target.get_id() lang = comp.get_language() - tgt = self.introspection_data[id] + tgt = self.introspection_data[tid] # Find an existing entry or create a new one id_hash = (lang, tuple(parameters)) src_block = tgt.get(id_hash, None) @@ -520,7 +520,7 @@ int dummy; # This will be set as dependencies of all the target's sources. At the # same time, also deal with generated sources that need to be compiled. generated_source_files = [] - for rel_src, gensrc in generated_sources.items(): + for rel_src in generated_sources.keys(): dirpart, fnamepart = os.path.split(rel_src) raw_src = File(True, dirpart, fnamepart) if self.environment.is_source(rel_src) and not self.environment.is_header(rel_src): @@ -585,7 +585,7 @@ int dummy; obj_list.append(self.generate_single_compile(target, src, 'vala', [], header_deps)) # Generate compile targets for all the pre-existing sources for this target - for f, src in target_sources.items(): + for src in target_sources.values(): if not self.environment.is_header(src): if self.environment.is_llvm_ir(src): obj_list.append(self.generate_llvm_ir_compile(target, src)) @@ -912,7 +912,7 @@ int dummy; # Add possible java generated files to src list generated_sources = self.get_target_generated_sources(target) gen_src_list = [] - for rel_src, gensrc in generated_sources.items(): + for rel_src in generated_sources.keys(): dirpart, fnamepart = os.path.split(rel_src) raw_src = File(True, dirpart, fnamepart) if rel_src.endswith('.java'): @@ -1041,7 +1041,7 @@ int dummy; def generate_single_java_compile(self, src, target, compiler, args): deps = [os.path.join(self.get_target_dir(l), l.get_filename()) for l in target.link_targets] generated_sources = self.get_target_generated_sources(target) - for rel_src, gensrc in generated_sources.items(): + for rel_src in generated_sources.keys(): if rel_src.endswith('.java'): deps.append(rel_src) rel_src = src.rel_to_builddir(self.build_to_src) @@ -1227,7 +1227,7 @@ int dummy; if len(target.install_dir) > 3 and target.install_dir[3] is True: target.install_dir[3] = os.path.join(self.environment.get_datadir(), 'gir-1.0') # Detect gresources and add --gresources arguments for each - for (gres, gensrc) in other_src[1].items(): + for gensrc in other_src[1].values(): if isinstance(gensrc, modules.GResourceTarget): gres_xml, = self.get_custom_target_sources(gensrc) args += ['--gresources=' + gres_xml] @@ -2751,7 +2751,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) return super().get_introspection_data(target_id, target) result = [] - for _, i in self.introspection_data[target_id].items(): + for i in self.introspection_data[target_id].values(): result += [i] return result diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index afbd59b..d25798e 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -312,7 +312,7 @@ class Vs2010Backend(backends.Backend): target = self.build.targets[prj[0]] lang = 'default' if hasattr(target, 'compilers') and target.compilers: - for (lang_out, _) in target.compilers.items(): + for lang_out in target.compilers.keys(): lang = lang_out break prj_line = prj_templ % ( @@ -386,7 +386,7 @@ class Vs2010Backend(backends.Backend): for p in projlist: if p[1].parent != PurePath('.'): ofile.write("\t\t{%s} = {%s}\n" % (p[2], self.subdirs[p[1].parent][0])) - for (_, subdir) in self.subdirs.items(): + for subdir in self.subdirs.values(): if subdir[1]: ofile.write("\t\t{%s} = {%s}\n" % (subdir[0], subdir[1])) ofile.write('\tEndGlobalSection\n') @@ -463,7 +463,7 @@ class Vs2010Backend(backends.Backend): def add_target_deps(self, root, target): target_dict = {target.get_id(): target} - for name, dep in self.get_target_deps(target_dict).items(): + for dep in self.get_target_deps(target_dict).values(): if dep.get_id() in self.handled_target_deps[target.get_id()]: # This dependency was already handled manually. continue diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 7dd3674..be22c78 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -345,7 +345,7 @@ class XCodeBackend(backends.Backend): self.ofile.write('/* End PBXFileReference section */\n') def generate_pbx_frameworks_buildphase(self): - for tname, t in self.build.targets.items(): + for t in self.build.targets.values(): self.ofile.write('\n/* Begin PBXFrameworksBuildPhase section */\n') self.write_line('%s /* %s */ = {\n' % (t.buildphasemap['Frameworks'], 'Frameworks')) self.indent_level += 1 @@ -587,7 +587,7 @@ class XCodeBackend(backends.Backend): def generate_pbx_sources_build_phase(self): self.ofile.write('\n/* Begin PBXSourcesBuildPhase section */\n') - for name, phase_id in self.source_phase.items(): + for name in self.source_phase.keys(): t = self.build.targets[name] self.write_line('%s /* Sources */ = {' % (t.buildphasemap[name])) self.indent_level += 1 diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 1fad9e0..603e0d0 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import List import copy, os, re from collections import OrderedDict import itertools, pathlib @@ -88,7 +89,7 @@ known_build_target_kwargs = ( rust_kwargs | cs_kwargs) -known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic', 'pie'} +known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic', 'link_language', 'pie'} known_shlib_kwargs = known_build_target_kwargs | {'version', 'soversion', 'vs_module_defs', 'darwin_versions'} known_shmod_kwargs = known_build_target_kwargs known_stlib_kwargs = known_build_target_kwargs | {'pic'} @@ -425,7 +426,7 @@ a hard error in the future.''' % name) self.option_overrides = self.parse_overrides(kwargs) - def parse_overrides(self, kwargs): + def parse_overrides(self, kwargs) -> dict: result = {} overrides = stringlistify(kwargs.get('override_options', [])) for o in overrides: @@ -437,7 +438,7 @@ a hard error in the future.''' % name) result[k] = v return result - def is_linkable_target(self): + def is_linkable_target(self) -> bool: return False class BuildTarget(Target): @@ -454,6 +455,7 @@ class BuildTarget(Target): self.objects = [] self.external_deps = [] self.include_dirs = [] + self.link_language = kwargs.get('link_language') self.link_targets = [] self.link_whole_targets = [] self.link_depends = [] @@ -571,6 +573,9 @@ class BuildTarget(Target): else: compilers = self.environment.coredata.compilers + # did user override clink_langs for this target? + link_langs = [self.link_language] if self.link_language else clink_langs + # If this library is linked against another library we need to consider # the languages of those libraries as well. if self.link_targets or self.link_whole_targets: @@ -579,7 +584,7 @@ class BuildTarget(Target): if isinstance(t, CustomTarget) or isinstance(t, CustomTargetIndex): continue # We can't know anything about these. for name, compiler in t.compilers.items(): - if name in clink_langs: + if name in link_langs: extra.add((name, compiler)) for name, compiler in sorted(extra, key=lambda p: sort_clink(p[0])): self.compilers[name] = compiler @@ -588,7 +593,7 @@ class BuildTarget(Target): # No source files or parent targets, target consists of only object # files of unknown origin. Just add the first clink compiler # that we have and hope that it can link these objects - for lang in clink_langs: + for lang in link_langs: if lang in compilers: self.compilers[lang] = compilers[lang] break @@ -1149,7 +1154,7 @@ You probably should put it in link_with instead.''') def get_aliases(self): return {} - def get_langs_used_by_deps(self): + def get_langs_used_by_deps(self) -> List[str]: ''' Sometimes you want to link to a C++ library that exports C API, which means the linker must link in the C++ stdlib, and we must use a C++ @@ -1159,6 +1164,11 @@ You probably should put it in link_with instead.''') See: https://github.com/mesonbuild/meson/issues/1653 ''' langs = [] + + # User specified link_language of target (for multi-language targets) + if self.link_language: + return [self.link_language] + # Check if any of the external libraries were written in this language for dep in self.external_deps: if dep.language is None: @@ -1173,6 +1183,7 @@ You probably should put it in link_with instead.''') for language in link_target.compilers: if language not in langs: langs.append(language) + return langs def get_clink_dynamic_linker_and_stdlibs(self): diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 54ca894..1aeb637 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -368,7 +368,7 @@ class CCompiler(Compiler): return self.compiles(code.format(**fargs), env, extra_args=extra_args, dependencies=dependencies) - def has_header(self, hname, prefix, env, *, extra_args=None, dependencies=None): + def has_header(self, hname, prefix, env, *, extra_args=None, dependencies=None, disable_cache=False): fargs = {'prefix': prefix, 'header': hname} code = '''{prefix} #ifdef __has_include @@ -379,7 +379,7 @@ class CCompiler(Compiler): #include <{header}> #endif''' return self.compiles(code.format(**fargs), env, extra_args=extra_args, - dependencies=dependencies, mode='preprocess') + dependencies=dependencies, mode='preprocess', disable_cache=disable_cache) def has_header_symbol(self, hname, symbol, prefix, env, *, extra_args=None, dependencies=None): fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol} @@ -444,17 +444,19 @@ class CCompiler(Compiler): args += extra_args return args - def compiles(self, code, env, *, extra_args=None, dependencies=None, mode='compile'): - with self._build_wrapper(code, env, extra_args, dependencies, mode) as p: - return p.returncode == 0 + def compiles(self, code, env, *, extra_args=None, dependencies=None, mode='compile', disable_cache=False): + with self._build_wrapper(code, env, extra_args, dependencies, mode, disable_cache=disable_cache) as p: + return p.returncode == 0, p.cached - def _build_wrapper(self, code, env, extra_args, dependencies=None, mode='compile', want_output=False): + def _build_wrapper(self, code, env, extra_args, dependencies=None, mode='compile', want_output=False, disable_cache=False): args = self._get_compiler_check_args(env, extra_args, dependencies, mode) - return self.compile(code, args, mode, want_output=want_output) + if disable_cache or want_output: + return self.compile(code, extra_args=args, mode=mode, want_output=want_output) + return self.cached_compile(code, env.coredata, extra_args=args, mode=mode) - def links(self, code, env, *, extra_args=None, dependencies=None): + def links(self, code, env, *, extra_args=None, dependencies=None, disable_cache=False): return self.compiles(code, env, extra_args=extra_args, - dependencies=dependencies, mode='link') + dependencies=dependencies, mode='link', disable_cache=disable_cache) def run(self, code: str, env, *, extra_args=None, dependencies=None): if self.is_cross and self.exe_wrapper is None: @@ -487,7 +489,7 @@ class CCompiler(Compiler): {prefix} int main() {{ static int a[1-2*!({expression})]; a[0]=0; return 0; }}''' return self.compiles(t.format(**fargs), env, extra_args=extra_args, - dependencies=dependencies) + dependencies=dependencies)[0] def cross_compute_int(self, expression, low, high, guess, prefix, env, extra_args, dependencies): # Try user's guess first @@ -567,7 +569,7 @@ class CCompiler(Compiler): {type} something; }}''' if not self.compiles(t.format(**fargs), env, extra_args=extra_args, - dependencies=dependencies): + dependencies=dependencies)[0]: return -1 return self.cross_compute_int('sizeof(%s)' % typename, None, None, None, prefix, env, extra_args, dependencies) @@ -602,7 +604,7 @@ class CCompiler(Compiler): {type} something; }}''' if not self.compiles(t.format(**fargs), env, extra_args=extra_args, - dependencies=dependencies): + dependencies=dependencies)[0]: return -1 t = '''#include <stddef.h> {prefix} @@ -641,7 +643,7 @@ class CCompiler(Compiler): raise EnvironmentException('Could not determine alignment of %s. Sorry. You might want to file a bug.' % typename) return align - def get_define(self, dname, prefix, env, extra_args, dependencies): + def get_define(self, dname, prefix, env, extra_args, dependencies, disable_cache=False): delim = '"MESON_GET_DEFINE_DELIMITER"' fargs = {'prefix': prefix, 'define': dname, 'delim': delim} code = ''' @@ -652,13 +654,17 @@ class CCompiler(Compiler): {delim}\n{define}''' args = self._get_compiler_check_args(env, extra_args, dependencies, mode='preprocess').to_native() - with self.compile(code.format(**fargs), args, 'preprocess') as p: + func = lambda: self.cached_compile(code.format(**fargs), env.coredata, extra_args=args, mode='preprocess') + if disable_cache: + func = lambda: self.compile(code.format(**fargs), extra_args=args, mode='preprocess') + with func() as p: + cached = p.cached if p.returncode != 0: raise EnvironmentException('Could not get define {!r}'.format(dname)) # Get the preprocessed value after the delimiter, # minus the extra newline at the end and # merge string literals. - return CCompiler.concatenate_string_literals(p.stdo.split(delim + '\n')[-1][:-1]) + return CCompiler.concatenate_string_literals(p.stdo.split(delim + '\n')[-1][:-1]), cached def get_return_value(self, fname, rtype, prefix, env, extra_args, dependencies): if rtype == 'string': @@ -762,7 +768,7 @@ class CCompiler(Compiler): val = env.properties.host.get(varname, None) if val is not None: if isinstance(val, bool): - return val + return val, False raise EnvironmentException('Cross variable {0} is not a boolean.'.format(varname)) fargs = {'prefix': prefix, 'func': funcname} @@ -792,13 +798,14 @@ class CCompiler(Compiler): head, main = self._no_prototype_templ() templ = head + stubs_fail + main - if self.links(templ.format(**fargs), env, extra_args=extra_args, - dependencies=dependencies): - return True + res, cached = self.links(templ.format(**fargs), env, extra_args=extra_args, + dependencies=dependencies) + if res: + return True, cached # MSVC does not have compiler __builtin_-s. if self.get_id() == 'msvc': - return False + return False, False # Detect function as a built-in # @@ -1020,7 +1027,7 @@ class CCompiler(Compiler): libname in self.internal_libs): args = ['-l' + libname] largs = self.linker_to_compiler_args(self.get_allow_undefined_link_args()) - if self.links(code, env, extra_args=(args + largs)): + if self.links(code, env, extra_args=(args + largs), disable_cache=True)[0]: return args # Don't do a manual search for internal libs if libname in self.internal_libs: @@ -1036,7 +1043,7 @@ class CCompiler(Compiler): elf_class = 2 else: elf_class = 1 - except: + except (MesonException, KeyError): # TODO evaluate if catching KeyError is wanted here elf_class = 0 # Search in the specified dirs, and then in the system libraries for d in itertools.chain(extra_dirs, self.get_library_dirs(env, elf_class)): @@ -1109,7 +1116,7 @@ class CCompiler(Compiler): # then we must also pass -L/usr/lib to pick up libSystem.dylib extra_args = [] if allow_system else ['-Z', '-L/usr/lib'] link_args += ['-framework', name] - if self.links(code, env, extra_args=(extra_args + link_args)): + if self.links(code, env, extra_args=(extra_args + link_args), disable_cache=True)[0]: return link_args def find_framework_impl(self, name, env, extra_dirs, allow_system): @@ -1176,7 +1183,7 @@ class CCompiler(Compiler): fatal_warnings_args = ['-Wl,--fatal-warnings'] if self.has_fatal_warnings_link_arg is None: self.has_fatal_warnings_link_arg = False - self.has_fatal_warnings_link_arg = self.has_multi_link_arguments(fatal_warnings_args, env) + self.has_fatal_warnings_link_arg = self.has_multi_link_arguments(fatal_warnings_args, env)[0] if self.has_fatal_warnings_link_arg: args = fatal_warnings_args + args @@ -1201,7 +1208,7 @@ class CCompiler(Compiler): if not (for_windows(env.is_cross_build(), env) or for_cygwin(env.is_cross_build(), env)): if name in ['dllimport', 'dllexport']: - return False + return False, False # Clang and GCC both return warnings if the __attribute__ is undefined, # so set -Werror @@ -1348,7 +1355,7 @@ class ElbrusCCompiler(GnuCCompiler, ElbrusCompiler): # So we should explicitly fail at this case. def has_function(self, funcname, prefix, env, *, extra_args=None, dependencies=None): if funcname == 'lchmod': - return False + return False, False else: return super().has_function(funcname, prefix, env, extra_args=extra_args, @@ -1609,8 +1616,8 @@ class VisualStudioCCompiler(CCompiler): args = args + ['-Werror=unknown-argument'] with self._build_wrapper(code, env, extra_args=args, mode=mode) as p: if p.returncode != 0: - return False - return not(warning_text in p.stde or warning_text in p.stdo) + return False, p.cached + return not(warning_text in p.stde or warning_text in p.stdo), p.cached def get_compile_debugfile_args(self, rel_obj, pch=False): pdbarr = rel_obj.split('.')[:-1] @@ -1706,7 +1713,7 @@ class VisualStudioCCompiler(CCompiler): def has_func_attribute(self, name, env): # MSVC doesn't have __attribute__ like Clang and GCC do, so just return # false without compiling anything - return name in ['dllimport', 'dllexport'] + return name in ['dllimport', 'dllexport'], False def get_argument_syntax(self): return 'msvc' diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 04cc31a..2f3c7b7 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -875,8 +875,6 @@ class Compiler: # Libraries that are internal compiler implementations, and must not be # manually searched. internal_libs = () - # Cache for the result of compiler checks which can be cached - compiler_check_cache = {} def __init__(self, exelist, version, **kwargs): if isinstance(exelist, str): @@ -903,7 +901,7 @@ class Compiler: return repr_str.format(self.__class__.__name__, self.version, ' '.join(self.exelist)) - def can_compile(self, src): + def can_compile(self, src) -> bool: if hasattr(src, 'fname'): src = src.fname suffix = os.path.splitext(src)[1].lower() @@ -911,40 +909,40 @@ class Compiler: return True return False - def get_id(self): + def get_id(self) -> str: return self.id - def get_version_string(self): + def get_version_string(self) -> str: details = [self.id, self.version] if self.full_version: details += ['"%s"' % (self.full_version)] return '(%s)' % (' '.join(details)) - def get_language(self): + def get_language(self) -> str: return self.language - def get_display_language(self): + def get_display_language(self) -> str: return self.language.capitalize() - def get_default_suffix(self): + def get_default_suffix(self) -> str: return self.default_suffix - def get_define(self, dname, prefix, env, extra_args, dependencies): + def get_define(self, dname, prefix, env, extra_args, dependencies) -> Tuple[str, bool]: raise EnvironmentException('%s does not support get_define ' % self.get_id()) - def compute_int(self, expression, low, high, guess, prefix, env, extra_args, dependencies): + def compute_int(self, expression, low, high, guess, prefix, env, extra_args, dependencies) -> int: raise EnvironmentException('%s does not support compute_int ' % self.get_id()) def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): raise EnvironmentException('%s does not support compute_parameters_with_absolute_paths ' % self.get_id()) - def has_members(self, typename, membernames, prefix, env, *, extra_args=None, dependencies=None): + def has_members(self, typename, membernames, prefix, env, *, extra_args=None, dependencies=None) -> Tuple[bool, bool]: raise EnvironmentException('%s does not support has_member(s) ' % self.get_id()) - def has_type(self, typename, prefix, env, extra_args, *, dependencies=None): + def has_type(self, typename, prefix, env, extra_args, *, dependencies=None) -> Tuple[bool, bool]: raise EnvironmentException('%s does not support has_type ' % self.get_id()) - def symbols_have_underscore_prefix(self, env): + def symbols_have_underscore_prefix(self, env) -> bool: raise EnvironmentException('%s does not support symbols_have_underscore_prefix ' % self.get_id()) def get_exelist(self): @@ -1087,31 +1085,31 @@ class Compiler: def get_option_link_args(self, options): return [] - def check_header(self, *args, **kwargs): + def check_header(self, *args, **kwargs) -> Tuple[bool, bool]: raise EnvironmentException('Language %s does not support header checks.' % self.get_display_language()) - def has_header(self, *args, **kwargs): + def has_header(self, *args, **kwargs) -> Tuple[bool, bool]: raise EnvironmentException('Language %s does not support header checks.' % self.get_display_language()) - def has_header_symbol(self, *args, **kwargs): + def has_header_symbol(self, *args, **kwargs) -> Tuple[bool, bool]: raise EnvironmentException('Language %s does not support header symbol checks.' % self.get_display_language()) - def compiles(self, *args, **kwargs): + def compiles(self, *args, **kwargs) -> Tuple[bool, bool]: raise EnvironmentException('Language %s does not support compile checks.' % self.get_display_language()) - def links(self, *args, **kwargs): + def links(self, *args, **kwargs) -> Tuple[bool, bool]: raise EnvironmentException('Language %s does not support link checks.' % self.get_display_language()) - def run(self, *args, **kwargs): + def run(self, *args, **kwargs) -> RunResult: raise EnvironmentException('Language %s does not support run checks.' % self.get_display_language()) - def sizeof(self, *args, **kwargs): + def sizeof(self, *args, **kwargs) -> int: raise EnvironmentException('Language %s does not support sizeof checks.' % self.get_display_language()) - def alignment(self, *args, **kwargs): + def alignment(self, *args, **kwargs) -> int: raise EnvironmentException('Language %s does not support alignment checks.' % self.get_display_language()) - def has_function(self, *args, **kwargs): + def has_function(self, *args, **kwargs) -> Tuple[bool, bool]: raise EnvironmentException('Language %s does not support function checks.' % self.get_display_language()) @classmethod @@ -1125,12 +1123,12 @@ class Compiler: def get_library_dirs(self, *args, **kwargs): return () - def has_multi_arguments(self, args, env): + def has_multi_arguments(self, args, env) -> Tuple[bool, bool]: raise EnvironmentException( 'Language {} does not support has_multi_arguments.'.format( self.get_display_language())) - def has_multi_link_arguments(self, args, env): + def has_multi_link_arguments(self, args, env) -> Tuple[bool, bool]: raise EnvironmentException( 'Language {} does not support has_multi_link_arguments.'.format( self.get_display_language())) @@ -1150,21 +1148,7 @@ class Compiler: @contextlib.contextmanager def compile(self, code, extra_args=None, mode='link', want_output=False): if extra_args is None: - textra_args = None extra_args = [] - else: - textra_args = tuple(extra_args) - key = (code, textra_args, mode) - if not want_output: - if key in self.compiler_check_cache: - p = self.compiler_check_cache[key] - mlog.debug('Using cached compile:') - mlog.debug('Cached command line: ', ' '.join(p.commands), '\n') - mlog.debug('Code:\n', code) - mlog.debug('Cached compiler stdout:\n', p.stdo) - mlog.debug('Cached compiler stderr:\n', p.stde) - yield p - return try: with tempfile.TemporaryDirectory() as tmpdirname: if isinstance(code, str): @@ -1206,8 +1190,7 @@ class Compiler: p.input_name = srcname if want_output: p.output_name = output - else: - self.compiler_check_cache[key] = p + p.cached = False # Make sure that the cached attribute always exists yield p except (PermissionError, OSError): # On Windows antivirus programs and the like hold on to files so @@ -1215,6 +1198,39 @@ class Compiler: # catch OSError because the directory is then no longer empty. pass + @contextlib.contextmanager + def cached_compile(self, code, cdata: coredata.CoreData, extra_args=None, mode: str = 'link'): + assert(isinstance(cdata, coredata.CoreData)) + + # Calculate the key + textra_args = tuple(extra_args) if extra_args is not None else None + key = (tuple(self.exelist), self.version, code, textra_args, mode) + + # Check if not cached + if key not in cdata.compiler_check_cache: + with self.compile(code, extra_args=extra_args, mode=mode, want_output=False) as p: + # Remove all attributes except the following + # This way the object can be serialized + tokeep = ['args', 'commands', 'input_name', 'output_name', + 'pid', 'returncode', 'stdo', 'stde', 'text_mode'] + todel = [x for x in vars(p).keys() if x not in tokeep] + for i in todel: + delattr(p, i) + p.cached = False + cdata.compiler_check_cache[key] = p + yield p + return + + # Return cached + p = cdata.compiler_check_cache[key] + p.cached = True + mlog.debug('Using cached compile:') + mlog.debug('Cached command line: ', ' '.join(p.commands), '\n') + mlog.debug('Code:\n', code) + mlog.debug('Cached compiler stdout:\n', p.stdo) + mlog.debug('Cached compiler stderr:\n', p.stde) + yield p + def get_colorout_args(self, colortype): return [] diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 2b2c4a0..e2bcaf0 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -64,10 +64,11 @@ class CPPCompiler(CCompiler): def has_header_symbol(self, hname, symbol, prefix, env, *, extra_args=None, dependencies=None): # Check if it's a C-like symbol - if super().has_header_symbol(hname, symbol, prefix, env, - extra_args=extra_args, - dependencies=dependencies): - return True + found, cached = super().has_header_symbol(hname, symbol, prefix, env, + extra_args=extra_args, + dependencies=dependencies) + if found: + return True, cached # Check if it's a class or a template if extra_args is None: extra_args = [] @@ -263,7 +264,7 @@ class ElbrusCPPCompiler(GnuCPPCompiler, ElbrusCompiler): # So we should explicitly fail at this case. def has_function(self, funcname, prefix, env, *, extra_args=None, dependencies=None): if funcname == 'lchmod': - return False + return False, False else: return super().has_function(funcname, prefix, env, extra_args=extra_args, diff --git a/mesonbuild/compilers/cs.py b/mesonbuild/compilers/cs.py index cd67da0..c6355f2 100644 --- a/mesonbuild/compilers/cs.py +++ b/mesonbuild/compilers/cs.py @@ -28,10 +28,10 @@ cs_optimization_args = {'0': [], } class CsCompiler(Compiler): - def __init__(self, exelist, version, id, runner=None): + def __init__(self, exelist, version, comp_id, runner=None): self.language = 'cs' super().__init__(exelist, version) - self.id = id + self.id = comp_id self.is_cross = False self.runner = runner diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index 21fa498..51a1300 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -146,8 +146,9 @@ class CudaCompiler(Compiler): return super().get_compiler_check_args() + [] def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None): - if super().has_header_symbol(hname, symbol, prefix, env, extra_args, dependencies): - return True + result, cached = super().has_header_symbol(hname, symbol, prefix, env, extra_args, dependencies) + if result: + return True, cached if extra_args is None: extra_args = [] fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol} diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 529919b..46cc054 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -326,8 +326,8 @@ class DCompiler(Compiler): def compiles(self, code, env, *, extra_args=None, dependencies=None, mode='compile'): args = self._get_compiler_check_args(env, extra_args, dependencies, mode) - with self.compile(code, args, mode) as p: - return p.returncode == 0 + with self.cached_compile(code, env.coredata, extra_args=args, mode=mode) as p: + return p.returncode == 0, p.cached def has_multi_arguments(self, args, env): return self.compiles('int i;\n', env, extra_args=args) diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index dd54fd0..b4eb327 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -250,16 +250,16 @@ class FortranCompiler(Compiler): def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'): return CCompiler._get_compiler_check_args(self, env, extra_args, dependencies, mode=mode) - def compiles(self, code, env, *, extra_args=None, dependencies=None, mode='compile'): + def compiles(self, code, env, *, extra_args=None, dependencies=None, mode='compile', disable_cache=False): return CCompiler.compiles(self, code, env, extra_args=extra_args, - dependencies=dependencies, mode=mode) + dependencies=dependencies, mode=mode, disable_cache=disable_cache) - def _build_wrapper(self, code, env, extra_args, dependencies=None, mode='compile', want_output=False): - return CCompiler._build_wrapper(self, code, env, extra_args, dependencies, mode, want_output) + def _build_wrapper(self, code, env, extra_args, dependencies=None, mode='compile', want_output=False, disable_cache=False): + return CCompiler._build_wrapper(self, code, env, extra_args, dependencies, mode, want_output, disable_cache=disable_cache) - def links(self, code, env, *, extra_args=None, dependencies=None): + def links(self, code, env, *, extra_args=None, dependencies=None, disable_cache=False): return CCompiler.links(self, code, env, extra_args=extra_args, - dependencies=dependencies) + dependencies=dependencies, disable_cache=disable_cache) def run(self, code, env, *, extra_args=None, dependencies=None): return CCompiler.run(self, code, env, extra_args=extra_args, dependencies=dependencies) @@ -297,11 +297,11 @@ class FortranCompiler(Compiler): def has_multi_arguments(self, args, env): return CCompiler.has_multi_arguments(self, args, env) - def has_header(self, hname, prefix, env, *, extra_args=None, dependencies=None): - return CCompiler.has_header(self, hname, prefix, env, extra_args=extra_args, dependencies=dependencies) + def has_header(self, hname, prefix, env, *, extra_args=None, dependencies=None, disable_cache=False): + return CCompiler.has_header(self, hname, prefix, env, extra_args=extra_args, dependencies=dependencies, disable_cache=disable_cache) - def get_define(self, dname, prefix, env, extra_args, dependencies): - return CCompiler.get_define(self, dname, prefix, env, extra_args, dependencies) + def get_define(self, dname, prefix, env, extra_args, dependencies, disable_cache=False): + return CCompiler.get_define(self, dname, prefix, env, extra_args, dependencies, disable_cache=disable_cache) @classmethod def _get_trials_from_pattern(cls, pattern, directory, libname): @@ -333,7 +333,6 @@ class GnuFortranCompiler(GnuCompiler, FortranCompiler): def language_stdlib_only_link_flags(self): return ['-lgfortran', '-lm'] - class ElbrusFortranCompiler(GnuFortranCompiler, ElbrusCompiler): def __init__(self, exelist, version, compiler_type, is_cross, exe_wrapper=None, defines=None, **kwargs): GnuFortranCompiler.__init__(self, exelist, version, compiler_type, is_cross, exe_wrapper, defines, **kwargs) @@ -427,6 +426,9 @@ class PGIFortranCompiler(PGICompiler, FortranCompiler): FortranCompiler.__init__(self, exelist, version, is_cross, exe_wrapper, **kwags) PGICompiler.__init__(self, compiler_type) + def language_stdlib_only_link_flags(self) -> List[str]: + return ['-lpgf90rtl', '-lpgf90', '-lpgf90_rpm1', '-lpgf902', + '-lpgf90rtl', '-lpgftnrtl', '-lrt'] class FlangFortranCompiler(ClangCompiler, FortranCompiler): def __init__(self, exelist, version, is_cross, exe_wrapper=None, **kwags): diff --git a/mesonbuild/compilers/vala.py b/mesonbuild/compilers/vala.py index 98b8b42..c0b2a68 100644 --- a/mesonbuild/compilers/vala.py +++ b/mesonbuild/compilers/vala.py @@ -96,7 +96,7 @@ class ValaCompiler(Compiler): extra_flags += self.get_compile_only_args() else: extra_flags += environment.coredata.get_external_link_args(for_machine, self.language) - with self.compile(code, extra_flags, 'compile') as p: + with self.cached_compile(code, environment.coredata, extra_args=extra_flags, mode='compile') as p: if p.returncode != 0: msg = 'Vala compiler {!r} can not compile programs' \ ''.format(self.name_string()) @@ -121,7 +121,7 @@ class ValaCompiler(Compiler): args = env.coredata.get_external_args(for_machine, self.language) vapi_args = ['--pkg', libname] args += vapi_args - with self.compile(code, args, 'compile') as p: + with self.cached_compile(code, env.coredata, extra_args=args, mode='compile') as p: if p.returncode == 0: return vapi_args # Not found? Try to find the vapi file itself. diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index b836086..183b333 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -247,6 +247,7 @@ class CoreData: self.compilers = OrderedDict() self.cross_compilers = OrderedDict() self.deps = OrderedDict() + self.compiler_check_cache = OrderedDict() # Only to print a warning if it changes between Meson invocations. self.config_files = self.__load_config_files(options.native_file, 'native') self.libdir_cross_fixup() diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 8432ab6..7a10d69 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -940,9 +940,11 @@ class CMakeTraceLine: return s.format(self.file, self.line, self.func, self.args) class CMakeTarget: - def __init__(self, name, type, properies = {}): + def __init__(self, name, target_type, properies=None): + if properies is None: + properies = {} self.name = name - self.type = type + self.type = target_type self.properies = properies def __repr__(self): @@ -1123,7 +1125,7 @@ class CMakeDependency(ExternalDependency): for l in lexer1: if l.func == 'set': self._cmake_set(l) - except: + except MesonException: return None # Extract the variables and sanity check them diff --git a/mesonbuild/dependencies/boost.py b/mesonbuild/dependencies/boost.py index 46a479e..0de1372 100644 --- a/mesonbuild/dependencies/boost.py +++ b/mesonbuild/dependencies/boost.py @@ -250,7 +250,7 @@ class BoostDependency(ExternalDependency): def detect_headers_and_version(self): try: - version = self.clib_compiler.get_define('BOOST_LIB_VERSION', '#include <boost/version.hpp>', self.env, self.get_compile_args(), []) + version = self.clib_compiler.get_define('BOOST_LIB_VERSION', '#include <boost/version.hpp>', self.env, self.get_compile_args(), [], disable_cache=True)[0] except mesonlib.EnvironmentException: return except TypeError: diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 55cb569..72ba7b3 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -368,7 +368,7 @@ class OpenMPDependency(ExternalDependency): self.is_found = False try: openmp_date = self.clib_compiler.get_define( - '_OPENMP', '', self.env, self.clib_compiler.openmp_flags(), [self]) + '_OPENMP', '', self.env, self.clib_compiler.openmp_flags(), [self], disable_cache=True)[0] except mesonlib.EnvironmentException as e: mlog.debug('OpenMP support not available in the compiler') mlog.debug(e) @@ -376,7 +376,7 @@ class OpenMPDependency(ExternalDependency): if openmp_date: self.version = self.VERSIONS[openmp_date] - if self.clib_compiler.has_header('omp.h', '', self.env, dependencies=[self]): + if self.clib_compiler.has_header('omp.h', '', self.env, dependencies=[self], disable_cache=True)[0]: self.is_found = True self.compile_args = self.link_args = self.clib_compiler.openmp_flags() else: diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index ce1ca68..b1fa632 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -298,8 +298,8 @@ class QtBaseDependency(ExternalDependency): # the Qt + m_name there is not a symlink, it's a file mod_private_dir = qt_inc_dir mod_private_inc = _qt_get_private_includes(mod_private_dir, m_name, m.version) - for dir in mod_private_inc: - self.compile_args.append('-I' + dir) + for directory in mod_private_inc: + self.compile_args.append('-I' + directory) self.link_args += m.get_link_args() if 'Core' in modules: @@ -402,8 +402,8 @@ class QtBaseDependency(ExternalDependency): if self.private_headers: priv_inc = self.get_private_includes(mincdir, module) - for dir in priv_inc: - self.compile_args.append('-I' + dir) + for directory in priv_inc: + self.compile_args.append('-I' + directory) libfile = self.clib_compiler.find_library(self.qtpkgname + module + modules_lib_suffix, self.env, libdir) @@ -641,7 +641,7 @@ class VulkanDependency(ExternalDependency): else: # simply try to guess it, usually works on linux libs = self.clib_compiler.find_library('vulkan', environment, []) - if libs is not None and self.clib_compiler.has_header('vulkan/vulkan.h', '', environment): + if libs is not None and self.clib_compiler.has_header('vulkan/vulkan.h', '', environment, disable_cache=True)[0]: self.type_name = 'system' self.is_found = True for lib in libs: diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py index 977d930..70f964e 100644 --- a/mesonbuild/envconfig.py +++ b/mesonbuild/envconfig.py @@ -134,7 +134,7 @@ class Properties(HasEnvVarFallback): def get_sys_root(self) -> typing.Optional[typing.Union[str, typing.List[str]]]: return self.properties.get('sys_root', None) - def __eq__(self, other: typing.Any) -> typing.Union[bool, 'NotImplemented']: + def __eq__(self, other: typing.Any) -> 'typing.Union[bool, NotImplemented]': if isinstance(other, type(self)): return self.properties == other.properties return NotImplemented @@ -159,7 +159,7 @@ class MachineInfo: self.endian = endian self.is_64_bit = cpu_family in CPU_FAMILES_64_BIT # type: bool - def __eq__(self, other: typing.Any) -> typing.Union[bool, 'NotImplemented']: + def __eq__(self, other: typing.Any) -> 'typing.Union[bool, NotImplemented]': if self.__class__ is not other.__class__: return NotImplemented return \ @@ -168,7 +168,7 @@ class MachineInfo: self.cpu == other.cpu and \ self.endian == other.endian - def __ne__(self, other: typing.Any) -> typing.Union[bool, 'NotImplemented']: + def __ne__(self, other: typing.Any) -> 'typing.Union[bool, NotImplemented]': if self.__class__ is not other.__class__: return NotImplemented return not self.__eq__(other) @@ -198,7 +198,7 @@ class MachineInfo: """ Machine is windows? """ - return self.system == 'windows' + return self.system in {'windows', 'mingw'} def is_cygwin(self) -> bool: """ @@ -216,7 +216,7 @@ class MachineInfo: """ Machine is Darwin (iOS/OS X)? """ - return self.system in ('darwin', 'ios') + return self.system in {'darwin', 'ios'} def is_android(self) -> bool: """ diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index a3505a4..ba97083 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -1134,16 +1134,17 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('Prefix argument of has_member must be a string.') extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs) - had = self.compiler.has_members(typename, [membername], prefix, - self.environment, - extra_args=extra_args, - dependencies=deps) + had, cached = self.compiler.has_members(typename, [membername], prefix, + self.environment, + extra_args=extra_args, + dependencies=deps) + cached = '(cached)' if cached else '' if had: hadtxt = mlog.green('YES') else: hadtxt = mlog.red('NO') mlog.log('Checking whether type', mlog.bold(typename, True), - 'has member', mlog.bold(membername, True), msg, hadtxt) + 'has member', mlog.bold(membername, True), msg, hadtxt, cached) return had @permittedKwargs({ @@ -1163,17 +1164,18 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('Prefix argument of has_members must be a string.') extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs) - had = self.compiler.has_members(typename, membernames, prefix, - self.environment, - extra_args=extra_args, - dependencies=deps) + had, cached = self.compiler.has_members(typename, membernames, prefix, + self.environment, + extra_args=extra_args, + dependencies=deps) + cached = '(cached)' if cached else '' if had: hadtxt = mlog.green('YES') else: hadtxt = mlog.red('NO') members = mlog.bold(', '.join(['"{}"'.format(m) for m in membernames])) mlog.log('Checking whether type', mlog.bold(typename, True), - 'has members', members, msg, hadtxt) + 'has members', members, msg, hadtxt, cached) return had @permittedKwargs({ @@ -1193,14 +1195,15 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('Prefix argument of has_function must be a string.') extra_args = self.determine_args(kwargs) deps, msg = self.determine_dependencies(kwargs) - had = self.compiler.has_function(funcname, prefix, self.environment, - extra_args=extra_args, - dependencies=deps) + had, cached = self.compiler.has_function(funcname, prefix, self.environment, + extra_args=extra_args, + dependencies=deps) + cached = '(cached)' if cached else '' if had: hadtxt = mlog.green('YES') else: hadtxt = mlog.red('NO') - mlog.log('Checking for function', mlog.bold(funcname, True), msg, hadtxt) + mlog.log('Checking for function', mlog.bold(funcname, True), msg, hadtxt, cached) return had @permittedKwargs({ @@ -1220,13 +1223,14 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('Prefix argument of has_type must be a string.') extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs) - had = self.compiler.has_type(typename, prefix, self.environment, - extra_args=extra_args, dependencies=deps) + had, cached = self.compiler.has_type(typename, prefix, self.environment, + extra_args=extra_args, dependencies=deps) + cached = '(cached)' if cached else '' if had: hadtxt = mlog.green('YES') else: hadtxt = mlog.red('NO') - mlog.log('Checking for type', mlog.bold(typename, True), msg, hadtxt) + mlog.log('Checking for type', mlog.bold(typename, True), msg, hadtxt, cached) return had @FeatureNew('compiler.compute_int', '0.40.0') @@ -1305,10 +1309,11 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('Prefix argument of get_define() must be a string.') extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs) - value = self.compiler.get_define(element, prefix, self.environment, - extra_args=extra_args, - dependencies=deps) - mlog.log('Fetching value of define', mlog.bold(element, True), msg, value) + value, cached = self.compiler.get_define(element, prefix, self.environment, + extra_args=extra_args, + dependencies=deps) + cached = '(cached)' if cached else '' + mlog.log('Fetching value of define', mlog.bold(element, True), msg, value, cached) return value @permittedKwargs({ @@ -1332,15 +1337,16 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('Testname argument must be a string.') extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs, endl=None) - result = self.compiler.compiles(code, self.environment, - extra_args=extra_args, - dependencies=deps) + result, cached = self.compiler.compiles(code, self.environment, + extra_args=extra_args, + dependencies=deps) if len(testname) > 0: if result: h = mlog.green('YES') else: h = mlog.red('NO') - mlog.log('Checking if', mlog.bold(testname, True), msg, 'compiles:', h) + cached = '(cached)' if cached else '' + mlog.log('Checking if', mlog.bold(testname, True), msg, 'compiles:', h, cached) return result @permittedKwargs({ @@ -1364,15 +1370,16 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('Testname argument must be a string.') extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs, endl=None) - result = self.compiler.links(code, self.environment, - extra_args=extra_args, - dependencies=deps) + result, cached = self.compiler.links(code, self.environment, + extra_args=extra_args, + dependencies=deps) + cached = '(cached)' if cached else '' if len(testname) > 0: if result: h = mlog.green('YES') else: h = mlog.red('NO') - mlog.log('Checking if', mlog.bold(testname, True), msg, 'links:', h) + mlog.log('Checking if', mlog.bold(testname, True), msg, 'links:', h, cached) return result @FeatureNew('compiler.check_header', '0.47.0') @@ -1392,16 +1399,17 @@ class CompilerHolder(InterpreterObject): return False extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs) - haz = self.compiler.check_header(hname, prefix, self.environment, - extra_args=extra_args, - dependencies=deps) + haz, cached = self.compiler.check_header(hname, prefix, self.environment, + extra_args=extra_args, + dependencies=deps) + cached = '(cached)' if cached else '' if required and not haz: raise InterpreterException('{} header {!r} not usable'.format(self.compiler.get_display_language(), hname)) elif haz: h = mlog.green('YES') else: h = mlog.red('NO') - mlog.log('Check usable header', mlog.bold(hname, True), msg, h) + mlog.log('Check usable header', mlog.bold(hname, True), msg, h, cached) return haz @FeatureNewKwargs('compiler.has_header', '0.50.0', ['required']) @@ -1420,15 +1428,16 @@ class CompilerHolder(InterpreterObject): return False extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs) - haz = self.compiler.has_header(hname, prefix, self.environment, - extra_args=extra_args, dependencies=deps) + haz, cached = self.compiler.has_header(hname, prefix, self.environment, + extra_args=extra_args, dependencies=deps) + cached = '(cached)' if cached else '' if required and not haz: raise InterpreterException('{} header {!r} not found'.format(self.compiler.get_display_language(), hname)) elif haz: h = mlog.green('YES') else: h = mlog.red('NO') - mlog.log('Has header', mlog.bold(hname, True), msg, h) + mlog.log('Has header', mlog.bold(hname, True), msg, h, cached) return haz @FeatureNewKwargs('compiler.has_header_symbol', '0.50.0', ['required']) @@ -1447,16 +1456,17 @@ class CompilerHolder(InterpreterObject): return False extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs) - haz = self.compiler.has_header_symbol(hname, symbol, prefix, self.environment, - extra_args=extra_args, - dependencies=deps) + haz, cached = self.compiler.has_header_symbol(hname, symbol, prefix, self.environment, + extra_args=extra_args, + dependencies=deps) if required and not haz: raise InterpreterException('{} symbol {} not found in header {}'.format(self.compiler.get_display_language(), symbol, hname)) elif haz: h = mlog.green('YES') else: h = mlog.red('NO') - mlog.log('Header <{0}> has symbol'.format(hname), mlog.bold(symbol, True), msg, h) + cached = '(cached)' if cached else '' + mlog.log('Header <{0}> has symbol'.format(hname), mlog.bold(symbol, True), msg, h, cached) return haz def notfound_library(self, libname): @@ -1518,15 +1528,16 @@ class CompilerHolder(InterpreterObject): @permittedKwargs({}) def has_multi_arguments_method(self, args, kwargs): args = mesonlib.stringlistify(args) - result = self.compiler.has_multi_arguments(args, self.environment) + result, cached = self.compiler.has_multi_arguments(args, self.environment) if result: h = mlog.green('YES') else: h = mlog.red('NO') + cached = '(cached)' if cached else '' mlog.log( 'Compiler for {} supports arguments {}:'.format( self.compiler.get_display_language(), ' '.join(args)), - h) + h, cached) return result @FeatureNew('compiler.get_supported_arguments', '0.43.0') @@ -1560,7 +1571,8 @@ class CompilerHolder(InterpreterObject): @permittedKwargs({}) def has_multi_link_arguments_method(self, args, kwargs): args = mesonlib.stringlistify(args) - result = self.compiler.has_multi_link_arguments(args, self.environment) + result, cached = self.compiler.has_multi_link_arguments(args, self.environment) + cached = '(cached)' if cached else '' if result: h = mlog.green('YES') else: @@ -1568,7 +1580,7 @@ class CompilerHolder(InterpreterObject): mlog.log( 'Compiler for {} supports link arguments {}:'.format( self.compiler.get_display_language(), ' '.join(args)), - h) + h, cached) return result @FeatureNew('compiler.get_supported_link_arguments_method', '0.46.0') @@ -1597,9 +1609,10 @@ class CompilerHolder(InterpreterObject): args = mesonlib.stringlistify(args) if len(args) != 1: raise InterpreterException('has_func_attribute takes exactly one argument.') - result = self.compiler.has_func_attribute(args[0], self.environment) + result, cached = self.compiler.has_func_attribute(args[0], self.environment) + cached = '(cached)' if cached else '' h = mlog.green('YES') if result else mlog.red('NO') - mlog.log('Compiler for {} supports function attribute {}:'.format(self.compiler.get_display_language(), args[0]), h) + mlog.log('Compiler for {} supports function attribute {}:'.format(self.compiler.get_display_language(), args[0]), h, cached) return result @FeatureNew('compiler.get_supported_function_attributes', '0.48.0') @@ -2193,7 +2206,7 @@ class Interpreter(InterpreterBase): def check_cross_stdlibs(self): if self.build.environment.is_cross_build(): props = self.build.environment.properties.host - for l, c in self.build.cross_compilers.items(): + for l in self.build.cross_compilers.keys(): try: di = mesonlib.stringlistify(props.get_stdlib(l)) if len(di) != 2: @@ -2454,7 +2467,7 @@ external dependencies (including libraries) must go to "dependencies".''') with mlog.nested(): # Suppress the 'ERROR:' prefix because this exception is not # fatal and VS CI treat any logs with "ERROR:" as fatal. - mlog.exception(e, prefix=None) + mlog.exception(e, prefix=mlog.yellow('Exception:')) mlog.log('\nSubproject', mlog.bold(dirname), 'is buildable:', mlog.red('NO'), '(disabling)') return self.disabled_subproject(dirname) raise e diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py index c148cbd..71a4ef3 100644 --- a/mesonbuild/interpreterbase.py +++ b/mesonbuild/interpreterbase.py @@ -144,7 +144,7 @@ def stringArgs(f): return wrapped def noArgsFlattening(f): - setattr(f, 'no-args-flattening', True) + setattr(f, 'no-args-flattening', True) # noqa: B010 return f def disablerIfNotFound(f): diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index 1e776e4..f78fa35 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -720,11 +720,11 @@ def has_path_sep(name, sep='/\\'): return True return False -def do_replacement(regex, line, format, confdata): +def do_replacement(regex, line, variable_format, confdata): missing_variables = set() start_tag = '@' backslash_tag = '\\@' - if format == 'cmake': + if variable_format == 'cmake': start_tag = '${' backslash_tag = '\\${' @@ -777,7 +777,7 @@ def do_mesondefine(line, confdata): raise MesonException('#mesondefine argument "%s" is of unknown type.' % varname) -def do_conf_file(src, dst, confdata, format, encoding='utf-8'): +def do_conf_file(src, dst, confdata, variable_format, encoding='utf-8'): try: with open(src, encoding=encoding, newline='') as f: data = f.readlines() @@ -785,15 +785,15 @@ def do_conf_file(src, dst, confdata, format, encoding='utf-8'): raise MesonException('Could not read input file %s: %s' % (src, str(e))) # Only allow (a-z, A-Z, 0-9, _, -) as valid characters for a define # Also allow escaping '@' with '\@' - if format in ['meson', 'cmake@']: + if variable_format in ['meson', 'cmake@']: regex = re.compile(r'(?:\\\\)+(?=\\?@)|\\@|@([-a-zA-Z0-9_]+)@') - elif format == 'cmake': + elif variable_format == 'cmake': regex = re.compile(r'(?:\\\\)+(?=\\?\$)|\\\${|\${([-a-zA-Z0-9_]+)}') else: - raise MesonException('Format "{}" not handled'.format(format)) + raise MesonException('Format "{}" not handled'.format(variable_format)) search_token = '#mesondefine' - if format != 'meson': + if variable_format != 'meson': search_token = '#cmakedefine' result = [] @@ -806,7 +806,7 @@ def do_conf_file(src, dst, confdata, format, encoding='utf-8'): confdata_useless = False line = do_mesondefine(line, confdata) else: - line, missing = do_replacement(regex, line, format, confdata) + line, missing = do_replacement(regex, line, variable_format, confdata) missing_variables.update(missing) if missing: confdata_useless = False @@ -920,7 +920,8 @@ def extract_as_list(dict_object, *keys, pop=False, **kwargs): result.append(listify(fetch(key, []), **kwargs)) return result -def typeslistify(item: typing.Union[_T, typing.List[_T]], types: typing.Union[typing.Type[_T], typing.Tuple[typing.Type[_T]]]) -> typing.List[_T]: +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 diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 4326c20..c94f1bf 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -41,41 +41,41 @@ class CommandLineParser: self.subparsers = self.parser.add_subparsers(title='Commands', description='If no command is specified it defaults to setup command.') self.add_command('setup', msetup.add_arguments, msetup.run, - help='Configure the project') + help_msg='Configure the project') self.add_command('configure', mconf.add_arguments, mconf.run, - help='Change project options',) + help_msg='Change project options',) self.add_command('install', minstall.add_arguments, minstall.run, - help='Install the project') + help_msg='Install the project') self.add_command('introspect', mintro.add_arguments, mintro.run, - help='Introspect project') + help_msg='Introspect project') self.add_command('init', minit.add_arguments, minit.run, - help='Create a new project') + help_msg='Create a new project') self.add_command('test', mtest.add_arguments, mtest.run, - help='Run tests') + help_msg='Run tests') self.add_command('wrap', wraptool.add_arguments, wraptool.run, - help='Wrap tools') + help_msg='Wrap tools') self.add_command('subprojects', msubprojects.add_arguments, msubprojects.run, - help='Manage subprojects') + help_msg='Manage subprojects') self.add_command('help', self.add_help_arguments, self.run_help_command, - help='Print help of a subcommand') + help_msg='Print help of a subcommand') self.add_command('rewrite', lambda parser: rewriter.add_arguments(parser, self.formater), rewriter.run, - help='Modify the project definition') + help_msg='Modify the project definition') # Hidden commands self.add_command('runpython', self.add_runpython_arguments, self.run_runpython_command, - help=argparse.SUPPRESS) + help_msg=argparse.SUPPRESS) self.add_command('unstable-coredata', munstable_coredata.add_arguments, munstable_coredata.run, - help=argparse.SUPPRESS) + help_msg=argparse.SUPPRESS) - def add_command(self, name, add_arguments_func, run_func, help, aliases=None): + def add_command(self, name, add_arguments_func, run_func, help_msg, aliases=None): aliases = aliases or [] # FIXME: Cannot have hidden subparser: # https://bugs.python.org/issue22848 - if help == argparse.SUPPRESS: + if help_msg == argparse.SUPPRESS: p = argparse.ArgumentParser(prog='meson ' + name, formatter_class=self.formater) self.hidden_commands.append(name) else: - p = self.subparsers.add_parser(name, help=help, aliases=aliases, formatter_class=self.formater) + p = self.subparsers.add_parser(name, help=help_msg, aliases=aliases, formatter_class=self.formater) add_arguments_func(p) p.set_defaults(run_func=run_func) for i in [name] + aliases: diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index c6b6bbf..ed82c37 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -65,7 +65,7 @@ class DirMaker: def __enter__(self): return self - def __exit__(self, type, value, traceback): + def __exit__(self, exception_type, value, traceback): self.dirs.reverse() for d in self.dirs: append_to_log(self.lf, d) @@ -93,7 +93,7 @@ def set_chown(path, user=None, group=None, dir_fd=None, follow_symlinks=True): dir_fd=dir_fd, follow_symlinks=follow_symlinks) shutil.chown(path, user, group) - except: + except Exception: raise finally: os.chown = real_os_chown diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index c47fffd..8c8aa15 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -117,11 +117,11 @@ def list_installed(installdata): for t in installdata.targets: res[os.path.join(installdata.build_dir, t.fname)] = \ os.path.join(installdata.prefix, t.outdir, os.path.basename(t.fname)) - for path, installpath, unused_prefix in installdata.data: + for path, installpath, _ in installdata.data: res[path] = os.path.join(installdata.prefix, installpath) - for path, installdir, unused_custom_install_mode in installdata.headers: + for path, installdir, _ in installdata.headers: res[path] = os.path.join(installdata.prefix, installdir, os.path.basename(path)) - for path, installpath, unused_custom_install_mode in installdata.man: + for path, installpath, _ in installdata.man: res[path] = os.path.join(installdata.prefix, installpath) return res diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py index e8ee6c8..79dee47 100644 --- a/mesonbuild/mlog.py +++ b/mesonbuild/mlog.py @@ -19,6 +19,7 @@ import time import platform from contextlib import contextmanager import typing +from typing import Any, Generator, List, Optional, Sequence, TextIO, Union """This is (mostly) a standalone module used to write logging information about Meson runs. Some output goes to screen, @@ -43,11 +44,11 @@ if platform.system().lower() == 'windows': 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 # type: typing.Optional[str] -log_file = None # type: typing.Optional[typing.TextIO] +log_dir = None # type: Optional[str] +log_file = None # type: Optional[TextIO] log_fname = 'meson-log.txt' # type: str log_depth = 0 # type: int -log_timestamp_start = None # type: typing.Optional[float] +log_timestamp_start = None # type: Optional[float] log_fatal_warnings = False # type: bool log_disable_stdout = False # type: bool log_errors_only = False # type: bool @@ -78,7 +79,7 @@ def set_timestamp_start(start: float) -> None: global log_timestamp_start log_timestamp_start = start -def shutdown() -> typing.Optional[str]: +def shutdown() -> Optional[str]: global log_file if log_file is not None: path = log_file.name @@ -124,8 +125,8 @@ def cyan(text: str) -> AnsiDecorator: # 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] +def process_markup(args: Sequence[Union[AnsiDecorator, str]], keep: bool) -> List[str]: + arr = [] # type: List[str] if log_timestamp_start is not None: arr = ['[{:.3f}]'.format(time.monotonic() - log_timestamp_start)] for arg in args: @@ -139,7 +140,7 @@ def process_markup(args: typing.Sequence[typing.Union[AnsiDecorator, str]], keep arr.append(str(arg)) return arr -def force_print(*args: str, **kwargs: typing.Any) -> None: +def force_print(*args: str, **kwargs: Any) -> None: global log_disable_stdout if log_disable_stdout: return @@ -160,14 +161,14 @@ def force_print(*args: str, **kwargs: typing.Any) -> None: print(cleaned, end='') # 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: +def debug(*args: Union[str, AnsiDecorator], **kwargs: Any) -> None: arr = process_markup(args, False) if log_file is not None: print(*arr, file=log_file, **kwargs) log_file.flush() -def log(*args: typing.Union[str, AnsiDecorator], is_error: bool = False, - **kwargs: typing.Any) -> None: +def log(*args: Union[str, AnsiDecorator], is_error: bool = False, + **kwargs: Any) -> None: global log_errors_only arr = process_markup(args, False) if log_file is not None: @@ -178,7 +179,7 @@ def log(*args: typing.Union[str, AnsiDecorator], is_error: bool = False, if not log_errors_only or is_error: force_print(*arr, **kwargs) -def _log_error(severity: str, *rargs: typing.Union[str, AnsiDecorator], **kwargs: typing.Any) -> None: +def _log_error(severity: str, *rargs: Union[str, AnsiDecorator], **kwargs: Any) -> None: from .mesonlib import get_error_location_string from .environment import build_filename from .mesonlib import MesonException @@ -186,7 +187,7 @@ def _log_error(severity: str, *rargs: typing.Union[str, AnsiDecorator], **kwargs # 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': - label = [yellow('WARNING:')] # type: typing.List[typing.Union[str, AnsiDecorator]] + label = [yellow('WARNING:')] # type: List[Union[str, AnsiDecorator]] elif severity == 'error': label = [red('ERROR:')] elif severity == 'deprecation': @@ -202,7 +203,7 @@ def _log_error(severity: str, *rargs: typing.Union[str, AnsiDecorator], **kwargs location_str = get_error_location_string(location_file, location.lineno) # 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]) + location_list = typing.cast(List[Union[str, AnsiDecorator]], [location_str]) args = location_list + args log(*args, **kwargs) @@ -211,18 +212,20 @@ def _log_error(severity: str, *rargs: typing.Union[str, AnsiDecorator], **kwargs if log_fatal_warnings: raise MesonException("Fatal warnings enabled, aborting") -def error(*args: typing.Union[str, AnsiDecorator], **kwargs: typing.Any) -> None: +def error(*args: Union[str, AnsiDecorator], **kwargs: Any) -> None: return _log_error('error', *args, **kwargs, is_error=True) -def warning(*args: typing.Union[str, AnsiDecorator], **kwargs: typing.Any) -> None: +def warning(*args: Union[str, AnsiDecorator], **kwargs: Any) -> None: return _log_error('warning', *args, **kwargs, is_error=True) -def deprecation(*args: typing.Union[str, AnsiDecorator], **kwargs: typing.Any) -> None: +def deprecation(*args: Union[str, AnsiDecorator], **kwargs: Any) -> None: return _log_error('deprecation', *args, **kwargs, is_error=True) -def exception(e: Exception, prefix: AnsiDecorator = red('ERROR:')) -> None: +def exception(e: Exception, prefix: Optional[AnsiDecorator] = None) -> None: + if prefix is None: + prefix = red('ERROR:') log() - args = [] # type: typing.List[typing.Union[AnsiDecorator, str]] + args = [] # type: List[Union[AnsiDecorator, str]] if hasattr(e, 'file') and hasattr(e, 'lineno') and hasattr(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. @@ -234,19 +237,19 @@ def exception(e: Exception, prefix: AnsiDecorator = red('ERROR:')) -> None: # 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_: typing.List[str]) -> str: - l = len(list_) +def format_list(input_list: List[str]) -> str: + l = len(input_list) if l > 2: - return ' and '.join([', '.join(list_[:-1]), list_[-1]]) + return ' and '.join([', '.join(input_list[:-1]), input_list[-1]]) elif l == 2: - return ' and '.join(list_) + return ' and '.join(input_list) elif l == 1: - return list_[0] + return input_list[0] else: return '' @contextmanager -def nested() -> typing.Generator[None, None, None]: +def nested() -> Generator[None, None, None]: global log_depth log_depth += 1 try: diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index a0ebe0e..04941ea 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -477,9 +477,9 @@ class PythonModule(ExtensionModule): ver = {'python2': '-2', 'python3': '-3'}[name_or_path] cmd = ['py', ver, '-c', "import sysconfig; print(sysconfig.get_config_var('BINDIR'))"] _, stdout, _ = mesonlib.Popen_safe(cmd) - dir = stdout.strip() - if os.path.exists(dir): - return os.path.join(dir, 'python') + directory = stdout.strip() + if os.path.exists(directory): + return os.path.join(directory, 'python') else: return None diff --git a/mesonbuild/modules/unstable_simd.py b/mesonbuild/modules/unstable_simd.py index 18a1099..2f2f67f 100644 --- a/mesonbuild/modules/unstable_simd.py +++ b/mesonbuild/modules/unstable_simd.py @@ -66,7 +66,7 @@ class SimdModule(ExtensionModule): mlog.log('Compiler supports %s:' % iset, mlog.red('NO')) continue if args: - if not compiler.has_multi_arguments(args, state.environment): + if not compiler.has_multi_arguments(args, state.environment)[0]: mlog.log('Compiler supports %s:' % iset, mlog.red('NO')) continue mlog.log('Compiler supports %s:' % iset, mlog.green('YES')) diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py index e8d266e..87a83fe 100644 --- a/mesonbuild/modules/windows.py +++ b/mesonbuild/modules/windows.py @@ -59,7 +59,7 @@ class WindowsModule(ExtensionModule): if not rescomp.found(): raise MesonException('Could not find Windows resource compiler') - for (arg, match, type) in [ + for (arg, match, rc_type) in [ ('/?', '^.*Microsoft.*Resource Compiler.*$', ResourceCompilerType.rc), ('--version', '^.*GNU windres.*$', ResourceCompilerType.windres), ]: @@ -67,7 +67,7 @@ class WindowsModule(ExtensionModule): m = re.search(match, o, re.MULTILINE) if m: mlog.log('Windows resource compiler: %s' % m.group()) - self._rescomp = (rescomp, type) + self._rescomp = (rescomp, rc_type) break else: raise MesonException('Could not determine type of Windows resource compiler') diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py index 4305a7c..b4fb032 100644 --- a/mesonbuild/mparser.py +++ b/mesonbuild/mparser.py @@ -217,7 +217,7 @@ class BaseNode: fname = 'visit_{}'.format(type(self).__name__) if hasattr(visitor, fname): func = getattr(visitor, fname) - if hasattr(func, '__call__'): + if callable(func): func(self) class ElementaryNode(BaseNode): diff --git a/mesonbuild/munstable_coredata.py b/mesonbuild/munstable_coredata.py index aaf6523..f16468c 100644 --- a/mesonbuild/munstable_coredata.py +++ b/mesonbuild/munstable_coredata.py @@ -31,9 +31,12 @@ def dump_compilers(compilers): print(' ' + lang + ':') print(' Id: ' + compiler.id) print(' Command: ' + ' '.join(compiler.exelist)) - print(' Full version: ' + compiler.full_version) - print(' Detected version: ' + compiler.version) - print(' Detected type: ' + repr(compiler.compiler_type)) + if compiler.full_version: + print(' Full version: ' + compiler.full_version) + if compiler.version: + print(' Detected version: ' + compiler.version) + if hasattr(compiler, 'compiler_type'): + print(' Detected type: ' + repr(compiler.compiler_type)) #pprint.pprint(compiler.__dict__) @@ -51,7 +54,7 @@ def run(options): 'change the working directory to it.') return 1 - all = options.all + all_backends = options.all print('This is a dump of the internal unstable cache of meson. This is for debugging only.') print('Do NOT parse, this will change from version to version in incompatible ways') @@ -64,18 +67,18 @@ def run(options): # use `meson configure` to view these pass elif k in ['install_guid', 'test_guid', 'regen_guid']: - if all or backend.startswith('vs'): + if all_backends or backend.startswith('vs'): print(k + ': ' + v) elif k == 'target_guids': - if all or backend.startswith('vs'): + if all_backends or backend.startswith('vs'): print(k + ':') dump_guids(v) elif k in ['lang_guids']: - if all or backend.startswith('vs') or backend == 'xcode': + if all_backends or backend.startswith('vs') or backend == 'xcode': print(k + ':') dump_guids(v) elif k == 'meson_command': - if all or backend.startswith('vs'): + if all_backends or backend.startswith('vs'): print('Meson command used in build file regeneration: ' + ' '.join(v)) elif k == 'pkgconf_envvar': print('Last seen PKGCONFIG enviroment variable value: ' + v) @@ -97,7 +100,7 @@ def run(options): native = [] cross = [] for dep_key, dep in sorted(v.items()): - if dep_key[2]: + if dep_key[1]: cross.append((dep_key, dep)) else: native.append((dep_key, dep)) diff --git a/mesonbuild/scripts/depfixer.py b/mesonbuild/scripts/depfixer.py index 7294186..cc4669c 100644 --- a/mesonbuild/scripts/depfixer.py +++ b/mesonbuild/scripts/depfixer.py @@ -123,7 +123,7 @@ class Elf(DataSizes): self.parse_header() self.parse_sections() self.parse_dynamic() - except: + except (struct.error, RuntimeError): self.bf.close() raise @@ -180,7 +180,7 @@ class Elf(DataSizes): def parse_sections(self): self.bf.seek(self.e_shoff) self.sections = [] - for i in range(self.e_shnum): + for _ in range(self.e_shnum): self.sections.append(SectionHeader(self.bf, self.ptrsize, self.is_le)) def read_str(self): diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index 4d9d032..3eb68a7 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -84,7 +84,7 @@ class PackageDefinition: try: self.config = configparser.ConfigParser(interpolation=None) self.config.read(fname) - except: + except configparser.Error: raise WrapException('Failed to parse {}'.format(self.basename)) if len(self.config.sections()) < 1: raise WrapException('Missing sections in {}'.format(self.basename)) @@ -338,7 +338,7 @@ class Resolver: """ Copy directory tree. Overwrites also read only files. """ - for src_dir, dirs, files in os.walk(root_src_dir): + for src_dir, _, files in os.walk(root_src_dir): dst_dir = src_dir.replace(root_src_dir, root_dst_dir, 1) if not os.path.exists(dst_dir): os.makedirs(dst_dir) diff --git a/mesonbuild/wrap/wraptool.py b/mesonbuild/wrap/wraptool.py index 132decf..80cc027 100644 --- a/mesonbuild/wrap/wraptool.py +++ b/mesonbuild/wrap/wraptool.py @@ -177,7 +177,7 @@ def promote(options): # check if the argument is a full path to a subproject directory or wrap file system_native_path_argument = argument.replace('/', os.sep) - for _, matches in sprojs.items(): + for matches in sprojs.values(): if system_native_path_argument in matches: do_promotion(system_native_path_argument, spdir_name) return diff --git a/run_project_tests.py b/run_project_tests.py index c1d42fc..324d824 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -505,6 +505,10 @@ def skippable(suite, test): if test.endswith('netcdf'): return True + # MSVC doesn't link with GFortran + if test.endswith('14 fortran links c'): + return True + # No frameworks test should be skipped on linux CI, as we expect all # prerequisites to be installed if mesonlib.is_linux(): @@ -774,7 +778,7 @@ def detect_system_compiler(): try: comp = env.compiler_from_language(lang, env.is_cross_build()) details = '%s %s' % (' '.join(comp.get_exelist()), comp.get_version_string()) - except: + except mesonlib.MesonException: comp = None details = 'not found' print('%-7s: %s' % (lang, details)) @@ -819,7 +823,7 @@ if __name__ == '__main__': print(l, '\n') except UnicodeError: print(l.encode('ascii', errors='replace').decode(), '\n') - for name, dirs, skip in all_tests: + for name, dirs, _ in all_tests: dirs = (x.name for x in dirs) for k, g in itertools.groupby(dirs, key=lambda x: x.split()[0]): tests = list(g) diff --git a/run_tests.py b/run_tests.py index fb3bc28..a4b0fa2 100755 --- a/run_tests.py +++ b/run_tests.py @@ -136,7 +136,7 @@ def find_vcxproj_with_target(builddir, target): p = r'<TargetName>{}</TargetName>\s*<TargetExt>\{}</TargetExt>'.format(t, ext) else: p = r'<TargetName>{}</TargetName>'.format(t) - for root, dirs, files in os.walk(builddir): + for _, _, files in os.walk(builddir): for f in fnmatch.filter(files, '*.vcxproj'): f = os.path.join(builddir, f) with open(f, 'r', encoding='utf-8') as o: diff --git a/run_unittests.py b/run_unittests.py index 110782b..3a473ea 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -1244,7 +1244,7 @@ class BasePlatformTests(unittest.TestCase): print('Stderr:\n') print(err) raise RuntimeError('Configure failed') - except: + except Exception: self._print_meson_log() raise finally: @@ -1257,7 +1257,7 @@ class BasePlatformTests(unittest.TestCase): out = self._run(self.setup_command + args + extra_args) except unittest.SkipTest: raise unittest.SkipTest('Project requested skipping: ' + srcdir) - except: + except Exception: self._print_meson_log() raise return out @@ -2681,9 +2681,9 @@ int main(int argc, char **argv) { if ninja is None: raise unittest.SkipTest('This test currently requires ninja. Fix this once "meson build" works.') for lang in ('c', 'cpp'): - for type in ('executable', 'library'): + for target_type in ('executable', 'library'): with tempfile.TemporaryDirectory() as tmpdir: - self._run(self.meson_command + ['init', '--language', lang, '--type', type], + self._run(self.meson_command + ['init', '--language', lang, '--type', target_type], workdir=tmpdir) self._run(self.setup_command + ['--backend=ninja', 'builddir'], workdir=tmpdir) @@ -3642,6 +3642,12 @@ recommended as it is not supported on some platforms''') self.maxDiff = None self.assertListEqual(res_nb, expected) + def test_unstable_coredata(self): + testdir = os.path.join(self.common_test_dir, '1 trivial') + self.init(testdir) + # just test that the command does not fail (e.g. because it throws an exception) + self._run([*self.meson_command, 'unstable-coredata', self.builddir]) + class FailureTests(BasePlatformTests): ''' Tests that test failure conditions. Build files here should be dynamically @@ -4443,7 +4449,7 @@ class LinuxlikeTests(BasePlatformTests): self.assertIn(cmd_std, cmd) try: self.build() - except: + except Exception: print('{} was {!r}'.format(lang_std, v)) raise self.wipe() @@ -5527,7 +5533,7 @@ class RewriterTests(BasePlatformTests): out = self.rewrite(self.builddir, os.path.join(self.builddir, 'info.json')) expected = {'name': 'myExe', 'sources': ['main.cpp']} self.assertEqual(len(out['target']), 2) - for _, val in out['target'].items(): + for val in out['target'].values(): self.assertDictEqual(expected, val) def test_kwargs_info(self): @@ -28,4 +28,6 @@ ignore = E722 # W504: line break after binary operator W504 + # A003: builtin class attribute + A003 max-line-length = 120 @@ -1,3 +1,7 @@ linter: flake8: version: 3 + plugins: + - flake8-blind-except + - flake8-builtins + - flake8-bugbear diff --git a/test cases/fortran/14 fortran links c/clib.c b/test cases/fortran/14 fortran links c/clib.c new file mode 100644 index 0000000..81b2e0c --- /dev/null +++ b/test cases/fortran/14 fortran links c/clib.c @@ -0,0 +1,7 @@ +#include <stdio.h> + +void hello(void){ + + printf("hello from C\n"); + +} diff --git a/test cases/fortran/14 fortran links c/f_call_c.f90 b/test cases/fortran/14 fortran links c/f_call_c.f90 new file mode 100644 index 0000000..af1e79c --- /dev/null +++ b/test cases/fortran/14 fortran links c/f_call_c.f90 @@ -0,0 +1,10 @@ +implicit none + +interface +subroutine hello() bind (c) +end subroutine hello +end interface + +call hello() + +end program diff --git a/test cases/fortran/14 fortran links c/meson.build b/test cases/fortran/14 fortran links c/meson.build new file mode 100644 index 0000000..163aec6 --- /dev/null +++ b/test cases/fortran/14 fortran links c/meson.build @@ -0,0 +1,13 @@ +project('Fortran calling C', 'fortran', 'c') + +ccid = meson.get_compiler('c').get_id() +if ccid == 'msvc' or ccid == 'clang-cl' + error('MESON_SKIP_TEST: MSVC and GCC do not interoperate like this.') +endif + +c_lib = library('clib', 'clib.c') + +f_call_c = executable('f_call_c', 'f_call_c.f90', + link_with: c_lib, + link_language: 'fortran') +test('Fortran calling C', f_call_c) diff --git a/tools/boost_names.py b/tools/boost_names.py index d381162..d0e5444 100755 --- a/tools/boost_names.py +++ b/tools/boost_names.py @@ -132,7 +132,7 @@ def get_modules_2(): # The python module uses an older build system format and is not easily parseable. # We add the python module libraries manually. modules.append(Module('python', 'Python', ['boost_python', 'boost_python3', 'boost_numpy', 'boost_numpy3'])) - for (root, dirs, files) in os.walk(LIBS): + for (root, _, files) in os.walk(LIBS): for f in files: if f == "libraries.json": projectdir = os.path.dirname(root) |