Files
gex/scripts/units-for.sh
velzieandClaude Opus 5.5 a6e7c17f9e Restructure: hosts/, page/, ports/, site/, toolchain/, wine/, scripts/
The tree by what each part is, behind the top-level Makefile:
- shims/ -> hosts/ (the browser, kernel, native and Wayland hosts)
- web/ -> page/ (the page's source, build and serve), ports/mesa,
  ports/xwayland and ports/tint (the ports the page loads), site/ (static
  sites and the eject page)
- tools/gexcc -> toolchain/gexcc, with the sysroot and busybox builds from
  tools/wine-wasm in toolchain/sysroot and toolchain/busybox, and one
  toolchain/pins (musl, the LP64 musl, LLVM, busybox)
- tools/wine-wasm -> wine/: dist.sh (was wine64-dist.sh), wine.pins (64-bit
  Wine, and the 32-bit tree its wineserver builds from), libs.pins (the Unix
  libraries), the 32-bit-only pieces in wine/legacy32 (its own pins and
  legacy.mk, which wine/Makefile includes)
- tools/*.py and tools/*.sh -> scripts/ (the GL generators in scripts/gl)
- wine32/src.sh is scripts/fetch-pinned.sh, which takes the trees the old
  script marked as its own

References follow the moves: paths from the tree's root by the map, paths
from a script's own directory by where their target went.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_e0aa53aa95eb4b86910519b4d14812a5
2026-11-04 17:30:24 -05:00

89 lines
4.6 KiB
Bash
Executable File

#!/bin/bash
# The function-tier units of a frozen set of images, for one gexc: prints the
# cache directory (GEX_CGEN_CACHE) that holds them, building it first when no
# set exists for that gexc's unit-key inputs. Gate legs that run frozen Wine
# and game bundles on units (a game repo's page leg) call it, so a gex
# change that moves every unit key (gex-cgen's sources, gex-ld's through the
# recovery inputs, the ABI) costs one rebuild inside the leg, not a hand step.
#
# scripts/units-for.sh DIR GEXC
#
# DIR holds the frozen inputs:
# units.list one image per line, "FILE BASE": the image (a path relative to
# DIR, or absolute) and the hex base it is mapped at in the runs.
# A hard-linked copy under DIR/images keeps them frozen.
# icall/ optional: an indirect-call profile per image identity (the
# same image at the same base, whatever the gexc), as
# a game's own tooling places it. Each set is built
# with it (devirtualised hot table calls: the "second pass").
# and the sets, DIR/ukeys/ID (ID: `gexc cgen-image --inputs`, a hash of
# everything a unit key takes besides its image), each with its build log at
# DIR/ukeys/ID.log. A set is complete once DIR/ukeys/ID/.complete exists; a
# build holds DIR/ukeys/.lock, so legs sharing DIR wait for one build. The
# three most recently used sets are kept; older ones are removed.
#
# Besides the images, each set holds the i386 trampoline page's unit
# (`gexc cgen-image --nt-trampolines32`).
#
# Stdout: the set's directory. Stderr: one line when a set is built ("units:
# built ...", with the time and the log), or the failure. Exit 1 on failure:
# a GEXC that is not an x86 gexc (no wine_libm provider), an image that does
# not build, or no units.list.
#
# Environment: UNITS_JOBS (default 12) jobs per image; the flavour switches a
# unit build takes (GEX_CGEN_GUARD=1 and so on) are passed through and are
# part of ID.
set -u
D=$(cd "${1:?usage: units-for.sh DIR GEXC}" && pwd)
G=$(realpath "${2:?usage: units-for.sh DIR GEXC}")
fail() { echo "units-for: $*" >&2; exit 1; }
[ -f "$D/units.list" ] || fail "no $D/units.list"
[ -x "$G" ] || fail "no gexc at $G"
K=$("$G" cgen-image --inputs 2>/dev/null) && [ -n "$K" ] || fail "$G cgen-image --inputs gave no id (a gexc older than --inputs?)"
mkdir -p "$D/ukeys"
U=$D/ukeys/$K
exec 9> "$D/ukeys/.lock"
flock 9
if [ ! -f "$U/.complete" ]; then
t0=$(date +%s); log=$D/ukeys/$K.log
rm -rf "$U.new"; mkdir -p "$U.new"
[ -d "$D/icall" ] && cp -al "$D/icall" "$U.new/icall"
run() { GEX_CGEN_CACHE="$U.new" GEX_CGEN_ICALL_COUNT=0 systemd-run --user --scope -q -p TasksMax=4096 -p MemoryMax=32G nice -n 19 "$G" cgen-image "$@"; }
{
echo "== units for inputs $K, gexc $G, $(date -Is)"
run --providers 2>&1
} > "$log"
grep -q '^provider wine_libm:' "$log" || { rm -rf "$U.new"; fail "$G is not an x86 gexc (no wine_libm provider; log $log)"; }
n=0 bad=0 nohints=0 nocode=0
while read -r f b; do
[ -z "$f" ] || [ "${f#\#}" != "$f" ] && continue
case $f in /*) ;; *) f=$D/$f ;; esac
n=$((n + 1))
out=$(run "$f" --pe-base "$b" --jobs "${UNITS_JOBS:-12}" 2>&1); rc=$?
echo "$out" | grep -a "indirect-call hints\|rror\|^\[cgen\] [0-9]\| at 0x" | cut -c1-300 >> "$log"
# An image with no code (resources only: normaliz.dll, tzres.dll) has
# nothing to link and no unit; not a failure.
if [ $rc != 0 ] && echo "$out" | grep -q "wasm-ld: error: no input files"; then
rc=0; nocode=$((nocode + 1)); echo "no code: $f at $b" >> "$log"
fi
[ $rc = 0 ] || { bad=$((bad + 1)); echo "FAILED ($rc): $f at $b" >> "$log"; }
[ -d "$D/icall" ] && echo "$out" | grep -q "indirect-call hints: none" && nohints=$((nohints + 1))
done < "$D/units.list"
[ $bad = 0 ] || { rm -rf "$U.new"; fail "$bad of $n images did not build (log $log)"; }
mv "$U.new" "$U" && touch "$U/.complete"
echo "units: built $n images for inputs $K in $(( $(date +%s) - t0 )) s ($nohints without an indirect-call profile, $nocode with no code; log $log)" >&2
fi
# The i386 trampoline page's unit, which no image in units.list is: a 32-bit Wine
# page with units places it with its first image and wants it. Added to a set
# built before it was (cached: a no-op once there).
if ! GEX_CGEN_CACHE="$U" GEX_CGEN_ICALL_COUNT=0 systemd-run --user --scope -q -p TasksMax=4096 -p MemoryMax=32G nice -n 19 "$G" cgen-image --nt-trampolines32 > "$U.tramp.log" 2>&1; then
fail "the trampoline page's unit did not build (log $U.tramp.log)"
fi
rm -f "$U.tramp.log"
touch "$U"
# Keep the three most recently used sets.
ls -1dt "$D"/ukeys/*/ 2>/dev/null | tail -n +4 | while read -r old; do
old=${old%/}; [ "$old" = "$U" ] || rm -rf "$old" "$old.log"
done
echo "$U"