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