Merge pull request #3353 from xclaesse/has-link-argument

Add has_link_argument() and friends
pull/3372/head
Jussi Pakkanen 7 years ago committed by GitHub
commit 8ee1e49ae6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 24
      docs/markdown/Reference-manual.md
  2. 9
      docs/markdown/snippets/has-link-argument.md
  3. 75
      mesonbuild/compilers/c.py
  4. 18
      mesonbuild/compilers/compilers.py
  5. 14
      mesonbuild/compilers/cpp.py
  6. 64
      mesonbuild/interpreter.py
  7. 42
      test cases/common/191 has link arg/meson.build

@ -1508,7 +1508,11 @@ the following methods:
- `first_supported_argument(list_of_strings)`, given a list of - `first_supported_argument(list_of_strings)`, given a list of
strings, returns the first argument that passes the `has_argument` strings, returns the first argument that passes the `has_argument`
test above or an empty array if none pass. test or an empty array if none pass.
- `first_supported_link_argument(list_of_strings)` *(added 0.46.0)*, given a
list of strings, returns the first argument that passes the
`has_link_argument` test or an empty array if none pass.
- `get_define(definename)` returns the given preprocessor symbol's - `get_define(definename)` returns the given preprocessor symbol's
value as a string or empty string if it is not defined. value as a string or empty string if it is not defined.
@ -1520,11 +1524,19 @@ the following methods:
an array containing only the arguments supported by the compiler, an array containing only the arguments supported by the compiler,
as if `has_argument` were called on them individually. as if `has_argument` were called on them individually.
- `get_supported_link_arguments(list_of_string)` *(added 0.46.0)* returns
an array containing only the arguments supported by the linker,
as if `has_link_argument` were called on them individually.
- `has_argument(argument_name)` returns true if the compiler accepts - `has_argument(argument_name)` returns true if the compiler accepts
the specified command line argument, that is, can compile code the specified command line argument, that is, can compile code
without erroring out or printing a warning about an unknown flag, without erroring out or printing a warning about an unknown flag.
you can specify external dependencies to use with `dependencies`
keyword argument. - `has_link_argument(argument_name)` *(added 0.46.0)* returns true if the linker
accepts the specified command line argument, that is, can compile and link
code without erroring out or printing a warning about an unknown flag. Link
arguments will be passed to the compiler, so should usually have the `-Wl,`
prefix. On VisualStudio a `/link` argument will be prepended.
- `has_function(funcname)` returns true if the given function is - `has_function(funcname)` returns true if the given function is
provided by the standard library or a library passed in with the provided by the standard library or a library passed in with the
@ -1559,6 +1571,10 @@ the following methods:
`has_argument` but takes multiple arguments and uses them all in a `has_argument` but takes multiple arguments and uses them all in a
single compiler invocation, available since 0.37.0. single compiler invocation, available since 0.37.0.
- `has_multi_link_arguments(arg1, arg2, arg3, ...)` *(added 0.46.0)* is the same
as `has_link_argument` but takes multiple arguments and uses them all in a
single compiler invocation.
- `has_type(typename)` returns true if the specified token is a type, - `has_type(typename)` returns true if the specified token is a type,
you can specify external dependencies to use with `dependencies` you can specify external dependencies to use with `dependencies`
keyword argument. keyword argument.

@ -0,0 +1,9 @@
## has_link_argument() and friends
A new set of methods has been added on compiler objects to test if the linker
supports given arguments.
- `has_link_argument()`
- `has_multi_link_arguments()`
- `get_supported_link_arguments()`
- `first_supported_link_argument()`

