compilers: Use keyword only arguments for compiler interfaces

Because we need to inherit them in some cases, and python's
keyword-or-positional arguments make this really painful, especially
with inheritance. They do this in two ways:

1) If you want to intercept the arguments you need to check for both a
   keyword and a positional argument, because you could get either. Then
   you need to make sure that you only pass one of those down to the
   next layer.

2) After you do that, if the layer below you decides to do the same
   thing, but uses the other form (you used keyword by the lower level
   uses positional or vice versa), then you'll get a TypeError since two
   layers down got the argument as both a positional and a keyword.

All of this is bad. Fortunately python 3.x provides a mechanism to solve
this, keyword only arguments. These arguments cannot be based
positionally, the interpreter will give us an error in that case.

I have made a best effort to do this correctly, and I've verified it
with GCC, Clang, ICC, and MSVC, but there are other compilers like Arm
and Elbrus that I don't have access to.
pull/4509/head
Dylan Baker 6 years ago committed by Jussi Pakkanen
parent cecad3b9db
commit de175aac00
  1. 87
      mesonbuild/compilers/c.py
  2. 9
      mesonbuild/compilers/compilers.py
  3. 15
      mesonbuild/compilers/cpp.py
  4. 2
      mesonbuild/compilers/d.py
  5. 12
      mesonbuild/compilers/fortran.py
  6. 52
      mesonbuild/interpreter.py

