1# Copyright 2012-2017 The Meson development team 2 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6 7# http://www.apache.org/licenses/LICENSE-2.0 8 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import copy 16import functools 17import os.path 18import typing as T 19 20from .. import coredata 21from .. import mlog 22from ..mesonlib import MesonException, MachineChoice, version_compare, OptionKey 23 24from .compilers import ( 25 gnu_winlibs, 26 msvc_winlibs, 27 Compiler, 28 CompileCheckMode, 29) 30from .c_function_attributes import CXX_FUNC_ATTRIBUTES, C_FUNC_ATTRIBUTES 31from .mixins.clike import CLikeCompiler 32from .mixins.ccrx import CcrxCompiler 33from .mixins.c2000 import C2000Compiler 34from .mixins.arm import ArmCompiler, ArmclangCompiler 35from .mixins.visualstudio import MSVCCompiler, ClangClCompiler 36from .mixins.gnu import GnuCompiler 37from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler 38from .mixins.clang import ClangCompiler 39from .mixins.elbrus import ElbrusCompiler 40from .mixins.pgi import PGICompiler 41from .mixins.emscripten import EmscriptenMixin 42 43if T.TYPE_CHECKING: 44 from ..coredata import KeyedOptionDictType 45 from ..dependencies import Dependency 46 from ..envconfig import MachineInfo 47 from ..environment import Environment 48 from ..linkers import DynamicLinker 49 from ..programs import ExternalProgram 50 CompilerMixinBase = CLikeCompiler 51else: 52 CompilerMixinBase = object 53 54 55def non_msvc_eh_options(eh: str, args: T.List[str]) -> None: 56 if eh == 'none': 57 args.append('-fno-exceptions') 58 elif eh == 's' or eh == 'c': 59 mlog.warning('non-MSVC compilers do not support ' + eh + ' exception handling.' + 60 'You may want to set eh to \'default\'.') 61 62class CPPCompiler(CLikeCompiler, Compiler): 63 64 @classmethod 65 def attribute_check_func(cls, name: str) -> str: 66 try: 67 return CXX_FUNC_ATTRIBUTES.get(name, C_FUNC_ATTRIBUTES[name]) 68 except KeyError: 69 raise MesonException(f'Unknown function attribute "{name}"') 70 71 language = 'cpp' 72 73 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, 74 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, 75 linker: T.Optional['DynamicLinker'] = None, 76 full_version: T.Optional[str] = None): 77 # If a child ObjCPP class has already set it, don't set it ourselves 78 Compiler.__init__(self, exelist, version, for_machine, info, 79 is_cross=is_cross, linker=linker, 80 full_version=full_version) 81 CLikeCompiler.__init__(self, exe_wrapper) 82 83 @staticmethod 84 def get_display_language() -> str: 85 return 'C++' 86 87 def get_no_stdinc_args(self) -> T.List[str]: 88 return ['-nostdinc++'] 89 90 def sanity_check(self, work_dir: str, environment: 'Environment') -> None: 91 code = 'class breakCCompiler;int main(void) { return 0; }\n' 92 return self._sanity_check_impl(work_dir, environment, 'sanitycheckcpp.cc', code) 93 94 def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: 95 # -fpermissive allows non-conforming code to compile which is necessary 96 # for many C++ checks. Particularly, the has_header_symbol check is 97 # too strict without this and always fails. 98 return super().get_compiler_check_args(mode) + ['-fpermissive'] 99 100 def has_header_symbol(self, hname: str, symbol: str, prefix: str, 101 env: 'Environment', *, 102 extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None, 103 dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]: 104 # Check if it's a C-like symbol 105 found, cached = super().has_header_symbol(hname, symbol, prefix, env, 106 extra_args=extra_args, 107 dependencies=dependencies) 108 if found: 109 return True, cached 110 # Check if it's a class or a template 111 if extra_args is None: 112 extra_args = [] 113 t = f'''{prefix} 114 #include <{hname}> 115 using {symbol}; 116 int main(void) {{ return 0; }}''' 117 return self.compiles(t, env, extra_args=extra_args, 118 dependencies=dependencies) 119 120 def _test_cpp_std_arg(self, cpp_std_value: str) -> bool: 121 # Test whether the compiler understands a -std=XY argument 122 assert cpp_std_value.startswith('-std=') 123 124 # This test does not use has_multi_arguments() for two reasons: 125 # 1. has_multi_arguments() requires an env argument, which the compiler 126 # object does not have at this point. 127 # 2. even if it did have an env object, that might contain another more 128 # recent -std= argument, which might lead to a cascaded failure. 129 CPP_TEST = 'int i = static_cast<int>(0);' 130 with self.compile(CPP_TEST, extra_args=[cpp_std_value], mode='compile') as p: 131 if p.returncode == 0: 132 mlog.debug(f'Compiler accepts {cpp_std_value}:', 'YES') 133 return True 134 else: 135 mlog.debug(f'Compiler accepts {cpp_std_value}:', 'NO') 136 return False 137 138 @functools.lru_cache() 139 def _find_best_cpp_std(self, cpp_std: str) -> str: 140 # The initial version mapping approach to make falling back 141 # from '-std=c++14' to '-std=c++1y' was too brittle. For instance, 142 # Apple's Clang uses a different versioning scheme to upstream LLVM, 143 # making the whole detection logic awfully brittle. Instead, let's 144 # just see if feeding GCC or Clang our '-std=' setting works, and 145 # if not, try the fallback argument. 146 CPP_FALLBACKS = { 147 'c++11': 'c++0x', 148 'gnu++11': 'gnu++0x', 149 'c++14': 'c++1y', 150 'gnu++14': 'gnu++1y', 151 'c++17': 'c++1z', 152 'gnu++17': 'gnu++1z', 153 'c++20': 'c++2a', 154 'gnu++20': 'gnu++2a', 155 } 156 157 # Currently, remapping is only supported for Clang, Elbrus and GCC 158 assert self.id in frozenset(['clang', 'lcc', 'gcc', 'emscripten']) 159 160 if cpp_std not in CPP_FALLBACKS: 161 # 'c++03' and 'c++98' don't have fallback types 162 return '-std=' + cpp_std 163 164 for i in (cpp_std, CPP_FALLBACKS[cpp_std]): 165 cpp_std_value = '-std=' + i 166 if self._test_cpp_std_arg(cpp_std_value): 167 return cpp_std_value 168 169 raise MesonException(f'C++ Compiler does not support -std={cpp_std}') 170 171 def get_options(self) -> 'KeyedOptionDictType': 172 opts = super().get_options() 173 key = OptionKey('std', machine=self.for_machine, lang=self.language) 174 opts.update({ 175 key: coredata.UserComboOption( 176 'C++ language standard to use', 177 ['none'], 178 'none', 179 ), 180 }) 181 return opts 182 183 184class ClangCPPCompiler(ClangCompiler, CPPCompiler): 185 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, 186 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, 187 linker: T.Optional['DynamicLinker'] = None, 188 defines: T.Optional[T.Dict[str, str]] = None, 189 full_version: T.Optional[str] = None): 190 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 191 info, exe_wrapper, linker=linker, full_version=full_version) 192 ClangCompiler.__init__(self, defines) 193 default_warn_args = ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'] 194 self.warn_args = {'0': [], 195 '1': default_warn_args, 196 '2': default_warn_args + ['-Wextra'], 197 '3': default_warn_args + ['-Wextra', '-Wpedantic']} 198 199 def get_options(self) -> 'KeyedOptionDictType': 200 opts = CPPCompiler.get_options(self) 201 key = OptionKey('key', machine=self.for_machine, lang=self.language) 202 opts.update({ 203 key.evolve('eh'): coredata.UserComboOption( 204 'C++ exception handling type.', 205 ['none', 'default', 'a', 's', 'sc'], 206 'default', 207 ), 208 key.evolve('rtti'): coredata.UserBooleanOption('Enable RTTI', True), 209 }) 210 opts[key.evolve('std')].choices = [ 211 'none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z', 212 'c++2a', 'c++20', 'gnu++11', 'gnu++14', 'gnu++17', 'gnu++1z', 213 'gnu++2a', 'gnu++20', 214 ] 215 if self.info.is_windows() or self.info.is_cygwin(): 216 opts.update({ 217 key.evolve('winlibs'): coredata.UserArrayOption( 218 'Standard Win libraries to link against', 219 gnu_winlibs, 220 ), 221 }) 222 return opts 223 224 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 225 args = [] 226 key = OptionKey('std', machine=self.for_machine, lang=self.language) 227 std = options[key] 228 if std.value != 'none': 229 args.append(self._find_best_cpp_std(std.value)) 230 231 non_msvc_eh_options(options[key.evolve('eh')].value, args) 232 233 if not options[key.evolve('rtti')].value: 234 args.append('-fno-rtti') 235 236 return args 237 238 def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 239 if self.info.is_windows() or self.info.is_cygwin(): 240 # without a typedict mypy can't understand this. 241 key = OptionKey('winlibs', machine=self.for_machine, lang=self.language) 242 libs = options[key].value.copy() 243 assert isinstance(libs, list) 244 for l in libs: 245 assert isinstance(l, str) 246 return libs 247 return [] 248 249 def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]: 250 # We need to apply the search prefix here, as these link arguments may 251 # be passed to a different compiler with a different set of default 252 # search paths, such as when using Clang for C/C++ and gfortran for 253 # fortran, 254 search_dir = self._get_search_dirs(env) 255 search_dirs: T.List[str] = [] 256 if search_dir is not None: 257 for d in search_dir.split()[-1][len('libraries: ='):].split(':'): 258 search_dirs.append(f'-L{d}') 259 return search_dirs + ['-lstdc++'] 260 261 262class AppleClangCPPCompiler(ClangCPPCompiler): 263 def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]: 264 # We need to apply the search prefix here, as these link arguments may 265 # be passed to a different compiler with a different set of default 266 # search paths, such as when using Clang for C/C++ and gfortran for 267 # fortran, 268 search_dir = self._get_search_dirs(env) 269 search_dirs: T.List[str] = [] 270 if search_dir is not None: 271 for d in search_dir.split()[-1][len('libraries: ='):].split(':'): 272 search_dirs.append(f'-L{d}') 273 return search_dirs + ['-lc++'] 274 275 276class EmscriptenCPPCompiler(EmscriptenMixin, ClangCPPCompiler): 277 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, 278 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, 279 linker: T.Optional['DynamicLinker'] = None, 280 defines: T.Optional[T.Dict[str, str]] = None, 281 full_version: T.Optional[str] = None): 282 if not is_cross: 283 raise MesonException('Emscripten compiler can only be used for cross compilation.') 284 ClangCPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 285 info, exe_wrapper=exe_wrapper, linker=linker, 286 defines=defines, full_version=full_version) 287 self.id = 'emscripten' 288 289 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 290 args = [] 291 key = OptionKey('std', machine=self.for_machine, lang=self.language) 292 std = options[key] 293 if std.value != 'none': 294 args.append(self._find_best_cpp_std(std.value)) 295 return args 296 297 298class ArmclangCPPCompiler(ArmclangCompiler, CPPCompiler): 299 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, 300 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, 301 linker: T.Optional['DynamicLinker'] = None, 302 full_version: T.Optional[str] = None): 303 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 304 info, exe_wrapper, linker=linker, full_version=full_version) 305 ArmclangCompiler.__init__(self) 306 default_warn_args = ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'] 307 self.warn_args = {'0': [], 308 '1': default_warn_args, 309 '2': default_warn_args + ['-Wextra'], 310 '3': default_warn_args + ['-Wextra', '-Wpedantic']} 311 312 def get_options(self) -> 'KeyedOptionDictType': 313 opts = CPPCompiler.get_options(self) 314 key = OptionKey('std', machine=self.for_machine, lang=self.language) 315 opts.update({ 316 key.evolve('eh'): coredata.UserComboOption( 317 'C++ exception handling type.', 318 ['none', 'default', 'a', 's', 'sc'], 319 'default', 320 ), 321 }) 322 opts[key].choices = [ 323 'none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'gnu++98', 324 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++17', 325 ] 326 return opts 327 328 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 329 args = [] 330 key = OptionKey('std', machine=self.for_machine, lang=self.language) 331 std = options[key] 332 if std.value != 'none': 333 args.append('-std=' + std.value) 334 335 non_msvc_eh_options(options[key.evolve('eh')].value, args) 336 337 return args 338 339 def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 340 return [] 341 342 343class GnuCPPCompiler(GnuCompiler, CPPCompiler): 344 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, 345 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, 346 linker: T.Optional['DynamicLinker'] = None, 347 defines: T.Optional[T.Dict[str, str]] = None, 348 full_version: T.Optional[str] = None): 349 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 350 info, exe_wrapper, linker=linker, full_version=full_version) 351 GnuCompiler.__init__(self, defines) 352 default_warn_args = ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'] 353 self.warn_args = {'0': [], 354 '1': default_warn_args, 355 '2': default_warn_args + ['-Wextra'], 356 '3': default_warn_args + ['-Wextra', '-Wpedantic']} 357 358 def get_options(self) -> 'KeyedOptionDictType': 359 key = OptionKey('std', machine=self.for_machine, lang=self.language) 360 opts = CPPCompiler.get_options(self) 361 opts.update({ 362 key.evolve('eh'): coredata.UserComboOption( 363 'C++ exception handling type.', 364 ['none', 'default', 'a', 's', 'sc'], 365 'default', 366 ), 367 key.evolve('rtti'): coredata.UserBooleanOption('Enable RTTI', True), 368 key.evolve('debugstl'): coredata.UserBooleanOption( 369 'STL debug mode', 370 False, 371 ) 372 }) 373 opts[key].choices = [ 374 'none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z', 375 'c++2a', 'c++20', 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++17', 376 'gnu++1z', 'gnu++2a', 'gnu++20', 377 ] 378 if self.info.is_windows() or self.info.is_cygwin(): 379 opts.update({ 380 key.evolve('winlibs'): coredata.UserArrayOption( 381 'Standard Win libraries to link against', 382 gnu_winlibs, 383 ), 384 }) 385 return opts 386 387 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 388 args = [] 389 key = OptionKey('std', machine=self.for_machine, lang=self.language) 390 std = options[key] 391 if std.value != 'none': 392 args.append(self._find_best_cpp_std(std.value)) 393 394 non_msvc_eh_options(options[key.evolve('eh')].value, args) 395 396 if not options[key.evolve('rtti')].value: 397 args.append('-fno-rtti') 398 399 if options[key.evolve('debugstl')].value: 400 args.append('-D_GLIBCXX_DEBUG=1') 401 return args 402 403 def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 404 if self.info.is_windows() or self.info.is_cygwin(): 405 # without a typedict mypy can't understand this. 406 key = OptionKey('winlibs', machine=self.for_machine, lang=self.language) 407 libs = options[key].value.copy() 408 assert isinstance(libs, list) 409 for l in libs: 410 assert isinstance(l, str) 411 return libs 412 return [] 413 414 def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]: 415 return ['-fpch-preprocess', '-include', os.path.basename(header)] 416 417 def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]: 418 # We need to apply the search prefix here, as these link arguments may 419 # be passed to a different compiler with a different set of default 420 # search paths, such as when using Clang for C/C++ and gfortran for 421 # fortran, 422 search_dir = self._get_search_dirs(env) 423 search_dirs: T.List[str] = [] 424 if search_dir is not None: 425 for d in search_dir.split()[-1][len('libraries: ='):].split(':'): 426 search_dirs.append(f'-L{d}') 427 return ['-lstdc++'] 428 429 430class PGICPPCompiler(PGICompiler, CPPCompiler): 431 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, 432 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, 433 linker: T.Optional['DynamicLinker'] = None, 434 full_version: T.Optional[str] = None): 435 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 436 info, exe_wrapper, linker=linker, full_version=full_version) 437 PGICompiler.__init__(self) 438 439 440class NvidiaHPC_CPPCompiler(PGICompiler, CPPCompiler): 441 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, 442 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, 443 linker: T.Optional['DynamicLinker'] = None, 444 full_version: T.Optional[str] = None): 445 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 446 info, exe_wrapper, linker=linker, full_version=full_version) 447 PGICompiler.__init__(self) 448 449 self.id = 'nvidia_hpc' 450 451 452class ElbrusCPPCompiler(ElbrusCompiler, CPPCompiler): 453 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, 454 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, 455 linker: T.Optional['DynamicLinker'] = None, 456 defines: T.Optional[T.Dict[str, str]] = None, 457 full_version: T.Optional[str] = None): 458 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 459 info, exe_wrapper, linker=linker, full_version=full_version) 460 ElbrusCompiler.__init__(self) 461 462 def get_options(self) -> 'KeyedOptionDictType': 463 opts = CPPCompiler.get_options(self) 464 465 cpp_stds = ['none', 'c++98', 'gnu++98'] 466 if version_compare(self.version, '>=1.20.00'): 467 cpp_stds += ['c++03', 'c++0x', 'c++11', 'gnu++03', 'gnu++0x', 'gnu++11'] 468 if version_compare(self.version, '>=1.21.00') and version_compare(self.version, '<1.22.00'): 469 cpp_stds += ['c++14', 'gnu++14', 'c++1y', 'gnu++1y'] 470 if version_compare(self.version, '>=1.22.00'): 471 cpp_stds += ['c++14', 'gnu++14'] 472 if version_compare(self.version, '>=1.23.00'): 473 cpp_stds += ['c++1y', 'gnu++1y'] 474 if version_compare(self.version, '>=1.24.00'): 475 cpp_stds += ['c++1z', 'c++17', 'gnu++1z', 'gnu++17'] 476 if version_compare(self.version, '>=1.25.00'): 477 cpp_stds += ['c++2a', 'gnu++2a'] 478 if version_compare(self.version, '>=1.26.00'): 479 cpp_stds += ['c++20', 'gnu++20'] 480 481 key = OptionKey('std', machine=self.for_machine, lang=self.language) 482 opts.update({ 483 key.evolve('eh'): coredata.UserComboOption( 484 'C++ exception handling type.', 485 ['none', 'default', 'a', 's', 'sc'], 486 'default', 487 ), 488 key.evolve('debugstl'): coredata.UserBooleanOption( 489 'STL debug mode', 490 False, 491 ), 492 }) 493 opts[key].choices = cpp_stds 494 return opts 495 496 # Elbrus C++ compiler does not have lchmod, but there is only linker warning, not compiler error. 497 # So we should explicitly fail at this case. 498 def has_function(self, funcname: str, prefix: str, env: 'Environment', *, 499 extra_args: T.Optional[T.List[str]] = None, 500 dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]: 501 if funcname == 'lchmod': 502 return False, False 503 else: 504 return super().has_function(funcname, prefix, env, 505 extra_args=extra_args, 506 dependencies=dependencies) 507 508 # Elbrus C++ compiler does not support RTTI, so don't check for it. 509 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 510 args = [] 511 key = OptionKey('std', machine=self.for_machine, lang=self.language) 512 std = options[key] 513 if std.value != 'none': 514 args.append(self._find_best_cpp_std(std.value)) 515 516 non_msvc_eh_options(options[key.evolve('eh')].value, args) 517 518 if options[key.evolve('debugstl')].value: 519 args.append('-D_GLIBCXX_DEBUG=1') 520 return args 521 522 523class IntelCPPCompiler(IntelGnuLikeCompiler, CPPCompiler): 524 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, 525 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, 526 linker: T.Optional['DynamicLinker'] = None, 527 full_version: T.Optional[str] = None): 528 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 529 info, exe_wrapper, linker=linker, full_version=full_version) 530 IntelGnuLikeCompiler.__init__(self) 531 self.lang_header = 'c++-header' 532 default_warn_args = ['-Wall', '-w3', '-diag-disable:remark', 533 '-Wpch-messages', '-Wnon-virtual-dtor'] 534 self.warn_args = {'0': [], 535 '1': default_warn_args, 536 '2': default_warn_args + ['-Wextra'], 537 '3': default_warn_args + ['-Wextra']} 538 539 def get_options(self) -> 'KeyedOptionDictType': 540 opts = CPPCompiler.get_options(self) 541 # Every Unix compiler under the sun seems to accept -std=c++03, 542 # with the exception of ICC. Instead of preventing the user from 543 # globally requesting C++03, we transparently remap it to C++98 544 c_stds = ['c++98', 'c++03'] 545 g_stds = ['gnu++98', 'gnu++03'] 546 if version_compare(self.version, '>=15.0.0'): 547 c_stds += ['c++11', 'c++14'] 548 g_stds += ['gnu++11'] 549 if version_compare(self.version, '>=16.0.0'): 550 c_stds += ['c++17'] 551 if version_compare(self.version, '>=17.0.0'): 552 g_stds += ['gnu++14'] 553 if version_compare(self.version, '>=19.1.0'): 554 c_stds += ['c++2a'] 555 g_stds += ['gnu++2a'] 556 557 key = OptionKey('std', machine=self.for_machine, lang=self.language) 558 opts.update({ 559 key.evolve('eh'): coredata.UserComboOption( 560 'C++ exception handling type.', 561 ['none', 'default', 'a', 's', 'sc'], 562 'default', 563 ), 564 key.evolve('rtti'): coredata.UserBooleanOption('Enable RTTI', True), 565 key.evolve('debugstl'): coredata.UserBooleanOption('STL debug mode', False), 566 }) 567 opts[key].choices = ['none'] + c_stds + g_stds 568 return opts 569 570 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 571 args = [] 572 key = OptionKey('std', machine=self.for_machine, lang=self.language) 573 std = options[key] 574 if std.value != 'none': 575 remap_cpp03 = { 576 'c++03': 'c++98', 577 'gnu++03': 'gnu++98' 578 } 579 args.append('-std=' + remap_cpp03.get(std.value, std.value)) 580 if options[key.evolve('eh')].value == 'none': 581 args.append('-fno-exceptions') 582 if not options[key.evolve('rtti')].value: 583 args.append('-fno-rtti') 584 if options[key.evolve('debugstl')].value: 585 args.append('-D_GLIBCXX_DEBUG=1') 586 return args 587 588 def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 589 return [] 590 591 592class VisualStudioLikeCPPCompilerMixin(CompilerMixinBase): 593 594 """Mixin for C++ specific method overrides in MSVC-like compilers.""" 595 596 VC_VERSION_MAP = { 597 'none': (True, None), 598 'vc++11': (True, 11), 599 'vc++14': (True, 14), 600 'vc++17': (True, 17), 601 'vc++20': (True, 20), 602 'vc++latest': (True, "latest"), 603 'c++11': (False, 11), 604 'c++14': (False, 14), 605 'c++17': (False, 17), 606 'c++20': (False, 20), 607 'c++latest': (False, "latest"), 608 } 609 610 def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 611 # need a typeddict for this 612 key = OptionKey('winlibs', machine=self.for_machine, lang=self.language) 613 return T.cast(T.List[str], options[key].value[:]) 614 615 def _get_options_impl(self, opts: 'KeyedOptionDictType', cpp_stds: T.List[str]) -> 'KeyedOptionDictType': 616 key = OptionKey('std', machine=self.for_machine, lang=self.language) 617 opts.update({ 618 key.evolve('eh'): coredata.UserComboOption( 619 'C++ exception handling type.', 620 ['none', 'default', 'a', 's', 'sc'], 621 'default', 622 ), 623 key.evolve('rtti'): coredata.UserBooleanOption('Enable RTTI', True), 624 key.evolve('winlibs'): coredata.UserArrayOption( 625 'Windows libs to link against.', 626 msvc_winlibs, 627 ), 628 }) 629 opts[key.evolve('std')].choices = cpp_stds 630 return opts 631 632 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 633 args = [] 634 key = OptionKey('std', machine=self.for_machine, lang=self.language) 635 636 eh = options[key.evolve('eh')] 637 if eh.value == 'default': 638 args.append('/EHsc') 639 elif eh.value == 'none': 640 args.append('/EHs-c-') 641 else: 642 args.append('/EH' + eh.value) 643 644 if not options[key.evolve('rtti')].value: 645 args.append('/GR-') 646 647 permissive, ver = self.VC_VERSION_MAP[options[key].value] 648 649 if ver is not None: 650 args.append(f'/std:c++{ver}') 651 652 if not permissive: 653 args.append('/permissive-') 654 655 return args 656 657 def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: 658 # XXX: this is a hack because so much GnuLike stuff is in the base CPPCompiler class. 659 return Compiler.get_compiler_check_args(self, mode) 660 661 662class CPP11AsCPP14Mixin(CompilerMixinBase): 663 664 """Mixin class for VisualStudio and ClangCl to replace C++11 std with C++14. 665 666 This is a limitation of Clang and MSVC that ICL doesn't share. 667 """ 668 669 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 670 # Note: there is no explicit flag for supporting C++11; we attempt to do the best we can 671 # which means setting the C++ standard version to C++14, in compilers that support it 672 # (i.e., after VS2015U3) 673 # if one is using anything before that point, one cannot set the standard. 674 key = OptionKey('std', machine=self.for_machine, lang=self.language) 675 if options[key].value in {'vc++11', 'c++11'}: 676 mlog.warning(self.id, 'does not support C++11;', 677 'attempting best effort; setting the standard to C++14', once=True) 678 # Don't mutate anything we're going to change, we need to use 679 # deepcopy since we're messing with members, and we can't simply 680 # copy the members because the option proxy doesn't support it. 681 options = copy.deepcopy(options) 682 if options[key].value == 'vc++11': 683 options[key].value = 'vc++14' 684 else: 685 options[key].value = 'c++14' 686 return super().get_option_compile_args(options) 687 688 689class VisualStudioCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixin, MSVCCompiler, CPPCompiler): 690 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, 691 is_cross: bool, info: 'MachineInfo', target: str, 692 exe_wrapper: T.Optional['ExternalProgram'] = None, 693 linker: T.Optional['DynamicLinker'] = None, 694 full_version: T.Optional[str] = None): 695 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 696 info, exe_wrapper, linker=linker, full_version=full_version) 697 MSVCCompiler.__init__(self, target) 698 self.id = 'msvc' 699 700 def get_options(self) -> 'KeyedOptionDictType': 701 cpp_stds = ['none', 'c++11', 'vc++11'] 702 # Visual Studio 2015 and later 703 if version_compare(self.version, '>=19'): 704 cpp_stds.extend(['c++14', 'c++latest', 'vc++latest']) 705 # Visual Studio 2017 and later 706 if version_compare(self.version, '>=19.11'): 707 cpp_stds.extend(['vc++14', 'c++17', 'vc++17']) 708 if version_compare(self.version, '>=19.29'): 709 cpp_stds.extend(['c++20', 'vc++20']) 710 return self._get_options_impl(super().get_options(), cpp_stds) 711 712 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 713 key = OptionKey('std', machine=self.for_machine, lang=self.language) 714 if options[key].value != 'none' and version_compare(self.version, '<19.00.24210'): 715 mlog.warning('This version of MSVC does not support cpp_std arguments') 716 options = copy.copy(options) 717 options[key].value = 'none' 718 719 args = super().get_option_compile_args(options) 720 721 if version_compare(self.version, '<19.11'): 722 try: 723 i = args.index('/permissive-') 724 except ValueError: 725 return args 726 del args[i] 727 return args 728 729class ClangClCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixin, ClangClCompiler, CPPCompiler): 730 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, 731 is_cross: bool, info: 'MachineInfo', target: str, 732 exe_wrapper: T.Optional['ExternalProgram'] = None, 733 linker: T.Optional['DynamicLinker'] = None, 734 full_version: T.Optional[str] = None): 735 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 736 info, exe_wrapper, linker=linker, full_version=full_version) 737 ClangClCompiler.__init__(self, target) 738 self.id = 'clang-cl' 739 740 def get_options(self) -> 'KeyedOptionDictType': 741 cpp_stds = ['none', 'c++11', 'vc++11', 'c++14', 'vc++14', 'c++17', 'vc++17', 'c++latest'] 742 return self._get_options_impl(super().get_options(), cpp_stds) 743 744 745class IntelClCPPCompiler(VisualStudioLikeCPPCompilerMixin, IntelVisualStudioLikeCompiler, CPPCompiler): 746 747 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, 748 is_cross: bool, info: 'MachineInfo', target: str, 749 exe_wrapper: T.Optional['ExternalProgram'] = None, 750 linker: T.Optional['DynamicLinker'] = None, 751 full_version: T.Optional[str] = None): 752 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 753 info, exe_wrapper, linker=linker, full_version=full_version) 754 IntelVisualStudioLikeCompiler.__init__(self, target) 755 756 def get_options(self) -> 'KeyedOptionDictType': 757 # This has only been tested with version 19.0, 758 cpp_stds = ['none', 'c++11', 'vc++11', 'c++14', 'vc++14', 'c++17', 'vc++17', 'c++latest'] 759 return self._get_options_impl(super().get_options(), cpp_stds) 760 761 def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: 762 # XXX: this is a hack because so much GnuLike stuff is in the base CPPCompiler class. 763 return IntelVisualStudioLikeCompiler.get_compiler_check_args(self, mode) 764 765 766class ArmCPPCompiler(ArmCompiler, CPPCompiler): 767 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, 768 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, 769 linker: T.Optional['DynamicLinker'] = None, 770 full_version: T.Optional[str] = None): 771 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 772 info, exe_wrapper, linker=linker, full_version=full_version) 773 ArmCompiler.__init__(self) 774 775 def get_options(self) -> 'KeyedOptionDictType': 776 opts = CPPCompiler.get_options(self) 777 key = OptionKey('std', machine=self.for_machine, lang=self.language) 778 opts[key].choices = ['none', 'c++03', 'c++11'] 779 return opts 780 781 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 782 args = [] 783 key = OptionKey('std', machine=self.for_machine, lang=self.language) 784 std = options[key] 785 if std.value == 'c++11': 786 args.append('--cpp11') 787 elif std.value == 'c++03': 788 args.append('--cpp') 789 return args 790 791 def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 792 return [] 793 794 def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: 795 return [] 796 797 798class CcrxCPPCompiler(CcrxCompiler, CPPCompiler): 799 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, 800 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, 801 linker: T.Optional['DynamicLinker'] = None, 802 full_version: T.Optional[str] = None): 803 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 804 info, exe_wrapper, linker=linker, full_version=full_version) 805 CcrxCompiler.__init__(self) 806 807 # Override CCompiler.get_always_args 808 def get_always_args(self) -> T.List[str]: 809 return ['-nologo', '-lang=cpp'] 810 811 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 812 return [] 813 814 def get_compile_only_args(self) -> T.List[str]: 815 return [] 816 817 def get_output_args(self, target: str) -> T.List[str]: 818 return ['-output=obj=%s' % target] 819 820 def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 821 return [] 822 823 def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: 824 return [] 825 826class C2000CPPCompiler(C2000Compiler, CPPCompiler): 827 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, 828 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, 829 linker: T.Optional['DynamicLinker'] = None, 830 full_version: T.Optional[str] = None): 831 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 832 info, exe_wrapper, linker=linker, full_version=full_version) 833 C2000Compiler.__init__(self) 834 835 def get_options(self) -> 'KeyedOptionDictType': 836 opts = CPPCompiler.get_options(self) 837 key = OptionKey('std', machine=self.for_machine, lang=self.language) 838 opts[key].choices = ['none', 'c++03'] 839 return opts 840 841 def get_always_args(self) -> T.List[str]: 842 return ['-nologo', '-lang=cpp'] 843 844 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 845 return [] 846 847 def get_compile_only_args(self) -> T.List[str]: 848 return [] 849 850 def get_output_args(self, target: str) -> T.List[str]: 851 return ['-output=obj=%s' % target] 852 853 def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 854 return [] 855 856 def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: 857 return [] 858