@ -323,25 +323,21 @@ class CCompiler(Compiler):
args += extra_args args += extra_args
return args return args
def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile', want_output=False): 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._build_wrapper(code, env, extra_args, dependencies, mode) as p:
# We only want to compile; not link
with self.compile(code, args.to_native(), mode) as p:
return p.returncode == 0 return p.returncode == 0
def _links_wrapper(self, code, env, extra_args, dependencies, want_output=False): def _build_wrapper(self, code, env, extra_args, dependencies=None, mode='compile', want_output=False):
"Shares common code between self.links and self.run" args = self._get_compiler_check_args(env, extra_args, dependencies, mode)
args = self._get_compiler_check_args(env, extra_args, dependencies, mode='link') return self.compile(code, args.to_native(), mode, want_output=want_output)
return self.compile(code, args, want_output=want_output)
def links(self, code, env, extra_args=None, dependencies=None): def links(self, code, env, extra_args=None, dependencies=None):
with self._links_wrapper(code, env, extra_args, dependencies) as p: return self.compiles(code, env, extra_args, dependencies, mode='link')
return p.returncode == 0
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: if self.is_cross and self.exe_wrapper is None:
raise CrossNoRunException('Can not run test applications in this cross environment.') raise CrossNoRunException('Can not run test applications in this cross environment.')
with self._links_wrapper(code, env, extra_args, dependencies, True) as p: with self._build_wrapper(code, env, extra_args, dependencies, mode='link', want_output=True) as p:
if p.returncode != 0: if p.returncode != 0:
mlog.debug('Could not compile test file %s: %d\n' % ( mlog.debug('Could not compile test file %s: %d\n' % (
p.input_name, p.input_name,
@ -841,6 +837,12 @@ class CCompiler(Compiler):
return [] return []
return ['-pthread'] return ['-pthread']
def linker_to_compiler_args(self, args):
return args
def has_arguments(self, args, env, code, mode):
return self.compiles(code, env, extra_args=args, mode=mode)
def has_multi_arguments(self, args, env): def has_multi_arguments(self, args, env):
for arg in args[:]: for arg in args[:]:
# some compilers, e.g. GCC, don't warn for unsupported warning-disable # some compilers, e.g. GCC, don't warn for unsupported warning-disable
@ -849,13 +851,21 @@ class CCompiler(Compiler):
if arg.startswith('-Wno-'): if arg.startswith('-Wno-'):
args.append('-W' + arg[5:]) args.append('-W' + arg[5:])
if arg.startswith('-Wl,'): if arg.startswith('-Wl,'):
mlog.warning('''{} looks like a linker argument, but has_argument mlog.warning('{} looks like a linker argument, '
and other similar methods only support checking compiler arguments. 'but has_argument and other similar methods only '
Using them to check linker arguments are never supported, and results 'support checking compiler arguments. Using them '
are likely to be wrong regardless of the compiler you are using. 'to check linker arguments are never supported, '
'''.format(arg)) 'and results are likely to be wrong regardless of '
return self.compiles('int i;\n', env, extra_args=args) 'the compiler you are using. has_link_argument or '
'other similar method can be used instead.'
.format(arg))
code = 'int i;\n'
return self.has_arguments(args, env, code, mode='compile')
def has_multi_link_arguments(self, args, env):
args = self.linker_to_compiler_args(args)
code = 'int main(int argc, char **argv) { return 0; }'
return self.has_arguments(args, env, code, mode='link')
class ClangCCompiler(ClangCompiler, CCompiler): class ClangCCompiler(ClangCompiler, CCompiler):
def __init__(self, exelist, version, clang_type, is_cross, exe_wrapper=None, **kwargs): def __init__(self, exelist, version, clang_type, is_cross, exe_wrapper=None, **kwargs):
@ -981,8 +991,8 @@ class IntelCCompiler(IntelCompiler, CCompiler):
def get_std_shared_lib_link_args(self): def get_std_shared_lib_link_args(self):
return ['-shared'] return ['-shared']
def has_multi_arguments(self, args, env): def has_arguments(self, args, env, code, mode):
return super().has_multi_arguments(args + ['-diag-error', '10006'], env) return super().has_arguments(args + ['-diag-error', '10006'], env, code, mode)
class VisualStudioCCompiler(CCompiler): class VisualStudioCCompiler(CCompiler):
@ -1066,6 +1076,9 @@ class VisualStudioCCompiler(CCompiler):
def get_linker_search_args(self, dirname): def get_linker_search_args(self, dirname):
return ['/LIBPATH:' + dirname] return ['/LIBPATH:' + dirname]
def linker_to_compiler_args(self, args):
return ['/link'] + args
def get_gui_app_args(self): def get_gui_app_args(self):
return ['/SUBSYSTEM:WINDOWS'] return ['/SUBSYSTEM:WINDOWS']
@ -1149,24 +1162,12 @@ class VisualStudioCCompiler(CCompiler):
# Visual Studio is special. It ignores some arguments it does not # Visual Studio is special. It ignores some arguments it does not
# understand and you can't tell it to error out on those. # understand and you can't tell it to error out on those.
# http://stackoverflow.com/questions/15259720/how-can-i-make-the-microsoft-c-compiler-treat-unknown-flags-as-errors-rather-t # http://stackoverflow.com/questions/15259720/how-can-i-make-the-microsoft-c-compiler-treat-unknown-flags-as-errors-rather-t
def has_multi_arguments(self, args, env): def has_arguments(self, args, env, code, mode):
warning_text = '9002' warning_text = '4044' if mode == 'link' else '9002'
code = 'int i;\n' with self._build_wrapper(code, env, extra_args=args, mode=mode) as p:
(fd, srcname) = tempfile.mkstemp(suffix='.' + self.default_suffix) if p.returncode != 0:
os.close(fd) return False
with open(srcname, 'w') as ofile: return not(warning_text in p.stde or warning_text in p.stdo)
ofile.write(code)
# Read c_args/cpp_args/etc from the cross-info file (if needed)
extra_args = self.get_cross_extra_flags(env, link=False)
extra_args += self.get_compile_only_args()
commands = self.exelist + args + extra_args + [srcname]
mlog.debug('Running VS compile:')
mlog.debug('Command line: ', ' '.join(commands))
mlog.debug('Code:\n', code)
p, stdo, stde = Popen_safe(commands, cwd=os.path.dirname(srcname))
if p.returncode != 0:
return False
return not(warning_text in stde or warning_text in stdo)
def get_compile_debugfile_args(self, rel_obj, pch=False): def get_compile_debugfile_args(self, rel_obj, pch=False):
pdbarr = rel_obj.split('.')[:-1] pdbarr = rel_obj.split('.')[:-1]

@ -745,20 +745,15 @@ class Compiler:
def get_library_dirs(self): def get_library_dirs(self):
return [] return []
def has_argument(self, arg, env):
return self.has_multi_arguments([arg], env)
def has_multi_arguments(self, args, env): def has_multi_arguments(self, args, env):
raise EnvironmentException( raise EnvironmentException(
'Language {} does not support has_multi_arguments.'.format( 'Language {} does not support has_multi_arguments.'.format(
self.get_display_language())) self.get_display_language()))
def get_supported_arguments(self, args, env): def has_multi_link_arguments(self, args, env):
supported_args = [] raise EnvironmentException(
for arg in args: 'Language {} does not support has_multi_link_arguments.'.format(
if self.has_argument(arg, env): self.get_display_language()))
supported_args.append(arg)
return supported_args
def get_cross_extra_flags(self, environment, link): def get_cross_extra_flags(self, environment, link):
extra_flags = [] extra_flags = []
@ -815,7 +810,6 @@ class Compiler:
# Construct the compiler command-line # Construct the compiler command-line
commands = CompilerArgs(self) commands = CompilerArgs(self)
commands.append(srcname) commands.append(srcname)
commands += extra_args
commands += self.get_always_args() commands += self.get_always_args()
if mode == 'compile': if mode == 'compile':
commands += self.get_compile_only_args() commands += self.get_compile_only_args()
@ -825,6 +819,10 @@ class Compiler:
else: else:
output = self._get_compile_output(tmpdirname, mode) output = self._get_compile_output(tmpdirname, mode)
commands += self.get_output_args(output) commands += self.get_output_args(output)
# extra_args must be last because it could contain '/link' to
# pass args to VisualStudio's linker. In that case everything
# in the command line after '/link' is given to the linker.
commands += extra_args
# Generate full command-line with the exelist # Generate full command-line with the exelist
commands = self.get_exelist() + commands.to_native() commands = self.get_exelist() + commands.to_native()
mlog.debug('Running compile:') mlog.debug('Running compile:')

@ -199,20 +199,10 @@ class IntelCPPCompiler(IntelCompiler, CPPCompiler):
def get_option_link_args(self, options): def get_option_link_args(self, options):
return [] return []
def has_multi_arguments(self, args, env):
for arg in args:
if arg.startswith('-Wl,'):
mlog.warning('''{} looks like a linker argument, but has_argument
and other similar methods only support checking compiler arguments.
Using them to check linker arguments are never supported, and results
are likely to be wrong regardless of the compiler you are using.
'''.format(arg))
return super().has_multi_arguments(args + ['-diag-error', '10006'], env)
class VisualStudioCPPCompiler(VisualStudioCCompiler, CPPCompiler): class VisualStudioCPPCompiler(VisualStudioCCompiler, CPPCompiler):
def __init__(self, exelist, version, is_cross, exe_wrap, is_64): def __init__(self, exelist, version, is_cross, exe_wrap, is_64):
self.language = 'cpp' CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap)
VisualStudioCCompiler.__init__(self, exelist, version, is_cross, exe_wrap, is_64) VisualStudioCCompiler.__init__(self, exelist, version, is_cross, exe_wrap, is_64)
self.base_options = ['b_pch'] # FIXME add lto, pgo and the like self.base_options = ['b_pch'] # FIXME add lto, pgo and the like
@ -239,7 +229,7 @@ class VisualStudioCPPCompiler(VisualStudioCCompiler, CPPCompiler):
def get_compiler_check_args(self): def get_compiler_check_args(self):
# Visual Studio C++ compiler doesn't support -fpermissive, # Visual Studio C++ compiler doesn't support -fpermissive,
# so just use the plain C args. # so just use the plain C args.
return super(VisualStudioCCompiler, self).get_compiler_check_args() return VisualStudioCCompiler.get_compiler_check_args(self)
class ArmCPPCompiler(ArmCompiler, CPPCompiler): class ArmCPPCompiler(ArmCompiler, CPPCompiler):

@ -741,6 +741,10 @@ class CompilerHolder(InterpreterObject):
'has_multi_arguments': self.has_multi_arguments_method, 'has_multi_arguments': self.has_multi_arguments_method,
'get_supported_arguments': self.get_supported_arguments_method, 'get_supported_arguments': self.get_supported_arguments_method,
'first_supported_argument': self.first_supported_argument_method, 'first_supported_argument': self.first_supported_argument_method,
'has_link_argument': self.has_link_argument_method,
'has_multi_link_arguments': self.has_multi_link_arguments_method,
'get_supported_link_arguments': self.get_supported_link_arguments_method,
'first_supported_link_argument': self.first_supported_link_argument_method,
'unittest_args': self.unittest_args_method, 'unittest_args': self.unittest_args_method,
'symbols_have_underscore_prefix': self.symbols_have_underscore_prefix_method, 'symbols_have_underscore_prefix': self.symbols_have_underscore_prefix_method,
}) })
@ -1183,14 +1187,8 @@ class CompilerHolder(InterpreterObject):
def has_argument_method(self, args, kwargs): def has_argument_method(self, args, kwargs):
args = mesonlib.stringlistify(args) args = mesonlib.stringlistify(args)
if len(args) != 1: if len(args) != 1:
raise InterpreterException('Has_arg takes exactly one argument.') raise InterpreterException('has_argument takes exactly one argument.')
result = self.compiler.has_argument(args[0], self.environment) return self.has_multi_arguments_method(args, kwargs)
if result:
h = mlog.green('YES')
else:
h = mlog.red('NO')
mlog.log('Compiler for {} supports argument {}:'.format(self.compiler.get_display_language(), args[0]), h)
return result
@permittedMethodKwargs({}) @permittedMethodKwargs({})
def has_multi_arguments_method(self, args, kwargs): def has_multi_arguments_method(self, args, kwargs):
@ -1209,26 +1207,58 @@ class CompilerHolder(InterpreterObject):
@permittedMethodKwargs({}) @permittedMethodKwargs({})
def get_supported_arguments_method(self, args, kwargs): def get_supported_arguments_method(self, args, kwargs):
args = mesonlib.stringlistify(args) args = mesonlib.stringlistify(args)
result = self.compiler.get_supported_arguments(args, self.environment) supported_args = []
if len(result) == len(args): for arg in args:
if self.has_argument_method(arg, kwargs):
supported_args.append(arg)
return supported_args
@permittedMethodKwargs({})
def first_supported_argument_method(self, args, kwargs):
for i in mesonlib.stringlistify(args):
if self.has_argument_method(i, kwargs):
mlog.log('First supported argument:', mlog.bold(i))
return [i]
mlog.log('First supported argument:', mlog.red('None'))
return []
@permittedMethodKwargs({})
def has_link_argument_method(self, args, kwargs):
args = mesonlib.stringlistify(args)
if len(args) != 1:
raise InterpreterException('has_link_argument takes exactly one argument.')
return self.has_multi_link_arguments_method(args, kwargs)
@permittedMethodKwargs({})
def has_multi_link_arguments_method(self, args, kwargs):
args = mesonlib.stringlistify(args)
result = self.compiler.has_multi_link_arguments(args, self.environment)
if result:
h = mlog.green('YES') h = mlog.green('YES')
elif len(result) > 0:
h = mlog.yellow('SOME')
else: else:
h = mlog.red('NO') h = mlog.red('NO')
mlog.log( mlog.log(
'Compiler for {} supports arguments {}:'.format( 'Compiler for {} supports link arguments {}:'.format(
self.compiler.get_display_language(), ' '.join(args)), self.compiler.get_display_language(), ' '.join(args)),
h) h)
return result return result
@permittedMethodKwargs({}) @permittedMethodKwargs({})
def first_supported_argument_method(self, args, kwargs): def get_supported_link_arguments_method(self, args, kwargs):
args = mesonlib.stringlistify(args)
supported_args = []
for arg in args:
if self.has_link_argument_method(arg, kwargs):
supported_args.append(arg)
return supported_args
@permittedMethodKwargs({})
def first_supported_link_argument_method(self, args, kwargs):
for i in mesonlib.stringlistify(args): for i in mesonlib.stringlistify(args):
if self.compiler.has_argument(i, self.environment): if self.has_link_argument_method(i, kwargs):
mlog.log('First supported argument:', mlog.bold(i)) mlog.log('First supported link argument:', mlog.bold(i))
return [i] return [i]
mlog.log('First supported argument:', mlog.red('None')) mlog.log('First supported link argument:', mlog.red('None'))
return [] return []
ModuleState = namedtuple('ModuleState', [ ModuleState = namedtuple('ModuleState', [

@ -0,0 +1,42 @@
project('has link arg', 'c', 'cpp')
cc = meson.get_compiler('c')
cpp = meson.get_compiler('cpp')
if cc.get_id() == 'msvc'
is_arg = '/OPT:REF'
useless = '/DEBUG'
isnt_arg = '/iambroken'
else
is_arg = '-Wl,-Lfoo'
useless = '-Wl,-Lbar'
isnt_arg = '-Wl,-iambroken'
endif
assert(cc.has_link_argument(is_arg), 'Arg that should have worked does not work.')
assert(not cc.has_link_argument(isnt_arg), 'Arg that should be broken is not.')
assert(cpp.has_link_argument(is_arg), 'Arg that should have worked does not work.')
assert(not cpp.has_link_argument(isnt_arg), 'Arg that should be broken is not.')
assert(cc.get_supported_link_arguments([is_arg, isnt_arg, useless]) == [is_arg, useless], 'Arg filtering returned different result.')
assert(cpp.get_supported_link_arguments([is_arg, isnt_arg, useless]) == [is_arg, useless], 'Arg filtering returned different result.')
# Have useless at the end to ensure that the search goes from front to back.
l1 = cc.first_supported_link_argument([isnt_arg, is_arg, isnt_arg, useless])
l2 = cc.first_supported_link_argument(isnt_arg, isnt_arg, isnt_arg)
assert(l1.length() == 1, 'First supported returned wrong result.')
assert(l1.get(0) == is_arg, 'First supported returned wrong argument.')
assert(l2.length() == 0, 'First supported did not return empty array.')
l1 = cpp.first_supported_link_argument([isnt_arg, is_arg, isnt_arg, useless])
l2 = cpp.first_supported_link_argument(isnt_arg, isnt_arg, isnt_arg)
assert(l1.length() == 1, 'First supported returned wrong result.')
assert(l1.get(0) == is_arg, 'First supported returned wrong argument.')
assert(l2.length() == 0, 'First supported did not return empty array.')
assert(not cc.has_multi_link_arguments([isnt_arg, is_arg]), 'Arg that should be broken is not.')
assert(cc.has_multi_link_arguments(is_arg), 'Arg that should have worked does not work.')
assert(cc.has_multi_link_arguments([useless, is_arg]), 'Arg that should have worked does not work.')
Loading…
Cancel
Save