diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2023-08-30 12:04:09 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-30 12:04:09 -0700 |
commit | 08d83a4a97e66c83d23f10f853d6948921ad144c (patch) | |
tree | ce6b7197fcf7d450419f699f2d06aa1bf31343fa /mesonbuild | |
parent | 494bdbd3345d1c2d20cf2520249962bd32fc61e6 (diff) | |
parent | 82a8c72187f844713618526ed3890d7b313b2065 (diff) | |
download | meson-08d83a4a97e66c83d23f10f853d6948921ad144c.zip meson-08d83a4a97e66c83d23f10f853d6948921ad144c.tar.gz meson-08d83a4a97e66c83d23f10f853d6948921ad144c.tar.bz2 |
Merge pull request #10332 from xclaesse/std-opt
c_std, cpp_std: Change to a list of desired versions in preference order
Diffstat (limited to 'mesonbuild')
-rw-r--r-- | mesonbuild/compilers/c.py | 114 | ||||
-rw-r--r-- | mesonbuild/compilers/compilers.py | 4 | ||||
-rw-r--r-- | mesonbuild/compilers/cpp.py | 76 | ||||
-rw-r--r-- | mesonbuild/coredata.py | 76 | ||||
-rw-r--r-- | mesonbuild/mcompile.py | 2 | ||||
-rw-r--r-- | mesonbuild/optinterpreter.py | 7 |
6 files changed, 168 insertions, 111 deletions
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index d514650..7f9e584 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -56,6 +56,10 @@ if T.TYPE_CHECKING: else: CompilerMixinBase = object +_ALL_STDS = ['c89', 'c9x', 'c90', 'c99', 'c1x', 'c11', 'c17', 'c18', 'c2x'] +_ALL_STDS += [f'gnu{std[1:]}' for std in _ALL_STDS] +_ALL_STDS += ['iso9899:1990', 'iso9899:199409', 'iso9899:1999', 'iso9899:2011', 'iso9899:2017', 'iso9899:2018'] + class CCompiler(CLikeCompiler, Compiler): def attribute_check_func(self, name: str) -> str: @@ -101,12 +105,9 @@ class CCompiler(CLikeCompiler, Compiler): def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() + key = OptionKey('std', machine=self.for_machine, lang=self.language) opts.update({ - OptionKey('std', machine=self.for_machine, lang=self.language): coredata.UserComboOption( - 'C language standard to use', - ['none'], - 'none', - ) + key: coredata.UserStdOption('C', _ALL_STDS), }) return opts @@ -125,20 +126,18 @@ class _ClangCStds(CompilerMixinBase): def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() - c_stds = ['c89', 'c99', 'c11'] - g_stds = ['gnu89', 'gnu99', 'gnu11'] + stds = ['c89', 'c99', 'c11'] # https://releases.llvm.org/6.0.0/tools/clang/docs/ReleaseNotes.html # https://en.wikipedia.org/wiki/Xcode#Latest_versions if version_compare(self.version, self._C17_VERSION): - c_stds += ['c17'] - g_stds += ['gnu17'] + stds += ['c17'] if version_compare(self.version, self._C18_VERSION): - c_stds += ['c18'] - g_stds += ['gnu18'] + stds += ['c18'] if version_compare(self.version, self._C2X_VERSION): - c_stds += ['c2x'] - g_stds += ['gnu2x'] - opts[OptionKey('std', machine=self.for_machine, lang=self.language)].choices = ['none'] + c_stds + g_stds + stds += ['c2x'] + std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(stds, gnu=True) return opts @@ -244,8 +243,9 @@ class ArmclangCCompiler(ArmclangCompiler, CCompiler): def get_options(self) -> 'MutableKeyedOptionDictType': opts = CCompiler.get_options(self) - key = OptionKey('std', machine=self.for_machine, lang=self.language) - opts[key].choices = ['none', 'c90', 'c99', 'c11', 'gnu90', 'gnu99', 'gnu11'] + std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(['c90', 'c99', 'c11'], gnu=True) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: @@ -285,16 +285,15 @@ class GnuCCompiler(GnuCompiler, CCompiler): def get_options(self) -> 'MutableKeyedOptionDictType': opts = CCompiler.get_options(self) - c_stds = ['c89', 'c99', 'c11'] - g_stds = ['gnu89', 'gnu99', 'gnu11'] + stds = ['c89', 'c99', 'c11'] if version_compare(self.version, self._C18_VERSION): - c_stds += ['c17', 'c18'] - g_stds += ['gnu17', 'gnu18'] + stds += ['c17', 'c18'] if version_compare(self.version, self._C2X_VERSION): - c_stds += ['c2x'] - g_stds += ['gnu2x'] + stds += ['c2x'] key = OptionKey('std', machine=self.for_machine, lang=self.language) - opts[key].choices = ['none'] + c_stds + g_stds + std_opt = opts[key] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(stds, gnu=True) if self.info.is_windows() or self.info.is_cygwin(): opts.update({ key.evolve('winlibs'): coredata.UserArrayOption( @@ -370,7 +369,9 @@ class ElbrusCCompiler(ElbrusCompiler, CCompiler): stds += ['c90', 'c1x', 'gnu90', 'gnu1x', 'iso9899:2011'] if version_compare(self.version, '>=1.26.00'): stds += ['c17', 'c18', 'iso9899:2017', 'iso9899:2018', 'gnu17', 'gnu18'] - opts[OptionKey('std', machine=self.for_machine, lang=self.language)].choices = ['none'] + stds + std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(stds) return opts # Elbrus C compiler does not have lchmod, but there is only linker warning, not compiler error. @@ -404,11 +405,12 @@ class IntelCCompiler(IntelGnuLikeCompiler, CCompiler): def get_options(self) -> 'MutableKeyedOptionDictType': opts = CCompiler.get_options(self) - c_stds = ['c89', 'c99'] - g_stds = ['gnu89', 'gnu99'] + stds = ['c89', 'c99'] if version_compare(self.version, '>=16.0.0'): - c_stds += ['c11'] - opts[OptionKey('std', machine=self.for_machine, lang=self.language)].choices = ['none'] + c_stds + g_stds + stds += ['c11'] + std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(stds, gnu=True) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: @@ -465,33 +467,23 @@ class VisualStudioCCompiler(MSVCCompiler, VisualStudioLikeCCompilerMixin, CCompi def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() - c_stds = ['c89', 'c99'] - # Need to have these to be compatible with projects - # that set c_std to e.g. gnu99. - # https://github.com/mesonbuild/meson/issues/7611 - g_stds = ['gnu89', 'gnu90', 'gnu9x', 'gnu99'] + stds = ['c89', 'c99'] if version_compare(self.version, self._C11_VERSION): - c_stds += ['c11'] - g_stds += ['gnu1x', 'gnu11'] + stds += ['c11'] if version_compare(self.version, self._C17_VERSION): - c_stds += ['c17', 'c18'] - g_stds += ['gnu17', 'gnu18'] - key = OptionKey('std', machine=self.for_machine, lang=self.language) - opts[key].choices = ['none'] + c_stds + g_stds + stds += ['c17', 'c18'] + std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(stds, gnu=True, gnu_deprecated=True) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: args = [] std = options[OptionKey('std', machine=self.for_machine, lang=self.language)] - if std.value.startswith('gnu'): - mlog.log( - 'cl.exe does not actually support gnu standards, and meson ' - 'will instead demote to the nearest ISO C standard. This ' - 'may cause compilation to fail.', once=True) # As of MVSC 16.8, /std:c11 and /std:c17 are the only valid C standard options. - if std.value in {'c11', 'gnu1x', 'gnu11'}: + if std.value in {'c11'}: args.append('/std:c11') - elif std.value in {'c17', 'c18', 'gnu17', 'gnu18'}: + elif std.value in {'c17', 'c18'}: args.append('/std:c17') return args @@ -531,8 +523,9 @@ class IntelClCCompiler(IntelVisualStudioLikeCompiler, VisualStudioLikeCCompilerM def get_options(self) -> 'MutableKeyedOptionDictType': opts = super().get_options() - key = OptionKey('std', machine=self.for_machine, lang=self.language) - opts[key].choices = ['none', 'c89', 'c99', 'c11'] + std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(['c89', 'c99', 'c11']) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: @@ -564,8 +557,9 @@ class ArmCCompiler(ArmCompiler, CCompiler): def get_options(self) -> 'MutableKeyedOptionDictType': opts = CCompiler.get_options(self) - key = OptionKey('std', machine=self.for_machine, lang=self.language) - opts[key].choices = ['none', 'c89', 'c99', 'c11'] + std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(['c89', 'c99', 'c11']) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: @@ -593,8 +587,9 @@ class CcrxCCompiler(CcrxCompiler, CCompiler): def get_options(self) -> 'MutableKeyedOptionDictType': opts = CCompiler.get_options(self) - key = OptionKey('std', machine=self.for_machine, lang=self.language) - opts[key].choices = ['none', 'c89', 'c99'] + std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(['c89', 'c99']) return opts def get_no_stdinc_args(self) -> T.List[str]: @@ -640,8 +635,9 @@ class Xc16CCompiler(Xc16Compiler, CCompiler): def get_options(self) -> 'MutableKeyedOptionDictType': opts = CCompiler.get_options(self) - key = OptionKey('std', machine=self.for_machine, lang=self.language) - opts[key].choices = ['none', 'c89', 'c99', 'gnu89', 'gnu99'] + std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(['c89', 'c99'], gnu=True) return opts def get_no_stdinc_args(self) -> T.List[str]: @@ -685,8 +681,9 @@ class CompCertCCompiler(CompCertCompiler, CCompiler): def get_options(self) -> 'MutableKeyedOptionDictType': opts = CCompiler.get_options(self) - key = OptionKey('std', machine=self.for_machine, lang=self.language) - opts[key].choices = ['none', 'c89', 'c99'] + std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(['c89', 'c99']) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: @@ -722,8 +719,9 @@ class TICCompiler(TICompiler, CCompiler): def get_options(self) -> 'MutableKeyedOptionDictType': opts = CCompiler.get_options(self) - key = OptionKey('std', machine=self.for_machine, lang=self.language) - opts[key].choices = ['none', 'c89', 'c99', 'c11'] + std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(['c89', 'c99', 'c11']) return opts def get_no_stdinc_args(self) -> T.List[str]: diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 7a8ae72..c7af1ca 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1380,11 +1380,11 @@ def get_global_options(lang: str, cargs = coredata.UserArrayOption( description + ' compiler', - comp_options, split_args=True, user_input=True, allow_dups=True) + comp_options, split_args=True, allow_dups=True) largs = coredata.UserArrayOption( description + ' linker', - link_options, split_args=True, user_input=True, allow_dups=True) + link_options, split_args=True, allow_dups=True) if comp.INVOKES_LINKER and comp_key == envkey: # If the compiler acts as a linker driver, and we're using the diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 8c80437..3e96682 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -55,6 +55,10 @@ if T.TYPE_CHECKING: else: CompilerMixinBase = object +_ALL_STDS = ['c++98', 'c++0x', 'c++03', 'c++1y', 'c++1z', 'c++11', 'c++14', 'c++17', 'c++2a', 'c++20', 'c++23'] +_ALL_STDS += [f'gnu{std[1:]}' for std in _ALL_STDS] +_ALL_STDS += ['vc++11', 'vc++14', 'vc++17', 'vc++20', 'vc++latest', 'c++latest'] + def non_msvc_eh_options(eh: str, args: T.List[str]) -> None: if eh == 'none': @@ -178,11 +182,7 @@ class CPPCompiler(CLikeCompiler, Compiler): opts = super().get_options() key = OptionKey('std', machine=self.for_machine, lang=self.language) opts.update({ - key: coredata.UserComboOption( - 'C++ language standard to use', - ['none'], - 'none', - ), + key: coredata.UserStdOption('C++', _ALL_STDS), }) return opts @@ -257,17 +257,15 @@ class ClangCPPCompiler(_StdCPPLibMixin, ClangCompiler, CPPCompiler): key.evolve('rtti'): coredata.UserBooleanOption('Enable RTTI', True), }) cppstd_choices = [ - 'none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z', - 'c++2a', 'c++20', 'gnu++11', 'gnu++14', 'gnu++17', 'gnu++1z', - 'gnu++2a', 'gnu++20', + 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z', 'c++2a', 'c++20', ] if version_compare(self.version, self._CPP23_VERSION): cppstd_choices.append('c++23') - cppstd_choices.append('gnu++23') if version_compare(self.version, self._CPP26_VERSION): cppstd_choices.append('c++26') - cppstd_choices.append('gnu++26') - opts[key.evolve('std')].choices = cppstd_choices + std_opt = opts[key.evolve('std')] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(cppstd_choices, gnu=True) if self.info.is_windows() or self.info.is_cygwin(): opts.update({ key.evolve('winlibs'): coredata.UserArrayOption( @@ -371,10 +369,9 @@ class ArmclangCPPCompiler(ArmclangCompiler, CPPCompiler): 'default', ), }) - opts[key].choices = [ - 'none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'gnu++98', - 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++17', - ] + std_opt = opts[key] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(['c++98', 'c++03', 'c++11', 'c++14', 'c++17'], gnu=True) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: @@ -426,17 +423,16 @@ class GnuCPPCompiler(_StdCPPLibMixin, GnuCompiler, CPPCompiler): ) }) cppstd_choices = [ - 'none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z', - 'c++2a', 'c++20', 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++17', - 'gnu++1z', 'gnu++2a', 'gnu++20', + 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z', + 'c++2a', 'c++20', ] if version_compare(self.version, '>=12.2.0'): cppstd_choices.append('c++23') - cppstd_choices.append('gnu++23') if version_compare(self.version, '>=14.0.0'): cppstd_choices.append('c++26') - cppstd_choices.append('gnu++26') - opts[key].choices = cppstd_choices + std_opt = opts[key] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(cppstd_choices, gnu=True) if self.info.is_windows() or self.info.is_cygwin(): opts.update({ key.evolve('winlibs'): coredata.UserArrayOption( @@ -513,21 +509,21 @@ class ElbrusCPPCompiler(ElbrusCompiler, CPPCompiler): def get_options(self) -> 'MutableKeyedOptionDictType': opts = CPPCompiler.get_options(self) - cpp_stds = ['none', 'c++98', 'gnu++98'] + cpp_stds = ['c++98'] if version_compare(self.version, '>=1.20.00'): - cpp_stds += ['c++03', 'c++0x', 'c++11', 'gnu++03', 'gnu++0x', 'gnu++11'] + cpp_stds += ['c++03', 'c++0x', 'c++11'] if version_compare(self.version, '>=1.21.00') and version_compare(self.version, '<1.22.00'): - cpp_stds += ['c++14', 'gnu++14', 'c++1y', 'gnu++1y'] + cpp_stds += ['c++14', 'c++1y'] if version_compare(self.version, '>=1.22.00'): - cpp_stds += ['c++14', 'gnu++14'] + cpp_stds += ['c++14'] if version_compare(self.version, '>=1.23.00'): - cpp_stds += ['c++1y', 'gnu++1y'] + cpp_stds += ['c++1y'] if version_compare(self.version, '>=1.24.00'): - cpp_stds += ['c++1z', 'c++17', 'gnu++1z', 'gnu++17'] + cpp_stds += ['c++1z', 'c++17'] if version_compare(self.version, '>=1.25.00'): - cpp_stds += ['c++2a', 'gnu++2a'] + cpp_stds += ['c++2a'] if version_compare(self.version, '>=1.26.00'): - cpp_stds += ['c++20', 'gnu++20'] + cpp_stds += ['c++20'] key = OptionKey('std', machine=self.for_machine, lang=self.language) opts.update({ @@ -541,7 +537,9 @@ class ElbrusCPPCompiler(ElbrusCompiler, CPPCompiler): False, ), }) - opts[key].choices = cpp_stds + std_opt = opts[key] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(cpp_stds, gnu=True) return opts # Elbrus C++ compiler does not have lchmod, but there is only linker warning, not compiler error. @@ -615,7 +613,9 @@ class IntelCPPCompiler(IntelGnuLikeCompiler, CPPCompiler): key.evolve('rtti'): coredata.UserBooleanOption('Enable RTTI', True), key.evolve('debugstl'): coredata.UserBooleanOption('STL debug mode', False), }) - opts[key].choices = ['none'] + c_stds + g_stds + std_opt = opts[key] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(c_stds + g_stds) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: @@ -682,7 +682,9 @@ class VisualStudioLikeCPPCompilerMixin(CompilerMixinBase): msvc_winlibs, ), }) - opts[key.evolve('std')].choices = cpp_stds + std_opt = opts[key] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(cpp_stds) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: @@ -846,8 +848,9 @@ class ArmCPPCompiler(ArmCompiler, CPPCompiler): def get_options(self) -> 'MutableKeyedOptionDictType': opts = CPPCompiler.get_options(self) - key = OptionKey('std', machine=self.for_machine, lang=self.language) - opts[key].choices = ['none', 'c++03', 'c++11'] + std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(['c++03', 'c++11']) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: @@ -906,8 +909,9 @@ class TICPPCompiler(TICompiler, CPPCompiler): def get_options(self) -> 'MutableKeyedOptionDictType': opts = CPPCompiler.get_options(self) - key = OptionKey('std', machine=self.for_machine, lang=self.language) - opts[key].choices = ['none', 'c++03'] + std_opt = opts[OptionKey('std', machine=self.for_machine, lang=self.language)] + assert isinstance(std_opt, coredata.UserStdOption), 'for mypy' + std_opt.set_versions(['c++03']) return opts def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index b0dad13..4b0f9af 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -248,23 +248,17 @@ class UserComboOption(UserOption[str]): class UserArrayOption(UserOption[T.List[str]]): def __init__(self, description: str, value: T.Union[str, T.List[str]], - split_args: bool = False, user_input: bool = False, + split_args: bool = False, allow_dups: bool = False, yielding: bool = DEFAULT_YIELDING, choices: T.Optional[T.List[str]] = None, deprecated: T.Union[bool, str, T.Dict[str, str], T.List[str]] = False): super().__init__(description, choices if choices is not None else [], yielding, deprecated) self.split_args = split_args self.allow_dups = allow_dups - self.value = self.validate_value(value, user_input=user_input) - - def listify(self, value: T.Union[str, T.List[str]], user_input: bool = True) -> T.List[str]: - # User input is for options defined on the command line (via -D - # options). Users can put their input in as a comma separated - # string, but for defining options in meson_options.txt the format - # should match that of a combo - if not user_input and isinstance(value, str) and not value.startswith('['): - raise MesonException('Value does not define an array: ' + value) + self.set_value(value) + @staticmethod + def listify_value(value: T.Union[str, T.List[str]], shlex_split_args: bool = False) -> T.List[str]: if isinstance(value, str): if value.startswith('['): try: @@ -274,7 +268,7 @@ class UserArrayOption(UserOption[T.List[str]]): elif value == '': newvalue = [] else: - if self.split_args: + if shlex_split_args: newvalue = split_args(value) else: newvalue = [v.strip() for v in value.split(',')] @@ -284,8 +278,11 @@ class UserArrayOption(UserOption[T.List[str]]): raise MesonException(f'"{value}" should be a string array, but it is not') return newvalue - def validate_value(self, value: T.Union[str, T.List[str]], user_input: bool = True) -> T.List[str]: - newvalue = self.listify(value, user_input) + def listify(self, value: T.Any) -> T.List[T.Any]: + return self.listify_value(value, self.split_args) + + def validate_value(self, value: T.Union[str, T.List[str]]) -> T.List[str]: + newvalue = self.listify(value) if not self.allow_dups and len(set(newvalue)) != len(newvalue): msg = 'Duplicated values in array option is deprecated. ' \ @@ -324,6 +321,59 @@ class UserFeatureOption(UserComboOption): def is_auto(self) -> bool: return self.value == 'auto' +class UserStdOption(UserComboOption): + ''' + UserOption specific to c_std and cpp_std options. User can set a list of + STDs in preference order and it selects the first one supported by current + compiler. + + For historical reasons, some compilers (msvc) allowed setting a GNU std and + silently fell back to C std. This is now deprecated. Projects that support + both GNU and MSVC compilers should set e.g. c_std=gnu11,c11. + + This is not using self.deprecated mechanism we already have for project + options because we want to print a warning if ALL values are deprecated, not + if SOME values are deprecated. + ''' + def __init__(self, lang: str, all_stds: T.List[str]) -> None: + self.lang = lang.lower() + self.all_stds = ['none'] + all_stds + # Map a deprecated std to its replacement. e.g. gnu11 -> c11. + self.deprecated_stds: T.Dict[str, str] = {} + super().__init__(f'{lang} language standard to use', ['none'], 'none') + + def set_versions(self, versions: T.List[str], gnu: bool = False, gnu_deprecated: bool = False) -> None: + assert all(std in self.all_stds for std in versions) + self.choices += versions + if gnu: + gnu_stds_map = {f'gnu{std[1:]}': std for std in versions} + if gnu_deprecated: + self.deprecated_stds.update(gnu_stds_map) + else: + self.choices += gnu_stds_map.keys() + + def validate_value(self, value: T.Union[str, T.List[str]]) -> str: + candidates = UserArrayOption.listify_value(value) + unknown = [std for std in candidates if std not in self.all_stds] + if unknown: + raise MesonException(f'Unknown {self.lang.upper()} std {unknown}. Possible values are {self.all_stds}.') + # Check first if any of the candidates are not deprecated + for std in candidates: + if std in self.choices: + return std + # Fallback to a deprecated std if any + for std in candidates: + newstd = self.deprecated_stds.get(std) + if newstd is not None: + mlog.deprecation( + f'None of the values {candidates} are supported by the {self.lang} compiler.\n' + + f'However, the deprecated {std} std currently falls back to {newstd}.\n' + + 'This will be an error in the future.\n' + + 'If the project supports both GNU and MSVC compilers, a value such as\n' + + '"c_std=gnu11,c11" specifies that GNU is prefered but it can safely fallback to plain c11.') + return newstd + raise MesonException(f'None of values {candidates} are supported by the {self.lang.upper()} compiler. ' + + f'Possible values are {self.choices}') class DependencyCacheType(enum.Enum): diff --git a/mesonbuild/mcompile.py b/mesonbuild/mcompile.py index c36c65b..19875c2 100644 --- a/mesonbuild/mcompile.py +++ b/mesonbuild/mcompile.py @@ -35,7 +35,7 @@ if T.TYPE_CHECKING: import argparse def array_arg(value: str) -> T.List[str]: - return UserArrayOption(None, value, allow_dups=True, user_input=True).value + return UserArrayOption.listify_value(value) def validate_builddir(builddir: Path) -> None: if not (builddir / 'meson-private' / 'coredata.dat').is_file(): diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py index 8377614..8ad84aa 100644 --- a/mesonbuild/optinterpreter.py +++ b/mesonbuild/optinterpreter.py @@ -20,7 +20,7 @@ from . import coredata from . import mesonlib from . import mparser from . import mlog -from .interpreterbase import FeatureNew, typed_pos_args, typed_kwargs, ContainerTypeInfo, KwargInfo +from .interpreterbase import FeatureNew, FeatureDeprecated, typed_pos_args, typed_kwargs, ContainerTypeInfo, KwargInfo from .interpreter.type_checking import NoneType, in_set_validator if T.TYPE_CHECKING: @@ -266,6 +266,11 @@ class OptionInterpreter: def string_array_parser(self, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: StringArrayArgs) -> coredata.UserOption: choices = kwargs['choices'] value = kwargs['value'] if kwargs['value'] is not None else choices + if isinstance(value, str): + if value.startswith('['): + FeatureDeprecated('String value for array option', '1.3.0').use(self.subproject) + else: + raise mesonlib.MesonException('Value does not define an array: ' + value) return coredata.UserArrayOption(description, value, choices=choices, yielding=args[0], |