Skip to content

Structure

Structure #841

Workflow file for this run

name: Python Artifacts
env:
TRIGGER_ON_PR_PUSH: true # Set to true to enable triggers on PR pushes
on:
push:
branches:
- main
- dev
- development
- master
tags:
- 'py-*'
pull_request:
branches:
- main
- dev
- development
- master
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:
check_pr_push:
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-')
outputs:
run: ${{ steps.check.outputs.run }}
steps:
- name: Check if should run on PR push
id: check
run: |
# Always run on tag pushes (py-* tags trigger releases)
if [ "${{ github.event_name }}" = "push" ] && [[ "${{ github.ref }}" == refs/tags/py-* ]]; then
echo "run=true" >> $GITHUB_OUTPUT
# Always run on pushes to main branches
elif [ "${{ github.event_name }}" = "push" ] && [[ "${{ github.ref_name }}" =~ ^(main|master|dev|development)$ ]]; then
echo "run=true" >> $GITHUB_OUTPUT
# For PRs, check the TRIGGER_ON_PR_PUSH setting
elif [ "${{ github.event_name }}" = "pull_request" ] && [ "${{ env.TRIGGER_ON_PR_PUSH }}" = "true" ]; then
echo "run=true" >> $GITHUB_OUTPUT
else
echo "run=false" >> $GITHUB_OUTPUT
fi
build_wheelspecos_rslib:
needs: check_pr_push
if: needs.check_pr_push.result == 'success' && needs.check_pr_push.outputs.run == 'true'
runs-on: ${{ matrix.runner || matrix.os }}
strategy:
fail-fast: false
matrix:
include:
# Linux x86_64 with CUDA support (uses GCC Toolset 13 for CUDA compatibility)
- os: ubuntu-latest
architecture: x86_64
cibw_archs: x86_64
cuda_feature: "--features cuda"
install_cuda: true
# GCC Toolset 13 paths for CUDA compatibility
gcc_path_prefix: "/opt/rh/gcc-toolset-13/root/usr/bin:"
gcc_ld_path: "/opt/rh/gcc-toolset-13/root/usr/lib64:/opt/rh/gcc-toolset-13/root/usr/lib:"
gcc_cc: "/opt/rh/gcc-toolset-13/root/usr/bin/gcc"
gcc_cxx: "/opt/rh/gcc-toolset-13/root/usr/bin/g++"
# Linux aarch64 - DISABLED: LLVM 14 not available in manylinux_2_28 (AlmaLinux 8)
# The prebuilt LLVM 14 binary is incompatible with the container, and
# the llvm-toolset module only provides LLVM 13. Re-enable when we have
# a solution (custom Docker image, build from source, or inkwell LLVM 13 support)
# - os: ubuntu-24.04-arm
# architecture: aarch64
# runner: ubuntu-24.04-arm
# cibw_archs: aarch64
# cuda_feature: ""
# 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 (not macos-14) because Xcode 15.4's libc++ has a
# pointer_traits bug with CXX crate's contiguous iterators in C++20.
- os: macos-15
architecture: aarch64
cuda_feature: ""
install_cuda: false
- os: macos-15-intel
architecture: x86_64
cuda_feature: ""
install_cuda: false
# 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
cuda_feature: "--features cuda"
install_cuda: true
steps:
- uses: actions/checkout@v6
with:
ref: ${{ inputs.sha || github.sha }}
submodules: recursive
# 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
uses: ilammy/msvc-dev-cmd@v1
with:
arch: 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@v0.2.30
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.
# Create .cargo/config.toml with the explicit MSVC linker path so cargo
# uses the correct linker inside cibuildwheel (which runs on the host).
- name: Configure MSVC linker for Cargo (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$vsWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
$vsPath = & $vsWhere -latest -property installationPath
$linkPath = Get-ChildItem -Path "$vsPath\VC\Tools\MSVC" -Recurse -Filter "link.exe" |
Where-Object { $_.FullName -like "*\bin\Hostx64\x64\*" } |
Select-Object -First 1 -ExpandProperty FullName
if ($linkPath) {
$escapedPath = $linkPath.Replace('\', '/')
$configContent = "[target.x86_64-pc-windows-msvc]`nlinker = `"$escapedPath`""
# Create in repo root (for CIBW_BEFORE_ALL cargo commands)
New-Item -ItemType Directory -Force -Path .cargo | Out-Null
$configContent | Out-File -FilePath ".cargo\config.toml" -Encoding UTF8
# Create in pecos-rslib directory (for maturin builds)
New-Item -ItemType Directory -Force -Path "python\pecos-rslib\.cargo" | Out-Null
$configContent | Out-File -FilePath "python\pecos-rslib\.cargo\config.toml" -Encoding UTF8
# Create in user cargo home (fallback)
$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"
} else {
Write-Error "Could not find MSVC link.exe"
exit 1
}
- name: Build wheels
uses: pypa/cibuildwheel@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 - GCC Toolset and CUDA paths are conditional via matrix variables
CIBW_ENVIRONMENT_LINUX: >
PATH=${{ matrix.gcc_path_prefix }}$HOME/.cargo/bin:$HOME/.pecos/llvm/bin:/usr/local/cuda-12.6/bin:$PATH
LD_LIBRARY_PATH=${{ matrix.gcc_ld_path }}$LD_LIBRARY_PATH
LLVM_SYS_140_PREFIX=$HOME/.pecos/llvm
CUDA_PATH=/usr/local/cuda-12.6
MATURIN_PEP517_ARGS="${{ matrix.cuda_feature }}"
CIBW_BEFORE_ALL_LINUX: |
curl -sSf https://sh.rustup.rs | sh -s -- -y
source $HOME/.cargo/env
dnf install libffi-devel -y
# Install CUDA Toolkit for GPU support on x86_64 (compile-time only, no GPU needed)
if [ "${{ matrix.install_cuda }}" = "true" ]; then
echo "Installing GCC 13 (required for CUDA 12.6 compatibility)..."
dnf install -y gcc-toolset-13
source /opt/rh/gcc-toolset-13/enable
echo "Installing CUDA Toolkit from NVIDIA repos..."
dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel8/x86_64/cuda-rhel8.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
gcc --version
else
echo "Skipping CUDA installation (GPU support not enabled for this build)"
fi
cargo run --release -p pecos --features cli -- install llvm --force
CIBW_REPAIR_WHEEL_COMMAND_LINUX: >
auditwheel repair -w {dest_dir} {wheel} &&
pipx run abi3audit --strict --report {wheel}
# macOS configuration
CIBW_ENVIRONMENT_MACOS: >
PATH=$HOME/.cargo/bin:$HOME/.pecos/llvm/bin:$PATH
LLVM_SYS_140_PREFIX=$HOME/.pecos/llvm
MACOSX_DEPLOYMENT_TARGET=13.2
CIBW_BEFORE_ALL_MACOS: |
curl -sSf https://sh.rustup.rs | sh -s -- -y
source $HOME/.cargo/env
rustup update
cargo run --release -p pecos --features cli -- install llvm --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/llvm/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\\llvm\\bin;$PATH"
LLVM_SYS_140_PREFIX="C:\\Users\\runneradmin\\.pecos\\llvm"
MATURIN_PEP517_ARGS="${{ matrix.cuda_feature }}"
CIBW_BEFORE_ALL_WINDOWS: >
echo "=== Installing LLVM using pecos ===" &&
rustup update &&
echo "=== Running pecos install llvm ===" &&
cargo run --release -p pecos --features cli -- install llvm --force &&
echo "=== Checking LLVM installation ===" &&
(test -d "C:\\Users\\runneradmin\\.pecos\\llvm" && echo "LLVM directory exists" && ls -la "C:\\Users\\runneradmin\\.pecos\\llvm" && (ls -la "C:\\Users\\runneradmin\\.pecos\\llvm\\bin" || echo "bin directory not found")) || (echo "ERROR: LLVM directory not found!" && exit 1) &&
echo "=== Verifying LLVM_SYS_140_PREFIX ===" &&
echo "LLVM_SYS_140_PREFIX will be set to: C:\\Users\\runneradmin\\.pecos\\llvm"
# 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\\llvm\\bin" --no-dll "combase.dll;rmclient.dll" -w {dest_dir} {wheel} &&
pipx run abi3audit --strict --report {wheel}
- name: Upload wheels
uses: actions/upload-artifact@v7
with:
name: wheel-pecos-rslib-${{ matrix.os }}-${{ matrix.architecture }}
path: ./wheelhouse/*.whl
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-latest
os: windows-2022
architecture: x86_64
- runner: macos-15-intel
os: macos-15-intel
architecture: x86_64
- runner: macos-15
os: macos-15
architecture: aarch64
steps:
- uses: actions/checkout@v6
with:
ref: ${{ inputs.sha || github.sha }}
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Download abi3 wheel
uses: actions/download-artifact@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"
}
build_sdist_quantum_pecos:
needs: build_wheelspecos_rslib
if: |
always() &&
needs.build_wheelspecos_rslib.result == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: ${{ inputs.sha || github.sha }}
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.10'
- name: Download pecos-rslib wheel
uses: actions/download-artifact@v7
with:
name: wheel-pecos-rslib-ubuntu-latest-x86_64
path: ./pecos-rslib-wheel
- name: Install pecos-rslib
run: pip install ./pecos-rslib-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@v7
with:
name: sdist-quantum-pecos
path: python/quantum-pecos/dist/*.tar.gz
build_wheels_quantum_pecos:
needs: build_wheelspecos_rslib
if: |
always() &&
needs.build_wheelspecos_rslib.result == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: ${{ inputs.sha || github.sha }}
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.10'
- name: Download pecos-rslib wheel
uses: actions/download-artifact@v7
with:
name: wheel-pecos-rslib-ubuntu-latest-x86_64
path: ./pecos-rslib-wheel
- name: Install pecos-rslib
run: pip install ./pecos-rslib-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@v7
with:
name: wheel-quantum-pecos
path: python/quantum-pecos/dist/*.whl
collect_artifacts:
needs: [build_wheelspecos_rslib, build_sdist_quantum_pecos, build_wheels_quantum_pecos, test_abi3_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'
runs-on: ubuntu-latest
steps:
- name: Create distribution directories
run: |
mkdir -p dist/pecos-rslib
mkdir -p dist/quantum-pecos
# Download artifacts into temp directory first
- name: Download all artifacts
uses: actions/download-artifact@v7
with:
path: temp-artifacts/
- name: Organize distribution files
run: |
# Debug: Show what we downloaded
echo "=== Downloaded artifacts structure ==="
ls -la temp-artifacts/
# Move pecos-rslib wheels to distribution directory
for artifact in temp-artifacts/wheel-pecos-rslib-*/; do
if [ -d "$artifact" ]; then
echo "Processing $artifact"
mv "$artifact"*.whl dist/pecos-rslib/ 2>/dev/null || true
fi
done
# Move quantum-pecos files to distribution directory
for artifact in temp-artifacts/*-quantum-pecos*/; do
if [ -d "$artifact" ]; then
echo "Processing $artifact"
mv "$artifact"*.whl dist/quantum-pecos/ 2>/dev/null || true
mv "$artifact"*.tar.gz dist/quantum-pecos/ 2>/dev/null || true
fi
done
# Clean up
rm -rf temp-artifacts
- name: List all collected artifacts
run: |
echo "=== pecos-rslib artifacts ==="
ls -la dist/pecos-rslib/
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 "quantum-pecos distributions: $(ls -1 dist/quantum-pecos/* 2>/dev/null | wc -l)"
- name: Upload distribution bundle
uses: actions/upload-artifact@v7
with:
name: pecos-distribution
path: dist/