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 differen 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 differen 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 differen 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(GnuCPPCompiler, ElbrusCompiler): 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 GnuCPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 459 info, exe_wrapper, linker=linker, 460 full_version=full_version, defines=defines) 461 ElbrusCompiler.__init__(self) 462 463 def get_options(self) -> 'KeyedOptionDictType': 464 opts = CPPCompiler.get_options(self) 465 466 cpp_stds = [ 467 'none', 'c++98', 'c++03', 'c++0x', 'c++11', 'c++14', 'c++1y', 468 'gnu++98', 'gnu++03', 'gnu++0x', 'gnu++11', 'gnu++14', 'gnu++1y', 469 ] 470 471 if version_compare(self.version, '>=1.24.00'): 472 cpp_stds += [ 'c++1z', 'c++17', 'gnu++1z', 'gnu++17' ] 473 474 if version_compare(self.version, '>=1.25.00'): 475 cpp_stds += [ 'c++2a', 'gnu++2a' ] 476 477 key = OptionKey('std', machine=self.for_machine, lang=self.language) 478 opts.update({ 479 key.evolve('eh'): coredata.UserComboOption( 480 'C++ exception handling type.', 481 ['none', 'default', 'a', 's', 'sc'], 482 'default', 483 ), 484 key.evolve('debugstl'): coredata.UserBooleanOption( 485 'STL debug mode', 486 False, 487 ), 488 }) 489 opts[key].choices = cpp_stds 490 return opts 491 492 # Elbrus C++ compiler does not have lchmod, but there is only linker warning, not compiler error. 493 # So we should explicitly fail at this case. 494 def has_function(self, funcname: str, prefix: str, env: 'Environment', *, 495 extra_args: T.Optional[T.List[str]] = None, 496 dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]: 497 if funcname == 'lchmod': 498 return False, False 499 else: 500 return super().has_function(funcname, prefix, env, 501 extra_args=extra_args, 502 dependencies=dependencies) 503 504 # Elbrus C++ compiler does not support RTTI, so don't check for it. 505 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 506 args = [] 507 key = OptionKey('std', machine=self.for_machine, lang=self.language) 508 std = options[key] 509 if std.value != 'none': 510 args.append(self._find_best_cpp_std(std.value)) 511 512 non_msvc_eh_options(options[key.evolve('eh')].value, args) 513 514 if options[key.evolve('debugstl')].value: 515 args.append('-D_GLIBCXX_DEBUG=1') 516 return args 517 518 519class IntelCPPCompiler(IntelGnuLikeCompiler, CPPCompiler): 520 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, 521 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, 522 linker: T.Optional['DynamicLinker'] = None, 523 full_version: T.Optional[str] = None): 524 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 525 info, exe_wrapper, linker=linker, full_version=full_version) 526 IntelGnuLikeCompiler.__init__(self) 527 self.lang_header = 'c++-header' 528 default_warn_args = ['-Wall', '-w3', '-diag-disable:remark', 529 '-Wpch-messages', '-Wnon-virtual-dtor'] 530 self.warn_args = {'0': [], 531 '1': default_warn_args, 532 '2': default_warn_args + ['-Wextra'], 533 '3': default_warn_args + ['-Wextra']} 534 535 def get_options(self) -> 'KeyedOptionDictType': 536 opts = CPPCompiler.get_options(self) 537 # Every Unix compiler under the sun seems to accept -std=c++03, 538 # with the exception of ICC. Instead of preventing the user from 539 # globally requesting C++03, we transparently remap it to C++98 540 c_stds = ['c++98', 'c++03'] 541 g_stds = ['gnu++98', 'gnu++03'] 542 if version_compare(self.version, '>=15.0.0'): 543 c_stds += ['c++11', 'c++14'] 544 g_stds += ['gnu++11'] 545 if version_compare(self.version, '>=16.0.0'): 546 c_stds += ['c++17'] 547 if version_compare(self.version, '>=17.0.0'): 548 g_stds += ['gnu++14'] 549 if version_compare(self.version, '>=19.1.0'): 550 c_stds += ['c++2a'] 551 g_stds += ['gnu++2a'] 552 553 554 key = OptionKey('std', machine=self.for_machine, lang=self.language) 555 opts.update({ 556 key.evolve('eh'): coredata.UserComboOption( 557 'C++ exception handling type.', 558 ['none', 'default', 'a', 's', 'sc'], 559 'default', 560 ), 561 key.evolve('rtti'): coredata.UserBooleanOption('Enable RTTI', True), 562 key.evolve('debugstl'): coredata.UserBooleanOption('STL debug mode', False), 563 }) 564 opts[key].choices = ['none'] + c_stds + g_stds 565 return opts 566 567 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 568 args = [] 569 key = OptionKey('std', machine=self.for_machine, lang=self.language) 570 std = options[key] 571 if std.value != 'none': 572 remap_cpp03 = { 573 'c++03': 'c++98', 574 'gnu++03': 'gnu++98' 575 } 576 args.append('-std=' + remap_cpp03.get(std.value, std.value)) 577 if options[key.evolve('eh')].value == 'none': 578 args.append('-fno-exceptions') 579 if not options[key.evolve('rtti')].value: 580 args.append('-fno-rtti') 581 if options[key.evolve('debugstl')].value: 582 args.append('-D_GLIBCXX_DEBUG=1') 583 return args 584 585 def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 586 return [] 587 588 589class VisualStudioLikeCPPCompilerMixin(CompilerMixinBase): 590 591 """Mixin for C++ specific method overrides in MSVC-like compilers.""" 592 593 VC_VERSION_MAP = { 594 'none': (True, None), 595 'vc++11': (True, 11), 596 'vc++14': (True, 14), 597 'vc++17': (True, 17), 598 'vc++latest': (True, "latest"), 599 'c++11': (False, 11), 600 'c++14': (False, 14), 601 'c++17': (False, 17), 602 'c++latest': (False, "latest"), 603 } 604 605 def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 606 # need a typeddict for this 607 key = OptionKey('winlibs', machine=self.for_machine, lang=self.language) 608 return T.cast(T.List[str], options[key].value[:]) 609 610 def _get_options_impl(self, opts: 'KeyedOptionDictType', cpp_stds: T.List[str]) -> 'KeyedOptionDictType': 611 key = OptionKey('std', machine=self.for_machine, lang=self.language) 612 opts.update({ 613 key.evolve('eh'): coredata.UserComboOption( 614 'C++ exception handling type.', 615 ['none', 'default', 'a', 's', 'sc'], 616 'default', 617 ), 618 key.evolve('rtti'): coredata.UserBooleanOption('Enable RTTI', True), 619 key.evolve('winlibs'): coredata.UserArrayOption( 620 'Windows libs to link against.', 621 msvc_winlibs, 622 ), 623 }) 624 opts[key.evolve('std')].choices = cpp_stds 625 return opts 626 627 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 628 args = [] 629 key = OptionKey('std', machine=self.for_machine, lang=self.language) 630 631 eh = options[key.evolve('eh')] 632 if eh.value == 'default': 633 args.append('/EHsc') 634 elif eh.value == 'none': 635 args.append('/EHs-c-') 636 else: 637 args.append('/EH' + eh.value) 638 639 if not options[key.evolve('rtti')].value: 640 args.append('/GR-') 641 642 permissive, ver = self.VC_VERSION_MAP[options[key].value] 643 644 if ver is not None: 645 args.append(f'/std:c++{ver}') 646 647 if not permissive: 648 args.append('/permissive-') 649 650 return args 651 652 def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: 653 # XXX: this is a hack because so much GnuLike stuff is in the base CPPCompiler class. 654 return Compiler.get_compiler_check_args(self, mode) 655 656 657class CPP11AsCPP14Mixin(CompilerMixinBase): 658 659 """Mixin class for VisualStudio and ClangCl to replace C++11 std with C++14. 660 661 This is a limitation of Clang and MSVC that ICL doesn't share. 662 """ 663 664 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 665 # Note: there is no explicit flag for supporting C++11; we attempt to do the best we can 666 # which means setting the C++ standard version to C++14, in compilers that support it 667 # (i.e., after VS2015U3) 668 # if one is using anything before that point, one cannot set the standard. 669 key = OptionKey('std', machine=self.for_machine, lang=self.language) 670 if options[key].value in {'vc++11', 'c++11'}: 671 mlog.warning(self.id, 'does not support C++11;', 672 'attempting best effort; setting the standard to C++14', once=True) 673 # Don't mutate anything we're going to change, we need to use 674 # deepcopy since we're messing with members, and we can't simply 675 # copy the members because the option proxy doesn't support it. 676 options = copy.deepcopy(options) 677 if options[key].value == 'vc++11': 678 options[key].value = 'vc++14' 679 else: 680 options[key].value = 'c++14' 681 return super().get_option_compile_args(options) 682 683 684class VisualStudioCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixin, MSVCCompiler, CPPCompiler): 685 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, 686 is_cross: bool, info: 'MachineInfo', target: str, 687 exe_wrapper: T.Optional['ExternalProgram'] = None, 688 linker: T.Optional['DynamicLinker'] = None, 689 full_version: T.Optional[str] = None): 690 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 691 info, exe_wrapper, linker=linker, full_version=full_version) 692 MSVCCompiler.__init__(self, target) 693 self.id = 'msvc' 694 695 def get_options(self) -> 'KeyedOptionDictType': 696 cpp_stds = ['none', 'c++11', 'vc++11'] 697 # Visual Studio 2015 and later 698 if version_compare(self.version, '>=19'): 699 cpp_stds.extend(['c++14', 'c++latest', 'vc++latest']) 700 # Visual Studio 2017 and later 701 if version_compare(self.version, '>=19.11'): 702 cpp_stds.extend(['vc++14', 'c++17', 'vc++17']) 703 return self._get_options_impl(super().get_options(), cpp_stds) 704 705 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 706 key = OptionKey('std', machine=self.for_machine, lang=self.language) 707 if options[key].value != 'none' and version_compare(self.version, '<19.00.24210'): 708 mlog.warning('This version of MSVC does not support cpp_std arguments') 709 options = copy.copy(options) 710 options[key].value = 'none' 711 712 args = super().get_option_compile_args(options) 713 714 if version_compare(self.version, '<19.11'): 715 try: 716 i = args.index('/permissive-') 717 except ValueError: 718 return args 719 del args[i] 720 return args 721 722class ClangClCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixin, ClangClCompiler, CPPCompiler): 723 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, 724 is_cross: bool, info: 'MachineInfo', target: str, 725 exe_wrapper: T.Optional['ExternalProgram'] = None, 726 linker: T.Optional['DynamicLinker'] = None, 727 full_version: T.Optional[str] = None): 728 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 729 info, exe_wrapper, linker=linker, full_version=full_version) 730 ClangClCompiler.__init__(self, target) 731 self.id = 'clang-cl' 732 733 def get_options(self) -> 'KeyedOptionDictType': 734 cpp_stds = ['none', 'c++11', 'vc++11', 'c++14', 'vc++14', 'c++17', 'vc++17', 'c++latest'] 735 return self._get_options_impl(super().get_options(), cpp_stds) 736 737 738class IntelClCPPCompiler(VisualStudioLikeCPPCompilerMixin, IntelVisualStudioLikeCompiler, CPPCompiler): 739 740 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, 741 is_cross: bool, info: 'MachineInfo', target: str, 742 exe_wrapper: T.Optional['ExternalProgram'] = None, 743 linker: T.Optional['DynamicLinker'] = None, 744 full_version: T.Optional[str] = None): 745 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 746 info, exe_wrapper, linker=linker, full_version=full_version) 747 IntelVisualStudioLikeCompiler.__init__(self, target) 748 749 def get_options(self) -> 'KeyedOptionDictType': 750 # This has only been tested with version 19.0, 751 cpp_stds = ['none', 'c++11', 'vc++11', 'c++14', 'vc++14', 'c++17', 'vc++17', 'c++latest'] 752 return self._get_options_impl(super().get_options(), cpp_stds) 753 754 def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: 755 # XXX: this is a hack because so much GnuLike stuff is in the base CPPCompiler class. 756 return IntelVisualStudioLikeCompiler.get_compiler_check_args(self, mode) 757 758 759class ArmCPPCompiler(ArmCompiler, CPPCompiler): 760 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, 761 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, 762 linker: T.Optional['DynamicLinker'] = None, 763 full_version: T.Optional[str] = None): 764 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 765 info, exe_wrapper, linker=linker, full_version=full_version) 766 ArmCompiler.__init__(self) 767 768 def get_options(self) -> 'KeyedOptionDictType': 769 opts = CPPCompiler.get_options(self) 770 key = OptionKey('std', machine=self.for_machine, lang=self.language) 771 opts[key].choices = ['none', 'c++03', 'c++11'] 772 return opts 773 774 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 775 args = [] 776 key = OptionKey('std', machine=self.for_machine, lang=self.language) 777 std = options[key] 778 if std.value == 'c++11': 779 args.append('--cpp11') 780 elif std.value == 'c++03': 781 args.append('--cpp') 782 return args 783 784 def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 785 return [] 786 787 def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: 788 return [] 789 790 791class CcrxCPPCompiler(CcrxCompiler, CPPCompiler): 792 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, 793 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, 794 linker: T.Optional['DynamicLinker'] = None, 795 full_version: T.Optional[str] = None): 796 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 797 info, exe_wrapper, linker=linker, full_version=full_version) 798 CcrxCompiler.__init__(self) 799 800 # Override CCompiler.get_always_args 801 def get_always_args(self) -> T.List[str]: 802 return ['-nologo', '-lang=cpp'] 803 804 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 805 return [] 806 807 def get_compile_only_args(self) -> T.List[str]: 808 return [] 809 810 def get_output_args(self, target: str) -> T.List[str]: 811 return ['-output=obj=%s' % target] 812 813 def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 814 return [] 815 816 def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: 817 return [] 818 819class C2000CPPCompiler(C2000Compiler, CPPCompiler): 820 def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, 821 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, 822 linker: T.Optional['DynamicLinker'] = None, 823 full_version: T.Optional[str] = None): 824 CPPCompiler.__init__(self, exelist, version, for_machine, is_cross, 825 info, exe_wrapper, linker=linker, full_version=full_version) 826 C2000Compiler.__init__(self) 827 828 def get_options(self) -> 'KeyedOptionDictType': 829 opts = CPPCompiler.get_options(self) 830 key = OptionKey('std', machine=self.for_machine, lang=self.language) 831 opts[key].choices = ['none', 'c++03'] 832 return opts 833 834 def get_always_args(self) -> T.List[str]: 835 return ['-nologo', '-lang=cpp'] 836 837 def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 838 return [] 839 840 def get_compile_only_args(self) -> T.List[str]: 841 return [] 842 843 def get_output_args(self, target: str) -> T.List[str]: 844 return ['-output=obj=%s' % target] 845 846 def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: 847 return [] 848 849 def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: 850 return [] 851