Adopt dual licensing #15
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-maxwell | |
| on: | |
| push: | |
| branches: | |
| - master | |
| - "codex/**" | |
| pull_request: | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| jobs: | |
| validate_release: | |
| if: github.event_name != 'release' || (startsWith(github.event.release.tag_name, 'witwin-maxwell-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 maxwell/pyproject.toml ]; then | |
| echo "path=maxwell" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "Unable to find pyproject.toml for the witwin-maxwell 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-maxwell-v(?P<version>[0-9A-Za-z.+_-]+)", tag) | |
| if match is None: | |
| print(f"Release tag '{tag}' must match 'witwin-maxwell-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-maxwell": | |
| print(f"Expected package name 'witwin-maxwell', 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 stable CUDA wheel / ${{ matrix.os }} | |
| needs: validate_release | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-22.04, windows-2022] | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.10" | |
| - 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.35 | |
| with: | |
| cuda: "12.8.1" | |
| method: local | |
| log-file-suffix: "${{ runner.os }}-stable-abi" | |
| - 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.10.0 --index-url https://download.pytorch.org/whl/cu128 | |
| python -m pip install build | |
| - name: Build packaged stable FDTD CUDA extension | |
| shell: bash | |
| env: | |
| MAX_JOBS: "2" | |
| WITWIN_CUDA_GENCODE_ARCHES: "7.0;7.5;8.0;8.6;8.9;9.0;10.0;10.1;12.0+PTX" | |
| PACKAGE_DIR: ${{ needs.validate_release.outputs.package_dir }} | |
| WITWIN_MAXWELL_FDTD_CUDA_BUILD_DIR: ${{ runner.temp }}/witwin_maxwell_fdtd_cuda/stable_abi_v1 | |
| run: | | |
| python "$PACKAGE_DIR"/scripts/build_fdtd_cuda_prebuilt.py --verbose | |
| - name: Verify compiled CUDA architectures | |
| shell: bash | |
| env: | |
| PACKAGE_DIR: ${{ needs.validate_release.outputs.package_dir }} | |
| run: | | |
| python "$PACKAGE_DIR"/scripts/verify_cuda_binary_arches.py \ | |
| --stem witwin_maxwell_fdtd_cuda \ | |
| "$PACKAGE_DIR"/witwin/maxwell/fdtd/cuda/prebuilt | |
| - name: Build platform wheel | |
| shell: bash | |
| env: | |
| PACKAGE_DIR: ${{ needs.validate_release.outputs.package_dir }} | |
| run: | | |
| python -m build --wheel "$PACKAGE_DIR" | |
| - name: Repair Linux wheel platform tag | |
| if: runner.os == 'Linux' | |
| shell: bash | |
| env: | |
| PACKAGE_DIR: ${{ needs.validate_release.outputs.package_dir }} | |
| run: | | |
| python -m pip install auditwheel patchelf | |
| mkdir -p "$PACKAGE_DIR"/wheelhouse | |
| for whl in "$PACKAGE_DIR"/dist/*-linux_x86_64.whl; do | |
| python -m auditwheel repair "$whl" \ | |
| --plat manylinux_2_35_x86_64 \ | |
| --wheel-dir "$PACKAGE_DIR"/wheelhouse \ | |
| --exclude libc10.so --exclude libc10_cuda.so \ | |
| --exclude libtorch.so --exclude libtorch_cpu.so \ | |
| --exclude libtorch_cuda.so --exclude libtorch_python.so \ | |
| --exclude libcudart.so.12 --exclude libcuda.so.1 | |
| done | |
| rm -f "$PACKAGE_DIR"/dist/*-linux_x86_64.whl | |
| mv "$PACKAGE_DIR"/wheelhouse/*.whl "$PACKAGE_DIR"/dist/ | |
| ls -l "$PACKAGE_DIR"/dist | |
| - name: Smoke install prebuilt wheel | |
| shell: bash | |
| env: | |
| PACKAGE_DIR: ${{ needs.validate_release.outputs.package_dir }} | |
| WITWIN_MAXWELL_FDTD_CUDA_PREBUILT: "1" | |
| WITWIN_MAXWELL_FDTD_CUDA_BUILD_DIR: ${{ runner.temp }}/missing_prebuilt_build_dir | |
| run: | | |
| python -m pip uninstall -y witwin-maxwell || true | |
| python -m pip install --no-deps "$PACKAGE_DIR"/dist/*.whl | |
| python - <<'PY' | |
| import importlib.util | |
| import os | |
| import site | |
| import zipfile | |
| from pathlib import Path | |
| wheel = next((Path(os.environ["PACKAGE_DIR"]) / "dist").glob("*.whl")) | |
| names = zipfile.ZipFile(wheel).namelist() | |
| native = [name for name in names if "witwin_maxwell_fdtd_cuda" in name and name.endswith((".so", ".pyd"))] | |
| assert len(native) == 1, native | |
| assert "/prebuilt/" in f"/{native[0]}" | |
| assert "torch_" not in native[0] | |
| candidates = [] | |
| for root in site.getsitepackages() + [site.getusersitepackages()]: | |
| candidates.append(Path(root) / "witwin" / "maxwell" / "fdtd" / "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_maxwell_fdtd_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}" | |
| assert path.parent.name == "prebuilt" | |
| extension = module.build_extension() | |
| print(f"Loaded packaged FDTD CUDA extension from {extension.__file__}") | |
| PY | |
| - name: Upload wheel artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wheel-${{ matrix.os }} | |
| path: ${{ needs.validate_release.outputs.package_dir }}/dist/*.whl | |
| if-no-files-found: error | |
| test_torch_compatibility: | |
| name: Stable ABI / ${{ matrix.os }} / py${{ matrix.compatibility.python_version }} / torch${{ matrix.compatibility.torch_version }} | |
| needs: | |
| - validate_release | |
| - build_cuda_wheels | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-22.04, windows-2022] | |
| compatibility: | |
| - {python_version: "3.10", torch_version: "2.10.0", cuda_index: "cu128"} | |
| - {python_version: "3.11", torch_version: "2.10.0", cuda_index: "cu128"} | |
| - {python_version: "3.12", torch_version: "2.10.0", cuda_index: "cu128"} | |
| - {python_version: "3.13", torch_version: "2.10.0", cuda_index: "cu128"} | |
| - {python_version: "3.14", torch_version: "2.10.0", cuda_index: "cu128"} | |
| - {python_version: "3.14", torch_version: "2.11.0", cuda_index: "cu128"} | |
| - {python_version: "3.14", torch_version: "2.12.0", cuda_index: "cu126"} | |
| steps: | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.compatibility.python_version }} | |
| - name: Download stable wheel | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: wheel-${{ matrix.os }} | |
| path: dist | |
| - name: Install compatibility environment | |
| shell: bash | |
| run: | | |
| python -m pip install --upgrade pip | |
| python -m pip install --no-cache-dir "torch==${{ matrix.compatibility.torch_version }}" \ | |
| --index-url "https://download.pytorch.org/whl/${{ matrix.compatibility.cuda_index }}" | |
| python -m pip install --no-deps dist/*.whl | |
| - name: Load the same stable binary | |
| shell: bash | |
| env: | |
| EXPECTED_TORCH: ${{ matrix.compatibility.torch_version }} | |
| WITWIN_MAXWELL_FDTD_CUDA_PREBUILT: "1" | |
| WITWIN_MAXWELL_FDTD_CUDA_BUILD_DIR: ${{ runner.temp }}/must-not-jit-build | |
| run: | | |
| python - <<'PY' | |
| import importlib.util | |
| import os | |
| import site | |
| from pathlib import Path | |
| import torch | |
| assert torch.__version__.split("+", 1)[0] == os.environ["EXPECTED_TORCH"] | |
| candidates = [ | |
| Path(root) / "witwin" / "maxwell" / "fdtd" / "cuda" / "build.py" | |
| for root in site.getsitepackages() + [site.getusersitepackages()] | |
| ] | |
| build_path = next((path for path in candidates if path.exists()), None) | |
| assert build_path is not None, candidates | |
| spec = importlib.util.spec_from_file_location("witwin_maxwell_fdtd_cuda_build", build_path) | |
| module = importlib.util.module_from_spec(spec) | |
| spec.loader.exec_module(module) | |
| native_path = module.prebuilt_extension_path() | |
| assert native_path.exists(), native_path | |
| extension = module.build_extension() | |
| assert Path(extension.__file__).resolve() == native_path.resolve() | |
| assert extension.update_magnetic_hx_standard is not None | |
| print(f"Loaded {native_path.name} with torch {torch.__version__} on Python {os.sys.version.split()[0]}") | |
| PY | |
| 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 | |
| - test_torch_compatibility | |
| - build_sdist | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write | |
| environment: | |
| name: pypi | |
| url: https://pypi.org/project/witwin-maxwell/ | |
| 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 |