ls *.h missed headers in subdirectories like crt/host_defines.h; use find -maxdepth 2 instead. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
187 lines
7.6 KiB
Bash
187 lines
7.6 KiB
Bash
#!/bin/sh
|
|
# build-cublas.sh — download cuBLASLt/cuBLAS/cudart runtime + headers for bee-gpu-stress.
|
|
#
|
|
# Downloads .deb packages from NVIDIA's CUDA apt repository (Debian 12, x86_64),
|
|
# verifies them against Packages.gz, and extracts the small subset we need:
|
|
# - headers for compiling bee-gpu-stress against cuBLASLt
|
|
# - runtime libs for libcublas, libcublasLt, libcudart inside the ISO
|
|
|
|
set -e
|
|
|
|
CUBLAS_VERSION="$1"
|
|
CUDA_USERSPACE_VERSION="$2"
|
|
CUDA_SERIES="$3"
|
|
DIST_DIR="$4"
|
|
|
|
[ -n "$CUBLAS_VERSION" ] || { echo "usage: $0 <cublas-version> <cuda-userspace-version> <cuda-series> <dist-dir>"; exit 1; }
|
|
[ -n "$CUDA_USERSPACE_VERSION" ] || { echo "usage: $0 <cublas-version> <cuda-userspace-version> <cuda-series> <dist-dir>"; exit 1; }
|
|
[ -n "$CUDA_SERIES" ] || { echo "usage: $0 <cublas-version> <cuda-userspace-version> <cuda-series> <dist-dir>"; exit 1; }
|
|
[ -n "$DIST_DIR" ] || { echo "usage: $0 <cublas-version> <cuda-userspace-version> <cuda-series> <dist-dir>"; exit 1; }
|
|
|
|
CUDA_SERIES_DASH=$(printf '%s' "$CUDA_SERIES" | tr '.' '-')
|
|
REPO_BASE="https://developer.download.nvidia.com/compute/cuda/repos/debian12/x86_64"
|
|
CACHE_DIR="${DIST_DIR}/cublas-${CUBLAS_VERSION}+cuda${CUDA_SERIES}"
|
|
CACHE_ROOT="${BEE_CACHE_DIR:-${DIST_DIR}/cache}"
|
|
DOWNLOAD_CACHE_DIR="${CACHE_ROOT}/cublas-downloads"
|
|
PACKAGES_GZ="${DOWNLOAD_CACHE_DIR}/Packages.gz"
|
|
|
|
echo "=== cuBLAS ${CUBLAS_VERSION} / cudart ${CUDA_USERSPACE_VERSION} / CUDA ${CUDA_SERIES} ==="
|
|
|
|
if [ -f "${CACHE_DIR}/include/cublasLt.h" ] && [ -f "${CACHE_DIR}/include/cuda_runtime_api.h" ] \
|
|
&& [ -f "${CACHE_DIR}/include/crt/host_defines.h" ] \
|
|
&& [ "$(find "${CACHE_DIR}/lib" \( -name 'libcublas.so*' -o -name 'libcublasLt.so*' -o -name 'libcudart.so*' \) 2>/dev/null | wc -l)" -gt 0 ]; then
|
|
echo "=== cuBLAS cached, skipping download ==="
|
|
echo "cache: $CACHE_DIR"
|
|
exit 0
|
|
fi
|
|
|
|
mkdir -p "${DOWNLOAD_CACHE_DIR}" "${CACHE_DIR}/include" "${CACHE_DIR}/lib"
|
|
|
|
echo "=== downloading Packages.gz ==="
|
|
wget -q -O "${PACKAGES_GZ}" "${REPO_BASE}/Packages.gz"
|
|
|
|
lookup_pkg() {
|
|
pkg="$1"
|
|
ver="$2" # if empty, match any version (first found)
|
|
gzip -dc "${PACKAGES_GZ}" | awk -v pkg="$pkg" -v ver="$ver" '
|
|
/^Package: / { cur_pkg=$2; gsub(/\r/, "", cur_pkg) }
|
|
/^Version: / { cur_ver=$2; gsub(/\r/, "", cur_ver) }
|
|
/^Filename: / { cur_file=$2; gsub(/\r/, "", cur_file) }
|
|
/^SHA256: / { cur_sha=$2; gsub(/\r/, "", cur_sha) }
|
|
/^$/ {
|
|
if (cur_pkg == pkg && (ver == "" || cur_ver == ver)) {
|
|
print cur_file " " cur_sha
|
|
printed=1
|
|
exit
|
|
}
|
|
cur_pkg=""; cur_ver=""; cur_file=""; cur_sha=""
|
|
}
|
|
END {
|
|
if (!printed && cur_pkg == pkg && (ver == "" || cur_ver == ver)) {
|
|
print cur_file " " cur_sha
|
|
}
|
|
}'
|
|
}
|
|
|
|
download_verified_pkg() {
|
|
pkg="$1"
|
|
ver="$2"
|
|
|
|
meta="$(lookup_pkg "$pkg" "$ver")"
|
|
[ -n "$meta" ] || { echo "ERROR: package metadata not found for ${pkg} ${ver}"; exit 1; }
|
|
|
|
repo_file="$(printf '%s\n' "$meta" | awk '{print $1}')"
|
|
repo_sha="$(printf '%s\n' "$meta" | awk '{print $2}')"
|
|
[ -n "$repo_file" ] || { echo "ERROR: package filename missing for ${pkg}"; exit 1; }
|
|
[ -n "$repo_sha" ] || { echo "ERROR: package sha missing for ${pkg}"; exit 1; }
|
|
|
|
out="${DOWNLOAD_CACHE_DIR}/$(basename "$repo_file")"
|
|
if [ -f "$out" ]; then
|
|
actual_sha="$(sha256sum "$out" | awk '{print $1}')"
|
|
if [ "$actual_sha" = "$repo_sha" ]; then
|
|
echo "=== using cached $(basename "$repo_file") ===" >&2
|
|
printf '%s\n' "$out"
|
|
return 0
|
|
fi
|
|
echo "=== removing stale $(basename "$repo_file") (sha256 mismatch) ===" >&2
|
|
rm -f "$out"
|
|
fi
|
|
|
|
echo "=== downloading $(basename "$repo_file") ===" >&2
|
|
wget --show-progress -O "$out" "${REPO_BASE}/$(basename "$repo_file")"
|
|
|
|
actual_sha="$(sha256sum "$out" | awk '{print $1}')"
|
|
if [ "$actual_sha" != "$repo_sha" ]; then
|
|
echo "ERROR: sha256 mismatch for $(basename "$repo_file")" >&2
|
|
echo " expected: $repo_sha" >&2
|
|
echo " actual: $actual_sha" >&2
|
|
rm -f "$out"
|
|
exit 1
|
|
fi
|
|
echo "sha256 OK: $(basename "$repo_file")" >&2
|
|
printf '%s\n' "$out"
|
|
}
|
|
|
|
extract_deb() {
|
|
deb="$1"
|
|
dst="$2"
|
|
mkdir -p "$dst"
|
|
(
|
|
cd "$dst"
|
|
ar x "$deb"
|
|
data_tar=$(ls data.tar.* 2>/dev/null | head -1)
|
|
[ -n "$data_tar" ] || { echo "ERROR: data.tar.* not found in $deb"; exit 1; }
|
|
tar xf "$data_tar"
|
|
)
|
|
}
|
|
|
|
copy_headers() {
|
|
from="$1"
|
|
if [ -d "${from}/usr/include" ]; then
|
|
cp -a "${from}/usr/include/." "${CACHE_DIR}/include/"
|
|
fi
|
|
# NVIDIA CUDA packages install headers under /usr/local/cuda-X.Y/targets/x86_64-linux/include/
|
|
find "$from" -type d -name include | while read -r inc_dir; do
|
|
case "$inc_dir" in
|
|
*/usr/include) ;; # already handled above
|
|
*)
|
|
if find "${inc_dir}" -name '*.h' -maxdepth 2 | grep -q .; then
|
|
cp -a "${inc_dir}/." "${CACHE_DIR}/include/"
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
copy_libs() {
|
|
from="$1"
|
|
find "$from" \( -name 'libcublas.so*' -o -name 'libcublasLt.so*' -o -name 'libcudart.so*' \) \
|
|
\( -type f -o -type l \) -exec cp -a {} "${CACHE_DIR}/lib/" \;
|
|
}
|
|
|
|
make_links() {
|
|
base="$1"
|
|
versioned=$(find "${CACHE_DIR}/lib" -maxdepth 1 -name "${base}.so.[0-9]*" -type f | sort | head -1)
|
|
[ -n "$versioned" ] || return 0
|
|
soname=$(printf '%s\n' "$versioned" | sed -E "s#.*/(${base}\.so\.[0-9]+).*#\\1#")
|
|
target=$(basename "$versioned")
|
|
ln -sf "$target" "${CACHE_DIR}/lib/${soname}" 2>/dev/null || true
|
|
ln -sf "${soname}" "${CACHE_DIR}/lib/${base}.so" 2>/dev/null || true
|
|
}
|
|
|
|
TMP_DIR=$(mktemp -d)
|
|
trap 'rm -rf "$TMP_DIR"' EXIT INT TERM
|
|
|
|
CUBLAS_RT_DEB=$(download_verified_pkg "libcublas-${CUDA_SERIES_DASH}" "${CUBLAS_VERSION}")
|
|
CUBLAS_DEV_DEB=$(download_verified_pkg "libcublas-dev-${CUDA_SERIES_DASH}" "${CUBLAS_VERSION}")
|
|
CUDART_RT_DEB=$(download_verified_pkg "cuda-cudart-${CUDA_SERIES_DASH}" "${CUDA_USERSPACE_VERSION}")
|
|
CUDART_DEV_DEB=$(download_verified_pkg "cuda-cudart-dev-${CUDA_SERIES_DASH}" "${CUDA_USERSPACE_VERSION}")
|
|
CUDA_CRT_DEB=$(download_verified_pkg "cuda-crt-${CUDA_SERIES_DASH}" "")
|
|
|
|
extract_deb "$CUBLAS_RT_DEB" "${TMP_DIR}/cublas-rt"
|
|
extract_deb "$CUBLAS_DEV_DEB" "${TMP_DIR}/cublas-dev"
|
|
extract_deb "$CUDART_RT_DEB" "${TMP_DIR}/cudart-rt"
|
|
extract_deb "$CUDART_DEV_DEB" "${TMP_DIR}/cudart-dev"
|
|
extract_deb "$CUDA_CRT_DEB" "${TMP_DIR}/cuda-crt"
|
|
|
|
copy_headers "${TMP_DIR}/cublas-dev"
|
|
copy_headers "${TMP_DIR}/cudart-dev"
|
|
copy_headers "${TMP_DIR}/cuda-crt"
|
|
copy_libs "${TMP_DIR}/cublas-rt"
|
|
copy_libs "${TMP_DIR}/cudart-rt"
|
|
|
|
make_links "libcublas"
|
|
make_links "libcublasLt"
|
|
make_links "libcudart"
|
|
|
|
[ -f "${CACHE_DIR}/include/cublasLt.h" ] || { echo "ERROR: cublasLt.h not extracted"; exit 1; }
|
|
[ -f "${CACHE_DIR}/include/cuda_runtime_api.h" ] || { echo "ERROR: cuda_runtime_api.h not extracted"; exit 1; }
|
|
[ "$(find "${CACHE_DIR}/lib" -maxdepth 1 -name 'libcublasLt.so*' | wc -l)" -gt 0 ] || { echo "ERROR: libcublasLt not extracted"; exit 1; }
|
|
[ "$(find "${CACHE_DIR}/lib" -maxdepth 1 -name 'libcublas.so*' | wc -l)" -gt 0 ] || { echo "ERROR: libcublas not extracted"; exit 1; }
|
|
[ "$(find "${CACHE_DIR}/lib" -maxdepth 1 -name 'libcudart.so*' | wc -l)" -gt 0 ] || { echo "ERROR: libcudart not extracted"; exit 1; }
|
|
|
|
echo "=== cuBLAS extraction complete ==="
|
|
echo "cache: $CACHE_DIR"
|
|
echo "headers: $(find "${CACHE_DIR}/include" -type f | wc -l)"
|
|
echo "libs: $(find "${CACHE_DIR}/lib" -maxdepth 1 \( -name 'libcublas*.so*' -o -name 'libcudart.so*' \) | wc -l)"
|