Skip to content

Commit

Permalink
Add has_link_argument() and friends
Browse files Browse the repository at this point in the history
  • Loading branch information
xclaesse committed Apr 12, 2018
1 parent 950c01f commit afdcd87
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 21 deletions.
22 changes: 19 additions & 3 deletions docs/markdown/Reference-manual.md
Expand Up @@ -1497,6 +1497,10 @@ the following methods:
strings, returns the first argument that passes the `has_argument`
test above 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 above 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.

Expand All @@ -1507,11 +1511,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. Note
that linker arguments will be converted to compiler arguments by prepending
'-Wl,' if needed, or by adding '/link' argument for VisualStudio.

- `has_function(funcname)` returns true if the given function is
provided by the standard library or a library passed in with the
Expand Down Expand Up @@ -1546,6 +1558,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.
Expand Down
9 changes: 9 additions & 0 deletions docs/markdown/snippets/has-link-argument.md
@@ -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()`
34 changes: 27 additions & 7 deletions mesonbuild/compilers/c.py
Expand Up @@ -830,16 +830,34 @@ def thread_link_flags(self, env):
return []
return ['-pthread']

def linker_to_compiler_args(self, args):
result = []
for arg in args:
if arg.startswith('-Wl,'):
result.append(arg)
else:
result.append('-Wl,' + arg)
return result

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:
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.
has_link_argument or other similar method can be used instead.
'''.format(arg))
return self.compiles('int i;\n', env, extra_args=args)
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):
Expand Down Expand Up @@ -942,8 +960,8 @@ def get_option_compile_args(self, options):
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):
Expand Down Expand Up @@ -1027,6 +1045,9 @@ def get_linker_output_args(self, outputname):
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']

Expand Down Expand Up @@ -1107,10 +1128,9 @@ def get_include_args(self, path, is_system):
# 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'
with self._build_wrapper(code, env, extra_args=args, mode='compile') as p:
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)
Expand Down
10 changes: 9 additions & 1 deletion mesonbuild/compilers/compilers.py
Expand Up @@ -718,6 +718,11 @@ def has_multi_arguments(self, args, env):
'Language {} does not support has_multi_arguments.'.format(
self.get_display_language()))

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 = []
if self.is_cross and environment:
Expand Down Expand Up @@ -760,7 +765,6 @@ def compile(self, code, extra_args=None, mode='link'):
# 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()
Expand All @@ -769,6 +773,10 @@ def compile(self, code, extra_args=None, mode='link'):
commands += self.get_preprocess_only_args()
else:
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:')
Expand Down
10 changes: 0 additions & 10 deletions mesonbuild/compilers/cpp.py
Expand Up @@ -174,16 +174,6 @@ def get_option_compile_args(self, options):
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):
Expand Down
43 changes: 43 additions & 0 deletions mesonbuild/interpreter.py
Expand Up @@ -741,6 +741,10 @@ def __init__(self, compiler, env):
'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,
})
Expand Down Expand Up @@ -1218,6 +1222,45 @@ def first_supported_argument_method(self, args, kwargs):
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')
else:
h = mlog.red('NO')
mlog.log(
'Compiler for {} supports link arguments {}:'.format(
self.compiler.get_display_language(), ' '.join(args)),
h)
return result

@permittedMethodKwargs({})
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.has_link_argument_method(i, kwargs):
mlog.log('First supported link argument:', mlog.bold(i))
return [i]
mlog.log('First supported link argument:', mlog.red('None'))
return []

ModuleState = namedtuple('ModuleState', [
'build_to_src', 'subproject', 'subdir', 'current_lineno', 'environment',
'project_name', 'project_version', 'backend', 'compilers', 'targets',
Expand Down
42 changes: 42 additions & 0 deletions test cases/common/189 has link arg/meson.build
@@ -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.')

0 comments on commit afdcd87

Please sign in to comment.