mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-15 04:40:40 -07:00
fix: update package_cuda.py for PyInstaller 6.18 layout and remove split_binary.py
- Fix is_nvidia_file() to match NVIDIA DLLs in _internal/torch/lib/ (PyInstaller 6.18 + torch 2.10 no longer uses nvidia/ subdirectories) - Remove deprecated split_binary.py (both archives are under 2GB) - Update torch_compat range to >=2.6.0,<2.11.0 - Update build docs for new dual-archive packaging flow
This commit is contained in:
+45
-19
@@ -19,41 +19,67 @@ import sys
|
||||
import tarfile
|
||||
from pathlib import Path
|
||||
|
||||
# Directories / prefixes that belong in the CUDA libs archive.
|
||||
# PyInstaller --onedir puts NVIDIA packages in nvidia/ subdirectories
|
||||
# (e.g. nvidia/cublas/lib/, nvidia/cudnn/lib/, etc.)
|
||||
NVIDIA_PREFIXES = (
|
||||
"nvidia/",
|
||||
"nvidia\\",
|
||||
)
|
||||
|
||||
# Individual DLL patterns that may end up at the top level on Windows
|
||||
# DLL name prefixes that identify NVIDIA CUDA runtime libraries.
|
||||
# These DLLs may appear in different locations depending on the torch
|
||||
# and PyInstaller version:
|
||||
# - nvidia/ subdirectories (older torch with separate nvidia-* packages)
|
||||
# - _internal/torch/lib/ (torch 2.10+ bundles NVIDIA DLLs directly)
|
||||
# - Top-level directory (some PyInstaller versions)
|
||||
NVIDIA_DLL_PREFIXES = (
|
||||
"cublas",
|
||||
"cublaslt",
|
||||
"cudart",
|
||||
"cudnn",
|
||||
"cufft",
|
||||
"cufftw",
|
||||
"curand",
|
||||
"cusolver",
|
||||
"cusolvermg",
|
||||
"cusparse",
|
||||
"nvjitlink",
|
||||
"nvrtc",
|
||||
"nccl",
|
||||
"caffe2_nvrtc",
|
||||
)
|
||||
|
||||
# Files to keep in the server core even if they match NVIDIA prefixes.
|
||||
# These are small Python modules or stubs, not the large runtime DLLs.
|
||||
NVIDIA_KEEP_IN_CORE = {
|
||||
"torch/cuda/nccl.py",
|
||||
"torch/_inductor/codegen/cuda/cutlass_lib_extensions/cutlass_mock_imports/cuda/cudart.py",
|
||||
}
|
||||
|
||||
|
||||
def is_nvidia_file(rel_path: str) -> bool:
|
||||
"""Check if a relative path belongs to the NVIDIA CUDA libs."""
|
||||
"""Check if a relative path belongs to the NVIDIA CUDA libs.
|
||||
|
||||
Identifies large NVIDIA runtime DLLs (.dll/.so) regardless of where
|
||||
PyInstaller placed them. Excludes small Python stubs that happen to
|
||||
share NVIDIA-related names.
|
||||
"""
|
||||
rel_lower = rel_path.lower().replace("\\", "/")
|
||||
|
||||
# Files under nvidia/ subdirectory tree
|
||||
if rel_lower.startswith("nvidia/"):
|
||||
return True
|
||||
# Never split out Python source files or small stubs
|
||||
if rel_lower in NVIDIA_KEEP_IN_CORE:
|
||||
return False
|
||||
|
||||
# Top-level NVIDIA DLLs (Windows) — e.g. cublas64_12.dll
|
||||
name = rel_lower.rsplit("/", 1)[-1]
|
||||
for prefix in NVIDIA_DLL_PREFIXES:
|
||||
if name.startswith(prefix) and (name.endswith(".dll") or name.endswith(".so")):
|
||||
# Files under nvidia/ subdirectory tree (older torch layout)
|
||||
if rel_lower.startswith("nvidia/") or "nvidia/" in rel_lower.split("/", 1)[-1:]:
|
||||
# Only DLLs/shared objects — not .py, .dist-info, etc.
|
||||
if rel_lower.endswith((".dll", ".so")):
|
||||
return True
|
||||
# Include entire nvidia/ namespace package tree
|
||||
for part in rel_lower.split("/"):
|
||||
if part == "nvidia":
|
||||
return True
|
||||
|
||||
# NVIDIA DLLs anywhere in the tree (e.g. _internal/torch/lib/cublas64_12.dll)
|
||||
name = rel_lower.rsplit("/", 1)[-1]
|
||||
if name.endswith(".dll") or name.endswith(".so"):
|
||||
name_no_ext = name.rsplit(".", 1)[0]
|
||||
for prefix in NVIDIA_DLL_PREFIXES:
|
||||
if name_no_ext.startswith(prefix):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@@ -186,8 +212,8 @@ def main():
|
||||
parser.add_argument(
|
||||
"--torch-compat",
|
||||
type=str,
|
||||
default=">=2.6.0,<2.8.0",
|
||||
help="Torch version compatibility range (default: >=2.6.0,<2.8.0)",
|
||||
default=">=2.6.0,<2.11.0",
|
||||
help="Torch version compatibility range (default: >=2.6.0,<2.11.0)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
"""
|
||||
Split a large binary into chunks for GitHub Releases (<2 GB each).
|
||||
|
||||
DEPRECATED: For CUDA builds, use scripts/package_cuda.py instead.
|
||||
This script was used when the CUDA binary was built with --onefile and
|
||||
needed to be split into parts for the 2GB GitHub Release asset limit.
|
||||
With the switch to --onedir + dual archives (server core + CUDA libs),
|
||||
package_cuda.py handles the packaging.
|
||||
|
||||
Usage:
|
||||
python scripts/split_binary.py backend/dist/voicebox-server-cuda.exe
|
||||
python scripts/split_binary.py backend/dist/voicebox-server-cuda.exe --chunk-size 1900000000
|
||||
python scripts/split_binary.py backend/dist/voicebox-server-cuda.exe --output release-assets/
|
||||
|
||||
The script produces:
|
||||
- voicebox-server-cuda.part00.exe, .part01.exe, ... (binary chunks)
|
||||
- voicebox-server-cuda.sha256 (SHA-256 checksum of the complete file)
|
||||
- voicebox-server-cuda.manifest (ordered list of part filenames)
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def split(input_path: Path, chunk_size: int, output_dir: Path):
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
data = input_path.read_bytes()
|
||||
total_size = len(data)
|
||||
|
||||
# Write SHA-256 of the complete file
|
||||
sha256 = hashlib.sha256(data).hexdigest()
|
||||
checksum_file = output_dir / f"{input_path.stem}.sha256"
|
||||
checksum_file.write_text(f"{sha256} {input_path.name}\n")
|
||||
|
||||
# Split into chunks
|
||||
parts = []
|
||||
for i in range(0, total_size, chunk_size):
|
||||
part_index = len(parts)
|
||||
part_name = f"{input_path.stem}.part{part_index:02d}{input_path.suffix}"
|
||||
part_path = output_dir / part_name
|
||||
part_path.write_bytes(data[i : i + chunk_size])
|
||||
parts.append(part_name)
|
||||
|
||||
# Write manifest (ordered list of part filenames)
|
||||
manifest_file = output_dir / f"{input_path.stem}.manifest"
|
||||
manifest_file.write_text("\n".join(parts) + "\n")
|
||||
|
||||
print(f"Input: {input_path} ({total_size / (1024**3):.2f} GB)")
|
||||
print(f"Output: {output_dir}/")
|
||||
print(f"Parts: {len(parts)} (chunk size: {chunk_size / (1024**3):.2f} GB)")
|
||||
print(f"SHA-256: {sha256}")
|
||||
print(f"Manifest: {manifest_file.name}")
|
||||
for p in parts:
|
||||
size = (output_dir / p).stat().st_size
|
||||
print(f" {p} ({size / (1024**3):.2f} GB)")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Split a large binary into chunks for GitHub Releases"
|
||||
)
|
||||
parser.add_argument("input", type=Path, help="Path to the binary file to split")
|
||||
parser.add_argument(
|
||||
"--chunk-size",
|
||||
type=int,
|
||||
default=1_900_000_000, # 1.9 GB — safely under 2 GB GitHub limit
|
||||
help="Maximum chunk size in bytes (default: 1.9 GB)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
type=Path,
|
||||
default=None,
|
||||
help="Output directory (default: same directory as input)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.input.exists():
|
||||
print(f"Error: {args.input} does not exist", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
output_dir = args.output or args.input.parent
|
||||
split(args.input, args.chunk_size, output_dir)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user