fetch-deps.sh 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. #!/usr/bin/env bash
  2. # Fetch every vLLM build sub-dependency for the r27 image at the pins the
  3. # r27 tree itself declares (cmake/external_projects/*.cmake + CMakeLists.txt).
  4. #
  5. # No fallbacks: each clone fetches the pinned ref directly and fails the
  6. # build loudly on any mismatch. Before cloning, every pin printed below is
  7. # grep-verified against the cloned vLLM tree — if a pin has drifted from
  8. # what the tree expects, the mismatch aborts with the offending pin(s).
  9. #
  10. # Lock: after staging, the script verifies the staged state against
  11. # $DEPS_LOCK (default /tmp/deps.lock, shipped in the image dir as
  12. # locks/deps.lock and COPY'd by the Dockerfile). Every build therefore
  13. # proves: each dep is a clean checkout at the locked commit, every vendored
  14. # cutlass gitlink is populated at the locked SHA (r18's empty-stub failure
  15. # mode), and the non-git triton_kernels tree has the locked content hash.
  16. # Regenerate the lock after a pin cut: `--regen-lock <out>` (or in-build:
  17. # --build-arg REGEN_LOCK=1). `--emit-lock <out>` writes the lock from an
  18. # already-staged $DEPS without re-cloning (offline lock generation).
  19. #
  20. # Runs INSIDE the build container: /workspace/vllm must already contain the
  21. # gated r27 tree (see Dockerfile `vllm-clone` stage). Populates /deps2.
  22. set -euo pipefail
  23. WORK=${WORK:-/workspace/vllm}
  24. DEPS=${DEPS:-/deps2}
  25. LOCK=${DEPS_LOCK:-/tmp/deps.lock}
  26. git config --global protocol.version 0
  27. fetch() { # repo ref dst
  28. local repo="$1" ref="$2" dst="$3"
  29. echo "[deps] $dst @ $ref ($repo)"
  30. git init -q "$dst"
  31. git -C "$dst" fetch -q --depth 1 "$repo" "$ref"
  32. git -C "$dst" checkout -q FETCH_HEAD
  33. git -C "$dst" config --global --add safe.directory "$dst"
  34. }
  35. gate() { # file:pattern ... — every pin must appear verbatim in the tree
  36. local missing=0
  37. for g in "$@"; do
  38. local file="${g%%:*}" pattern="${g#*:}"
  39. local fp="$WORK/cmake/external_projects/$file"
  40. [ -f "$fp" ] || fp="$WORK/$file" # CMakeLists.txt lives at the root
  41. if ! grep -Fq "$pattern" "$fp"; then
  42. echo "GATE FAILED: pin '$pattern' not found in $file (tree/pin drift)" >&2
  43. missing=1
  44. fi
  45. done
  46. [ "$missing" = 0 ] || { echo "aborting: staged pins drifted from the tree" >&2; exit 1; }
  47. echo "[deps] all pins verified against the r27 tree"
  48. }
  49. # ---- 1. Verify our pins against the tree's own declarations ----
  50. stage_pins() {
  51. gate CMakeLists.txt:nvidia/cutlass.git \
  52. CMakeLists.txt:v4.4.2 \
  53. deepgemm.cmake:deepseek-ai/DeepGEMM \
  54. deepgemm.cmake:8b1392b978f5a03c828dd1711090d7fb50958b8a \
  55. flashkda.cmake:3b225bf26bb8e218928a1fe14751cb48cf31d11b \
  56. flashmla.cmake:a8f794d1251cbfd88a5011445dd5582289c727e4 \
  57. fmha_sm100.cmake:087c161814d4d9c735b46c21212a09e5f8eb92fa \
  58. qutlass.cmake:e74319e3405ce6d71965732880f5dc1f52371f64 \
  59. tml_fa4.cmake:b206834606ed5b5f21f8eed6b0683f528ea9cf7d \
  60. vllm_flash_attn.cmake:f3e1a4f74c99145c0717709860bf765de1703779 \
  61. vllm_flash_attn.cmake:vllm-project/flash-attention \
  62. triton_kernels.cmake:triton-lang/triton \
  63. triton_kernels.cmake:v3.5.1
  64. }
  65. # ---- 2. Clone deps at the tree's pins ----
  66. stage_clones() {
  67. mkdir -p "$DEPS"
  68. fetch https://github.com/nvidia/cutlass.git v4.4.2 "$DEPS/cutlass"
  69. fetch https://github.com/vllm-project/flash-attention.git \
  70. f3e1a4f74c99145c0717709860bf765de1703779 "$DEPS/flash-attention"
  71. fetch https://github.com/vllm-project/MSA.git \
  72. 087c161814d4d9c735b46c21212a09e5f8eb92fa "$DEPS/MSA"
  73. fetch https://github.com/vllm-project/FlashMLA.git \
  74. a8f794d1251cbfd88a5011445dd5582289c727e4 "$DEPS/FlashMLA"
  75. fetch https://github.com/vllm-project/FlashKDA.git \
  76. 3b225bf26bb8e218928a1fe14751cb48cf31d11b "$DEPS/FlashKDA"
  77. fetch https://github.com/IST-DASLab/qutlass.git \
  78. e74319e3405ce6d71965732880f5dc1f52371f64 "$DEPS/qutlass"
  79. fetch https://github.com/vllm-project/tml-fa4.git \
  80. b206834606ed5b5f21f8eed6b0683f528ea9cf7d "$DEPS/tml-fa4"
  81. fetch https://github.com/triton-lang/triton.git v3.5.1 "$DEPS/triton"
  82. }
  83. # ---- 3. Sub-module populations the tree's install steps require ----
  84. # Several deps vendor cutlass via a 160000 gitlink and their cmake install
  85. # steps require the tree to EXIST (r18 lesson: an empty stub dies at cmake
  86. # INSTALL — and r27's FlashMLA addendum died at compile). Generic rule: walk
  87. # every dep's tree recursively and populate each gitlink at its pinned SHA.
  88. # AMD-only trees (composable_kernel, aiter) are skipped explicitly — the
  89. # CUDA cmake path never references them. All gitlinks so far vendor
  90. # nvidia/cutlass; anything else aborts loudly, keeping the contract explicit.
  91. stage_gitlinks() {
  92. local d path sha
  93. for d in flash-attention MSA FlashMLA FlashKDA qutlass tml-fa4; do
  94. while IFS=$'\t' read -r path sha; do
  95. [ -n "${path:-}" ] || continue
  96. case "$path" in
  97. */composable_kernel|composable_kernel|*/aiter|aiter)
  98. echo "[deps] skip AMD-only gitlink $path (CUDA path unused)"; continue ;;
  99. esac
  100. case "$(basename "$path")" in
  101. cutlass) ;; # expected vendor
  102. *) echo "ABORT: unhandled gitlink '$path' in $d (populate explicitly)" >&2; exit 1 ;;
  103. esac
  104. # a gitlink path EXISTS in the worktree as an EMPTY dir (git's
  105. # clone behavior) — the gate is "did WE populate it", .git marker.
  106. # Skipping on plain -d reproduces the r18 empty-stub failure.
  107. if [ ! -e "$DEPS/$d/$path/.git" ]; then
  108. fetch https://github.com/nvidia/cutlass.git "$sha" "$DEPS/$d/$path"
  109. fi
  110. done < <(git -C "$DEPS/$d" ls-tree -r -t HEAD | awk '$1=="160000" {print $4"\t"$3}')
  111. done
  112. echo "[deps] gitlink populations verified"
  113. }
  114. # ---- 4. Triton into the layout the Dockerfile/build expects ----
  115. stage_triton_layout() {
  116. mv "$DEPS/triton/python/triton_kernels/triton_kernels" "$DEPS/triton_kernels"
  117. rm -rf "$DEPS/triton"
  118. }
  119. stage_all() {
  120. stage_pins
  121. stage_clones
  122. stage_gitlinks
  123. stage_triton_layout
  124. # ---- 5. Manifest ----
  125. echo "[deps] staged:"
  126. local d
  127. for d in cutlass flash-attention MSA FlashMLA FlashKDA qutlass tml-fa4 triton_kernels; do
  128. printf ' %-16s %s\n' "$d" "$(git -C "$DEPS/$d" rev-parse HEAD 2>/dev/null | cut -c1-12)"
  129. done
  130. }
  131. # ---------------------------------------------------------------- lock ----
  132. # sha256 over the sorted file list (relpath + per-file sha256) — the
  133. # file-level fingerprint for non-git content (git checkouts fingerprint via
  134. # commit + clean worktree instead: git objects already hash every byte).
  135. blob_hash() {
  136. (cd "$1" && find . -type f | sort | while IFS= read -r f; do sha256sum "$f"; done | sha256sum | cut -d' ' -f1)
  137. }
  138. emit_lock() { # out — write the lock from the staged $DEPS state
  139. local out="$1" d path sha h
  140. {
  141. echo "# sm121 deps.lock — staged-content fingerprints for fetch-deps.sh"
  142. echo "# generated: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
  143. echo "# format: dep <name> <commit> | gitlink <parent>/<path> <commit>"
  144. echo "# | skip <parent>/<path> <commit> | blob <name> <sha256>"
  145. echo "# regenerate: bash fetch-deps.sh --regen-lock <out> (in-build: --build-arg REGEN_LOCK=1)"
  146. for d in cutlass flash-attention MSA FlashMLA FlashKDA qutlass tml-fa4; do
  147. [ -e "$DEPS/$d/.git" ] || { echo "emit_lock: $d not staged" >&2; exit 1; }
  148. echo "dep $d $(git -C "$DEPS/$d" rev-parse HEAD)"
  149. done
  150. for d in flash-attention MSA FlashMLA FlashKDA qutlass tml-fa4; do
  151. while IFS=$'\t' read -r path sha; do
  152. [ -n "${path:-}" ] || continue
  153. case "$path" in
  154. */composable_kernel|composable_kernel|*/aiter|aiter)
  155. echo "skip $d/$path $sha" ;;
  156. *)
  157. echo "gitlink $d/$path $sha" ;;
  158. esac
  159. done < <(git -C "$DEPS/$d" ls-tree -r -t HEAD | awk '$1=="160000" {print $4"\t"$3}')
  160. done
  161. [ -d "$DEPS/triton_kernels" ] || { echo "emit_lock: triton_kernels missing (run stage_triton_layout first)" >&2; exit 1; }
  162. h=$(blob_hash "$DEPS/triton_kernels")
  163. echo "blob triton_kernels $h"
  164. } > "$out"
  165. echo "[deps] lock written: $out"
  166. }
  167. verify_lock() { # lock — abort the build if staged content != lock
  168. local lock="$1"
  169. if [ ! -f "$lock" ]; then
  170. echo "[deps] no lock at $lock — nothing to verify against (staging unverified)"
  171. return 0
  172. fi
  173. local kind name want got d fail=0
  174. while read -r kind name want; do
  175. case "$kind" in
  176. dep | gitlink)
  177. d="$DEPS/$name"
  178. if [ ! -e "$d/.git" ]; then
  179. echo "LOCK FAIL: $kind $name — missing or not a git checkout (empty-stub r18 failure mode)" >&2
  180. fail=1
  181. continue
  182. fi
  183. got=$(git -C "$d" rev-parse HEAD 2>/dev/null) || got="<no HEAD>"
  184. if [ "$got" != "$want" ]; then
  185. echo "LOCK FAIL: $kind $name @ ${got:0:12} != locked ${want:0:12}" >&2
  186. fail=1
  187. continue
  188. fi
  189. if [ -n "$(git -C "$d" status --porcelain 2>/dev/null)" ]; then
  190. echo "LOCK FAIL: $kind $name — dirty worktree (expected a clean checkout)" >&2
  191. fail=1
  192. fi
  193. ;;
  194. skip)
  195. d="$DEPS/${name%%/*}"
  196. got=$(git -C "$d" ls-tree -r -t HEAD 2>/dev/null | awk -v p="${name#*/}" '$1=="160000" && $4==p {print $3}')
  197. if [ "$got" != "$want" ]; then
  198. echo "LOCK FAIL: skip $name — gitlink changed (${got:-gone} vs locked ${want:0:12})" >&2
  199. fail=1
  200. fi
  201. ;;
  202. blob)
  203. got=$(blob_hash "$DEPS/$name" 2>/dev/null) || got="<unreadable>"
  204. if [ "$got" != "$want" ]; then
  205. echo "LOCK FAIL: blob $name — content hash $got != locked $want" >&2
  206. fail=1
  207. fi
  208. ;;
  209. *)
  210. echo "LOCK FAIL: unknown line kind '$kind' in $lock" >&2
  211. fail=1
  212. ;;
  213. esac
  214. done < <(grep -v '^#' "$lock" | grep -v '^[[:space:]]*$' || true)
  215. if [ "$fail" != 0 ]; then
  216. echo "aborting: staged deps do not match $lock" >&2
  217. exit 1
  218. fi
  219. echo "[deps] lock verified: staged content matches $lock"
  220. }
  221. case "${1:-}" in
  222. --regen-lock)
  223. stage_all
  224. emit_lock "${2:?usage: fetch-deps.sh --regen-lock <out>}"
  225. ;;
  226. --emit-lock)
  227. emit_lock "${2:?usage: fetch-deps.sh --emit-lock <out>}"
  228. ;;
  229. *)
  230. stage_all
  231. verify_lock "$LOCK"
  232. ;;
  233. esac