From 6a9761a2c0c099d20885f748a373b214c2cdb8d4 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 28 Sep 2020 14:50:51 -0700 Subject: [PATCH] compilers/d: add type annotations --- mesonbuild/compilers/compilers.py | 5 + mesonbuild/compilers/d.py | 266 ++++++++++++++++-------------- run_mypy.py | 1 + 3 files changed, 144 insertions(+), 128 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index b1bb2695f..67552ee36 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1192,6 +1192,11 @@ class Compiler(metaclass=abc.ABCMeta): return self.compiles(code, env, extra_args=extra_args, dependencies=dependencies, mode='link', disable_cache=disable_cache) + def get_feature_args(self, kwargs: T.Dict[str, T.Any], build_to_src: str) -> T.List[str]: + """Used by D for extra language features.""" + # TODO: using a TypeDict here would improve this + raise EnvironmentError('{} does not implement get_feature_args'.format(self.id)) + def get_args_from_envvars(lang: str, for_machine: MachineChoice, diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 5bcda5864..630291af8 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -30,8 +30,13 @@ from .compilers import ( from .mixins.gnu import GnuCompiler if T.TYPE_CHECKING: - from ..dependencies import ExternalProgram + from .compilers import Compiler as CompilerMixinBase + from ..dependencies import Dependency, ExternalProgram from ..envconfig import MachineInfo + from ..environment import Environment + from ..linkers import DynamicLinker +else: + CompilerMixinBase = object d_feature_args = {'gcc': {'unittest': '-funittest', 'debug': '-fdebug', @@ -48,7 +53,7 @@ d_feature_args = {'gcc': {'unittest': '-funittest', 'version': '-version', 'import_dir': '-J' } - } + } # type: T.Dict[str, T.Dict[str, str]] ldc_optimization_args = {'0': [], 'g': [], @@ -56,7 +61,7 @@ ldc_optimization_args = {'0': [], '2': ['-O2'], '3': ['-O3'], 's': ['-Os'], - } + } # type: T.Dict[str, T.List[str]] dmd_optimization_args = {'0': [], 'g': [], @@ -64,23 +69,35 @@ dmd_optimization_args = {'0': [], '2': ['-O'], '3': ['-O'], 's': ['-O'], - } + } # type: T.Dict[str, T.List[str]] -class DmdLikeCompilerMixin: +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. + """ + + if T.TYPE_CHECKING: + mscrt_args = {} # type: T.Dict[str, T.List[str]] + + def _get_target_arch_args(self) -> T.List[str]: ... LINKER_PREFIX = '-L=' - def get_output_args(self, target): - return ['-of=' + target] + def get_output_args(self, outputname: str) -> T.List[str]: + return ['-of=' + outputname] - def get_linker_output_args(self, target): - return ['-of=' + target] + def get_linker_output_args(self, outputname: str) -> T.List[str]: + return ['-of=' + outputname] - def get_include_args(self, path, is_system): + def get_include_args(self, path: str, is_system: bool) -> T.List[str]: return ['-I=' + path] - def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + 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:])) @@ -93,33 +110,34 @@ class DmdLikeCompilerMixin: return parameter_list - def get_warn_args(self, level): + def get_warn_args(self, level: str) -> T.List[str]: return ['-wi'] - def get_werror_args(self): + def get_werror_args(self) -> T.List[str]: return ['-w'] - def get_coverage_args(self): + def get_coverage_args(self) -> T.List[str]: return ['-cov'] - def get_coverage_link_args(self): + def get_coverage_link_args(self) -> T.List[str]: return [] - def get_preprocess_only_args(self): + def get_preprocess_only_args(self) -> T.List[str]: return ['-E'] - def get_compile_only_args(self): + def get_compile_only_args(self) -> T.List[str]: return ['-c'] - def get_depfile_suffix(self): + def get_depfile_suffix(self) -> str: return 'deps' - def get_pic_args(self): + def get_pic_args(self) -> T.List[str]: if self.info.is_windows(): return [] return ['-fPIC'] - def get_feature_args(self, kwargs, build_to_src): + def get_feature_args(self, kwargs: T.Dict[str, T.Any], build_to_src: str) -> T.List[str]: + # TODO: using a TypeDict here would improve this res = [] if 'unittest' in kwargs: unittest = kwargs.pop('unittest') @@ -201,15 +219,17 @@ class DmdLikeCompilerMixin: return res - def get_buildtype_linker_args(self, buildtype): + def get_buildtype_linker_args(self, buildtype: str) -> T.List[str]: if buildtype != 'plain': - return self.get_target_arch_args() + return self._get_target_arch_args() return [] - def gen_import_library_args(self, implibname): + def gen_import_library_args(self, implibname: str) -> T.List[str]: return self.linker.import_library_args(implibname) - def build_rpath_args(self, env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): + def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, + rpath_paths: str, build_rpath: str, + install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]: if self.info.is_windows(): return ([], set()) @@ -233,7 +253,7 @@ class DmdLikeCompilerMixin: return super().build_rpath_args( env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath) - def translate_args_to_nongnu(self, args): + def _translate_args_to_nongnu(self, args: T.List[str]) -> T.List[str]: dcargs = [] # Translate common arguments to flags the LDC/DMD compilers # can understand. @@ -241,11 +261,11 @@ class DmdLikeCompilerMixin: # and are therefore out of the user's control. for arg in args: # Translate OS specific arguments first. - osargs = [] + osargs = [] # type: T.List[str] if self.info.is_windows(): osargs = self.translate_arg_to_windows(arg) elif self.info.is_darwin(): - osargs = self.translate_arg_to_osx(arg) + osargs = self._translate_arg_to_osx(arg) if osargs: dcargs.extend(osargs) continue @@ -324,7 +344,7 @@ class DmdLikeCompilerMixin: return dcargs @classmethod - def translate_arg_to_windows(cls, arg): + def translate_arg_to_windows(cls, arg: str) -> T.List[str]: args = [] if arg.startswith('-Wl,'): # Translate linker arguments here. @@ -350,20 +370,20 @@ class DmdLikeCompilerMixin: return args @classmethod - def translate_arg_to_osx(cls, arg): + def _translate_arg_to_osx(cls, arg: str) -> T.List[str]: args = [] if arg.startswith('-install_name'): args.append('-L=' + arg) return args - def get_debug_args(self, is_debug): + 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, buildtype): + def _get_crt_args(self, crt_val: str, buildtype: str) -> T.List[str]: if not self.info.is_windows(): return [] @@ -386,26 +406,32 @@ class DmdLikeCompilerMixin: assert(buildtype == 'custom') raise EnvironmentException('Requested C runtime based on buildtype, but buildtype is "custom".') - def get_soname_args(self, *args, **kwargs) -> T.List[str]: + def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str, + suffix: str, soversion: str, + darwin_versions: T.Tuple[str, str], + is_shared_module: bool) -> T.List[str]: + sargs = super().get_soname_args(env, prefix, shlib_name, suffix, + soversion, darwin_versions, is_shared_module) + # LDC and DMD actually do use a linker, but they proxy all of that with # their own arguments if self.linker.id.startswith('ld.'): soargs = [] - for arg in super().get_soname_args(*args, **kwargs): + for arg in sargs: a, b = arg.split(',', maxsplit=1) soargs.append(a) soargs.append(self.LINKER_PREFIX + b) return soargs elif self.linker.id.startswith('ld64'): soargs = [] - for arg in super().get_soname_args(*args, **kwargs): + for arg in sargs: if not arg.startswith(self.LINKER_PREFIX): soargs.append(self.LINKER_PREFIX + arg) else: soargs.append(arg) return soargs else: - return super().get_soname_args(*args, **kwargs) + return sargs def get_allow_undefined_link_args(self) -> T.List[str]: args = self.linker.get_allow_undefined_args() @@ -418,9 +444,11 @@ class DmdLikeCompilerMixin: args = [a.replace('-L=', '-Xcc=-Wl,') for a in args] return args + class DCompilerArgs(CompilerArgs): prepend_prefixes = ('-I', '-L') - dedup2_prefixes = ('-I') + dedup2_prefixes = ('-I', ) + class DCompiler(Compiler): mscrt_args = { @@ -434,19 +462,22 @@ class DCompiler(Compiler): language = 'd' def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, - info: 'MachineInfo', arch: str, exe_wrapper: T.Optional['ExternalProgram'] = None, - **kwargs): - super().__init__(exelist, version, for_machine, info, **kwargs) - self.id = 'unknown' + info: 'MachineInfo', arch: str, *, + exe_wrapper: T.Optional['ExternalProgram'] = None, + linker: T.Optional['DynamicLinker'] = None, + full_version: T.Optional[str] = None, + is_cross: bool = False): + super().__init__(exelist, version, for_machine, info, linker=linker, + full_version=full_version, is_cross=is_cross) self.arch = arch self.exe_wrapper = exe_wrapper - def sanity_check(self, work_dir, environment): + def sanity_check(self, work_dir: str, environment: 'Environment') -> None: source_name = os.path.join(work_dir, 'sanity.d') output_name = os.path.join(work_dir, 'dtest') with open(source_name, 'w') as ofile: ofile.write('''void main() { }''') - pc = subprocess.Popen(self.exelist + self.get_output_args(output_name) + self.get_target_arch_args() + [source_name], cwd=work_dir) + pc = subprocess.Popen(self.exelist + self.get_output_args(output_name) + self._get_target_arch_args() + [source_name], cwd=work_dir) pc.wait() if pc.returncode != 0: raise EnvironmentException('D compiler %s can not compile programs.' % self.name_string()) @@ -460,18 +491,19 @@ class DCompiler(Compiler): if subprocess.call(cmdlist) != 0: raise EnvironmentException('Executables created by D compiler %s are not runnable.' % self.name_string()) - def needs_static_linker(self): + def needs_static_linker(self) -> bool: return True - def get_depfile_suffix(self): + def get_depfile_suffix(self) -> str: return 'deps' - def get_pic_args(self): + def get_pic_args(self) -> T.List[str]: if self.info.is_windows(): return [] return ['-fPIC'] - def get_feature_args(self, kwargs, build_to_src): + def get_feature_args(self, kwargs: T.Dict[str, T.Any], build_to_src: str) -> T.List[str]: + # TODO: using a TypeDict here would improve this res = [] if 'unittest' in kwargs: unittest = kwargs.pop('unittest') @@ -553,54 +585,18 @@ class DCompiler(Compiler): return res - def get_buildtype_linker_args(self, buildtype): + def get_buildtype_linker_args(self, buildtype: str) -> T.List[str]: if buildtype != 'plain': - return self.get_target_arch_args() + return self._get_target_arch_args() return [] - def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'): - if callable(extra_args): - extra_args = extra_args(mode) - if extra_args is None: - extra_args = [] - elif isinstance(extra_args, str): - extra_args = [extra_args] - if dependencies is None: - dependencies = [] - elif not isinstance(dependencies, list): - dependencies = [dependencies] - # Collect compiler arguments - args = self.compiler_args() - for d in dependencies: - # Add compile flags needed by dependencies - args += d.get_compile_args() - if mode == 'link': - # Add link flags needed to find dependencies - args += d.get_link_args() - - if mode == 'compile': - # Add DFLAGS from the env - args += env.coredata.get_external_args(self.for_machine, self.language) - elif mode == 'link': - # Add LDFLAGS from the env - args += env.coredata.get_external_link_args(self.for_machine, self.language) - # extra_args must override all other arguments, so we add them last - args += extra_args - return args - def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> DCompilerArgs: return DCompilerArgs(self, args) - def compiles(self, code, env, *, extra_args=None, dependencies=None, mode='compile'): - args = self._get_compiler_check_args(env, extra_args, dependencies, mode) - - with self.cached_compile(code, env.coredata, extra_args=args, mode=mode) as p: - return p.returncode == 0, p.cached - - def has_multi_arguments(self, args, env): + def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]: return self.compiles('int i;\n', env, extra_args=args) - def get_target_arch_args(self): + 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(): @@ -609,15 +605,12 @@ class DCompiler(Compiler): return ['-m32'] return [] - def get_crt_compile_args(self, crt_val, buildtype): + def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]: return [] - def get_crt_link_args(self, crt_val, buildtype): + def get_crt_link_args(self, crt_val: str, buildtype: str) -> T.List[str]: return [] - def thread_link_flags(self, env): - return self.linker.thread_flags(env) - class GnuDCompiler(GnuCompiler, DCompiler): @@ -625,9 +618,14 @@ class GnuDCompiler(GnuCompiler, DCompiler): LINKER_PREFIX = GnuCompiler.LINKER_PREFIX def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, - info: 'MachineInfo', arch: str, *, exe_wrapper: T.Optional['ExternalProgram'] = None, - **kwargs): - DCompiler.__init__(self, exelist, version, for_machine, info, arch, exe_wrapper=exe_wrapper, **kwargs) + info: 'MachineInfo', arch: str, *, + exe_wrapper: T.Optional['ExternalProgram'] = None, + linker: T.Optional['DynamicLinker'] = None, + full_version: T.Optional[str] = None, + is_cross: bool = False): + DCompiler.__init__(self, exelist, version, for_machine, info, arch, + exe_wrapper=exe_wrapper, linker=linker, + full_version=full_version, is_cross=is_cross) GnuCompiler.__init__(self, {}) self.id = 'gcc' default_warn_args = ['-Wall', '-Wdeprecated'] @@ -643,23 +641,24 @@ class GnuDCompiler(GnuCompiler, DCompiler): # (and some backported versions) self._has_deps_support = version_compare(self.version, '>=7.1') - def get_colorout_args(self, colortype): + 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, outfile): + 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): + def get_warn_args(self, level: str) -> T.List[str]: return self.warn_args[level] - def get_buildtype_args(self, buildtype): + def get_buildtype_args(self, buildtype: str) -> T.List[str]: return d_gdc_buildtype_args[buildtype] - def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + 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:])) @@ -675,46 +674,51 @@ class GnuDCompiler(GnuCompiler, DCompiler): return args return args + ['-shared-libphobos'] - def get_disable_assert_args(self): + def get_disable_assert_args(self) -> T.List[str]: return ['-frelease'] class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler): def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, - info: 'MachineInfo', arch: str, **kwargs): - DCompiler.__init__(self, exelist, version, for_machine, info, arch, **kwargs) + info: 'MachineInfo', arch: str, *, + exe_wrapper: T.Optional['ExternalProgram'] = None, + linker: T.Optional['DynamicLinker'] = None, + full_version: T.Optional[str] = None, + is_cross: bool = False): + DCompiler.__init__(self, exelist, version, for_machine, info, arch, + exe_wrapper=exe_wrapper, linker=linker, + full_version=full_version, is_cross=is_cross) self.id = 'llvm' self.base_options = ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug'] - def get_colorout_args(self, colortype): + def get_colorout_args(self, colortype: str) -> T.List[str]: if colortype == 'always': return ['-enable-color'] return [] - def get_warn_args(self, level): - if level == '2' or level == '3': + def get_warn_args(self, level: str) -> T.List[str]: + if level in {'2', '3'}: return ['-wi', '-dw'] elif level == '1': return ['-wi'] - else: - return [] + return [] - def get_buildtype_args(self, buildtype): + def get_buildtype_args(self, buildtype: str) -> T.List[str]: if buildtype != 'plain': - return self.get_target_arch_args() + d_ldc_buildtype_args[buildtype] + return self._get_target_arch_args() + d_ldc_buildtype_args[buildtype] return d_ldc_buildtype_args[buildtype] - def get_pic_args(self): + def get_pic_args(self) -> T.List[str]: return ['-relocation-model=pic'] - def get_crt_link_args(self, crt_val, buildtype): - return self.get_crt_args(crt_val, buildtype) + def get_crt_link_args(self, crt_val: str, buildtype: str) -> T.List[str]: + return self._get_crt_args(crt_val, buildtype) - def unix_args_to_native(self, args): - return self.translate_args_to_nongnu(args) + def unix_args_to_native(self, args: T.List[str]) -> T.List[str]: + return self._translate_args_to_nongnu(args) - def get_optimization_args(self, optimization_level): + def get_optimization_args(self, optimization_level: str) -> T.List[str]: return ldc_optimization_args[optimization_level] @classmethod @@ -734,22 +738,28 @@ class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler): class DmdDCompiler(DmdLikeCompilerMixin, DCompiler): def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, - info: 'MachineInfo', arch: str, **kwargs): - DCompiler.__init__(self, exelist, version, for_machine, info, arch, **kwargs) + info: 'MachineInfo', arch: str, *, + exe_wrapper: T.Optional['ExternalProgram'] = None, + linker: T.Optional['DynamicLinker'] = None, + full_version: T.Optional[str] = None, + is_cross: bool = False): + DCompiler.__init__(self, exelist, version, for_machine, info, arch, + exe_wrapper=exe_wrapper, linker=linker, + full_version=full_version, is_cross=is_cross) self.id = 'dmd' self.base_options = ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug'] - def get_colorout_args(self, colortype): + def get_colorout_args(self, colortype: str) -> T.List[str]: if colortype == 'always': return ['-color=on'] return [] - def get_buildtype_args(self, buildtype): + def get_buildtype_args(self, buildtype: str) -> T.List[str]: if buildtype != 'plain': - return self.get_target_arch_args() + d_dmd_buildtype_args[buildtype] + return self._get_target_arch_args() + d_dmd_buildtype_args[buildtype] return d_dmd_buildtype_args[buildtype] - def get_std_exe_link_args(self): + 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. @@ -760,7 +770,7 @@ class DmdDCompiler(DmdLikeCompilerMixin, DCompiler): return ['phobos.lib'] return [] - def get_std_shared_lib_link_args(self): + def get_std_shared_lib_link_args(self) -> T.List[str]: libname = 'libphobos2.so' if self.info.is_windows(): if self.arch == 'x86_64': @@ -771,7 +781,7 @@ class DmdDCompiler(DmdLikeCompilerMixin, DCompiler): libname = 'phobos.lib' return ['-shared', '-defaultlib=' + libname] - def get_target_arch_args(self): + 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. @@ -783,13 +793,13 @@ class DmdDCompiler(DmdLikeCompilerMixin, DCompiler): return ['-m32'] return [] - def get_crt_compile_args(self, crt_val, buildtype): - return self.get_crt_args(crt_val, buildtype) + def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]: + return self._get_crt_args(crt_val, buildtype) - def unix_args_to_native(self, args): - return self.translate_args_to_nongnu(args) + def unix_args_to_native(self, args: T.List[str]) -> T.List[str]: + return self._translate_args_to_nongnu(args) - def get_optimization_args(self, optimization_level): + def get_optimization_args(self, optimization_level: str) -> T.List[str]: return dmd_optimization_args[optimization_level] def can_linker_accept_rsp(self) -> bool: diff --git a/run_mypy.py b/run_mypy.py index dc7002732..114ec4739 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -21,6 +21,7 @@ modules = [ 'mesonbuild/compilers/c_function_attributes.py', 'mesonbuild/compilers/cpp.py', 'mesonbuild/compilers/cuda.py', + 'mesonbuild/compilers/d.py', 'mesonbuild/compilers/fortran.py', 'mesonbuild/compilers/java.py', 'mesonbuild/compilers/objc.py',