| # SPDX-License-Identifier: Apache-2.0 | |
| # Copyright 2012-2022 The Meson development team | |
| from __future__ import annotations | |
| import os.path | |
| import re | |
| import typing as T | |
| from .. import mesonlib | |
| from ..arglist import CompilerArgs | |
| from ..linkers import RSPFileSyntax | |
| from ..mesonlib import ( | |
| EnvironmentException, MesonBugException, version_compare, is_windows | |
| ) | |
| from ..options import OptionKey | |
| from .compilers import ( | |
| clike_debug_args, | |
| Compiler, | |
| CompileCheckMode, | |
| SimplePrefixLinkerOptionStyle, | |
| ) | |
| from .mixins.gnu import GnuCompiler | |
| from .mixins.gnu import gnu_common_warning_args | |
| if T.TYPE_CHECKING: | |
| from . import compilers | |
| from ..build import DFeatures | |
| from ..dependencies import Dependency | |
| from ..envconfig import MachineInfo | |
| from ..environment import Environment | |
| from ..linkers.linkers import DynamicLinker | |
| from ..mesonlib import MachineChoice | |
| CompilerMixinBase = Compiler | |
| else: | |
| CompilerMixinBase = object | |
| d_feature_args: T.Dict[str, T.Dict[str, str]] = { | |
| 'gcc': { | |
| 'unittest': '-funittest', | |
| 'debug': '-fdebug', | |
| 'version': '-fversion', | |
| 'import_dir': '-J' | |
| }, | |
| 'llvm': { | |
| 'unittest': '-unittest', | |
| 'debug': '-d-debug', | |
| 'version': '-d-version', | |
| 'import_dir': '-J' | |
| }, | |
| 'dmd': { | |
| 'unittest': '-unittest', | |
| 'debug': '-debug', | |
| 'version': '-version', | |
| 'import_dir': '-J' | |
| } | |
| } | |
| ldc_optimization_args: T.Dict[str, T.List[str]] = { | |
| 'plain': [], | |
| '0': [], | |
| 'g': [], | |
| '1': ['-O1'], | |
| '2': ['-O2', '-enable-inlining', '-Hkeep-all-bodies'], | |
| '3': ['-O3', '-enable-inlining', '-Hkeep-all-bodies'], | |
| 's': ['-Oz'], | |
| } | |
| dmd_optimization_args: T.Dict[str, T.List[str]] = { | |
| 'plain': [], | |
| '0': [], | |
| 'g': [], | |
| '1': ['-O'], | |
| '2': ['-O', '-inline'], | |
| '3': ['-O', '-inline'], | |
| 's': ['-O'], | |
| } | |
| gdc_optimization_args: T.Dict[str, T.List[str]] = { | |
| 'plain': [], | |
| '0': ['-O0'], | |
| 'g': ['-Og'], | |
| '1': ['-O1'], | |
| '2': ['-O2', '-finline-functions'], | |
| '3': ['-O3', '-finline-functions'], | |
| 's': ['-Os'], | |
| } | |
| class DmdLikeCompilerMixin(CompilerMixinBase): | |
| """Mixin class for DMD and LDC. | |
| LDC has a number of DMD like arguments, and this class allows for code | |
| sharing between them as makes sense. | |
| """ | |
| def __init__(self, dmd_frontend_version: T.Optional[str]): | |
| if dmd_frontend_version is None: | |
| self._dmd_has_depfile = False | |
| else: | |
| # -makedeps switch introduced in 2.095 frontend | |
| self._dmd_has_depfile = version_compare(dmd_frontend_version, ">=2.095.0") | |
| if T.TYPE_CHECKING: | |
| mscrt_args: T.Dict[str, T.List[str]] = {} | |
| def _get_target_arch_args(self) -> T.List[str]: ... | |
| LINKER_OPTION_STYLE = SimplePrefixLinkerOptionStyle('-L=') | |
| def get_output_args(self, outputname: str) -> T.List[str]: | |
| return ['-of=' + outputname] | |
| def get_linker_output_args(self, outputname: str) -> T.List[str]: | |
| return ['-of=' + outputname] | |
| def get_include_args(self, path: str, is_system: bool) -> T.List[str]: | |
| if path == "": | |
| path = "." | |
| return ['-I=' + path] | |
| def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], | |
| build_dir: str) -> T.List[str]: | |
| for idx, i in enumerate(parameter_list): | |
| if i[:3] == '-I=': | |
| parameter_list[idx] = i[:3] + os.path.normpath(os.path.join(build_dir, i[3:])) | |
| if i[:4] == '-L-L': | |
| parameter_list[idx] = i[:4] + os.path.normpath(os.path.join(build_dir, i[4:])) | |
| if i[:5] == '-L=-L': | |
| parameter_list[idx] = i[:5] + os.path.normpath(os.path.join(build_dir, i[5:])) | |
| if i[:6] == '-Wl,-L': | |
| parameter_list[idx] = i[:6] + os.path.normpath(os.path.join(build_dir, i[6:])) | |
| return parameter_list | |
| def get_warn_args(self, level: str) -> T.List[str]: | |
| return ['-wi'] | |
| def get_werror_args(self) -> T.List[str]: | |
| return ['-w'] | |
| def get_coverage_args(self) -> T.List[str]: | |
| return ['-cov'] | |
| def get_coverage_link_args(self) -> T.List[str]: | |
| return [] | |
| def get_preprocess_only_args(self) -> T.List[str]: | |
| return ['-E'] | |
| def get_compile_only_args(self) -> T.List[str]: | |
| return ['-c'] | |
| def get_depfile_suffix(self) -> str: | |
| return 'deps' | |
| def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]: | |
| if self._dmd_has_depfile: | |
| return [f'-makedeps={outfile}'] | |
| return [] | |
| def get_pic_args(self) -> T.List[str]: | |
| if self.info.is_windows(): | |
| return [] | |
| return ['-fPIC'] | |
| def get_optimization_link_args(self, optimization_level: str) -> T.List[str]: | |
| if optimization_level != 'plain': | |
| return self._get_target_arch_args() | |
| return [] | |
| def gen_import_library_args(self, implibname: str) -> T.List[str]: | |
| return self.linker.import_library_args(implibname) | |
| @classmethod | |
| def _translate_args_to_nongnu(cls, args: T.List[str], info: MachineInfo, link_id: str) -> T.List[str]: | |
| # Translate common arguments to flags the LDC/DMD compilers | |
| # can understand. | |
| # The flags might have been added by pkg-config files, | |
| # and are therefore out of the user's control. | |
| dcargs: T.List[str] = [] | |
| # whether we hit a linker argument that expect another arg | |
| # see the comment in the "-L" section | |
| link_expect_arg = False | |
| link_flags_with_arg = [ | |
| '-rpath', '-rpath-link', '-soname', '-compatibility_version', '-current_version', | |
| ] | |
| for arg in args: | |
| # Translate OS specific arguments first. | |
| osargs: T.List[str] = [] | |
| if info.is_windows(): | |
| osargs = cls.translate_arg_to_windows(arg) | |
| elif info.is_darwin(): | |
| osargs = cls._translate_arg_to_osx(arg) | |
| if osargs: | |
| dcargs.extend(osargs) | |
| continue | |
| # Translate common D arguments here. | |
| if arg == '-pthread': | |
| continue | |
| if arg.startswith('-fstack-protector'): | |
| continue | |
| if arg.startswith('-D') and not (arg == '-D' or arg.startswith(('-Dd', '-Df'))): | |
| # ignore all '-D*' flags (like '-D_THREAD_SAFE') | |
| # unless they are related to documentation | |
| continue | |
| if arg.startswith('-Wl,'): | |
| # Translate linker arguments here. | |
| linkargs = arg[arg.index(',') + 1:].split(',') | |
| for la in linkargs: | |
| dcargs.append('-L=' + la.strip()) | |
| continue | |
| elif arg.startswith(('-link-defaultlib', '-linker', '-link-internally', '-linkonce-templates', '-lib')): | |
| # these are special arguments to the LDC linker call, | |
| # arguments like "-link-defaultlib-shared" do *not* | |
| # denote a library to be linked, but change the default | |
| # Phobos/DRuntime linking behavior, while "-linker" sets the | |
| # default linker. | |
| dcargs.append(arg) | |
| continue | |
| elif arg.startswith('-l'): | |
| # translate library link flag | |
| dcargs.append('-L=' + arg) | |
| continue | |
| elif arg.startswith('-isystem'): | |
| # translate -isystem system include path | |
| # this flag might sometimes be added by C library Cflags via | |
| # pkg-config. | |
| # NOTE: -isystem and -I are not 100% equivalent, so this is just | |
| # a workaround for the most common cases. | |
| if arg.startswith('-isystem='): | |
| dcargs.append('-I=' + arg[9:]) | |
| else: | |
| dcargs.append('-I' + arg[8:]) | |
| continue | |
| elif arg.startswith('-idirafter'): | |
| # same as -isystem, but appends the path instead | |
| if arg.startswith('-idirafter='): | |
| dcargs.append('-I=' + arg[11:]) | |
| else: | |
| dcargs.append('-I' + arg[10:]) | |
| continue | |
| elif arg.startswith('-L'): | |
| # The D linker expect library search paths in the form of -L=-L/path (the '=' is optional). | |
| # | |
| # This function receives a mix of arguments already prepended | |
| # with -L for the D linker driver and other linker arguments. | |
| # The arguments starting with -L can be: | |
| # - library search path (with or without a second -L) | |
| # - it can come from pkg-config (a single -L) | |
| # - or from the user passing linker flags (-L-L would be expected) | |
| # - arguments like "-L=-rpath" that expect a second argument (also prepended with -L) | |
| # - arguments like "-L=@rpath/xxx" without a second argument (on Apple platform) | |
| # - arguments like "-L=/SUBSYSTEM:CONSOLE (for Windows linker) | |
| # | |
| # The logic that follows tries to detect all these cases (some may be missing) | |
| # in order to prepend a -L only for the library search paths with a single -L | |
| if arg.startswith('-L='): | |
| suffix = arg[3:] | |
| else: | |
| suffix = arg[2:] | |
| if link_expect_arg: | |
| # flags like rpath and soname expect a path or filename respectively, | |
| # we must not alter it (i.e. prefixing with -L for a lib search path) | |
| dcargs.append(arg) | |
| link_expect_arg = False | |
| continue | |
| if suffix in link_flags_with_arg: | |
| link_expect_arg = True | |
| if suffix.startswith('-') or suffix.startswith('@'): | |
| # this is not search path | |
| dcargs.append(arg) | |
| continue | |
| # linker flag such as -L=/DEBUG must pass through | |
| if info.is_windows() and link_id == 'link' and suffix.startswith('/'): | |
| dcargs.append(arg) | |
| continue | |
| # Make sure static library files are passed properly to the linker. | |
| if arg.endswith('.a') or arg.endswith('.lib'): | |
| if len(suffix) > 0 and not suffix.startswith('-'): | |
| dcargs.append('-L=' + suffix) | |
| continue | |
| dcargs.append('-L=' + arg) | |
| continue | |
| elif not arg.startswith('-') and arg.endswith(('.a', '.lib')): | |
| # ensure static libraries are passed through to the linker | |
| dcargs.append('-L=' + arg) | |
| continue | |
| else: | |
| dcargs.append(arg) | |
| return dcargs | |
| @classmethod | |
| def translate_arg_to_windows(cls, arg: str) -> T.List[str]: | |
| args: T.List[str] = [] | |
| if arg.startswith('-Wl,'): | |
| # Translate linker arguments here. | |
| linkargs = arg[arg.index(',') + 1:].split(',') | |
| for la in linkargs: | |
| if la.startswith('--out-implib='): | |
| # Import library name | |
| args.append('-L=/IMPLIB:' + la[13:].strip()) | |
| elif arg.startswith('-mscrtlib='): | |
| args.append(arg) | |
| mscrtlib = arg[10:].lower() | |
| if cls is LLVMDCompiler: | |
| # Default crt libraries for LDC2 must be excluded for other | |
| # selected crt options. | |
| if mscrtlib != 'libcmt': | |
| args.append('-L=/NODEFAULTLIB:libcmt') | |
| args.append('-L=/NODEFAULTLIB:libvcruntime') | |
| # Fixes missing definitions for printf-functions in VS2017 | |
| if mscrtlib.startswith('msvcrt'): | |
| args.append('-L=/DEFAULTLIB:legacy_stdio_definitions.lib') | |
| return args | |
| @classmethod | |
| def _translate_arg_to_osx(cls, arg: str) -> T.List[str]: | |
| args: T.List[str] = [] | |
| if arg.startswith('-install_name'): | |
| args.append('-L=' + arg) | |
| return args | |
| @classmethod | |
| def _unix_args_to_native(cls, args: T.List[str], info: MachineInfo, link_id: str = '') -> T.List[str]: | |
| return cls._translate_args_to_nongnu(args, info, link_id) | |
| def get_debug_args(self, is_debug: bool) -> T.List[str]: | |
| ddebug_args = [] | |
| if is_debug: | |
| ddebug_args = [d_feature_args[self.id]['debug']] | |
| return clike_debug_args[is_debug] + ddebug_args | |
| def _get_crt_args(self, crt_val: str) -> T.List[str]: | |
| if not self.info.is_windows(): | |
| return [] | |
| return self.mscrt_args[self.get_crt_val(crt_val)] | |
| def get_soname_args(self, prefix: str, shlib_name: str, suffix: str, soversion: str, | |
| darwin_versions: T.Tuple[str, str]) -> T.List[str]: | |
| sargs = super().get_soname_args(prefix, shlib_name, suffix, soversion, darwin_versions) | |
| if not all(arg.startswith(self.LINKER_OPTION_STYLE.prefix) for arg in sargs): | |
| raise MesonBugException(f'Not all soname arguments for the D compiler start with {repr(self.LINKER_OPTION_STYLE.prefix)}: {sargs}') | |
| return sargs | |
| def get_allow_undefined_link_args(self) -> T.List[str]: | |
| args = self.linker.get_allow_undefined_args() | |
| if self.info.is_darwin(): | |
| # On macOS we're passing these options to the C compiler, but | |
| # they're linker options and need -Wl, so clang/gcc knows what to | |
| # do with them. I'm assuming, but don't know for certain, that | |
| # ldc/dmd do some kind of mapping internally for arguments they | |
| # understand, but pass arguments they don't understand directly. | |
| args = [a.replace('-L=', '-Xcc=-Wl,') for a in args] | |
| return args | |
| class DCompilerArgs(CompilerArgs): | |
| prepend_prefixes = ('-I', '-L') | |
| dedup2_prefixes = ('-I', ) | |
| class DCompiler(Compiler): | |
| mscrt_args = { | |
| 'none': ['-mscrtlib='], | |
| 'md': ['-mscrtlib=msvcrt'], | |
| 'mdd': ['-mscrtlib=msvcrtd'], | |
| 'mt': ['-mscrtlib=libcmt'], | |
| 'mtd': ['-mscrtlib=libcmtd'], | |
| } | |
| language = 'd' | |
| def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, | |
| env: Environment, arch: str, *, | |
| linker: T.Optional['DynamicLinker'] = None, | |
| full_version: T.Optional[str] = None): | |
| super().__init__([], exelist, version, for_machine, env, linker=linker, | |
| full_version=full_version) | |
| self.arch = arch | |
| def _sanity_check_source_code(self) -> str: | |
| return 'void main() { }' | |
| def _sanity_check_compile_args(self, sourcename: str, binname: str | |
| ) -> T.Tuple[T.List[str], T.List[str]]: | |
| args, largs = super()._sanity_check_compile_args(sourcename, binname) | |
| largs.extend(self._get_target_arch_args()) | |
| return args, largs | |
| def needs_static_linker(self) -> bool: | |
| return True | |
| def get_depfile_suffix(self) -> str: | |
| return 'deps' | |
| def get_pic_args(self) -> T.List[str]: | |
| if self.info.is_windows(): | |
| return [] | |
| return ['-fPIC'] | |
| def get_feature_args(self, kwargs: DFeatures, build_to_src: str) -> T.List[str]: | |
| res: T.List[str] = [] | |
| unittest_arg = d_feature_args[self.id]['unittest'] | |
| if not unittest_arg: | |
| raise EnvironmentException('D compiler %s does not support the "unittest" feature.' % self.name_string()) | |
| if kwargs['unittest']: | |
| res.append(unittest_arg) | |
| debug_level = -1 | |
| debug_arg = d_feature_args[self.id]['debug'] | |
| if not debug_arg: | |
| raise EnvironmentException('D compiler %s does not support conditional debug identifiers.' % self.name_string()) | |
| # Parse all debug identifiers and the largest debug level identifier | |
| for d in kwargs['debug']: | |
| if isinstance(d, int): | |
| debug_level = max(debug_level, d) | |
| elif isinstance(d, str) and d.isdigit(): | |
| debug_level = max(debug_level, int(d)) | |
| else: | |
| res.append(f'{debug_arg}={d}') | |
| if debug_level >= 0: | |
| res.append(f'{debug_arg}={debug_level}') | |
| version_level = -1 | |
| version_arg = d_feature_args[self.id]['version'] | |
| if not version_arg: | |
| raise EnvironmentException('D compiler %s does not support conditional version identifiers.' % self.name_string()) | |
| # Parse all version identifiers and the largest version level identifier | |
| for v in kwargs['versions']: | |
| if isinstance(v, int): | |
| version_level = max(version_level, v) | |
| elif isinstance(v, str) and v.isdigit(): | |
| version_level = max(version_level, int(v)) | |
| else: | |
| res.append(f'{version_arg}={v}') | |
| if version_level >= 0: | |
| res.append(f'{version_arg}={version_level}') | |
| import_dir_arg = d_feature_args[self.id]['import_dir'] | |
| if not import_dir_arg: | |
| raise EnvironmentException('D compiler %s does not support the "string import directories" feature.' % self.name_string()) | |
| for idir_obj in kwargs['import_dirs']: | |
| res.extend(f'{import_dir_arg}{i}' for i in idir_obj.rel_string_list(build_to_src)) | |
| return res | |
| def get_optimization_link_args(self, optimization_level: str) -> T.List[str]: | |
| if optimization_level != 'plain': | |
| return self._get_target_arch_args() | |
| return [] | |
| def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> DCompilerArgs: | |
| return DCompilerArgs(self, args) | |
| def has_multi_arguments(self, args: T.List[str]) -> T.Tuple[bool, bool]: | |
| return self.compiles('int i;\n', extra_args=args) | |
| def _get_target_arch_args(self) -> T.List[str]: | |
| # LDC2 on Windows targets to current OS architecture, but | |
| # it should follow the target specified by the MSVC toolchain. | |
| if self.info.is_windows(): | |
| if self.is_cross: | |
| return [f'-mtriple={self.arch}-windows-msvc'] | |
| elif self.arch == 'x86_64': | |
| return ['-m64'] | |
| return ['-m32'] | |
| return [] | |
| def get_crt_compile_args(self, crt_val: str) -> T.List[str]: | |
| return [] | |
| def get_crt_link_args(self, crt_val: str) -> T.List[str]: | |
| return [] | |
| def _get_compile_extra_args(self, extra_args: T.Union[T.List[str], T.Callable[[CompileCheckMode], T.List[str]], None] = None) -> T.List[str]: | |
| args = self._get_target_arch_args() | |
| if extra_args: | |
| if callable(extra_args): | |
| extra_args = extra_args(CompileCheckMode.COMPILE) | |
| if isinstance(extra_args, list): | |
| args.extend(extra_args) | |
| elif isinstance(extra_args, str): | |
| args.append(extra_args) | |
| return args | |
| def run(self, code: 'mesonlib.FileOrString', | |
| extra_args: T.Union[T.List[str], T.Callable[[CompileCheckMode], T.List[str]], None] = None, | |
| dependencies: T.Optional[T.List['Dependency']] = None, | |
| run_env: T.Optional[T.Dict[str, str]] = None, | |
| run_cwd: T.Optional[str] = None) -> compilers.RunResult: | |
| extra_args = self._get_compile_extra_args(extra_args) | |
| return super().run(code, extra_args, dependencies, run_env, run_cwd) | |
| def sizeof(self, typename: str, prefix: str, *, | |
| extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None, | |
| dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[int, bool]: | |
| if extra_args is None: | |
| extra_args = [] | |
| t = f''' | |
| import std.stdio : writeln; | |
| {prefix} | |
| void main() {{ | |
| writeln(({typename}).sizeof); | |
| }} | |
| ''' | |
| res = self.cached_run(t, extra_args=extra_args, | |
| dependencies=dependencies) | |
| if not res.compiled: | |
| return -1, False | |
| if res.returncode != 0: | |
| raise mesonlib.EnvironmentException('Could not run sizeof test binary.') | |
| return int(res.stdout), res.cached | |
| def alignment(self, typename: str, prefix: str, *, | |
| extra_args: T.Optional[T.List[str]] = None, | |
| dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[int, bool]: | |
| if extra_args is None: | |
| extra_args = [] | |
| t = f''' | |
| import std.stdio : writeln; | |
| {prefix} | |
| void main() {{ | |
| writeln(({typename}).alignof); | |
| }} | |
| ''' | |
| res = self.run(t, extra_args=extra_args, dependencies=dependencies) | |
| if not res.compiled: | |
| raise mesonlib.EnvironmentException('Could not compile alignment test.') | |
| if res.returncode != 0: | |
| raise mesonlib.EnvironmentException('Could not run alignment test binary.') | |
| align = int(res.stdout) | |
| if align == 0: | |
| raise mesonlib.EnvironmentException(f'Could not determine alignment of {typename}. Sorry. You might want to file a bug.') | |
| return align, res.cached | |
| def has_header(self, hname: str, prefix: str, *, | |
| extra_args: T.Union[None, T.List[str], T.Callable[['CompileCheckMode'], T.List[str]]] = None, | |
| dependencies: T.Optional[T.List['Dependency']] = None, | |
| disable_cache: bool = False) -> T.Tuple[bool, bool]: | |
| extra_args = self._get_compile_extra_args(extra_args) | |
| code = f'''{prefix} | |
| import {hname}; | |
| ''' | |
| return self.compiles(code, extra_args=extra_args, | |
| dependencies=dependencies, mode=CompileCheckMode.COMPILE, disable_cache=disable_cache) | |
| class GnuDCompiler(GnuCompiler, DCompiler): | |
| # we mostly want DCompiler, but that gives us the Compiler.LINKER_OPTION_STYLE instead | |
| LINKER_OPTION_STYLE = GnuCompiler.LINKER_OPTION_STYLE | |
| id = 'gcc' | |
| def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, | |
| env: Environment, arch: str, *, | |
| linker: T.Optional['DynamicLinker'] = None, | |
| full_version: T.Optional[str] = None): | |
| DCompiler.__init__(self, exelist, version, for_machine, env, arch, | |
| linker=linker, full_version=full_version) | |
| GnuCompiler.__init__(self, {}) | |
| default_warn_args = ['-Wall', '-Wdeprecated'] | |
| self.warn_args = {'0': [], | |
| '1': default_warn_args, | |
| '2': default_warn_args + ['-Wextra'], | |
| '3': default_warn_args + ['-Wextra', '-Wpedantic'], | |
| 'everything': (default_warn_args + ['-Wextra', '-Wpedantic'] + | |
| self.supported_warn_args(gnu_common_warning_args))} | |
| self.base_options = { | |
| OptionKey(o) for o in [ | |
| 'b_colorout', 'b_sanitize', 'b_staticpic', 'b_vscrt', | |
| 'b_coverage', 'b_pgo', 'b_ndebug']} | |
| self._has_color_support = version_compare(self.version, '>=4.9') | |
| # dependencies were implemented before, but broken - support was fixed in GCC 7.1+ | |
| # (and some backported versions) | |
| self._has_deps_support = version_compare(self.version, '>=7.1') | |
| def get_colorout_args(self, colortype: str) -> T.List[str]: | |
| if self._has_color_support: | |
| super().get_colorout_args(colortype) | |
| return [] | |
| def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]: | |
| if self._has_deps_support: | |
| return super().get_dependency_gen_args(outtarget, outfile) | |
| return [] | |
| def get_warn_args(self, level: str) -> T.List[str]: | |
| return self.warn_args[level] | |
| def get_optimization_args(self, optimization_level: str) -> T.List[str]: | |
| return gdc_optimization_args[optimization_level] | |
| def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], | |
| build_dir: str) -> T.List[str]: | |
| for idx, i in enumerate(parameter_list): | |
| if i[:2] == '-I' or i[:2] == '-L': | |
| parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) | |
| return parameter_list | |
| def get_allow_undefined_link_args(self) -> T.List[str]: | |
| return self.linker.get_allow_undefined_args() | |
| def get_linker_always_args(self) -> T.List[str]: | |
| args = super().get_linker_always_args() | |
| if self.info.is_windows(): | |
| return args | |
| return args + ['-shared-libphobos'] | |
| def get_assert_args(self, disable: bool) -> T.List[str]: | |
| if disable: | |
| return ['-frelease'] | |
| return [] | |
| # LDC uses the DMD frontend code to parse and analyse the code. | |
| # It then uses LLVM for the binary code generation and optimizations. | |
| # This function retrieves the dmd frontend version, which determines | |
| # the common features between LDC and DMD. | |
| # We need the complete version text because the match is not on first line | |
| # of version_output | |
| def find_ldc_dmd_frontend_version(version_output: T.Optional[str]) -> T.Optional[str]: | |
| if version_output is None: | |
| return None | |
| version_regex = re.search(r'DMD v(\d+\.\d+\.\d+)', version_output) | |
| if version_regex: | |
| return version_regex.group(1) | |
| return None | |
| class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler): | |
| id = 'llvm' | |
| def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, | |
| env: Environment, arch: str, *, | |
| linker: T.Optional['DynamicLinker'] = None, | |
| full_version: T.Optional[str] = None, | |
| version_output: T.Optional[str] = None): | |
| DCompiler.__init__(self, exelist, version, for_machine, env, arch, | |
| linker=linker, full_version=full_version) | |
| DmdLikeCompilerMixin.__init__(self, dmd_frontend_version=find_ldc_dmd_frontend_version(version_output)) | |
| self.base_options = {OptionKey(o) for o in ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']} | |
| def get_colorout_args(self, colortype: str) -> T.List[str]: | |
| if colortype == 'always': | |
| return ['-enable-color'] | |
| return [] | |
| def get_warn_args(self, level: str) -> T.List[str]: | |
| if level in {'2', '3'}: | |
| return ['-wi', '-dw'] | |
| elif level == '1': | |
| return ['-wi'] | |
| return [] | |
| def get_pic_args(self) -> T.List[str]: | |
| return ['-relocation-model=pic'] | |
| def get_crt_link_args(self, crt_val: str) -> T.List[str]: | |
| return self._get_crt_args(crt_val) | |
| def unix_args_to_native(self, args: T.List[str]) -> T.List[str]: | |
| return self._unix_args_to_native(args, self.info, self.linker.id) | |
| def get_optimization_args(self, optimization_level: str) -> T.List[str]: | |
| if optimization_level != 'plain': | |
| return self._get_target_arch_args() + ldc_optimization_args[optimization_level] | |
| return ldc_optimization_args[optimization_level] | |
| @classmethod | |
| def use_linker_args(cls, linker: str, version: str) -> T.List[str]: | |
| return [f'-linker={linker}'] | |
| def get_linker_always_args(self) -> T.List[str]: | |
| args = super().get_linker_always_args() | |
| if self.info.is_windows(): | |
| return args | |
| return args + ['-link-defaultlib-shared'] | |
| def get_assert_args(self, disable: bool) -> T.List[str]: | |
| if disable: | |
| return ['--release'] | |
| return [] | |
| def rsp_file_syntax(self) -> RSPFileSyntax: | |
| # We use `mesonlib.is_windows` here because we want to know what the | |
| # build machine is, not the host machine. This really means we would | |
| # have the Environment not the MachineInfo in the compiler. | |
| return RSPFileSyntax.MSVC if is_windows() else RSPFileSyntax.GCC | |
| class DmdDCompiler(DmdLikeCompilerMixin, DCompiler): | |
| id = 'dmd' | |
| def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, | |
| env: Environment, arch: str, *, | |
| linker: T.Optional['DynamicLinker'] = None, | |
| full_version: T.Optional[str] = None): | |
| DCompiler.__init__(self, exelist, version, for_machine, env, arch, | |
| linker=linker, full_version=full_version) | |
| DmdLikeCompilerMixin.__init__(self, version) | |
| self.base_options = {OptionKey(o) for o in ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']} | |
| def get_colorout_args(self, colortype: str) -> T.List[str]: | |
| if colortype == 'always': | |
| return ['-color=on'] | |
| return [] | |
| def get_std_exe_link_args(self) -> T.List[str]: | |
| if self.info.is_windows(): | |
| # DMD links against D runtime only when main symbol is found, | |
| # so these needs to be inserted when linking static D libraries. | |
| if self.arch == 'x86_64': | |
| return ['phobos64.lib'] | |
| elif self.arch == 'x86_mscoff': | |
| return ['phobos32mscoff.lib'] | |
| return ['phobos.lib'] | |
| return [] | |
| def get_std_shared_lib_link_args(self) -> T.List[str]: | |
| libname = 'libphobos2.so' | |
| if self.info.is_windows(): | |
| if self.arch == 'x86_64': | |
| libname = 'phobos64.lib' | |
| elif self.arch == 'x86_mscoff': | |
| libname = 'phobos32mscoff.lib' | |
| else: | |
| libname = 'phobos.lib' | |
| return ['-shared', '-defaultlib=' + libname] | |
| def _get_target_arch_args(self) -> T.List[str]: | |
| # DMD32 and DMD64 on 64-bit Windows defaults to 32-bit (OMF). | |
| # Force the target to 64-bit in order to stay consistent | |
| # across the different platforms. | |
| if self.info.is_windows(): | |
| if self.arch == 'x86_64': | |
| return ['-m64'] | |
| elif self.arch == 'x86_mscoff': | |
| return ['-m32mscoff'] | |
| return ['-m32'] | |
| return [] | |
| def get_crt_compile_args(self, crt_val: str) -> T.List[str]: | |
| return self._get_crt_args(crt_val) | |
| def unix_args_to_native(self, args: T.List[str]) -> T.List[str]: | |
| return self._unix_args_to_native(args, self.info, self.linker.id) | |
| def get_optimization_args(self, optimization_level: str) -> T.List[str]: | |
| if optimization_level != 'plain': | |
| return self._get_target_arch_args() + dmd_optimization_args[optimization_level] | |
| return dmd_optimization_args[optimization_level] | |
| def can_linker_accept_rsp(self) -> bool: | |
| return False | |
| def get_linker_always_args(self) -> T.List[str]: | |
| args = super().get_linker_always_args() | |
| if self.info.is_windows(): | |
| return args | |
| return args + ['-defaultlib=phobos2', '-debuglib=phobos2'] | |
| def get_assert_args(self, disable: bool) -> T.List[str]: | |
| if disable: | |
| return ['-release'] | |
| return [] | |
| def rsp_file_syntax(self) -> RSPFileSyntax: | |
| return RSPFileSyntax.MSVC |