@ -209,7 +209,9 @@ class CCompiler(Compiler):
def _get_search_dirs(self, env):
extra_args = ['--print-search-dirs']
stdo = None
with self._build_wrapper('', env, extra_args, None, 'compile', True) as p:
with self._build_wrapper('', env, extra_args=extra_args,
dependencies=None, mode='compile',
want_output=True) as p:
stdo = p.stdo
return stdo
@ -361,13 +363,14 @@ class CCompiler(Compiler):
code = 'int main(int argc, char **argv) { int class=0; return class; }\n'
return self.sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code)
def check_header(self, hname, prefix, env, extra_args=None, dependencies=None):
def check_header(self, hname, prefix, env, *, extra_args=None, dependencies=None):
fargs = {'prefix': prefix, 'header': hname}
code = '''{prefix}
#include <{header}>'''
return self.compiles(code.format(**fargs), env, extra_args, dependencies)
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):
fargs = {'prefix': prefix, 'header': hname}
code = '''{prefix}
#ifdef __has_include
@ -377,10 +380,10 @@ class CCompiler(Compiler):
#else
#include <{header}>
#endif'''
return self.compiles(code.format(**fargs), env, extra_args,
dependencies, 'preprocess')
return self.compiles(code.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies, mode='preprocess')
def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None):
def has_header_symbol(self, hname, symbol, prefix, env, *, extra_args=None, dependencies=None):
fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol}
t = '''{prefix}
#include <{header}>
@ -390,7 +393,8 @@ class CCompiler(Compiler):
{symbol};
#endif
}}'''
return self.compiles(t.format(**fargs), env, extra_args, dependencies)
return self.compiles(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'):
if callable(extra_args):
@ -437,7 +441,7 @@ class CCompiler(Compiler):
args += extra_args
return args
def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile'):
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
@ -445,10 +449,11 @@ class CCompiler(Compiler):
args = self._get_compiler_check_args(env, extra_args, dependencies, mode)
return self.compile(code, args, mode, want_output=want_output)
def links(self, code, env, extra_args=None, dependencies=None):
return self.compiles(code, env, extra_args, dependencies, mode='link')
def links(self, code, env, *, extra_args=None, dependencies=None):
return self.compiles(code, env, extra_args=extra_args,
dependencies=dependencies, mode='link')
def run(self, code, env, extra_args=None, dependencies=None):
def run(self, code, env, *, extra_args=None, dependencies=None):
if self.is_cross and self.exe_wrapper is None:
raise CrossNoRunException('Can not run test applications in this cross environment.')
with self._build_wrapper(code, env, extra_args, dependencies, mode='link', want_output=True) as p:
@ -478,7 +483,8 @@ class CCompiler(Compiler):
t = '''#include <stdio.h>
{prefix}
int main() {{ static int a[1-2*!({expression})]; a[0]=0; return 0; }}'''
return self.compiles(t.format(**fargs), env, extra_args, dependencies)
return self.compiles(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
def cross_compute_int(self, expression, low, high, guess, prefix, env, extra_args, dependencies):
# Try user's guess first
@ -528,7 +534,7 @@ class CCompiler(Compiler):
return low
def compute_int(self, expression, low, high, guess, prefix, env, extra_args=None, dependencies=None):
def compute_int(self, expression, low, high, guess, prefix, env, *, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
if self.is_cross:
@ -540,14 +546,15 @@ class CCompiler(Compiler):
printf("%ld\\n", (long)({expression}));
return 0;
}};'''
res = self.run(t.format(**fargs), env, extra_args, dependencies)
res = self.run(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
if not res.compiled:
return -1
if res.returncode != 0:
raise EnvironmentException('Could not run compute_int test binary.')
return int(res.stdout)
def cross_sizeof(self, typename, prefix, env, extra_args=None, dependencies=None):
def cross_sizeof(self, typename, prefix, env, *, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
fargs = {'prefix': prefix, 'type': typename}
@ -556,30 +563,33 @@ class CCompiler(Compiler):
int main(int argc, char **argv) {{
{type} something;
}}'''
if not self.compiles(t.format(**fargs), env, extra_args, dependencies):
if not self.compiles(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies):
return -1
return self.cross_compute_int('sizeof(%s)' % typename, None, None, None, prefix, env, extra_args, dependencies)
def sizeof(self, typename, prefix, env, extra_args=None, dependencies=None):
def sizeof(self, typename, prefix, env, *, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
fargs = {'prefix': prefix, 'type': typename}
if self.is_cross:
return self.cross_sizeof(typename, prefix, env, extra_args, dependencies)
return self.cross_sizeof(typename, prefix, env, extra_args=extra_args,
dependencies=dependencies)
t = '''#include<stdio.h>
{prefix}
int main(int argc, char **argv) {{
printf("%ld\\n", (long)(sizeof({type})));
return 0;
}};'''
res = self.run(t.format(**fargs), env, extra_args, dependencies)
res = self.run(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
if not res.compiled:
return -1
if res.returncode != 0:
raise EnvironmentException('Could not run sizeof test binary.')
return int(res.stdout)
def cross_alignment(self, typename, prefix, env, extra_args=None, dependencies=None):
def cross_alignment(self, typename, prefix, env, *, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
fargs = {'prefix': prefix, 'type': typename}
@ -588,7 +598,8 @@ class CCompiler(Compiler):
int main(int argc, char **argv) {{
{type} something;
}}'''
if not self.compiles(t.format(**fargs), env, extra_args, dependencies):
if not self.compiles(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies):
return -1
t = '''#include <stddef.h>
{prefix}
@ -598,11 +609,12 @@ class CCompiler(Compiler):
}};'''
return self.cross_compute_int('offsetof(struct tmp, target)', None, None, None, t.format(**fargs), env, extra_args, dependencies)
def alignment(self, typename, prefix, env, extra_args=None, dependencies=None):
def alignment(self, typename, prefix, env, *, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
if self.is_cross:
return self.cross_alignment(typename, prefix, env, extra_args, dependencies)
return self.cross_alignment(typename, prefix, env, extra_args=extra_args,
dependencies=dependencies)
fargs = {'prefix': prefix, 'type': typename}
t = '''#include <stdio.h>
#include <stddef.h>
@ -615,7 +627,8 @@ class CCompiler(Compiler):
printf("%d", (int)offsetof(struct tmp, target));
return 0;
}}'''
res = self.run(t.format(**fargs), env, extra_args, dependencies)
res = self.run(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
if not res.compiled:
raise EnvironmentException('Could not compile alignment test.')
if res.returncode != 0:
@ -659,7 +672,7 @@ class CCompiler(Compiler):
int main(int argc, char *argv[]) {{
printf ("{fmt}", {cast} {f}());
}}'''.format(**fargs)
res = self.run(code, env, extra_args, dependencies)
res = self.run(code, env, extra_args=extra_args, dependencies=dependencies)
if not res.compiled:
m = 'Could not get return value of {}()'
raise EnvironmentException(m.format(fname))
@ -728,7 +741,7 @@ class CCompiler(Compiler):
}}'''
return head, main
def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None):
def has_function(self, funcname, prefix, env, *, extra_args=None, dependencies=None):
"""
First, this function looks for the symbol in the default libraries
provided by the compiler (stdlib + a few others usually). If that
@ -776,7 +789,8 @@ class CCompiler(Compiler):
head, main = self._no_prototype_templ()
templ = head + stubs_fail + main
if self.links(templ.format(**fargs), env, extra_args, dependencies):
if self.links(templ.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies):
return True
# MSVC does not have compiler __builtin_-s.
@ -809,9 +823,10 @@ class CCompiler(Compiler):
#endif
#endif
}}'''
return self.links(t.format(**fargs), env, extra_args, dependencies)
return self.links(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
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):
if extra_args is None:
extra_args = []
fargs = {'prefix': prefix, 'type': typename, 'name': 'foo'}
@ -825,7 +840,8 @@ class CCompiler(Compiler):
{type} {name};
{members}
}};'''
return self.compiles(t.format(**fargs), env, extra_args, dependencies)
return self.compiles(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
def has_type(self, typename, prefix, env, extra_args, dependencies=None):
fargs = {'prefix': prefix, 'type': typename}
@ -833,7 +849,8 @@ class CCompiler(Compiler):
void bar() {{
sizeof({type});
}};'''
return self.compiles(t.format(**fargs), env, extra_args, dependencies)
return self.compiles(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
def symbols_have_underscore_prefix(self, env):
'''
@ -1221,11 +1238,13 @@ class ElbrusCCompiler(GnuCCompiler, ElbrusCompiler):
# Elbrus C compiler does not have lchmod, but there is only linker warning, not compiler error.
# So we should explicitly fail at this case.
def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None):
def has_function(self, funcname, prefix, env, *, extra_args=None, dependencies=None):
if funcname == 'lchmod':
return False
else:
return super().has_function(funcname, prefix, env, extra_args, dependencies)
return super().has_function(funcname, prefix, env,
extra_args=extra_args,
dependencies=dependencies)
class IntelCCompiler(IntelCompiler, CCompiler):

@ -868,10 +868,10 @@ class Compiler:
def compute_int(self, expression, low, high, guess, prefix, env, extra_args, dependencies):
raise EnvironmentException('%s does not support compute_int ' % 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):
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):
raise EnvironmentException('%s does not support has_type ' % self.get_id())
def symbols_have_underscore_prefix(self, env):
@ -1614,7 +1614,7 @@ class ClangCompiler(GnuLikeCompiler):
myargs + args,
env)
def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None):
def has_function(self, funcname, prefix, env, *, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
# Starting with XCode 8, we need to pass this to force linker
@ -1623,7 +1623,8 @@ class ClangCompiler(GnuLikeCompiler):
# https://github.com/Homebrew/homebrew-core/issues/3727
if self.compiler_type.is_osx_compiler and version_compare(self.version, '>=8.0'):
extra_args.append('-Wl,-no_weak_imports')
return super().has_function(funcname, prefix, env, extra_args, dependencies)
return super().has_function(funcname, prefix, env, extra_args=extra_args,
dependencies=dependencies)
def openmp_flags(self):
if version_compare(self.version, '>=3.8.0'):

@ -62,9 +62,11 @@ class CPPCompiler(CCompiler):
# too strict without this and always fails.
return super().get_compiler_check_args() + ['-fpermissive']
def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None):
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, dependencies):
if super().has_header_symbol(hname, symbol, prefix, env,
extra_args=extra_args,
dependencies=dependencies):
return True
# Check if it's a class or a template
if extra_args is None:
@ -74,7 +76,8 @@ class CPPCompiler(CCompiler):
#include <{header}>
using {symbol};
int main () {{ return 0; }}'''
return self.compiles(t.format(**fargs), env, extra_args, dependencies)
return self.compiles(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
def _test_cpp_std_arg(self, cpp_std_value):
# Test whether the compiler understands a -std=XY argument
@ -246,11 +249,13 @@ class ElbrusCPPCompiler(GnuCPPCompiler, ElbrusCompiler):
# Elbrus C++ compiler does not have lchmod, but there is only linker warning, not compiler error.
# So we should explicitly fail at this case.
def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None):
def has_function(self, funcname, prefix, env, *, extra_args=None, dependencies=None):
if funcname == 'lchmod':
return False
else:
return super().has_function(funcname, prefix, env, extra_args, dependencies)
return super().has_function(funcname, prefix, env,
extra_args=extra_args,
dependencies=dependencies)
class IntelCPPCompiler(IntelCompiler, CPPCompiler):

@ -303,7 +303,7 @@ class DCompiler(Compiler):
args += extra_args
return args
def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile'):
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:

@ -210,16 +210,18 @@ end program prog
def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'):
return CCompiler._get_compiler_check_args(self, env, extra_args, dependencies, mode='compile')
def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile'):
return CCompiler.compiles(self, code, env, extra_args, dependencies, mode)
def compiles(self, code, env, *, extra_args=None, dependencies=None, mode='compile'):
return CCompiler.compiles(self, code, env, extra_args=extra_args,
dependencies=dependencies, mode=mode)
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 links(self, code, env, extra_args=None, dependencies=None):
return CCompiler.links(self, code, env, extra_args, dependencies)
def links(self, code, env, *, extra_args=None, dependencies=None):
return CCompiler.links(self, code, env, extra_args=extra_args,
dependencies=dependencies)
def run(self, code, env, extra_args=None, dependencies=None):
def run(self, code, env, *, extra_args=None, dependencies=None):
return CCompiler.run(self, code, env, extra_args, dependencies)
def _get_patterns(self, *args, **kwargs):

@ -1018,7 +1018,9 @@ class CompilerHolder(InterpreterObject):
raise InterpreterException('Prefix argument of sizeof must be a string.')
extra_args = mesonlib.stringlistify(kwargs.get('args', []))
deps, msg = self.determine_dependencies(kwargs)
result = self.compiler.alignment(typename, prefix, self.environment, extra_args, deps)
result = self.compiler.alignment(typename, prefix, self.environment,
extra_args=extra_args,
dependencies=deps)
mlog.log('Checking for alignment of', mlog.bold(typename, True), msg, result)
return result
@ -1043,7 +1045,8 @@ 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.run(code, self.environment, extra_args, deps)
result = self.compiler.run(code, self.environment, extra_args=extra_args,
dependencies=deps)
if len(testname) > 0:
if not result.compiled:
h = mlog.red('DID NOT COMPILE')
@ -1099,7 +1102,9 @@ class CompilerHolder(InterpreterObject):
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, deps)
self.environment,
extra_args=extra_args,
dependencies=deps)
if had:
hadtxt = mlog.green('YES')
else:
@ -1127,7 +1132,9 @@ class CompilerHolder(InterpreterObject):
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, deps)
self.environment,
extra_args=extra_args,
dependencies=deps)
if had:
hadtxt = mlog.green('YES')
else:
@ -1154,7 +1161,9 @@ 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, deps)
had = self.compiler.has_function(funcname, prefix, self.environment,
extra_args=extra_args,
dependencies=deps)
if had:
hadtxt = mlog.green('YES')
else:
@ -1179,7 +1188,8 @@ 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, deps)
had = self.compiler.has_type(typename, prefix, self.environment,
extra_args=extra_args, dependencies=deps)
if had:
hadtxt = mlog.green('YES')
else:
@ -1217,7 +1227,9 @@ class CompilerHolder(InterpreterObject):
raise InterpreterException('Guess argument of compute_int must be an int.')
extra_args = functools.partial(self.determine_args, kwargs)
deps, msg = self.determine_dependencies(kwargs)
res = self.compiler.compute_int(expression, low, high, guess, prefix, self.environment, extra_args, deps)
res = self.compiler.compute_int(expression, low, high, guess, prefix,
self.environment, extra_args=extra_args,
dependencies=deps)
mlog.log('Computing int of', mlog.bold(expression, True), msg, res)
return res
@ -1238,7 +1250,8 @@ class CompilerHolder(InterpreterObject):
raise InterpreterException('Prefix argument of sizeof must be a string.')
extra_args = functools.partial(self.determine_args, kwargs)
deps, msg = self.determine_dependencies(kwargs)
esize = self.compiler.sizeof(element, prefix, self.environment, extra_args, deps)
esize = self.compiler.sizeof(element, prefix, self.environment,
extra_args=extra_args, dependencies=deps)
mlog.log('Checking for size of', mlog.bold(element, True), msg, esize)
return esize
@ -1260,7 +1273,9 @@ 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, deps)
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)
return value
@ -1285,7 +1300,9 @@ 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, deps)
result = self.compiler.compiles(code, self.environment,
extra_args=extra_args,
dependencies=deps)
if len(testname) > 0:
if result:
h = mlog.green('YES')
@ -1315,7 +1332,9 @@ 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, deps)
result = self.compiler.links(code, self.environment,
extra_args=extra_args,
dependencies=deps)
if len(testname) > 0:
if result:
h = mlog.green('YES')
@ -1342,7 +1361,9 @@ class CompilerHolder(InterpreterObject):
raise InterpreterException('Prefix argument of has_header must be a string.')
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, deps)
haz = self.compiler.check_header(hname, prefix, self.environment,
extra_args=extra_args,
dependencies=deps)
if haz:
h = mlog.green('YES')
else:
@ -1367,7 +1388,8 @@ class CompilerHolder(InterpreterObject):
raise InterpreterException('Prefix argument of has_header must be a string.')
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, deps)
haz = self.compiler.has_header(hname, prefix, self.environment,
extra_args=extra_args, dependencies=deps)
if haz:
h = mlog.green('YES')
else:
@ -1393,7 +1415,9 @@ class CompilerHolder(InterpreterObject):
raise InterpreterException('Prefix argument of has_header_symbol must be a string.')
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, deps)
haz = self.compiler.has_header_symbol(hname, symbol, prefix, self.environment,
extra_args=extra_args,
dependencies=deps)
if haz:
h = mlog.green('YES')
else:

Loading…
Cancel
Save