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, subprocess
16import typing as T
17
18from ..mesonlib import (
19    EnvironmentException, MachineChoice, version_compare,
20)
21
22from ..arglist import CompilerArgs
23from .compilers import (
24    d_dmd_buildtype_args,
25    d_gdc_buildtype_args,
26    d_ldc_buildtype_args,
27    clike_debug_args,
28    Compiler,
29)
30from .mixins.gnu import GnuCompiler
31
32if T.TYPE_CHECKING:
33    from ..envconfig import MachineInfo
34
35d_feature_args = {'gcc':  {'unittest': '-funittest',
36                           'debug': '-fdebug',
37                           'version': '-fversion',
38                           'import_dir': '-J'
39                           },
40                  'llvm': {'unittest': '-unittest',
41                           'debug': '-d-debug',
42                           'version': '-d-version',
43                           'import_dir': '-J'
44                           },
45                  'dmd':  {'unittest': '-unittest',
46                           'debug': '-debug',
47                           'version': '-version',
48                           'import_dir': '-J'
49                           }
50                  }
51
52ldc_optimization_args = {'0': [],
53                         'g': [],
54                         '1': ['-O1'],
55                         '2': ['-O2'],
56                         '3': ['-O3'],
57                         's': ['-Os'],
58                         }
59
60dmd_optimization_args = {'0': [],
61                         'g': [],
62                         '1': ['-O'],
63                         '2': ['-O'],
64                         '3': ['-O'],
65                         's': ['-O'],
66                         }
67
68
69class DmdLikeCompilerMixin:
70
71    LINKER_PREFIX = '-L='
72
73    def get_output_args(self, target):
74        return ['-of=' + target]
75
76    def get_linker_output_args(self, target):
77        return ['-of=' + target]
78
79    def get_include_args(self, path, is_system):
80        return ['-I=' + path]
81
82    def compute_parameters_with_absolute_paths(self, parameter_list, build_dir):
83        for idx, i in enumerate(parameter_list):
84            if i[:3] == '-I=':
85                parameter_list[idx] = i[:3] + os.path.normpath(os.path.join(build_dir, i[3:]))
86            if i[:4] == '-L-L':
87                parameter_list[idx] = i[:4] + os.path.normpath(os.path.join(build_dir, i[4:]))
88            if i[:5] == '-L=-L':
89                parameter_list[idx] = i[:5] + os.path.normpath(os.path.join(build_dir, i[5:]))
90            if i[:6] == '-Wl,-L':
91                parameter_list[idx] = i[:6] + os.path.normpath(os.path.join(build_dir, i[6:]))
92
93        return parameter_list
94
95    def get_warn_args(self, level):
96        return ['-wi']
97
98    def get_werror_args(self):
99        return ['-w']
100
101    def get_dependency_gen_args(self, outtarget, outfile):
102        # DMD and LDC does not currently return Makefile-compatible dependency info.
103        return []
104
105    def get_coverage_args(self):
106        return ['-cov']
107
108    def get_coverage_link_args(self):
109        return []
110
111    def get_preprocess_only_args(self):
112        return ['-E']
113
114    def get_compile_only_args(self):
115        return ['-c']
116
117    def depfile_for_object(self, objfile):
118        return objfile + '.' + self.get_depfile_suffix()
119
120    def get_depfile_suffix(self):
121        return 'deps'
122
123    def get_pic_args(self):
124        if self.info.is_windows():
125            return []
126        return ['-fPIC']
127
128    def get_feature_args(self, kwargs, build_to_src):
129        res = []
130        if 'unittest' in kwargs:
131            unittest = kwargs.pop('unittest')
132            unittest_arg = d_feature_args[self.id]['unittest']
133            if not unittest_arg:
134                raise EnvironmentException('D compiler %s does not support the "unittest" feature.' % self.name_string())
135            if unittest:
136                res.append(unittest_arg)
137
138        if 'debug' in kwargs:
139            debug_level = -1
140            debugs = kwargs.pop('debug')
141            if not isinstance(debugs, list):
142                debugs = [debugs]
143
144            debug_arg = d_feature_args[self.id]['debug']
145            if not debug_arg:
146                raise EnvironmentException('D compiler %s does not support conditional debug identifiers.' % self.name_string())
147
148            # Parse all debug identifiers and the largest debug level identifier
149            for d in debugs:
150                if isinstance(d, int):
151                    if d > debug_level:
152                        debug_level = d
153                elif isinstance(d, str) and d.isdigit():
154                    if int(d) > debug_level:
155                        debug_level = int(d)
156                else:
157                    res.append('{0}={1}'.format(debug_arg, d))
158
159            if debug_level >= 0:
160                res.append('{0}={1}'.format(debug_arg, debug_level))
161
162        if 'versions' in kwargs:
163            version_level = -1
164            versions = kwargs.pop('versions')
165            if not isinstance(versions, list):
166                versions = [versions]
167
168            version_arg = d_feature_args[self.id]['version']
169            if not version_arg:
170                raise EnvironmentException('D compiler %s does not support conditional version identifiers.' % self.name_string())
171
172            # Parse all version identifiers and the largest version level identifier
173            for v in versions:
174                if isinstance(v, int):
175                    if v > version_level:
176                        version_level = v
177                elif isinstance(v, str) and v.isdigit():
178                    if int(v) > version_level:
179                        version_level = int(v)
180                else:
181                    res.append('{0}={1}'.format(version_arg, v))
182
183            if version_level >= 0:
184                res.append('{0}={1}'.format(version_arg, version_level))
185
186        if 'import_dirs' in kwargs:
187            import_dirs = kwargs.pop('import_dirs')
188            if not isinstance(import_dirs, list):
189                import_dirs = [import_dirs]
190
191            import_dir_arg = d_feature_args[self.id]['import_dir']
192            if not import_dir_arg:
193                raise EnvironmentException('D compiler %s does not support the "string import directories" feature.' % self.name_string())
194            for idir_obj in import_dirs:
195                basedir = idir_obj.get_curdir()
196                for idir in idir_obj.get_incdirs():
197                    # Avoid superfluous '/.' at the end of paths when d is '.'
198                    if idir not in ('', '.'):
199                        expdir = os.path.join(basedir, idir)
200                    else:
201                        expdir = basedir
202                    srctreedir = os.path.join(build_to_src, expdir)
203                    res.append('{0}{1}'.format(import_dir_arg, srctreedir))
204
205        if kwargs:
206            raise EnvironmentException('Unknown D compiler feature(s) selected: %s' % ', '.join(kwargs.keys()))
207
208        return res
209
210    def get_buildtype_linker_args(self, buildtype):
211        if buildtype != 'plain':
212            return self.get_target_arch_args()
213        return []
214
215    def get_std_exe_link_args(self):
216        return []
217
218    def gen_import_library_args(self, implibname):
219        return self.linker.import_library_args(implibname)
220
221    def build_rpath_args(self, env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath):
222        if self.info.is_windows():
223            return ([], set())
224
225        # GNU ld, solaris ld, and lld acting like GNU ld
226        if self.linker.id.startswith('ld'):
227            # The way that dmd and ldc pass rpath to gcc is different than we would
228            # do directly, each argument -rpath and the value to rpath, need to be
229            # split into two separate arguments both prefaced with the -L=.
230            args = []
231            (rpath_args, rpath_dirs_to_remove) = super().build_rpath_args(
232                    env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath)
233            for r in rpath_args:
234                if ',' in r:
235                    a, b = r.split(',', maxsplit=1)
236                    args.append(a)
237                    args.append(self.LINKER_PREFIX + b)
238                else:
239                    args.append(r)
240            return (args, rpath_dirs_to_remove)
241
242        return super().build_rpath_args(
243            env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath)
244
245    def translate_args_to_nongnu(self, args):
246        dcargs = []
247        # Translate common arguments to flags the LDC/DMD compilers
248        # can understand.
249        # The flags might have been added by pkg-config files,
250        # and are therefore out of the user's control.
251        for arg in args:
252            # Translate OS specific arguments first.
253            osargs = []
254            if self.info.is_windows():
255                osargs = self.translate_arg_to_windows(arg)
256            elif self.info.is_darwin():
257                osargs = self.translate_arg_to_osx(arg)
258            if osargs:
259                dcargs.extend(osargs)
260                continue
261
262            # Translate common D arguments here.
263            if arg == '-pthread':
264                continue
265            if arg.startswith('-fstack-protector'):
266                continue
267            if arg.startswith('-D'):
268                continue
269            if arg.startswith('-Wl,'):
270                # Translate linker arguments here.
271                linkargs = arg[arg.index(',') + 1:].split(',')
272                for la in linkargs:
273                    dcargs.append('-L=' + la.strip())
274                continue
275            elif arg.startswith(('-link-defaultlib', '-linker', '-link-internally', '-linkonce-templates', '-lib')):
276                # these are special arguments to the LDC linker call,
277                # arguments like "-link-defaultlib-shared" do *not*
278                # denote a library to be linked, but change the default
279                # Phobos/DRuntime linking behavior, while "-linker" sets the
280                # default linker.
281                dcargs.append(arg)
282                continue
283            elif arg.startswith('-l'):
284                # translate library link flag
285                dcargs.append('-L=' + arg)
286                continue
287            elif arg.startswith('-isystem'):
288                # translate -isystem system include path
289                # this flag might sometimes be added by C library Cflags via
290                # pkg-config.
291                # NOTE: -isystem and -I are not 100% equivalent, so this is just
292                # a workaround for the most common cases.
293                if arg.startswith('-isystem='):
294                    dcargs.append('-I=' + arg[9:])
295                else:
296                    dcargs.append('-I' + arg[8:])
297                continue
298            elif arg.startswith('-idirafter'):
299                # same as -isystem, but appends the path instead
300                if arg.startswith('-idirafter='):
301                    dcargs.append('-I=' + arg[11:])
302                else:
303                    dcargs.append('-I' + arg[10:])
304                continue
305            elif arg.startswith('-L/') or arg.startswith('-L./'):
306                # we need to handle cases where -L is set by e.g. a pkg-config
307                # setting to select a linker search path. We can however not
308                # unconditionally prefix '-L' with '-L' because the user might
309                # have set this flag too to do what it is intended to for this
310                # compiler (pass flag through to the linker)
311                # Hence, we guess here whether the flag was intended to pass
312                # a linker search path.
313
314                # Make sure static library files are passed properly to the linker.
315                if arg.endswith('.a') or arg.endswith('.lib'):
316                    if arg.startswith('-L='):
317                        farg = arg[3:]
318                    else:
319                        farg = arg[2:]
320                    if len(farg) > 0 and not farg.startswith('-'):
321                        dcargs.append('-L=' + farg)
322                        continue
323
324                dcargs.append('-L=' + arg)
325                continue
326            elif not arg.startswith('-') and arg.endswith(('.a', '.lib')):
327                # ensure static libraries are passed through to the linker
328                dcargs.append('-L=' + arg)
329                continue
330            else:
331                dcargs.append(arg)
332
333        return dcargs
334
335    @classmethod
336    def translate_arg_to_windows(cls, arg):
337        args = []
338        if arg.startswith('-Wl,'):
339            # Translate linker arguments here.
340            linkargs = arg[arg.index(',') + 1:].split(',')
341            for la in linkargs:
342                if la.startswith('--out-implib='):
343                    # Import library name
344                    args.append('-L=/IMPLIB:' + la[13:].strip())
345        elif arg.startswith('-mscrtlib='):
346            args.append(arg)
347            mscrtlib = arg[10:].lower()
348            if cls is LLVMDCompiler:
349                # Default crt libraries for LDC2 must be excluded for other
350                # selected crt options.
351                if mscrtlib != 'libcmt':
352                    args.append('-L=/NODEFAULTLIB:libcmt')
353                    args.append('-L=/NODEFAULTLIB:libvcruntime')
354
355                # Fixes missing definitions for printf-functions in VS2017
356                if mscrtlib.startswith('msvcrt'):
357                    args.append('-L=/DEFAULTLIB:legacy_stdio_definitions.lib')
358
359        return args
360
361    @classmethod
362    def translate_arg_to_osx(cls, arg):
363        args = []
364        if arg.startswith('-install_name'):
365            args.append('-L=' + arg)
366        return args
367
368    def get_debug_args(self, is_debug):
369        ddebug_args = []
370        if is_debug:
371            ddebug_args = [d_feature_args[self.id]['debug']]
372
373        return clike_debug_args[is_debug] + ddebug_args
374
375    def get_crt_args(self, crt_val, buildtype):
376        if not self.info.is_windows():
377            return []
378
379        if crt_val in self.mscrt_args:
380            return self.mscrt_args[crt_val]
381        assert(crt_val == 'from_buildtype')
382
383        # Match what build type flags used to do.
384        if buildtype == 'plain':
385            return []
386        elif buildtype == 'debug':
387            return self.mscrt_args['mdd']
388        elif buildtype == 'debugoptimized':
389            return self.mscrt_args['md']
390        elif buildtype == 'release':
391            return self.mscrt_args['md']
392        elif buildtype == 'minsize':
393            return self.mscrt_args['md']
394        else:
395            assert(buildtype == 'custom')
396            raise EnvironmentException('Requested C runtime based on buildtype, but buildtype is "custom".')
397
398    def get_soname_args(self, *args, **kwargs) -> T.List[str]:
399        # LDC and DMD actually do use a linker, but they proxy all of that with
400        # their own arguments
401        if self.linker.id.startswith('ld.'):
402            soargs = []
403            for arg in super().get_soname_args(*args, **kwargs):
404                a, b = arg.split(',', maxsplit=1)
405                soargs.append(a)
406                soargs.append(self.LINKER_PREFIX + b)
407            return soargs
408        elif self.linker.id.startswith('ld64'):
409            soargs = []
410            for arg in super().get_soname_args(*args, **kwargs):
411                if not arg.startswith(self.LINKER_PREFIX):
412                    soargs.append(self.LINKER_PREFIX + arg)
413                else:
414                    soargs.append(arg)
415            return soargs
416        else:
417            return super().get_soname_args(*args, **kwargs)
418
419    def get_allow_undefined_link_args(self) -> T.List[str]:
420        args = self.linker.get_allow_undefined_args()
421        if self.info.is_darwin():
422            # On macOS we're passing these options to the C compiler, but
423            # they're linker options and need -Wl, so clang/gcc knows what to
424            # do with them. I'm assuming, but don't know for certain, that
425            # ldc/dmd do some kind of mapping internally for arguments they
426            # understand, but pass arguments they don't understand directly.
427            args = [a.replace('-L=', '-Xcc=-Wl,') for a in args]
428        return args
429
430class DCompilerArgs(CompilerArgs):
431    prepend_prefixes = ('-I', '-L')
432    dedup2_prefixes = ('-I')
433
434class DCompiler(Compiler):
435    mscrt_args = {
436        'none': ['-mscrtlib='],
437        'md': ['-mscrtlib=msvcrt'],
438        'mdd': ['-mscrtlib=msvcrtd'],
439        'mt': ['-mscrtlib=libcmt'],
440        'mtd': ['-mscrtlib=libcmtd'],
441    }
442
443    language = 'd'
444
445    def __init__(self, exelist, version, for_machine: MachineChoice,
446                 info: 'MachineInfo', arch, is_cross, exe_wrapper, **kwargs):
447        super().__init__(exelist, version, for_machine, info, **kwargs)
448        self.id = 'unknown'
449        self.arch = arch
450        self.exe_wrapper = exe_wrapper
451        self.is_cross = is_cross
452
453    def sanity_check(self, work_dir, environment):
454        source_name = os.path.join(work_dir, 'sanity.d')
455        output_name = os.path.join(work_dir, 'dtest')
456        with open(source_name, 'w') as ofile:
457            ofile.write('''void main() { }''')
458        pc = subprocess.Popen(self.exelist + self.get_output_args(output_name) + self.get_target_arch_args() + [source_name], cwd=work_dir)
459        pc.wait()
460        if pc.returncode != 0:
461            raise EnvironmentException('D compiler %s can not compile programs.' % self.name_string())
462        if self.is_cross:
463            if self.exe_wrapper is None:
464                # Can't check if the binaries run so we have to assume they do
465                return
466            cmdlist = self.exe_wrapper.get_command() + [output_name]
467        else:
468            cmdlist = [output_name]
469        if subprocess.call(cmdlist) != 0:
470            raise EnvironmentException('Executables created by D compiler %s are not runnable.' % self.name_string())
471
472    def needs_static_linker(self):
473        return True
474
475    def depfile_for_object(self, objfile):
476        return objfile + '.' + self.get_depfile_suffix()
477
478    def get_depfile_suffix(self):
479        return 'deps'
480
481    def get_pic_args(self):
482        if self.info.is_windows():
483            return []
484        return ['-fPIC']
485
486    def get_feature_args(self, kwargs, build_to_src):
487        res = []
488        if 'unittest' in kwargs:
489            unittest = kwargs.pop('unittest')
490            unittest_arg = d_feature_args[self.id]['unittest']
491            if not unittest_arg:
492                raise EnvironmentException('D compiler %s does not support the "unittest" feature.' % self.name_string())
493            if unittest:
494                res.append(unittest_arg)
495
496        if 'debug' in kwargs:
497            debug_level = -1
498            debugs = kwargs.pop('debug')
499            if not isinstance(debugs, list):
500                debugs = [debugs]
501
502            debug_arg = d_feature_args[self.id]['debug']
503            if not debug_arg:
504                raise EnvironmentException('D compiler %s does not support conditional debug identifiers.' % self.name_string())
505
506            # Parse all debug identifiers and the largest debug level identifier
507            for d in debugs:
508                if isinstance(d, int):
509                    if d > debug_level:
510                        debug_level = d
511                elif isinstance(d, str) and d.isdigit():
512                    if int(d) > debug_level:
513                        debug_level = int(d)
514                else:
515                    res.append('{0}={1}'.format(debug_arg, d))
516
517            if debug_level >= 0:
518                res.append('{0}={1}'.format(debug_arg, debug_level))
519
520        if 'versions' in kwargs:
521            version_level = -1
522            versions = kwargs.pop('versions')
523            if not isinstance(versions, list):
524                versions = [versions]
525
526            version_arg = d_feature_args[self.id]['version']
527            if not version_arg:
528                raise EnvironmentException('D compiler %s does not support conditional version identifiers.' % self.name_string())
529
530            # Parse all version identifiers and the largest version level identifier
531            for v in versions:
532                if isinstance(v, int):
533                    if v > version_level:
534                        version_level = v
535                elif isinstance(v, str) and v.isdigit():
536                    if int(v) > version_level:
537                        version_level = int(v)
538                else:
539                    res.append('{0}={1}'.format(version_arg, v))
540
541            if version_level >= 0:
542                res.append('{0}={1}'.format(version_arg, version_level))
543
544        if 'import_dirs' in kwargs:
545            import_dirs = kwargs.pop('import_dirs')
546            if not isinstance(import_dirs, list):
547                import_dirs = [import_dirs]
548
549            import_dir_arg = d_feature_args[self.id]['import_dir']
550            if not import_dir_arg:
551                raise EnvironmentException('D compiler %s does not support the "string import directories" feature.' % self.name_string())
552            for idir_obj in import_dirs:
553                basedir = idir_obj.get_curdir()
554                for idir in idir_obj.get_incdirs():
555                    # Avoid superfluous '/.' at the end of paths when d is '.'
556                    if idir not in ('', '.'):
557                        expdir = os.path.join(basedir, idir)
558                    else:
559                        expdir = basedir
560                    srctreedir = os.path.join(build_to_src, expdir)
561                    res.append('{0}{1}'.format(import_dir_arg, srctreedir))
562
563        if kwargs:
564            raise EnvironmentException('Unknown D compiler feature(s) selected: %s' % ', '.join(kwargs.keys()))
565
566        return res
567
568    def get_buildtype_linker_args(self, buildtype):
569        if buildtype != 'plain':
570            return self.get_target_arch_args()
571        return []
572
573    def get_std_exe_link_args(self):
574        return []
575
576    def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'):
577        if callable(extra_args):
578            extra_args = extra_args(mode)
579        if extra_args is None:
580            extra_args = []
581        elif isinstance(extra_args, str):
582            extra_args = [extra_args]
583        if dependencies is None:
584            dependencies = []
585        elif not isinstance(dependencies, list):
586            dependencies = [dependencies]
587        # Collect compiler arguments
588        args = self.compiler_args()
589        for d in dependencies:
590            # Add compile flags needed by dependencies
591            args += d.get_compile_args()
592            if mode == 'link':
593                # Add link flags needed to find dependencies
594                args += d.get_link_args()
595
596        if mode == 'compile':
597            # Add DFLAGS from the env
598            args += env.coredata.get_external_args(self.for_machine, self.language)
599        elif mode == 'link':
600            # Add LDFLAGS from the env
601            args += env.coredata.get_external_link_args(self.for_machine, self.language)
602        # extra_args must override all other arguments, so we add them last
603        args += extra_args
604        return args
605
606    def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> DCompilerArgs:
607        return DCompilerArgs(self, args)
608
609    def compiles(self, code, env, *, extra_args=None, dependencies=None, mode='compile'):
610        args = self._get_compiler_check_args(env, extra_args, dependencies, mode)
611
612        with self.cached_compile(code, env.coredata, extra_args=args, mode=mode) as p:
613            return p.returncode == 0, p.cached
614
615    def has_multi_arguments(self, args, env):
616        return self.compiles('int i;\n', env, extra_args=args)
617
618    def get_target_arch_args(self):
619        # LDC2 on Windows targets to current OS architecture, but
620        # it should follow the target specified by the MSVC toolchain.
621        if self.info.is_windows():
622            if self.arch == 'x86_64':
623                return ['-m64']
624            return ['-m32']
625        return []
626
627    def get_crt_compile_args(self, crt_val, buildtype):
628        return []
629
630    def get_crt_link_args(self, crt_val, buildtype):
631        return []
632
633    def thread_link_flags(self, env):
634        return self.linker.thread_flags(env)
635
636    def name_string(self):
637        return ' '.join(self.exelist)
638
639
640class GnuDCompiler(GnuCompiler, DCompiler):
641
642    # we mostly want DCompiler, but that gives us the Compiler.LINKER_PREFIX instead
643    LINKER_PREFIX = GnuCompiler.LINKER_PREFIX
644
645    def __init__(self, exelist, version, for_machine: MachineChoice,
646                 info: 'MachineInfo', is_cross, exe_wrapper, arch, **kwargs):
647        DCompiler.__init__(self, exelist, version, for_machine, info, is_cross, exe_wrapper, arch, **kwargs)
648        GnuCompiler.__init__(self, {})
649        self.id = 'gcc'
650        default_warn_args = ['-Wall', '-Wdeprecated']
651        self.warn_args = {'0': [],
652                          '1': default_warn_args,
653                          '2': default_warn_args + ['-Wextra'],
654                          '3': default_warn_args + ['-Wextra', '-Wpedantic']}
655        self.base_options = ['b_colorout', 'b_sanitize', 'b_staticpic',
656                             'b_vscrt', 'b_coverage', 'b_pgo', 'b_ndebug']
657
658        self._has_color_support = version_compare(self.version, '>=4.9')
659        # dependencies were implemented before, but broken - support was fixed in GCC 7.1+
660        # (and some backported versions)
661        self._has_deps_support = version_compare(self.version, '>=7.1')
662
663    def get_colorout_args(self, colortype):
664        if self._has_color_support:
665            super().get_colorout_args(colortype)
666        return []
667
668    def get_dependency_gen_args(self, outtarget, outfile):
669        if self._has_deps_support:
670            return super().get_dependency_gen_args(outtarget, outfile)
671        return []
672
673    def get_warn_args(self, level):
674        return self.warn_args[level]
675
676    def get_buildtype_args(self, buildtype):
677        return d_gdc_buildtype_args[buildtype]
678
679    def compute_parameters_with_absolute_paths(self, parameter_list, build_dir):
680        for idx, i in enumerate(parameter_list):
681            if i[:2] == '-I' or i[:2] == '-L':
682                parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:]))
683
684        return parameter_list
685
686    def get_allow_undefined_link_args(self) -> T.List[str]:
687        return self.linker.get_allow_undefined_args()
688
689    def get_linker_always_args(self) -> T.List[str]:
690        args = super().get_linker_always_args()
691        if self.info.is_windows():
692            return args
693        return args + ['-shared-libphobos']
694
695    def get_disable_assert_args(self):
696        return ['-frelease']
697
698
699class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler):
700
701    def __init__(self, exelist, version, for_machine: MachineChoice,
702                 info: 'MachineInfo', arch, **kwargs):
703        DCompiler.__init__(self, exelist, version, for_machine, info, arch, False, None, **kwargs)
704        self.id = 'llvm'
705        self.base_options = ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']
706
707    def get_colorout_args(self, colortype):
708        if colortype == 'always':
709            return ['-enable-color']
710        return []
711
712    def get_warn_args(self, level):
713        if level == '2' or level == '3':
714            return ['-wi', '-dw']
715        elif level == '1':
716            return ['-wi']
717        else:
718            return []
719
720    def get_buildtype_args(self, buildtype):
721        if buildtype != 'plain':
722            return self.get_target_arch_args() + d_ldc_buildtype_args[buildtype]
723        return d_ldc_buildtype_args[buildtype]
724
725    def get_pic_args(self):
726        return ['-relocation-model=pic']
727
728    def get_crt_link_args(self, crt_val, buildtype):
729        return self.get_crt_args(crt_val, buildtype)
730
731    def unix_args_to_native(self, args):
732        return self.translate_args_to_nongnu(args)
733
734    def get_optimization_args(self, optimization_level):
735        return ldc_optimization_args[optimization_level]
736
737    @classmethod
738    def use_linker_args(cls, linker: str) -> T.List[str]:
739        return ['-linker={}'.format(linker)]
740
741    def get_linker_always_args(self) -> T.List[str]:
742        args = super().get_linker_always_args()
743        if self.info.is_windows():
744            return args
745        return args + ['-link-defaultlib-shared']
746
747    def get_disable_assert_args(self) -> T.List[str]:
748        return ['--release']
749
750
751class DmdDCompiler(DmdLikeCompilerMixin, DCompiler):
752
753    def __init__(self, exelist, version, for_machine: MachineChoice,
754                 info: 'MachineInfo', arch, **kwargs):
755        DCompiler.__init__(self, exelist, version, for_machine, info, arch, False, None, **kwargs)
756        self.id = 'dmd'
757        self.base_options = ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']
758
759    def get_colorout_args(self, colortype):
760        if colortype == 'always':
761            return ['-color=on']
762        return []
763
764    def get_buildtype_args(self, buildtype):
765        if buildtype != 'plain':
766            return self.get_target_arch_args() + d_dmd_buildtype_args[buildtype]
767        return d_dmd_buildtype_args[buildtype]
768
769    def get_std_exe_link_args(self):
770        if self.info.is_windows():
771            # DMD links against D runtime only when main symbol is found,
772            # so these needs to be inserted when linking static D libraries.
773            if self.arch == 'x86_64':
774                return ['phobos64.lib']
775            elif self.arch == 'x86_mscoff':
776                return ['phobos32mscoff.lib']
777            return ['phobos.lib']
778        return []
779
780    def get_std_shared_lib_link_args(self):
781        libname = 'libphobos2.so'
782        if self.info.is_windows():
783            if self.arch == 'x86_64':
784                libname = 'phobos64.lib'
785            elif self.arch == 'x86_mscoff':
786                libname = 'phobos32mscoff.lib'
787            else:
788                libname = 'phobos.lib'
789        return ['-shared', '-defaultlib=' + libname]
790
791    def get_target_arch_args(self):
792        # DMD32 and DMD64 on 64-bit Windows defaults to 32-bit (OMF).
793        # Force the target to 64-bit in order to stay consistent
794        # across the different platforms.
795        if self.info.is_windows():
796            if self.arch == 'x86_64':
797                return ['-m64']
798            elif self.arch == 'x86_mscoff':
799                return ['-m32mscoff']
800            return ['-m32']
801        return []
802
803    def get_crt_compile_args(self, crt_val, buildtype):
804        return self.get_crt_args(crt_val, buildtype)
805
806    def unix_args_to_native(self, args):
807        return self.translate_args_to_nongnu(args)
808
809    def get_optimization_args(self, optimization_level):
810        return dmd_optimization_args[optimization_level]
811
812    def can_linker_accept_rsp(self) -> bool:
813        return False
814
815    def get_linker_always_args(self) -> T.List[str]:
816        args = super().get_linker_always_args()
817        if self.info.is_windows():
818            return args
819        return args + ['-defaultlib=phobos2', '-debuglib=phobos2']
820
821    def get_disable_assert_args(self) -> T.List[str]:
822        return ['-release']
823