Files
dtourolle 2836ec2881 Build the Windows installer in a container, and run it under Wine
docs/windows.md specified it; this is §9 steps 1, 2 and 4 run, and the
report in §10. A Debian trixie image with rustup, the MinGW cross
compiler, NSIS and Wine; a build.sh in the shape of the Android one;
a package.sh that stages the executable and the seven models behind
the same LFS-pointer guard every other packager carries, then runs
makensis; and the .nsi itself — per-user, no elevation, an uninstaller
that leaves the library alone.

Measured: the executable links first time once the link flags were
right, imports only Windows system DLLs, prints its version under
Wine, and the installer installs and uninstalls silently under Wine
with the registry key and the models where §5.2 says. What Wine
cannot show is the Start Menu shortcut: CreateShortcut is IShellLink
and does nothing headless.

Four claims in the spec's first draft were wrong and are corrected in
place with the reasoning kept: the whole-archive winpthread flag
breaks the link and was never needed; build scripts need a host gcc;
bookworm's Wine lacks the bcryptprimitives.dll rustc's std imports,
so the image is trixie; and NSIS's default stub is 32-bit, so the
installer says amd64-unicode and needs no i386 Wine.
2026-09-12 00:54:11 +02:00

77 lines
2.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# Run a command inside the DarkRoom Windows cross-build container.
#
# ./docker/windows/build.sh cargo build --release --target x86_64-pc-windows-gnu -p darkroom-desktop
# ./docker/windows/build.sh cargo clippy --target x86_64-pc-windows-gnu -p darkroom-desktop
# ./docker/windows/build.sh # interactive shell
#
# Builds the image on first use. Rebuild after editing the Dockerfile with:
# ./docker/windows/build.sh --rebuild
set -euo pipefail
IMAGE="darkroom-windows:latest"
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO="$(cd "${HERE}/../.." && pwd)"
# Prefer podman (rootless by default); fall back to docker.
if command -v podman >/dev/null 2>&1; then
ENGINE=podman
elif command -v docker >/dev/null 2>&1; then
ENGINE=docker
else
echo "error: neither podman nor docker found" >&2
exit 1
fi
if [[ "${1:-}" == "--rebuild" ]]; then
shift
"${ENGINE}" build -t "${IMAGE}" "${HERE}"
elif ! "${ENGINE}" image exists "${IMAGE}" 2>/dev/null && \
! "${ENGINE}" image inspect "${IMAGE}" >/dev/null 2>&1; then
echo "==> building ${IMAGE} (first run; several minutes)"
"${ENGINE}" build -t "${IMAGE}" "${HERE}"
fi
# Persist the cargo registry and target dir across runs, or every build
# re-downloads the crate index.
CACHE="${XDG_CACHE_HOME:-${HOME}/.cache}/darkroom-windows"
# `home` is the container's $HOME: Wine keeps its prefix there for the smoke
# tests, and refuses one it does not own — which rules out anything the image
# could have created, since the container runs as the host user.
mkdir -p "${CACHE}/registry" "${CACHE}/target" "${CACHE}/home"
ARGS=(
--rm
-v "${REPO}:/work:z"
-v "${CACHE}/registry:/opt/cargo/registry:z"
-v "${CACHE}/target:/work/target-windows:z"
-v "${CACHE}/home:/tmp/home:z"
-e CARGO_TARGET_DIR=/work/target-windows
-w /work
)
# Build parallelism. A full cross-compile will otherwise take every thread on
# the host and make the machine unusable for the length of the build, which is
# a poor trade when it is running in the background.
#
# Both halves are needed: CARGO_BUILD_JOBS caps how many rustc processes cargo
# starts, while --cpus caps what the container gets no matter what any nested
# build script decides to spawn (cc, cmake, and ring's asm build all parallelise
# on their own account and do not consult cargo).
JOBS="${DARKROOM_BUILD_JOBS:-8}"
if [[ "${JOBS}" != "0" ]]; then
ARGS+=(--cpus "${JOBS}" -e "CARGO_BUILD_JOBS=${JOBS}")
fi
# Rootless podman already maps the host user; docker needs it stated.
if [[ "${ENGINE}" == "docker" ]]; then
ARGS+=(--user "$(id -u):$(id -g)")
fi
if [[ $# -eq 0 ]]; then
ARGS+=(-it)
set -- /bin/bash
fi
exec "${ENGINE}" run "${ARGS[@]}" "${IMAGE}" "$@"