build-windows-base.sh: show a window by default, fix macOS bash 3.2 and fork crashes

Replace --window with --headless (VNC); the window is the default. Fill the
QEMU config placeholders without nested quotes (bash 3.2 kept them literally),
and run QEMU as a background job instead of -daemonize when it shows a window
(Cocoa crashes in fork()).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
guofangandClaude Sonnet 5.5 committed 2026-11-02 21:31:10 +08:00
1 parent 4eb26e3a96
commit 91ae8cb147
2 files changed
+28 -13

No files matched your search

+27 -12
View File
@@ -24,7 +24,7 @@
# install-iso/ (see README.md).
#
# Usage: build-windows-base.sh [--force] [--keep-work] [--compress] [--from-img]
# [--window] [OUTPUT.qcow2]
# [--headless] [OUTPUT.qcow2]
# OUTPUT defaults to the path that "download-images.sh --print-path windows11"
# prints.
set -euo pipefail
@@ -38,7 +38,7 @@ win_dir="${images_dir}/windows11"
usage() {
cat <<EOF
Usage: $(basename "$0") [--force] [--keep-work] [--compress] [--from-img]
[--window] [OUTPUT.qcow2]
[--headless] [OUTPUT.qcow2]
Install Windows 11 from the ISO, generalize it with sysprep and store the disk
as the read-only, generic base image that VM disks are cloned from.
@@ -49,8 +49,9 @@ as the read-only, generic base image that VM disks are cloned from.
--from-img Install from the disk image made by build-windows-install-disk.sh
(the ISO path ending in .img), which reads much faster than
the emulated CD-ROM
--window Show the guest in a QEMU window (needs a desktop session)
instead of the VNC display on 127.0.0.1:5990
--headless Serve the guest screen over VNC on 127.0.0.1:5990 instead of
showing a QEMU window (the default; without a desktop
session on Linux, VNC is used as well)
-h, --help Show this help
Environment variables:
@@ -60,7 +61,7 @@ Environment variables:
WIN11_VM_DISK_SIZE Size of the system disk (default: 64G)
WIN11_VM_UEFI_CODE OVMF read-only firmware (auto-detected if unset)
WIN11_VM_UEFI_VARS OVMF variable-store template (auto-detected if unset)
WIN11_VM_DISPLAY QEMU display with --window (default: cocoa on macOS,
WIN11_VM_DISPLAY QEMU window display (default: cocoa on macOS,
gtk elsewhere)
BASE_BUILD_TMPDIR Parent of the scratch state directory, which needs about
25 GB (default: /var/tmp)
@@ -77,7 +78,7 @@ force=0
keep_work=0
compress=0
from_img=0
window=0
window=1
output=""
while (($#)); do
case "$1" in
@@ -86,7 +87,7 @@ while (($#)); do
--keep-work) keep_work=1 ;;
--compress) compress=1 ;;
--from-img) from_img=1 ;;
--window) window=1 ;;
--headless) window=0 ;;
-*) usage >&2; exit 2 ;;
*) [[ -z "$output" ]] || { usage >&2; exit 2; }; output="$1" ;;
esac
@@ -240,13 +241,15 @@ config_text="$(<"$config_template")"
for placeholder in uefi_code uefi_vars disk_image monitor_socket; do
value="${!placeholder}"
[[ "$value" != *\"* ]] || die "${placeholder} must not contain double quotes: ${value}"
config_text="${config_text//"\${${placeholder}}"/"$value"}"
# No outer double quotes: bash 3.2 (macOS) keeps inner quotes literally.
config_text=${config_text//"\${${placeholder}}"/"$value"}
done
printf '%s\n' "$config_text" >"$qemu_config"
if ((window)) && [[ -z "${DISPLAY:-}${WAYLAND_DISPLAY:-}" && "$host_os" != darwin ]]; then
window=0
fi
if ((window)); then
[[ -n "${DISPLAY:-}${WAYLAND_DISPLAY:-}" || "$host_os" == darwin ]] \
|| die "--window needs a desktop session (DISPLAY or WAYLAND_DISPLAY is not set)."
if [[ -n "${WIN11_VM_DISPLAY:-}" ]]; then display="$WIN11_VM_DISPLAY"
elif [[ "$host_os" == darwin ]]; then display=cocoa
else display=gtk; fi
@@ -263,8 +266,11 @@ qemu_args=(
-netdev user,id=net0
-nic none
-display "$display"
-serial "file:${work}/console.log" -daemonize -pidfile "$pid_file"
-serial "file:${work}/console.log" -pidfile "$pid_file"
)
# The Cocoa/GTK window needs the original process (macOS crashes in fork()), so
# only the VNC display is daemonized; the window runs as a background job.
((window)) || qemu_args+=(-daemonize)
if [[ "$host_os" == darwin && "$arch" == arm64 ]] && ((!from_img)); then
# With the HVF in-kernel GICv3, QEMU's usb-storage races on the status packet
# and Windows Setup stalls 10 s after every burst of reads (utmapp/UTM#7970),
@@ -308,7 +314,16 @@ press_key() {
fi
}
"$qemu_binary" "${qemu_args[@]}"
if ((window)); then
"$qemu_binary" "${qemu_args[@]}" </dev/null &
qemu_job=$!
until [[ -s "$pid_file" ]]; do
kill -0 "$qemu_job" 2>/dev/null || die "QEMU exited before it started the guest."
sleep 0.2
done
else
"$qemu_binary" "${qemu_args[@]}"
fi
if ((!from_img)); then
(
deadline=$((SECONDS + 30))