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 os.path 16import typing as T 17 18from .. import coredata 19from ..mesonlib import MachineChoice, MesonException, mlog, version_compare 20from ..linkers import LinkerEnvVarsMixin 21from .c_function_attributes import C_FUNC_ATTRIBUTES 22from .mixins.clike import CLikeCompiler 23from .mixins.ccrx import CcrxCompiler 24from .mixins.xc16 import Xc16Compiler 25from .mixins.c2000 import C2000Compiler 26from .mixins.arm import ArmCompiler, ArmclangCompiler 27from .mixins.visualstudio import MSVCCompiler, ClangClCompiler 28from .mixins.gnu import GnuCompiler 29from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler 30from .mixins.clang import ClangCompiler 31from .mixins.elbrus import ElbrusCompiler 32from .mixins.pgi import PGICompiler 33from .mixins.emscripten import EmscriptenMixin 34from .compilers import ( 35 gnu_winlibs, 36 msvc_winlibs, 37 Compiler, 38) 39 40if T.TYPE_CHECKING: 41 from ..envconfig import MachineInfo 42 43 44class CCompiler(CLikeCompiler, Compiler): 45 46 @staticmethod 47 def attribute_check_func(name): 48 try: 49 return C_FUNC_ATTRIBUTES[name] 50 except KeyError: 51 raise MesonException('Unknown function attribute "{}"'.format(name)) 52 53 language = 'c' 54 55 def __init__(self, exelist, version, for_machine: MachineChoice, is_cross: bool, 56 info: 'MachineInfo', exe_wrapper: T.Optional[str] = None, **kwargs): 57 # If a child ObjC or CPP class has already set it, don't set it ourselves 58 Compiler.__init__(self, exelist, version, for_machine, info, **kwargs) 59 CLikeCompiler.__init__(self, is_cross, exe_wrapper) 60 61 def get_no_stdinc_args(self): 62 return ['-nostdinc'] 63 64 def sanity_check(self, work_dir, environment): 65 code = 'int main(void) { int class=0; return class; }\n' 66 return self.sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code) 67 68 def has_header_symbol(self, hname, symbol, prefix, env, *, extra_args=None, dependencies=None): 69 fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol} 70 t = '''{prefix} 71 #include <{header}> 72 int main(void) {{ 73 /* If it's not defined as a macro, try to use as a symbol */ 74 #ifndef {symbol} 75 {symbol}; 76 #endif 77 return 0; 78 }}''' 79 return self.compiles(t.format(**fargs), env, extra_args=extra_args, 80 dependencies=dependencies) 81 82 83class ClangCCompiler(ClangCompiler, CCompiler): 84 85 _C17_VERSION = '>=6.0.0' 86 _C18_VERSION = '>=8.0.0' 87 88 def __init__(self, exelist, version, for_machine: MachineChoice, 89 is_cross, info: 'MachineInfo', exe_wrapper=None, 90 defines: T.Optional[T.List[str]] = None, **kwargs): 91 CCompiler.__init__(self, exelist, version, for_machine, is_cross, info, exe_wrapper, **kwargs) 92 ClangCompiler.__init__(self, defines) 93 default_warn_args = ['-Wall', '-Winvalid-pch'] 94 self.warn_args = {'0': [], 95 '1': default_warn_args, 96 '2': default_warn_args + ['-Wextra'], 97 '3': default_warn_args + ['-Wextra', '-Wpedantic']} 98 99 def get_options(self): 100 opts = CCompiler.get_options(self) 101 c_stds = ['c89', 'c99', 'c11'] 102 g_stds = ['gnu89', 'gnu99', 'gnu11'] 103 # https://releases.llvm.org/6.0.0/tools/clang/docs/ReleaseNotes.html 104 # https://en.wikipedia.org/wiki/Xcode#Latest_versions 105 if version_compare(self.version, self._C17_VERSION): 106 c_stds += ['c17'] 107 g_stds += ['gnu17'] 108 if version_compare(self.version, self._C18_VERSION): 109 c_stds += ['c18'] 110 g_stds += ['gnu18'] 111 opts.update({ 112 'std': coredata.UserComboOption( 113 'C language standard to use', 114 ['none'] + c_stds + g_stds, 115 'none', 116 ), 117 }) 118 if self.info.is_windows() or self.info.is_cygwin(): 119 opts.update({ 120 'winlibs': coredata.UserArrayOption( 121 'Standard Win libraries to link against', 122 gnu_winlibs, 123 ), 124 }) 125 return opts 126 127 def get_option_compile_args(self, options): 128 args = [] 129 std = options['std'] 130 if std.value != 'none': 131 args.append('-std=' + std.value) 132 return args 133 134 def get_option_link_args(self, options): 135 if self.info.is_windows() or self.info.is_cygwin(): 136 return options['winlibs'].value[:] 137 return [] 138 139 140class AppleClangCCompiler(ClangCCompiler): 141 142 """Handle the differences between Apple Clang and Vanilla Clang. 143 144 Right now this just handles the differences between the versions that new 145 C standards were added. 146 """ 147 148 _C17_VERSION = '>=10.0.0' 149 _C18_VERSION = '>=11.0.0' 150 151 152class EmscriptenCCompiler(EmscriptenMixin, LinkerEnvVarsMixin, ClangCCompiler): 153 def __init__(self, exelist, version, for_machine: MachineChoice, 154 is_cross: bool, info: 'MachineInfo', exe_wrapper=None, **kwargs): 155 if not is_cross: 156 raise MesonException('Emscripten compiler can only be used for cross compilation.') 157 ClangCCompiler.__init__(self, exelist=exelist, version=version, 158 for_machine=for_machine, is_cross=is_cross, 159 info=info, exe_wrapper=exe_wrapper, **kwargs) 160 self.id = 'emscripten' 161 162 163class ArmclangCCompiler(ArmclangCompiler, CCompiler): 164 def __init__(self, exelist, version, for_machine: MachineChoice, 165 is_cross, info: 'MachineInfo', exe_wrapper=None, **kwargs): 166 CCompiler.__init__(self, exelist, version, for_machine, is_cross, 167 info, exe_wrapper, **kwargs) 168 ArmclangCompiler.__init__(self) 169 default_warn_args = ['-Wall', '-Winvalid-pch'] 170 self.warn_args = {'0': [], 171 '1': default_warn_args, 172 '2': default_warn_args + ['-Wextra'], 173 '3': default_warn_args + ['-Wextra', '-Wpedantic']} 174 175 def get_options(self): 176 opts = CCompiler.get_options(self) 177 opts.update({ 178 'std': coredata.UserComboOption( 179 'C language standard to use', 180 ['none', 'c90', 'c99', 'c11', 'gnu90', 'gnu99', 'gnu11'], 181 'none', 182 ), 183 }) 184 return opts 185 186 def get_option_compile_args(self, options): 187 args = [] 188 std = options['std'] 189 if std.value != 'none': 190 args.append('-std=' + std.value) 191 return args 192 193 def get_option_link_args(self, options): 194 return [] 195 196 197class GnuCCompiler(GnuCompiler, CCompiler): 198 def __init__(self, exelist, version, for_machine: MachineChoice, 199 is_cross, info: 'MachineInfo', exe_wrapper=None, 200 defines=None, **kwargs): 201 CCompiler.__init__(self, exelist, version, for_machine, is_cross, 202 info, exe_wrapper, **kwargs) 203 GnuCompiler.__init__(self, defines) 204 default_warn_args = ['-Wall', '-Winvalid-pch'] 205 self.warn_args = {'0': [], 206 '1': default_warn_args, 207 '2': default_warn_args + ['-Wextra'], 208 '3': default_warn_args + ['-Wextra', '-Wpedantic']} 209 210 def get_options(self): 211 opts = CCompiler.get_options(self) 212 c_stds = ['c89', 'c99', 'c11'] 213 g_stds = ['gnu89', 'gnu99', 'gnu11'] 214 v = '>=8.0.0' 215 if version_compare(self.version, v): 216 c_stds += ['c17', 'c18'] 217 g_stds += ['gnu17', 'gnu18'] 218 opts.update({ 219 'std': coredata.UserComboOption( 220 'C language standard to use', 221 ['none'] + c_stds + g_stds, 222 'none', 223 ), 224 }) 225 if self.info.is_windows() or self.info.is_cygwin(): 226 opts.update({ 227 'winlibs': coredata.UserArrayOption( 228 'Standard Win libraries to link against', 229 gnu_winlibs, 230 ), 231 }) 232 return opts 233 234 def get_option_compile_args(self, options): 235 args = [] 236 std = options['std'] 237 if std.value != 'none': 238 args.append('-std=' + std.value) 239 return args 240 241 def get_option_link_args(self, options): 242 if self.info.is_windows() or self.info.is_cygwin(): 243 return options['winlibs'].value[:] 244 return [] 245 246 def get_pch_use_args(self, pch_dir, header): 247 return ['-fpch-preprocess', '-include', os.path.basename(header)] 248 249 250class PGICCompiler(PGICompiler, CCompiler): 251 def __init__(self, exelist, version, for_machine: MachineChoice, 252 is_cross, info: 'MachineInfo', exe_wrapper=None, **kwargs): 253 CCompiler.__init__(self, exelist, version, for_machine, is_cross, 254 info, exe_wrapper, **kwargs) 255 PGICompiler.__init__(self) 256 257 258class ElbrusCCompiler(GnuCCompiler, ElbrusCompiler): 259 def __init__(self, exelist, version, for_machine: MachineChoice, 260 is_cross, info: 'MachineInfo', exe_wrapper=None, 261 defines=None, **kwargs): 262 GnuCCompiler.__init__(self, exelist, version, for_machine, is_cross, 263 info, exe_wrapper, defines, **kwargs) 264 ElbrusCompiler.__init__(self) 265 266 # It does support some various ISO standards and c/gnu 90, 9x, 1x in addition to those which GNU CC supports. 267 def get_options(self): 268 opts = CCompiler.get_options(self) 269 opts.update({ 270 'std': coredata.UserComboOption( 271 'C language standard to use', 272 [ 273 'none', 'c89', 'c90', 'c9x', 'c99', 'c1x', 'c11', 274 'gnu89', 'gnu90', 'gnu9x', 'gnu99', 'gnu1x', 'gnu11', 275 'iso9899:2011', 'iso9899:1990', 'iso9899:199409', 'iso9899:1999', 276 ], 277 'none', 278 ), 279 }) 280 return opts 281 282 # Elbrus C compiler does not have lchmod, but there is only linker warning, not compiler error. 283 # So we should explicitly fail at this case. 284 def has_function(self, funcname, prefix, env, *, extra_args=None, dependencies=None): 285 if funcname == 'lchmod': 286 return False, False 287 else: 288 return super().has_function(funcname, prefix, env, 289 extra_args=extra_args, 290 dependencies=dependencies) 291 292 293class IntelCCompiler(IntelGnuLikeCompiler, CCompiler): 294 def __init__(self, exelist, version, for_machine: MachineChoice, 295 is_cross, info: 'MachineInfo', exe_wrapper=None, **kwargs): 296 CCompiler.__init__(self, exelist, version, for_machine, is_cross, 297 info, exe_wrapper, **kwargs) 298 IntelGnuLikeCompiler.__init__(self) 299 self.lang_header = 'c-header' 300 default_warn_args = ['-Wall', '-w3', '-diag-disable:remark'] 301 self.warn_args = {'0': [], 302 '1': default_warn_args, 303 '2': default_warn_args + ['-Wextra'], 304 '3': default_warn_args + ['-Wextra']} 305 306 def get_options(self): 307 opts = CCompiler.get_options(self) 308 c_stds = ['c89', 'c99'] 309 g_stds = ['gnu89', 'gnu99'] 310 if version_compare(self.version, '>=16.0.0'): 311 c_stds += ['c11'] 312 opts.update({ 313 'std': coredata.UserComboOption( 314 'C language standard to use', 315 ['none'] + c_stds + g_stds, 316 'none', 317 ), 318 }) 319 return opts 320 321 def get_option_compile_args(self, options): 322 args = [] 323 std = options['std'] 324 if std.value != 'none': 325 args.append('-std=' + std.value) 326 return args 327 328 329class VisualStudioLikeCCompilerMixin: 330 331 """Shared methods that apply to MSVC-like C compilers.""" 332 333 def get_options(self): 334 opts = super().get_options() 335 opts.update({ 336 'winlibs': coredata.UserArrayOption( 337 'Windows libs to link against.', 338 msvc_winlibs, 339 ), 340 }) 341 return opts 342 343 def get_option_link_args(self, options): 344 return options['winlibs'].value[:] 345 346 347class VisualStudioCCompiler(MSVCCompiler, VisualStudioLikeCCompilerMixin, CCompiler): 348 349 def __init__(self, exelist, version, for_machine: MachineChoice, 350 is_cross, info: 'MachineInfo', exe_wrap, target: str, 351 **kwargs): 352 CCompiler.__init__(self, exelist, version, for_machine, is_cross, 353 info, exe_wrap, **kwargs) 354 MSVCCompiler.__init__(self, target) 355 356 357class ClangClCCompiler(ClangClCompiler, VisualStudioLikeCCompilerMixin, CCompiler): 358 def __init__(self, exelist, version, for_machine: MachineChoice, 359 is_cross, info: 'MachineInfo', exe_wrap, target, **kwargs): 360 CCompiler.__init__(self, exelist, version, for_machine, is_cross, 361 info, exe_wrap, **kwargs) 362 ClangClCompiler.__init__(self, target) 363 364 365class IntelClCCompiler(IntelVisualStudioLikeCompiler, VisualStudioLikeCCompilerMixin, CCompiler): 366 367 """Intel "ICL" compiler abstraction.""" 368 369 def __init__(self, exelist, version, for_machine: MachineChoice, 370 is_cross, info: 'MachineInfo', exe_wrap, target, **kwargs): 371 CCompiler.__init__(self, exelist, version, for_machine, is_cross, 372 info, exe_wrap, **kwargs) 373 IntelVisualStudioLikeCompiler.__init__(self, target) 374 375 def get_options(self): 376 opts = super().get_options() 377 c_stds = ['none', 'c89', 'c99', 'c11'] 378 opts.update({ 379 'std': coredata.UserComboOption( 380 'C language standard to use', 381 c_stds, 382 'none', 383 ), 384 }) 385 return opts 386 387 def get_option_compile_args(self, options): 388 args = [] 389 std = options['std'] 390 if std.value == 'c89': 391 mlog.warning("ICL doesn't explicitly implement c89, setting the standard to 'none', which is close.", once=True) 392 elif std.value != 'none': 393 args.append('/Qstd:' + std.value) 394 return args 395 396 397class ArmCCompiler(ArmCompiler, CCompiler): 398 def __init__(self, exelist, version, for_machine: MachineChoice, 399 is_cross, info: 'MachineInfo', exe_wrapper=None, **kwargs): 400 CCompiler.__init__(self, exelist, version, for_machine, is_cross, 401 info, exe_wrapper, **kwargs) 402 ArmCompiler.__init__(self) 403 404 def get_options(self): 405 opts = CCompiler.get_options(self) 406 opts.update({ 407 'std': coredata.UserComboOption( 408 'C language standard to use', 409 ['none', 'c90', 'c99'], 410 'none', 411 ), 412 }) 413 return opts 414 415 def get_option_compile_args(self, options): 416 args = [] 417 std = options['std'] 418 if std.value != 'none': 419 args.append('--' + std.value) 420 return args 421 422 423class CcrxCCompiler(CcrxCompiler, CCompiler): 424 def __init__(self, exelist, version, for_machine: MachineChoice, 425 is_cross, info: 'MachineInfo', exe_wrapper=None, **kwargs): 426 CCompiler.__init__(self, exelist, version, for_machine, is_cross, 427 info, exe_wrapper, **kwargs) 428 CcrxCompiler.__init__(self) 429 430 # Override CCompiler.get_always_args 431 def get_always_args(self): 432 return ['-nologo'] 433 434 def get_options(self): 435 opts = CCompiler.get_options(self) 436 opts.update({ 437 'std': coredata.UserComboOption( 438 'C language standard to use', 439 ['none', 'c89', 'c99'], 440 'none', 441 ), 442 }) 443 return opts 444 445 def get_no_stdinc_args(self): 446 return [] 447 448 def get_option_compile_args(self, options): 449 args = [] 450 std = options['std'] 451 if std.value == 'c89': 452 args.append('-lang=c') 453 elif std.value == 'c99': 454 args.append('-lang=c99') 455 return args 456 457 def get_compile_only_args(self): 458 return [] 459 460 def get_no_optimization_args(self): 461 return ['-optimize=0'] 462 463 def get_output_args(self, target): 464 return ['-output=obj=%s' % target] 465 466 def get_werror_args(self): 467 return ['-change_message=error'] 468 469 def get_include_args(self, path, is_system): 470 if path == '': 471 path = '.' 472 return ['-include=' + path] 473 474 475class Xc16CCompiler(Xc16Compiler, CCompiler): 476 def __init__(self, exelist, version, for_machine: MachineChoice, 477 is_cross, info: 'MachineInfo', exe_wrapper=None, **kwargs): 478 CCompiler.__init__(self, exelist, version, for_machine, is_cross, 479 info, exe_wrapper, **kwargs) 480 Xc16Compiler.__init__(self) 481 482 def get_options(self): 483 opts = CCompiler.get_options(self) 484 opts.update({'c_std': coredata.UserComboOption('C language standard to use', 485 ['none', 'c89', 'c99', 'gnu89', 'gnu99'], 486 'none')}) 487 return opts 488 489 def get_no_stdinc_args(self): 490 return [] 491 492 def get_option_compile_args(self, options): 493 args = [] 494 std = options['c_std'] 495 if std.value != 'none': 496 args.append('-ansi') 497 args.append('-std=' + std.value) 498 return args 499 500 def get_compile_only_args(self): 501 return [] 502 503 def get_no_optimization_args(self): 504 return ['-O0'] 505 506 def get_output_args(self, target): 507 return ['-o%s' % target] 508 509 def get_werror_args(self): 510 return ['-change_message=error'] 511 512 def get_include_args(self, path, is_system): 513 if path == '': 514 path = '.' 515 return ['-I' + path] 516 517 518class C2000CCompiler(C2000Compiler, CCompiler): 519 def __init__(self, exelist, version, for_machine: MachineChoice, 520 is_cross, info: 'MachineInfo', exe_wrapper=None, **kwargs): 521 CCompiler.__init__(self, exelist, version, for_machine, is_cross, 522 info, exe_wrapper, **kwargs) 523 C2000Compiler.__init__(self) 524 525 # Override CCompiler.get_always_args 526 def get_always_args(self): 527 return [] 528 529 def get_options(self): 530 opts = CCompiler.get_options(self) 531 opts.update({'c_std': coredata.UserComboOption('C language standard to use', 532 ['none', 'c89', 'c99', 'c11'], 533 'none')}) 534 return opts 535 536 def get_no_stdinc_args(self): 537 return [] 538 539 def get_option_compile_args(self, options): 540 args = [] 541 std = options['c_std'] 542 if std.value != 'none': 543 args.append('--' + std.value) 544 return args 545 546 def get_compile_only_args(self): 547 return [] 548 549 def get_no_optimization_args(self): 550 return ['-Ooff'] 551 552 def get_output_args(self, target): 553 return ['--output_file=%s' % target] 554 555 def get_werror_args(self): 556 return ['-change_message=error'] 557 558 def get_include_args(self, path, is_system): 559 if path == '': 560 path = '.' 561 return ['--include_path=' + path] 562