aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/Reference-manual.md12
-rw-r--r--docs/markdown/Reference-tables.md63
-rw-r--r--docs/markdown/snippets/function_attributes.md29
-rw-r--r--mesonbuild/compilers/c.py29
-rw-r--r--mesonbuild/compilers/c_function_attributes.py123
-rw-r--r--mesonbuild/compilers/compilers.py4
-rw-r--r--mesonbuild/compilers/cpp.py6
-rw-r--r--mesonbuild/interpreter.py20
-rw-r--r--test cases/common/204 function attributes/meson.build103
9 files changed, 388 insertions, 1 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index e59c153..71771a5 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -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
diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md
index 46bcc3d..ab79abd 100644
--- a/docs/markdown/Reference-tables.md
+++ b/docs/markdown/Reference-tables.md
@@ -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 |
diff --git a/docs/markdown/snippets/function_attributes.md b/docs/markdown/snippets/function_attributes.md
new file mode 100644
index 0000000..5514494
--- /dev/null
+++ b/docs/markdown/snippets/function_attributes.md
@@ -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
+```
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index 436f699..bde9f63 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -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)
diff --git a/mesonbuild/compilers/c_function_attributes.py b/mesonbuild/compilers/c_function_attributes.py
new file mode 100644
index 0000000..9aeaaf2
--- /dev/null
+++ b/mesonbuild/compilers/c_function_attributes.py
@@ -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")));'),
+}
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index 36507b0..88e9bce 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -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
diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py
index 85766f7..2173655 100644
--- a/mesonbuild/compilers/cpp.py
+++ b/mesonbuild/compilers/cpp.py
@@ -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'):
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 98fab7c..0e6a041 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -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',
diff --git a/test cases/common/204 function attributes/meson.build b/test cases/common/204 function attributes/meson.build
new file mode 100644
index 0000000..bc049d7
--- /dev/null
+++ b/test cases/common/204 function attributes/meson.build
@@ -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++)')