Skip to content

Build Wheels

Build Wheels #20

Workflow file for this run

name: Build Wheels
on:
push:
tags: [ 'v*' ]
workflow_dispatch:
jobs:
build_wheels:
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-24.04
arch: x86_64
- os: windows-2022
arch: AMD64
- os: macos-13
arch: x86_64
- os: macos-14
arch: arm64
steps:
- name: Checkout PyHelios
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0 # Required for setuptools-scm
# Ensure all tags are fetched
fetch-tags: true
- name: Debug version detection
shell: bash
run: |
echo "=== Git tag information ==="
git tag --list | head -10
echo "Current HEAD: $(git rev-parse HEAD)"
echo "Describe: $(git describe --tags --always --dirty)"
echo "=== Setuptools-scm version detection ==="
python -m pip install setuptools-scm
python -c "from setuptools_scm import get_version; print(f'Detected version: {get_version()}')" || echo "Version detection failed"
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.8' # Use lowest supported version for compatibility
- name: Setup MSVC (Windows)
if: runner.os == 'Windows'
uses: ilammy/msvc-dev-cmd@v1
- name: Install Helios dependencies (Linux)
if: runner.os == 'Linux'
run: |
cd helios-core/utilities
sudo bash dependencies.sh ALL
- name: Install Helios dependencies (macOS)
if: runner.os == 'macOS'
run: |
cd helios-core/utilities
# Install base + visualization dependencies (no GPU/CUDA for macOS builds)
bash dependencies.sh BASE
bash dependencies.sh VIS
- name: Debug environment (macOS)
if: runner.os == 'macOS'
run: |
echo "=== Directory structure ==="
ls -la
echo "=== PyHelios build scripts ==="
ls -la build_scripts/
echo "=== Helios core ==="
ls -la helios-core/ || echo "helios-core not found"
echo "=== Python version and location ==="
python --version
which python
echo "=== Environment ==="
env | grep -E "(PYTHON|PATH)" | head -10
- name: Install CUDA Toolkit (Windows)
if: runner.os == 'Windows'
shell: powershell
run: |
# Download CUDA 12.6 installer with integrity verification
$installerUrl = "https://developer.download.nvidia.com/compute/cuda/12.6.2/network_installers/cuda_12.6.2_windows_network.exe"
$expectedMD5 = "d109e3e1720d33f9ea75379c619e22a6" # Official MD5 hash from NVIDIA
Write-Host "Downloading CUDA toolkit installer..."
Invoke-WebRequest -Uri $installerUrl -OutFile "cuda_installer.exe"
# Verify file integrity using official NVIDIA MD5 checksum
Write-Host "Verifying installer integrity..."
$actualMD5 = (Get-FileHash -Algorithm MD5 "cuda_installer.exe").Hash
if ($actualMD5 -ne $expectedMD5) {
Write-Error "CUDA installer MD5 verification failed!"
Write-Error "Expected: $expectedMD5"
Write-Error "Actual: $actualMD5"
Write-Error "This may indicate a corrupted download or security issue."
exit 1
}
Write-Host "✓ Installer integrity verified using official NVIDIA MD5 checksum"
# Install CUDA toolkit components needed for compilation
Write-Host "Installing CUDA toolkit components..."
Start-Process -FilePath ".\cuda_installer.exe" -ArgumentList "-s","nvcc_12.6","cudart_12.6","nvrtc_12.6","nvrtc_dev_12.6","visual_studio_integration_12.6" -Wait
# Add CUDA to PATH for subsequent steps
echo "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.6\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
Write-Host "✓ CUDA toolkit installation completed"
- name: Install cibuildwheel and repair tools
shell: bash
run: |
python -m pip install --upgrade pip
python -m pip install cibuildwheel
# Install platform-specific wheel repair tools
if [ "${{ runner.os }}" = "Linux" ]; then
python -m pip install auditwheel
elif [ "${{ runner.os }}" = "macOS" ]; then
python -m pip install delocate
fi
- name: Debug version detection
shell: bash
run: |
echo "=== Version Detection Debug ==="
echo "Git describe: $(git describe --tags --long --dirty)"
echo "Git tags:"
git tag -l --sort=-version:refname | head -10
echo "Git log (recent commits):"
git log --oneline --decorate -5
pip install setuptools-scm
echo "setuptools-scm version: $(python -m setuptools_scm)"
echo "================================"
- name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse
env:
# Build for Python 3.8+ on all platforms
CIBW_BUILD: cp38-* cp39-* cp310-* cp311-* cp312-*
# Skip 32-bit builds and PyPy
CIBW_SKIP: "*-win32 *-manylinux_i686 *-musllinux* pp*"
# Architecture configuration based on runner
CIBW_ARCHS: ${{ matrix.arch }}
# Platform-specific build commands with broad CUDA compatibility
CIBW_BEFORE_BUILD_MACOS: "python build_scripts/prepare_wheel.py --buildmode release --nogpu --verbose"
CIBW_ENVIRONMENT_MACOS: "MACOSX_DEPLOYMENT_TARGET=14.0"
CIBW_BEFORE_BUILD_WINDOWS: "set CMAKE_RC_COMPILER= && set PYHELIOS_CUDA_ARCHITECTURES=50;60;70;75;80;86;90 && python build_scripts/prepare_wheel.py --buildmode release --verbose"
CIBW_BEFORE_BUILD_LINUX: "export PYHELIOS_CUDA_ARCHITECTURES=50;60;70;75;80;86;90 && python build_scripts/prepare_wheel.py --buildmode release --verbose"
# Comprehensive wheel testing using pytest suite
CIBW_TEST_COMMAND: |
python -c "
import pyhelios
print(f'PyHelios {pyhelios.__version__} imported successfully')
# Quick asset validation
from pyhelios.assets import get_asset_manager
manager = get_asset_manager()
helios_build = manager._get_helios_build_path()
if helios_build:
print(f'HELIOS_BUILD assets found at: {helios_build}')
else:
raise RuntimeError('CRITICAL: HELIOS_BUILD assets not found')
# Plugin status check
from pyhelios.plugins import print_plugin_status
print_plugin_status()
print('Initial validation completed')
" &&
python -m pytest {project}/tests/test_cross_platform.py {project}/tests/test_datatypes.py --tb=short -v
CIBW_TEST_REQUIRES: "pytest pytest-forked"
# Skip problematic platforms for testing
CIBW_TEST_SKIP: "*-win32 *-manylinux_i686 *-musllinux*"
# Repair wheels to bundle dependencies
CIBW_REPAIR_WHEEL_COMMAND_MACOS: "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}"
CIBW_REPAIR_WHEEL_COMMAND_LINUX: "auditwheel repair -w {dest_dir} {wheel}"
- name: Debug build failure
if: failure()
shell: bash
run: |
echo "=== Build Failure Diagnostics ==="
echo "Build directory contents:"
find pyhelios_build -name "*.so" -o -name "*.dll" -o -name "*.dylib" 2>/dev/null || echo "No build directory found"
echo ""
echo "Plugin directory contents:"
ls -la pyhelios/plugins/ 2>/dev/null || echo "No plugins directory found"
echo ""
echo "Wheel directory contents:"
ls -la wheelhouse/ 2>/dev/null || echo "No wheelhouse directory found"
echo ""
echo "Python environment:"
python --version
pip list | grep -E "(cibuildwheel|auditwheel|delocate)" || echo "Wheel tools not found"
- name: Upload wheels as artifacts
uses: actions/upload-artifact@v4
with:
name: wheels-${{ matrix.os }}-${{ matrix.arch }}
path: wheelhouse/*.whl
retention-days: 7
test_wheels:
name: Test wheels on ${{ matrix.os }} Python ${{ matrix.python-version }}
runs-on: ${{ matrix.os }}
needs: build_wheels
strategy:
matrix:
include:
- os: ubuntu-24.04
python-version: '3.8'
- os: ubuntu-24.04
python-version: '3.11'
- os: windows-2022
python-version: '3.8'
- os: windows-2022
python-version: '3.11'
- os: macos-13
python-version: '3.8'
- os: macos-13
python-version: '3.11'
- os: macos-14
python-version: '3.8'
- os: macos-14
python-version: '3.11'
steps:
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Download wheels
uses: actions/download-artifact@v4
with:
pattern: wheels-*
merge-multiple: true
path: wheelhouse
- name: Install wheel
run: |
python -m pip install --upgrade pip
python -m pip install --find-links wheelhouse --no-index pyhelios
- name: Test basic functionality and asset validation
run: |
python -c "
import pyhelios
from pyhelios import Context, WeberPennTree, WPTType
from pyhelios.types import vec2, vec3, RGBcolor
print(f'PyHelios version: {pyhelios.__version__}')
# Test basic Context operations
context = Context()
center = vec3(1, 2, 3)
color = RGBcolor(0.5, 0.5, 0.5)
uuid = context.addPatch(center=center, size=vec2(1, 1), color=color)
print(f'Added patch with UUID: {uuid}')
# Test plugin availability reporting
from pyhelios.plugins import print_plugin_status
print_plugin_status()
# Comprehensive asset validation for pip-installed wheels
print('\\n=== Asset Validation ===')
from pyhelios.assets import get_asset_manager
from pathlib import Path
import os
manager = get_asset_manager()
# Test HELIOS_BUILD path resolution priority
helios_build = manager._get_helios_build_path()
if helios_build:
print(f'HELIOS_BUILD path: {helios_build}')
if 'pyhelios/assets/build' in str(helios_build):
print('✓ Using pip-installed assets (correct priority)')
else:
print('⚠ Using development assets (check if intended)')
else:
print('✗ HELIOS_BUILD path not found')
# Validate core assets exist
package_root = Path(pyhelios.__file__).parent
core_assets = package_root / 'assets' / 'build' / 'lib' / 'images'
if core_assets.exists():
asset_files = list(core_assets.glob('*'))
print(f'✓ Found {len(asset_files)} core asset files')
for asset in asset_files[:3]: # Show first 3
print(f' - {asset.name}')
else:
print('✗ Core assets not found at expected location')
# Test environment variable setting
original_helios_build = os.environ.get('HELIOS_BUILD')
manager.initialize()
new_helios_build = os.environ.get('HELIOS_BUILD')
if new_helios_build:
print(f'✓ HELIOS_BUILD environment variable set: {new_helios_build}')
else:
print('✗ HELIOS_BUILD environment variable not set')
# Test asset validation function
validation_results = manager.validate_assets()
total_assets = sum(result['files_found'] for result in validation_results.values())
print(f'✓ Asset validation completed: {total_assets} total assets found')
print('Asset validation passed')
"
# publish:
# name: Publish to PyPI
# runs-on: ubuntu-latest
# needs: [build_wheels, test_wheels]
# if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
# environment:
# name: pypi
# url: https://pypi.org/p/pyhelios
# permissions:
# id-token: write # Required for trusted publishing
#
# steps:
# - name: Download all wheels
# uses: actions/download-artifact@v4
# with:
# pattern: wheels-*
# merge-multiple: true
# path: wheelhouse
#
# - name: Publish to PyPI
# uses: pypa/gh-action-pypi-publish@release/v1
# with:
# packages-dir: wheelhouse/
# verify-metadata: false # Skip metadata verification due to dynamic versioning