diff options
-rw-r--r-- | docs/markdown/Reference-manual.md | 12 | ||||
-rw-r--r-- | docs/markdown/Reference-tables.md | 63 | ||||
-rw-r--r-- | docs/markdown/snippets/function_attributes.md | 29 | ||||
-rw-r--r-- | mesonbuild/compilers/c.py | 29 | ||||
-rw-r--r-- | mesonbuild/compilers/c_function_attributes.py | 123 | ||||
-rw-r--r-- | mesonbuild/compilers/compilers.py | 4 | ||||
-rw-r--r-- | mesonbuild/compilers/cpp.py | 6 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 20 | ||||
-rw-r--r-- | test cases/common/204 function attributes/meson.build | 103 |
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++)') |