Add method to check for C/C++ function attributes

It's fairly common on Linux and *BSD platforms to check for these
attributes existence, so it makes sense to me to have this checking
build into meson itself. Autotools also has a builtin for handling
these, and by building them in we can short circuit cases that we know
that these don't exist (MSVC).

Additionally this adds support for two common MSVC __declspec
attributes, dllimport and dllexport. This implements the declspec
version (even though GCC has an __attribute__ version that both it and
clang support), since GCC and Clang support the MSVC version as well.
Thus it seems reasonable to assume that most projects will use the
__declspec version over teh __attribute__ version.
pull/3986/head
Dylan Baker 8 years ago committed by Nirbheek Chauhan
parent 8ca463f9f1
commit 51e9db370a
  1. 12
      docs/markdown/Reference-manual.md
  2. 63
      docs/markdown/Reference-tables.md
  3. 29
      docs/markdown/snippets/function_attributes.md
  4. 29
      mesonbuild/compilers/c.py
  5. 123
      mesonbuild/compilers/c_function_attributes.py
  6. 4
      mesonbuild/compilers/compilers.py
  7. 6
      mesonbuild/compilers/cpp.py
  8. 20
      mesonbuild/interpreter.py
  9. 103
      test cases/common/204 function attributes/meson.build

@ -1753,6 +1753,18 @@ the following methods:
- `version()` returns the compiler's version number as a string.
- `has_function_attribute(name)` *(added in 0.48.0)* returns `true` if the
compiler supports the GNU style (`__attribute__(...)`) `name`. This is
preferable to manual compile checks as it may be optimized for compilers that
do not support such attributes.
[This table](Reference-tables.html#gcc-attribute-support) Lists all of the
supported attributes.
- `get_supported_function_attributes(list_of_names)` *(added in 0.48.0)
returns an array containing any names that are supported GCC style
attributes. Equivalent to `has_function_attribute` was called on each of them
individually.
The following keyword arguments can be used:
- `args` can be used to pass a list of compiler arguments that are

@ -99,3 +99,66 @@ These are the parameter names for passing language specific arguments to your bu
| Objective C++ | objcpp_args |
| Rust | rust_args |
| Vala | vala_args |
## Function Attributes
These are the parameters names that are supported using
`compiler.has_function_attribute()` or
`compiler.get_supported_function_attributes()`
### GCC __attribute__
These values are supported using the GCC style `__attribute__` annotations,
which are supported by GCC, Clang, and other compilers.
| Name |
|----------------------|
| alias |
| aligned |
| alloc_size |
| always_inline |
| artificial |
| cold |
| const |
| constructor |
| constructor_priority |
| deprecated |
| destructor |
| error |
| externally_visible |
| fallthrough |
| flatten |
| format |
| format_arg |
| gnu_inline |
| hot |
| ifunc |
| malloc |
| noclone |
| noinline |
| nonnull |
| noreturn |
| nothrow |
| optimize |
| packed |
| pure |
| returns_nonnull |
| unused |
| used |
| visibility |
| warning |
| warn_unused_result |
| weak |
| weakreaf |
### MSVC __declspec
These values are supported using the MSVC style `__declspec` annotation,
which are supported by MSVC, GCC, Clang, and other compilers.
| Name |
|----------------------|
| dllexport |
| dllimport |

@ -0,0 +1,29 @@
## Helper methods added for checking GNU style attributes: __attribute__(...)
A set of new helpers have been added to the C and C++ compiler objects for
checking GNU style function attributes. These are not just simpler to use, they
may be optimized to return fast on compilers that don't support these
attributes. Currently this is true for MSVC.
```meson
cc = meson.get_compiler('c')
if cc.has_function_attribute('aligned')
add_project_arguments('-DHAVE_ALIGNED', language : 'c')
endif
```
Would replace code like:
```meson
if cc.compiles('''into foo(void) __attribute__((aligned(32)))''')
add_project_arguments('-DHAVE_ALIGNED', language : 'c')
endif
```
Additionally, a multi argument version has been added:
```meson
foreach s : cc.get_supported_function_attributes(['hidden', 'alias'])
add_project_arguments('-DHAVE_@0@'.format(s.to_upper()), language : 'c')
endforeach
```

@ -24,9 +24,10 @@ from .. import mlog
from .. import coredata
from . import compilers
from ..mesonlib import (
EnvironmentException, version_compare, Popen_safe, listify,
EnvironmentException, MesonException, version_compare, Popen_safe, listify,
for_windows, for_darwin, for_cygwin, for_haiku, for_openbsd,
)
from .c_function_attributes import C_FUNC_ATTRIBUTES
from .compilers import (
GCC_MINGW,
@ -57,6 +58,13 @@ class CCompiler(Compiler):
find_library_cache = {}
internal_libs = gnu_compiler_internal_libs
@staticmethod
def attribute_check_func(name):
try:
return C_FUNC_ATTRIBUTES[name]
except KeyError:
raise MesonException('Unknown function attribute "{}"'.format(name))
def __init__(self, exelist, version, is_cross, exe_wrapper=None, **kwargs):
# If a child ObjC or CPP class has already set it, don't set it ourselves
if not hasattr(self, 'language'):
@ -1045,6 +1053,19 @@ class CCompiler(Compiler):
m = pattern.match(ret)
return ret
def has_func_attribute(self, name, env):
# Just assume that if we're not on windows that dllimport and dllexport
# don't work
if not (for_windows(env.is_cross_build(), env) or
for_cygwin(env.is_cross_build(), env)):
if name in ['dllimport', 'dllexport']:
return False
# Clang and GCC both return warnings if the __attribute__ is undefined,
# so set -Werror
return self.compiles(self.attribute_check_func(name), env, extra_args='-Werror')
class ClangCCompiler(ClangCompiler, CCompiler):
def __init__(self, exelist, version, clang_type, is_cross, exe_wrapper=None, **kwargs):
CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper, **kwargs)
@ -1490,6 +1511,12 @@ class VisualStudioCCompiler(CCompiler):
assert(buildtype == 'custom')
raise EnvironmentException('Requested C runtime based on buildtype, but buildtype is "custom".')
def has_func_attribute(self, name, env):
# MSVC doesn't have __attribute__ like Clang and GCC do, so just return
# false without compiling anything
return name in ['dllimport', 'dllexport']
class ArmCCompiler(ArmCompiler, CCompiler):
def __init__(self, exelist, version, is_cross, exe_wrapper=None, **kwargs):
CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper, **kwargs)

