Download src/tools/build_wheel.sh from CIIL/Anuvani-Diarization: direct link, hf CLI and curl.
- Browser
- Download file 8.76 kB
-
https://huggingface.co/CIIL/Anuvani-Diarization/resolve/main/src/tools/build_wheel.sh
- Command line
-
hf download hf://CIIL/Anuvani-Diarization/src/tools/build_wheel.sh
-
curl -L -o build_wheel.sh https://huggingface.co/CIIL/Anuvani-Diarization/resolve/main/src/tools/build_wheel.sh
8.76 kB
| # Copyright (c) 2026 The Anuvani-Diarization authors | |
| # SPDX-License-Identifier: MIT OR Apache-2.0 | |
| # Build the self-contained wheel: CPython >= 3.10 (abi3), manylinux_2_28 x86_64, inside a pinned container. | |
| # | |
| # tools/build_wheel.sh -> artifacts/wheels/anuvani_diar-<version>-cp310-abi3-manylinux_2_28_x86_64.whl | |
| # artifacts/wheels/<wheel>.build.json (what was built, from what, and the checks) | |
| # | |
| # Pinned: the build image by digest, rustup-init 1.29.1 by sha256, rustc 1.94.1 (minimal profile), maturin 1.15.0, | |
| # the crates by Cargo.lock (--locked), file times by SOURCE_DATE_EPOCH (the commit's time). | |
| # | |
| # 1. tools/stage_wheel_files.py stages the bundled libraries, their licence texts and the assets (they come from the | |
| # reference environment) and verifies them. | |
| # 2. The build context is the files git tracks for the build (working-tree content) plus the staged files, copied | |
| # to artifacts/wheel_build/anuvani_diar_rs, so nothing else in this tree (target/, .venv/, a stray _core.so) can | |
| # enter the wheel. The build starts from an empty target directory. | |
| # 3. In the container: maturin build --release --locked --strip --compatibility manylinux_2_28, with every source | |
| # path remapped (no host or build path in _core), and --auditwheel check (verifies the tag, changes nothing). | |
| # 4. Checks, also in the container: `auditwheel show` (never `repair`: it would rewrite the bundled libraries and | |
| # break their sha256); the bundled files' sha256 in the wheel; `strings` on _core for host or build | |
| # paths; the glibc symbol versions _core needs (<= 2.28) and its NEEDED libraries. | |
| # | |
| # The image is pulled once (docker pull); rustup, the toolchain, the crates and maturin are cached in | |
| # artifacts/wheel_build/cache (git-ignored) after the first build. | |
| set -euo pipefail | |
| PROJ=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) | |
| IMAGE="quay.io/pypa/manylinux_2_28_x86_64@sha256:39df0042d5cc900b085aa25a0659368b42a0006c54c474299b785b44c1b4ff82" | |
| RUSTUP_VERSION=1.29.1 | |
| RUSTUP_SHA256=dda7234360b7f578ca8b0ddcb80145646fa61a67c1720a5abc7051b35c9fcb71 | |
| RUST_VERSION=1.94.1 | |
| MATURIN_VERSION=1.15.0 | |
| WORK="$PROJ/artifacts/wheel_build" | |
| OUT="$PROJ/artifacts/wheels" | |
| cd "$PROJ" | |
| python3 tools/stage_wheel_files.py "$@" | |
| COMMIT=$(git rev-parse HEAD) | |
| DIRTY=$(git status --porcelain -- . | grep -q . && echo true || echo false) | |
| SOURCE_DATE_EPOCH=$(git log -1 --format=%ct) | |
| rm -rf "$WORK/anuvani_diar_rs" "$WORK/target" "$WORK/dist" | |
| mkdir -p "$WORK/anuvani_diar_rs" "$WORK/cache" "$OUT" | |
| git ls-files -z -- Cargo.toml Cargo.lock pyproject.toml crates python tools/wheel_files.json assets/MANIFEST.json \ | |
| LICENSE-MIT LICENSE-APACHE THIRD_PARTY_NOTICES.md LICENSES \ | |
| | tar --null -T - -cf - | tar -xf - -C "$WORK/anuvani_diar_rs" | |
| cp -a python/anuvani_diar/_libs python/anuvani_diar/assets "$WORK/anuvani_diar_rs/python/anuvani_diar/" | |
| START=$(date +%s) | |
| docker run --rm -i --user "$(id -u):$(id -g)" -v "$WORK:/build" -v "$WORK/cache:/cache" -v "$OUT:/out" \ | |
| -e IMAGE="$IMAGE" -e RUSTUP_VERSION="$RUSTUP_VERSION" -e RUSTUP_SHA256="$RUSTUP_SHA256" -e RUST_VERSION="$RUST_VERSION" \ | |
| -e MATURIN_VERSION="$MATURIN_VERSION" -e SOURCE_DATE_EPOCH="$SOURCE_DATE_EPOCH" -e COMMIT="$COMMIT" -e DIRTY="$DIRTY" \ | |
| "$IMAGE" bash -s <<'IN_CONTAINER' | |
| set -euo pipefail | |
| export HOME=/tmp/home RUSTUP_HOME=/cache/rustup CARGO_HOME=/cache/cargo | |
| export PATH=/cache/cargo/bin:$PATH | |
| mkdir -p "$HOME" | |
| if [[ ! -x /cache/cargo/bin/rustup ]]; then | |
| curl -sSf -o /tmp/rustup-init "https://static.rust-lang.org/rustup/archive/$RUSTUP_VERSION/x86_64-unknown-linux-gnu/rustup-init" | |
| echo "$RUSTUP_SHA256 /tmp/rustup-init" | sha256sum -c --quiet - | |
| chmod +x /tmp/rustup-init | |
| /tmp/rustup-init -y -q --no-modify-path --profile minimal --default-toolchain none | |
| fi | |
| rustup toolchain install "$RUST_VERSION" --profile minimal >/dev/null 2>&1 || rustup toolchain install "$RUST_VERSION" --profile minimal | |
| export RUSTUP_TOOLCHAIN="$RUST_VERSION" | |
| M=/cache/maturin-$MATURIN_VERSION | |
| if [[ ! -x $M/bin/maturin ]]; then | |
| /opt/python/cp312-cp312/bin/python -m venv "$M" | |
| "$M/bin/pip" install -q "maturin==$MATURIN_VERSION" | |
| fi | |
| cd /build/anuvani_diar_rs | |
| # no path of the build machine in the binary: sources, the crate registry, the toolchain | |
| export RUSTFLAGS="--remap-path-prefix=/build/anuvani_diar_rs=/anuvani_diar_rs --remap-path-prefix=/cache/cargo=/cargo --remap-path-prefix=/cache/rustup=/rustup" | |
| export CARGO_TARGET_DIR=/build/target | |
| if ! "$M/bin/maturin" build --release --locked --strip --compatibility manylinux_2_28 --auditwheel check \ | |
| -i /opt/python/cp310-cp310/bin/python3.10 --out /build/dist > /build/maturin.log 2>&1; then | |
| tail -40 /build/maturin.log | |
| exit 1 | |
| fi | |
| grep -v "^\s*\(Compiling\|Downloaded\|Downloading\) " /build/maturin.log | tail -8 | |
| WHEEL=$(ls /build/dist/*.whl) | |
| cp "$WHEEL" /out/ | |
| W=/out/$(basename "$WHEEL") | |
| # ---- checks | |
| auditwheel show "$W" > /build/auditwheel_show.txt 2>&1 || true | |
| /opt/python/cp312-cp312/bin/python - "$W" <<'PY' | |
| import hashlib, json, os, re, subprocess, sys, zipfile | |
| from pathlib import Path | |
| wheel = Path(sys.argv[1]) | |
| z = zipfile.ZipFile(wheel) | |
| names = z.namelist() | |
| spec = json.loads(Path("tools/wheel_files.json").read_text()) | |
| arrays = json.loads(Path("assets/MANIFEST.json").read_text())["arrays"] | |
| bad = [] | |
| for name, e in spec["libs"].items(): | |
| member = f"anuvani_diar/_libs/{name}" | |
| if member not in names: | |
| bad.append(f"missing {member}") | |
| elif e["sha256"] and hashlib.sha256(z.read(member)).hexdigest() != e["sha256"]: | |
| bad.append(f"{member}: sha256 differs from tools/wheel_files.json") | |
| for name in spec["assets"]: | |
| member = f"anuvani_diar/assets/{name}" | |
| if member not in names: | |
| bad.append(f"missing {member}") | |
| elif name.endswith(".bin") and hashlib.sha256(z.read(member)).hexdigest() != arrays[name[:-4]]["sha256"]: | |
| bad.append(f"{member}: sha256 differs from assets/MANIFEST.json") | |
| core = "anuvani_diar/_core.abi3.so" | |
| Path("/build/_core.abi3.so").write_bytes(z.read(core)) | |
| strings = subprocess.run(["strings", "-a", "/build/_core.abi3.so"], capture_output=True, text=True, check=True).stdout | |
| # a host or build path: one of these prefixes where a path starts (std's own "/rustc/<hash>/library/std/ | |
| # src/io/..." is not a hit), or a tool directory anywhere | |
| leaks = sorted({m.group(0) for m in re.finditer( | |
| r"((?<![\w./-])(/home/|/root/|/mnt/|/tmp/|/build/|/cache/|/opt/|/io/|/usr/local/)|anaconda|\.cargo/|\.rustup/)[^\s\"']*", strings)}) | |
| prefixes = {} | |
| for m in re.finditer(r"(?<![\w./-])/[\w.-]+/[\w.-]+", strings): | |
| prefixes[m.group(0)] = prefixes.get(m.group(0), 0) + 1 | |
| if leaks: | |
| bad.append(f"_core.abi3.so names build or host paths: {leaks[:10]}") | |
| glibc = sorted(set(re.findall(r"GLIBC_([0-9.]+)", subprocess.run(["objdump", "-T", "/build/_core.abi3.so"], capture_output=True, | |
| text=True, check=True).stdout)), key=lambda v: tuple(map(int, v.split(".")))) | |
| if glibc and tuple(map(int, glibc[-1].split("."))) > (2, 28): | |
| bad.append(f"_core.abi3.so needs GLIBC_{glibc[-1]} (> 2.28)") | |
| needed = re.findall(r"\(NEEDED\).*\[(.*)\]", subprocess.run(["readelf", "-d", "/build/_core.abi3.so"], capture_output=True, | |
| text=True, check=True).stdout) | |
| rustc = subprocess.run(["rustc", "-vV"], capture_output=True, text=True, check=True).stdout | |
| maturin = subprocess.run([f"/cache/maturin-{os.environ['MATURIN_VERSION']}/bin/maturin", "--version"], capture_output=True, | |
| text=True, check=True).stdout.strip() | |
| record = dict( | |
| wheel=wheel.name, sha256=hashlib.sha256(wheel.read_bytes()).hexdigest(), bytes=wheel.stat().st_size, | |
| commit=os.environ["COMMIT"], dirty=os.environ["DIRTY"] == "true", source_date_epoch=int(os.environ["SOURCE_DATE_EPOCH"]), | |
| image=os.environ["IMAGE"], rustc=rustc.splitlines()[0], rustc_host=re.search(r"host: (.*)", rustc).group(1), | |
| maturin=maturin, core_bytes=len(z.read(core)), core_glibc_max=glibc[-1] if glibc else None, core_needed=needed, | |
| files=len(names), path_strings_in_core=leaks, path_prefixes_in_core=dict(sorted(prefixes.items())), auditwheel_show=Path("/build/auditwheel_show.txt").read_text(), | |
| problems=bad) | |
| Path(f"/out/{wheel.name}.build.json").write_text(json.dumps(record, indent=1) + "\n") | |
| print(json.dumps({k: v for k, v in record.items() if k != "auditwheel_show"}, indent=1)) | |
| print(record["auditwheel_show"]) | |
| sys.exit(1 if bad else 0) | |
| PY | |
| IN_CONTAINER | |
| # the container must have written this run's record (a container that ran nothing would also exit 0) | |
| RECORD=$(ls -t "$OUT"/*.build.json 2>/dev/null | head -1) | |
| if [[ -z $RECORD || $(stat -c %Y "$RECORD") -lt $START ]]; then | |
| echo "build_wheel.sh: no wheel was built" >&2 | |
| exit 1 | |
| fi | |
| echo "built ${RECORD%.build.json} (record: $RECORD)" | |