Ship one preset cache per vendor in place of the profile JSONs

Each vendor's system presets serialize into a single <vendor>.opc built at
package time, and a shipped build carries that file alone — the profile JSON
and its sub-file tree are pruned. The vendor loader, the setup wizard's profile
list and the resource installer all read a vendor through its cache, falling
back to parsing whenever one is absent, stale or unreadable, so the cache stays
an optimization and never a source of truth. Caches hold presets in source form
and resolve inheritance at load, through the same code the JSON path uses.
This commit is contained in:
SoftFever
2026-08-05 21:16:41 +08:00
parent 55fb4703c9
commit 8c050894db
26 changed files with 2675 additions and 1372 deletions

View File

@@ -0,0 +1,122 @@
@echo off
rem Build the per-vendor system preset caches (one <vendor>.opc per vendor) by
rem running the generate_system_cache.exe dev tool against a profiles directory,
rem and make every profiles directory named on the command line ship-ready:
rem install the caches into it and delete the preset JSONs they replace, so a
rem build ships one copy of its presets instead of two.
rem
rem scripts\build_preset_cache.bat [build_dir] [target_dir ...]
rem
rem build_dir defaults to "build"
rem target_dir profiles directories to ship into. Caches are generated into
rem the source tree's resources\profiles, which is what every
rem packaging step copies from; a target may be that same
rem directory, which then only gets pruned.
rem
rem Shipping deletes, so it is a CI packaging step. A vendor's own <vendor>.json
rem goes along with its preset JSONs: the cache carries the vendor profile and
rem the version it was built at, so discovery, version checks and installing all
rem read it there. Only a vendor that has a cache is pruned, so non-vendor JSONs
rem (blacklist.json) are left alone, as are the vendor directories themselves -
rem thumbnails, covers and bed models still live there.
rem
rem set CONFIG=<cfg> to pin the build config for multi-config generators
rem (default: the config of the tool already in the build tree, else Release)
setlocal enabledelayedexpansion
set "REPO_ROOT=%~dp0.."
set "BUILD_DIR=%~1"
if "%BUILD_DIR%"=="" set "BUILD_DIR=build"
if not exist "%BUILD_DIR%\" (
echo ERROR: build tree not found: %BUILD_DIR% 1>&2
exit /b 1
)
if not "%~1"=="" shift
rem Newest match wins: a stale binary silently produces a stale cache layout.
call :find_tool
if not defined CONFIG (
for %%c in (Debug Release RelWithDebInfo MinSizeRel) do (
echo !TOOL! | findstr /i "\\%%c\\" >nul && set "CONFIG=%%c"
)
)
if not defined CONFIG set "CONFIG=Release"
echo Building generate_system_cache in %BUILD_DIR% (%CONFIG%)
cmake --build "%BUILD_DIR%" --config %CONFIG% --target generate_system_cache
if errorlevel 1 (
echo ERROR: could not build generate_system_cache - configure the build tree with -DORCA_TOOLS=ON: 1>&2
echo cmake -S "%REPO_ROOT%" -B "%BUILD_DIR%" -DORCA_TOOLS=ON 1>&2
exit /b 1
)
call :find_tool
if not defined TOOL (
echo ERROR: generate_system_cache.exe not found under %BUILD_DIR% - build with -DORCA_TOOLS=ON 1>&2
exit /b 1
)
set "PROFILES=%REPO_ROOT%\resources\profiles"
if not exist "%PROFILES%\" (
echo ERROR: profiles directory not found: %PROFILES% 1>&2
exit /b 1
)
for %%d in ("%PROFILES%") do set "PROFILES=%%~fd"
rem Add the slicer's runtime DLL directory to PATH so generate_system_cache.exe
rem can resolve its dependencies (TKernel.dll etc.) without a full install step.
set "DLL_DIR="
for /f "delims=" %%f in ('dir /s /b "%BUILD_DIR%\TKernel.dll" 2^>nul') do (
if not defined DLL_DIR set "DLL_DIR=%%~dpf"
)
if defined DLL_DIR set "PATH=%DLL_DIR%;%PATH%"
echo Generating per-vendor preset caches in %PROFILES%
rem Start clean so vendors that went away - and caches written by older tool
rem versions - don't linger next to the freshly generated ones.
del /q "%PROFILES%\*.opc" 2>nul
del /q "%PROFILES%\*.cache" 2>nul
"%TOOL%" --path "%PROFILES%" --log_level 2
if errorlevel 1 exit /b %errorlevel%
:next_target
if "%~1"=="" exit /b 0
call :ship "%~1"
if errorlevel 1 exit /b 1
shift
goto :next_target
:ship
set "TARGET=%~1"
if not exist "%TARGET%\" (
echo ERROR: profiles directory not found: %TARGET% 1>&2
exit /b 1
)
for %%d in ("%TARGET%") do set "TARGET=%%~fd"
if /i not "%TARGET%"=="%PROFILES%" copy /y "%PROFILES%\*.opc" "%TARGET%\" >nul
set /a SHIPPED=0
set /a PRUNED=0
for %%c in ("%PROFILES%\*.opc") do (
set /a SHIPPED+=1
set "VENDOR=%%~nc"
if exist "%TARGET%\!VENDOR!.json" (
del /q "%TARGET%\!VENDOR!.json"
set /a PRUNED+=1
)
if exist "%TARGET%\!VENDOR!\" (
for /f %%n in ('dir /s /b "%TARGET%\!VENDOR!\*.json" 2^>nul ^| find /c /v ""') do set /a PRUNED+=%%n
del /s /q "%TARGET%\!VENDOR!\*.json" >nul 2>&1
rem Deepest first, so a directory the delete above emptied goes too; rd
rem refuses the ones still holding covers or meshes.
for /f "delims=" %%d in ('dir /s /b /ad "%TARGET%\!VENDOR!" 2^>nul ^| sort /r') do rd "%%d" 2>nul
)
)
echo %TARGET%: !SHIPPED! caches, dropped !PRUNED! preset JSONs
exit /b 0
:find_tool
set "TOOL="
for /f "delims=" %%f in ('dir /s /b /o-d "%BUILD_DIR%\generate_system_cache.exe" 2^>nul') do (
if not defined TOOL set "TOOL=%%f"
)
exit /b 0