@ -0,0 +1,123 @@
# These functions are based on the following code:
# https://git.savannah.gnu.org/gitweb/?p=autoconf-archive.git;a=blob_plain;f=m4/ax_gcc_func_attribute.m4,
# which is licensed under the following terms:
#
# Copyright (c) 2013 Gabriele Svelto <gabriele.svelto@gmail.com>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#
C_FUNC_ATTRIBUTES = {
'alias': '''
int foo(void) { return 0; }
int bar(void) __attribute__((alias("foo")));''',
'aligned':
'int foo(void) __attribute__((aligned(32)));',
'alloc_size':
'void *foo(int a) __attribute__((alloc_size(1)));',
'always_inline':
'inline __attribute__((always_inline)) int foo(void) { return 0; }',
'artificial':
'inline __attribute__((artificial)) int foo(void) { return 0; }',
'cold':
'int foo(void) __attribute__((cold));',
'const':
'int foo(void) __attribute__((const));',
'constructor':
'int foo(void) __attribute__((constructor));',
'constructor_priority':
'int foo( void ) __attribute__((__constructor__(65535/2)));',
'deprecated':
'int foo(void) __attribute__((deprecated("")));',
'destructor':
'int foo(void) __attribute__((destructor));',
'dllexport':
'__declspec(dllexport) int foo(void) { return 0; }',
'dllimport':
'__declspec(dllimport) int foo(void);',
'error':
'int foo(void) __attribute__((error("")));',
'externally_visible':
'int foo(void) __attribute__((externally_visible));',
'fallthrough': '''
int foo( void ) {
switch (0) {
case 1: __attribute__((fallthrough));
case 2: break;
}
return 0;
};''',
'flatten':
'int foo(void) __attribute__((flatten));',
'format':
'int foo(const char * p, ...) __attribute__((format(printf, 1, 2)));',
'format_arg':
'char * foo(const char * p) __attribute__((format_arg(1)));',
'gnu_inline':
'inline __attribute__((gnu_inline)) int foo(void) { return 0; }',
'hot':
'int foo(void) __attribute__((hot));',
'ifunc':
('int my_foo(void) { return 0; }'
'static int (*resolve_foo(void))(void) { return my_foo; }'
'int foo(void) __attribute__((ifunc("resolve_foo")));'),
'leaf':
'__attribute__((leaf)) int foo(void) { return 0; }',
'malloc':
'int *foo(void) __attribute__((malloc));',
'noclone':
'int foo(void) __attribute__((noclone));',
'noinline':
'__attribute__((noinline)) int foo(void) { return 0; }',
'nonnull':
'int foo(char * p) __attribute__((nonnull(1)));',
'noreturn':
'int foo(void) __attribute__((noreturn));',
'nothrow':
'int foo(void) __attribute__((nothrow));',
'optimize':
'__attribute__((optimize(3))) int foo(void) { return 0; }',
'packed':
'struct __attribute__((packed)) foo { int bar; };',
'pure':
'int foo(void) __attribute__((pure));',
'returns_nonnull':
'int *foo(void) __attribute__((returns_nonnull));',
'unused':
'int foo(void) __attribute__((unused));',
'used':
'int foo(void) __attribute__((used));',
'visibility': '''
int foo_def(void) __attribute__((visibility(("default"))));
int foo_hid(void) __attribute__((visibility(("hidden"))));
int foo_int(void) __attribute__((visibility(("internal"))));
int foo_pro(void) __attribute__((visibility(("protected"))));''',
'warning':
'int foo(void) __attribute__((warning("")));',
'warn_unused_result':
'int foo(void) __attribute__((warn_unused_result));',
'weak':
'int foo(void) __attribute__((weak));',
'weakref': '''
static int foo(void) { return 0; }
static int var(void) __attribute__((weakref("foo")));''',
}
CXX_FUNC_ATTRIBUTES = {
# Alias must be applied to the mangled name in C++
'alias':
('extern "C" {'
'int foo(void) { return 0; }'
'}'
'int bar(void) __attribute__((alias("foo")));'
),
'ifunc':
('extern "C" {'
'int my_foo(void) { return 0; }'
'static int (*resolve_foo(void))(void) { return my_foo; }'
'}'
'int foo(void) __attribute__((ifunc("resolve_foo")));'),
}

