Skip to content

Python Artifacts

Python Artifacts #1303

Workflow file for this run

name: Python Artifacts
permissions:
contents: read
env:
TRIGGER_ON_PR_PUSH: true # Set to true to enable triggers on PR pushes
LLVM_VERSION: "21.1"
LLVM_RELEASE_VERSION: "21.1.8"
MACOS_WHEEL_DEPLOYMENT_TARGET: "13.2"
CARGO_HTTP_MULTIPLEXING: "false"
CARGO_NET_GIT_FETCH_WITH_CLI: "true"
CARGO_NET_RETRY: "10"
# Must match pecos_build::cmake::CMAKE_VERSION. The MWPF decoder feature
# uses highs-sys, which needs cmake; the wheel build installs this version
# via `pecos install cmake` and points highs-sys's cmake-rs at it via $CMAKE.
PECOS_CMAKE_VERSION: "3.31.12"
on:
push:
branches:
- main
- dev
- development
- master
tags:
- 'py-*'
paths:
- 'python/pecos-rslib/**'
- 'python/pecos-rslib-llvm/**'
- 'python/quantum-pecos/**'
- 'crates/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'uv.lock'
- 'scripts/ci/**'
- '.github/workflows/python-release.yml'
pull_request:
branches:
- main
- dev
- development
- master
paths:
- 'python/pecos-rslib/**'
- 'python/pecos-rslib-llvm/**'
- 'python/quantum-pecos/**'
- 'crates/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'uv.lock'
- 'scripts/ci/**'
- '.github/workflows/python-release.yml'
workflow_dispatch:
inputs:
sha:
description: Commit SHA
type: string
dry_run:
description: 'Dry run (build but not publish)'
type: boolean
default: true
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
resolve_release_mode:
runs-on: ubuntu-latest
if: |
github.event_name == 'pull_request' && github.event.action != 'closed' ||
github.event_name == 'push' && contains(fromJSON('["main", "master", "dev", "development"]'), github.ref_name) ||
github.event_name == 'push' && startsWith(github.ref, 'refs/tags/py-') ||
github.event_name == 'workflow_dispatch'
outputs:
pr_smoke: ${{ steps.mode.outputs.pr_smoke }}
full_release: ${{ steps.mode.outputs.full_release }}
steps:
- name: Harden the runner (egress audit)
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- name: Resolve release mode
id: mode
env:
EVENT_NAME: ${{ github.event_name }}
REF: ${{ github.ref }}
REF_NAME: ${{ github.ref_name }}
TRIGGER_ON_PR_PUSH_VALUE: ${{ env.TRIGGER_ON_PR_PUSH }}
run: |
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
echo "pr_smoke=false" >> $GITHUB_OUTPUT
echo "full_release=true" >> $GITHUB_OUTPUT
elif [ "$EVENT_NAME" = "push" ] && [[ "$REF" == refs/tags/py-* ]]; then
echo "pr_smoke=false" >> $GITHUB_OUTPUT
echo "full_release=true" >> $GITHUB_OUTPUT
elif [ "$EVENT_NAME" = "push" ] && [[ "$REF_NAME" =~ ^(main|master|dev|development)$ ]]; then
echo "pr_smoke=false" >> $GITHUB_OUTPUT
echo "full_release=true" >> $GITHUB_OUTPUT
elif [ "$EVENT_NAME" = "pull_request" ] && [ "$TRIGGER_ON_PR_PUSH_VALUE" = "true" ]; then
echo "pr_smoke=true" >> $GITHUB_OUTPUT
echo "full_release=false" >> $GITHUB_OUTPUT
else
echo "pr_smoke=false" >> $GITHUB_OUTPUT
echo "full_release=false" >> $GITHUB_OUTPUT
fi
pr_smoke_build:
needs: resolve_release_mode
if: needs.resolve_release_mode.outputs.pr_smoke == 'true'
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Harden the runner (egress audit)
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
ref: ${{ github.event.inputs.sha || github.sha }}
submodules: recursive
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: "3.12"
- name: Install uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
with:
version: "0.11.14"
enable-cache: true
save-cache: ${{ github.event_name == 'push' && contains(fromJSON('["main", "master", "development", "dev"]'), github.ref_name) }}
- name: Set up Rust
run: |
bash scripts/ci/ensure-rust.sh stable minimal
export PATH="$HOME/.cargo/bin:$PATH"
rustup show
- name: Install just
uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4
- name: Cache Rust
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
cache-bin: false
prefix-key: v1-rust-no-bin
save-if: ${{ github.event_name == 'push' && contains(fromJSON('["main", "master", "development", "dev"]'), github.ref_name) }}
workspaces: |
python/pecos-rslib
python/pecos-rslib-llvm
- name: Cache LLVM
id: cache-llvm
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ~/.pecos/deps/llvm-21.1
key: llvm-${{ env.LLVM_RELEASE_VERSION }}-${{ runner.os }}-${{ runner.arch }}-v3
- name: Ensure LLVM
run: just ci-env
- name: Install Python build tooling
run: uv pip install --system build maturin
- name: Build pecos-rslib wheel
run: |
mkdir -p dist/pecos-rslib
maturin build --locked --manifest-path python/pecos-rslib/Cargo.toml --out dist/pecos-rslib
- name: Build pecos-rslib-llvm wheel
run: |
mkdir -p dist/pecos-rslib-llvm
maturin build --locked --manifest-path python/pecos-rslib-llvm/Cargo.toml --out dist/pecos-rslib-llvm
- name: Build quantum-pecos wheel
run: |
mkdir -p dist/quantum-pecos
cd python/quantum-pecos
python -m build --wheel --outdir ../../dist/quantum-pecos
- name: Upload PR smoke artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: pr-smoke-python-artifacts
path: |
dist/pecos-rslib/*.whl
dist/pecos-rslib-llvm/*.whl
dist/quantum-pecos/*.whl
if-no-files-found: error
pr_smoke_test:
needs: [resolve_release_mode, pr_smoke_build]
if: needs.resolve_release_mode.outputs.pr_smoke == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
python-version: ['3.10', '3.14']
steps:
- name: Harden the runner (egress audit)
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
with:
version: "0.11.14"
- name: Download PR smoke artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: pr-smoke-python-artifacts
path: ./pr-smoke-artifacts
- name: Install PR smoke artifacts
run: |
# Install all three local wheels in ONE resolver invocation: the local
# wheels satisfy quantum-pecos's exact pecos-rslib/-llvm pins (which do
# not exist on PyPI for unreleased versions) and every other dependency
# resolves from the wheel's own Requires-Dist -- no hand-maintained
# duplicate pin list that can drift from the package metadata.
uv pip install --system --force-reinstall \
./pr-smoke-artifacts/pecos-rslib/pecos_rslib-*.whl \
./pr-smoke-artifacts/pecos-rslib-llvm/pecos_rslib_llvm-*.whl \
./pr-smoke-artifacts/quantum-pecos/quantum_pecos-*.whl
uv pip check --system
- name: Import PR smoke artifacts
run: |
python -c 'from importlib.metadata import version; import pecos; import pecos_rslib; import pecos_rslib_llvm; print({"pecos": pecos.__version__, "pecos_rslib": pecos_rslib.__version__, "pecos_rslib_llvm": version("pecos-rslib-llvm")})'
build_wheelspecos_rslib:
needs: resolve_release_mode
if: needs.resolve_release_mode.outputs.full_release == 'true'
runs-on: ${{ matrix.runner || matrix.os }}
strategy:
fail-fast: false
matrix:
include:
# Linux x86_64 with CUDA support. Build in manylinux 2.28 and use the
# GCC 13 toolset that previously produced policy-compatible wheels.
- os: ubuntu-latest
architecture: x86_64
cibw_archs: x86_64
install_cuda: true
python_command: python3
expected_wheel_platform_tag: manylinux_2_28_x86_64
# Linux aarch64 - temporarily disabled while the LLVM 21.1
# manylinux 2.28 bootstrap is validated on the primary x86_64 lane.
# - os: ubuntu-24.04-arm
# architecture: aarch64
# runner: ubuntu-24.04-arm
# cibw_archs: aarch64
# install_cuda: false
# gcc_path_prefix: ""
# gcc_ld_path: ""
# gcc_cc: ""
# gcc_cxx: ""
# macOS without CUDA (NVIDIA dropped macOS CUDA support in 2019)
# Use macos-15 builders (not macos-14) because Xcode 15.4's libc++
# has a pointer_traits bug with CXX crate's contiguous iterators in
# C++20. MACOSX_DEPLOYMENT_TARGET still produces macOS 13 wheels.
- os: macos-15
architecture: aarch64
install_cuda: false
python_command: python3
expected_wheel_platform_tag: macosx_13_0_arm64
- os: macos-15-intel
architecture: x86_64
install_cuda: false
python_command: python3
expected_wheel_platform_tag: macosx_13_0_x86_64
# Windows x86_64 with CUDA support
# Note: Uses specific sub-packages to avoid VS integration bug (see Jimver/cuda-toolkit#382)
- os: windows-2022
architecture: x86_64
install_cuda: true
python_command: python
expected_wheel_platform_tag: win_amd64
steps:
- name: Harden the runner (egress audit)
if: runner.os == 'Linux'
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
ref: ${{ github.event.inputs.sha || github.sha }}
submodules: recursive
- name: Build LLVM 21.1 for manylinux 2.28
if: runner.os == 'Linux'
run: |
mkdir -p .ci-llvm
docker run --rm \
--volume "$PWD:/project" \
--workdir /project \
quay.io/pypa/manylinux_2_28_x86_64:2026.01.04-1 \
bash scripts/ci/build-llvm-21-manylinux.sh \
/project/.ci-llvm/llvm-21.1.8-manylinux_2_28-x86_64.tar.xz
- name: Build LLVM 21.1 for macOS 13
if: runner.os == 'macOS'
run: |
mkdir -p .ci-llvm
bash scripts/ci/build-llvm-21-macos.sh \
.ci-llvm/llvm-21.1.8-macos-${{ matrix.architecture }}.tar.gz
# Set up Visual Studio environment on Windows (required for nvcc to find cl.exe)
- name: Set up Visual Studio environment (Windows)
if: runner.os == 'Windows' && matrix.install_cuda
shell: pwsh
run: ./scripts/ci/setup-msvc.ps1 -Arch x64 -HostArch x64
# Install CUDA on Windows before cibuildwheel (cibuildwheel runs on host, not in containers)
# Uses specific sub-packages to avoid VS 17.3.x bug that hangs on NSight/VS Integration
# See: https://github.com/Jimver/cuda-toolkit/issues/382
- name: Install CUDA Toolkit (Windows)
if: runner.os == 'Windows' && matrix.install_cuda
uses: Jimver/cuda-toolkit@3d45d157f327c09c04b50ee6ccdea2d9d017ec76 # v0.2.35
with:
cuda: '12.5.1'
method: 'local'
sub-packages: '["nvcc", "cudart", "cublas", "cublas_dev", "thrust"]'
# On Windows, Git Bash's /usr/bin/link.exe shadows MSVC's link.exe, so
# pin the linker in .cargo/config.toml for the cargo commands cibuildwheel
# runs on the host. Derive it from VCToolsInstallDir -- the toolset the
# prior setup-msvc.ps1 step's VsDevCmd actually configured -- so the
# linker always matches the LIB/INCLUDE in effect (no lexical toolset
# guessing, which could select a mismatched toolset and cause LNK1181).
- name: Configure MSVC linker for Cargo (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$vcTools = $env:VCToolsInstallDir
if (-not $vcTools) {
Write-Error "VCToolsInstallDir not set; the setup-msvc.ps1 step must run first"
exit 1
}
$linkPath = Join-Path ($vcTools.TrimEnd('\', '/')) "bin\Hostx64\x64\link.exe"
if (-not (Test-Path $linkPath)) {
Write-Error "MSVC link.exe not found at $linkPath"
exit 1
}
$escapedPath = $linkPath.Replace('\', '/')
$configContent = "[target.x86_64-pc-windows-msvc]`nlinker = `"$escapedPath`""
# cibuildwheel and maturin run in different working directories;
# write the config where each one looks for it.
New-Item -ItemType Directory -Force -Path .cargo | Out-Null
$configContent | Out-File -FilePath ".cargo\config.toml" -Encoding UTF8
New-Item -ItemType Directory -Force -Path "python\pecos-rslib\.cargo" | Out-Null
$configContent | Out-File -FilePath "python\pecos-rslib\.cargo\config.toml" -Encoding UTF8
$cargoHome = if ($env:CARGO_HOME) { $env:CARGO_HOME } else { "$env:USERPROFILE\.cargo" }
New-Item -ItemType Directory -Force -Path $cargoHome | Out-Null
if (Test-Path "$cargoHome\config.toml") {
"`n$configContent" | Out-File -FilePath "$cargoHome\config.toml" -Encoding UTF8 -Append
} else {
$configContent | Out-File -FilePath "$cargoHome\config.toml" -Encoding UTF8
}
Write-Host "Configured MSVC linker: $linkPath"
- name: Build wheels
uses: pypa/cibuildwheel@298ed2fb2c105540f5ed055e8a6ad78d82dd3a7e # v3.3.1
with:
package-dir: python/pecos-rslib
output-dir: wheelhouse
env:
# Build configuration
CIBW_BUILD: "cp310-*"
CIBW_SKIP: "*-win32 *-manylinux_i686 *-musllinux*"
CIBW_ARCHS_LINUX: ${{ matrix.cibw_archs }}
CIBW_MANYLINUX_X86_64_IMAGE: "manylinux_2_28"
CIBW_MANYLINUX_AARCH64_IMAGE: "manylinux_2_28"
# Linux configuration - CUDA paths are conditional via matrix variables
CIBW_ENVIRONMENT_LINUX: >
PATH=/opt/rh/gcc-toolset-13/root/usr/bin:$HOME/.cargo/bin:$HOME/.pecos/deps/llvm-21.1/bin:$HOME/.pecos/deps/cmake-${{ env.PECOS_CMAKE_VERSION }}/bin:/usr/local/cuda-12.6/bin:$PATH
LD_LIBRARY_PATH=/opt/rh/gcc-toolset-13/root/usr/lib64:/opt/rh/gcc-toolset-13/root/usr/lib:$LD_LIBRARY_PATH
LLVM_SYS_211_PREFIX=$HOME/.pecos/deps/llvm-21.1
LIBCLANG_PATH=$HOME/.pecos/deps/llvm-21.1/lib
CMAKE=$HOME/.pecos/deps/cmake-${{ env.PECOS_CMAKE_VERSION }}/bin/cmake
CUDA_PATH=/usr/local/cuda-12.6
CC=/opt/rh/gcc-toolset-13/root/usr/bin/gcc
CXX=/opt/rh/gcc-toolset-13/root/usr/bin/g++
CUDAHOSTCXX=/opt/rh/gcc-toolset-13/root/usr/bin/g++
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=/opt/rh/gcc-toolset-13/root/usr/bin/gcc
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/opt/rh/gcc-toolset-13/root/usr/bin/gcc
MATURIN_PEP517_ARGS="--locked --features=extension-module,mwpf"
CIBW_BEFORE_ALL_LINUX: |
bash scripts/ci/ensure-rust.sh stable minimal
export PATH=$HOME/.cargo/bin:$PATH
dnf install -y bzip2 gcc-toolset-13 libffi-devel xz
source /opt/rh/gcc-toolset-13/enable
export CC=/opt/rh/gcc-toolset-13/root/usr/bin/gcc
export CXX=/opt/rh/gcc-toolset-13/root/usr/bin/g++
export CUDAHOSTCXX=/opt/rh/gcc-toolset-13/root/usr/bin/g++
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=/opt/rh/gcc-toolset-13/root/usr/bin/gcc
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/opt/rh/gcc-toolset-13/root/usr/bin/gcc
# Install CUDA Toolkit for GPU support on x86_64 (compile-time only, no GPU needed)
if [ "${{ matrix.install_cuda }}" = "true" ]; then
echo "Installing CUDA Toolkit from NVIDIA repos..."
. /etc/os-release
CUDA_RHEL_MAJOR="${VERSION_ID%%.*}"
dnf config-manager --add-repo "https://developer.download.nvidia.com/compute/cuda/repos/rhel${CUDA_RHEL_MAJOR}/x86_64/cuda-rhel${CUDA_RHEL_MAJOR}.repo"
dnf install -y cuda-nvcc-12-6 cuda-cudart-devel-12-6 libcublas-devel-12-6
export CUDA_PATH=/usr/local/cuda-12.6
export PATH=$CUDA_PATH/bin:$PATH
echo "CUDA installed at $CUDA_PATH"
nvcc --version
else
echo "Skipping CUDA installation (GPU support not enabled for this build)"
fi
"$CC" --version
"$CXX" --version
bash scripts/ci/install-llvm-21-manylinux.sh .ci-llvm/llvm-21.1.8-manylinux_2_28-x86_64.tar.xz
cargo run --locked --release -p pecos-cli -- llvm configure "$HOME/.pecos/deps/llvm-21.1"
cargo run --locked --release -p pecos-cli -- install cmake --force
CIBW_REPAIR_WHEEL_COMMAND_LINUX: >
LD_LIBRARY_PATH=$HOME/.pecos/deps/llvm-21.1/lib:$LD_LIBRARY_PATH
auditwheel repair --plat manylinux_2_28_${{ matrix.cibw_archs }} -w {dest_dir} {wheel} &&
pipx run abi3audit --strict --report {wheel}
# macOS configuration
CIBW_ENVIRONMENT_MACOS: >
PATH=$HOME/.cargo/bin:$HOME/.pecos/deps/llvm-21.1/bin:$HOME/.pecos/deps/cmake-${{ env.PECOS_CMAKE_VERSION }}/CMake.app/Contents/bin:$PATH
LLVM_SYS_211_PREFIX=$HOME/.pecos/deps/llvm-21.1
LIBCLANG_PATH=$HOME/.pecos/deps/llvm-21.1/lib
CMAKE=$HOME/.pecos/deps/cmake-${{ env.PECOS_CMAKE_VERSION }}/CMake.app/Contents/bin/cmake
MACOSX_DEPLOYMENT_TARGET=${{ env.MACOS_WHEEL_DEPLOYMENT_TARGET }}
SDKROOT=$(xcrun --show-sdk-path)
MATURIN_PEP517_ARGS="--locked --features=extension-module,mwpf"
CIBW_BEFORE_ALL_MACOS: |
bash scripts/ci/ensure-rust.sh stable minimal
export PATH=$HOME/.cargo/bin:$PATH
rustup update
bash scripts/ci/install-llvm-21-macos.sh .ci-llvm/llvm-21.1.8-macos-${{ matrix.architecture }}.tar.gz
cargo run --locked --release -p pecos-cli -- llvm configure "$HOME/.pecos/deps/llvm-21.1"
cargo run --locked --release -p pecos-cli -- install cmake --force
# Create a codesign wrapper that strips DYLD_LIBRARY_PATH to prevent
# crashes on macOS 15 when bundled libc++ conflicts with system libc++
mkdir -p $HOME/.pecos/bin
printf '#!/bin/bash\nunset DYLD_LIBRARY_PATH\nexec /usr/bin/codesign "$@"\n' > $HOME/.pecos/bin/codesign
chmod +x $HOME/.pecos/bin/codesign
CIBW_REPAIR_WHEEL_COMMAND_MACOS: >
PATH=$HOME/.pecos/bin:$PATH DYLD_LIBRARY_PATH=$HOME/.pecos/deps/llvm-21.1/lib delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel} &&
pipx run abi3audit --strict --report {wheel}
# Windows configuration - CUDA via Jimver/cuda-toolkit (installed before cibuildwheel)
CIBW_ENVIRONMENT_WINDOWS: >
PATH="C:\\Users\\runneradmin\\.pecos\\deps\\llvm-21.1\\Library\\bin;C:\\Users\\runneradmin\\.pecos\\deps\\cmake-${{ env.PECOS_CMAKE_VERSION }}\\bin;$PATH"
LLVM_SYS_211_PREFIX="C:\\Users\\runneradmin\\.pecos\\deps\\llvm-21.1\\Library"
LIBCLANG_PATH="C:\\Users\\runneradmin\\.pecos\\deps\\llvm-21.1\\Library\\bin"
CMAKE="C:\\Users\\runneradmin\\.pecos\\deps\\cmake-${{ env.PECOS_CMAKE_VERSION }}\\bin\\cmake.exe"
MATURIN_PEP517_ARGS="--locked --features=extension-module,mwpf"
CIBW_BEFORE_ALL_WINDOWS: >
echo "=== Installing conda-forge LLVM 21.1 ===" &&
rustup update &&
powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\ci\install-llvm-21-windows.ps1 -InstallDir "C:\Users\runneradmin\.pecos\deps\llvm-21.1" -Version ${{ env.LLVM_RELEASE_VERSION }} &&
cargo run --locked --release -p pecos-cli -- llvm configure "C:\Users\runneradmin\.pecos\deps\llvm-21.1\Library" &&
cargo run --locked --release -p pecos-cli -- install cmake --force &&
echo "=== Checking LLVM installation ===" &&
if exist "C:\Users\runneradmin\.pecos\deps\llvm-21.1\Library\bin\llvm-config.exe" (echo LLVM directory exists) else (echo ERROR: LLVM directory not found! && exit /b 1)
# Install delvewheel and patch it to ignore ext-ms-win-* API sets
# (delvewheel ignores api-ms-win-* but not ext-ms-win-* which are also Windows API sets)
CIBW_BEFORE_BUILD_WINDOWS: >
pip install delvewheel &&
python -c "import delvewheel._dll_list as d,inspect,re as r;p=inspect.getfile(d);c=open(p).read();n=chr(10);open(p,'w').write(c.replace(r\"re.compile('api-.*'),\",r\"re.compile('api-.*'),\"+n+r\" re.compile('ext-.*'),\")) if 'ext-.*' not in c else None"
# Note: --no-dll excludes Windows system DLLs that should not be bundled
# combase.dll and rmclient.dll are core Windows components that fail when bundled
CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: >
delvewheel repair -v --add-path "C:\\Users\\runneradmin\\.pecos\\deps\\llvm-21.1\\Library\\bin" --no-dll "combase.dll;rmclient.dll" -w {dest_dir} {wheel} &&
pipx run abi3audit --strict --report {wheel}
- name: Verify pecos-rslib wheel platform tag
run: >
${{ matrix.python_command }} scripts/ci/check_wheel_platform_tags.py
--expected-platform-tag "${{ matrix.expected_wheel_platform_tag }}"
wheelhouse/*.whl
- name: Upload pecos-rslib wheels
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: wheel-pecos-rslib-${{ matrix.os }}-${{ matrix.architecture }}
path: ./wheelhouse/*.whl
if-no-files-found: error
# Build pecos-rslib-llvm wheel (reuses LLVM already installed by pecos-rslib build)
- name: Build pecos-rslib-llvm wheels
uses: pypa/cibuildwheel@298ed2fb2c105540f5ed055e8a6ad78d82dd3a7e # v3.3.1
with:
package-dir: python/pecos-rslib-llvm
output-dir: wheelhouse-llvm
env:
CIBW_BUILD: "cp310-*"
CIBW_SKIP: "*-win32 *-manylinux_i686 *-musllinux*"
CIBW_ARCHS_LINUX: ${{ matrix.cibw_archs }}
CIBW_MANYLINUX_X86_64_IMAGE: "manylinux_2_28"
CIBW_MANYLINUX_AARCH64_IMAGE: "manylinux_2_28"
CIBW_ENVIRONMENT_LINUX: >
PATH=/opt/rh/gcc-toolset-13/root/usr/bin:$HOME/.cargo/bin:$HOME/.pecos/deps/llvm-21.1/bin:$PATH
LD_LIBRARY_PATH=/opt/rh/gcc-toolset-13/root/usr/lib64:/opt/rh/gcc-toolset-13/root/usr/lib:$HOME/.pecos/deps/llvm-21.1/lib:$LD_LIBRARY_PATH
LLVM_SYS_211_PREFIX=$HOME/.pecos/deps/llvm-21.1
LIBCLANG_PATH=$HOME/.pecos/deps/llvm-21.1/lib
CC=/opt/rh/gcc-toolset-13/root/usr/bin/gcc
CXX=/opt/rh/gcc-toolset-13/root/usr/bin/g++
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=/opt/rh/gcc-toolset-13/root/usr/bin/gcc
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/opt/rh/gcc-toolset-13/root/usr/bin/gcc
CIBW_BEFORE_ALL_LINUX: |
bash scripts/ci/ensure-rust.sh stable minimal
export PATH=$HOME/.cargo/bin:$PATH
dnf install -y bzip2 gcc-toolset-13 libffi-devel xz
source /opt/rh/gcc-toolset-13/enable
export CC=/opt/rh/gcc-toolset-13/root/usr/bin/gcc
export CXX=/opt/rh/gcc-toolset-13/root/usr/bin/g++
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=/opt/rh/gcc-toolset-13/root/usr/bin/gcc
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/opt/rh/gcc-toolset-13/root/usr/bin/gcc
"$CC" --version
"$CXX" --version
bash scripts/ci/install-llvm-21-manylinux.sh .ci-llvm/llvm-21.1.8-manylinux_2_28-x86_64.tar.xz
cargo run --locked --release -p pecos-cli -- llvm configure "$HOME/.pecos/deps/llvm-21.1"
CIBW_REPAIR_WHEEL_COMMAND_LINUX: >
LD_LIBRARY_PATH=$HOME/.pecos/deps/llvm-21.1/lib:$LD_LIBRARY_PATH
auditwheel repair --plat manylinux_2_28_${{ matrix.cibw_archs }} -w {dest_dir} {wheel} &&
pipx run abi3audit --strict --report {wheel}
CIBW_ENVIRONMENT_MACOS: >
PATH=$HOME/.cargo/bin:$HOME/.pecos/deps/llvm-21.1/bin:$PATH
LLVM_SYS_211_PREFIX=$HOME/.pecos/deps/llvm-21.1
LIBCLANG_PATH=$HOME/.pecos/deps/llvm-21.1/lib
MACOSX_DEPLOYMENT_TARGET=${{ env.MACOS_WHEEL_DEPLOYMENT_TARGET }}
SDKROOT=$(xcrun --show-sdk-path)
CIBW_BEFORE_ALL_MACOS: |
if ! command -v cargo >/dev/null 2>&1; then
bash scripts/ci/ensure-rust.sh stable minimal
export PATH=$HOME/.cargo/bin:$PATH
fi
if [ ! -d "$HOME/.pecos/deps/llvm-21.1/bin" ]; then
rustup update
bash scripts/ci/install-llvm-21-macos.sh .ci-llvm/llvm-21.1.8-macos-${{ matrix.architecture }}.tar.gz
cargo run --locked --release -p pecos-cli -- llvm configure "$HOME/.pecos/deps/llvm-21.1"
else
echo "LLVM already installed from pecos-rslib build, skipping"
fi
mkdir -p $HOME/.pecos/bin
printf '#!/bin/bash\nunset DYLD_LIBRARY_PATH\nexec /usr/bin/codesign "$@"\n' > $HOME/.pecos/bin/codesign
chmod +x $HOME/.pecos/bin/codesign
CIBW_REPAIR_WHEEL_COMMAND_MACOS: >
PATH=$HOME/.pecos/bin:$PATH DYLD_LIBRARY_PATH=$HOME/.pecos/deps/llvm-21.1/lib delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel} &&
pipx run abi3audit --strict --report {wheel}
CIBW_ENVIRONMENT_WINDOWS: >
PATH="C:\\Users\\runneradmin\\.pecos\\deps\\llvm-21.1\\Library\\bin;$PATH"
LLVM_SYS_211_PREFIX="C:\\Users\\runneradmin\\.pecos\\deps\\llvm-21.1\\Library"
LIBCLANG_PATH="C:\\Users\\runneradmin\\.pecos\\deps\\llvm-21.1\\Library\\bin"
CIBW_BEFORE_ALL_WINDOWS: >
rustup update &&
if not exist "C:\Users\runneradmin\.pecos\deps\llvm-21.1\Library\bin\llvm-config.exe" (powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\ci\install-llvm-21-windows.ps1 -InstallDir "C:\Users\runneradmin\.pecos\deps\llvm-21.1" -Version ${{ env.LLVM_RELEASE_VERSION }}) else (echo LLVM already installed from pecos-rslib build) &&
cargo run --locked --release -p pecos-cli -- llvm configure "C:\Users\runneradmin\.pecos\deps\llvm-21.1\Library"
CIBW_BEFORE_BUILD_WINDOWS: >
pip install delvewheel &&
python -c "import delvewheel._dll_list as d,inspect,re as r;p=inspect.getfile(d);c=open(p).read();n=chr(10);open(p,'w').write(c.replace(r\"re.compile('api-.*'),\",r\"re.compile('api-.*'),\"+n+r\" re.compile('ext-.*'),\")) if 'ext-.*' not in c else None"
CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: >
delvewheel repair -v --add-path "C:\\Users\\runneradmin\\.pecos\\deps\\llvm-21.1\\Library\\bin" --no-dll "combase.dll;rmclient.dll" -w {dest_dir} {wheel} &&
pipx run abi3audit --strict --report {wheel}
- name: Verify pecos-rslib-llvm wheel platform tag
run: >
${{ matrix.python_command }} scripts/ci/check_wheel_platform_tags.py
--expected-platform-tag "${{ matrix.expected_wheel_platform_tag }}"
wheelhouse-llvm/*.whl
- name: Upload pecos-rslib-llvm wheels
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: wheel-pecos-rslib-llvm-${{ matrix.os }}-${{ matrix.architecture }}
path: ./wheelhouse-llvm/*.whl
if-no-files-found: error
test_abi3_wheels:
needs: build_wheelspecos_rslib
if: |
always() &&
needs.build_wheelspecos_rslib.result == 'success'
runs-on: ${{ matrix.platform.runner }}
strategy:
fail-fast: false
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
platform:
- runner: ubuntu-latest
os: ubuntu-latest
architecture: x86_64
- runner: windows-2022
os: windows-2022
architecture: x86_64
- runner: macos-15-intel
os: macos-15-intel
architecture: x86_64
# Build on macOS 15, then exercise the arm64 wheel on the oldest
# standard macOS runner still available from GitHub.
- runner: macos-14
os: macos-15
architecture: aarch64
steps:
- name: Harden the runner (egress audit)
if: runner.os == 'Linux'
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
ref: ${{ github.event.inputs.sha || github.sha }}
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: ${{ matrix.python-version }}
- name: Download abi3 wheel
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: wheel-pecos-rslib-${{ matrix.platform.os }}-${{ matrix.platform.architecture }}
path: ./pecos-rslib-wheel
- name: Test abi3 wheel with Python ${{ matrix.python-version }}
run: |
echo "Testing abi3 wheel with Python ${{ matrix.python-version }}"
python --version
pip install --force-reinstall --verbose ./pecos-rslib-wheel/*.whl
python -c 'import pecos_rslib; print(f"pecos_rslib version: {pecos_rslib.__version__}")'
python -c 'import sys; print(f"Python version: {sys.version}")'
- name: Debug DLL loading (Windows)
if: failure() && runner.os == 'Windows'
shell: pwsh
run: |
Write-Host "=== Listing wheel contents ==="
$wheelFile = Get-ChildItem -Path ./pecos-rslib-wheel/*.whl | Select-Object -First 1
python -m zipfile -l $wheelFile
Write-Host "`n=== Finding pecos_rslib installation ==="
$sitePackages = python -c "import site; print(site.getsitepackages()[0])"
Write-Host "Site packages: $sitePackages"
Write-Host "`n=== Listing pecos_rslib directory ==="
Get-ChildItem -Path "$sitePackages\pecos_rslib*" -Recurse | Select-Object FullName, Length
Write-Host "`n=== Checking DLL dependencies with dumpbin ==="
$pydFile = Get-ChildItem -Path "$sitePackages\pecos_rslib\*.pyd" -ErrorAction SilentlyContinue | Select-Object -First 1
if ($pydFile) {
& "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.44.35207\bin\Hostx64\x64\dumpbin.exe" /dependents $pydFile.FullName 2>&1 | Select-Object -First 50
}
Write-Host "`n=== Checking pecos_rslib.libs directory ==="
$libsDir = "$sitePackages\pecos_rslib.libs"
if (Test-Path $libsDir) {
Get-ChildItem -Path $libsDir | ForEach-Object {
Write-Host "DLL: $($_.Name)"
& "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.44.35207\bin\Hostx64\x64\dumpbin.exe" /dependents $_.FullName 2>&1 | Select-Object -First 30
}
} else {
Write-Host "No pecos_rslib.libs directory found"
}
test_manylinux_2_28_wheels:
needs: build_wheelspecos_rslib
if: |
always() &&
needs.build_wheelspecos_rslib.result == 'success'
runs-on: ubuntu-latest
container: quay.io/pypa/manylinux_2_28_x86_64
steps:
- name: Harden the runner (egress audit)
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- name: Download pecos-rslib wheel
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: wheel-pecos-rslib-ubuntu-latest-x86_64
path: ./pecos-rslib-wheel
- name: Download pecos-rslib-llvm wheel
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: wheel-pecos-rslib-llvm-ubuntu-latest-x86_64
path: ./pecos-rslib-llvm-wheel
- name: Install and import wheels on glibc 2.28
run: |
/opt/python/cp310-cp310/bin/python -m pip install --no-index \
./pecos-rslib-wheel/*.whl \
./pecos-rslib-llvm-wheel/*.whl
/opt/python/cp310-cp310/bin/python -c 'import pecos_rslib; import pecos_rslib_llvm; print({"pecos_rslib": pecos_rslib.__version__})'
build_sdist_quantum_pecos:
needs: build_wheelspecos_rslib
if: |
always() &&
needs.build_wheelspecos_rslib.result == 'success'
runs-on: ubuntu-latest
steps:
- name: Harden the runner (egress audit)
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
ref: ${{ github.event.inputs.sha || github.sha }}
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: '3.10'
- name: Download pecos-rslib wheel
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: wheel-pecos-rslib-ubuntu-latest-x86_64
path: ./pecos-rslib-wheel
- name: Download pecos-rslib-llvm wheel
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: wheel-pecos-rslib-llvm-ubuntu-latest-x86_64
path: ./pecos-rslib-llvm-wheel
- name: Install pecos-rslib and pecos-rslib-llvm
run: |
pip install ./pecos-rslib-wheel/*.whl
pip install ./pecos-rslib-llvm-wheel/*.whl
- name: Install build dependencies
run: pip install build
- name: Build quantum-pecos SDist
run: |
cd python/quantum-pecos
python -m build --sdist --outdir dist
- name: Test quantum-pecos SDist
run: |
pip install python/quantum-pecos/dist/*.tar.gz
python -c 'import pecos; print(pecos.__version__)'
- name: Upload quantum-pecos SDist
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: sdist-quantum-pecos
path: python/quantum-pecos/dist/*.tar.gz
if-no-files-found: error
build_wheels_quantum_pecos:
needs: build_wheelspecos_rslib
if: |
always() &&
needs.build_wheelspecos_rslib.result == 'success'
runs-on: ubuntu-latest
steps:
- name: Harden the runner (egress audit)
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
ref: ${{ github.event.inputs.sha || github.sha }}
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: '3.10'
- name: Download pecos-rslib wheel
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: wheel-pecos-rslib-ubuntu-latest-x86_64
path: ./pecos-rslib-wheel
- name: Download pecos-rslib-llvm wheel
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: wheel-pecos-rslib-llvm-ubuntu-latest-x86_64
path: ./pecos-rslib-llvm-wheel
- name: Install pecos-rslib and pecos-rslib-llvm
run: |
pip install ./pecos-rslib-wheel/*.whl
pip install ./pecos-rslib-llvm-wheel/*.whl
- name: Install build dependencies
run: pip install build
- name: Build quantum-pecos wheel
run: |
cd python/quantum-pecos
python -m build --wheel --outdir dist
- name: Test quantum-pecos wheel
run: |
pip install python/quantum-pecos/dist/*.whl
python -c 'import pecos; print(pecos.__version__)'
- name: Upload quantum-pecos wheel
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: wheel-quantum-pecos
path: python/quantum-pecos/dist/*.whl
if-no-files-found: error
collect_artifacts:
needs:
- build_wheelspecos_rslib
- build_sdist_quantum_pecos
- build_wheels_quantum_pecos
- test_abi3_wheels
- test_manylinux_2_28_wheels
if: |
needs.build_wheelspecos_rslib.result == 'success' &&
needs.build_sdist_quantum_pecos.result == 'success' &&
needs.build_wheels_quantum_pecos.result == 'success' &&
needs.test_abi3_wheels.result == 'success' &&
needs.test_manylinux_2_28_wheels.result == 'success'
runs-on: ubuntu-latest
steps:
- name: Harden the runner (egress audit)
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- name: Create distribution directories
run: |
mkdir -p dist/pecos-rslib
mkdir -p dist/pecos-rslib-llvm
mkdir -p dist/quantum-pecos
# Download artifacts into temp directory first
- name: Download all artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
path: temp-artifacts/
- name: Organize distribution files
run: |
shopt -s nullglob
# Debug: Show what we downloaded
echo "=== Downloaded artifacts structure ==="
ls -la temp-artifacts/
# Move pecos-rslib and pecos-rslib-llvm wheels to distribution directory
llvm_dirs=(temp-artifacts/wheel-pecos-rslib-llvm-*/)
if [ "${#llvm_dirs[@]}" -eq 0 ]; then
echo "Expected pecos-rslib-llvm artifact directories, but none were found."
exit 1
fi
for artifact in temp-artifacts/wheel-pecos-rslib-llvm-*/; do
if [ -d "$artifact" ]; then
echo "Processing $artifact"
llvm_wheels=("$artifact"*.whl)
if [ "${#llvm_wheels[@]}" -eq 0 ]; then
echo "Expected pecos-rslib-llvm wheels in $artifact, but none were found."
exit 1
fi
mv "${llvm_wheels[@]}" dist/pecos-rslib-llvm/
fi
done
# Collect only base pecos-rslib dirs; exclude pecos-rslib-llvm-*,
# whose prefix overlaps the glob and whose wheels were moved above.
rslib_dirs=()
for artifact in temp-artifacts/wheel-pecos-rslib-*/; do
case "$artifact" in
temp-artifacts/wheel-pecos-rslib-llvm-*/) continue ;;
esac
rslib_dirs+=("$artifact")
done
if [ "${#rslib_dirs[@]}" -eq 0 ]; then
echo "Expected pecos-rslib artifact directories, but none were found."
exit 1
fi
for artifact in "${rslib_dirs[@]}"; do
if [ -d "$artifact" ]; then
echo "Processing $artifact"
rslib_wheels=("$artifact"*.whl)
if [ "${#rslib_wheels[@]}" -eq 0 ]; then
echo "Expected pecos-rslib wheels in $artifact, but none were found."
exit 1
fi
mv "${rslib_wheels[@]}" dist/pecos-rslib/
fi
done
# Move quantum-pecos files to distribution directory
quantum_dirs=(temp-artifacts/*-quantum-pecos*/)
if [ "${#quantum_dirs[@]}" -eq 0 ]; then
echo "Expected quantum-pecos artifact directories, but none were found."
exit 1
fi
for artifact in temp-artifacts/*-quantum-pecos*/; do
if [ -d "$artifact" ]; then
echo "Processing $artifact"
quantum_dists=("$artifact"*.whl "$artifact"*.tar.gz)
if [ "${#quantum_dists[@]}" -eq 0 ]; then
echo "Expected quantum-pecos distributions in $artifact, but none were found."
exit 1
fi
mv "${quantum_dists[@]}" dist/quantum-pecos/
fi
done
llvm_wheels=(dist/pecos-rslib-llvm/*.whl)
if [ "${#llvm_wheels[@]}" -eq 0 ]; then
echo "Expected collected pecos-rslib-llvm wheels, but none were found."
exit 1
fi
# Clean up
rm -rf temp-artifacts
- name: List all collected artifacts
run: |
echo "=== pecos-rslib artifacts ==="
ls -la dist/pecos-rslib/
echo ""
echo "=== pecos-rslib-llvm artifacts ==="
ls -la dist/pecos-rslib-llvm/
echo ""
echo "=== quantum-pecos artifacts ==="
ls -la dist/quantum-pecos/
echo ""
echo "=== Summary ==="
echo "pecos-rslib wheels: $(ls -1 dist/pecos-rslib/*.whl 2>/dev/null | wc -l)"
echo "pecos-rslib-llvm wheels: $(ls -1 dist/pecos-rslib-llvm/*.whl 2>/dev/null | wc -l)"
echo "quantum-pecos distributions: $(ls -1 dist/quantum-pecos/* 2>/dev/null | wc -l)"
- name: Upload distribution bundle
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: pecos-distribution
path: dist/
if-no-files-found: error