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
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
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,
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
the specified command line argument, that is, can compile code
without erroring out or printing a warning about an unknown flag,
you can specify external dependencies to use with `dependencies`
keyword argument.
without erroring out or printing a warning about an unknown flag.
- `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
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
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,
you can specify external dependencies to use with `dependencies`
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
return args
def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile', want_output=False):
args = self._get_compiler_check_args(env, extra_args, dependencies, mode)
# We only want to compile; not link
with self.compile(code, args.to_native(), mode) as p:
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 _links_wrapper(self, code, env, extra_args, dependencies, want_output=False):
"Shares common code between self.links and self.run"
args = self._get_compiler_check_args(env, extra_args, dependencies, mode='link')
return self.compile(code, args, want_output=want_output)
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.to_native(), mode, want_output=want_output)
def links(self, code, env, extra_args=None, dependencies=None):
with self._links_wrapper(code, env, extra_args, dependencies) as p:
return p.returncode == 0
return self.compiles(code, env, extra_args, dependencies, mode='link')
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._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:
mlog.debug('Could not compile test file %s: %d\n' % (
p.input_name,
@ -841,6 +837,12 @@ class CCompiler(Compiler):
return []
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):
for arg in args[:]:
# some compilers, e.g. GCC, don't warn for unsupported warning-disable
@ -849,13 +851,21 @@ class CCompiler(Compiler):
if arg.startswith('-Wno-'):
args.append('-W' + arg[5:])
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 self.compiles('int i;\n', env, extra_args=args)
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. 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):
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):
return ['-shared']
def has_multi_arguments(self, args, env):
return super().has_multi_arguments(args + ['-diag-error', '10006'], env)
def has_arguments(self, args, env, code, mode):
return super().has_arguments(args + ['-diag-error', '10006'], env, code, mode)
class VisualStudioCCompiler(CCompiler):
@ -1066,6 +1076,9 @@ class VisualStudioCCompiler(CCompiler):
def get_linker_search_args(self, dirname):
return ['/LIBPATH:' + dirname]
def linker_to_compiler_args(self, args):
return ['/link'] + args
def get_gui_app_args(self):
return ['/SUBSYSTEM:WINDOWS']
@ -1149,24 +1162,12 @@ class VisualStudioCCompiler(CCompiler):
# Visual Studio is special. It ignores some arguments it does not
# 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
def has_multi_arguments(self, args, env):
warning_text = '9002'
code = 'int i;\n'
(fd, srcname) = tempfile.mkstemp(suffix='.' + self.default_suffix)
os.close(fd)
with open(srcname, 'w') as ofile:
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 has_arguments(self, args, env, code, mode):
warning_text = '4044' if mode == 'link' else '9002'
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)
def get_compile_debugfile_args(self, rel_obj, pch=False):
pdbarr = rel_obj.split('.')[:-1]

@ -745,20 +745,15 @@ class Compiler:
def get_library_dirs(self):
return []
def has_argument(self, arg, env):
return self.has_multi_arguments([arg], env)
def has_multi_arguments(self, args, env):
raise EnvironmentException(
'Language {} does not support has_multi_arguments.'.format(
self.get_display_language()))
def get_supported_arguments(self, args, env):
supported_args = []
for arg in args:
if self.has_argument(arg, env):
supported_args.append(arg)
return supported_args
def has_multi_link_arguments(self, args, env):
raise EnvironmentException(
'Language {} does not support has_multi_link_arguments.'.format(
self.get_display_language()))
def get_cross_extra_flags(self, environment, link):
extra_flags = []
@ -815,7 +810,6 @@ class Compiler:
# Construct the compiler command-line
commands = CompilerArgs(self)
commands.append(srcname)
commands += extra_args
commands += self.get_always_args()
if mode == 'compile':
commands += self.get_compile_only_args()
@ -825,6 +819,10 @@ class Compiler:
else:
output = self._get_compile_output(tmpdirname, mode)
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
commands = self.get_exelist() + commands.to_native()
mlog.debug('Running compile:')

@ -199,20 +199,10 @@ class IntelCPPCompiler(IntelCompiler, CPPCompiler):
def get_option_link_args(self, options):
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):
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)
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):
# Visual Studio C++ compiler doesn't support -fpermissive,
# 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):

@ -741,6 +741,10 @@ class CompilerHolder(InterpreterObject):
'has_multi_arguments': self.has_multi_arguments_method,
'get_supported_arguments': self.get_supported_arguments_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,
'symbols_have_underscore_prefix': self.symbols_have_underscore_prefix_method,
})
@ -1183,14 +1187,8 @@ class CompilerHolder(InterpreterObject):
def has_argument_method(self, args, kwargs):
args = mesonlib.stringlistify(args)
if len(args) != 1:
raise InterpreterException('Has_arg takes exactly one argument.')
result = self.compiler.has_argument(args[0], self.environment)
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
raise InterpreterException('has_argument takes exactly one argument.')
return self.has_multi_arguments_method(args, kwargs)
@permittedMethodKwargs({})
def has_multi_arguments_method(self, args, kwargs):
@ -1209,26 +1207,58 @@ class CompilerHolder(InterpreterObject):
@permittedMethodKwargs({})
def get_supported_arguments_method(self, args, kwargs):
args = mesonlib.stringlistify(args)
result = self.compiler.get_supported_arguments(args, self.environment)
if len(result) == len(args):
supported_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')
elif len(result) > 0:
h = mlog.yellow('SOME')
else:
h = mlog.red('NO')
mlog.log(
'Compiler for {} supports arguments {}:'.format(
'Compiler for {} supports link arguments {}:'.format(
self.compiler.get_display_language(), ' '.join(args)),
h)
return result
@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):
if self.compiler.has_argument(i, self.environment):
mlog.log('First supported argument:', mlog.bold(i))
if self.has_link_argument_method(i, kwargs):
mlog.log('First supported link argument:', mlog.bold(i))
return [i]
mlog.log('First supported argument:', mlog.red('None'))
mlog.log('First supported link argument:', mlog.red('None'))
return []
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