Native CUDA mesh SDF backend + prebuilt CUDA wheels (0.0.2) #3
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 publish witwin | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - "codex/**" | |
| pull_request: | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| jobs: | |
| validate_release: | |
| if: github.event_name != 'release' || (startsWith(github.event.release.tag_name, 'witwin-v') && !github.event.release.prerelease) | |
| name: Validate release | |
| runs-on: ubuntu-latest | |
| outputs: | |
| package_dir: ${{ steps.package_dir.outputs.path }} | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Resolve package directory | |
| id: package_dir | |
| shell: bash | |
| run: | | |
| if [ -f pyproject.toml ]; then | |
| echo "path=." >> "$GITHUB_OUTPUT" | |
| elif [ -f core/pyproject.toml ]; then | |
| echo "path=core" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "Unable to find pyproject.toml for the witwin package." >&2 | |
| exit 1 | |
| fi | |
| - name: Validate release tag and package version | |
| if: github.event_name == 'release' | |
| shell: bash | |
| env: | |
| PACKAGE_DIR: ${{ steps.package_dir.outputs.path }} | |
| RELEASE_TAG: ${{ github.event.release.tag_name }} | |
| run: | | |
| python - <<'PY' | |
| import os | |
| import re | |
| import sys | |
| import tomllib | |
| from pathlib import Path | |
| tag = os.environ["RELEASE_TAG"] | |
| match = re.fullmatch(r"witwin-v(?P<version>[0-9A-Za-z.+_-]+)", tag) | |
| if match is None: | |
| print(f"Release tag '{tag}' must match 'witwin-v<version>'.", file=sys.stderr) | |
| raise SystemExit(1) | |
| expected_version = match.group("version") | |
| pyproject_path = Path(os.environ["PACKAGE_DIR"]) / "pyproject.toml" | |
| data = tomllib.loads(pyproject_path.read_text(encoding="utf-8")) | |
| project = data["project"] | |
| package_name = project["name"] | |
| package_version = project["version"] | |
| if package_name != "witwin": | |
| print(f"Expected package name 'witwin', found '{package_name}'.", file=sys.stderr) | |
| raise SystemExit(1) | |
| if package_version != expected_version: | |
| print( | |
| f"Release tag version '{expected_version}' does not match pyproject version '{package_version}'.", | |
| file=sys.stderr, | |
| ) | |
| raise SystemExit(1) | |
| print(f"Validated {package_name} {package_version} from {pyproject_path}.") | |
| PY | |
| build_cuda_wheels: | |
| name: Build CUDA wheel / ${{ matrix.os }} / py${{ matrix.python_version }} | |
| needs: validate_release | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-22.04, windows-2022] | |
| python_version: ["3.10", "3.11", "3.12"] | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python_version }} | |
| - name: Free disk space (Linux) | |
| if: runner.os == 'Linux' | |
| shell: bash | |
| run: | | |
| echo "Disk before cleanup:"; df -h / | |
| sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android \ | |
| /usr/local/share/boost /opt/hostedtoolcache/CodeQL \ | |
| /usr/local/share/powershell /usr/share/swift || true | |
| sudo docker image prune --all --force || true | |
| echo "Disk after cleanup:"; df -h / | |
| - name: Install CUDA Toolkit | |
| uses: Jimver/cuda-toolkit@v0.2.29 | |
| with: | |
| cuda: "12.5.0" | |
| method: local | |
| log-file-suffix: "${{ runner.os }}-py${{ matrix.python_version }}" | |
| - name: Set up MSVC | |
| if: runner.os == 'Windows' | |
| uses: ilammy/msvc-dev-cmd@v1 | |
| with: | |
| arch: x64 | |
| - name: Export Windows CUDA_HOME | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| $cudaRoot = $env:CUDA_PATH | |
| if (-not $cudaRoot) { | |
| $nvcc = (Get-Command nvcc -ErrorAction Stop).Source | |
| $cudaRoot = Split-Path (Split-Path $nvcc -Parent) -Parent | |
| } | |
| "CUDA_HOME=$cudaRoot" >> $env:GITHUB_ENV | |
| "CUDA_PATH=$cudaRoot" >> $env:GITHUB_ENV | |
| "CUDA_ROOT=$cudaRoot" >> $env:GITHUB_ENV | |
| "CUDA_BIN_PATH=$cudaRoot" >> $env:GITHUB_ENV | |
| - name: Show toolchain | |
| shell: bash | |
| run: | | |
| python --version | |
| nvcc --version | |
| command -v cl || true | |
| - name: Install CUDA extension build dependencies | |
| run: | | |
| python -m pip install --upgrade pip setuptools wheel ninja numpy | |
| python -m pip install torch==2.6.0 --index-url https://download.pytorch.org/whl/cu124 | |
| python -m pip install build | |
| - name: Build packaged mesh SDF CUDA extension | |
| shell: bash | |
| env: | |
| MAX_JOBS: "2" | |
| TORCH_CUDA_ARCH_LIST: "7.0;8.0;8.6;8.9;9.0+PTX" | |
| WITWIN_CORE_MESH_SDF_CUDA_BUILD_DIR: ${{ runner.temp }}/witwin_core_mesh_sdf_cuda | |
| PACKAGE_DIR: ${{ needs.validate_release.outputs.package_dir }} | |
| run: | | |
| python "$PACKAGE_DIR"/scripts/build_mesh_sdf_cuda_prebuilt.py --verbose | |
| - name: Build platform wheel | |
| shell: bash | |
| env: | |
| PACKAGE_DIR: ${{ needs.validate_release.outputs.package_dir }} | |
| run: | | |
| python -m build --wheel "$PACKAGE_DIR" | |
| - name: Normalize Linux wheel platform tag | |
| if: runner.os == 'Linux' | |
| shell: bash | |
| env: | |
| PACKAGE_DIR: ${{ needs.validate_release.outputs.package_dir }} | |
| run: | | |
| # PyPI rejects the raw linux_x86_64 tag; the extension is built on the | |
| # ubuntu-22.04 runner (glibc 2.35), so retag to the matching manylinux | |
| # tag. torch/CUDA runtime libraries are runtime dependencies and stay | |
| # unbundled, so no auditwheel repair is required. | |
| shopt -s nullglob | |
| for whl in "$PACKAGE_DIR"/dist/*-linux_x86_64.whl; do | |
| echo "Retagging $whl" | |
| python -m wheel tags --platform-tag manylinux_2_35_x86_64 --remove "$whl" | |
| done | |
| ls -l "$PACKAGE_DIR"/dist | |
| - name: Smoke install prebuilt wheel | |
| shell: bash | |
| env: | |
| PACKAGE_DIR: ${{ needs.validate_release.outputs.package_dir }} | |
| WITWIN_CORE_MESH_SDF_CUDA_PREBUILT: "1" | |
| WITWIN_CORE_MESH_SDF_CUDA_BUILD_DIR: ${{ runner.temp }}/missing_prebuilt_build_dir | |
| run: | | |
| python -m pip uninstall -y witwin || true | |
| python -m pip install --no-deps "$PACKAGE_DIR"/dist/*.whl | |
| python - <<'PY' | |
| import importlib.util | |
| import site | |
| from pathlib import Path | |
| candidates = [] | |
| for root in site.getsitepackages() + [site.getusersitepackages()]: | |
| candidates.append(Path(root) / "witwin" / "core" / "geometry" / "cuda" / "build.py") | |
| build_path = next((path for path in candidates if path.exists()), None) | |
| assert build_path is not None, f"installed build.py not found in {candidates}" | |
| spec = importlib.util.spec_from_file_location("witwin_core_mesh_sdf_cuda_build", build_path) | |
| module = importlib.util.module_from_spec(spec) | |
| spec.loader.exec_module(module) | |
| path = module.prebuilt_extension_path() | |
| assert path.exists(), f"missing packaged prebuilt extension: {path}" | |
| extension = module.build_extension() | |
| print(f"Loaded packaged mesh SDF CUDA extension from {extension.__file__}") | |
| PY | |
| - name: Upload wheel artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wheel-${{ matrix.os }}-py${{ matrix.python_version }} | |
| path: ${{ needs.validate_release.outputs.package_dir }}/dist/*.whl | |
| if-no-files-found: error | |
| build_sdist: | |
| name: Build source distribution | |
| needs: validate_release | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Build distributions | |
| shell: bash | |
| env: | |
| PACKAGE_DIR: ${{ needs.validate_release.outputs.package_dir }} | |
| run: | | |
| python -m pip install --upgrade pip build | |
| python -m build --sdist "$PACKAGE_DIR" | |
| - name: Upload source distribution artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sdist | |
| path: ${{ needs.validate_release.outputs.package_dir }}/dist/*.tar.gz | |
| if-no-files-found: error | |
| publish: | |
| name: Publish to PyPI | |
| if: github.event_name == 'release' | |
| needs: | |
| - build_cuda_wheels | |
| - build_sdist | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write | |
| environment: | |
| name: pypi | |
| url: https://pypi.org/project/witwin/ | |
| steps: | |
| - name: Download wheel artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: wheel-* | |
| path: dist | |
| merge-multiple: true | |
| - name: Download source distribution artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: sdist | |
| path: dist | |
| - name: Publish distributions to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| packages-dir: dist | |
| skip-existing: true |