From 79d1ecd5bc550395ca62ebecdcf37a4c40d0e1cc Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 21 Aug 2020 15:04:37 -0700 Subject: compilers/compilers: Fully type annotate --- mesonbuild/compilers/compilers.py | 417 +++++++++++++++++++++++--------------- mesonbuild/compilers/cuda.py | 4 +- mesonbuild/compilers/rust.py | 3 + mesonbuild/coredata.py | 5 +- mesonbuild/mesonlib.py | 2 + run_mypy.py | 1 + run_unittests.py | 2 +- 7 files changed, 266 insertions(+), 168 deletions(-) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index dc6dd5c..5b2e15d 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -24,28 +24,32 @@ from .. import mesonlib from ..linkers import LinkerEnvVarsMixin from ..mesonlib import ( EnvironmentException, MachineChoice, MesonException, - Popen_safe, split_args + Popen_safe, split_args, LibType ) from ..envconfig import ( Properties, get_env_var ) + from ..arglist import CompilerArgs if T.TYPE_CHECKING: + from ..build import BuildTarget from ..coredata import OptionDictType from ..envconfig import MachineInfo from ..environment import Environment from ..linkers import DynamicLinker # noqa: F401 + from ..dependencies import Dependency CompilerType = T.TypeVar('CompilerType', bound=Compiler) + _T = T.TypeVar('_T') """This file contains the data files of all compilers Meson knows about. To support a new compiler, add its information below. Also add corresponding autodetection code in environment.py.""" -header_suffixes = ('h', 'hh', 'hpp', 'hxx', 'H', 'ipp', 'moc', 'vapi', 'di') -obj_suffixes = ('o', 'obj', 'res') -lib_suffixes = ('a', 'lib', 'dll', 'dll.a', 'dylib', 'so') +header_suffixes = ('h', 'hh', 'hpp', 'hxx', 'H', 'ipp', 'moc', 'vapi', 'di') # type: T.Tuple[str, ...] +obj_suffixes = ('o', 'obj', 'res') # type: T.Tuple[str, ...] +lib_suffixes = ('a', 'lib', 'dll', 'dll.a', 'dylib', 'so') # type: T.Tuple[str, ...] # Mapping of language to suffixes of files that should always be in that language # This means we can't include .h headers here since they could be C, C++, ObjC, etc. lang_suffixes = { @@ -63,26 +67,26 @@ lang_suffixes = { 'cs': ('cs',), 'swift': ('swift',), 'java': ('java',), -} +} # type: T.Dict[str, T.Tuple[str, ...]] all_languages = lang_suffixes.keys() -cpp_suffixes = lang_suffixes['cpp'] + ('h',) -c_suffixes = lang_suffixes['c'] + ('h',) +cpp_suffixes = lang_suffixes['cpp'] + ('h',) # type: T.Tuple[str, ...] +c_suffixes = lang_suffixes['c'] + ('h',) # type: T.Tuple[str, ...] # List of languages that by default consume and output libraries following the # C ABI; these can generally be used interchangebly -clib_langs = ('objcpp', 'cpp', 'objc', 'c', 'fortran',) +clib_langs = ('objcpp', 'cpp', 'objc', 'c', 'fortran',) # type: T.Tuple[str, ...] # List of languages that can be linked with C code directly by the linker # used in build.py:process_compilers() and build.py:get_dynamic_linker() -clink_langs = ('d', 'cuda') + clib_langs -clink_suffixes = () +clink_langs = ('d', 'cuda') + clib_langs # type: T.Tuple[str, ...] +clink_suffixes = tuple() # type: T.Tuple[str, ...] for _l in clink_langs + ('vala',): clink_suffixes += lang_suffixes[_l] clink_suffixes += ('h', 'll', 's') -all_suffixes = set(itertools.chain(*lang_suffixes.values(), clink_suffixes)) +all_suffixes = set(itertools.chain(*lang_suffixes.values(), clink_suffixes)) # type: T.Set[str] # Languages that should use LDFLAGS arguments when linking. -languages_using_ldflags = {'objcpp', 'cpp', 'objc', 'c', 'fortran', 'd', 'cuda'} +languages_using_ldflags = {'objcpp', 'cpp', 'objc', 'c', 'fortran', 'd', 'cuda'} # type: T.Set[str] # Languages that should use CPPFLAGS arguments when linking. -languages_using_cppflags = {'c', 'cpp', 'objc', 'objcpp'} +languages_using_cppflags = {'c', 'cpp', 'objc', 'objcpp'} # type: T.Set[str] soregex = re.compile(r'.*\.so(\.[0-9]+)?(\.[0-9]+)?(\.[0-9]+)?$') # Environment variables that each lang uses. @@ -94,14 +98,14 @@ cflags_mapping = {'c': 'CFLAGS', 'fortran': 'FFLAGS', 'd': 'DFLAGS', 'vala': 'VALAFLAGS', - 'rust': 'RUSTFLAGS'} + 'rust': 'RUSTFLAGS'} # type: T.Dict[str, str] cexe_mapping = {'c': 'CC', 'cpp': 'CXX'} # All these are only for C-linkable languages; see `clink_langs` above. -def sort_clink(lang): +def sort_clink(lang: str) -> int: ''' Sorting function to sort the list of languages according to reversed(compilers.clink_langs) and append the unknown langs in the end. @@ -112,40 +116,40 @@ def sort_clink(lang): return 1 return -clink_langs.index(lang) -def is_header(fname): - if hasattr(fname, 'fname'): +def is_header(fname: 'mesonlib.FileOrString') -> bool: + if isinstance(fname, mesonlib.File): fname = fname.fname suffix = fname.split('.')[-1] return suffix in header_suffixes -def is_source(fname): - if hasattr(fname, 'fname'): +def is_source(fname: 'mesonlib.FileOrString') -> bool: + if isinstance(fname, mesonlib.File): fname = fname.fname suffix = fname.split('.')[-1].lower() return suffix in clink_suffixes -def is_assembly(fname): - if hasattr(fname, 'fname'): +def is_assembly(fname: 'mesonlib.FileOrString') -> bool: + if isinstance(fname, mesonlib.File): fname = fname.fname return fname.split('.')[-1].lower() == 's' -def is_llvm_ir(fname): - if hasattr(fname, 'fname'): +def is_llvm_ir(fname: 'mesonlib.FileOrString') -> bool: + if isinstance(fname, mesonlib.File): fname = fname.fname return fname.split('.')[-1] == 'll' @lru_cache(maxsize=None) -def cached_by_name(fname): +def cached_by_name(fname: 'mesonlib.FileOrString') -> bool: suffix = fname.split('.')[-1] return suffix in obj_suffixes -def is_object(fname): - if hasattr(fname, 'fname'): +def is_object(fname: 'mesonlib.FileOrString') -> bool: + if isinstance(fname, mesonlib.File): fname = fname.fname return cached_by_name(fname) -def is_library(fname): - if hasattr(fname, 'fname'): +def is_library(fname: 'mesonlib.FileOrString') -> bool: + if isinstance(fname, mesonlib.File): fname = fname.fname if soregex.match(fname): @@ -154,8 +158,8 @@ def is_library(fname): suffix = fname.split('.')[-1] return suffix in lib_suffixes -def is_known_suffix(fname): - if hasattr(fname, 'fname'): +def is_known_suffix(fname: 'mesonlib.FileOrString') -> bool: + if isinstance(fname, mesonlib.File): fname = fname.fname suffix = fname.split('.')[-1] @@ -166,14 +170,14 @@ cuda_buildtype_args = {'plain': [], 'debugoptimized': [], 'release': [], 'minsize': [], - } + } # type: T.Dict[str, T.List[str]] java_buildtype_args = {'plain': [], 'debug': ['-g'], 'debugoptimized': ['-g'], 'release': [], 'minsize': [], 'custom': [], - } + } # type: T.Dict[str, T.List[str]] rust_buildtype_args = {'plain': [], 'debug': [], @@ -181,7 +185,7 @@ rust_buildtype_args = {'plain': [], 'release': [], 'minsize': [], 'custom': [], - } + } # type: T.Dict[str, T.List[str]] d_gdc_buildtype_args = {'plain': [], 'debug': [], @@ -189,7 +193,7 @@ d_gdc_buildtype_args = {'plain': [], 'release': ['-finline-functions'], 'minsize': [], 'custom': [], - } + } # type: T.Dict[str, T.List[str]] d_ldc_buildtype_args = {'plain': [], 'debug': [], @@ -197,7 +201,7 @@ d_ldc_buildtype_args = {'plain': [], 'release': ['-enable-inlining', '-Hkeep-all-bodies'], 'minsize': [], 'custom': [], - } + } # type: T.Dict[str, T.List[str]] d_dmd_buildtype_args = {'plain': [], 'debug': [], @@ -205,7 +209,7 @@ d_dmd_buildtype_args = {'plain': [], 'release': ['-inline'], 'minsize': [], 'custom': [], - } + } # type: T.Dict[str, T.List[str]] mono_buildtype_args = {'plain': [], 'debug': [], @@ -213,7 +217,7 @@ mono_buildtype_args = {'plain': [], 'release': ['-optimize+'], 'minsize': [], 'custom': [], - } + } # type: T.Dict[str, T.List[str]] swift_buildtype_args = {'plain': [], 'debug': [], @@ -221,14 +225,14 @@ swift_buildtype_args = {'plain': [], 'release': [], 'minsize': [], 'custom': [], - } + } # type: T.Dict[str, T.List[str]] gnu_winlibs = ['-lkernel32', '-luser32', '-lgdi32', '-lwinspool', '-lshell32', - '-lole32', '-loleaut32', '-luuid', '-lcomdlg32', '-ladvapi32'] + '-lole32', '-loleaut32', '-luuid', '-lcomdlg32', '-ladvapi32'] # type: T.List[str] msvc_winlibs = ['kernel32.lib', 'user32.lib', 'gdi32.lib', 'winspool.lib', 'shell32.lib', 'ole32.lib', 'oleaut32.lib', - 'uuid.lib', 'comdlg32.lib', 'advapi32.lib'] + 'uuid.lib', 'comdlg32.lib', 'advapi32.lib'] # type: T.List[str] clike_optimization_args = {'0': [], 'g': [], @@ -236,7 +240,7 @@ clike_optimization_args = {'0': [], '2': ['-O2'], '3': ['-O3'], 's': ['-Os'], - } + } # type: T.Dict[str, T.List[str]] cuda_optimization_args = {'0': [], 'g': ['-O0'], @@ -244,13 +248,13 @@ cuda_optimization_args = {'0': [], '2': ['-O2'], '3': ['-O3'], 's': ['-O3'] - } + } # type: T.Dict[str, T.List[str]] cuda_debug_args = {False: [], - True: ['-g']} + True: ['-g']} # type: T.Dict[bool, T.List[str]] clike_debug_args = {False: [], - True: ['-g']} + True: ['-g']} # type: T.Dict[bool, T.List[str]] base_options = {'b_pch': coredata.UserBooleanOption('Use precompiled headers', True), 'b_lto': coredata.UserBooleanOption('Use link time optimization', False), @@ -278,18 +282,21 @@ base_options = {'b_pch': coredata.UserBooleanOption('Use precompiled headers', T 'b_vscrt': coredata.UserComboOption('VS run-time library type to use.', ['none', 'md', 'mdd', 'mt', 'mtd', 'from_buildtype'], 'from_buildtype'), - } + } # type: OptionDictType -def option_enabled(boptions, options, option): +def option_enabled(boptions: T.List[str], options: 'OptionDictType', + option: str) -> bool: try: if option not in boptions: return False - return options[option].value + ret = options[option].value + assert isinstance(ret, bool), 'must return bool' # could also be str + return ret except KeyError: return False -def get_base_compile_args(options, compiler): - args = [] +def get_base_compile_args(options: 'OptionDictType', compiler: 'Compiler') -> T.List[str]: + args = [] # type T.List[str] try: if options['b_lto'].value: args.extend(compiler.get_lto_compile_args()) @@ -337,8 +344,9 @@ def get_base_compile_args(options, compiler): pass return args -def get_base_link_args(options, linker, is_shared_module): - args = [] +def get_base_link_args(options: 'OptionDictType', linker: 'Compiler', + is_shared_module: bool) -> T.List[str]: + args = [] # type: T.List[str] try: if options['b_lto'].value: args.extend(linker.get_lto_link_args()) @@ -398,7 +406,8 @@ class CrossNoRunException(MesonException): pass class RunResult: - def __init__(self, compiled, returncode=999, stdout='UNDEFINED', stderr='UNDEFINED'): + def __init__(self, compiled: bool, returncode: int = 999, + stdout: str = 'UNDEFINED', stderr: str = 'UNDEFINED'): self.compiled = compiled self.returncode = returncode self.stdout = stdout @@ -431,22 +440,24 @@ class CompileResult: class Compiler(metaclass=abc.ABCMeta): # Libraries to ignore in find_library() since they are provided by the # compiler or the C library. Currently only used for MSVC. - ignore_libs = () + ignore_libs = () # type: T.Tuple[str, ...] # Libraries that are internal compiler implementations, and must not be # manually searched. - internal_libs = () + internal_libs = () # type: T.Tuple[str, ...] LINKER_PREFIX = None # type: T.Union[None, str, T.List[str]] INVOKES_LINKER = True - def __init__(self, exelist, version, for_machine: MachineChoice, info: 'MachineInfo', - linker: T.Optional['DynamicLinker'] = None, **kwargs): - if isinstance(exelist, str): - self.exelist = [exelist] - elif isinstance(exelist, list): - self.exelist = exelist - else: - raise TypeError('Unknown argument to Compiler') + # TODO: these could be forward declarations once we drop 3.5 support + if T.TYPE_CHECKING: + language = 'unset' + id = '' + + def __init__(self, exelist: T.List[str], version: str, + for_machine: MachineChoice, info: 'MachineInfo', + linker: T.Optional['DynamicLinker'] = None, + full_version: T.Optional[str] = None): + self.exelist = exelist # In case it's been overridden by a child class already if not hasattr(self, 'file_suffixes'): self.file_suffixes = lang_suffixes[self.language] @@ -454,28 +465,23 @@ class Compiler(metaclass=abc.ABCMeta): self.can_compile_suffixes = set(self.file_suffixes) self.default_suffix = self.file_suffixes[0] self.version = version - if 'full_version' in kwargs: - self.full_version = kwargs['full_version'] - else: - self.full_version = None + self.full_version = full_version self.for_machine = for_machine - self.base_options = [] + self.base_options = [] # type: T.List[str] self.linker = linker self.info = info - def __repr__(self): + def __repr__(self) -> str: repr_str = "<{0}: v{1} `{2}`>" return repr_str.format(self.__class__.__name__, self.version, ' '.join(self.exelist)) @lru_cache(maxsize=None) - def can_compile(self, src) -> bool: - if hasattr(src, 'fname'): + def can_compile(self, src: 'mesonlib.FileOrString') -> bool: + if isinstance(src, mesonlib.File): src = src.fname suffix = os.path.splitext(src)[1].lower() - if suffix and suffix[1:] in self.can_compile_suffixes: - return True - return False + return bool(suffix) and suffix[1:] in self.can_compile_suffixes def get_id(self) -> str: return self.id @@ -505,42 +511,54 @@ class Compiler(metaclass=abc.ABCMeta): def get_default_suffix(self) -> str: return self.default_suffix - def get_define(self, dname, prefix, env, extra_args, dependencies) -> T.Tuple[str, bool]: + def get_define(self, dname: str, prefix: str, env: 'Environment', + extra_args: T.Sequence[str], dependencies: T.Sequence['Dependency'], + disable_cache: bool = False) -> T.Tuple[str, bool]: raise EnvironmentException('%s does not support get_define ' % self.get_id()) - def compute_int(self, expression, low, high, guess, prefix, env, extra_args, dependencies) -> int: + def compute_int(self, expression: str, low: T.Optional[int], high: T.Optional[int], + guess: T.Optional[int], prefix: str, env: 'Environment', + extra_args: T.Sequence[str], dependencies: T.Sequence['Dependency']) -> int: raise EnvironmentException('%s does not support compute_int ' % self.get_id()) - def compute_parameters_with_absolute_paths(self, parameter_list, build_dir): + def compute_parameters_with_absolute_paths(self, parameter_list: T.Sequence[str], + build_dir: str) -> T.List[str]: raise EnvironmentException('%s does not support compute_parameters_with_absolute_paths ' % self.get_id()) - def has_members(self, typename, membernames, prefix, env, *, - extra_args=None, dependencies=None) -> T.Tuple[bool, bool]: + def has_members(self, typename: str, membernames: T.Sequence[str], + prefix: str, env: 'Environment', *, + extra_args: T.Optional[T.Sequence[str]] = None, + dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]: raise EnvironmentException('%s does not support has_member(s) ' % self.get_id()) - def has_type(self, typename, prefix, env, extra_args, *, - dependencies=None) -> T.Tuple[bool, bool]: + def has_type(self, typename: str, prefix: str, env: 'Environment', + extra_args: T.Sequence[str], *, + dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]: raise EnvironmentException('%s does not support has_type ' % self.get_id()) - def symbols_have_underscore_prefix(self, env) -> bool: + def symbols_have_underscore_prefix(self, env: 'Environment') -> bool: raise EnvironmentException('%s does not support symbols_have_underscore_prefix ' % self.get_id()) - def get_exelist(self): - return self.exelist[:] + def get_exelist(self) -> T.List[str]: + return self.exelist.copy() def get_linker_exelist(self) -> T.List[str]: return self.linker.get_exelist() + @abc.abstractmethod + def get_output_args(self, outputname: str) -> T.List[str]: + pass + def get_linker_output_args(self, outputname: str) -> T.List[str]: return self.linker.get_output_args(outputname) - def get_builtin_define(self, *args, **kwargs): + def get_builtin_define(self, define: str) -> T.Optional[str]: raise EnvironmentException('%s does not support get_builtin_define.' % self.id) - def has_builtin_define(self, *args, **kwargs): + def has_builtin_define(self, define: str) -> bool: raise EnvironmentException('%s does not support has_builtin_define.' % self.id) - def get_always_args(self): + def get_always_args(self) -> T.List[str]: return [] def can_linker_accept_rsp(self) -> bool: @@ -552,10 +570,10 @@ class Compiler(metaclass=abc.ABCMeta): def get_linker_always_args(self) -> T.List[str]: return self.linker.get_always_args() - def get_linker_lib_prefix(self): + def get_linker_lib_prefix(self) -> str: return self.linker.get_lib_prefix() - def gen_import_library_args(self, implibname): + def gen_import_library_args(self, implibname: str) -> T.List[str]: """ Used only on Windows for libraries that need an import library. This currently means C, C++, Fortran. @@ -567,62 +585,114 @@ class Compiler(metaclass=abc.ABCMeta): is_cross: bool) -> T.List[str]: return self.linker.get_args_from_envvars(for_machine, is_cross) - def get_options(self) -> T.Dict[str, coredata.UserOption]: + def get_options(self) -> 'OptionDictType': return {} - def get_option_compile_args(self, options): + def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]: return [] def get_option_link_args(self, options: 'OptionDictType') -> T.List[str]: return self.linker.get_option_args(options) - def check_header(self, *args, **kwargs) -> T.Tuple[bool, bool]: + def check_header(self, hname: str, prefix: str, env: 'Environment', *, + extra_args: T.Optional[T.List[str]] = None, + dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]: + """Check that header is usable. + + Returns a two item tuple of bools. The first bool is whether the + check succeeded, the second is whether the result was cached (True) + or run fresh (False). + """ raise EnvironmentException('Language %s does not support header checks.' % self.get_display_language()) - def has_header(self, *args, **kwargs) -> T.Tuple[bool, bool]: + def has_header(self, hname: str, prefix: str, env: 'Environment', *, + extra_args: T.Optional[T.List[str]] = None, + dependencies: T.Optional[T.List['Dependency']] = None, + disable_cache: bool = False) -> T.Tuple[bool, bool]: + """Check that header is exists. + + This check will return true if the file exists, even if it contains: + + ```c + # error "You thought you could use this, LOLZ!" + ``` + + Use check_header if your header only works in some cases. + + Returns a two item tuple of bools. The first bool is whether the + check succeeded, the second is whether the result was cached (True) + or run fresh (False). + """ raise EnvironmentException('Language %s does not support header checks.' % self.get_display_language()) - def has_header_symbol(self, *args, **kwargs) -> T.Tuple[bool, bool]: + def has_header_symbol(self, hname: str, symbol: str, prefix: str, + env: 'Environment', *, + extra_args: T.Optional[T.List[str]] = None, + dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]: raise EnvironmentException('Language %s does not support header symbol checks.' % self.get_display_language()) - def compiles(self, *args, **kwargs) -> T.Tuple[bool, bool]: + def compiles(self, code: str, env: 'Environment', *, + extra_args: T.Sequence[T.Union[T.Sequence[str], str]] = None, + dependencies: T.Optional[T.List['Dependency']] = None, + mode: str = 'compile', + disable_cache: bool = False) -> T.Tuple[bool, bool]: raise EnvironmentException('Language %s does not support compile checks.' % self.get_display_language()) - def links(self, *args, **kwargs) -> T.Tuple[bool, bool]: + def links(self, code: str, env: 'Environment', *, + extra_args: T.Sequence[T.Union[T.Sequence[str], str]] = None, + dependencies: T.Optional[T.List['Dependency']] = None, + mode: str = 'compile', + disable_cache: bool = False) -> T.Tuple[bool, bool]: raise EnvironmentException('Language %s does not support link checks.' % self.get_display_language()) - def run(self, *args, **kwargs) -> RunResult: + def run(self, code: str, env: 'Environment', *, + extra_args: T.Optional[T.List[str]] = None, + dependencies: T.Optional[T.List['Dependency']] = None) -> RunResult: raise EnvironmentException('Language %s does not support run checks.' % self.get_display_language()) - def sizeof(self, *args, **kwargs) -> int: + def sizeof(self, typename: str, prefix: str, env: 'Environment', *, + extra_args: T.Optional[T.List[str]] = None, + dependencies: T.Optional[T.List['Dependency']] = None) -> int: raise EnvironmentException('Language %s does not support sizeof checks.' % self.get_display_language()) - def alignment(self, *args, **kwargs) -> int: + def alignment(self, typename: str, prefix: str, env: 'Environment', *, + extra_args: T.Optional[T.List[str]] = None, + dependencies: T.Optional[T.List['Dependency']] = None) -> int: raise EnvironmentException('Language %s does not support alignment checks.' % self.get_display_language()) - def has_function(self, *args, **kwargs) -> T.Tuple[bool, bool]: + def has_function(self, funcname: str, prefix: str, env: 'Environment', *, + extra_args: T.Optional[T.Iterable[str]] = None, + dependencies: T.Optional[T.Iterable['Dependency']] = None) -> T.Tuple[bool, bool]: + """See if a function exists. + + Returns a two item tuple of bools. The first bool is whether the + check succeeded, the second is whether the result was cached (True) + or run fresh (False). + """ raise EnvironmentException('Language %s does not support function checks.' % self.get_display_language()) @classmethod def unix_args_to_native(cls, args: T.List[str]) -> T.List[str]: "Always returns a copy that can be independently mutated" - return args[:] + return args.copy() @classmethod def native_args_to_unix(cls, args: T.List[str]) -> T.List[str]: "Always returns a copy that can be independently mutated" - return args[:] + return args.copy() - def find_library(self, *args, **kwargs): + def find_library(self, libname: str, env: 'Environment', extra_dirs: T.List[str], + libtype: LibType = LibType.PREFER_SHARED) -> T.Optional[T.List[str]]: raise EnvironmentException('Language {} does not support library finding.'.format(self.get_display_language())) - def get_library_dirs(self, *args, **kwargs): + def get_library_naming(self, env: 'Environment', libtype: LibType, + strict: bool = False) -> T.Tuple[str, ...]: return () - def get_program_dirs(self, *args, **kwargs): + def get_program_dirs(self, env: 'Environment') -> T.List[str]: return [] - def has_multi_arguments(self, args, env) -> T.Tuple[bool, bool]: + def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]: raise EnvironmentException( 'Language {} does not support has_multi_arguments.'.format( self.get_display_language())) @@ -630,7 +700,8 @@ class Compiler(metaclass=abc.ABCMeta): def has_multi_link_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]: return self.linker.has_multi_arguments(args, env) - def _get_compile_output(self, dirname, mode): + def _get_compile_output(self, dirname: str, mode: str) -> str: + # TODO: mode should really be an enum # In pre-processor mode, the output is sent to stdout and discarded if mode == 'preprocess': return None @@ -642,8 +713,9 @@ class Compiler(metaclass=abc.ABCMeta): suffix = 'obj' return os.path.join(dirname, 'output.' + suffix) - def get_compiler_args_for_mode(self, mode): - args = [] + def get_compiler_args_for_mode(self, mode: str) -> T.List[str]: + # TODO: mode should really be an enum + args = [] # type: T.List[str] args += self.get_always_args() if mode == 'compile': args += self.get_compile_only_args() @@ -656,7 +728,9 @@ class Compiler(metaclass=abc.ABCMeta): return CompilerArgs(self, args) @contextlib.contextmanager - def compile(self, code: str, extra_args: list = None, *, mode: str = 'link', want_output: bool = False, temp_dir: str = None) -> T.Iterator[CompileResult]: + def compile(self, code: 'mesonlib.FileOrString', extra_args: T.Optional[T.List[str]] = None, + *, mode: str = 'link', want_output: bool = False, + temp_dir: T.Optional[str] = None) -> T.Iterator[T.Optional[CompileResult]]: if extra_args is None: extra_args = [] try: @@ -669,8 +743,11 @@ class Compiler(metaclass=abc.ABCMeta): ofile.write(code) # ccache would result in a cache miss no_ccache = True + contents = code elif isinstance(code, mesonlib.File): srcname = code.fname + with open(code.fname, 'r') as f: + contents = f.read() # Construct the compiler command-line commands = self.compiler_args() @@ -685,16 +762,16 @@ class Compiler(metaclass=abc.ABCMeta): # in the command line after '/link' is given to the linker. commands += extra_args # Generate full command-line with the exelist - commands = self.get_exelist() + commands.to_native() + command_list = self.get_exelist() + commands.to_native() mlog.debug('Running compile:') mlog.debug('Working directory: ', tmpdirname) - mlog.debug('Command line: ', ' '.join(commands), '\n') - mlog.debug('Code:\n', code) + mlog.debug('Command line: ', ' '.join(command_list), '\n') + mlog.debug('Code:\n', contents) os_env = os.environ.copy() os_env['LC_ALL'] = 'C' if no_ccache: os_env['CCACHE_DISABLE'] = '1' - p, stdo, stde = Popen_safe(commands, cwd=tmpdirname, env=os_env) + p, stdo, stde = Popen_safe(command_list, cwd=tmpdirname, env=os_env) mlog.debug('Compiler stdout:\n', stdo) mlog.debug('Compiler stderr:\n', stde) @@ -706,39 +783,39 @@ class Compiler(metaclass=abc.ABCMeta): # On Windows antivirus programs and the like hold on to files so # they can't be deleted. There's not much to do in this case. Also, # catch OSError because the directory is then no longer empty. - pass + yield None @contextlib.contextmanager - def cached_compile(self, code, cdata: coredata.CoreData, *, extra_args=None, mode: str = 'link', temp_dir=None) -> T.Iterator[CompileResult]: - assert(isinstance(cdata, coredata.CoreData)) - + def cached_compile(self, code: str, cdata: coredata.CoreData, *, + extra_args: T.Optional[T.List[str]] = None, + mode: str = 'link', + temp_dir: T.Optional[str] = None) -> T.Iterator[T.Optional[CompileResult]]: # Calculate the key - textra_args = tuple(extra_args) if extra_args is not None else None - key = (tuple(self.exelist), self.version, code, textra_args, mode) - - # Check if not cached - if key not in cdata.compiler_check_cache: + textra_args = tuple(extra_args) if extra_args is not None else tuple() # type: T.Tuple[str, ...] + key = (tuple(self.exelist), self.version, code, textra_args, mode) # type: coredata.CompilerCheckCacheKey + + # Check if not cached, and generate, otherwise get from the cache + if key in cdata.compiler_check_cache: + p = cdata.compiler_check_cache[key] # type: CompileResult + p.cached = True + mlog.debug('Using cached compile:') + mlog.debug('Cached command line: ', ' '.join(p.command), '\n') + mlog.debug('Code:\n', code) + mlog.debug('Cached compiler stdout:\n', p.stdout) + mlog.debug('Cached compiler stderr:\n', p.stderr) + yield p + else: with self.compile(code, extra_args=extra_args, mode=mode, want_output=False, temp_dir=temp_dir) as p: cdata.compiler_check_cache[key] = p yield p - p.cached = True - return - - # Return cached - p = cdata.compiler_check_cache[key] - mlog.debug('Using cached compile:') - mlog.debug('Cached command line: ', ' '.join(p.commands), '\n') - mlog.debug('Code:\n', code) - mlog.debug('Cached compiler stdout:\n', p.stdo) - mlog.debug('Cached compiler stderr:\n', p.stde) - yield p - - def get_colorout_args(self, colortype): + + def get_colorout_args(self, colortype: str) -> T.List[str]: + # TODO: colortype can probably be an emum return [] # Some compilers (msvc) write debug info to a separate file. # These args specify where it should be written. - def get_compile_debugfile_args(self, rel_obj, **kwargs): + def get_compile_debugfile_args(self, rel_obj: str, pch: bool = False) -> T.List[str]: return [] def get_link_debugfile_name(self, targetfile: str) -> str: @@ -762,10 +839,12 @@ class Compiler(metaclass=abc.ABCMeta): def no_undefined_link_args(self) -> T.List[str]: return self.linker.no_undefined_args() - # Compiler arguments needed to enable the given instruction set. - # May be [] meaning nothing needed or None meaning the given set - # is not supported. - def get_instruction_set_args(self, instruction_set): + def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[str]]: + """Compiler arguments needed to enable the given instruction set. + + Return type ay be an empty list meaning nothing needed or None + meaning the given set is not supported. + """ return None def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str, @@ -774,40 +853,40 @@ class Compiler(metaclass=abc.ABCMeta): return self.linker.build_rpath_args( env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath) - def thread_flags(self, env): + def thread_flags(self, env: 'Environment') -> T.List[str]: return [] - def openmp_flags(self): + def openmp_flags(self) -> T.List[str]: raise EnvironmentException('Language %s does not support OpenMP flags.' % self.get_display_language()) - def openmp_link_flags(self): + def openmp_link_flags(self) -> T.List[str]: return self.openmp_flags() - def language_stdlib_only_link_flags(self): + def language_stdlib_only_link_flags(self) -> T.List[str]: return [] - def gnu_symbol_visibility_args(self, vistype): + def gnu_symbol_visibility_args(self, vistype: str) -> T.List[str]: return [] - def get_gui_app_args(self, value): + def get_gui_app_args(self, value: bool) -> T.List[str]: return [] - def has_func_attribute(self, name, env): + def has_func_attribute(self, name: str, env: 'Environment') -> T.List[str]: raise EnvironmentException( 'Language {} does not support function attributes.'.format(self.get_display_language())) - def get_pic_args(self): + def get_pic_args(self) -> T.List[str]: m = 'Language {} does not support position-independent code' raise EnvironmentException(m.format(self.get_display_language())) - def get_pie_args(self): + def get_pie_args(self) -> T.List[str]: m = 'Language {} does not support position-independent executable' raise EnvironmentException(m.format(self.get_display_language())) def get_pie_link_args(self) -> T.List[str]: return self.linker.get_pie_args() - def get_argument_syntax(self): + def get_argument_syntax(self) -> str: """Returns the argument family type. Compilers fall into families if they try to emulate the command line @@ -818,22 +897,19 @@ class Compiler(metaclass=abc.ABCMeta): """ return 'other' - def get_profile_generate_args(self): + def get_profile_generate_args(self) -> T.List[str]: raise EnvironmentException( '%s does not support get_profile_generate_args ' % self.get_id()) - def get_profile_use_args(self): + def get_profile_use_args(self) -> T.List[str]: raise EnvironmentException( '%s does not support get_profile_use_args ' % self.get_id()) - def get_undefined_link_args(self) -> T.List[str]: - return self.linker.get_undefined_link_args() - - def remove_linkerlike_args(self, args): + def remove_linkerlike_args(self, args: T.List[str]) -> T.List[str]: rm_exact = ('-headerpad_max_install_names',) rm_prefixes = ('-Wl,', '-L',) rm_next = ('-L', '-framework',) - ret = [] + ret = [] # T.List[str] iargs = iter(args) for arg in iargs: # Remove this argument @@ -882,13 +958,13 @@ class Compiler(metaclass=abc.ABCMeta): env, prefix, shlib_name, suffix, soversion, darwin_versions, is_shared_module) - def get_target_link_args(self, target): + def get_target_link_args(self, target: 'BuildTarget') -> T.List[str]: return target.link_args - def get_dependency_compile_args(self, dep): + def get_dependency_compile_args(self, dep: 'Dependency') -> T.List[str]: return dep.get_compile_args() - def get_dependency_link_args(self, dep): + def get_dependency_link_args(self, dep: 'Dependency') -> T.List[str]: return dep.get_link_args() @classmethod @@ -897,14 +973,29 @@ class Compiler(metaclass=abc.ABCMeta): """ return [] + def get_coverage_args(self) -> T.List[str]: + return [] + def get_coverage_link_args(self) -> T.List[str]: return self.linker.get_coverage_args() def get_disable_assert_args(self) -> T.List[str]: return [] + def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]: + raise EnvironmentError('This compiler does not support Windows CRT selection') + + def get_crt_link_args(self, crt_val: str, buildtype: str) -> T.List[str]: + raise EnvironmentError('This compiler does not support Windows CRT selection') + + def get_compile_only_args(self) -> T.List[str]: + return [] + + def get_preprocess_only_args(self) -> T.List[str]: + raise EnvironmentError('This compiler does not have a preprocessor') + -def get_largefile_args(compiler): +def get_largefile_args(compiler: Compiler) -> T.List[str]: ''' Enable transparent large-file-support for 32-bit UNIX systems ''' @@ -967,7 +1058,7 @@ def get_global_options(lang: str, comp: T.Type[Compiler], for_machine: MachineChoice, is_cross: bool, - properties: Properties) -> T.Dict[str, coredata.UserOption]: + properties: Properties) -> 'OptionDictType': """Retreive options that apply to all compilers for a given language.""" description = 'Extra arguments passed to the {}'.format(lang) opts = { @@ -977,7 +1068,7 @@ def get_global_options(lang: str, 'link_args': coredata.UserArrayOption( description + ' linker', [], split_args=True, user_input=True, allow_dups=True), - } + } # type: OptionDictType # Get from env vars. compile_args, link_args = get_args_from_envvars( diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index 934ad12..0d89bbc 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -161,7 +161,7 @@ class CudaCompiler(Compiler): mlog.debug('cudaGetDeviceCount() returned ' + stde) def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None): - result, cached = super().has_header_symbol(hname, symbol, prefix, env, extra_args, dependencies) + result, cached = super().has_header_symbol(hname, symbol, prefix, env, extra_args=extra_args, dependencies=dependencies) if result: return True, cached if extra_args is None: @@ -171,7 +171,7 @@ class CudaCompiler(Compiler): #include <{header}> using {symbol}; int main(void) {{ return 0; }}''' - return self.compiles(t.format(**fargs), env, extra_args, dependencies) + return self.compiles(t.format(**fargs), env, extra_args=extra_args, dependencies=dependencies) def get_options(self): opts = super().get_options() diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index c2e21c4..a47d6ed 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -110,6 +110,9 @@ class RustCompiler(Compiler): def get_std_exe_link_args(self): return [] + def get_output_args(self, outputname: str) -> T.List[str]: + return ['-o', outputname] + # Rust does not have a use_linker_args because it dispatches to a gcc-like # C compiler for dynamic linking, as such we invoke the C compiler's # use_linker_args method instead. diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index baae32b..5827a4e 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -32,10 +32,11 @@ import typing as T if T.TYPE_CHECKING: from . import dependencies - from .compilers import Compiler, CompileResult # noqa: F401 + from .compilers.compilers import Compiler, CompileResult # noqa: F401 from .environment import Environment OptionDictType = T.Dict[str, 'UserOption[T.Any]'] + CompilerCheckCacheKey = T.Tuple[T.Tuple[str, ...], str, str, T.Tuple[str, ...], str] version = '0.55.999' backendlist = ['ninja', 'vs', 'vs2010', 'vs2015', 'vs2017', 'vs2019', 'xcode'] @@ -394,7 +395,7 @@ class CoreData: build_cache = DependencyCache(self.builtins_per_machine, MachineChoice.BUILD) host_cache = DependencyCache(self.builtins_per_machine, MachineChoice.BUILD) self.deps = PerMachine(build_cache, host_cache) # type: PerMachine[DependencyCache] - self.compiler_check_cache = OrderedDict() # type: T.MutableMapping[T.Tuple[T.Tuple[str, ...], str, str, T.Tuple[str, ...], str], CompileResult] + self.compiler_check_cache = OrderedDict() # type: T.Dict[CompilerCheckCacheKey, compiler.CompileResult] # Only to print a warning if it changes between Meson invocations. self.config_files = self.__load_config_files(options, scratch_dir, 'native') diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index dd2fe8e..17d2733 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -34,6 +34,8 @@ if T.TYPE_CHECKING: from .compilers.compilers import CompilerType from .interpreterbase import ObjectHolder + FileOrString = T.Union['File', str] + _T = T.TypeVar('_T') _U = T.TypeVar('_U') diff --git a/run_mypy.py b/run_mypy.py index 953c20d..317210a 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -14,6 +14,7 @@ modules = [ # specific files 'mesonbuild/arglist.py', + 'mesonbuild/compilers/compilers.py', # 'mesonbuild/compilers/mixins/intel.py', # 'mesonbuild/coredata.py', 'mesonbuild/dependencies/boost.py', diff --git a/run_unittests.py b/run_unittests.py index 6b817bb..d8a2a7b 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -377,7 +377,7 @@ class InternalTests(unittest.TestCase): self.assertEqual(a, ['-I.', '-I./tests2/', '-I./tests/', '-I..']) def test_compiler_args_class_d(self): - d = mesonbuild.compilers.DCompiler([], 'fake', MachineChoice.HOST, 'info', 'arch', False, None) + d = mesonbuild.compilers.DmdDCompiler([], 'fake', MachineChoice.HOST, 'info', 'arch') # check include order is kept when deduplicating a = d.compiler_args(['-Ifirst', '-Isecond', '-Ithird']) a += ['-Ifirst'] -- cgit v1.1