aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/Builtin-options.md18
-rw-r--r--docs/markdown/snippets/b_sanitizer_changes.md17
-rw-r--r--mesonbuild/backend/vs2010backend.py3
-rw-r--r--mesonbuild/compilers/compilers.py16
-rw-r--r--mesonbuild/compilers/cuda.py6
-rw-r--r--mesonbuild/compilers/mixins/gnu.py11
-rw-r--r--mesonbuild/compilers/mixins/islinker.py3
-rw-r--r--mesonbuild/compilers/mixins/visualstudio.py11
-rw-r--r--mesonbuild/interpreter/interpreter.py17
-rw-r--r--mesonbuild/linkers/linkers.py22
-rw-r--r--mesonbuild/modules/gnome.py4
-rw-r--r--test cases/unit/125 sanitizers/meson.build8
-rw-r--r--unittests/allplatformstests.py5
-rw-r--r--unittests/linuxliketests.py22
14 files changed, 119 insertions, 44 deletions
diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md
index ffbab47..e1686f8 100644
--- a/docs/markdown/Builtin-options.md
+++ b/docs/markdown/Builtin-options.md
@@ -231,10 +231,20 @@ available on all platforms or with all compilers:
| b_pie | false | true, false | Build position-independent executables (since 0.49.0) |
| b_vscrt | from_buildtype | none, md, mdd, mt, mtd, from_buildtype, static_from_buildtype | VS runtime library to use (since 0.48.0) (static_from_buildtype since 0.56.0) |
-The value of `b_sanitize` can be one of: `none`, `address`, `thread`,
-`undefined`, `memory`, `leak`, `address,undefined`, but note that some
-compilers might not support all of them. For example Visual Studio
-only supports the address sanitizer.
+The default and possible values of sanitizers changed in 1.8. Before 1.8 they
+were string values, and restricted to a specific subset of values: `none`,
+`address`, `thread`, `undefined`, `memory`, `leak`, or `address,undefined`. In
+1.8 it was changed to a free form array of sanitizers, which are checked by a
+compiler and linker check. For backwards compatibility reasons
+`get_option('b_sanitize')` continues to return a string with the array values
+separated by a comma. Furthermore:
+
+ - If the `b_sanitize` option is empty, the `'none'` string is returned.
+
+ - If it contains only the values `'address'` and `'undefined'`, they are
+ always returned as the `'address,undefined'` string, in this order.
+
+ - Otherwise, the array elements are returned in undefined order.
\* < 0 means disable, == 0 means automatic selection, > 0 sets a specific number to use
diff --git a/docs/markdown/snippets/b_sanitizer_changes.md b/docs/markdown/snippets/b_sanitizer_changes.md
new file mode 100644
index 0000000..f726d70
--- /dev/null
+++ b/docs/markdown/snippets/b_sanitizer_changes.md
@@ -0,0 +1,17 @@
+## Changes to the b_sanitize option
+
+Before 1.8 the `b_sanitize` option was a combo option, which is an enumerated
+set of values. In 1.8 this was changed to a free-form array of options where
+available sanitizers are not hardcoded anymore but instead verified via a
+compiler check.
+
+This solves a number of longstanding issues such as:
+
+ - Sanitizers may be supported by a compiler, but not on a specific platform
+ (OpenBSD).
+ - New sanitizers are not recognized by Meson.
+ - Using sanitizers in previously-unsupported combinations.
+
+To not break backwards compatibility, calling `get_option('b_sanitize')`
+continues to return the configured value as a string, with a guarantee that
+`address,undefined` remains ordered.
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index 1015083..feef3a7 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2014-2016 The Meson development team
+# Copyright © 2023-2024 Intel Corporation
from __future__ import annotations
import copy
@@ -272,7 +273,7 @@ class Vs2010Backend(backends.Backend):
try:
self.sanitize = self.environment.coredata.get_option(OptionKey('b_sanitize'))
except KeyError:
- self.sanitize = 'none'
+ self.sanitize = []
sln_filename = os.path.join(self.environment.get_build_dir(), self.build.project_name + '.sln')
projlist = self.generate_projects(vslite_ctx)
self.gen_testproj()
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index 27bc44b..f0744f8 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -222,9 +222,7 @@ BASE_OPTIONS: T.Mapping[OptionKey, options.AnyOptionType] = {
options.UserComboOption('b_lto_mode', 'Select between different LTO modes.', 'default', choices=['default', 'thin']),
options.UserBooleanOption('b_thinlto_cache', 'Use LLVM ThinLTO caching for faster incremental builds', False),
options.UserStringOption('b_thinlto_cache_dir', 'Directory to store ThinLTO cache objects', ''),
- options.UserComboOption(
- 'b_sanitize', 'Code sanitizer to use', 'none',
- choices=['none', 'address', 'thread', 'undefined', 'memory', 'leak', 'address,undefined']),
+ options.UserStringArrayOption('b_sanitize', 'Code sanitizer to use', []),
options.UserBooleanOption('b_lundef', 'Use -Wl,--no-undefined when linking', True),
options.UserBooleanOption('b_asneeded', 'Use -Wl,--as-needed when linking', True),
options.UserComboOption(
@@ -307,7 +305,9 @@ def get_base_compile_args(target: 'BuildTarget', compiler: 'Compiler', env: 'Env
pass
try:
sanitize = env.coredata.get_option_for_target(target, 'b_sanitize')
- assert isinstance(sanitize, str)
+ assert isinstance(sanitize, list)
+ if sanitize == ['none']:
+ sanitize = []
sanitize_args = compiler.sanitizer_compile_args(sanitize)
# We consider that if there are no sanitizer arguments returned, then
# the language doesn't support them.
@@ -376,7 +376,9 @@ def get_base_link_args(target: 'BuildTarget',
pass
try:
sanitizer = env.coredata.get_option_for_target(target, 'b_sanitize')
- assert isinstance(sanitizer, str)
+ assert isinstance(sanitizer, list)
+ if sanitizer == ['none']:
+ sanitizer = []
sanitizer_args = linker.sanitizer_link_args(sanitizer)
# We consider that if there are no sanitizer arguments returned, then
# the language doesn't support them.
@@ -1044,10 +1046,10 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
thinlto_cache_dir: T.Optional[str] = None) -> T.List[str]:
return self.linker.get_lto_args()
- def sanitizer_compile_args(self, value: str) -> T.List[str]:
+ def sanitizer_compile_args(self, value: T.List[str]) -> T.List[str]:
return []
- def sanitizer_link_args(self, value: str) -> T.List[str]:
+ def sanitizer_link_args(self, value: T.List[str]) -> T.List[str]:
return self.linker.sanitizer_args(value)
def get_asneeded_args(self) -> T.List[str]:
diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py
index 134cd4e..612643c 100644
--- a/mesonbuild/compilers/cuda.py
+++ b/mesonbuild/compilers/cuda.py
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2012-2017 The Meson development team
-# Copyright © 2024 Intel Corporation
+# Copyright © 2023-2024 Intel Corporation
from __future__ import annotations
@@ -700,10 +700,10 @@ class CudaCompiler(Compiler):
# return self._to_host_flags(self.host_compiler.get_optimization_args(optimization_level))
return cuda_optimization_args[optimization_level]
- def sanitizer_compile_args(self, value: str) -> T.List[str]:
+ def sanitizer_compile_args(self, value: T.List[str]) -> T.List[str]:
return self._to_host_flags(self.host_compiler.sanitizer_compile_args(value))
- def sanitizer_link_args(self, value: str) -> T.List[str]:
+ def sanitizer_link_args(self, value: T.List[str]) -> T.List[str]:
return self._to_host_flags(self.host_compiler.sanitizer_link_args(value))
def get_debug_args(self, is_debug: bool) -> T.List[str]:
diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py
index 4dc3445..70fd9ee 100644
--- a/mesonbuild/compilers/mixins/gnu.py
+++ b/mesonbuild/compilers/mixins/gnu.py
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2019-2022 The meson development team
+# Copyright © 2023 Intel Corporation
from __future__ import annotations
@@ -495,11 +496,11 @@ class GnuLikeCompiler(Compiler, metaclass=abc.ABCMeta):
# for their specific arguments
return ['-flto']
- def sanitizer_compile_args(self, value: str) -> T.List[str]:
- if value == 'none':
- return []
- args = ['-fsanitize=' + value]
- if 'address' in value: # for -fsanitize=address,undefined
+ def sanitizer_compile_args(self, value: T.List[str]) -> T.List[str]:
+ if not value:
+ return value
+ args = ['-fsanitize=' + ','.join(value)]
+ if 'address' in value:
args.append('-fno-omit-frame-pointer')
return args
diff --git a/mesonbuild/compilers/mixins/islinker.py b/mesonbuild/compilers/mixins/islinker.py
index 44040a7..3f35619 100644
--- a/mesonbuild/compilers/mixins/islinker.py
+++ b/mesonbuild/compilers/mixins/islinker.py
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2019 The Meson development team
+# Copyright © 2023 Intel Corporation
from __future__ import annotations
@@ -37,7 +38,7 @@ class BasicLinkerIsCompilerMixin(Compiler):
functionality itself.
"""
- def sanitizer_link_args(self, value: str) -> T.List[str]:
+ def sanitizer_link_args(self, value: T.List[str]) -> T.List[str]:
return []
def get_lto_link_args(self, *, threads: int = 0, mode: str = 'default',
diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py
index 30127ec..275e7ab 100644
--- a/mesonbuild/compilers/mixins/visualstudio.py
+++ b/mesonbuild/compilers/mixins/visualstudio.py
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2019 The meson development team
+# Copyright © 2023 Intel Corporation
from __future__ import annotations
@@ -166,12 +167,10 @@ class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta):
def get_no_optimization_args(self) -> T.List[str]:
return ['/Od', '/Oi-']
- def sanitizer_compile_args(self, value: str) -> T.List[str]:
- if value == 'none':
- return []
- if value != 'address':
- raise mesonlib.MesonException('VS only supports address sanitizer at the moment.')
- return ['/fsanitize=address']
+ def sanitizer_compile_args(self, value: T.List[str]) -> T.List[str]:
+ if not value:
+ return value
+ return [f'/fsanitize={",".join(value)}']
def get_output_args(self, outputname: str) -> T.List[str]:
if self.mode == 'PREPROCESSOR':
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
index ff93ac6..42e6243 100644
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -1068,14 +1068,14 @@ class Interpreter(InterpreterBase, HoldableObject):
@typed_pos_args('get_option', str)
@noKwargs
- def func_get_option(self, nodes: mparser.BaseNode, args: T.Tuple[str],
- kwargs: 'TYPE_kwargs') -> T.Union[options.UserOption, 'TYPE_var']:
+ def func_get_option(self, node: mparser.BaseNode, args: T.Tuple[str],
+ kwargs: kwtypes.FuncGetOption) -> T.Union[options.UserOption, 'TYPE_var']:
optname = args[0]
+
if ':' in optname:
raise InterpreterException('Having a colon in option name is forbidden, '
'projects are not allowed to directly access '
'options of other subprojects.')
-
if optname_regex.search(optname.split('.', maxsplit=1)[-1]) is not None:
raise InterpreterException(f'Invalid option name {optname!r}')
@@ -1096,6 +1096,15 @@ class Interpreter(InterpreterBase, HoldableObject):
ocopy.name = optname
ocopy.value = value
return ocopy
+ elif optname == 'b_sanitize':
+ assert isinstance(value_object, options.UserStringArrayOption)
+ # To ensure backwards compatibility this always returns a string.
+ # We may eventually want to introduce a new "format" kwarg that
+ # allows the user to modify this behaviour, but for now this is
+ # likely good enough for most usecases.
+ if not value:
+ return 'none'
+ return ','.join(sorted(value))
elif isinstance(value_object, options.UserOption):
if isinstance(value_object.value, str):
return P_OBJ.OptionString(value, f'{{{optname}}}')
@@ -3090,7 +3099,7 @@ class Interpreter(InterpreterBase, HoldableObject):
if OptionKey('b_sanitize') not in self.coredata.optstore:
return
if (self.coredata.optstore.get_value('b_lundef') and
- self.coredata.optstore.get_value('b_sanitize') != 'none'):
+ self.coredata.optstore.get_value('b_sanitize')):
value = self.coredata.optstore.get_value('b_sanitize')
mlog.warning(textwrap.dedent(f'''\
Trying to use {value} sanitizer on Clang with b_lundef.
diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py
index 0dc2c0b..b114d49 100644
--- a/mesonbuild/linkers/linkers.py
+++ b/mesonbuild/linkers/linkers.py
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2012-2022 The Meson development team
+# Copyright © 2023 Intel Corporation
from __future__ import annotations
@@ -223,7 +224,7 @@ class DynamicLinker(metaclass=abc.ABCMeta):
def get_thinlto_cache_args(self, path: str) -> T.List[str]:
return []
- def sanitizer_args(self, value: str) -> T.List[str]:
+ def sanitizer_args(self, value: T.List[str]) -> T.List[str]:
return []
def get_asneeded_args(self) -> T.List[str]:
@@ -599,6 +600,9 @@ class PosixDynamicLinkerMixin(DynamicLinkerBase):
def get_search_args(self, dirname: str) -> T.List[str]:
return ['-L' + dirname]
+ def sanitizer_args(self, value: T.List[str]) -> T.List[str]:
+ return []
+
class GnuLikeDynamicLinkerMixin(DynamicLinkerBase):
@@ -654,10 +658,10 @@ class GnuLikeDynamicLinkerMixin(DynamicLinkerBase):
def get_lto_args(self) -> T.List[str]:
return ['-flto']
- def sanitizer_args(self, value: str) -> T.List[str]:
- if value == 'none':
- return []
- return ['-fsanitize=' + value]
+ def sanitizer_args(self, value: T.List[str]) -> T.List[str]:
+ if not value:
+ return value
+ return [f'-fsanitize={",".join(value)}']
def get_coverage_args(self) -> T.List[str]:
return ['--coverage']
@@ -811,10 +815,10 @@ class AppleDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
def get_coverage_args(self) -> T.List[str]:
return ['--coverage']
- def sanitizer_args(self, value: str) -> T.List[str]:
- if value == 'none':
- return []
- return ['-fsanitize=' + value]
+ def sanitizer_args(self, value: T.List[str]) -> T.List[str]:
+ if not value:
+ return value
+ return [f'-fsanitize={",".join(value)}']
def no_undefined_args(self) -> T.List[str]:
# We used to emit -undefined,error, but starting with Xcode 15 /
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index dffc615..e3b1f3d 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2015-2016 The Meson development team
+# Copyright © 2023-2024 Intel Corporation
'''This module provides helper functions for Gnome/GLib related
functionality such as gobject-introspection, gresources and gtk-doc'''
@@ -912,9 +913,8 @@ class GnomeModule(ExtensionModule):
cflags += state.project_args[lang]
if OptionKey('b_sanitize') in compiler.base_options:
sanitize = state.environment.coredata.optstore.get_value('b_sanitize')
- assert isinstance(sanitize, str)
+ assert isinstance(sanitize, list)
cflags += compiler.sanitizer_compile_args(sanitize)
- sanitize = sanitize.split(',')
# These must be first in ldflags
if 'address' in sanitize:
internal_ldflags += ['-lasan']
diff --git a/test cases/unit/125 sanitizers/meson.build b/test cases/unit/125 sanitizers/meson.build
new file mode 100644
index 0000000..b42fb35
--- /dev/null
+++ b/test cases/unit/125 sanitizers/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright © 2023-2024 Intel Corporation
+
+project('sanitizer', 'c', meson_version : '>= 1.8')
+
+summary({
+ 'value': get_option('b_sanitize'),
+}, section: 'summary')
diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py
index 7f4e1e7..84d5245 100644
--- a/unittests/allplatformstests.py
+++ b/unittests/allplatformstests.py
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2016-2021 The Meson development team
+# Copyright © 2023-2024 Intel Corporation
import subprocess
import re
@@ -2770,7 +2771,7 @@ class AllPlatformTests(BasePlatformTests):
obj = mesonbuild.coredata.load(self.builddir)
self.assertEqual(obj.optstore.get_value('bindir'), 'bar')
self.assertEqual(obj.optstore.get_value('buildtype'), 'release')
- self.assertEqual(obj.optstore.get_value('b_sanitize'), 'thread')
+ self.assertEqual(obj.optstore.get_value('b_sanitize'), ['thread'])
self.assertEqual(obj.optstore.get_value(OptionKey('c_args')), ['-Dbar'])
self.setconf(['--bindir=bar', '--bindir=foo',
'-Dbuildtype=release', '-Dbuildtype=plain',
@@ -2779,7 +2780,7 @@ class AllPlatformTests(BasePlatformTests):
obj = mesonbuild.coredata.load(self.builddir)
self.assertEqual(obj.optstore.get_value('bindir'), 'foo')
self.assertEqual(obj.optstore.get_value('buildtype'), 'plain')
- self.assertEqual(obj.optstore.get_value('b_sanitize'), 'address')
+ self.assertEqual(obj.optstore.get_value('b_sanitize'), ['address'])
self.assertEqual(obj.optstore.get_value(OptionKey('c_args')), ['-Dfoo'])
self.wipe()
except KeyError:
diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py
index 08eb4b9..01862b6 100644
--- a/unittests/linuxliketests.py
+++ b/unittests/linuxliketests.py
@@ -1930,3 +1930,25 @@ class LinuxlikeTests(BasePlatformTests):
self.check_has_flag(compdb, mainsrc, '-O3')
self.check_has_flag(compdb, sub1src, '-O2')
self.check_has_flag(compdb, sub2src, '-O2')
+
+ def test_sanitizers(self):
+ testdir = os.path.join(self.unit_test_dir, '125 sanitizers')
+
+ with self.subTest('no b_sanitize value'):
+ try:
+ out = self.init(testdir)
+ self.assertRegex(out, 'value *: *none')
+ finally:
+ self.wipe()
+
+ for value, expected in { '': 'none',
+ 'none': 'none',
+ 'address': 'address',
+ 'undefined,address': 'address,undefined',
+ 'address,undefined': 'address,undefined' }.items():
+ with self.subTest('b_sanitize=' + value):
+ try:
+ out = self.init(testdir, extra_args=['-Db_sanitize=' + value])
+ self.assertRegex(out, 'value *: *' + expected)
+ finally:
+ self.wipe()