diff --git a/.mypy.ini b/.mypy.ini index 42bd17e1a1c6..2c92d1ea313f 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -75,5 +75,8 @@ strict_optional = True [mypy-mesonbuild.programs] strict_optional = True +[mypy-mesonbuild.scripts.symbolextractor] +strict_optional = True + [mypy-mesonbuild.tooldetect] strict_optional = True diff --git a/mesonbuild/scripts/symbolextractor.py b/mesonbuild/scripts/symbolextractor.py index efe46dcd6b13..3424ef8b60e3 100644 --- a/mesonbuild/scripts/symbolextractor.py +++ b/mesonbuild/scripts/symbolextractor.py @@ -17,11 +17,14 @@ from ..mesonlib import Popen_safe import argparse -parser = argparse.ArgumentParser() +if T.TYPE_CHECKING: + class Arguments(T.Protocol): -parser.add_argument('--cross-host', default=None, dest='cross_host', - help='cross compilation host platform') -parser.add_argument('args', nargs='+') + cross_host: str | None + builddir: str + libfilename: str + impfilename: str + outfilename: str TOOL_WARNING_FILE: str RELINKING_WARNING = 'Relinking will always happen on source changes.' @@ -60,7 +63,7 @@ def get_tool(name: str) -> T.List[str]: return shlex.split(os.environ[evar]) return [name] -def call_tool(name: str, args: T.List[str], **kwargs: T.Any) -> str: +def call_tool(name: str, args: T.List[str], **kwargs: T.Any) -> str | None: tool = get_tool(name) try: p, output, e = Popen_safe(tool + args, **kwargs) @@ -75,7 +78,7 @@ def call_tool(name: str, args: T.List[str], **kwargs: T.Any) -> str: return None return output -def call_tool_nowarn(tool: T.List[str], **kwargs: T.Any) -> T.Tuple[str, str]: +def call_tool_nowarn(tool: T.List[str], **kwargs: T.Any) -> T.Tuple[str | None, str | None]: try: p, output, e = Popen_safe(tool, **kwargs) except FileNotFoundError: @@ -198,7 +201,7 @@ def cygwin_syms(impfilename: str, outfilename: str) -> None: result.append(line.split(maxsplit=1)[0]) write_if_changed('\n'.join(result) + '\n', outfilename) -def _get_implib_dllname(impfilename: str) -> T.Tuple[T.List[str], str]: +def _get_implib_dllname(impfilename: str) -> T.Tuple[T.List[str], str | None]: all_stderr = '' # First try lib.exe, which is provided by MSVC. Then llvm-lib.exe, by LLVM # for clang-cl. @@ -214,15 +217,17 @@ def _get_implib_dllname(impfilename: str) -> T.Tuple[T.List[str], str]: # a single DLL, so we can pick any of these. Pick the last one for # simplicity. Also skip the last line, which is empty. return output.split('\n')[-2:-1], None - all_stderr += e + if e: + all_stderr += e # Next, try dlltool.exe which is provided by MinGW output, e = call_tool_nowarn(get_tool('dlltool') + ['-I', impfilename]) if output: return [output], None - all_stderr += e + if e: + all_stderr += e return ([], all_stderr) -def _get_implib_exports(impfilename: str) -> T.Tuple[T.List[str], str]: +def _get_implib_exports(impfilename: str) -> T.Tuple[T.List[str], str | None]: all_stderr = '' # Force dumpbin.exe to use en-US so we can parse its output env = os.environ.copy() @@ -233,7 +238,8 @@ def _get_implib_exports(impfilename: str) -> T.Tuple[T.List[str], str]: start = lines.index('File Type: LIBRARY') end = lines.index(' Summary') return lines[start:end], None - all_stderr += e + if e: + all_stderr += e # Next, try llvm-nm.exe provided by LLVM, then nm.exe provided by MinGW for nm in ('llvm-nm', 'nm'): output, e = call_tool_nowarn(get_tool(nm) + ['--extern-only', '--defined-only', @@ -245,7 +251,8 @@ def _get_implib_exports(impfilename: str) -> T.Tuple[T.List[str], str]: continue result.append(line.split(maxsplit=1)[0]) return result, None - all_stderr += e + if e: + all_stderr += e return ([], all_stderr) def windows_syms(impfilename: str, outfilename: str) -> None: @@ -285,48 +292,48 @@ def os2_syms(impfilename: str, outfilename: str) -> None: all_stderr += e print_tool_warning(['nm', 'listomf'], 'do not work or were not found', all_stderr) -def gen_symbols(libfilename: str, impfilename: str, outfilename: str, cross_host: str) -> None: - if cross_host is not None: +def gen_symbols(options: Arguments) -> None: + if options.cross_host is not None: # In case of cross builds just always relink. In theory we could # determine the correct toolset, but we would need to use the correct # `nm`, `readelf`, etc, from the cross info which requires refactoring. - if cross_host == 'windows' and os.path.isfile(impfilename): - windows_syms(impfilename, outfilename) + if options.cross_host == 'windows' and os.path.isfile(options.impfilename): + windows_syms(options.impfilename, options.outfilename) else: - dummy_syms(outfilename) + dummy_syms(options.outfilename) elif mesonlib.is_linux() or mesonlib.is_hurd() or mesonlib.is_haiku(): - gnu_syms(libfilename, outfilename) + gnu_syms(options.libfilename, options.outfilename) elif mesonlib.is_osx(): - osx_syms(libfilename, outfilename) + osx_syms(options.libfilename, options.outfilename) elif mesonlib.is_openbsd(): - openbsd_syms(libfilename, outfilename) + openbsd_syms(options.libfilename, options.outfilename) elif mesonlib.is_freebsd(): - freebsd_syms(libfilename, outfilename) + freebsd_syms(options.libfilename, options.outfilename) elif mesonlib.is_netbsd(): - freebsd_syms(libfilename, outfilename) + freebsd_syms(options.libfilename, options.outfilename) elif mesonlib.is_windows(): - if os.path.isfile(impfilename): - windows_syms(impfilename, outfilename) + if os.path.isfile(options.impfilename): + windows_syms(options.impfilename, options.outfilename) else: # No import library. Not sure how the DLL is being used, so just # rebuild everything that links to it every time. - dummy_syms(outfilename) + dummy_syms(options.outfilename) elif mesonlib.is_cygwin(): - if os.path.isfile(impfilename): - cygwin_syms(impfilename, outfilename) + if os.path.isfile(options.impfilename): + cygwin_syms(options.impfilename, options.outfilename) else: # No import library. Not sure how the DLL is being used, so just # rebuild everything that links to it every time. - dummy_syms(outfilename) + dummy_syms(options.outfilename) elif mesonlib.is_sunos(): - solaris_syms(libfilename, outfilename) + solaris_syms(options.libfilename, options.outfilename) elif mesonlib.is_os2(): - if os.path.isfile(impfilename): - os2_syms(impfilename, outfilename) + if os.path.isfile(options.impfilename): + os2_syms(options.impfilename, options.outfilename) else: # No import library. Not sure how the DLL is being used, so just # rebuild everything that links to it every time. - dummy_syms(outfilename) + dummy_syms(options.outfilename) else: if not os.path.exists(TOOL_WARNING_FILE): mlog.warning('Symbol extracting has not been implemented for this ' @@ -334,20 +341,22 @@ def gen_symbols(libfilename: str, impfilename: str, outfilename: str, cross_host # Write it out so we don't warn again with open(TOOL_WARNING_FILE, 'w', encoding='utf-8'): pass - dummy_syms(outfilename) + dummy_syms(options.outfilename) def run(args: T.List[str]) -> int: global TOOL_WARNING_FILE # pylint: disable=global-statement - options = parser.parse_args(args) - if len(options.args) != 4: - print('symbolextractor.py ') - sys.exit(1) - privdir = os.path.join(options.args[0], 'meson-private') + + parser = argparse.ArgumentParser() + parser.add_argument('--cross-host', default=None, dest='cross_host', + help='cross compilation host platform') + parser.add_argument('builddir', metavar='build directory') + parser.add_argument('libfilename', metavar='shared library') + parser.add_argument('impfilename', metavar='import library') + parser.add_argument('outfilename', metavar='output') + options = T.cast('Arguments', parser.parse_args(args)) + privdir = os.path.join(options.builddir, 'meson-private') TOOL_WARNING_FILE = os.path.join(privdir, 'symbolextractor_tool_warning_printed') - libfile = options.args[1] - impfile = options.args[2] # Only used on Windows - outfile = options.args[3] - gen_symbols(libfile, impfile, outfile, options.cross_host) + gen_symbols(options) return 0 if __name__ == '__main__':