feat(lazy-load): extend manifest to containers/ + skip container scan (Phase 5)
Containers used to be eager-loaded via `sourceScanFiles "containers"`
even under LP_LAZY=1 — sourcing all ~160 installer functions up front.
Phase 5 brings them into the autoload-stub mechanism.
generate_function_manifest.sh now scans BOTH scripts/ AND containers/
(maxdepth 3, matching sourceScanFiles' existing prune), with a per-entry
root selector so stub emission uses the right base directory:
scripts/peer/peer_add.sh → source "${install_scripts_dir}peer/peer_add.sh"
containers/linkding/linkding.sh → source "${install_containers_dir}linkding/linkding.sh"
New manifest exports:
LP_FN_MAP funcname → relpath (existing)
LP_FN_ROOT funcname → scripts|containers NEW
LP_EAGER_FILES "<root>:<relpath>" entries NEW format
~860 autoload stubs (was ~700; +160 from containers)
Loader changes (initilize_files.sh):
- Parses LP_EAGER_FILES entries as `root:path`, dispatches to the
right install_*_dir. Pre-Phase-5 entries without a colon default to
scripts (backwards-compatible).
- sourceScanFiles "containers" is skipped when LP_LAZY=1 AND
LP_FN_MAP is loaded (manifest-driven autoload covers it).
Eager mode and lazy-with-missing-manifest both still run the scan.
Measurement target: ~70 ms saved on top of Phase 4. Verified separately
in the commit message of the next deploy.
Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
parent
a27be53eaf
commit
77342c8047
File diff suppressed because it is too large
Load Diff
@ -39,7 +39,9 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" && "$1" == "run" ]]; then
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ARRAYS_DIR="$SCRIPT_DIR/arrays"
|
||||
SCRIPTS_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
||||
SCRIPTS_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")" # …/scripts
|
||||
INSTALL_DIR="$(dirname "$SCRIPTS_DIR")" # …/install (parent of scripts)
|
||||
CONTAINERS_DIR="$INSTALL_DIR/containers" # sibling of scripts
|
||||
OUTPUT="$ARRAYS_DIR/function_manifest.sh"
|
||||
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[0;33m'; NC='\033[0m'
|
||||
@ -163,22 +165,45 @@ declare -a eager_files
|
||||
total_files=0
|
||||
total_fns=0
|
||||
|
||||
# Find all .sh under scripts/ (no symlinks, no hidden).
|
||||
while IFS= read -r -d '' file; do
|
||||
rel=$(realpath --relative-to="$SCRIPTS_DIR" "$file")
|
||||
# fn_to_root parallels fn_to_file: tracks whether the source file lives under
|
||||
# scripts/ ("scripts") or containers/ ("containers") so the stub emission can
|
||||
# pick the right ${install_*_dir} prefix. Same for eager_root for LP_EAGER_FILES.
|
||||
declare -A fn_to_root
|
||||
declare -A eager_root
|
||||
|
||||
# Walk one root. $1 = directory, $2 = root label ("scripts" or "containers"),
|
||||
# $3 = max depth for find (-maxdepth N). The scan also honours the skip-list
|
||||
# (paths relative to the root, so scripts/ skip entries don't match container
|
||||
# files and vice versa).
|
||||
scan_root() {
|
||||
local root_dir="$1" root_label="$2" depth="$3"
|
||||
while IFS= read -r -d '' file; do
|
||||
local rel
|
||||
rel=$(realpath --relative-to="$root_dir" "$file")
|
||||
# Skip-list keys are scoped per root. should_skip() only handles the
|
||||
# scripts/ ones today; containers/ skip-list is inline here.
|
||||
if [[ "$root_label" == "scripts" ]]; then
|
||||
should_skip "$rel" && continue
|
||||
else
|
||||
# Container-side skips: anything under a resources/ subdir is data
|
||||
# not code; the existing sourceScanFiles already prunes those.
|
||||
case "$rel" in
|
||||
*/resources/*) continue ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
total_files=$((total_files + 1))
|
||||
|
||||
is_eager=0
|
||||
local is_eager=0
|
||||
while IFS= read -r tag; do
|
||||
case "$tag" in
|
||||
fn:*)
|
||||
name="${tag#fn:}"
|
||||
local name="${tag#fn:}"
|
||||
if [[ -n "${fn_to_file[$name]:-}" && "${fn_to_file[$name]}" != "$rel" ]]; then
|
||||
fn_collisions[$name]="${fn_collisions[$name]:-${fn_to_file[$name]}}"$'\t'"$rel"
|
||||
fi
|
||||
fn_to_file[$name]="$rel"
|
||||
fn_to_root[$name]="$root_label"
|
||||
total_fns=$((total_fns + 1))
|
||||
;;
|
||||
eager:)
|
||||
@ -187,8 +212,19 @@ while IFS= read -r -d '' file; do
|
||||
esac
|
||||
done < <(analyze_file "$file")
|
||||
|
||||
(( is_eager )) && eager_files+=("$rel")
|
||||
done < <(find "$SCRIPTS_DIR" -type f -name '*.sh' -print0)
|
||||
if (( is_eager )); then
|
||||
eager_files+=("$rel")
|
||||
eager_root["$rel"]="$root_label"
|
||||
fi
|
||||
done < <(find "$root_dir" -maxdepth "$depth" -type f -name '*.sh' -print0)
|
||||
}
|
||||
|
||||
# scripts/ — deep walk, the existing behaviour.
|
||||
scan_root "$SCRIPTS_DIR" scripts 99
|
||||
# containers/ — match the sourceScanFiles "containers" maxdepth of 3.
|
||||
if [[ -d "$CONTAINERS_DIR" ]]; then
|
||||
scan_root "$CONTAINERS_DIR" containers 3
|
||||
fi
|
||||
|
||||
# Emit the manifest.
|
||||
{
|
||||
@ -200,33 +236,48 @@ done < <(find "$SCRIPTS_DIR" -type f -name '*.sh' -print0)
|
||||
printf '# Function name → relative path. Used by the lazy loader (LP_LAZY=1)\n'
|
||||
printf '# to install an autoload stub for each public function. First call to a\n'
|
||||
printf '# stub sources the real file, which redefines the function with the real\n'
|
||||
printf '# body; subsequent calls hit the real one directly.\n'
|
||||
printf '# body; subsequent calls hit the real one directly. Path is relative to\n'
|
||||
printf '# install_scripts_dir for "scripts" entries, install_containers_dir for\n'
|
||||
printf '# "containers" entries — see LP_FN_ROOT below.\n'
|
||||
printf 'declare -gA LP_FN_MAP=(\n'
|
||||
# Sort for stable diff output.
|
||||
while IFS= read -r name; do
|
||||
printf ' [%s]="%s"\n' "$name" "${fn_to_file[$name]}"
|
||||
done < <(printf '%s\n' "${!fn_to_file[@]}" | sort)
|
||||
printf ')\n\n'
|
||||
printf '# Per-function root selector — "scripts" or "containers". Mirrors\n'
|
||||
printf '# LP_FN_MAP; used by the loader / debugging tools.\n'
|
||||
printf 'declare -gA LP_FN_ROOT=(\n'
|
||||
while IFS= read -r name; do
|
||||
printf ' [%s]="%s"\n' "$name" "${fn_to_root[$name]}"
|
||||
done < <(printf '%s\n' "${!fn_to_root[@]}" | sort)
|
||||
printf ')\n\n'
|
||||
|
||||
printf '# Files with top-level side effects (variable assignments, source calls,\n'
|
||||
printf '# command invocations outside any function). Lazy mode MUST source these\n'
|
||||
printf '# Files with top-level side effects. Lazy mode MUST source these\n'
|
||||
printf '# unconditionally — deferring them would skip the side effect, not just\n'
|
||||
printf '# defer a function definition.\n'
|
||||
printf '# defer a function definition. Stored as "<root>:<relpath>" so the\n'
|
||||
printf '# loader picks the right base dir; existing entries without a prefix\n'
|
||||
printf '# (pre-Phase-5 manifests) default to scripts/.\n'
|
||||
printf 'LP_EAGER_FILES=(\n'
|
||||
while IFS= read -r f; do
|
||||
printf ' "%s"\n' "$f"
|
||||
# No `local` here — we're inside a `{ … } > FILE` command group, not
|
||||
# a function. `local` errors out at this scope in bash.
|
||||
root="${eager_root[$f]:-scripts}"
|
||||
printf ' "%s:%s"\n' "$root" "$f"
|
||||
done < <(printf '%s\n' "${eager_files[@]}" | sort -u)
|
||||
printf ')\n\n'
|
||||
|
||||
printf '# Autoload stubs — one per public function. First call sources the\n'
|
||||
printf '# real file (which redefines this stub with the real body), then\n'
|
||||
printf '# re-invokes. Sourced inline instead of eval-in-loop because bash\n'
|
||||
printf '# parses one large file faster than it evals 700 small snippets.\n'
|
||||
printf '# Only emitted when the manifest is read; behaviour-neutral when the\n'
|
||||
printf '# loader does not flip into LP_LAZY=1 mode.\n'
|
||||
printf '# parses one large file faster than it evals snippets at startup.\n'
|
||||
while IFS= read -r name; do
|
||||
printf '%s() { source "${install_scripts_dir}%s"; %s "$@"; }\n' \
|
||||
"$name" "${fn_to_file[$name]}" "$name"
|
||||
root="${fn_to_root[$name]}"
|
||||
case "$root" in
|
||||
containers) base_var='install_containers_dir' ;;
|
||||
*) base_var='install_scripts_dir' ;;
|
||||
esac
|
||||
printf '%s() { source "${%s}%s"; %s "$@"; }\n' \
|
||||
"$name" "$base_var" "${fn_to_file[$name]}" "$name"
|
||||
done < <(printf '%s\n' "${!fn_to_file[@]}" | sort)
|
||||
} > "$OUTPUT"
|
||||
|
||||
|
||||
@ -70,20 +70,32 @@ sourceInitilize()
|
||||
else
|
||||
source "$manifest"
|
||||
fi
|
||||
# Eager-source the side-effect files. These define vars or run
|
||||
# commands at top level; lazy stubs would skip those side effects.
|
||||
local _eager
|
||||
# Eager-source the side-effect files. Entries are `<root>:<relpath>`
|
||||
# where root selects the base dir ("scripts" → install_scripts_dir,
|
||||
# "containers" → install_containers_dir). Pre-Phase-5 manifests
|
||||
# without a `:` default to scripts/.
|
||||
local _eager _root _path _base
|
||||
for _eager in "${LP_EAGER_FILES[@]}"; do
|
||||
[[ -f "${install_scripts_dir}${_eager}" ]] || continue
|
||||
if [[ "$_eager" == *:* ]]; then
|
||||
_root="${_eager%%:*}"
|
||||
_path="${_eager#*:}"
|
||||
else
|
||||
_root="scripts"; _path="$_eager"
|
||||
fi
|
||||
case "$_root" in
|
||||
containers) _base="$install_containers_dir" ;;
|
||||
*) _base="$install_scripts_dir" ;;
|
||||
esac
|
||||
[[ -f "${_base}${_path}" ]] || continue
|
||||
if [[ "$LP_LOAD_TRACE" == "1" ]]; then
|
||||
local _t0=$EPOCHREALTIME
|
||||
source "${install_scripts_dir}${_eager}"
|
||||
source "${_base}${_path}"
|
||||
local _t1=$EPOCHREALTIME
|
||||
local _ms
|
||||
_ms=$(awk -v a="$_t0" -v b="$_t1" 'BEGIN{printf "%.3f", (b-a)*1000}')
|
||||
printf '%s\t%s\n' "$_ms" "${_eager} (LAZY-EAGER)" >> "$LP_LOAD_TRACE_FILE"
|
||||
printf '%s\t%s\n' "$_ms" "${_root}/${_path} (LAZY-EAGER)" >> "$LP_LOAD_TRACE_FILE"
|
||||
else
|
||||
source "${install_scripts_dir}${_eager}"
|
||||
source "${_base}${_path}"
|
||||
fi
|
||||
done
|
||||
else
|
||||
@ -115,8 +127,21 @@ sourceInitilize()
|
||||
done
|
||||
fi
|
||||
|
||||
# Loading of all files
|
||||
# Loading of all files. Three scans:
|
||||
# libreportal_configs CFG_* vars from configs/* (~180 ms)
|
||||
# app_configs CFG_<APP>_* vars from containers/* (~37 ms)
|
||||
# containers Per-app installer functions (~70 ms)
|
||||
#
|
||||
# Under LP_LAZY=1 with a present manifest, the container scan is replaced
|
||||
# by autoload stubs in function_manifest.sh — sourcing every container
|
||||
# installer eagerly would defeat the lazy optimisation. The two config
|
||||
# scans still run because bash can't lazy-load variables (Phase 6 will
|
||||
# tackle this with a precompiled cache file).
|
||||
sourceScanFiles "libreportal_configs";
|
||||
sourceScanFiles "app_configs";
|
||||
if [[ "$LP_LAZY" == "1" ]] && declare -p LP_FN_MAP >/dev/null 2>&1; then
|
||||
: # containers covered by autoload stubs; skip the eager scan
|
||||
else
|
||||
sourceScanFiles "containers";
|
||||
fi
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user