Merge pull request #26 from shaia/fix/cuda-manylinux-wheel #168
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build and Test Wheels | |
| on: | |
| push: | |
| branches: [main, master] | |
| pull_request: | |
| workflow_dispatch: | |
| inputs: | |
| ref: | |
| description: 'Git ref to build from (tag like v0.1.6)' | |
| required: false | |
| type: string | |
| workflow_call: | |
| inputs: | |
| ref: | |
| description: 'Git ref to build from' | |
| required: false | |
| type: string | |
| env: | |
| # CFD C library version to build against | |
| # v0.1.6 introduces modular backend libraries | |
| CFD_VERSION: "v0.1.6" | |
| jobs: | |
| build_wheel: | |
| name: Build ${{ matrix.variant }} wheel on ${{ matrix.os }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| variant: [cpu, cuda] | |
| exclude: | |
| # macOS doesn't support CUDA | |
| - os: macos-latest | |
| variant: cuda | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ inputs.ref || github.ref }} | |
| - name: Checkout CFD C library | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: ${{ github.repository_owner }}/cfd | |
| ref: ${{ env.CFD_VERSION }} | |
| path: cfd | |
| fetch-depth: 0 | |
| - name: Set up Python 3.9 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.9" | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v4 | |
| with: | |
| enable-cache: true | |
| cache-dependency-glob: "pyproject.toml" | |
| - name: Install build dependencies | |
| run: uv pip install --system build scikit-build-core setuptools-scm | |
| # ============ CPU-only builds ============ | |
| # Note: Linux CPU build is done inside manylinux container (see Build wheel step) | |
| - name: Build CFD library (Linux - CPU only) | |
| if: runner.os == 'Linux' && matrix.variant == 'cpu' | |
| run: | | |
| echo "Linux CPU build will be done inside manylinux container" | |
| echo "Skipping host build to ensure glibc compatibility" | |
| - name: Build CFD library (macOS - CPU only) | |
| if: runner.os == 'macOS' | |
| run: | | |
| cmake -S cfd -B cfd/build \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DBUILD_SHARED_LIBS=OFF \ | |
| -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ | |
| -DCFD_ENABLE_CUDA=OFF | |
| cmake --build cfd/build --config Release | |
| echo "=== CFD library built (CPU-only) ===" | |
| ls -la cfd/build/lib/ | |
| - name: Build CFD library (Windows - CPU only) | |
| if: runner.os == 'Windows' && matrix.variant == 'cpu' | |
| run: | | |
| # Build CPU-only for maximum compatibility | |
| cmake -S cfd -B cfd/build ` | |
| -DCMAKE_BUILD_TYPE=Release ` | |
| -DBUILD_SHARED_LIBS=OFF ` | |
| -DCMAKE_POSITION_INDEPENDENT_CODE=ON ` | |
| -DCFD_ENABLE_CUDA=OFF | |
| cmake --build cfd/build --config Release | |
| echo "=== CFD library built (CPU-only) ===" | |
| dir cfd\build\lib\Release | |
| # ============ CUDA builds ============ | |
| # Note: Linux CUDA build is done inside NVIDIA manylinux container (see Build wheel step) | |
| - name: Build CFD library (Linux with CUDA) | |
| if: runner.os == 'Linux' && matrix.variant == 'cuda' | |
| run: | | |
| echo "Linux CUDA build will be done inside NVIDIA manylinux container" | |
| echo "Skipping host build to ensure manylinux compatibility" | |
| - name: Install CUDA Toolkit (Windows) | |
| if: runner.os == 'Windows' && matrix.variant == 'cuda' | |
| uses: Jimver/cuda-toolkit@v0.2.18 | |
| with: | |
| cuda: '12.4.0' | |
| method: 'network' | |
| # visual_studio_integration required for CMake to find CUDA toolset | |
| sub-packages: '["nvcc", "cudart", "nvrtc_dev", "cublas_dev", "cusparse_dev", "visual_studio_integration"]' | |
| - name: Build CFD library (Windows with CUDA) | |
| if: runner.os == 'Windows' && matrix.variant == 'cuda' | |
| run: | | |
| # Build with CUDA for Turing+ architectures | |
| cmake -S cfd -B cfd/build ` | |
| -DCMAKE_BUILD_TYPE=Release ` | |
| -DBUILD_SHARED_LIBS=OFF ` | |
| -DCMAKE_POSITION_INDEPENDENT_CODE=ON ` | |
| -DCFD_ENABLE_CUDA=ON ` | |
| -DCFD_CUDA_ARCHITECTURES="75;80;86;89;90" | |
| cmake --build cfd/build --config Release | |
| echo "=== CFD library built with CUDA ===" | |
| dir cfd\build\lib\Release | |
| # ============ Build wheels ============ | |
| # Linux wheels must be built inside manylinux container for glibc compatibility | |
| - name: Build wheel (Linux - CPU) | |
| if: runner.os == 'Linux' && matrix.variant == 'cpu' | |
| run: | | |
| docker run --rm \ | |
| -v "${{ github.workspace }}:/workspace" \ | |
| -w /workspace \ | |
| -e CFD_STATIC_LINK=ON \ | |
| -e CFD_USE_STABLE_ABI=ON \ | |
| quay.io/pypa/manylinux_2_28_x86_64 \ | |
| bash -c " | |
| set -e | |
| # Build CFD C library inside container | |
| cmake -S cfd -B cfd/build \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DBUILD_SHARED_LIBS=OFF \ | |
| -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ | |
| -DCFD_ENABLE_CUDA=OFF | |
| cmake --build cfd/build --config Release | |
| # Build Python wheel | |
| export CFD_ROOT=/workspace/cfd | |
| /opt/python/cp39-cp39/bin/pip install scikit-build-core setuptools-scm | |
| /opt/python/cp39-cp39/bin/pip wheel . --no-deps --wheel-dir dist_raw/ | |
| # Repair wheel for manylinux compatibility | |
| /opt/python/cp39-cp39/bin/pip install auditwheel | |
| auditwheel repair dist_raw/*.whl --plat manylinux_2_28_x86_64 -w dist/ | |
| " | |
| echo "=== Wheel built (manylinux) ===" | |
| ls -la dist/ | |
| - name: Build wheel (Linux - CUDA) | |
| if: runner.os == 'Linux' && matrix.variant == 'cuda' | |
| run: | | |
| # Build inside NVIDIA's manylinux-compatible CUDA container | |
| # Rocky Linux 8 is manylinux_2_28 compatible | |
| # Use --user to match host UID/GID for proper file ownership | |
| docker run --rm \ | |
| -v "${{ github.workspace }}:/workspace" \ | |
| -w /workspace \ | |
| -e CFD_STATIC_LINK=ON \ | |
| -e CFD_USE_STABLE_ABI=ON \ | |
| -e HOME=/tmp \ | |
| nvidia/cuda:12.4.0-devel-rockylinux8 \ | |
| bash -c " | |
| set -e | |
| # Install build tools (git for CMake FetchContent) | |
| dnf install -y cmake git python3.11 python3.11-pip python3.11-devel | |
| # Install patchelf via pip (EPEL version is too old for auditwheel) | |
| python3.11 -m pip install patchelf | |
| # Clean any previous build artifacts | |
| rm -rf cfd/build build dist dist_raw | |
| # Build CFD C library with CUDA | |
| # 75=Turing, 80=Ampere, 86=Ampere, 89=Ada, 90=Hopper | |
| cmake -S cfd -B cfd/build \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DBUILD_SHARED_LIBS=OFF \ | |
| -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ | |
| -DCFD_ENABLE_CUDA=ON \ | |
| -DCFD_CUDA_ARCHITECTURES='75;80;86;89;90' | |
| cmake --build cfd/build --config Release | |
| # Build Python wheel | |
| export CFD_ROOT=/workspace/cfd | |
| python3.11 -m pip install scikit-build-core setuptools-scm | |
| python3.11 -m pip wheel . --no-deps --wheel-dir dist_raw/ | |
| # Repair wheel for manylinux compatibility | |
| python3.11 -m pip install auditwheel | |
| auditwheel repair dist_raw/*.whl --plat manylinux_2_28_x86_64 -w dist/ | |
| # Fix permissions for host user | |
| chmod -R 777 dist/ | |
| " | |
| echo "=== Wheel built (CUDA manylinux) ===" | |
| ls -la dist/ | |
| - name: Build wheel (macOS) | |
| if: runner.os == 'macOS' | |
| env: | |
| CFD_ROOT: ${{ github.workspace }}/cfd | |
| CFD_STATIC_LINK: "ON" | |
| CFD_USE_STABLE_ABI: "ON" | |
| run: | | |
| pip wheel . --no-deps --wheel-dir dist/ | |
| echo "=== Wheel built ===" | |
| ls -la dist/ | |
| - name: Build wheel (Windows) | |
| if: runner.os == 'Windows' | |
| env: | |
| CFD_ROOT: ${{ github.workspace }}/cfd | |
| CFD_STATIC_LINK: "ON" | |
| CFD_USE_STABLE_ABI: "ON" | |
| run: | | |
| pip wheel . --no-deps --wheel-dir dist/ | |
| echo "=== Wheel built ===" | |
| dir dist | |
| - name: Inspect wheel contents | |
| run: | | |
| python -c " | |
| import glob, zipfile | |
| for wheel in glob.glob('dist/*.whl'): | |
| print(f'=== Contents of {wheel} ===') | |
| with zipfile.ZipFile(wheel) as zf: | |
| for name in zf.namelist(): | |
| print(name) | |
| " | |
| # Upload wheels with variant in artifact name | |
| # Note: Wheel filenames are standard (no variant suffix) to comply with PEP 427 | |
| # The variant (cpu/cuda) is encoded in the artifact name for differentiation | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: wheel-${{ matrix.os }}-${{ matrix.variant }} | |
| path: dist/*.whl | |
| test_wheel: | |
| name: Test ${{ matrix.variant }} wheel on ${{ matrix.os }} with Python ${{ matrix.python }} | |
| needs: [build_wheel] | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| python: ["3.9", "3.13"] | |
| variant: [cpu, cuda] | |
| exclude: | |
| # macOS doesn't have CUDA wheels | |
| - os: macos-latest | |
| variant: cuda | |
| steps: | |
| - name: Set up Python ${{ matrix.python }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "${{ matrix.python }}" | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v4 | |
| with: | |
| enable-cache: false | |
| # Install CUDA runtime for CUDA wheel tests (must match build version) | |
| - name: Install CUDA Toolkit (Linux - for testing) | |
| if: runner.os == 'Linux' && matrix.variant == 'cuda' | |
| run: | | |
| # Install CUDA runtime libraries via apt (more reliable than runfile installer) | |
| wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb | |
| sudo dpkg -i cuda-keyring_1.1-1_all.deb | |
| sudo apt-get update | |
| sudo apt-get install -y cuda-cudart-12-4 | |
| echo "LD_LIBRARY_PATH=/usr/local/cuda-12.4/lib64:$LD_LIBRARY_PATH" >> $GITHUB_ENV | |
| - name: Install CUDA Toolkit (Windows - for testing) | |
| if: runner.os == 'Windows' && matrix.variant == 'cuda' | |
| uses: Jimver/cuda-toolkit@v0.2.18 | |
| with: | |
| cuda: '12.4.0' | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: wheel-${{ matrix.os }}-${{ matrix.variant }} | |
| path: dist | |
| - name: Install wheel (Unix) | |
| if: runner.os != 'Windows' | |
| run: | | |
| python -m pip install dist/*.whl | |
| python -m pip install pytest numpy | |
| - name: Install wheel (Windows) | |
| if: runner.os == 'Windows' | |
| run: | | |
| python -m pip install (Get-ChildItem dist/*.whl).FullName | |
| python -m pip install pytest numpy | |
| - name: Test import (Unix) | |
| if: runner.os != 'Windows' | |
| run: | | |
| cd /tmp | |
| python -c " | |
| import cfd_python | |
| print('Package loaded:', cfd_python.__file__) | |
| print('Version:', cfd_python.__version__) | |
| print('Has list_solvers:', hasattr(cfd_python, 'list_solvers')) | |
| if hasattr(cfd_python, 'list_solvers'): | |
| print('Solvers:', cfd_python.list_solvers()) | |
| " | |
| - name: Test import (Windows) | |
| if: runner.os == 'Windows' | |
| run: | | |
| cd $env:TEMP | |
| python -c "import cfd_python; print('Package loaded:', cfd_python.__file__); print('Version:', cfd_python.__version__); print('Has list_solvers:', hasattr(cfd_python, 'list_solvers')); print('Solvers:', cfd_python.list_solvers()) if hasattr(cfd_python, 'list_solvers') else None" | |
| - uses: actions/checkout@v4 | |
| with: | |
| sparse-checkout: tests | |
| sparse-checkout-cone-mode: false | |
| - name: Run tests (Unix) | |
| if: runner.os != 'Windows' | |
| run: | | |
| cd /tmp | |
| pytest $GITHUB_WORKSPACE/tests/ -v | |
| - name: Run tests (Windows) | |
| if: runner.os == 'Windows' | |
| run: | | |
| cd $env:TEMP | |
| pytest "$env:GITHUB_WORKSPACE\tests" -v |