Update build to run qemu on MacOS
This commit is contained in:
@@ -13,11 +13,13 @@ kernel heap. See [`docs/`](docs/README.md) for how each piece works.
|
|||||||
versions are rejected, because Zig makes breaking changes between releases
|
versions are rejected, because Zig makes breaking changes between releases
|
||||||
pre-1.0. A toolchain manager such as [zvm](https://github.com/tristanisham/zvm)
|
pre-1.0. A toolchain manager such as [zvm](https://github.com/tristanisham/zvm)
|
||||||
or `zigup` will pick up `.zig-version` automatically.
|
or `zigup` will pick up `.zig-version` automatically.
|
||||||
- **QEMU** (`qemu-system-x86_64`) — to run and test the kernel.
|
- **QEMU** (`qemu-system-x86_64`) — to run and test the kernel. On macOS,
|
||||||
|
`brew install qemu` also bundles the OVMF firmware below.
|
||||||
- **OVMF** UEFI firmware — the `edk2-ovmf` package (Arch), `ovmf` (Debian/Ubuntu),
|
- **OVMF** UEFI firmware — the `edk2-ovmf` package (Arch), `ovmf` (Debian/Ubuntu),
|
||||||
or `edk2-ovmf` (Fedora). The build defaults to the Arch paths
|
or `edk2-ovmf` (Fedora); on macOS it ships inside the Homebrew `qemu` formula.
|
||||||
(`/usr/share/edk2/x64/OVMF_{CODE,VARS}.4m.fd`); override with `-Dovmf-code=` and
|
Both the build and the test harness probe the known Arch/Debian/Fedora/macOS
|
||||||
`-Dovmf-vars=` if your distro installs them elsewhere.
|
layouts and use the first that exists, so no configuration is normally needed.
|
||||||
|
Override with `-Dovmf-code=` / `-Dovmf-vars=` (build) if yours lives elsewhere.
|
||||||
- **Python 3** — for the QEMU integration test harness.
|
- **Python 3** — for the QEMU integration test harness.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|||||||
@@ -16,6 +16,18 @@ fn ensureZigVersion() void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the first path in `candidates` that exists on the build host, else the
|
||||||
|
/// first candidate as a fallback so a missing-firmware error still names a
|
||||||
|
/// concrete (and, by convention, the primary) path. Used to locate OVMF firmware
|
||||||
|
/// across distro/OS layouts without configuration.
|
||||||
|
fn firstExisting(io: std.Io, candidates: []const []const u8) []const u8 {
|
||||||
|
for (candidates) |path| {
|
||||||
|
std.Io.Dir.accessAbsolute(io, path, .{}) catch continue;
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
return candidates[0];
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(b: *std.Build) void {
|
pub fn build(b: *std.Build) void {
|
||||||
ensureZigVersion();
|
ensureZigVersion();
|
||||||
|
|
||||||
@@ -105,18 +117,33 @@ pub fn build(b: *std.Build) void {
|
|||||||
b.installArtifact(efiexe);
|
b.installArtifact(efiexe);
|
||||||
|
|
||||||
// --- run-efi: boot BOOTX64.efi in QEMU with OVMF firmware ---
|
// --- run-efi: boot BOOTX64.efi in QEMU with OVMF firmware ---
|
||||||
// OVMF paths follow the Arch (edk2-ovmf) layout; override with -Dovmf-code
|
// Firmware lives in different places per OS/distro, so probe the known
|
||||||
// and -Dovmf-vars if your distro installs them elsewhere.
|
// layouts (Arch, Debian/Ubuntu, Fedora, macOS Homebrew) and use the first
|
||||||
|
// that exists. Override with -Dovmf-code / -Dovmf-vars if yours is elsewhere.
|
||||||
const ovmf_code = b.option(
|
const ovmf_code = b.option(
|
||||||
[]const u8,
|
[]const u8,
|
||||||
"ovmf-code",
|
"ovmf-code",
|
||||||
"Path to the OVMF_CODE firmware image",
|
"Path to the OVMF_CODE firmware image",
|
||||||
) orelse "/usr/share/edk2/x64/OVMF_CODE.4m.fd";
|
) orelse firstExisting(b.graph.io, &.{
|
||||||
|
"/usr/share/edk2/x64/OVMF_CODE.4m.fd", // Arch
|
||||||
|
"/usr/share/OVMF/OVMF_CODE_4M.fd", // Debian/Ubuntu
|
||||||
|
"/usr/share/OVMF/OVMF_CODE.fd", // older Debian/Ubuntu
|
||||||
|
"/usr/share/edk2-ovmf/x64/OVMF_CODE.fd", // Fedora
|
||||||
|
"/opt/homebrew/share/qemu/edk2-x86_64-code.fd", // macOS Homebrew (Apple Silicon)
|
||||||
|
"/usr/local/share/qemu/edk2-x86_64-code.fd", // macOS Homebrew (Intel)
|
||||||
|
});
|
||||||
const ovmf_vars = b.option(
|
const ovmf_vars = b.option(
|
||||||
[]const u8,
|
[]const u8,
|
||||||
"ovmf-vars",
|
"ovmf-vars",
|
||||||
"Path to the OVMF_VARS firmware image (a writable copy is made)",
|
"Path to the OVMF_VARS firmware image (a writable copy is made)",
|
||||||
) orelse "/usr/share/edk2/x64/OVMF_VARS.4m.fd";
|
) orelse firstExisting(b.graph.io, &.{
|
||||||
|
"/usr/share/edk2/x64/OVMF_VARS.4m.fd", // Arch
|
||||||
|
"/usr/share/OVMF/OVMF_VARS_4M.fd", // Debian/Ubuntu
|
||||||
|
"/usr/share/OVMF/OVMF_VARS.fd", // older Debian/Ubuntu
|
||||||
|
"/usr/share/edk2-ovmf/x64/OVMF_VARS.fd", // Fedora
|
||||||
|
"/opt/homebrew/share/qemu/edk2-i386-vars.fd", // macOS Homebrew (Apple Silicon)
|
||||||
|
"/usr/local/share/qemu/edk2-i386-vars.fd", // macOS Homebrew (Intel)
|
||||||
|
});
|
||||||
|
|
||||||
// Assemble an EFI System Partition layout: esp/EFI/BOOT/BOOTX64.efi
|
// Assemble an EFI System Partition layout: esp/EFI/BOOT/BOOTX64.efi
|
||||||
const efi_install = b.addInstallArtifact(efiexe, .{
|
const efi_install = b.addInstallArtifact(efiexe, .{
|
||||||
|
|||||||
+39
-2
@@ -35,8 +35,26 @@ ARCHES = {
|
|||||||
"x86_64": {
|
"x86_64": {
|
||||||
"zig_flags": [], # build.zig currently pins the kernel to x86_64
|
"zig_flags": [], # build.zig currently pins the kernel to x86_64
|
||||||
"qemu": "qemu-system-x86_64",
|
"qemu": "qemu-system-x86_64",
|
||||||
"ovmf_code": "/usr/share/edk2/x64/OVMF_CODE.4m.fd",
|
# Firmware lives in different places per OS/distro; resolve_firmware()
|
||||||
"ovmf_vars": "/usr/share/edk2/x64/OVMF_VARS.4m.fd",
|
# picks the first that exists so the harness runs unconfigured on Arch,
|
||||||
|
# Debian/Ubuntu, Fedora, and macOS (Homebrew). Override by reordering or
|
||||||
|
# dropping an absolute path at the front.
|
||||||
|
"ovmf_code": [
|
||||||
|
"/usr/share/edk2/x64/OVMF_CODE.4m.fd", # Arch
|
||||||
|
"/usr/share/OVMF/OVMF_CODE_4M.fd", # Debian/Ubuntu
|
||||||
|
"/usr/share/OVMF/OVMF_CODE.fd", # older Debian/Ubuntu
|
||||||
|
"/usr/share/edk2-ovmf/x64/OVMF_CODE.fd", # Fedora
|
||||||
|
"/opt/homebrew/share/qemu/edk2-x86_64-code.fd", # macOS Homebrew (Apple Silicon)
|
||||||
|
"/usr/local/share/qemu/edk2-x86_64-code.fd", # macOS Homebrew (Intel)
|
||||||
|
],
|
||||||
|
"ovmf_vars": [
|
||||||
|
"/usr/share/edk2/x64/OVMF_VARS.4m.fd", # Arch
|
||||||
|
"/usr/share/OVMF/OVMF_VARS_4M.fd", # Debian/Ubuntu
|
||||||
|
"/usr/share/OVMF/OVMF_VARS.fd", # older Debian/Ubuntu
|
||||||
|
"/usr/share/edk2-ovmf/x64/OVMF_VARS.fd", # Fedora
|
||||||
|
"/opt/homebrew/share/qemu/edk2-i386-vars.fd", # macOS Homebrew (Apple Silicon)
|
||||||
|
"/usr/local/share/qemu/edk2-i386-vars.fd", # macOS Homebrew (Intel)
|
||||||
|
],
|
||||||
"efi_app": ("EFI/BOOT/BOOTX64.efi", "BOOTX64.efi"), # (dest in ESP, name in zig-out/bin)
|
"efi_app": ("EFI/BOOT/BOOTX64.efi", "BOOTX64.efi"), # (dest in ESP, name in zig-out/bin)
|
||||||
"kernel": ("danos", "danos"),
|
"kernel": ("danos", "danos"),
|
||||||
# Built as a function so we can splice in per-run paths.
|
# Built as a function so we can splice in per-run paths.
|
||||||
@@ -123,6 +141,24 @@ def make_esp(arch):
|
|||||||
return esp
|
return esp
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_firmware(arch):
|
||||||
|
"""Collapse the ovmf_code/ovmf_vars candidate lists to the first path that
|
||||||
|
exists on this machine. Mutates `arch` in place; idempotent (a resolved
|
||||||
|
string is left untouched). Exits with a clear message if none is found."""
|
||||||
|
for key, label in (("ovmf_code", "OVMF_CODE"), ("ovmf_vars", "OVMF_VARS")):
|
||||||
|
val = arch[key]
|
||||||
|
if isinstance(val, str):
|
||||||
|
continue # already resolved on a previous call
|
||||||
|
for cand in val:
|
||||||
|
if os.path.exists(cand):
|
||||||
|
arch[key] = cand
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
sys.exit(f"error: no {label} firmware image found; looked in:\n "
|
||||||
|
+ "\n ".join(val)
|
||||||
|
+ "\nInstall OVMF (edk2-ovmf / ovmf) or add its path above.")
|
||||||
|
|
||||||
|
|
||||||
def run_case(arch, case):
|
def run_case(arch, case):
|
||||||
err = build(arch, case["name"])
|
err = build(arch, case["name"])
|
||||||
if err:
|
if err:
|
||||||
@@ -176,6 +212,7 @@ def main():
|
|||||||
if not shutil.which(arch["qemu"]):
|
if not shutil.which(arch["qemu"]):
|
||||||
print(f"error: {arch['qemu']} not found on PATH", file=sys.stderr)
|
print(f"error: {arch['qemu']} not found on PATH", file=sys.stderr)
|
||||||
return 2
|
return 2
|
||||||
|
resolve_firmware(arch)
|
||||||
|
|
||||||
selected = [c for c in CASES if not args.cases or c["name"] in args.cases]
|
selected = [c for c in CASES if not args.cases or c["name"] in args.cases]
|
||||||
os.makedirs(WORK, exist_ok=True)
|
os.makedirs(WORK, exist_ok=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user