From daa368530f4dc8846d204ef454c5121949e9e4e7 Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Wed, 19 Aug 2026 14:20:55 +0200 Subject: [PATCH 1/7] [interop] Factor interpreter startup into focused helpers. NFC --- src/interop/interop_wrapper.cxx | 179 +++++++++++++++++--------------- 1 file changed, 98 insertions(+), 81 deletions(-) diff --git a/src/interop/interop_wrapper.cxx b/src/interop/interop_wrapper.cxx index f81e8a8..804c4f1 100644 --- a/src/interop/interop_wrapper.cxx +++ b/src/interop/interop_wrapper.cxx @@ -98,6 +98,99 @@ static InterOpPaths cppinterop_paths() { (anchor / CPPINTEROP_INCLUDE_DIR).string()}; } +// The one place libclangCppInterOp is dlopen'd. +static bool loadDispatchAPI(const InterOpPaths& Paths) { + if (!Cpp::LoadDispatchAPI(Paths.Library.c_str())) { + std::cerr << "[cppjit-backend] Failed to load CppInterOp" << std::endl; + return false; + } + return true; +} + +// CppInterOp itself appends CPPINTEROP_EXTRA_INTERPRETER_ARGS inside +// CreateInterpreter, so nothing needs to be forwarded from here. +static interop::TInterp_t acquireOrCreateInterpreter() { + if (auto existingInterp = Cpp::GetInterpreter()) + return existingInterp; + +#if defined(__arm64__) && defined(__APPLE__) + // If on apple silicon don't use -march=native + return Cpp::CreateInterpreter({"-std=c++17"}, /*GpuArgs=*/{}); +#else + return Cpp::CreateInterpreter({"-std=c++17", "-march=native"}, + /*GpuArgs=*/{}); +#endif +} + +static void configureInterpreter(const InterOpPaths& Paths) { + std::set bi{g_builtins}; + for (const auto& name : bi) { + for (const char* a : {"*", "&", "*&", "[]", "*[]"}) + g_builtins.insert(name + a); + } + + if (getenv("CPPJIT_DISABLE_FASTPATH")) + gEnableFastPath = false; + + // set opt level (default to 2 if not given; Cling itself defaults to 0) + int optLevel = 2; + + if (getenv("CPPJIT_OPT_LEVEL")) + optLevel = atoi(getenv("CPPJIT_OPT_LEVEL")); + + if (optLevel != 0) { + std::ostringstream s; + s << "#pragma cling optimize " << optLevel; + Cpp::Process(s.str().c_str()); + } + + Cpp::AddIncludePath(Paths.IncludeDir.c_str()); + Cpp::LoadLibrary("libstdc++", /* lookup= */ true); +} + +static void preloadHeaders() { + const char* code = "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" // for strcpy + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" // for the dispatcher code to + // use std::function + "#include \n" // FIXME: Replace with modules + "#include \n" // FIXME: Replace with modules + "#include \n" // FIXME: Replace with modules + "#include \n" // FIXME: Replace with modules + "#include \n" // FIXME: Replace with modules + "#include \n" // FIXME: Replace with modules + "#include \n" // FIXME: Replace with modules + "#include \n" // FIXME: Replace with modules + "#include \n" // FIXME: Replace with modules + "#if __has_include()\n" + "#include \n" + "#endif\n" + "#include \n"; + Cpp::Process(code); +} + +static void defineRuntimeHelpers() { + Cpp::Declare("namespace __cppjit_internal { template" + " bool is_equal(const C1& c1, const C2& c2) { return " + "(bool)(c1 == c2); } }", + /*silent=*/false); + Cpp::Declare("namespace __cppjit_internal { template" + " bool is_not_equal(const C1& c1, const C2& c2) { return " + "(bool)(c1 != c2); } }", + /*silent=*/false); + + // helper for multiple inheritance + Cpp::Declare("namespace __cppjit_internal { struct Sep; }", + /*silent=*/false); +} + } // unnamed namespace // Load CppInterOp and set up the interpreter. A dlopen during static @@ -114,89 +207,13 @@ extern "C" int LoadCppInterOp() { std::call_once(Once, [] { std::lock_guard Lock(InterOpMutex); const InterOpPaths Paths = cppinterop_paths(); - if (!Cpp::LoadDispatchAPI(Paths.Library.c_str())) { - std::cerr << "[cppjit-backend] Failed to load CppInterOp" << std::endl; + if (!loadDispatchAPI(Paths)) return; - } - // Check if somebody already loaded CppInterOp and created an - // interpreter for us. - if (!Cpp::GetInterpreter()) { - // CppInterOp itself appends CPPINTEROP_EXTRA_INTERPRETER_ARGS inside - // CreateInterpreter, so nothing needs to be forwarded from here. -#if defined(__arm64__) && defined(__APPLE__) - // If on apple silicon don't use -march=native - Cpp::CreateInterpreter({"-std=c++17"}, /*GpuArgs=*/{}); -#else - Cpp::CreateInterpreter({"-std=c++17", "-march=native"}, /*GpuArgs=*/{}); -#endif - } - - // fill out the builtins - std::set bi{g_builtins}; - for (const auto& name : bi) { - for (const char* a : {"*", "&", "*&", "[]", "*[]"}) - g_builtins.insert(name + a); - } - - // disable fast path if requested - if (getenv("CPPJIT_DISABLE_FASTPATH")) - gEnableFastPath = false; - - // set opt level (default to 2 if not given; Cling itself defaults to 0) - int optLevel = 2; - - if (getenv("CPPJIT_OPT_LEVEL")) - optLevel = atoi(getenv("CPPJIT_OPT_LEVEL")); - - if (optLevel != 0) { - std::ostringstream s; - s << "#pragma cling optimize " << optLevel; - Cpp::Process(s.str().c_str()); - } - Cpp::AddIncludePath(Paths.IncludeDir.c_str()); - Cpp::LoadLibrary("libstdc++", /* lookup= */ true); - - // load frequently used headers - const char* code = "#include \n" - "#include \n" - "#include \n" - "#include \n" - "#include \n" // for strcpy - "#include \n" - "#include \n" - "#include \n" - "#include \n" - "#include \n" // for the dispatcher code to - // use std::function - "#include \n" // FIXME: Replace with modules - "#include \n" // FIXME: Replace with modules - "#include \n" // FIXME: Replace with modules - "#include \n" // FIXME: Replace with modules - "#include \n" // FIXME: Replace with modules - "#include \n" // FIXME: Replace with modules - "#include \n" // FIXME: Replace with modules - "#include \n" // FIXME: Replace with modules - "#include \n" // FIXME: Replace with modules - "#if __has_include()\n" - "#include \n" - "#endif\n" - "#include \n"; - Cpp::Process(code); - - // create helpers for comparing thingies - Cpp::Declare("namespace __cppjit_internal { template" - " bool is_equal(const C1& c1, const C2& c2) { return " - "(bool)(c1 == c2); } }", - /*silent=*/false); - Cpp::Declare("namespace __cppjit_internal { template" - " bool is_not_equal(const C1& c1, const C2& c2) { return " - "(bool)(c1 != c2); } }", - /*silent=*/false); - - // helper for multiple inheritance - Cpp::Declare("namespace __cppjit_internal { struct Sep; }", - /*silent=*/false); + acquireOrCreateInterpreter(); + configureInterpreter(Paths); + preloadHeaders(); + defineRuntimeHelpers(); Loaded = 1; }); From fafddb7a5b826a717d753e8e4c6a8e7a2e573bd9 Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Tue, 18 Aug 2026 13:32:17 +0200 Subject: [PATCH 2/7] [python] Anchor the cpyrt header probe on the extension module In editable installs the cpyrt header probe checked `__file__`, which maps to the source tree, so it missed the headers installed by scikit-build-core into site-packages and warned on every import. Now use the libcppjit extension's location, which resolves editable and regular installs identically. Also remove all dead lookups inherited with the probe (the `cpyrt` pip-distribution query via pkg_resources and the site/pythonX.Y layout guesses). `CPPJIT_API_PATH` still overrides and `"none"` still disables. --- python/cppjit/__init__.py | 85 ++++++++------------------------------- 1 file changed, 17 insertions(+), 68 deletions(-) diff --git a/python/cppjit/__init__.py b/python/cppjit/__init__.py index bf9978f..81e5a96 100644 --- a/python/cppjit/__init__.py +++ b/python/cppjit/__init__.py @@ -51,6 +51,7 @@ ] import ctypes +import importlib.util import os import sys import sysconfig @@ -334,82 +335,30 @@ def add_library_path(path): if os.path.exists(apipath) and os.path.exists(os.path.join(apipath, "Python.h")): add_include_path(apipath) -# add access to extra headers for dispatcher (cpyrt only (?)) +# add access to the cpyrt dispatcher API headers, which install next to the +# extension module; anchoring on the extension resolves editable and regular +# installs alike. CPPJIT_API_PATH overrides ("none" disables the lookup). if not ispypy: - try: - apipath_extra = os.environ["CPPJIT_API_PATH"] + apipath_extra = os.environ.get("CPPJIT_API_PATH") + if apipath_extra: if os.path.basename(apipath_extra) == "cpyrt": apipath_extra = os.path.dirname(apipath_extra) - except KeyError: - apipath_extra = None - - if apipath_extra is None: - try: - import pkg_resources as pr - - d = pr.get_distribution("cpyrt") - for line in d.get_metadata_lines("RECORD"): - if "API.h" in line: - part = line[0 : line.find(",")] - - ape = os.path.join(d.location, part) - if os.path.exists(ape): - apipath_extra = os.path.dirname(os.path.dirname(ape)) - - del part, d, pr - except Exception: - pass - - if apipath_extra is None: - # for the monorepo: headers are at cppjit_backend/include/ - _cppjit_inc = os.path.join( - os.path.dirname(os.path.dirname(__file__)), "cppjit_backend", "include" - ) - if os.path.exists(os.path.join(_cppjit_inc, "cpyrt")): - apipath_extra = _cppjit_inc - del _cppjit_inc - - if apipath_extra is None: - ldversion = sysconfig.get_config_var("LDVERSION") - if not ldversion: - ldversion = sys.version[:3] + else: + _spec = importlib.util.find_spec("libcppjit") + if _spec is not None and _spec.origin: + apipath_extra = os.path.join( + os.path.dirname(_spec.origin), "cppjit_backend", "include" + ) + del _spec - apipath_extra = os.path.join( - os.path.dirname(apipath), "site", "python" + ldversion - ) - if not os.path.exists(os.path.join(apipath_extra, "cpyrt")): - import glob - - import libcppjit - - ape = os.path.dirname(libcppjit.__file__) - # a "normal" structure finds the include directory up to 3 levels up, - # ie. dropping lib/pythonx.y[md]/site-packages - for i in range(3): - if os.path.exists(os.path.join(ape, "include")): - break - ape = os.path.dirname(ape) - - ape = os.path.join(ape, "include") - if os.path.exists(os.path.join(ape, "cpyrt")): - apipath_extra = ape - else: - # add back pythonx.y or site/pythonx.y if present - for p in glob.glob( - os.path.join(ape, "python" + sys.version[:3] + "*") - ) + glob.glob(os.path.join(ape, "*", "python" + sys.version[:3] + "*")): - if os.path.exists(os.path.join(p, "cpyrt")): - apipath_extra = p - break - - if apipath_extra.lower() != "none": - if not os.path.exists(os.path.join(apipath_extra, "cpyrt")): + if apipath_extra and apipath_extra.lower() != "none": + if os.path.isdir(os.path.join(apipath_extra, "cpyrt")): + add_include_path(apipath_extra) + else: warnings.warn( "cpyrt API not found (tried: %s); set CPPJIT_API_PATH envar to the 'cpyrt' API directory to fix" % apipath_extra ) - else: - add_include_path(apipath_extra) del apipath_extra From 28cf40431d3d5439a38c943e1c673fcfc0143af4 Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Mon, 17 Aug 2026 21:53:36 +0200 Subject: [PATCH 3/7] [cmake] Require only Python Development.Module for the extension build --- CMakeLists.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 03eaeff..5fb23ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,13 +16,16 @@ set(CPPINTEROP_GIT_TAG "8d624c621a4b95e36ff73ac708c85a768287478f" CACHE STRING " set(CPPINTEROP_SOURCE_DIR "" CACHE PATH "Override default CppInterOp built by ExternalProject_Add, with a path to local CppInterOp source") +# Development.Module, not Development: the full component additionally +# requires libpython (Development.Embed), which manylinux images do not +# ship — extension modules only need headers and module-link rules. set(Python_FIND_VIRTUALENV ONLY) -find_package(Python COMPONENTS Interpreter Development) +find_package(Python COMPONENTS Interpreter Development.Module) if(NOT Python_FOUND) set(Python_FIND_VIRTUALENV STANDARD) - find_package(Python COMPONENTS Interpreter Development) + find_package(Python COMPONENTS Interpreter Development.Module) endif() -if(NOT Python_Development_FOUND) +if(NOT Python_Development.Module_FOUND) message(FATAL_ERROR "Python development headers not found") endif() From 790e68392032ee64e1738c40d89eb6d11df6796d Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Mon, 17 Aug 2026 23:28:54 +0200 Subject: [PATCH 4/7] [ci] Build manylinux and macOS arm64 wheels with cibuildwheel --- .github/wheel_smoke.py | 23 +++++++++++++ .github/workflows/wheels.yml | 62 ++++++++++++++++++++++++++++++++++++ pyproject.toml | 31 ++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 .github/wheel_smoke.py create mode 100644 .github/workflows/wheels.yml diff --git a/.github/wheel_smoke.py b/.github/wheel_smoke.py new file mode 100644 index 0000000..5a86263 --- /dev/null +++ b/.github/wheel_smoke.py @@ -0,0 +1,23 @@ +"""Wheel smoke test, run from a clean venv by cibuildwheel's test step: +libcppjit.so must locate libclangCppInterOp relative to its own path (the +build tree is gone by test time), and the template instantiation plus the +header check prove the shipped include tree.""" + +import os + +import cppjit +import cppjit_backend + +cppjit.cppdef("int wheel_smoke(int x) { return x + 1; }") +assert cppjit.gbl.wheel_smoke(41) == 42 + +v = cppjit.gbl.std.vector["int"]() +v.push_back(7) +assert v[0] == 7 + +api = os.path.join( + os.path.dirname(cppjit_backend.__file__), "include", "cpyrt", "API.h" +) +assert os.path.exists(api), api + +print("wheel smoke OK") diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml new file mode 100644 index 0000000..ae67ee1 --- /dev/null +++ b/.github/workflows/wheels.yml @@ -0,0 +1,62 @@ +name: Wheels + +# Build the distributable artifacts -- manylinux and macOS arm64 wheels via +# cibuildwheel (config in pyproject.toml) plus the sdist; no index publishing. +# The weekly schedule is a drift canary for the conda-forge/homebrew LLVM. + +on: + workflow_dispatch: + pull_request: + paths: + - '.github/workflows/wheels.yml' + - '.github/wheel_smoke.py' + - 'pyproject.toml' + - 'CMakeLists.txt' + - 'cmake/**' + - 'src/interop/**' + - 'python/cppjit/_cpython_cppjit.py' + push: + tags: ['v*'] + schedule: + - cron: '30 4 * * 1' + +permissions: + contents: read + +concurrency: + group: wheels-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + wheels: + name: wheels ${{ matrix.label }} + strategy: + fail-fast: false + matrix: + include: + - { os: ubuntu-24.04, label: manylinux-x86_64 } + - { os: macos-26, label: macosx-arm64 } + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v4 + + - uses: pypa/cibuildwheel@v4.1.1 + + - uses: actions/upload-artifact@v4 + with: + name: wheels-${{ matrix.label }} + path: wheelhouse/*.whl + + sdist: + name: sdist + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + + - run: pipx run build --sdist + + - uses: actions/upload-artifact@v4 + with: + name: sdist + path: dist/*.tar.gz diff --git a/pyproject.toml b/pyproject.toml index d7501d9..659e58f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,37 @@ cmake.build-type = "Release" metadata.version.provider = "scikit_build_core.metadata.regex" metadata.version.input = "python/cppjit/_version.py" +[tool.cibuildwheel] +build = ["cp312-*", "cp313-*", "cp314-*"] +skip = ["*-musllinux*"] +build-verbosity = 1 +test-command = "python {project}/.github/wheel_smoke.py" + +[tool.cibuildwheel.linux] +archs = ["x86_64"] +manylinux-x86_64-image = "manylinux_2_28" +# LLVM/Clang 21 from conda-forge: built against a glibc within the +# manylinux_2_28 policy, unlike apt.llvm.org or release-tarball builds. +# lld is mandatory: BFD ld mis-relaxes R_X86_64_GOTPCRELX relocations in +# the conda static archives, and the result crashes at the first JIT use. +before-all = """ +curl -fsSL --retry 5 -o /tmp/micromamba.tar.bz2 https://micro.mamba.pm/api/micromamba/linux-64/latest +tar -xjf /tmp/micromamba.tar.bz2 -C /usr/local bin/micromamba +export MAMBA_ROOT_PREFIX=/opt/mamba +micromamba create -y -p /opt/llvm -c conda-forge 'llvmdev=21.*' 'clangdev=21.*' lld zstd zlib libxml2 +""" +# Environment variables reach the CppInterOp ExternalProject sub-configure +# (find_package(zstd) needs the conda env) and auditwheel (LD_LIBRARY_PATH +# resolves the conda DT_NEEDEDs it grafts); ld.lld comes from PATH. +environment = { CMAKE_ARGS = "-DLLVM_DIR=/opt/llvm/lib/cmake/llvm -DClang_DIR=/opt/llvm/lib/cmake/clang", CMAKE_PREFIX_PATH = "/opt/llvm", LDFLAGS = "-fuse-ld=lld", PATH = "/opt/llvm/bin:$PATH", LD_LIBRARY_PATH = "/opt/llvm/lib" } + +[tool.cibuildwheel.macos] +archs = ["arm64"] +before-all = "brew install llvm@21" +# The deployment target must match the Homebrew bottles the wheel grafts +# (bottles target the runner's OS); delocate rejects the default 11.0 label. +environment = { CMAKE_ARGS = "-DLLVM_DIR=/opt/homebrew/opt/llvm@21/lib/cmake/llvm -DClang_DIR=/opt/homebrew/opt/llvm@21/lib/cmake/clang", MACOSX_DEPLOYMENT_TARGET = "26.0" } + [tool.pytest.ini_options] testpaths = ["test"] pythonpath = ["test"] From 72007c479bbf464b3449f9c662d36bac6b2685f1 Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Tue, 18 Aug 2026 10:43:02 +0200 Subject: [PATCH 5/7] [interop] Detect the resource dir of the required system LLVM --- CMakeLists.txt | 4 +++- src/interop/interop_wrapper.cxx | 21 +++++++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fb23ab..6342c77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,11 +119,13 @@ add_library(cppjit SHARED ${CPYRT_SOURCES} ${INTEROP_SOURCES}) add_dependencies(cppjit CppInterOp) # The wrapper anchors these relative spellings at its own load location, -# falling back to the install prefix (see cppinterop_paths()). +# falling back to the install prefix (see cppinterop_paths()); the clang +# major names the versioned compiler probed for the runtime resource dir. target_compile_definitions(cppjit PRIVATE CPPINTEROP_INSTALL_PREFIX="${CPPINTEROP_INSTALL_PREFIX}" CPPINTEROP_LIBRARY="cppjit_backend/lib/libclangCppInterOp${CMAKE_SHARED_LIBRARY_SUFFIX}" CPPINTEROP_INCLUDE_DIR="cppjit_backend/include" + CPPJIT_CLANG_MAJOR="${LLVM_VERSION_MAJOR}" ) target_include_directories(cppjit PRIVATE diff --git a/src/interop/interop_wrapper.cxx b/src/interop/interop_wrapper.cxx index 804c4f1..780cba1 100644 --- a/src/interop/interop_wrapper.cxx +++ b/src/interop/interop_wrapper.cxx @@ -113,13 +113,22 @@ static interop::TInterp_t acquireOrCreateInterpreter() { if (auto existingInterp = Cpp::GetInterpreter()) return existingInterp; -#if defined(__arm64__) && defined(__APPLE__) - // If on apple silicon don't use -march=native - return Cpp::CreateInterpreter({"-std=c++17"}, /*GpuArgs=*/{}); -#else - return Cpp::CreateInterpreter({"-std=c++17", "-march=native"}, - /*GpuArgs=*/{}); + std::vector args = {"-std=c++17"}; +#if !(defined(__arm64__) && defined(__APPLE__)) + // apple silicon clang rejects -march=native + args.push_back("-march=native"); #endif + // CppInterOp resolves the JIT's builtin headers from its build prefix + // or bare `clang`, but distributions spell the required major + // clang-; when only that spelling resolves, pass it explicitly. + std::string resourceDir; + if (Cpp::DetectResourceDir("clang").empty()) + resourceDir = Cpp::DetectResourceDir("clang-" CPPJIT_CLANG_MAJOR); + if (!resourceDir.empty()) { + args.push_back("-resource-dir"); + args.push_back(resourceDir.c_str()); + } + return Cpp::CreateInterpreter(args, /*GpuArgs=*/{}); } static void configureInterpreter(const InterOpPaths& Paths) { From ee9676caed0eead3c66599704daa65db03feac7b Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Mon, 17 Aug 2026 23:28:54 +0200 Subject: [PATCH 6/7] [ci] Test the linux wheel on a plain runner against the full suite --- .github/workflows/wheels.yml | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index ae67ee1..6a17811 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -60,3 +60,59 @@ jobs: with: name: sdist path: dist/*.tar.gz + + # Install the linux wheel on a plain runner, outside the manylinux + # container it was built in, and run the full suite against it. + test-wheel: + name: test wheel (full suite) + needs: wheels + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - uses: actions/download-artifact@v4 + with: + name: wheels-manylinux-x86_64 + path: wheelhouse + + - name: Install the LLVM the wheel requires at runtime + # The JIT needs the builtin headers of the wheel's LLVM major; + # the loader detects them from `clang` or `clang-21` on PATH. + run: wget -qO- https://apt.llvm.org/llvm.sh | sudo bash -s -- 21 + + - name: Install the wheel and the test requirements + run: | + python -m venv wheel-venv + wheel-venv/bin/pip install wheelhouse/cppjit-*cp312*.whl + wheel-venv/bin/pip install -r requirements.txt + + - name: Smoke the wheel outside pytest + # pytest captures output at the fd level, so a native abort during + # collection dies silently; this surfaces interpreter-boot errors. + run: | + clang --version + clang-21 -print-resource-dir + wheel-venv/bin/python -X faulthandler .github/wheel_smoke.py + + - name: Run the test suite against the installed wheel + env: + # match the CI cells, which run the interpreter under C++20 + CPPINTEROP_EXTRA_INTERPRETER_ARGS: -std=c++20 + run: | + cd test + make -j4 PYTHON=$GITHUB_WORKSPACE/wheel-venv/bin/python + rc=0 + $GITHUB_WORKSPACE/wheel-venv/bin/python -X faulthandler -m pytest -ra \ + > pytest.log 2>&1 || rc=$? + tail -80 pytest.log + exit $rc + + - name: Fail on dependency-driven skips + # "0 failed" proves nothing about tests that silently stopped + # running; a skip naming a missing module means the wheel venv + # lost dependency coverage. + run: "! grep -iE '^SKIPPED.*(no module named|could not import|module .* not installed)' test/pytest.log" From 74bb3ff66588b2869e69394fbb3e3f0f10031d79 Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Wed, 19 Aug 2026 08:05:35 +0200 Subject: [PATCH 7/7] [ci] Update the wheels workflow actions to their latest versions --- .github/workflows/wheels.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 6a17811..056daec 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -39,11 +39,11 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - - uses: pypa/cibuildwheel@v4.1.1 + - uses: pypa/cibuildwheel@v4.2.0 - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: wheels-${{ matrix.label }} path: wheelhouse/*.whl @@ -52,11 +52,11 @@ jobs: name: sdist runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - run: pipx run build --sdist - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: sdist path: dist/*.tar.gz @@ -68,13 +68,13 @@ jobs: needs: wheels runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v7 with: python-version: '3.12' - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: wheels-manylinux-x86_64 path: wheelhouse