Add --arch to run-windows11.py and download the Windows ISO with --download-only

Update vm-images: add a windows11 download target.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
GuofangandClaude Sonnet 5.5 committed 2026-10-21 16:45:37 +08:00
1 parent f8d02dfc70
commit fa9ddee2b6
2 files changed
+78 -53

No files matched your search

+77 -52
View File
@@ -77,85 +77,44 @@ def normalize_arch(arch):
die(f"unsupported architecture: {arch}") die(f"unsupported architecture: {arch}")
host_arch = normalize_arch(env("VM_IMAGE_ARCH") or platform.machine())
qemu_binary = "qemu-system-aarch64" if host_arch == "arm64" else "qemu-system-x86_64"
host_os = platform.system().lower()
if host_os == "darwin" and host_arch == "amd64":
die("amd64 guests are not supported on macOS hosts; use an arm64 image")
qemu_platform_config = script_dir / "qemu" / f"windows11-{host_os}-{host_arch}.ini"
def pick_firmware(override, *candidates):
if override:
return Path(override)
for candidate in candidates:
if Path(candidate).is_file():
return Path(candidate)
return Path(candidates[0])
if host_arch == "arm64":
uefi_code = pick_firmware(
env("WIN11_VM_UEFI_CODE"),
"/usr/share/AAVMF/AAVMF_CODE.fd",
"/usr/share/edk2/aarch64/QEMU_EFI.fd",
"/opt/homebrew/share/qemu/edk2-aarch64-code.fd",
"/usr/local/share/qemu/edk2-aarch64-code.fd",
)
uefi_vars_template = pick_firmware(
env("WIN11_VM_UEFI_VARS"),
"/usr/share/AAVMF/AAVMF_VARS.fd",
"/usr/share/edk2/aarch64/vars-template-pflash.raw",
"/opt/homebrew/share/qemu/edk2-arm-vars.fd",
"/usr/local/share/qemu/edk2-arm-vars.fd",
)
else:
uefi_code = pick_firmware(
env("WIN11_VM_UEFI_CODE"),
"/usr/share/OVMF/OVMF_CODE_4M.fd",
"/usr/share/edk2/ovmf/OVMF_CODE.fd",
"/opt/homebrew/share/qemu/edk2-x86_64-code.fd",
"/usr/local/share/qemu/edk2-x86_64-code.fd",
)
uefi_vars_template = pick_firmware(
env("WIN11_VM_UEFI_VARS"),
"/usr/share/OVMF/OVMF_VARS_4M.fd",
"/usr/share/edk2/ovmf/OVMF_VARS.fd",
"/opt/homebrew/share/qemu/edk2-i386-vars.fd",
"/usr/local/share/qemu/edk2-i386-vars.fd",
)
daemonize = False daemonize = False
install_from_iso = False install_from_iso = False
install_from_img = False install_from_img = False
download_only = False download_only = False
extra_args = [] extra_args = []
arch_option = None
def usage(stream=sys.stdout): def usage(stream=sys.stdout):
stream.write(f"""\ stream.write(f"""\
Usage: {Path(sys.argv[0]).name} [--daemon] [--download-only] [--install-from-iso | --install-from-img] Usage: {Path(sys.argv[0]).name} [--daemon] [--download-only] [--install-from-iso | --install-from-img]
[--state-dir DIR] [--rdp-port PORT] [--ssh-port PORT] [-- QEMU_ARGUMENT ...] [--arch ARCH] [--state-dir DIR] [--rdp-port PORT] [--ssh-port PORT] [-- QEMU_ARGUMENT ...]
If the system disk (DIR/disk.qcow2, see --state-dir) is missing, create it from If the system disk (DIR/disk.qcow2, see --state-dir) is missing, create it from
the Windows 11 QCOW2 base image. the Windows 11 QCOW2 base image.
The VM hardware (accelerator, memory, CPUs, drives, devices) is defined in The VM hardware (accelerator, memory, CPUs, drives, devices) is defined in
qemu/windows11.ini, which is rendered to qemu.ini in the state directory and qemu/windows11.ini, which is rendered to qemu.ini in the state directory and
loaded with QEMU's -readconfig, followed by qemu/windows11-<os>-<arch>.ini (os is linux or darwin, arch is amd64 or arm64, chosen from the host system and loaded with QEMU's -readconfig, followed by qemu/windows11-<os>-<arch>.ini (os is linux or darwin, arch is amd64 or arm64, chosen from the host system and
architecture or VM_IMAGE_ARCH). Later QEMU arguments override them, e.g. -- -m 4G -smp 4. architecture, --arch or VM_IMAGE_ARCH). Later QEMU arguments override them, e.g. -- -m 4G -smp 4.
Use --install-from-iso to perform an automatic ISO installation instead, or Use --install-from-iso to perform an automatic ISO installation instead, or
--install-from-img to install from a disk image built from the ISO. --install-from-img to install from a disk image built from the ISO.
If it exists, start Windows normally without attaching installation media. If it exists, start Windows normally without attaching installation media.
Options: Options:
--daemon Run QEMU in the background --daemon Run QEMU in the background
--download-only Download the VirtIO tools ISO if it is missing, then exit --download-only Download the VirtIO tools ISO and the Windows 11 ISO
(the Windows ISO and base QCOW2 are supplied manually) (for --arch) if they are missing, then exit; needs
Node.js and Chromium for the latter, see vm-images/README.md
(the base QCOW2 is supplied manually)
--install-from-iso Install from ISO when the system disk is missing --install-from-iso Install from ISO when the system disk is missing
--install-from-img Like --install-from-iso, but install from the disk image --install-from-img Like --install-from-iso, but install from the disk image
made by vm-images/build-windows-install-disk.sh, which made by vm-images/build-windows-install-disk.sh, which
is the ISO path ending in .img instead of .iso. is the ISO path ending in .img instead of .iso.
It is attached as an NVMe disk, much faster than the It is attached as an NVMe disk, much faster than the
emulated USB CD-ROM, and is never modified emulated USB CD-ROM, and is never modified
--arch ARCH Guest architecture: arm64 or amd64 (default: the host's;
overrides VM_IMAGE_ARCH). A guest that differs from the
host runs under slow TCG emulation
--state-dir DIR Directory for the VM disk, seed ISO and runtime files --state-dir DIR Directory for the VM disk, seed ISO and runtime files
(default: .vm/windows11; overrides WIN11_VM_STATE_DIR) (default: .vm/windows11; overrides WIN11_VM_STATE_DIR)
--rdp-port PORT Host port forwarded to guest RDP (default: 3389; --rdp-port PORT Host port forwarded to guest RDP (default: 3389;
@@ -212,6 +171,12 @@ while args:
rdp_port = value rdp_port = value
else: else:
ssh_port = value ssh_port = value
elif arg == "--arch":
if not args:
die(f"{arg} requires an architecture argument.", 2)
arch_option = args.pop(0)
elif arg.startswith("--arch="):
arch_option = arg.split("=", 1)[1]
elif arg in ("-h", "--help"): elif arg in ("-h", "--help"):
usage() usage()
sys.exit(0) sys.exit(0)
@@ -223,11 +188,62 @@ while args:
usage(sys.stderr) usage(sys.stderr)
sys.exit(2) sys.exit(2)
host_arch = normalize_arch(arch_option or env("VM_IMAGE_ARCH") or platform.machine())
# The shared image helper and build-seed.sh pick the architecture from here.
os.environ["VM_IMAGE_ARCH"] = host_arch
qemu_binary = "qemu-system-aarch64" if host_arch == "arm64" else "qemu-system-x86_64"
host_os = platform.system().lower()
if host_os == "darwin" and host_arch == "amd64":
die("amd64 guests are not supported on macOS hosts; use an arm64 image")
qemu_platform_config = script_dir / "qemu" / f"windows11-{host_os}-{host_arch}.ini"
def pick_firmware(override, *candidates):
if override:
return Path(override)
for candidate in candidates:
if Path(candidate).is_file():
return Path(candidate)
return Path(candidates[0])
if host_arch == "arm64":
uefi_code = pick_firmware(
env("WIN11_VM_UEFI_CODE"),
"/usr/share/AAVMF/AAVMF_CODE.fd",
"/usr/share/edk2/aarch64/QEMU_EFI.fd",
"/opt/homebrew/share/qemu/edk2-aarch64-code.fd",
"/usr/local/share/qemu/edk2-aarch64-code.fd",
)
uefi_vars_template = pick_firmware(
env("WIN11_VM_UEFI_VARS"),
"/usr/share/AAVMF/AAVMF_VARS.fd",
"/usr/share/edk2/aarch64/vars-template-pflash.raw",
"/opt/homebrew/share/qemu/edk2-arm-vars.fd",
"/usr/local/share/qemu/edk2-arm-vars.fd",
)
else:
uefi_code = pick_firmware(
env("WIN11_VM_UEFI_CODE"),
"/usr/share/OVMF/OVMF_CODE_4M.fd",
"/usr/share/edk2/ovmf/OVMF_CODE.fd",
"/opt/homebrew/share/qemu/edk2-x86_64-code.fd",
"/usr/local/share/qemu/edk2-x86_64-code.fd",
)
uefi_vars_template = pick_firmware(
env("WIN11_VM_UEFI_VARS"),
"/usr/share/OVMF/OVMF_VARS_4M.fd",
"/usr/share/edk2/ovmf/OVMF_VARS.fd",
"/opt/homebrew/share/qemu/edk2-i386-vars.fd",
"/usr/local/share/qemu/edk2-i386-vars.fd",
)
if install_from_img: if install_from_img:
install_from_iso = True install_from_iso = True
if download_only: if download_only:
# The VirtIO tools ISO is the only medium the launcher downloads itself. # The VirtIO tools ISO and the Windows installation ISO are the media the
# launcher downloads itself; the base QCOW2 is supplied manually.
image_downloader = None
if env("WIN11_VM_TOOLS_ISO"): if env("WIN11_VM_TOOLS_ISO"):
tools_iso = Path(env("WIN11_VM_TOOLS_ISO")) tools_iso = Path(env("WIN11_VM_TOOLS_ISO"))
if not tools_iso.is_file(): if not tools_iso.is_file():
@@ -240,6 +256,15 @@ if download_only:
if not tools_iso.is_file(): if not tools_iso.is_file():
die(f"downloader did not create: {tools_iso}") die(f"downloader did not create: {tools_iso}")
print(f"VirtIO Windows tools ISO is ready: {tools_iso}") print(f"VirtIO Windows tools ISO is ready: {tools_iso}")
if env("WIN11_VM_INSTALL_ISO"):
install_iso = Path(env("WIN11_VM_INSTALL_ISO"))
if not install_iso.is_file():
die(f"Windows 11 installation ISO was not found: {install_iso}")
else:
image_downloader = image_downloader or find_image_downloader()
run([str(image_downloader), "windows11"])
install_iso = Path(capture([str(image_downloader), "--print-path", "windows11", "--iso"]))
print(f"Windows 11 installation ISO is ready: {install_iso}")
sys.exit(0) sys.exit(0)
disk_image = Path(env("WIN11_VM_DISK_IMAGE") or state_dir / "disk.qcow2") disk_image = Path(env("WIN11_VM_DISK_IMAGE") or state_dir / "disk.qcow2")