From cffb4e699a217aedffc4297937df9bb8b07bc529 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 6 Dec 2025 14:09:06 +0000 Subject: [PATCH 1/2] Address code duplication in mypyc/lib-rt --- mypy/test/testmypyc.py | 12 ++++++ mypy_self_check.ini | 2 +- mypyc/build_setup.py | 4 ++ mypyc/lib-rt/build_setup.py | 76 +++++++++++++++++++++++++++++++++++++ mypyc/lib-rt/setup.py | 63 +----------------------------- tox.ini | 1 + 6 files changed, 96 insertions(+), 62 deletions(-) create mode 100644 mypyc/lib-rt/build_setup.py diff --git a/mypy/test/testmypyc.py b/mypy/test/testmypyc.py index e8436f407694..655b5b5bd08b 100644 --- a/mypy/test/testmypyc.py +++ b/mypy/test/testmypyc.py @@ -2,13 +2,25 @@ from __future__ import annotations +import filecmp import os from unittest import TestCase import mypy +import mypyc class MypycTest(TestCase): def test_using_mypyc(self) -> None: if os.getenv("TEST_MYPYC", None) == "1": assert not mypy.__file__.endswith(".py"), "Expected to find a mypyc-compiled version" + + def test_shared_files_consistent(self) -> None: + if os.getenv("TEST_MYPYC", None) != "1": + mypyc_path = mypyc.__path__[0] + for f in ["build_setup.py"]: + assert filecmp.cmp( + os.path.join(mypyc_path, f), + os.path.join(mypyc_path, "lib-rt", f), + shallow=False, + ), f"Shared files inconsistent, run cp mypyc/{f} mypyc/lib-rt" diff --git a/mypy_self_check.ini b/mypy_self_check.ini index e170647c439a..c52acb87f869 100644 --- a/mypy_self_check.ini +++ b/mypy_self_check.ini @@ -8,7 +8,7 @@ pretty = True always_false = MYPYC plugins = mypy.plugins.proper_plugin python_version = 3.10 -exclude = mypy/typeshed/|mypyc/test-data/ +exclude = mypy/typeshed/|mypyc/test-data/|mypyc/lib-rt/ enable_error_code = ignore-without-code,redundant-expr enable_incomplete_feature = PreciseTupleTypes show_error_code_links = True diff --git a/mypyc/build_setup.py b/mypyc/build_setup.py index 328518e220d6..8ac6e1918bba 100644 --- a/mypyc/build_setup.py +++ b/mypyc/build_setup.py @@ -1,3 +1,7 @@ +# This file must have the same content for mypyc/build_setup.py and lib-rt/build_setup.py, +# it exists to work around absence of support for per-file compile flags in setuptools. +# The version in mypyc/ is the source of truth, and should be copied to lib-rt if modified. + import os import platform import sys diff --git a/mypyc/lib-rt/build_setup.py b/mypyc/lib-rt/build_setup.py new file mode 100644 index 000000000000..8ac6e1918bba --- /dev/null +++ b/mypyc/lib-rt/build_setup.py @@ -0,0 +1,76 @@ +# This file must have the same content for mypyc/build_setup.py and lib-rt/build_setup.py, +# it exists to work around absence of support for per-file compile flags in setuptools. +# The version in mypyc/ is the source of truth, and should be copied to lib-rt if modified. + +import os +import platform +import sys + +try: + # Import setuptools so that it monkey-patch overrides distutils + import setuptools # noqa: F401 +except ImportError: + pass + +if sys.version_info >= (3, 12): + # From setuptools' monkeypatch + from distutils import ccompiler # type: ignore[import-not-found] +else: + from distutils import ccompiler + +EXTRA_FLAGS_PER_COMPILER_TYPE_PER_PATH_COMPONENT = { + "unix": { + "base64/arch/ssse3": ["-mssse3"], + "base64/arch/sse41": ["-msse4.1"], + "base64/arch/sse42": ["-msse4.2"], + "base64/arch/avx2": ["-mavx2"], + "base64/arch/avx": ["-mavx"], + }, + "msvc": { + "base64/arch/sse42": ["/arch:SSE4.2"], + "base64/arch/avx2": ["/arch:AVX2"], + "base64/arch/avx": ["/arch:AVX"], + }, +} + +ccompiler.CCompiler.__spawn = ccompiler.CCompiler.spawn # type: ignore[attr-defined] +X86_64 = platform.machine() in ("x86_64", "AMD64", "amd64") +PYODIDE = "PYODIDE" in os.environ + + +def spawn(self, cmd, **kwargs) -> None: # type: ignore[no-untyped-def] + if PYODIDE: + new_cmd = list(cmd) + for argument in reversed(new_cmd): + if not str(argument).endswith(".c"): + continue + if "base64/arch/" in str(argument): + new_cmd.extend(["-msimd128"]) + else: + compiler_type: str = self.compiler_type + extra_options = EXTRA_FLAGS_PER_COMPILER_TYPE_PER_PATH_COMPONENT[compiler_type] + new_cmd = list(cmd) + if X86_64 and extra_options is not None: + # filenames are closer to the end of command line + for argument in reversed(new_cmd): + # Check if the matching argument contains a source filename. + if not str(argument).endswith(".c"): + continue + + for path in extra_options.keys(): + if path in str(argument): + if compiler_type == "bcpp": + compiler = new_cmd.pop() + # Borland accepts a source file name at the end, + # insert the options before it + new_cmd.extend(extra_options[path]) + new_cmd.append(compiler) + else: + new_cmd.extend(extra_options[path]) + + # path component is found, no need to search any further + break + self.__spawn(new_cmd, **kwargs) + + +ccompiler.CCompiler.spawn = spawn # type: ignore[method-assign] diff --git a/mypyc/lib-rt/setup.py b/mypyc/lib-rt/setup.py index 1030ce496ccd..d82c367aec31 100644 --- a/mypyc/lib-rt/setup.py +++ b/mypyc/lib-rt/setup.py @@ -6,12 +6,12 @@ from __future__ import annotations import os -import platform import subprocess import sys from distutils import ccompiler, sysconfig from typing import Any +import build_setup # noqa: F401 from setuptools import Extension, setup from setuptools.command.build_ext import build_ext @@ -25,63 +25,6 @@ "pythonsupport.c", ] -EXTRA_FLAGS_PER_COMPILER_TYPE_PER_PATH_COMPONENT = { - "unix": { - "base64/arch/ssse3": ["-mssse3"], - "base64/arch/sse41": ["-msse4.1"], - "base64/arch/sse42": ["-msse4.2"], - "base64/arch/avx2": ["-mavx2"], - "base64/arch/avx": ["-mavx"], - }, - "msvc": { - "base64/arch/sse42": ["/arch:SSE4.2"], - "base64/arch/avx2": ["/arch:AVX2"], - "base64/arch/avx": ["/arch:AVX"], - }, -} - -ccompiler.CCompiler.__spawn = ccompiler.CCompiler.spawn # type: ignore[attr-defined] -X86_64 = platform.machine() in ("x86_64", "AMD64", "amd64") -PYODIDE = "PYODIDE" in os.environ - - -def spawn(self, cmd, **kwargs) -> None: # type: ignore[no-untyped-def] - if PYODIDE: - new_cmd = list(cmd) - for argument in reversed(new_cmd): - if not str(argument).endswith(".c"): - continue - if "base64/arch/" in str(argument): - new_cmd.extend(["-msimd128"]) - else: - compiler_type: str = self.compiler_type - extra_options = EXTRA_FLAGS_PER_COMPILER_TYPE_PER_PATH_COMPONENT[compiler_type] - new_cmd = list(cmd) - if X86_64 and extra_options is not None: - # filenames are closer to the end of command line - for argument in reversed(new_cmd): - # Check if the matching argument contains a source filename. - if not str(argument).endswith(".c"): - continue - - for path in extra_options.keys(): - if path in str(argument): - if compiler_type == "bcpp": - compiler = new_cmd.pop() - # Borland accepts a source file name at the end, - # insert the options before it - new_cmd.extend(extra_options[path]) - new_cmd.append(compiler) - else: - new_cmd.extend(extra_options[path]) - - # path component is found, no need to search any further - break - self.__spawn(new_cmd, **kwargs) - - -ccompiler.CCompiler.spawn = spawn # type: ignore[method-assign] - class BuildExtGtest(build_ext): def get_library_names(self) -> list[str]: @@ -130,13 +73,11 @@ def run(self) -> None: cmdclass={"build_ext": BuildExtGtest}, ) else: - # TODO: we need a way to share our preferred C flags and get_extension() logic with - # mypyc/build.py without code duplication. compiler = ccompiler.new_compiler() sysconfig.customize_compiler(compiler) cflags: list[str] = [] if compiler.compiler_type == "unix": # type: ignore[attr-defined] - cflags += ["-O3"] + cflags += ["-O3", "-Wno-unused-function"] elif compiler.compiler_type == "msvc": # type: ignore[attr-defined] cflags += ["/O2"] diff --git a/tox.ini b/tox.ini index 8e97590b0d2a..2126970afa99 100644 --- a/tox.ini +++ b/tox.ini @@ -62,3 +62,4 @@ commands = python runtests.py self python -m mypy --config-file mypy_self_check.ini misc --exclude misc/sync-typeshed.py python -m mypy --config-file mypy_self_check.ini test-data/unit/plugins + python -m mypy mypyc/lib-rt From 734d500496a6c893d6cd01b04cf6f03701cda20e Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 6 Dec 2025 14:20:34 +0000 Subject: [PATCH 2/2] Update mypyc allowlist --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 7c5a1a83d5c3..2892bdc64f38 100644 --- a/setup.py +++ b/setup.py @@ -98,6 +98,7 @@ def run(self) -> None: ) + ( # Don't want to grab this accidentally os.path.join("mypyc", "lib-rt", "setup.py"), + os.path.join("mypyc", "lib-rt", "build_setup.py"), # Uses __file__ at top level https://github.com/mypyc/mypyc/issues/700 os.path.join("mypyc", "__main__.py"), os.path.join("mypyc", "build_setup.py"), # for monkeypatching