Commit 3057bb66c for llama.cpp

commit 3057bb66c86c46d5781e50e85462a760ba7d1feb
Author: Adrien Gallouët <angt@huggingface.co>
Date:   Sat Sep 12 16:09:46 2026 +0200

    ui : add cache (#28802)

    Signed-off-by: Adrien Gallouët <angt@huggingface.co>

diff --git a/scripts/ui-assets.cmake b/scripts/ui-assets.cmake
index 402f95bd4..dec34ebda 100644
--- a/scripts/ui-assets.cmake
+++ b/scripts/ui-assets.cmake
@@ -21,6 +21,7 @@ set(DIST_DIR     "${UI_BINARY_DIR}/dist")
 set(SRC_DIST_DIR "${UI_SOURCE_DIR}/dist")
 set(WORK_DIR     "${UI_BINARY_DIR}/ui-src")
 set(STAMP_FILE   "${UI_BINARY_DIR}/.ui-stamp")
+set(EMBED_STAMP  "${UI_BINARY_DIR}/.ui-embed.sha256")
 set(UI_CPP       "${UI_BINARY_DIR}/ui.cpp")
 set(UI_H         "${UI_BINARY_DIR}/ui.h")

@@ -141,9 +142,59 @@ function(ui_validate_assets files in_dir)
 endfunction()

 # Generate ui.cpp/ui.h embedding every file of ${dist_dir} (empty table when
-# it has no index.html). When LLAMA_UI_GZIP is enabled, assets are compressed
-# first and served pre-gzipped (llama_ui_use_gzip()).
+# it has no index.html), gzip-compressed when LLAMA_UI_GZIP is enabled.
 function(emit_files dist_dir)
+    set(UI_TEMPLATE_DIR "${LLAMA_SOURCE_DIR}/tools/ui")
+
+    # Collect the asset list once and reuse it for the fingerprint,
+    # validation, compression and embedding.
+    set(assets "")
+    if(EXISTS "${dist_dir}/index.html")
+        file(GLOB_RECURSE assets
+            LIST_DIRECTORIES false
+            RELATIVE "${dist_dir}"
+            "${dist_dir}/*")
+        list(FILTER assets EXCLUDE REGEX "^_gzip/")
+        list(SORT assets)
+    endif()
+
+    if(LLAMA_UI_GZIP AND NOT DEFINED ENV{SOURCE_DATE_EPOCH})
+        # Zero the gzip header timestamp so identical inputs give identical
+        # bytes (and therefore stable ETags) on every machine.
+        set(ENV{SOURCE_DATE_EPOCH} 0)
+    endif()
+
+    # Fingerprint of every input that determines ui.cpp/ui.h: compression
+    # settings, the asset tree (names + SHA-256) and this script + templates.
+    set(fp "${LLAMA_UI_GZIP}|$ENV{SOURCE_DATE_EPOCH}|${CMAKE_VERSION}\n")
+    foreach(f ${assets})
+        file(SHA256 "${dist_dir}/${f}" h)
+        string(APPEND fp "${f} ${h}\n")
+    endforeach()
+    foreach(g
+            "${CMAKE_CURRENT_FUNCTION_LIST_FILE}"
+            "${UI_TEMPLATE_DIR}/ui.h.in"
+            "${UI_TEMPLATE_DIR}/ui.cpp.in")
+        file(SHA256 "${g}" h)
+        string(APPEND fp "gen ${h}\n")
+    endforeach()
+    string(SHA256 fingerprint "${fp}")
+
+    if(EXISTS "${EMBED_STAMP}" AND EXISTS "${UI_CPP}" AND EXISTS "${UI_H}")
+        file(READ "${EMBED_STAMP}" fp_saved)
+        string(STRIP "${fp_saved}" fp_saved)
+        if(fp_saved STREQUAL "${fingerprint}")
+            message(STATUS "UI: assets unchanged, skipping embedding")
+            return()
+        endif()
+    endif()
+
+    # Drop the old stamp up front so a crash mid-generation cannot leave
+    # outputs and stamp out of sync.
+    file(REMOVE "${EMBED_STAMP}")
+
+    ui_validate_assets("${assets}" "${dist_dir}")
+
     set(embed_dir "${dist_dir}")
     set(use_gzip FALSE)

@@ -156,21 +207,11 @@ function(emit_files dist_dir)
         endif()
         if(LLAMA_UI_GZIP)
             # Compress every asset into a parallel _gzip/ tree under the build
-            # directory (never write into the source or dist tree); the
-            # structure stays the same: /abc/def --> /_gzip/abc/def.
-            # FORMAT raw produces a bare gzip stream (no archive container)
-            # that can be served with Content-Encoding: gzip. SOURCE_DATE_EPOCH
-            # zeroes the header timestamp so identical inputs give identical
-            # bytes (and therefore stable ETags) on every machine.
-            if(NOT DEFINED ENV{SOURCE_DATE_EPOCH})
-                set(ENV{SOURCE_DATE_EPOCH} 0)
-            endif()
+            # directory, served with Content-Encoding: gzip.
             set(gzip_root "${UI_BINARY_DIR}/ui-gzip")
             set(gzip_dir  "${gzip_root}/_gzip")
             file(REMOVE_RECURSE "${gzip_root}")
-            file(GLOB_RECURSE all_files RELATIVE "${dist_dir}" "${dist_dir}/*")
-            list(FILTER all_files EXCLUDE REGEX "^_gzip/")
-            foreach(f ${all_files})
+            foreach(f IN LISTS assets)
                 get_filename_component(asset_path "${dist_dir}/${f}" REALPATH)
                 get_filename_component(dst_dir "${gzip_dir}/${f}" DIRECTORY)
                 file(MAKE_DIRECTORY "${dst_dir}")
@@ -187,21 +228,10 @@ function(emit_files dist_dir)
         endif()
     endif()

-    set(assets "")
-    if(EXISTS "${embed_dir}/index.html")
-        file(GLOB_RECURSE assets RELATIVE "${embed_dir}" "${embed_dir}/*")
-        list(FILTER assets EXCLUDE REGEX "^_gzip/")
-        list(SORT assets)
-        ui_validate_assets("${assets}" "${embed_dir}")
-    endif()
-
     list(LENGTH assets n_assets)

-    # Only the per-asset data arrays and table rows are built here; all
-    # static C++ lives in the ui.h.in / ui.cpp.in templates. configure_file
-    # rewrites an output only when its contents change, so the library is
-    # not recompiled needlessly. @ONLY keeps ${...} in the content literal;
-    # mime types come from a fixed list.
+    # Per-asset arrays and table rows go into the ui.h.in / ui.cpp.in templates;
+    # configure_file only rewrites on content change, avoiding needless recompiles.
     set(ASSET_ARRAYS "")
     set(ASSET_TABLE "")
     set(idx 0)
@@ -235,9 +265,11 @@ function(emit_files dist_dir)
         set(USE_GZIP true)
     endif()

-    set(UI_TEMPLATE_DIR "${LLAMA_SOURCE_DIR}/tools/ui")
     configure_file("${UI_TEMPLATE_DIR}/ui.h.in"   "${UI_H}"   @ONLY)
     configure_file("${UI_TEMPLATE_DIR}/ui.cpp.in" "${UI_CPP}" @ONLY)
+
+    # Write the embed stamp last, after both generated files succeeded.
+    file(WRITE "${EMBED_STAMP}" "${fingerprint}")
     message(STATUS "UI: embedded ${n_assets} assets")
 endfunction()

@@ -419,16 +451,8 @@ function(hf_download version out_var out_resolved)

         message(STATUS "UI: downloading from ${resolved}: ${base}/dist.tar.gz")

-        file(DOWNLOAD "${base}/dist.tar.gz?download=true" "${archive}"
-            STATUS status TIMEOUT 300 ${auth_headers}
-        )
-        list(GET status 0 rc)
-        if(NOT rc EQUAL 0)
-            list(GET status 1 errmsg)
-            message(STATUS "UI: download dist.tar.gz from ${resolved} failed: ${errmsg}")
-            continue()
-        endif()
-
+        # Fetch the checksum first: when the archive we already have matches
+        # it, the expensive download is skipped and only extraction repeats.
         file(DOWNLOAD "${base}/dist.tar.gz.sha256?download=true" "${archive}.sha256"
             STATUS status TIMEOUT 30 ${auth_headers}
         )
@@ -439,17 +463,44 @@ function(hf_download version out_var out_resolved)
             continue()
         endif()

-        # Validate sha256 checkums
+        # Validate the sha256 checksum: reject anything that is not a full
+        # 64-hex-digit digest before touching the archive.
         file(READ "${archive}.sha256" expected)
         string(REGEX MATCH "^[0-9a-fA-F]+" expected "${expected}")
         string(TOLOWER "${expected}" expected)
-        file(SHA256 "${archive}" actual)
-        if("${expected}" STREQUAL "" OR NOT "${actual}" STREQUAL "${expected}")
-            message(STATUS "UI: checksum mismatch for dist.tar.gz from ${resolved}")
+        string(LENGTH "${expected}" expected_len)
+        if(NOT expected_len EQUAL 64)
+            message(STATUS "UI: invalid checksum from ${resolved}")
             continue()
         endif()

-        # Clear DIST_DIR to remove stale files first
+        set(actual "")
+        if(EXISTS "${archive}")
+            file(SHA256 "${archive}" actual)
+        endif()
+
+        if("${actual}" STREQUAL "${expected}")
+            message(STATUS "UI: local dist.tar.gz matches checksum from ${resolved}, skipping download")
+        else()
+            file(DOWNLOAD "${base}/dist.tar.gz?download=true" "${archive}"
+                STATUS status TIMEOUT 300 ${auth_headers}
+            )
+            list(GET status 0 rc)
+            if(NOT rc EQUAL 0)
+                list(GET status 1 errmsg)
+                message(STATUS "UI: download dist.tar.gz from ${resolved} failed: ${errmsg}")
+                continue()
+            endif()
+
+            file(SHA256 "${archive}" actual)
+            if(NOT "${actual}" STREQUAL "${expected}")
+                message(STATUS "UI: checksum mismatch for dist.tar.gz from ${resolved}")
+                continue()
+            endif()
+        endif()
+
+        # Remove the stamp with the dist tree it describes, together.
+        file(REMOVE "${STAMP_FILE}")
         file(REMOVE_RECURSE "${DIST_DIR}")

         file(ARCHIVE_EXTRACT INPUT "${archive}" DESTINATION "${DIST_DIR}")
@@ -495,27 +546,27 @@ endif()
 if(NOT provisioned AND HF_ENABLED)
     resolve_version(VERSION)

+    # Stamp a successful HF download: records bucket + requested version and
+    # lets later steps distinguish downloaded assets from locally built ones.
+    set(stamp_key "${HF_BUCKET}|${VERSION}")
+
     set(stamp_ok FALSE)
-    if(EXISTS "${STAMP_FILE}" AND NOT "${VERSION}" STREQUAL "")
+    if(EXISTS "${STAMP_FILE}" AND EXISTS "${DIST_DIR}/index.html" AND NOT "${VERSION}" STREQUAL "")
         file(READ "${STAMP_FILE}" stamped)
         string(STRIP "${stamped}" stamped)
-        if("${stamped}" STREQUAL "${VERSION}")
+        if(stamped STREQUAL "${stamp_key}")
             set(stamp_ok TRUE)
         endif()
     endif()

-    set(have_assets FALSE)
-    if(EXISTS "${DIST_DIR}/index.html")
-        set(have_assets TRUE)
-    endif()
-    if(stamp_ok AND have_assets)
-        message(STATUS "UI: HF stamp '${stamped}' matches version, skipping HF fetch")
+    if(stamp_ok)
+        message(STATUS "UI: HF stamp matches '${stamp_key}', skipping HF fetch")
         set(provisioned TRUE)
     else()
         hf_download("${VERSION}" HF_OK HF_RESOLVED)
         if(HF_OK)
-            file(WRITE "${STAMP_FILE}" "${HF_RESOLVED}")
-            message(STATUS "UI: HF download succeeded, stamp updated (${HF_RESOLVED})")
+            file(WRITE "${STAMP_FILE}" "${stamp_key}")
+            message(STATUS "UI: HF download succeeded, stamp updated (${stamp_key}, resolved: ${HF_RESOLVED})")
             set(provisioned TRUE)
         else()
             message(STATUS "UI: HF download failed")