Files
Hermit/scripts/build_third_party_mingw.sh
T
WHC 2a43ae6622
ci / linux (clang) (push) Canceled after 0s
ci / linux (gcc) (push) Canceled after 0s
ci / sanitizers (push) Canceled after 0s
ci / musl (push) Canceled after 0s
ci / macos (push) Canceled after 0s
Windows: official port -- UCRT static build, UTF-8 throughout, Low IL sandbox, ncurses TUI, installer; v0.6.0
Build
  * cross build switches to pkgsCross.ucrt64 (shell-win.nix); curl (Schannel),
    winpthreads and ncurses 6.6 are linked statically, so the release is
    cst-agent.exe + busybox.exe (busybox-w32 Unicode) with no extra DLLs
  * windres embeds a manifest with activeCodePage=UTF-8 and a version resource
  * ncurses 6.6 Windows console driver patches (mouse button state, A_DIM,
    wide-char declarations): scripts/patches/
  * scripts/bootstrap_msys2.sh builds the same release natively in MSYS2
    UCRT64; PR #1's MINGW64 notes now point there
  * vendor/tre-regex (musl's TRE) provides <regex.h> on MinGW

UTF-8
  * paths cross the system call boundary as UTF-16 (*W APIs); console code
    pages set to 65001 and restored on exit; files are read and written in
    binary mode
  * child output that is not valid UTF-8 is decoded from the console, OEM or
    ANSI system code page
  * read_file hides CR; edit_file, apply_patch and write_file keep CRLF line
    endings and a UTF-8 BOM

OS layer and processes
  * os_dir_* and cst_fnmatch replace the POSIX-only directory walks in
    subagent, plugins, skills, undo and fs_tools; realpath / path_under and
    symlink rejection work on Windows
  * every child goes through CreateProcessW with its own environment block
    inside a Job Object, so timeouts and Esc end the whole process tree

Sandbox (auto mode, "auto · lowil")
  * run_shell runs with a restricted Low integrity token; the workspace gets
    an inheritable Low label once, the sandbox TEMP is labeled Low, and the
    credentials file gets a no-read-up label
  * drive roots, the profile and system folders are never labeled
  * network commands still ask, since Low IL does not block the network;
    Access denied and MSYS2's 0xc0000142 offer a rerun outside the sandbox
  * /sandbox lists labeled folders, /sandbox reset-labels restores them

TUI
  * raster console fonts are switched to Consolas / NSimSun while running
  * the home logo falls back to text when the console font lacks U+2580
  * mouse drag copy goes to the Windows clipboard

Distribution
  * scripts/install_windows.ps1 installs a Gitea release into
    %LOCALAPPDATA%\Programs\cst-agent and adds it to the user PATH;
    cst-agent-update.cmd reruns it; scripts/release_windows.sh uploads the
    zip with the token taken from the environment
  * scripts/run_windows.bat is gone; docs/README-windows.md is rewritten

Tests
  * new test_lowil, test_win_utf8 and test_os_dir; tests made portable
    (cst_test_util.h)
  * scripts/win_test.sh and scripts/winvm/ run the suite on English and
    Chinese Windows Server VMs as Administrator, a standard user and a
    Chinese user name: 29/29 on all six
2026-10-06 23:08:14 +08:00

128 lines
4.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# Build pinned third-party deps for Windows x86_64 (mingw-w64, UCRT) into
# third_party/prefix-mingw, plus busybox-w32 into third_party/busybox.
# nix-shell shell-win.nix --run 'scripts/build_third_party_mingw.sh [all|curl|pdcurses|ncurses|busybox-w32]'
# Works the same from an MSYS2 UCRT64 shell (MINGW_HOST= uses unprefixed tools).
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
TP="$ROOT/third_party"
PREFIX="$TP/prefix-mingw"
HOST="${MINGW_HOST-x86_64-w64-mingw32}"
T="${HOST:+$HOST-}"
JOBS="${JOBS:-$(nproc 2>/dev/null || echo 4)}"
CURL_TAG=curl-8_22_0
PDCURSES_TAG=3.9
NCURSES_VER=6.6
BUSYBOX_W32_REV=9b50a1e
mkdir -p "$TP" "$PREFIX"
need_cmd() { command -v "$1" >/dev/null || { echo "missing: $1" >&2; exit 1; }; }
need_cmd git
need_cmd make
need_cmd "${T}gcc"
checkout() { # url dir rev
local url="$1" dir="$2" rev="$3"
[[ -d "$dir/.git" ]] || git clone -q --filter=blob:none "$url" "$dir"
if ! git -C "$dir" rev-parse -q --verify "$rev^{commit}" >/dev/null; then
git -C "$dir" fetch -q --tags origin
fi
git -C "$dir" checkout -q --detach "$rev"
}
build_curl() {
checkout https://github.com/curl/curl.git "$TP/curl" "$CURL_TAG"
rm -rf "$TP/curl/build-mingw"
local tc=()
[[ -n "$HOST" ]] && tc=(-DCMAKE_SYSTEM_NAME=Windows -DCMAKE_C_COMPILER="${T}gcc" -DCMAKE_RC_COMPILER="${T}windres")
cmake -S "$TP/curl" -B "$TP/curl/build-mingw" -G Ninja "${tc[@]}" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="$PREFIX" \
-DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON \
-DCURL_USE_SCHANNEL=ON -DCURL_USE_OPENSSL=OFF \
-DCURL_WINDOWS_SSPI=ON -DENABLE_UNICODE=ON \
-DCURL_DISABLE_LDAP=ON -DCURL_USE_LIBPSL=OFF -DCURL_USE_LIBSSH2=OFF \
-DUSE_LIBIDN2=OFF -DUSE_NGHTTP2=OFF \
-DCURL_ZLIB=OFF -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF \
-DBUILD_CURL_EXE=OFF -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF \
-DBUILD_LIBCURL_DOCS=OFF -DBUILD_MISC_DOCS=OFF -DENABLE_CURL_MANUAL=OFF
cmake --build "$TP/curl/build-mingw" -j"$JOBS"
cmake --install "$TP/curl/build-mingw"
echo "curl $CURL_TAG -> $PREFIX"
}
build_pdcurses() {
checkout https://github.com/wmcbrine/PDCurses.git "$TP/PDCurses" "$PDCURSES_TAG"
cd "$TP/PDCurses/wincon"
make clean >/dev/null 2>&1 || true
make -j"$JOBS" CC="${T}gcc" AR="${T}ar" STRIP="${T}strip" WIDE=Y UTF8=Y pdcurses.a
mkdir -p "$PREFIX/include/pdcurses" "$PREFIX/lib"
cp -f ../curses.h ../panel.h "$PREFIX/include/pdcurses/"
cp -f pdcurses.a "$PREFIX/lib/libpdcurses.a"
echo "PDCurses $PDCURSES_TAG -> $PREFIX"
}
build_ncurses() {
local src="$TP/ncurses-$NCURSES_VER" tgz="$TP/ncurses-$NCURSES_VER.tar.gz"
[[ -f "$tgz" ]] || curl -fL -o "$tgz" \
"https://invisible-island.net/archives/ncurses/ncurses-$NCURSES_VER.tar.gz"
[[ -d "$src" ]] || tar -C "$TP" -xzf "$tgz"
local pf
for pf in "$ROOT"/scripts/patches/ncurses-"$NCURSES_VER"-*.patch; do
[[ -e "$pf" ]] || continue
patch -d "$src" -p1 -R --dry-run -s -f <"$pf" >/dev/null 2>&1 && continue
if ! patch -d "$src" -p1 --dry-run -s -f <"$pf" >/dev/null 2>&1; then
echo "ncurses: $src has another version of $(basename "$pf"); re-extracting"
rm -rf "$src"
tar -C "$TP" -xzf "$tgz"
fi
patch -d "$src" -p1 -s <"$pf"
done
rm -rf "$src/build-mingw"
mkdir -p "$src/build-mingw"
cd "$src/build-mingw"
../configure --host="${HOST:-x86_64-w64-mingw32}" --prefix="$PREFIX" \
--without-ada --without-cxx --without-cxx-binding --without-manpages \
--without-progs --without-tests --without-debug --without-shared \
--enable-widec --enable-sp-funcs --enable-term-driver \
--enable-interop --disable-home-terminfo --disable-db-install \
--with-fallbacks=ms-terminal,xterm-256color --without-pkg-config \
--disable-pc-files --with-build-cc="${BUILD_CC:-cc}" >/dev/null
make -j"$JOBS" libs >/dev/null
make install.libs >/dev/null
echo "ncurses $NCURSES_VER -> $PREFIX"
}
build_busybox_w32() {
checkout https://github.com/rmyorston/busybox-w32.git "$TP/busybox-w32" "$BUSYBOX_W32_REV"
cd "$TP/busybox-w32"
make -s mingw64u_defconfig >/dev/null
make -s -j"$JOBS" CROSS_COMPILE="$T" </dev/null
mkdir -p "$TP/busybox"
cp -f busybox.exe "$TP/busybox/busybox.exe"
cat >"$TP/busybox/LICENSE.busybox" <<EOF
BusyBox is GPLv2. This busybox.exe is busybox-w32 ($BUSYBOX_W32_REV, Unicode build):
https://github.com/rmyorston/busybox-w32
Upstream BusyBox: https://busybox.net/
EOF
ls -la "$TP/busybox/busybox.exe"
}
case "${1:-all}" in
curl) build_curl ;;
pdcurses) build_pdcurses ;;
ncurses) build_ncurses ;;
busybox-w32) build_busybox_w32 ;;
all)
build_curl
build_ncurses
build_busybox_w32
;;
*)
echo "usage: $0 [all|curl|pdcurses|ncurses|busybox-w32]"
exit 1
;;
esac
echo "DONE"