Skip to content

Commit

Permalink
compilers: Use keyword only arguments for compiler interfaces
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dcbaker authored and jpakkane committed Nov 12, 2018
1 parent cecad3b commit de175aa
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 63 deletions.
87 changes: 53 additions & 34 deletions mesonbuild/compilers/c.py
Expand Up @@ -209,7 +209,9 @@ def get_std_shared_lib_link_args(self):
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

Expand Down Expand Up @@ -361,13 +363,14 @@ def sanity_check(self, work_dir, environment):
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
Expand All @@ -377,10 +380,10 @@ def has_header(self, hname, prefix, env, extra_args=None, dependencies=None):
#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}>
Expand All @@ -390,7 +393,8 @@ def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, depende
{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):
Expand Down Expand Up @@ -437,18 +441,19 @@ def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'
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

def _build_wrapper(self, code, env, extra_args, dependencies=None, mode='compile', want_output=False):
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:
Expand Down Expand Up @@ -478,7 +483,8 @@ def _compile_int(self, expression, prefix, env, extra_args, dependencies):
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
Expand Down Expand Up @@ -528,7 +534,7 @@ def cross_compute_int(self, expression, low, high, guess, prefix, env, extra_arg

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:
Expand All @@ -540,14 +546,15 @@ def compute_int(self, expression, low, high, guess, prefix, env, extra_args=None
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}
Expand All @@ -556,30 +563,33 @@ def cross_sizeof(self, typename, prefix, env, extra_args=None, dependencies=None
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}
Expand All @@ -588,7 +598,8 @@ def cross_alignment(self, typename, prefix, env, extra_args=None, dependencies=N
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}
Expand All @@ -598,11 +609,12 @@ def cross_alignment(self, typename, prefix, env, extra_args=None, dependencies=N
}};'''
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>
Expand All @@ -615,7 +627,8 @@ def alignment(self, typename, prefix, env, extra_args=None, dependencies=None):
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:
Expand Down Expand Up @@ -659,7 +672,7 @@ def get_return_value(self, fname, rtype, prefix, env, extra_args, dependencies):
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))
Expand Down Expand Up @@ -728,7 +741,7 @@ def _have_prototype_templ():
}}'''
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
Expand Down Expand Up @@ -776,7 +789,8 @@ def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None
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.
Expand Down Expand Up @@ -809,9 +823,10 @@ def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None
#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'}
Expand All @@ -825,15 +840,17 @@ def has_members(self, typename, membernames, prefix, env, extra_args=None, depen
{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}
t = '''{prefix}
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):
'''
Expand Down Expand Up @@ -1221,11 +1238,13 @@ def get_options(self):

# 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):
Expand Down
9 changes: 5 additions & 4 deletions mesonbuild/compilers/compilers.py
Expand Up @@ -868,10 +868,10 @@ def get_define(self, dname, prefix, env, extra_args, dependencies):
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):
Expand Down Expand Up @@ -1614,7 +1614,7 @@ def has_multi_arguments(self, args, env):
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
Expand All @@ -1623,7 +1623,8 @@ def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None
# 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'):
Expand Down
15 changes: 10 additions & 5 deletions mesonbuild/compilers/cpp.py
Expand Up @@ -62,9 +62,11 @@ def get_compiler_check_args(self):
# 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:
Expand All @@ -74,7 +76,8 @@ def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, depende
#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
Expand Down Expand Up @@ -246,11 +249,13 @@ def get_options(self):

# 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):
Expand Down
2 changes: 1 addition & 1 deletion mesonbuild/compilers/d.py
Expand Up @@ -303,7 +303,7 @@ def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'
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:
Expand Down
12 changes: 7 additions & 5 deletions mesonbuild/compilers/fortran.py
Expand Up @@ -210,16 +210,18 @@ def gen_import_library_args(self, implibname):
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):
Expand Down

0 comments on commit de175aa

Please sign in to comment.