143
scripts/build_preset_cache.sh Executable file
View File

@@ -0,0 +1,143 @@
#!/usr/bin/env bash
# Build the per-vendor system preset caches (one <vendor>.opc per vendor) by
# running the generate_system_cache dev tool against a profiles directory, and
# make every profiles directory named on the command line ship-ready: install
# the caches into it and delete the preset JSONs they replace, so a build ships
# one copy of its presets instead of two.
#
# ./scripts/build_preset_cache.sh # caches into resources/profiles
# ./scripts/build_preset_cache.sh -b build/arm64 # search this build tree for the tool
# ./scripts/build_preset_cache.sh <dir> [<dir> ...] # and ship into these profiles dirs
#
# Caches are generated into the source tree's resources/profiles, which is what
# every packaging step copies from. Shipping deletes, so it is a CI packaging
# step: pass packaged output directories, or the checkout of a build that is
# about to be packaged from it.
#
# A vendor's own <vendor>.json goes along with its preset JSONs: the cache
# carries the vendor profile and the version it was built at, so discovery,
# version checks and installing all read it there. A shipped vendor is its cache
# and nothing else. Only a vendor that has a cache is pruned, so an ungenerated
# vendor keeps its JSONs and is simply parsed at startup; non-vendor JSONs
# (blacklist.json) are left alone, as are the vendor directories themselves —
# thumbnails, covers and bed models still live there.
#
# -b <dir> build tree holding the tool
# (default: build/arm64, build/x86_64, or build — first that exists)
# -p <dir> profiles directory to generate caches into
# (default: <repo>/resources/profiles)
# -c <cfg> build config for multi-config generators
# (default: the config of the tool already in the build tree, else
# the build tree's CMAKE_BUILD_TYPE)
# -n skip the rebuild and run the tool already in the build tree
# -l <level> tool log level (default: 2)
set -euo pipefail
repo_root="$(cd "$(dirname "$0")/.." && pwd -P)"
build_dir=""
profiles_dir=""
config=""
build_tool=1
log_level=2
while getopts "b:p:c:l:nh" opt; do
case $opt in
b) build_dir="$OPTARG" ;;
p) profiles_dir="$OPTARG" ;;
c) config="$OPTARG" ;;
n) build_tool=0 ;;
l) log_level="$OPTARG" ;;
h) sed -n '2,${/^#/!q;p;}' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
*) exit 1 ;;
esac
done
shift $((OPTIND - 1))
if [ -z "$build_dir" ]; then
for candidate in "$repo_root/build/arm64" "$repo_root/build/x86_64" "$repo_root/build"; do
if [ -d "$candidate" ]; then build_dir="$candidate"; break; fi
done
fi
if [ -z "$build_dir" ] || [ ! -d "$build_dir" ]; then
echo "ERROR: build tree not found (pass -b <build_dir>)" >&2
exit 1
fi
# Newest match wins: multi-config trees keep one binary per config, and a stale
# one silently produces a stale cache layout.
find_tool() {
local best="" f
while IFS= read -r f; do
[ -n "$f" ] || continue
if [ -z "$best" ] || [ "$f" -nt "$best" ]; then best="$f"; fi
done < <(find "$build_dir" -name generate_system_cache -type f 2>/dev/null)
printf '%s' "$best"
}
tool=$(find_tool)
if [ -z "$config" ]; then
case "$tool" in
*/Debug/*) config=Debug ;;
*/Release/*) config=Release ;;
*/RelWithDebInfo/*) config=RelWithDebInfo ;;
*/MinSizeRel/*) config=MinSizeRel ;;
*) config=$(sed -n 's/^CMAKE_BUILD_TYPE:[A-Z]*=\(.\+\)$/\1/p' "$build_dir/CMakeCache.txt" 2>/dev/null | head -1 || true) ;;
esac
fi
if [ "$build_tool" = 1 ]; then
echo "Building generate_system_cache in $build_dir${config:+ ($config)}"
build_args=(--build "$build_dir" --target generate_system_cache)
if [ -n "$config" ]; then build_args+=(--config "$config"); fi
if ! cmake "${build_args[@]}"; then
echo "ERROR: could not build generate_system_cache — configure the build tree with -DORCA_TOOLS=ON:" >&2
echo " cmake -S \"$repo_root\" -B \"$build_dir\" -DORCA_TOOLS=ON" >&2
exit 1
fi
tool=$(find_tool)
fi
if [ -z "$tool" ]; then
echo "ERROR: generate_system_cache not found under $build_dir — build with -DORCA_TOOLS=ON" >&2
exit 1
fi
if [ -z "$profiles_dir" ]; then profiles_dir="$repo_root/resources/profiles"; fi
if [ ! -d "$profiles_dir" ]; then
echo "ERROR: profiles directory not found: $profiles_dir" >&2
exit 1
fi
profiles_dir=$(cd "$profiles_dir" && pwd -P)
# Start clean so vendors that went away — and caches written by older tool
# versions — don't linger next to the freshly generated ones.
echo "Generating per-vendor preset caches in $profiles_dir"
rm -f "$profiles_dir"/*.opc "$profiles_dir"/*.cache
"$tool" --path "$profiles_dir" --log_level "$log_level"
for target in "$@"; do
resolved=$(cd "$target" 2>/dev/null && pwd -P) || {
echo "ERROR: profiles directory not found: $target" >&2
exit 1
}
if [ "$resolved" != "$profiles_dir" ]; then
cp "$profiles_dir"/*.opc "$resolved"/
fi
pruned=0
shipped=0
for cache in "$profiles_dir"/*.opc; do
vendor=$(basename "$cache" .opc)
shipped=$(( shipped + 1 ))
if [ -f "$resolved/$vendor.json" ]; then
rm -f "$resolved/$vendor.json"
pruned=$(( pruned + 1 ))
fi
[ -d "$resolved/$vendor" ] || continue
n=$(find "$resolved/$vendor" -name '*.json' | wc -l)
find "$resolved/$vendor" -name '*.json' -delete
find "$resolved/$vendor" -type d -empty -delete
pruned=$(( pruned + n ))
done
echo "$resolved: $shipped caches, dropped $pruned preset JSONs"
done