Download Isaac-GR00T/scripts/deployment/dgpu/bootstrap_wheels.sh from Timsty/groot_deployment: direct link, hf CLI and curl.
- Browser
- Download file 8.12 kB
-
https://huggingface.co/Timsty/groot_deployment/resolve/main/Isaac-GR00T/scripts/deployment/dgpu/bootstrap_wheels.sh
- Command line
-
hf download hf://Timsty/groot_deployment/Isaac-GR00T/scripts/deployment/dgpu/bootstrap_wheels.sh
-
curl -L -o bootstrap_wheels.sh https://huggingface.co/Timsty/groot_deployment/resolve/main/Isaac-GR00T/scripts/deployment/dgpu/bootstrap_wheels.sh
8.12 kB
| # Build missing aarch64 dGPU wheels before the root pyproject's path sources are | |
| # resolved by uv sync. Dependency pins are the source of truth; the pyproject | |
| # path sources are updated to point at the matching generated wheel names. | |
| set -euo pipefail | |
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" | |
| PYPROJECT="$REPO_ROOT/pyproject.toml" | |
| WHEEL_DIR="$SCRIPT_DIR/wheels" | |
| mkdir -p "$WHEEL_DIR" | |
| # Target CPython derived from the project's requires-python, so the build venv | |
| # and the wheel's cpXY tag track the pinned interpreter. A literal cp310/python3.10 | |
| # silently builds an unusable wheel after a runtime bump (e.g. the py3.12 migration). | |
| resolve_python_version() { | |
| python3 - "$PYPROJECT" <<'PY' | |
| import pathlib | |
| import re | |
| import sys | |
| text = pathlib.Path(sys.argv[1]).read_text() | |
| # Accept both TOML quote styles (" and ', written as hex \x22 / \x27). | |
| m = re.search(r"requires-python\s*=\s*[\x22\x27]([^\x22\x27]+)[\x22\x27]", text) | |
| if m is None: | |
| raise SystemExit("ERROR: requires-python not found in pyproject.toml") | |
| v = re.search(r"(\d+)\.(\d+)", m.group(1)) | |
| if v is None: | |
| raise SystemExit(f"ERROR: could not parse a CPython version from {m.group(1)!r}") | |
| print(f"{v.group(1)}.{v.group(2)}") | |
| PY | |
| } | |
| PY_VERSION="$(resolve_python_version)" | |
| PYTHON_BIN="python${PY_VERSION}" | |
| CP_TAG="cp${PY_VERSION/./}" | |
| resolve_dependency_version() { | |
| python3 - "$PYPROJECT" "$1" <<'PY' | |
| import pathlib | |
| import re | |
| import sys | |
| pyproject_path = pathlib.Path(sys.argv[1]) | |
| package = sys.argv[2] | |
| text = pyproject_path.read_text() | |
| dep_line = None | |
| for line in text.splitlines(): | |
| if f'"{package}==' in line and "platform_machine" in line and "aarch64" in line: | |
| dep_line = line | |
| break | |
| if dep_line is None: | |
| for line in text.splitlines(): | |
| if f'"{package}==' in line: | |
| dep_line = line | |
| break | |
| if dep_line is None: | |
| raise SystemExit(f"ERROR: pinned project dependency not found for {package}") | |
| dep_match = re.search(rf'"{re.escape(package)}==([^";]+)', dep_line) | |
| if dep_match is None: | |
| raise SystemExit(f"ERROR: could not parse pinned dependency version for {package}") | |
| print(dep_match.group(1)) | |
| PY | |
| } | |
| update_pyproject_path_source() { | |
| python3 - "$PYPROJECT" "$1" "$2" <<'PY' | |
| import pathlib | |
| import re | |
| import sys | |
| pyproject_path = pathlib.Path(sys.argv[1]) | |
| package = sys.argv[2] | |
| expected_path = sys.argv[3] | |
| text = pyproject_path.read_text() | |
| source_block_match = re.search( | |
| rf"^{re.escape(package)}\s*=\s*\[(.*?)^\]", | |
| text, | |
| flags=re.MULTILINE | re.DOTALL, | |
| ) | |
| if source_block_match is None: | |
| raise SystemExit(f"ERROR: [tool.uv.sources] entry not found for {package}") | |
| block_start, block_end = source_block_match.span(1) | |
| block = source_block_match.group(1) | |
| entry_pattern = re.compile(r"\{[^\n]*path\s*=\s*\"([^\"]+)\"[^\n]*\}") | |
| matches = list(entry_pattern.finditer(block)) | |
| aarch64_matches = [ | |
| match | |
| for match in matches | |
| if "platform_machine == 'aarch64'" in match.group(0) | |
| and "scripts/deployment/dgpu/wheels/" in match.group(1) | |
| ] | |
| if len(aarch64_matches) != 1: | |
| raise SystemExit( | |
| f"ERROR: expected exactly one dGPU aarch64 path source for {package}, " | |
| f"found {len(aarch64_matches)}" | |
| ) | |
| match = aarch64_matches[0] | |
| current_path = match.group(1) | |
| if current_path == expected_path: | |
| print(f"{package} path source already points to {expected_path}") | |
| raise SystemExit(0) | |
| entry = match.group(0) | |
| updated_entry = entry.replace(f'path = "{current_path}"', f'path = "{expected_path}"') | |
| updated_block = block[: match.start()] + updated_entry + block[match.end() :] | |
| updated_text = text[:block_start] + updated_block + text[block_end:] | |
| pyproject_path.write_text(updated_text) | |
| print(f"Updated {package} path source: {current_path} -> {expected_path}") | |
| PY | |
| } | |
| wheel_path_for() { | |
| package_prefix="$1" | |
| version="$2" | |
| printf "%s/%s-%s-${CP_TAG}-${CP_TAG}-linux_aarch64.whl" "$WHEEL_DIR" "$package_prefix" "$version" | |
| } | |
| # flash-attn now ships as official cu12torch2.9 cp312 release wheels for both | |
| # x86_64 and aarch64 (see [tool.uv.sources] in pyproject.toml), so the dGPU | |
| # bootstrap only owns the aarch64 torchcodec wheel (no PyPI aarch64 wheel). | |
| TORCHCODEC_VERSION="$(resolve_dependency_version "torchcodec")" | |
| TORCHCODEC_SOURCE_VERSION="$(printf "%s" "$TORCHCODEC_VERSION" | sed -E 's/a[0-9]+$//')" | |
| TORCHCODEC_WHEEL="$(wheel_path_for "torchcodec" "$TORCHCODEC_VERSION")" | |
| TORCHCODEC_PATH="scripts/deployment/dgpu/wheels/$(basename "$TORCHCODEC_WHEEL")" | |
| echo "Expected dGPU aarch64 wheels from dependency pins:" | |
| echo " torchcodec==$TORCHCODEC_VERSION -> $TORCHCODEC_PATH" | |
| update_pyproject_path_source "torchcodec" "$TORCHCODEC_PATH" | |
| if [ "${DGPU_WHEEL_BOOTSTRAP_VALIDATE_ONLY:-0}" = "1" ]; then | |
| exit 0 | |
| fi | |
| if [ "$(uname -m)" != "aarch64" ]; then | |
| echo "dGPU wheel bootstrap is only needed on aarch64; skipping." | |
| exit 0 | |
| fi | |
| if [ -f "$TORCHCODEC_WHEEL" ]; then | |
| echo "Matching dGPU aarch64 torchcodec wheel already exists; skipping source build." | |
| exit 0 | |
| fi | |
| if ! command -v uv &> /dev/null; then | |
| echo "ERROR: uv is required to bootstrap dGPU wheels." >&2 | |
| exit 1 | |
| fi | |
| BUILD_VENV="${DGPU_WHEEL_BUILD_VENV:-/tmp/gr00t-dgpu-wheel-build-venv}" | |
| BUILD_PYTHON="$BUILD_VENV/bin/python" | |
| TMP_BUILD_DIRS=() | |
| trap 'rm -rf "$BUILD_VENV"; for _d in "${TMP_BUILD_DIRS[@]:-}"; do rm -rf "$_d"; done' EXIT | |
| rm -rf "$BUILD_VENV" | |
| "$PYTHON_BIN" -m venv "$BUILD_VENV" | |
| uv_pip_install_retry() { | |
| for attempt in 1 2 3 4 5; do | |
| if uv pip install "$@"; then | |
| return 0 | |
| fi | |
| sleep $((attempt * 10)) | |
| done | |
| return 1 | |
| } | |
| # Build against the project's pinned torch / triton / numpy (derived from | |
| # pyproject.toml) so the generated wheel's ABI matches the runtime stack. | |
| # Hardcoded versions silently produce a wheel for the wrong torch after a bump. | |
| TORCH_VERSION="$(resolve_dependency_version "torch")" | |
| TRITON_VERSION="$(resolve_dependency_version "triton")" | |
| NUMPY_VERSION="$(resolve_dependency_version "numpy")" | |
| uv_pip_install_retry --python "$BUILD_PYTHON" \ | |
| --index-url https://download.pytorch.org/whl/cu128 \ | |
| --extra-index-url https://pypi.org/simple \ | |
| "torch==${TORCH_VERSION}" "triton==${TRITON_VERSION}" "numpy==${NUMPY_VERSION}" | |
| uv_pip_install_retry --python "$BUILD_PYTHON" pip setuptools wheel packaging ninja | |
| SITE_PKGS=$("$BUILD_PYTHON" - <<'PY' | |
| import site | |
| print(site.getsitepackages()[0]) | |
| PY | |
| ) | |
| NVIDIA_LIB_DIRS="$(find "${SITE_PKGS}/nvidia" -name "lib" -type d 2>/dev/null | tr '\n' ':')" | |
| export LD_LIBRARY_PATH="/usr/local/cuda/lib64:${SITE_PKGS}/torch/lib:${NVIDIA_LIB_DIRS}${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" | |
| export CUDA_HOME=/usr/local/cuda | |
| export CUDA_PATH=/usr/local/cuda | |
| export CPATH="${CUDA_HOME}/include:${CPATH:-}" | |
| export C_INCLUDE_PATH="${CUDA_HOME}/include:${C_INCLUDE_PATH:-}" | |
| export CPLUS_INCLUDE_PATH="${CUDA_HOME}/include:${CPLUS_INCLUDE_PATH:-}" | |
| export MAX_JOBS="${MAX_JOBS:-$(nproc)}" | |
| export NVCC_THREADS="${NVCC_THREADS:-1}" | |
| export CMAKE_BUILD_PARALLEL_LEVEL="${CMAKE_BUILD_PARALLEL_LEVEL:-$(nproc)}" | |
| build_torchcodec() { | |
| if [ -f "$TORCHCODEC_WHEEL" ]; then | |
| echo "torchcodec wheel already exists: $TORCHCODEC_WHEEL" | |
| return | |
| fi | |
| echo "No dGPU aarch64 torchcodec wheel found; building from source..." | |
| rm -rf /tmp/torchcodec | |
| TMP_BUILD_DIRS+=(/tmp/torchcodec) | |
| git clone --depth 1 --branch "v${TORCHCODEC_SOURCE_VERSION}" \ | |
| https://github.com/pytorch/torchcodec.git /tmp/torchcodec | |
| rm -rf /tmp/torchcodec/.git | |
| I_CONFIRM_THIS_IS_NOT_A_LICENSE_VIOLATION=1 "$BUILD_PYTHON" -m pip wheel \ | |
| --no-build-isolation \ | |
| --no-deps \ | |
| --wheel-dir "$WHEEL_DIR" \ | |
| /tmp/torchcodec | |
| if [ ! -f "$TORCHCODEC_WHEEL" ]; then | |
| echo "ERROR: torchcodec source build did not produce expected wheel:" >&2 | |
| echo " $TORCHCODEC_WHEEL" >&2 | |
| echo "Available torchcodec wheels:" >&2 | |
| find "$WHEEL_DIR" -maxdepth 1 -name 'torchcodec-*.whl' -print >&2 | |
| exit 1 | |
| fi | |
| } | |
| cd "$REPO_ROOT" | |
| build_torchcodec | |
| echo "dGPU aarch64 wheel bootstrap complete:" | |
| ls -lh "$TORCHCODEC_WHEEL" | |