From 5add95b7a316495cc2d41f0ce1aab0af5f204932 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Mon, 19 Jan 2026 20:36:56 -0800 Subject: [PATCH 01/16] Drop prebuilt wheels for python 3.8 and 3.9 - These versions are past end of life (3.9 EOL'd in 2025) - Also, the 3.8 OSX builds in CI were taking almost half an hour --- .github/workflows/ci.yml | 50 ++++++---------------------------------- 1 file changed, 7 insertions(+), 43 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 040221ad..e484d428 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,19 +29,13 @@ jobs: fail-fast: false matrix: os_dist: [ - {os: ubuntu-24.04, dist: cp38-manylinux_x86_64}, - {os: ubuntu-24.04, dist: cp39-manylinux_x86_64}, {os: ubuntu-24.04, dist: cp310-manylinux_x86_64}, {os: ubuntu-24.04, dist: cp311-manylinux_x86_64}, {os: ubuntu-24.04, dist: cp312-manylinux_x86_64}, {os: ubuntu-24.04, dist: cp313-manylinux_x86_64}, {os: ubuntu-24.04, dist: cp314-manylinux_x86_64}, - # cp38-manylinux_i686 disabled because pandas isn't prebuilt and takes 20 minutes to build. - # {os: ubuntu-latest, dist: cp38-manylinux_i686}, - # cp39-manylinux_i686 disabled because pandas isn't prebuilt and takes 20 minutes to build. - # {os: ubuntu-latest, dist: cp39-manylinux_i686}, - # cp310-manylinux_i686 disabled because scipy isn't prebuilt and fails to build. + # manylinux_i686 disabled because scipy isn't prebuilt and fails to build. # # The actual error seen in github actions: # @@ -59,11 +53,7 @@ jobs: # libraries found. To build Scipy from sources, BLAS & LAPACK # libraries need to be installed. # - # {os: ubuntu-latest, dist: pp37-manylinux_x86_64}, - # {os: ubuntu-latest, dist: pp38-manylinux_x86_64}, # {os: ubuntu-latest, dist: pp39-manylinux_x86_64}, - # {os: ubuntu-latest, dist: pp37-manylinux_i686}, - # {os: ubuntu-latest, dist: pp38-manylinux_i686}, # {os: ubuntu-latest, dist: pp39-manylinux_i686}, # musllinux builds disabled because scipy isn't prebuilt and fails to build. @@ -74,27 +64,15 @@ jobs: # libraries found. To build Scipy from sources, BLAS & LAPACK # libraries need to be installed. # - # {os: ubuntu-latest, dist: cp36-musllinux_x86_64}, - # {os: ubuntu-latest, dist: cp37-musllinux_x86_64}, - # {os: ubuntu-latest, dist: cp38-musllinux_x86_64}, - # {os: ubuntu-latest, dist: cp39-musllinux_x86_64}, # {os: ubuntu-latest, dist: cp310-musllinux_x86_64}, - # {os: ubuntu-latest, dist: cp36-musllinux_i686}, - # {os: ubuntu-latest, dist: cp37-musllinux_i686}, - # {os: ubuntu-latest, dist: cp38-musllinux_i686}, - # {os: ubuntu-latest, dist: cp39-musllinux_i686}, # {os: ubuntu-latest, dist: cp310-musllinux_i686}, - {os: macos-14, dist: cp38-macosx_x86_64, macosarch: x86_64}, - {os: macos-14, dist: cp39-macosx_x86_64, macosarch: x86_64}, {os: macos-14, dist: cp310-macosx_x86_64, macosarch: x86_64}, {os: macos-14, dist: cp311-macosx_x86_64, macosarch: x86_64}, {os: macos-14, dist: cp312-macosx_x86_64, macosarch: x86_64}, {os: macos-14, dist: cp313-macosx_x86_64, macosarch: x86_64}, {os: macos-14, dist: cp314-macosx_x86_64, macosarch: x86_64}, - {os: macos-14, dist: cp38-macosx_arm64, macosarch: arm64}, - {os: macos-14, dist: cp39-macosx_arm64, macosarch: arm64}, {os: macos-14, dist: cp310-macosx_arm64, macosarch: arm64}, {os: macos-14, dist: cp311-macosx_arm64, macosarch: arm64}, {os: macos-14, dist: cp312-macosx_arm64, macosarch: arm64}, @@ -112,28 +90,14 @@ jobs: # BLAS and LAPACK by setting the environment variables # NPY_BLAS_ORDER="" and NPY_LAPACK_ORDER="" before building NumPy. # - # {os: macOS-10.15, dist: pp37-macosx_x86_64}, - # {os: macOS-10.15, dist: pp38-macosx_x86_64}, # {os: macOS-10.15, dist: pp39-macosx_x86_64}, - {os: windows-2025, dist: cp38-win_amd64}, - {os: windows-2025, dist: cp39-win_amd64}, {os: windows-2025, dist: cp310-win_amd64}, {os: windows-2025, dist: cp311-win_amd64}, {os: windows-2025, dist: cp312-win_amd64}, {os: windows-2025, dist: cp313-win_amd64}, {os: windows-2025, dist: cp314-win_amd64}, - # cp38-win32 and cp39-win32 disabled because scipy fails to build. - # - # The actual error seen in github actions: - # - # Need python for 64-bit, but found 32-bit - # ..\..\meson.build:82:0: ERROR: Python dependency not found - # - #{os: windows-2025, dist: cp38-win32}, - #{os: windows-2025, dist: cp39-win32}, - # cp310-win32 disabled because numpy isn't prebuilt and fails to build. # # The actual error seen in github actions: @@ -254,7 +218,7 @@ jobs: steps: - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1 - run: cmake . - - run: make stim -j 2 + - run: make stim -j - run: echo -e "H 0 \n CNOT 0 1 \n M 0 1" | out/stim --sample build_bazel: runs-on: ubuntu-24.04 @@ -324,7 +288,7 @@ jobs: steps: - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1 - run: cmake . - - run: make libstim -j 2 + - run: make libstim -j - run: echo -e '#include "stim.h"\nint main(int argc,const char **argv) {return !stim::find_bool_argument("test", argc, argv);}' > test.cc - run: g++ -std=c++20 test.cc out/libstim.a -I src - run: ./a.out test @@ -334,7 +298,7 @@ jobs: - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1 - run: mkdir install_dir - run: cmake . -DCMAKE_INSTALL_PREFIX=install_dir - - run: make -j 2 + - run: make -j - run: make install - run: echo -e '#include "stim.h"\nint main(int argc,const char **argv) {return !stim::find_bool_argument("test", argc, argv);}' > test.cc - run: g++ -std=c++20 test.cc install_dir/lib/libstim.a -I install_dir/include @@ -356,7 +320,7 @@ jobs: steps: - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1 - run: cmake . -DSIMD_WIDTH=${{ matrix.simd_width }} - - run: make stim_perf -j 2 + - run: make stim_perf -j - run: out/stim_perf test: runs-on: ubuntu-24.04 @@ -373,7 +337,7 @@ jobs: make sudo make install - run: cmake . -DSIMD_WIDTH=${{ matrix.simd_width }} - - run: make stim_test -j 2 + - run: make stim_test -j - run: out/stim_test test_o3: runs-on: ubuntu-24.04 @@ -387,7 +351,7 @@ jobs: make sudo make install - run: cmake . -DSIMD_WIDTH=256 - - run: make stim_test_o3 -j 2 + - run: make stim_test_o3 -j - run: out/stim_test_o3 test_generated_docs_are_fresh: runs-on: ubuntu-24.04 From 53b00fb2d1a250e244de7316cac83b87d608d1cd Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Mon, 19 Jan 2026 20:45:53 -0800 Subject: [PATCH 02/16] getconf _NPROCESSORS_ONLN --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e484d428..9412fe24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -218,7 +218,7 @@ jobs: steps: - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1 - run: cmake . - - run: make stim -j + - run: make stim -j $(getconf _NPROCESSORS_ONLN) - run: echo -e "H 0 \n CNOT 0 1 \n M 0 1" | out/stim --sample build_bazel: runs-on: ubuntu-24.04 @@ -288,7 +288,7 @@ jobs: steps: - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1 - run: cmake . - - run: make libstim -j + - run: make libstim -j $(getconf _NPROCESSORS_ONLN) - run: echo -e '#include "stim.h"\nint main(int argc,const char **argv) {return !stim::find_bool_argument("test", argc, argv);}' > test.cc - run: g++ -std=c++20 test.cc out/libstim.a -I src - run: ./a.out test @@ -298,7 +298,7 @@ jobs: - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1 - run: mkdir install_dir - run: cmake . -DCMAKE_INSTALL_PREFIX=install_dir - - run: make -j + - run: make -j $(getconf _NPROCESSORS_ONLN) - run: make install - run: echo -e '#include "stim.h"\nint main(int argc,const char **argv) {return !stim::find_bool_argument("test", argc, argv);}' > test.cc - run: g++ -std=c++20 test.cc install_dir/lib/libstim.a -I install_dir/include @@ -320,7 +320,7 @@ jobs: steps: - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1 - run: cmake . -DSIMD_WIDTH=${{ matrix.simd_width }} - - run: make stim_perf -j + - run: make stim_perf -j $(getconf _NPROCESSORS_ONLN) - run: out/stim_perf test: runs-on: ubuntu-24.04 @@ -337,7 +337,7 @@ jobs: make sudo make install - run: cmake . -DSIMD_WIDTH=${{ matrix.simd_width }} - - run: make stim_test -j + - run: make stim_test -j $(getconf _NPROCESSORS_ONLN) - run: out/stim_test test_o3: runs-on: ubuntu-24.04 @@ -351,7 +351,7 @@ jobs: make sudo make install - run: cmake . -DSIMD_WIDTH=256 - - run: make stim_test_o3 -j + - run: make stim_test_o3 -j $(getconf _NPROCESSORS_ONLN) - run: out/stim_test_o3 test_generated_docs_are_fresh: runs-on: ubuntu-24.04 From b6717ff110b7bed25979beedccafb41778eb6145 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Mon, 19 Jan 2026 22:51:31 -0800 Subject: [PATCH 03/16] try a custom build --- glue/python/src/stim_custom_setup/__init__.py | 361 ++++++++++++++++++ pyproject.toml | 3 +- setup.py | 133 ------- src/stim/stabilizers/clifford_string.h | 2 +- 4 files changed, 364 insertions(+), 135 deletions(-) create mode 100644 glue/python/src/stim_custom_setup/__init__.py delete mode 100644 setup.py diff --git a/glue/python/src/stim_custom_setup/__init__.py b/glue/python/src/stim_custom_setup/__init__.py new file mode 100644 index 00000000..e94cbd04 --- /dev/null +++ b/glue/python/src/stim_custom_setup/__init__.py @@ -0,0 +1,361 @@ +"""This file is responsible for creating stim wheels. + +Basically, it plays the role that "setup.py" plays in most packages. + +Why does it exist? Because I got sick and tired of trying to get +setuptools to perform parallel builds and to lay out the wheel +files in the exact way that I wanted. +""" + +import base64 +import hashlib +import multiprocessing +import os +import pathlib +import platform +import subprocess +import sys +import sysconfig +import tempfile +import zipfile + +import glob +from typing import Literal, Tuple, Any + +import pybind11 + +__version__ = '1.16.dev0' + + +def build_object_file(args: dict[Literal['src_path', 'compiler', 'temp_dir', 'module_name', 'flags'], Any]): + src_path: str = args['src_path'] + compiler: str = args['compiler'] + temp_dir: pathlib.Path = args['temp_dir'] + module_name: str = args['module_name'] + flags: tuple[str, ...] = args['flags'] + + out_path = str(temp_dir / src_path) + ".o" + pathlib.Path(out_path).parent.mkdir(parents=True, exist_ok=True) + compiler_args = [ + compiler, + "-c", src_path, + "-o", out_path, + "-Isrc", + f"-I{pybind11.get_include()}", + f"-I{sysconfig.get_path('include')}", + "-Wall", + "-fPIC", + "-std=c++20", + "-fno-strict-aliasing", + "-O3", + "-g0", + "-DNDEBUG", + f"-DVERSION_INFO={__version__}", + f"-DSTIM_PYBIND11_MODULE_NAME={module_name}", + *flags, + ] + print(" ".join(compiler_args)) + subprocess.check_call(compiler_args, stderr=sys.stderr, stdout=sys.stdout) + return out_path + + +def link_shared_object(args: dict[Literal['src_paths', 'linker', 'temp_dir', 'module_name'], Any]): + src_paths: tuple[str, ...] = args['src_paths'] + linker: str = args['linker'] + temp_dir: pathlib.Path = args['temp_dir'] + module_name: str = args['module_name'] + + object_paths = [str(pathlib.Path(temp_dir) / src_path) + ".o" for src_path in src_paths] + linker_args = [ + linker, + "-shared", + f"-DVERSION_INFO={__version__}", + "-o", str(temp_dir / f"{module_name}.so"), + *[str(e) for e in object_paths], + ] + print(" ".join(linker_args)) + subprocess.check_call(linker_args, stderr=sys.stderr, stdout=sys.stdout) + + +def get_wheel_tag() -> str: + python_tag = { + 'cpython': f'cp{sys.version_info.major}{sys.version_info.minor}', + }.get(sys.implementation.name) + if python_tag is None: + raise NotImplementedError(f"Don't know package tag for {sys.implementation.name=}") + + abi_tag = python_tag + if sysconfig.get_config_var('Py_GIL_DISABLED'): + abi_tag += 't' + if sysconfig.get_config_var('Py_DEBUG'): + abi_tag += 'd' + + mac_ver = platform.mac_ver()[0] + if mac_ver: + mac_ver = mac_ver.split('.') + else: + mac_ver = '?', '?' + plat_tag = { + ( 'linux', 'x86_64' ): 'linux_x86_64', + ( 'linux', 'amd64' ): 'linux_x86_64', + ( 'linux', 'aarch64'): 'linux_aarch64', + ( 'linux', 'arm64' ): 'linux_arm64', + ( 'darwin', 'x86_64' ): f'macosx_{mac_ver[0]}_{mac_ver[1]}_x86_64', + ( 'darwin', 'amd64' ): f'macosx_{mac_ver[0]}_{mac_ver[1]}_x86_64', + ( 'darwin', 'aarch64'): f'macosx_{mac_ver[0]}_{mac_ver[1]}_aarch64', + ( 'darwin', 'arm64' ): f'macosx_{mac_ver[0]}_{mac_ver[1]}_arm64', + ('windows', 'x86_64' ): 'win_amd64', + ('windows', 'amd64' ): 'win_amd64', + ('windows', 'aarch64'): 'aarch64', + ('windows', 'arm64' ): 'arm64', + ('windows', '????' ): 'win32', + }.get((platform.system().lower(), platform.machine().lower())) + if plat_tag is None: + raise NotImplementedError(f"Don't know platform tag for {platform.system()=} {platform.machine()=}") + + return f"{python_tag}-{abi_tag}-{plat_tag}" + + +def get_content_hash(content: bytes) -> str: + digest = hashlib.sha256(content).digest() + hash_str = base64.urlsafe_b64encode(digest).decode().rstrip("=") + return f"sha256={hash_str},{len(content)}" + + +def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): + pool = multiprocessing.Pool() + with tempfile.TemporaryDirectory() as temp_dir: + temp_dir = pathlib.Path(temp_dir) + + # Collect source files. + ALL_SOURCE_FILES = glob.glob("src/**/*.cc", recursive=True) + MUX_SOURCE_FILES = glob.glob("src/**/march.pybind.cc", recursive=True) + TEST_FILES = glob.glob("src/**/*.test.cc", recursive=True) + PERF_FILES = glob.glob("src/**/*.perf.cc", recursive=True) + MAIN_FILES = glob.glob("src/**/main.cc", recursive=True) + HEADER_FILES = glob.glob("src/**/*.h", recursive=True) + glob.glob("src/**/*.inl", recursive=True) + RELEVANT_SOURCE_FILES = sorted(set(ALL_SOURCE_FILES) - set(TEST_FILES + PERF_FILES + MAIN_FILES + MUX_SOURCE_FILES)) + + # Determine the compiler to use. + compiler = None + if compiler is None and config_settings is not None: + compiler = config_settings.get("compiler", None) + if compiler is None: + compiler = os.environ.get('CXX', None) + if compiler is None: + if platform.system().startswith('Win'): + compiler = 'cl.exe' + else: + compiler = 'g++' + + # Determine the linker to use. + linker = None + if linker is None and config_settings is not None: + linker = config_settings.get("linker", None) + if linker is None: + linker = compiler + + # Plan out compiler and linker commands. + configs = { + '_detect_machine_architecture': ((), MUX_SOURCE_FILES), + '_stim_polyfill': ((), RELEVANT_SOURCE_FILES), + '_stim_sse2': (('-msse2', '-mno-avx2',), RELEVANT_SOURCE_FILES), + } + compile_commands = [] + link_commands = [] + for name, (flags, files) in configs.items(): + for e in files: + compile_commands.append({ + 'src_path': e, + 'compiler': compiler, + 'temp_dir': temp_dir / name, + 'module_name': name, + 'flags': flags, + }) + link_commands.append({ + 'src_paths': files, + 'linker': linker, + 'temp_dir': temp_dir / name, + 'module_name': name, + }) + + # Perform compilation and linking. + _ = list(pool.map(build_object_file, compile_commands)) + _ = list(pool.map(link_shared_object, link_commands)) + + # Create the wheel file. + files: dict[str, bytes] = {} + + for name in configs.keys(): + with open(temp_dir / name / f'{name}.so', 'rb') as f: + files[f'stim/{name}{sysconfig.get_config_var('EXT_SUFFIX')}'] = f.read() + + dist_info_dir = f"stim-{__version__}.dist-info" + files[f'{dist_info_dir}/entry_points.txt'] = """ +[console_scripts] +stim = stim._main_argv:main_argv +""".strip().encode('UTF-8') + + with open('LICENSE', 'rb') as f: + files[f'{dist_info_dir}/license/LICENSE'] = f.read() + + with open('glue/python/README.md', encoding='UTF-8') as f: + files[f'{dist_info_dir}/METADATA'] = (""" +Metadata-Version: 2.4 +Name: stim +Version: 1.15.0 +Summary: A fast library for analyzing with quantum stabilizer circuits. +Home-page: https://github.com/quantumlib/stim +Author: Craig Gidney +Author-email: craig.gidney@gmail.com +License: Apache 2 +Requires-Python: >=3.10.0 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: numpy + +""".lstrip() + f.read()).encode('UTF-8') + + files[f'{dist_info_dir}/top_level.txt'] = "stim".encode('UTF-8') + + files[f'{dist_info_dir}/WHEEL'] = f""" +Wheel-Version: 1.0 +Generator: stim_custom_setup +Root-Is-Purelib: false +Tag: {get_wheel_tag()} +""".lstrip().encode('UTF-8') + + for file in pathlib.Path("glue/python/src/stim").iterdir(): + with open(file, 'rb') as f: + files[f'stim/{file.name}'] = f.read() + + records = [] + for k, v in files.items(): + records.append(f"{k},{get_content_hash(v)}") + records.append(f"{dist_info_dir}/RECORD,,") + files[f'{dist_info_dir}/RECORD'] = "\n".join(records).encode('UTF-8') + + wheel_name = f'stim-{__version__}-{get_wheel_tag()}.whl' + with zipfile.ZipFile(wheel_name, 'w', compression=zipfile.ZIP_DEFLATED) as wheel: + for k, v in files.items(): + wheel.writestr(k, v) + + +if __name__ == '__main__': + build_wheel(None) +# +# if platform.system().startswith('Win'): +# common_compile_args = [ +# '/std:c++20', +# '/O2', +# f'/DVERSION_INFO={__version__}', +# ] +# arch_avx = ['/arch:AVX2'] +# arch_sse = ['/arch:SSE2'] +# arch_basic = [] +# else: +# common_compile_args = [ +# '-std=c++20', +# '-fno-strict-aliasing', +# '-O3', +# '-g0', +# f'-DVERSION_INFO={__version__}', +# ] +# arch_avx = ['-mavx2'] +# arch_sse = ['-msse2', '-mno-avx2'] +# arch_basic = [] +# +# class BuildExtParallel(build_ext): +# def finalize_options(self): +# super().finalize_options() +# if self.parallel is None: +# try: +# self.parallel = multiprocessing.cpu_count() +# except (ImportError, NotImplementedError): +# self.parallel = 1 +# print(f"-- Building with {self.parallel} parallel jobs --") +# +# stim_detect_machine_architecture = Extension( +# 'stim._detect_machine_architecture', +# sources=MUX_SOURCE_FILES, +# include_dirs=[pybind11.get_include(), "src"], +# language='c++', +# extra_compile_args=[ +# *common_compile_args, +# *arch_basic, +# ], +# ) +# stim_polyfill = Extension( +# 'stim._stim_polyfill', +# sources=RELEVANT_SOURCE_FILES, +# include_dirs=[pybind11.get_include(), "src"], +# language='c++', +# extra_compile_args=[ +# *common_compile_args, +# *arch_basic, +# '-DSTIM_PYBIND11_MODULE_NAME=_stim_polyfill', +# ], +# ) +# stim_sse2 = Extension( +# 'stim._stim_sse2', +# sources=RELEVANT_SOURCE_FILES, +# include_dirs=[pybind11.get_include(), "src"], +# language='c++', +# extra_compile_args=[ +# *common_compile_args, +# *arch_sse, +# '-DSTIM_PYBIND11_MODULE_NAME=_stim_sse2', +# ], +# ) +# +# # NOTE: disabled until https://github.com/quantumlib/Stim/issues/432 is fixed +# # stim_avx2 = Extension( +# # 'stim._stim_avx2', +# # sources=RELEVANT_SOURCE_FILES, +# # include_dirs=[pybind11.get_include(), "src"], +# # language='c++', +# # extra_compile_args=[ +# # *common_compile_args, +# # *arch_avx, +# # '-DSTIM_PYBIND11_MODULE_NAME=_stim_avx2', +# # ], +# # ) +# +# with open('glue/python/README.md', encoding='UTF-8') as f: +# long_description = f.read() +# +# def _get_extensions(): +# archs=["x86", "i686", "i386", "amd64"] +# if any(_ext in platform.processor().lower() for _ext in archs): +# # NOTE: disabled until https://github.com/quantumlib/Stim/issues/432 is fixed +# # stim_avx2, +# return [stim_detect_machine_architecture, stim_polyfill, +# # stim_avx2, +# stim_sse2] +# else: +# return [stim_detect_machine_architecture, stim_polyfill] +# +# setup( +# name='stim', +# cmdclass={'build_ext': BuildExtParallel}, +# version=__version__, +# author='Craig Gidney', +# author_email='craig.gidney@gmail.com', +# url='https://github.com/quantumlib/stim', +# license='Apache 2', +# description='A fast library for analyzing with quantum stabilizer circuits.', +# long_description=long_description, +# long_description_content_type='text/markdown', +# ext_modules=_get_extensions(), +# python_requires='>=3.6.0', +# packages=['stim'], +# package_dir={'stim': 'glue/python/src/stim'}, +# package_data={'': [*HEADER_FILES, 'glue/python/src/stim/__init__.pyi', 'glue/python/README.md', 'pyproject.toml']}, +# include_package_data=True, +# install_requires=['numpy'], +# entry_points={ +# 'console_scripts': ['stim=stim._main_argv:main_argv'], +# }, +# # Needed on Windows to avoid the default `build` colliding with Bazel's `BUILD`. +# options={'build': {'build_base': 'python_build_stim'}}, +# ) diff --git a/pyproject.toml b/pyproject.toml index 408cd00b..d8325843 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,4 @@ [build-system] requires = ["setuptools", "wheel", "pybind11~=2.11.1"] -build-backend = "setuptools.build_meta" +build-backend = "stim_custom_setup" +backend-path = ["glue/python/src"] diff --git a/setup.py b/setup.py deleted file mode 100644 index 238431d1..00000000 --- a/setup.py +++ /dev/null @@ -1,133 +0,0 @@ -# Copyright 2021 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import platform - -from setuptools import setup, Extension -import glob -import pybind11 - -ALL_SOURCE_FILES = glob.glob("src/**/*.cc", recursive=True) -MUX_SOURCE_FILES = glob.glob("src/**/march.pybind.cc", recursive=True) -TEST_FILES = glob.glob("src/**/*.test.cc", recursive=True) -PERF_FILES = glob.glob("src/**/*.perf.cc", recursive=True) -MAIN_FILES = glob.glob("src/**/main.cc", recursive=True) -HEADER_FILES = glob.glob("src/**/*.h", recursive=True) + glob.glob("src/**/*.inl", recursive=True) -RELEVANT_SOURCE_FILES = sorted(set(ALL_SOURCE_FILES) - set(TEST_FILES + PERF_FILES + MAIN_FILES + MUX_SOURCE_FILES)) - -__version__ = '1.16.dev0' - -if platform.system().startswith('Win'): - common_compile_args = [ - '/std:c++20', - '/O2', - f'/DVERSION_INFO={__version__}', - ] - arch_avx = ['/arch:AVX2'] - arch_sse = ['/arch:SSE2'] - arch_basic = [] -else: - common_compile_args = [ - '-std=c++20', - '-fno-strict-aliasing', - '-O3', - '-g0', - f'-DVERSION_INFO={__version__}', - ] - arch_avx = ['-mavx2'] - arch_sse = ['-msse2', '-mno-avx2'] - arch_basic = [] - -stim_detect_machine_architecture = Extension( - 'stim._detect_machine_architecture', - sources=MUX_SOURCE_FILES, - include_dirs=[pybind11.get_include(), "src"], - language='c++', - extra_compile_args=[ - *common_compile_args, - *arch_basic, - ], -) -stim_polyfill = Extension( - 'stim._stim_polyfill', - sources=RELEVANT_SOURCE_FILES, - include_dirs=[pybind11.get_include(), "src"], - language='c++', - extra_compile_args=[ - *common_compile_args, - *arch_basic, - '-DSTIM_PYBIND11_MODULE_NAME=_stim_polyfill', - ], -) -stim_sse2 = Extension( - 'stim._stim_sse2', - sources=RELEVANT_SOURCE_FILES, - include_dirs=[pybind11.get_include(), "src"], - language='c++', - extra_compile_args=[ - *common_compile_args, - *arch_sse, - '-DSTIM_PYBIND11_MODULE_NAME=_stim_sse2', - ], -) - -# NOTE: disabled until https://github.com/quantumlib/Stim/issues/432 is fixed -# stim_avx2 = Extension( -# 'stim._stim_avx2', -# sources=RELEVANT_SOURCE_FILES, -# include_dirs=[pybind11.get_include(), "src"], -# language='c++', -# extra_compile_args=[ -# *common_compile_args, -# *arch_avx, -# '-DSTIM_PYBIND11_MODULE_NAME=_stim_avx2', -# ], -# ) - -with open('glue/python/README.md', encoding='UTF-8') as f: - long_description = f.read() - -def _get_extensions(): - archs=["x86", "i686", "i386", "amd64"] - if any(_ext in platform.processor().lower() for _ext in archs): - # NOTE: disabled until https://github.com/quantumlib/Stim/issues/432 is fixed - # stim_avx2, - return [stim_detect_machine_architecture, stim_polyfill, - # stim_avx2, - stim_sse2] - else: - return [stim_detect_machine_architecture, stim_polyfill] - -setup( - name='stim', - version=__version__, - author='Craig Gidney', - author_email='craig.gidney@gmail.com', - url='https://github.com/quantumlib/stim', - license='Apache 2', - description='A fast library for analyzing with quantum stabilizer circuits.', - long_description=long_description, - long_description_content_type='text/markdown', - ext_modules=_get_extensions(), - python_requires='>=3.6.0', - packages=['stim'], - package_dir={'stim': 'glue/python/src/stim'}, - package_data={'': [*HEADER_FILES, 'glue/python/src/stim/__init__.pyi', 'glue/python/README.md', 'pyproject.toml']}, - include_package_data=True, - install_requires=['numpy'], - entry_points={ - 'console_scripts': ['stim=stim._main_argv:main_argv'], - }, - # Needed on Windows to avoid the default `build` colliding with Bazel's `BUILD`. - options={'build': {'build_base': 'python_build_stim'}}, -) diff --git a/src/stim/stabilizers/clifford_string.h b/src/stim/stabilizers/clifford_string.h index 8ca44c7b..8df3f0a2 100644 --- a/src/stim/stabilizers/clifford_string.h +++ b/src/stim/stabilizers/clifford_string.h @@ -362,7 +362,7 @@ struct CliffordString { for (size_t k = 0; k < x_signs.num_simd_words; k++) { auto delta = word_at(k); CliffordWord> total{}; - for (size_t step = 0; step < power; step++) { + for (size_t step = 0; step < (size_t)power; step++) { total = total * delta; } set_word_at(k, total); From 9be57011dc74e62cb7d6d75d9402a18a466109b8 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Tue, 20 Jan 2026 00:45:44 -0800 Subject: [PATCH 04/16] moved --- dev/clean_build_files.sh | 2 +- dev/overwrite_dev_versions_with_date.py | 4 +- glue/python/src/stim_custom_setup/__init__.py | 118 +----------------- 3 files changed, 5 insertions(+), 119 deletions(-) diff --git a/dev/clean_build_files.sh b/dev/clean_build_files.sh index 9f7fcdf8..52f7887e 100755 --- a/dev/clean_build_files.sh +++ b/dev/clean_build_files.sh @@ -2,7 +2,7 @@ set -e ######################################################################### -# Deletes files created by cmake, python setup.py, and other build steps. +# Deletes files created by cmake, pip install ., and other build steps. ######################################################################### # Get to this script's git repo root. diff --git a/dev/overwrite_dev_versions_with_date.py b/dev/overwrite_dev_versions_with_date.py index e031ffed..b9c192c5 100755 --- a/dev/overwrite_dev_versions_with_date.py +++ b/dev/overwrite_dev_versions_with_date.py @@ -23,7 +23,7 @@ def main(): # Generate dev version starting from major.minor version. # (Requires the existing version to have a 'dev' suffix.) # (Uses the timestamp of the HEAD commit, to ensure consistency when run multiple times.) - with open('setup.py') as f: + with open('glue/python/src/stim_custom_setup/__init__.py') as f: maj_min_version_line, = [line for line in f.read().splitlines() if re.match("^__version__ = '[^']+'", line)] maj_version, min_version, patch = maj_min_version_line.split()[-1].strip("'").split('.') if 'dev' not in patch: @@ -33,7 +33,7 @@ def main(): # Overwrite existing versions. package_setup_files = [ - "setup.py", + "glue/python/src/stim_custom_setup/__init__.py", "glue/cirq/setup.py", "glue/cirq/stimcirq/__init__.py", "glue/zx/stimzx/__init__.py", diff --git a/glue/python/src/stim_custom_setup/__init__.py b/glue/python/src/stim_custom_setup/__init__.py index e94cbd04..5860dbec 100644 --- a/glue/python/src/stim_custom_setup/__init__.py +++ b/glue/python/src/stim_custom_setup/__init__.py @@ -160,6 +160,8 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): '_detect_machine_architecture': ((), MUX_SOURCE_FILES), '_stim_polyfill': ((), RELEVANT_SOURCE_FILES), '_stim_sse2': (('-msse2', '-mno-avx2',), RELEVANT_SOURCE_FILES), + # NOTE: disabled until https://github.com/quantumlib/Stim/issues/432 is fixed + # '_stim_avx': (('-msse2', '-mavx2',), RELEVANT_SOURCE_FILES), } compile_commands = [] link_commands = [] @@ -243,119 +245,3 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): if __name__ == '__main__': build_wheel(None) -# -# if platform.system().startswith('Win'): -# common_compile_args = [ -# '/std:c++20', -# '/O2', -# f'/DVERSION_INFO={__version__}', -# ] -# arch_avx = ['/arch:AVX2'] -# arch_sse = ['/arch:SSE2'] -# arch_basic = [] -# else: -# common_compile_args = [ -# '-std=c++20', -# '-fno-strict-aliasing', -# '-O3', -# '-g0', -# f'-DVERSION_INFO={__version__}', -# ] -# arch_avx = ['-mavx2'] -# arch_sse = ['-msse2', '-mno-avx2'] -# arch_basic = [] -# -# class BuildExtParallel(build_ext): -# def finalize_options(self): -# super().finalize_options() -# if self.parallel is None: -# try: -# self.parallel = multiprocessing.cpu_count() -# except (ImportError, NotImplementedError): -# self.parallel = 1 -# print(f"-- Building with {self.parallel} parallel jobs --") -# -# stim_detect_machine_architecture = Extension( -# 'stim._detect_machine_architecture', -# sources=MUX_SOURCE_FILES, -# include_dirs=[pybind11.get_include(), "src"], -# language='c++', -# extra_compile_args=[ -# *common_compile_args, -# *arch_basic, -# ], -# ) -# stim_polyfill = Extension( -# 'stim._stim_polyfill', -# sources=RELEVANT_SOURCE_FILES, -# include_dirs=[pybind11.get_include(), "src"], -# language='c++', -# extra_compile_args=[ -# *common_compile_args, -# *arch_basic, -# '-DSTIM_PYBIND11_MODULE_NAME=_stim_polyfill', -# ], -# ) -# stim_sse2 = Extension( -# 'stim._stim_sse2', -# sources=RELEVANT_SOURCE_FILES, -# include_dirs=[pybind11.get_include(), "src"], -# language='c++', -# extra_compile_args=[ -# *common_compile_args, -# *arch_sse, -# '-DSTIM_PYBIND11_MODULE_NAME=_stim_sse2', -# ], -# ) -# -# # NOTE: disabled until https://github.com/quantumlib/Stim/issues/432 is fixed -# # stim_avx2 = Extension( -# # 'stim._stim_avx2', -# # sources=RELEVANT_SOURCE_FILES, -# # include_dirs=[pybind11.get_include(), "src"], -# # language='c++', -# # extra_compile_args=[ -# # *common_compile_args, -# # *arch_avx, -# # '-DSTIM_PYBIND11_MODULE_NAME=_stim_avx2', -# # ], -# # ) -# -# with open('glue/python/README.md', encoding='UTF-8') as f: -# long_description = f.read() -# -# def _get_extensions(): -# archs=["x86", "i686", "i386", "amd64"] -# if any(_ext in platform.processor().lower() for _ext in archs): -# # NOTE: disabled until https://github.com/quantumlib/Stim/issues/432 is fixed -# # stim_avx2, -# return [stim_detect_machine_architecture, stim_polyfill, -# # stim_avx2, -# stim_sse2] -# else: -# return [stim_detect_machine_architecture, stim_polyfill] -# -# setup( -# name='stim', -# cmdclass={'build_ext': BuildExtParallel}, -# version=__version__, -# author='Craig Gidney', -# author_email='craig.gidney@gmail.com', -# url='https://github.com/quantumlib/stim', -# license='Apache 2', -# description='A fast library for analyzing with quantum stabilizer circuits.', -# long_description=long_description, -# long_description_content_type='text/markdown', -# ext_modules=_get_extensions(), -# python_requires='>=3.6.0', -# packages=['stim'], -# package_dir={'stim': 'glue/python/src/stim'}, -# package_data={'': [*HEADER_FILES, 'glue/python/src/stim/__init__.pyi', 'glue/python/README.md', 'pyproject.toml']}, -# include_package_data=True, -# install_requires=['numpy'], -# entry_points={ -# 'console_scripts': ['stim=stim._main_argv:main_argv'], -# }, -# # Needed on Windows to avoid the default `build` colliding with Bazel's `BUILD`. -# options={'build': {'build_base': 'python_build_stim'}}, -# ) From ea2304e348d1522da76524d07e76ba8a65ab1566 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Tue, 20 Jan 2026 00:56:39 -0800 Subject: [PATCH 05/16] respect wheel directory --- glue/python/src/stim_custom_setup/__init__.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/glue/python/src/stim_custom_setup/__init__.py b/glue/python/src/stim_custom_setup/__init__.py index 5860dbec..28a8a559 100644 --- a/glue/python/src/stim_custom_setup/__init__.py +++ b/glue/python/src/stim_custom_setup/__init__.py @@ -47,6 +47,7 @@ def build_object_file(args: dict[Literal['src_path', 'compiler', 'temp_dir', 'mo "-fPIC", "-std=c++20", "-fno-strict-aliasing", + "-fvisibility=hidden", "-O3", "-g0", "-DNDEBUG", @@ -123,6 +124,8 @@ def get_content_hash(content: bytes) -> str: def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): + wheel_path = pathlib.Path(wheel_directory) / f'stim-{__version__}-{get_wheel_tag()}.whl' + pool = multiprocessing.Pool() with tempfile.TemporaryDirectory() as temp_dir: temp_dir = pathlib.Path(temp_dir) @@ -190,7 +193,7 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): for name in configs.keys(): with open(temp_dir / name / f'{name}.so', 'rb') as f: - files[f'stim/{name}{sysconfig.get_config_var('EXT_SUFFIX')}'] = f.read() + files[f'stim/{name}' + sysconfig.get_config_var('EXT_SUFFIX')] = f.read() dist_info_dir = f"stim-{__version__}.dist-info" files[f'{dist_info_dir}/entry_points.txt'] = """ @@ -237,11 +240,10 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): records.append(f"{dist_info_dir}/RECORD,,") files[f'{dist_info_dir}/RECORD'] = "\n".join(records).encode('UTF-8') - wheel_name = f'stim-{__version__}-{get_wheel_tag()}.whl' - with zipfile.ZipFile(wheel_name, 'w', compression=zipfile.ZIP_DEFLATED) as wheel: + with zipfile.ZipFile(wheel_path, 'w', compression=zipfile.ZIP_DEFLATED) as wheel: for k, v in files.items(): wheel.writestr(k, v) if __name__ == '__main__': - build_wheel(None) + build_wheel('') From 956447f7e60184b2a655e5a96d6da492dbb35631 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Tue, 20 Jan 2026 01:10:18 -0800 Subject: [PATCH 06/16] return --- glue/python/src/stim_custom_setup/__init__.py | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/glue/python/src/stim_custom_setup/__init__.py b/glue/python/src/stim_custom_setup/__init__.py index 28a8a559..54d9b796 100644 --- a/glue/python/src/stim_custom_setup/__init__.py +++ b/glue/python/src/stim_custom_setup/__init__.py @@ -27,7 +27,7 @@ __version__ = '1.16.dev0' -def build_object_file(args: dict[Literal['src_path', 'compiler', 'temp_dir', 'module_name', 'flags'], Any]): +def _build_object_file(args: dict[Literal['src_path', 'compiler', 'temp_dir', 'module_name', 'flags'], Any]): src_path: str = args['src_path'] compiler: str = args['compiler'] temp_dir: pathlib.Path = args['temp_dir'] @@ -60,7 +60,7 @@ def build_object_file(args: dict[Literal['src_path', 'compiler', 'temp_dir', 'mo return out_path -def link_shared_object(args: dict[Literal['src_paths', 'linker', 'temp_dir', 'module_name'], Any]): +def _link_shared_object(args: dict[Literal['src_paths', 'linker', 'temp_dir', 'module_name'], Any]): src_paths: tuple[str, ...] = args['src_paths'] linker: str = args['linker'] temp_dir: pathlib.Path = args['temp_dir'] @@ -78,7 +78,7 @@ def link_shared_object(args: dict[Literal['src_paths', 'linker', 'temp_dir', 'mo subprocess.check_call(linker_args, stderr=sys.stderr, stdout=sys.stdout) -def get_wheel_tag() -> str: +def _get_wheel_tag() -> str: python_tag = { 'cpython': f'cp{sys.version_info.major}{sys.version_info.minor}', }.get(sys.implementation.name) @@ -117,14 +117,15 @@ def get_wheel_tag() -> str: return f"{python_tag}-{abi_tag}-{plat_tag}" -def get_content_hash(content: bytes) -> str: +def _get_content_hash(content: bytes) -> str: digest = hashlib.sha256(content).digest() hash_str = base64.urlsafe_b64encode(digest).decode().rstrip("=") return f"sha256={hash_str},{len(content)}" def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): - wheel_path = pathlib.Path(wheel_directory) / f'stim-{__version__}-{get_wheel_tag()}.whl' + wheel_name = f'stim-{__version__}-{_get_wheel_tag()}.whl' + wheel_path = pathlib.Path(wheel_directory) / wheel_name pool = multiprocessing.Pool() with tempfile.TemporaryDirectory() as temp_dir: @@ -185,8 +186,8 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): }) # Perform compilation and linking. - _ = list(pool.map(build_object_file, compile_commands)) - _ = list(pool.map(link_shared_object, link_commands)) + _ = list(pool.map(_build_object_file, compile_commands)) + _ = list(pool.map(_link_shared_object, link_commands)) # Create the wheel file. files: dict[str, bytes] = {} @@ -227,7 +228,7 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): Wheel-Version: 1.0 Generator: stim_custom_setup Root-Is-Purelib: false -Tag: {get_wheel_tag()} +Tag: {_get_wheel_tag()} """.lstrip().encode('UTF-8') for file in pathlib.Path("glue/python/src/stim").iterdir(): @@ -236,7 +237,7 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): records = [] for k, v in files.items(): - records.append(f"{k},{get_content_hash(v)}") + records.append(f"{k},{_get_content_hash(v)}") records.append(f"{dist_info_dir}/RECORD,,") files[f'{dist_info_dir}/RECORD'] = "\n".join(records).encode('UTF-8') @@ -244,6 +245,8 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): for k, v in files.items(): wheel.writestr(k, v) + return wheel_name + if __name__ == '__main__': build_wheel('') From d07a10a6c9061a169db1ca2a82f8360a39c18964 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Tue, 20 Jan 2026 01:23:40 -0800 Subject: [PATCH 07/16] Try forcing path --- glue/python/src/stim_custom_setup/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/glue/python/src/stim_custom_setup/__init__.py b/glue/python/src/stim_custom_setup/__init__.py index 54d9b796..03e5ffe2 100644 --- a/glue/python/src/stim_custom_setup/__init__.py +++ b/glue/python/src/stim_custom_setup/__init__.py @@ -185,6 +185,10 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): 'module_name': name, }) + # Without this line, `pip install` will fail when the + # multiprocessing method is set to `spawn` instead of `fork`. + os.environ["PYTHONPATH"] = os.pathsep.join(sys.path) + # Perform compilation and linking. _ = list(pool.map(_build_object_file, compile_commands)) _ = list(pool.map(_link_shared_object, link_commands)) From 0d5df1172dd8f851fae2a3e8bc43cb1094fee490 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Tue, 20 Jan 2026 01:26:24 -0800 Subject: [PATCH 08/16] prepool --- glue/python/src/stim_custom_setup/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/glue/python/src/stim_custom_setup/__init__.py b/glue/python/src/stim_custom_setup/__init__.py index 03e5ffe2..b3479007 100644 --- a/glue/python/src/stim_custom_setup/__init__.py +++ b/glue/python/src/stim_custom_setup/__init__.py @@ -127,6 +127,10 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): wheel_name = f'stim-{__version__}-{_get_wheel_tag()}.whl' wheel_path = pathlib.Path(wheel_directory) / wheel_name + # Without this line, `pip install` will fail when the + # multiprocessing method is set to `spawn` instead of `fork`. + os.environ["PYTHONPATH"] = os.pathsep.join(sys.path) + pool = multiprocessing.Pool() with tempfile.TemporaryDirectory() as temp_dir: temp_dir = pathlib.Path(temp_dir) @@ -185,10 +189,6 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): 'module_name': name, }) - # Without this line, `pip install` will fail when the - # multiprocessing method is set to `spawn` instead of `fork`. - os.environ["PYTHONPATH"] = os.pathsep.join(sys.path) - # Perform compilation and linking. _ = list(pool.map(_build_object_file, compile_commands)) _ = list(pool.map(_link_shared_object, link_commands)) From 328e607d2ef5bd636ef89cac715fe467182a3a29 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Tue, 20 Jan 2026 01:32:09 -0800 Subject: [PATCH 09/16] initializer --- glue/python/src/stim_custom_setup/__init__.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/glue/python/src/stim_custom_setup/__init__.py b/glue/python/src/stim_custom_setup/__init__.py index b3479007..ccc48f93 100644 --- a/glue/python/src/stim_custom_setup/__init__.py +++ b/glue/python/src/stim_custom_setup/__init__.py @@ -123,15 +123,20 @@ def _get_content_hash(content: bytes) -> str: return f"sha256={hash_str},{len(content)}" +def workaround_path_loss(parent_sys_path): + # Without this line, `pip install` will fail when the + # multiprocessing method is set to `spawn` instead of `fork`. + import sys + for p in parent_sys_path: + if p not in sys.path: + sys.path.append(p) + + def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): wheel_name = f'stim-{__version__}-{_get_wheel_tag()}.whl' wheel_path = pathlib.Path(wheel_directory) / wheel_name - # Without this line, `pip install` will fail when the - # multiprocessing method is set to `spawn` instead of `fork`. - os.environ["PYTHONPATH"] = os.pathsep.join(sys.path) - - pool = multiprocessing.Pool() + pool = multiprocessing.Pool(initializer=workaround_path_loss, initargs=sys.path) with tempfile.TemporaryDirectory() as temp_dir: temp_dir = pathlib.Path(temp_dir) From 3fa7141d29f0bd883915afaf4f8388c773419788 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Tue, 20 Jan 2026 01:39:47 -0800 Subject: [PATCH 10/16] tuple --- glue/python/src/stim_custom_setup/__init__.py | 85 ++++++++++--------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/glue/python/src/stim_custom_setup/__init__.py b/glue/python/src/stim_custom_setup/__init__.py index ccc48f93..b4848658 100644 --- a/glue/python/src/stim_custom_setup/__init__.py +++ b/glue/python/src/stim_custom_setup/__init__.py @@ -136,46 +136,45 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): wheel_name = f'stim-{__version__}-{_get_wheel_tag()}.whl' wheel_path = pathlib.Path(wheel_directory) / wheel_name - pool = multiprocessing.Pool(initializer=workaround_path_loss, initargs=sys.path) + # Collect source files. + ALL_SOURCE_FILES = glob.glob("src/**/*.cc", recursive=True) + MUX_SOURCE_FILES = glob.glob("src/**/march.pybind.cc", recursive=True) + TEST_FILES = glob.glob("src/**/*.test.cc", recursive=True) + PERF_FILES = glob.glob("src/**/*.perf.cc", recursive=True) + MAIN_FILES = glob.glob("src/**/main.cc", recursive=True) + HEADER_FILES = glob.glob("src/**/*.h", recursive=True) + glob.glob("src/**/*.inl", recursive=True) + RELEVANT_SOURCE_FILES = sorted(set(ALL_SOURCE_FILES) - set(TEST_FILES + PERF_FILES + MAIN_FILES + MUX_SOURCE_FILES)) + + # Determine the compiler to use. + compiler = None + if compiler is None and config_settings is not None: + compiler = config_settings.get("compiler", None) + if compiler is None: + compiler = os.environ.get('CXX', None) + if compiler is None: + if platform.system().startswith('Win'): + compiler = 'cl.exe' + else: + compiler = 'g++' + + # Determine the linker to use. + linker = None + if linker is None and config_settings is not None: + linker = config_settings.get("linker", None) + if linker is None: + linker = compiler + + # Plan out compiler and linker commands. + configs = { + '_detect_machine_architecture': ((), MUX_SOURCE_FILES), + '_stim_polyfill': ((), RELEVANT_SOURCE_FILES), + '_stim_sse2': (('-msse2', '-mno-avx2',), RELEVANT_SOURCE_FILES), + # NOTE: disabled until https://github.com/quantumlib/Stim/issues/432 is fixed + # '_stim_avx': (('-msse2', '-mavx2',), RELEVANT_SOURCE_FILES), + } + with tempfile.TemporaryDirectory() as temp_dir: temp_dir = pathlib.Path(temp_dir) - - # Collect source files. - ALL_SOURCE_FILES = glob.glob("src/**/*.cc", recursive=True) - MUX_SOURCE_FILES = glob.glob("src/**/march.pybind.cc", recursive=True) - TEST_FILES = glob.glob("src/**/*.test.cc", recursive=True) - PERF_FILES = glob.glob("src/**/*.perf.cc", recursive=True) - MAIN_FILES = glob.glob("src/**/main.cc", recursive=True) - HEADER_FILES = glob.glob("src/**/*.h", recursive=True) + glob.glob("src/**/*.inl", recursive=True) - RELEVANT_SOURCE_FILES = sorted(set(ALL_SOURCE_FILES) - set(TEST_FILES + PERF_FILES + MAIN_FILES + MUX_SOURCE_FILES)) - - # Determine the compiler to use. - compiler = None - if compiler is None and config_settings is not None: - compiler = config_settings.get("compiler", None) - if compiler is None: - compiler = os.environ.get('CXX', None) - if compiler is None: - if platform.system().startswith('Win'): - compiler = 'cl.exe' - else: - compiler = 'g++' - - # Determine the linker to use. - linker = None - if linker is None and config_settings is not None: - linker = config_settings.get("linker", None) - if linker is None: - linker = compiler - - # Plan out compiler and linker commands. - configs = { - '_detect_machine_architecture': ((), MUX_SOURCE_FILES), - '_stim_polyfill': ((), RELEVANT_SOURCE_FILES), - '_stim_sse2': (('-msse2', '-mno-avx2',), RELEVANT_SOURCE_FILES), - # NOTE: disabled until https://github.com/quantumlib/Stim/issues/432 is fixed - # '_stim_avx': (('-msse2', '-mavx2',), RELEVANT_SOURCE_FILES), - } compile_commands = [] link_commands = [] for name, (flags, files) in configs.items(): @@ -194,9 +193,13 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): 'module_name': name, }) - # Perform compilation and linking. - _ = list(pool.map(_build_object_file, compile_commands)) - _ = list(pool.map(_link_shared_object, link_commands)) + with multiprocessing.Pool( + initializer=workaround_path_loss, + initargs=([str(e) for e in sys.path],), + ) as pool: + # Perform compilation and linking. + _ = list(pool.map(_build_object_file, compile_commands)) + _ = list(pool.map(_link_shared_object, link_commands)) # Create the wheel file. files: dict[str, bytes] = {} From 7c67b5f795927b9bade105ccdfcf2158b89dc966 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Tue, 20 Jan 2026 02:49:53 -0800 Subject: [PATCH 11/16] flatter --- glue/python/src/stim_custom_setup/__init__.py | 166 +++++++++--------- 1 file changed, 81 insertions(+), 85 deletions(-) diff --git a/glue/python/src/stim_custom_setup/__init__.py b/glue/python/src/stim_custom_setup/__init__.py index b4848658..e4afacbc 100644 --- a/glue/python/src/stim_custom_setup/__init__.py +++ b/glue/python/src/stim_custom_setup/__init__.py @@ -8,6 +8,7 @@ """ import base64 +import glob import hashlib import multiprocessing import os @@ -17,67 +18,14 @@ import sys import sysconfig import tempfile +import time import zipfile -import glob -from typing import Literal, Tuple, Any - import pybind11 __version__ = '1.16.dev0' -def _build_object_file(args: dict[Literal['src_path', 'compiler', 'temp_dir', 'module_name', 'flags'], Any]): - src_path: str = args['src_path'] - compiler: str = args['compiler'] - temp_dir: pathlib.Path = args['temp_dir'] - module_name: str = args['module_name'] - flags: tuple[str, ...] = args['flags'] - - out_path = str(temp_dir / src_path) + ".o" - pathlib.Path(out_path).parent.mkdir(parents=True, exist_ok=True) - compiler_args = [ - compiler, - "-c", src_path, - "-o", out_path, - "-Isrc", - f"-I{pybind11.get_include()}", - f"-I{sysconfig.get_path('include')}", - "-Wall", - "-fPIC", - "-std=c++20", - "-fno-strict-aliasing", - "-fvisibility=hidden", - "-O3", - "-g0", - "-DNDEBUG", - f"-DVERSION_INFO={__version__}", - f"-DSTIM_PYBIND11_MODULE_NAME={module_name}", - *flags, - ] - print(" ".join(compiler_args)) - subprocess.check_call(compiler_args, stderr=sys.stderr, stdout=sys.stdout) - return out_path - - -def _link_shared_object(args: dict[Literal['src_paths', 'linker', 'temp_dir', 'module_name'], Any]): - src_paths: tuple[str, ...] = args['src_paths'] - linker: str = args['linker'] - temp_dir: pathlib.Path = args['temp_dir'] - module_name: str = args['module_name'] - - object_paths = [str(pathlib.Path(temp_dir) / src_path) + ".o" for src_path in src_paths] - linker_args = [ - linker, - "-shared", - f"-DVERSION_INFO={__version__}", - "-o", str(temp_dir / f"{module_name}.so"), - *[str(e) for e in object_paths], - ] - print(" ".join(linker_args)) - subprocess.check_call(linker_args, stderr=sys.stderr, stdout=sys.stdout) - - def _get_wheel_tag() -> str: python_tag = { 'cpython': f'cp{sys.version_info.major}{sys.version_info.minor}', @@ -123,13 +71,45 @@ def _get_content_hash(content: bytes) -> str: return f"sha256={hash_str},{len(content)}" -def workaround_path_loss(parent_sys_path): - # Without this line, `pip install` will fail when the - # multiprocessing method is set to `spawn` instead of `fork`. - import sys - for p in parent_sys_path: - if p not in sys.path: - sys.path.append(p) +def run_processes_in_parallel(action_name, commands: list[list[str]]): + running = [] + try: + cpus = os.cpu_count() + left = len(commands) + for step, cmd in enumerate(commands): + # Busy-wait until fewer than n subprocesses are running. + while len(running) == cpus: + time.sleep(0.001) + for k in range(cpus)[::-1]: + return_code = running[k].poll() + if return_code: + raise RuntimeError("A sub-process failed.") + if return_code is not None: + running[k] = running[-1] + running.pop() + left -= 1 + + # Go! + print(" ".join(cmd), file=sys.stderr) + running.append(subprocess.Popen(cmd)) + print(f"# {action_name} (remaining={left} running={len(running)})", file=sys.stderr) + + # Wait for the remaining processes. + while running: + if running[-1].wait(): + raise RuntimeError("A sub-process failed.") + running.pop() + left -= 1 + if left: + print(f"# {action_name} (remaining={left} running={len(running)})", file=sys.stderr) + print(f"# done {action_name}") + + finally: + for r in running: + try: + r.kill() + except: + pass def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): @@ -167,39 +147,55 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): # Plan out compiler and linker commands. configs = { '_detect_machine_architecture': ((), MUX_SOURCE_FILES), - '_stim_polyfill': ((), RELEVANT_SOURCE_FILES), - '_stim_sse2': (('-msse2', '-mno-avx2',), RELEVANT_SOURCE_FILES), + # '_stim_polyfill': ((), RELEVANT_SOURCE_FILES), + # '_stim_sse2': (('-msse2', '-mno-avx2',), RELEVANT_SOURCE_FILES), # NOTE: disabled until https://github.com/quantumlib/Stim/issues/432 is fixed # '_stim_avx': (('-msse2', '-mavx2',), RELEVANT_SOURCE_FILES), } + multiprocessing.set_start_method('spawn') + with tempfile.TemporaryDirectory() as temp_dir: temp_dir = pathlib.Path(temp_dir) compile_commands = [] link_commands = [] for name, (flags, files) in configs.items(): - for e in files: - compile_commands.append({ - 'src_path': e, - 'compiler': compiler, - 'temp_dir': temp_dir / name, - 'module_name': name, - 'flags': flags, - }) - link_commands.append({ - 'src_paths': files, - 'linker': linker, - 'temp_dir': temp_dir / name, - 'module_name': name, - }) - - with multiprocessing.Pool( - initializer=workaround_path_loss, - initargs=([str(e) for e in sys.path],), - ) as pool: - # Perform compilation and linking. - _ = list(pool.map(_build_object_file, compile_commands)) - _ = list(pool.map(_link_shared_object, link_commands)) + object_paths = [] + for src_path in files: + out_path = str(temp_dir / name / src_path) + ".o" + object_paths.append(out_path) + pathlib.Path(out_path).parent.mkdir(parents=True, exist_ok=True) + compiler_args = [ + compiler, + "-c", src_path, + "-o", out_path, + "-Isrc", + f"-I{pybind11.get_include()}", + f"-I{sysconfig.get_path('include')}", + "-Wall", + "-fPIC", + "-std=c++20", + "-fno-strict-aliasing", + "-fvisibility=hidden", + "-O3", + "-g0", + "-DNDEBUG", + f"-DVERSION_INFO={__version__}", + f"-DSTIM_PYBIND11_MODULE_NAME={name}", + *flags, + ] + compile_commands.append(compiler_args) + link_commands.append([ + linker, + "-shared", + f"-DVERSION_INFO={__version__}", + "-o", str(temp_dir / name / f"{name}.so"), + *object_paths, + ]) + + # Perform compilation and linking. + run_processes_in_parallel("compiling", compile_commands) + run_processes_in_parallel("linking", link_commands) # Create the wheel file. files: dict[str, bytes] = {} From 3086f1a13468c16d007a0e462e2c7ef4593e532d Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Tue, 20 Jan 2026 02:50:49 -0800 Subject: [PATCH 12/16] typo --- glue/python/src/stim_custom_setup/__init__.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/glue/python/src/stim_custom_setup/__init__.py b/glue/python/src/stim_custom_setup/__init__.py index e4afacbc..d83a67c0 100644 --- a/glue/python/src/stim_custom_setup/__init__.py +++ b/glue/python/src/stim_custom_setup/__init__.py @@ -10,7 +10,6 @@ import base64 import glob import hashlib -import multiprocessing import os import pathlib import platform @@ -147,14 +146,12 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): # Plan out compiler and linker commands. configs = { '_detect_machine_architecture': ((), MUX_SOURCE_FILES), - # '_stim_polyfill': ((), RELEVANT_SOURCE_FILES), - # '_stim_sse2': (('-msse2', '-mno-avx2',), RELEVANT_SOURCE_FILES), + '_stim_polyfill': ((), RELEVANT_SOURCE_FILES), + '_stim_sse2': (('-msse2', '-mno-avx2',), RELEVANT_SOURCE_FILES), # NOTE: disabled until https://github.com/quantumlib/Stim/issues/432 is fixed # '_stim_avx': (('-msse2', '-mavx2',), RELEVANT_SOURCE_FILES), } - multiprocessing.set_start_method('spawn') - with tempfile.TemporaryDirectory() as temp_dir: temp_dir = pathlib.Path(temp_dir) compile_commands = [] From 73dfd4fa7a2514de83ffd45a9358ee6131a8a21d Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Tue, 20 Jan 2026 03:25:17 -0800 Subject: [PATCH 13/16] try windows --- glue/python/src/stim_custom_setup/__init__.py | 172 +++++++++++------- 1 file changed, 107 insertions(+), 65 deletions(-) diff --git a/glue/python/src/stim_custom_setup/__init__.py b/glue/python/src/stim_custom_setup/__init__.py index d83a67c0..163a54cc 100644 --- a/glue/python/src/stim_custom_setup/__init__.py +++ b/glue/python/src/stim_custom_setup/__init__.py @@ -70,7 +70,14 @@ def _get_content_hash(content: bytes) -> str: return f"sha256={hash_str},{len(content)}" -def run_processes_in_parallel(action_name, commands: list[list[str]]): +def _run_processes_in_parallel(action_name, commands: list[list[str]]): + """You're not gonna believe this, but this method... + + ...runs processes in parallel. + + It avoids starting more processes while cpu_count of them + are currently running. + """ running = [] try: cpus = os.cpu_count() @@ -101,8 +108,7 @@ def run_processes_in_parallel(action_name, commands: list[list[str]]): left -= 1 if left: print(f"# {action_name} (remaining={left} running={len(running)})", file=sys.stderr) - print(f"# done {action_name}") - + print(f"# done {action_name}", file=sys.stderr) finally: for r in running: try: @@ -124,6 +130,7 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): HEADER_FILES = glob.glob("src/**/*.h", recursive=True) + glob.glob("src/**/*.inl", recursive=True) RELEVANT_SOURCE_FILES = sorted(set(ALL_SOURCE_FILES) - set(TEST_FILES + PERF_FILES + MAIN_FILES + MUX_SOURCE_FILES)) + is_windows = platform.system().lower().startswith('win') # Determine the compiler to use. compiler = None if compiler is None and config_settings is not None: @@ -131,7 +138,7 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): if compiler is None: compiler = os.environ.get('CXX', None) if compiler is None: - if platform.system().startswith('Win'): + if is_windows: compiler = 'cl.exe' else: compiler = 'g++' @@ -141,75 +148,122 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): if linker is None and config_settings is not None: linker = config_settings.get("linker", None) if linker is None: - linker = compiler + if is_windows: + linker = 'link.exe' + else: + linker = compiler # Plan out compiler and linker commands. configs = { '_detect_machine_architecture': ((), MUX_SOURCE_FILES), '_stim_polyfill': ((), RELEVANT_SOURCE_FILES), - '_stim_sse2': (('-msse2', '-mno-avx2',), RELEVANT_SOURCE_FILES), + '_stim_sse2': ( + ('/arch:SSE2',) if is_windows else ('-msse2', '-mno-avx2',), + RELEVANT_SOURCE_FILES, + ), # NOTE: disabled until https://github.com/quantumlib/Stim/issues/432 is fixed # '_stim_avx': (('-msse2', '-mavx2',), RELEVANT_SOURCE_FILES), } + if is_windows: + so = 'pyd' + else: + so = 'so' + with tempfile.TemporaryDirectory() as temp_dir: - temp_dir = pathlib.Path(temp_dir) - compile_commands = [] - link_commands = [] + link_outputs = [] + temp_dir: pathlib.Path = pathlib.Path(temp_dir) + compile_commands: list[list[str]] = [] + link_commands: list[list[str]] = [] for name, (flags, files) in configs.items(): object_paths = [] for src_path in files: - out_path = str(temp_dir / name / src_path) + ".o" + out_path: str = str(temp_dir / name / src_path) + ".o" object_paths.append(out_path) pathlib.Path(out_path).parent.mkdir(parents=True, exist_ok=True) - compiler_args = [ - compiler, - "-c", src_path, - "-o", out_path, - "-Isrc", - f"-I{pybind11.get_include()}", - f"-I{sysconfig.get_path('include')}", - "-Wall", - "-fPIC", - "-std=c++20", - "-fno-strict-aliasing", - "-fvisibility=hidden", - "-O3", - "-g0", - "-DNDEBUG", - f"-DVERSION_INFO={__version__}", - f"-DSTIM_PYBIND11_MODULE_NAME={name}", - *flags, - ] - compile_commands.append(compiler_args) - link_commands.append([ - linker, - "-shared", - f"-DVERSION_INFO={__version__}", - "-o", str(temp_dir / name / f"{name}.so"), - *object_paths, - ]) + if is_windows: + compile_commands.append([ + compiler, + "/c", src_path, + f"/Fo{out_path}", + "/Isrc", + f"/I{pybind11.get_include()}", + f"/I{sysconfig.get_path('include')}", + "/W4", + "/std=c++20", + "/O2", + "/MD", + "/EHsc", + "/nologo", + "/DNDEBUG", + f"/DVERSION_INFO={__version__}", + f"/DSTIM_PYBIND11_MODULE_NAME={name}", + *flags, + ]) + else: + compile_commands.append([ + compiler, + "-c", src_path, + "-o", out_path, + "-Isrc", + f"-I{pybind11.get_include()}", + f"-I{sysconfig.get_path('include')}", + "-Wall", + "-fPIC", + "-std=c++20", + "-fno-strict-aliasing", + "-fvisibility=hidden", + "-O3", + "-g0", + "-DNDEBUG", + f"-DVERSION_INFO={__version__}", + f"-DSTIM_PYBIND11_MODULE_NAME={name}", + *flags, + ]) + link_out = str(temp_dir / name / f'{name}.{so}') + if is_windows: + link_commands.append([ + linker, + "/DLL", + f"/OUT:{link_out}", + "/nologo", + f"/LIBPATH:{pathlib.Path(sysconfig.get_config_var('BINDIR')) / 'libs'}", + *object_paths, + ]) + else: + link_commands.append([ + linker, + "-shared", + "-o", link_out, + *object_paths, + ]) # Perform compilation and linking. - run_processes_in_parallel("compiling", compile_commands) - run_processes_in_parallel("linking", link_commands) + _run_processes_in_parallel("compiling", compile_commands) + _run_processes_in_parallel("linking", link_commands) - # Create the wheel file. + # Define the files to put into the wheel file. files: dict[str, bytes] = {} - + dist_info_dir = f"stim-{__version__}.dist-info" + files[f'{dist_info_dir}/top_level.txt'] = "stim".encode('UTF-8') + files[f'{dist_info_dir}/WHEEL'] = f""" +Wheel-Version: 1.0 +Generator: stim_custom_setup +Root-Is-Purelib: false +Tag: {_get_wheel_tag()} +""".lstrip().encode('UTF-8') + with open('LICENSE', 'rb') as f: + files[f'{dist_info_dir}/license/LICENSE'] = f.read() + for file in pathlib.Path("glue/python/src/stim").iterdir(): + with open(file, 'rb') as f: + files[f'stim/{file.name}'] = f.read() for name in configs.keys(): - with open(temp_dir / name / f'{name}.so', 'rb') as f: + with open(temp_dir / name / f'{name}.{so}', 'rb') as f: files[f'stim/{name}' + sysconfig.get_config_var('EXT_SUFFIX')] = f.read() - - dist_info_dir = f"stim-{__version__}.dist-info" files[f'{dist_info_dir}/entry_points.txt'] = """ [console_scripts] stim = stim._main_argv:main_argv """.strip().encode('UTF-8') - - with open('LICENSE', 'rb') as f: - files[f'{dist_info_dir}/license/LICENSE'] = f.read() - with open('glue/python/README.md', encoding='UTF-8') as f: files[f'{dist_info_dir}/METADATA'] = (""" Metadata-Version: 2.4 @@ -227,28 +281,16 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): """.lstrip() + f.read()).encode('UTF-8') - files[f'{dist_info_dir}/top_level.txt'] = "stim".encode('UTF-8') - - files[f'{dist_info_dir}/WHEEL'] = f""" -Wheel-Version: 1.0 -Generator: stim_custom_setup -Root-Is-Purelib: false -Tag: {_get_wheel_tag()} -""".lstrip().encode('UTF-8') - - for file in pathlib.Path("glue/python/src/stim").iterdir(): - with open(file, 'rb') as f: - files[f'stim/{file.name}'] = f.read() - - records = [] + # Record files and their hashes in the RECORD file. + records = [f"{dist_info_dir}/RECORD,,"] for k, v in files.items(): records.append(f"{k},{_get_content_hash(v)}") - records.append(f"{dist_info_dir}/RECORD,,") files[f'{dist_info_dir}/RECORD'] = "\n".join(records).encode('UTF-8') - with zipfile.ZipFile(wheel_path, 'w', compression=zipfile.ZIP_DEFLATED) as wheel: + # Write the wheel file. + with zipfile.ZipFile(wheel_path, 'w', compression=zipfile.ZIP_DEFLATED) as f: for k, v in files.items(): - wheel.writestr(k, v) + f.writestr(k, v) return wheel_name From 7e33f0411d100db4266be3d1baa3d0b2f66c9ddb Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Tue, 20 Jan 2026 03:41:29 -0800 Subject: [PATCH 14/16] .......windows why --- glue/python/src/stim_custom_setup/__init__.py | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/glue/python/src/stim_custom_setup/__init__.py b/glue/python/src/stim_custom_setup/__init__.py index 163a54cc..4afeebec 100644 --- a/glue/python/src/stim_custom_setup/__init__.py +++ b/glue/python/src/stim_custom_setup/__init__.py @@ -117,6 +117,35 @@ def _run_processes_in_parallel(action_name, commands: list[list[str]]): pass +def find_cl_exe() -> str: + program_files = os.environ.get("ProgramFiles(x86)") + if program_files is None: + return 'cl.exe' + + vswhere = pathlib.Path(program_files) / "Microsoft Visual Studio" / "Installer" / "vswhere.exe" + if not vswhere.exists(): + return 'cl.exe' + + vs_root = subprocess.check_output([ + str(vswhere), + "-latest", + "-products", "*", + "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "-property", "installationPath" + ], encoding='utf-8').strip() + if not vs_root: + return 'cl.exe' + + msvc_root = pathlib.Path(vs_root) / "VC" / "Tools" / "MSVC" + version = sorted(msvc_root.iterdir())[-1].name + arch = "x64" if platform.machine().lower() in ("amd64", "x86_64") else "x86" + cl_path = msvc_root / version / "bin" / f"Host{arch}" / arch / "cl.exe" + if not cl_path.exists(): + return 'cl.exe' + + return str(cl_path) + + def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): wheel_name = f'stim-{__version__}-{_get_wheel_tag()}.whl' wheel_path = pathlib.Path(wheel_directory) / wheel_name @@ -139,7 +168,7 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): compiler = os.environ.get('CXX', None) if compiler is None: if is_windows: - compiler = 'cl.exe' + compiler = find_cl_exe() else: compiler = 'g++' @@ -149,7 +178,7 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): linker = config_settings.get("linker", None) if linker is None: if is_windows: - linker = 'link.exe' + linker = find_cl_exe()[:-6] + 'link.exe' else: linker = compiler From 08d149b16f26abd598689d6e62e388e99525cf18 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Tue, 20 Jan 2026 03:47:32 -0800 Subject: [PATCH 15/16] darwin flags --- glue/python/src/stim_custom_setup/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/glue/python/src/stim_custom_setup/__init__.py b/glue/python/src/stim_custom_setup/__init__.py index 4afeebec..59913e1c 100644 --- a/glue/python/src/stim_custom_setup/__init__.py +++ b/glue/python/src/stim_custom_setup/__init__.py @@ -260,9 +260,13 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): *object_paths, ]) else: + osx_flags = [] + if platform.system().lower() == 'darwin': + osx_flags = ["-undefined", "dynamic_lookup"] link_commands.append([ linker, "-shared", + *osx_flags, "-o", link_out, *object_paths, ]) From e64002e459e7b6769d7ef62cf784df1e974d66ee Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Tue, 20 Jan 2026 03:51:05 -0800 Subject: [PATCH 16/16] colon --- glue/python/src/stim_custom_setup/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glue/python/src/stim_custom_setup/__init__.py b/glue/python/src/stim_custom_setup/__init__.py index 59913e1c..68aed3fa 100644 --- a/glue/python/src/stim_custom_setup/__init__.py +++ b/glue/python/src/stim_custom_setup/__init__.py @@ -219,7 +219,7 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): f"/I{pybind11.get_include()}", f"/I{sysconfig.get_path('include')}", "/W4", - "/std=c++20", + "/std:c++20", "/O2", "/MD", "/EHsc",