fetch-deps.sh 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. #!/usr/bin/env bash
  2. # Fetch every vLLM build sub-dependency for the image at the pins the
  3. # cloned 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. # Pins verified against the r39 tree (9e5d1793, 2026-09-21): the lab moved
  52. # every external pin in one cut, DeepGEMM to its vllm-project fork, cutlass
  53. # v4.4.2 -> v4.7.1, and added DeepSelect. See profiles/r39.env.
  54. gate CMakeLists.txt:nvidia/cutlass.git \
  55. CMakeLists.txt:v4.7.1 \
  56. deepgemm.cmake:vllm-project/DeepGEMM \
  57. deepgemm.cmake:ad1f1726aa540a76c1d26d6a120effb8de21eaa4 \
  58. deepselect.cmake:vllm-project/DeepSelect \
  59. deepselect.cmake:d96d33afe1fab0d6066da49cdc91e64c2bee65ea \
  60. flashkda.cmake:b59532f1f464fbd536272780e30df5bf6a2ccc02 \
  61. flashmla.cmake:0eee43b12f034b657133cf2afca6a72ebb6efccf \
  62. fmha_sm100.cmake:f355c37eb4e1413f21ee2ad8bbad25079e6bef9d \
  63. qutlass.cmake:e74319e3405ce6d71965732880f5dc1f52371f64 \
  64. tml_fa4.cmake:75765e76a9c2c012c1f6ecd64577eb646eb4d303 \
  65. vllm_flash_attn.cmake:506341a143fcabd4bb79052a7605ada727d6b3f5 \
  66. vllm_flash_attn.cmake:vllm-project/flash-attention \
  67. triton_kernels.cmake:triton-lang/triton \
  68. triton_kernels.cmake:v3.5.1
  69. }
  70. # ---- 2. Clone deps at the tree's pins ----
  71. stage_clones() {
  72. mkdir -p "$DEPS"
  73. fetch https://github.com/nvidia/cutlass.git v4.7.1 "$DEPS/cutlass"
  74. fetch https://github.com/vllm-project/flash-attention.git \
  75. 506341a143fcabd4bb79052a7605ada727d6b3f5 "$DEPS/flash-attention"
  76. fetch https://github.com/vllm-project/MSA.git \
  77. f355c37eb4e1413f21ee2ad8bbad25079e6bef9d "$DEPS/MSA"
  78. fetch https://github.com/vllm-project/FlashMLA.git \
  79. 0eee43b12f034b657133cf2afca6a72ebb6efccf "$DEPS/FlashMLA"
  80. fetch https://github.com/vllm-project/FlashKDA.git \
  81. b59532f1f464fbd536272780e30df5bf6a2ccc02 "$DEPS/FlashKDA"
  82. fetch https://github.com/IST-DASLab/qutlass.git \
  83. e74319e3405ce6d71965732880f5dc1f52371f64 "$DEPS/qutlass"
  84. fetch https://github.com/vllm-project/tml-fa4.git \
  85. 75765e76a9c2c012c1f6ecd64577eb646eb4d303 "$DEPS/tml-fa4"
  86. fetch https://github.com/vllm-project/DeepSelect.git \
  87. d96d33afe1fab0d6066da49cdc91e64c2bee65ea "$DEPS/DeepSelect"
  88. fetch https://github.com/triton-lang/triton.git v3.5.1 "$DEPS/triton"
  89. }
  90. # ---- 3. Sub-module populations the tree's install steps require ----
  91. # Several deps vendor cutlass via a 160000 gitlink and their cmake install
  92. # steps require the tree to EXIST (r18 lesson: an empty stub dies at cmake
  93. # INSTALL — and r27's FlashMLA addendum died at compile). Generic rule: walk
  94. # every dep's tree recursively and populate each gitlink at its pinned SHA.
  95. # AMD-only trees (composable_kernel, aiter) are skipped explicitly — the
  96. # CUDA cmake path never references them. All gitlinks so far vendor
  97. # nvidia/cutlass; anything else aborts loudly, keeping the contract explicit.
  98. stage_gitlinks() {
  99. local d path sha
  100. for d in flash-attention MSA FlashMLA FlashKDA qutlass tml-fa4 DeepSelect; do
  101. while IFS=$'\t' read -r path sha; do
  102. [ -n "${path:-}" ] || continue
  103. case "$path" in
  104. */composable_kernel|composable_kernel|*/aiter|aiter)
  105. echo "[deps] skip AMD-only gitlink $path (CUDA path unused)"; continue ;;
  106. esac
  107. case "$(basename "$path")" in
  108. cutlass) ;; # expected vendor
  109. *) echo "ABORT: unhandled gitlink '$path' in $d (populate explicitly)" >&2; exit 1 ;;
  110. esac
  111. # a gitlink path EXISTS in the worktree as an EMPTY dir (git's
  112. # clone behavior) — the gate is "did WE populate it", .git marker.
  113. # Skipping on plain -d reproduces the r18 empty-stub failure.
  114. if [ ! -e "$DEPS/$d/$path/.git" ]; then
  115. fetch https://github.com/nvidia/cutlass.git "$sha" "$DEPS/$d/$path"
  116. fi
  117. done < <(git -C "$DEPS/$d" ls-tree -r -t HEAD | awk '$1=="160000" {print $4"\t"$3}')
  118. done
  119. echo "[deps] gitlink populations verified"
  120. }
  121. # ---- 4. Triton into the layout the Dockerfile/build expects ----
  122. stage_triton_layout() {
  123. mv "$DEPS/triton/python/triton_kernels/triton_kernels" "$DEPS/triton_kernels"
  124. rm -rf "$DEPS/triton"
  125. }
  126. stage_all() {
  127. stage_pins
  128. stage_clones
  129. stage_gitlinks
  130. stage_triton_layout
  131. # ---- 5. Manifest ----
  132. echo "[deps] staged:"
  133. local d
  134. for d in cutlass flash-attention MSA FlashMLA FlashKDA qutlass tml-fa4 DeepSelect triton_kernels; do
  135. printf ' %-16s %s\n' "$d" "$(git -C "$DEPS/$d" rev-parse HEAD 2>/dev/null | cut -c1-12)"
  136. done
  137. }
  138. # ---------------------------------------------------------------- lock ----
  139. # sha256 over the sorted file list (relpath + per-file sha256) — the
  140. # file-level fingerprint for non-git content (git checkouts fingerprint via
  141. # commit + clean worktree instead: git objects already hash every byte).
  142. blob_hash() {
  143. (cd "$1" && find . -type f | sort | while IFS= read -r f; do sha256sum "$f"; done | sha256sum | cut -d' ' -f1)
  144. }
  145. emit_lock() { # out — write the lock from the staged $DEPS state
  146. local out="$1" d path sha h
  147. {
  148. echo "# sm121 deps.lock — staged-content fingerprints for fetch-deps.sh"
  149. echo "# generated: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
  150. echo "# format: dep <name> <commit> | gitlink <parent>/<path> <commit>"
  151. echo "# | skip <parent>/<path> <commit> | blob <name> <sha256>"
  152. echo "# regenerate: bash fetch-deps.sh --regen-lock <out> (in-build: --build-arg REGEN_LOCK=1)"
  153. for d in cutlass flash-attention MSA FlashMLA FlashKDA qutlass tml-fa4 DeepSelect; do
  154. [ -e "$DEPS/$d/.git" ] || { echo "emit_lock: $d not staged" >&2; exit 1; }
  155. echo "dep $d $(git -C "$DEPS/$d" rev-parse HEAD)"
  156. done
  157. for d in flash-attention MSA FlashMLA FlashKDA qutlass tml-fa4 DeepSelect; do
  158. while IFS=$'\t' read -r path sha; do
  159. [ -n "${path:-}" ] || continue
  160. case "$path" in
  161. */composable_kernel|composable_kernel|*/aiter|aiter)
  162. echo "skip $d/$path $sha" ;;
  163. *)
  164. echo "gitlink $d/$path $sha" ;;
  165. esac
  166. done < <(git -C "$DEPS/$d" ls-tree -r -t HEAD | awk '$1=="160000" {print $4"\t"$3}')
  167. done
  168. [ -d "$DEPS/triton_kernels" ] || { echo "emit_lock: triton_kernels missing (run stage_triton_layout first)" >&2; exit 1; }
  169. h=$(blob_hash "$DEPS/triton_kernels")
  170. echo "blob triton_kernels $h"
  171. } > "$out"
  172. echo "[deps] lock written: $out"
  173. }
  174. verify_lock() { # lock — abort the build if staged content != lock
  175. local lock="$1"
  176. if [ ! -f "$lock" ]; then
  177. echo "[deps] no lock at $lock — nothing to verify against (staging unverified)"
  178. return 0
  179. fi
  180. local kind name want got d fail=0
  181. while read -r kind name want; do
  182. case "$kind" in
  183. dep | gitlink)
  184. d="$DEPS/$name"
  185. if [ ! -e "$d/.git" ]; then
  186. echo "LOCK FAIL: $kind $name — missing or not a git checkout (empty-stub r18 failure mode)" >&2
  187. fail=1
  188. continue
  189. fi
  190. got=$(git -C "$d" rev-parse HEAD 2>/dev/null) || got="<no HEAD>"
  191. if [ "$got" != "$want" ]; then
  192. echo "LOCK FAIL: $kind $name @ ${got:0:12} != locked ${want:0:12}" >&2
  193. fail=1
  194. continue
  195. fi
  196. if [ -n "$(git -C "$d" status --porcelain 2>/dev/null)" ]; then
  197. echo "LOCK FAIL: $kind $name — dirty worktree (expected a clean checkout)" >&2
  198. fail=1
  199. fi
  200. ;;
  201. skip)
  202. d="$DEPS/${name%%/*}"
  203. got=$(git -C "$d" ls-tree -r -t HEAD 2>/dev/null | awk -v p="${name#*/}" '$1=="160000" && $4==p {print $3}')
  204. if [ "$got" != "$want" ]; then
  205. echo "LOCK FAIL: skip $name — gitlink changed (${got:-gone} vs locked ${want:0:12})" >&2
  206. fail=1
  207. fi
  208. ;;
  209. blob)
  210. got=$(blob_hash "$DEPS/$name" 2>/dev/null) || got="<unreadable>"
  211. if [ "$got" != "$want" ]; then
  212. echo "LOCK FAIL: blob $name — content hash $got != locked $want" >&2
  213. fail=1
  214. fi
  215. ;;
  216. *)
  217. echo "LOCK FAIL: unknown line kind '$kind' in $lock" >&2
  218. fail=1
  219. ;;
  220. esac
  221. done < <(grep -v '^#' "$lock" | grep -v '^[[:space:]]*$' || true)
  222. if [ "$fail" != 0 ]; then
  223. echo "aborting: staged deps do not match $lock" >&2
  224. exit 1
  225. fi
  226. echo "[deps] lock verified: staged content matches $lock"
  227. }
  228. case "${1:-}" in
  229. --regen-lock)
  230. stage_all
  231. emit_lock "${2:?usage: fetch-deps.sh --regen-lock <out>}"
  232. ;;
  233. --emit-lock)
  234. emit_lock "${2:?usage: fetch-deps.sh --emit-lock <out>}"
  235. ;;
  236. *)
  237. stage_all
  238. verify_lock "$LOCK"
  239. ;;
  240. esac