@ -1137,6 +1137,10 @@ class Compiler:
def get_gui_app_args(self, value):
return []
def has_func_attribute(self, name, env):
raise EnvironmentException(
'Language {} does not support function attributes.'.format(self.get_display_language()))
GCC_STANDARD = 0
GCC_OSX = 1
GCC_MINGW = 2

@ -31,8 +31,14 @@ from .compilers import (
ArmCompiler,
ArmclangCompiler,
)
from .c_function_attributes import CXX_FUNC_ATTRIBUTES
class CPPCompiler(CCompiler):
@classmethod
def attribute_check_func(cls, name):
return CXX_FUNC_ATTRIBUTES.get(name, super().attribute_check_func(name))
def __init__(self, exelist, version, is_cross, exe_wrap, **kwargs):
# If a child ObjCPP class has already set it, don't set it ourselves
if not hasattr(self, 'language'):

@ -975,6 +975,8 @@ class CompilerHolder(InterpreterObject):
'cmd_array': self.cmd_array_method,
'find_library': self.find_library_method,
'has_argument': self.has_argument_method,
'has_function_attribute': self.has_func_attribute_method,
'get_supported_function_attributes': self.get_supported_function_attributes_method,
'has_multi_arguments': self.has_multi_arguments_method,
'get_supported_arguments': self.get_supported_arguments_method,
'first_supported_argument': self.first_supported_argument_method,
@ -1545,6 +1547,24 @@ class CompilerHolder(InterpreterObject):
mlog.log('First supported link argument:', mlog.red('None'))
return []
@FeatureNew('compiler.has_function_attribute', '0.48.0')
@permittedKwargs({})
def has_func_attribute_method(self, args, kwargs):
args = mesonlib.stringlistify(args)
if len(args) != 1:
raise InterpreterException('has_func_attribute takes exactly one argument.')
result = self.compiler.has_func_attribute(args[0], self.environment)
h = mlog.green('YES') if result else mlog.red('NO')
mlog.log('Compiler for {} supports function attribute {}:'.format(self.compiler.get_display_language(), args[0]), h)
return result
@FeatureNew('compiler.get_supported_function_attributes', '0.48.0')
@permittedKwargs({})
def get_supported_function_attributes_method(self, args, kwargs):
args = mesonlib.stringlistify(args)
return [a for a in args if self.has_func_attribute_method(a, kwargs)]
ModuleState = namedtuple('ModuleState', [
'build_to_src', 'subproject', 'subdir', 'current_lineno', 'environment',
'project_name', 'project_version', 'backend', 'compilers', 'targets',

@ -0,0 +1,103 @@
# Copyright © 2017-2018 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
project('gcc func attributes', ['c', 'cpp'])
# For msvc these will fail because msvc doesn't support __attribute__, for
# Clang and GCC, they should pass.
c = meson.get_compiler('c')
cpp = meson.get_compiler('cpp')
expected_result = c.get_id() != 'msvc'
# Q: Why is ifunc not in this list or any of the below lists?
# A: It's too damn hard to figure out if you actually support it, since it
# requires both compiler and libc support, and there isn't a good way to
# figure that out except by running the code we're trying to test.
attributes = [
'aligned',
'alloc_size',
'always_inline',
'cold',
'const',
'constructor',
'constructor_priority',
'deprecated',
'destructor',
'flatten',
'format',
'format_arg',
'gnu_inline',
'hot',
'malloc',
'noinline',
'nonnull',
'noreturn',
'nothrow',
'pure',
'unused',
'used',
'warn_unused_result',
'weak',
'weakref',
]
# These are unsupported on darwin with apple clang 9.1.0
if host_machine.system() != 'darwin'
attributes += 'alias'
attributes += 'visibility'
endif
if c.get_id() == 'gcc'
# not supported by clang as of 5.0.0 (at least up to 6.0.1)
attributes += 'artificial'
attributes += 'error'
attributes += 'externally_visible'
attributes += 'leaf'
attributes += 'noclone'
attributes += 'optimize'
attributes += 'warning'
if c.version().version_compare('>= 7.0.0')
attributes += 'fallthrough'
endif
endif
foreach a : attributes
x = c.has_function_attribute(a)
assert(x == expected_result, '@0@: @1@'.format(c.get_id(), a))
x = cpp.has_function_attribute(a)
assert(x == expected_result, '@0@: @1@'.format(cpp.get_id(), a))
endforeach
win_expect = ['windows', 'cygwin'].contains(host_machine.system())
foreach a : ['dllexport', 'dllimport']
assert(c.has_function_attribute(a) == win_expect,
'@0@: @1@'.format(c.get_id(), a))
assert(cpp.has_function_attribute(a) == win_expect,
'@0@: @1@'.format(cpp.get_id(), a))
endforeach
message('checking get_supported_function_attributes')
if c.get_id() != 'msvc'
multi_expected = attributes
else
multi_expected = []
endif
multi_check = c.get_supported_function_attributes(attributes)
assert(multi_check == multi_expected, 'get_supported_function_arguments works (C)')
multi_check = cpp.get_supported_function_attributes(attributes)
assert(multi_check == multi_expected, 'get_supported_function_arguments works (C++)')
Loading…
Cancel
Save