diff --git a/Makefile b/Makefile index 47b1860..9f84b08 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,10 @@ SYSROOT = sysroot # there is no reason to compile one file at a time. Override with `make -j1` # when you want readable output from a failing build. # ----------------------------------------------------------------------- -NPROC := $(shell nproc 2>/dev/null || echo 4) +# nproc is coreutils, so it exists on Linux and under Git Bash but not on +# macOS or the BSDs, where the same number comes from sysctl. Falling through +# to 4 is a working build either way, just a slower one. +NPROC := $(shell nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4) MAKEFLAGS += -j$(NPROC) # Kernel object files — organized by subsystem @@ -92,31 +95,29 @@ CMDBINS = $(addprefix $(BUILD)/cmd/, $(CMDS)) UNAME_S := $(shell uname -s) UNAME_P := $(shell uname -p) -# Windows, under Git Bash or MSYS2 — WINHOST. +# Host platform. +# +# WINHOST Windows, under Git Bash or MSYS2 +# MACHOST macOS +# BSDHOST FreeBSD / OpenBSD / NetBSD / DragonFly +# (else) Linux +# +# All four are supported hosts: this is developed from both Windows and Linux, +# and nothing here should care which. # # Building natively on Windows rather than in WSL, because WSL2 is a real Linux # kernel in a Hyper-V VM and so hard-requires virtualisation; turning that on # puts the Hyper-V hypervisor underneath the whole machine and demotes VMware # to running nested on top of it, which is a real and permanent cost to pay for # a build environment. -# -# The usual answer is a cross binutils+gcc, which is a day of work that then -# rots. clang does not need any of it: it is a cross-compiler by construction, -# so one ordinary LLVM install targets both the bare-metal kernel and Linux -# userland, and llvm-objcopy/objdump stand in for binutils. Everything else the -# build shells out to — perl, sed, dd, sha256sum — comes with Git Bash. ifneq (,$(filter MINGW% MSYS% CYGWIN%,$(UNAME_S))) WINHOST := 1 endif - -ifeq ($(UNAME_S),Linux) - ifeq ($(UNAME_P),aarch64) - TOOLPREFIX = x86_64-linux-gnu- - else - TOOLPREFIX = - endif -else ifeq ($(UNAME_S),Darwin) - TOOLPREFIX = x86_64-elf- +ifeq ($(UNAME_S),Darwin) + MACHOST := 1 +endif +ifneq (,$(filter FreeBSD OpenBSD NetBSD DragonFly,$(UNAME_S))) + BSDHOST := 1 endif QEMU = qemu-system-x86_64 @@ -133,10 +134,40 @@ QEMU = qemu-system-x86_64 # virtio-gpu wins the display class, stdvga carries the splash and text console. QEMU_DISPLAY = -vga std -device virtio-gpu-pci -ifdef WINHOST - # An explicit --target is what makes clang emit ELF instead of the COFF it - # would default to on this host. Without it every object is silently the - # wrong format and the first thing to complain is the linker. +# ----------------------------------------------------------------------- +# Cross-compilation: clang everywhere it can be had +# +# The kernel, the bootloader and the ports all need a compiler targeting +# something other than the host -- bare-metal ELF, 16/32-bit code below one +# megabyte, and Linux/musl userland respectively. The traditional answer is a +# cross binutils+gcc per host, which is a day of work per platform that then +# rots, and on macOS means brewing x86_64-elf-* before anything builds at all. +# +# clang needs none of it: it is a cross-compiler by construction, so one +# ordinary LLVM install produces every target from every host. That was already +# how the Windows build worked; using it on all four hosts collapses what used +# to be two divergent toolchain branches -- one of which had drifted into +# passing GNU-ld-only flags to what is nowadays lld on FreeBSD and macOS -- into +# a single path that gets exercised on every platform instead of one. +# +# GNU is kept as a fallback for hosts without clang, but it is the exception. +# ----------------------------------------------------------------------- +HAVE_CLANG := $(shell command -v clang 2>/dev/null) +HAVE_LLD := $(shell command -v ld.lld 2>/dev/null) + +ifeq ($(origin USE_GNU), undefined) + ifneq (,$(HAVE_CLANG)) + ifneq (,$(HAVE_LLD)) + USE_CLANG := 1 + endif + endif +endif + +ifdef USE_CLANG + # An explicit --target is what makes clang emit ELF rather than whatever the + # host defaults to -- COFF on Windows, Mach-O on macOS. Without it every + # object is silently the wrong format and the first thing to complain is the + # linker. CC = clang --target=x86_64-elf CC32 = clang --target=i386-elf LD = ld.lld @@ -151,49 +182,66 @@ ifdef WINHOST # and refuses, because for a hosted program that would be a bug. Saying the # base is zero is how you tell it this is not a hosted program. LD_LOWBASE = --image-base=0 - # Host tools (mkfs, mkschfs, the SchFS suites) are ordinary Windows programs. - # They must be built with mingw-w64 rather than clang's default MSVC target: - # tools/mkfs.c includes and , and the Windows SDK has - # neither. - HOSTCC = gcc - # mingw-w64's gcc warns about things glibc's does not (notably %lld/%zu in - # printf against msvcrt). Those warnings are about the host build only and - # say nothing about the image, so they must not stop it. - HOSTWARN = -Wall - # MinGW gcc appends .exe when the -o name has no extension, so a rule - # whose target lacks it is never considered built -- mkfs was recompiled on - # every single invocation of make, forever, because of this. - EXE = .exe - # `python3` is not on PATH from the python.org installer; `python` is. - PYTHON = python - # Compiler for ports/, which build *Linux userland* against musl -- a third - # target, distinct from both the kernel (bare-metal ELF) and the host tools - # (Windows PE). mingw's gcc cannot do it: it targets Windows, so linking it - # against musl gives undefined `stderr` and `__errno_location`. + # ports/ build *Linux userland* against musl -- a third target, distinct from + # both the kernel (bare-metal ELF) and the host tools (native). # # -nostdlib with the crt files named explicitly, because clang otherwise - # reaches for gcc's crtbeginT.o and libgcc, which do not exist on this host. + # reaches for gcc's crtbeginT.o and libgcc, which need not exist on any + # particular host. MUSLPREFIX = $(abspath $(BUILD))/ports/musl/prefix PORTCC = clang --target=x86_64-unknown-linux-musl -fuse-ld=lld PORTCRT = -nostdlib $(MUSLPREFIX)/lib/crt1.o $(MUSLPREFIX)/lib/crti.o PORTEND = $(MUSLPREFIX)/lib/crtn.o else + # GNU fallback. Needs a cross toolchain anywhere the host's own gcc does not + # already emit x86_64 ELF -- notably macOS, where TOOLPREFIX has to name a + # brewed x86_64-elf-gcc. + ifdef MACHOST + TOOLPREFIX ?= x86_64-elf- + else ifeq ($(UNAME_P),aarch64) + TOOLPREFIX ?= x86_64-linux-gnu- + else + TOOLPREFIX ?= + endif CC = $(TOOLPREFIX)gcc CC32 = $(CC) -m32 LD = $(TOOLPREFIX)ld OBJCOPY = $(TOOLPREFIX)objcopy OBJDUMP = $(TOOLPREFIX)objdump LDFLAGS = -m elf_x86_64 -nostdlib --no-warn-rwx-segments - HOSTCC = gcc - HOSTWARN = -Werror -Wall - EXE = - PYTHON = python3 LD_LOWBASE = - PORTCC = gcc + PORTCC = $(TOOLPREFIX)gcc PORTCRT = PORTEND = endif +# ----------------------------------------------------------------------- +# Host tools: mkfs, mkschfs, the SchFS suites, qrtest. Ordinary programs for +# the machine doing the building, so they use its native compiler. +# ----------------------------------------------------------------------- +HOSTCC ?= cc + +ifdef WINHOST + # mingw-w64's gcc, not clang: clang defaults to an MSVC target here, and + # tools/mkfs.c includes and , which the Windows SDK + # does not have. + HOSTCC = gcc + # mingw-w64's gcc warns about things glibc's does not (notably %lld/%zu in + # printf against msvcrt). Those warnings are about the host build only and + # say nothing about the image, so they must not stop it. + HOSTWARN = -Wall + # MinGW gcc appends .exe when the -o name has no extension, so a rule whose + # target lacks it is never considered built -- mkfs was recompiled on every + # single invocation of make, forever, because of this. + EXE = .exe + # `python3` is not on PATH from the python.org installer; `python` is. + PYTHON = python +else + HOSTWARN = -Werror -Wall + EXE = + PYTHON = python3 +endif + # Host tools are generated output and live under build/, per docs/layout.md. # $(EXE) matters: MinGW appends it, and a rule whose target lacks it never # looks satisfied. diff --git a/ports/mk/port.mk b/ports/mk/port.mk index 602d67e..52c666b 100644 --- a/ports/mk/port.mk +++ b/ports/mk/port.mk @@ -23,6 +23,11 @@ SYSROOT := $(TOPDIR)/sysroot DISTDIR := $(TOPDIR)/dist BUILDROOT := $(TOPDIR)/build/ports +# Verifying a downloaded tarball. Not `sha256sum -c -`: that command does not +# exist on macOS or the BSDs, and each of them spells it differently. The +# helper picks whichever of the five possibilities the host actually has. +SHA256CHECK := $(TOPDIR)/tools/sha256check.sh + # Port name is the directory name — no per-port boilerplate needed. PORT := $(notdir $(CURDIR)) PORTDIR := $(BUILDROOT)/$(PORT) diff --git a/ports/musl/Makefile b/ports/musl/Makefile index cc3776d..3a16645 100644 --- a/ports/musl/Makefile +++ b/ports/musl/Makefile @@ -14,7 +14,7 @@ TARBALL := $(DISTDIR)/musl-$(MUSL_VERSION).tar.gz $(TARBALL): | $(DISTDIR) curl -fsSL -o $@ $(MUSL_URL) - echo "$(MUSL_SHA256) $@" | sha256sum -c - + sh $(SHA256CHECK) "$(MUSL_SHA256)" "$@" download: $(TARBALL) diff --git a/ports/toybox/Makefile b/ports/toybox/Makefile index f2adb87..3e9ff53 100644 --- a/ports/toybox/Makefile +++ b/ports/toybox/Makefile @@ -26,7 +26,7 @@ LDFLAGS := -static -L$(MUSL)/lib -lc $(TARBALL): | $(DISTDIR) curl -fsSL -o $@ $(TOYBOX_URL) - echo "$(TOYBOX_SHA256) $@" | sha256sum -c - + sh $(SHA256CHECK) "$(TOYBOX_SHA256)" "$@" download: $(TARBALL) diff --git a/tools/bootcheck.ps1 b/tools/bootcheck.ps1 index aca71e0..ad4f3ca 100644 --- a/tools/bootcheck.ps1 +++ b/tools/bootcheck.ps1 @@ -17,8 +17,8 @@ # the pacing below -- not superstition, just the difference between a reliable # check and a flaky one. # -# tools\bootcheck.ps1 -# tools\bootcheck.ps1 -Tty -Ps +# tools/bootcheck.ps1 +# tools/bootcheck.ps1 -Tty -Ps param( [switch]$Fb, @@ -49,16 +49,39 @@ param( # else in this suite touches. [switch]$Ps, [int]$BootSeconds = 20, - [string]$Qemu = "$env:USERPROFILE\scoop\apps\qemu\current\qemu-system-x86_64.exe" + [string]$Qemu = (Find-Qemu) ) +# QEMU, on whichever host this is. +# +# PowerShell 7 runs on Linux, macOS and the BSDs as well as Windows, so these +# scripts are usable from all of them -- but the old default was a hardcoded +# scoop path under $env:USERPROFILE, which exists on exactly one of those. PATH +# first, because a package-managed qemu is on it everywhere; the scoop location +# is only a fallback for the Windows install that does not add itself to PATH. +function Find-Qemu { + $exe = if ($IsWindows -or $env:OS -eq 'Windows_NT') { 'qemu-system-x86_64.exe' } + else { 'qemu-system-x86_64' } + $onPath = Get-Command $exe -ErrorAction SilentlyContinue + if ($onPath) { return $onPath.Source } + if ($env:USERPROFILE) { + $scoop = Join-Path (Join-Path $env:USERPROFILE 'scoop/apps/qemu/current') $exe + if (Test-Path $scoop) { return $scoop } + } + foreach ($p in @('/usr/bin', '/usr/local/bin', '/opt/homebrew/bin')) { + $c = Join-Path $p $exe + if (Test-Path $c) { return $c } + } + throw "qemu-system-x86_64 not found on PATH. Install QEMU, or pass -Qemu ." +} + $ErrorActionPreference = 'Stop' $root = Split-Path $PSScriptRoot -Parent $hda = Join-Path $root 'schos.img' # The root filesystem. SchFS is the only root there is, so this is not a # choice -- a kernel handed anything else on device 1 panics in forkret(). -$hdb = Join-Path $root 'build\schfs.img' -$log = Join-Path $root 'build\bootcheck.log' +$hdb = Join-Path $root 'build/schfs.img' +$log = Join-Path $root 'build/bootcheck.log' Remove-Item -Force $log -ErrorAction SilentlyContinue @@ -196,7 +219,7 @@ $usbDevices = if ($Usb) { # there, which failed three USB checks that had nothing wrong with USB. # It also stops the same file being attached twice, once as the root # disk and once as a USB device. - "-drive id=usbstick,file=`"$(Join-Path $root 'build\fs.img')`",format=raw,if=none" + "-drive id=usbstick,file=`"$(Join-Path $root 'build/fs.img')`",format=raw,if=none" '-device usb-storage,bus=xhci.0,drive=usbstick') } else { @() } diff --git a/tools/crashshot.ps1 b/tools/crashshot.ps1 index 8978256..0a5d13e 100644 --- a/tools/crashshot.ps1 +++ b/tools/crashshot.ps1 @@ -9,23 +9,48 @@ # shell. screendump writes a PPM; the PNG conversion is optional and skipped if # ImageMagick is not around. # -# tools\crashshot.ps1 crash the ramdisk-root build -# tools\crashshot.ps1 -SchFS with a SchFS volume attached +# tools/crashshot.ps1 crash the ramdisk-root build param( - [switch]$SchFS, [int]$BootSeconds = 20, - [string]$Out = "build\crashscreen.ppm", - [string]$Qemu = "$env:USERPROFILE\scoop\apps\qemu\current\qemu-system-x86_64.exe" + [string]$Out = "build/crashscreen.ppm", + [string]$Qemu = (Find-Qemu) ) +# QEMU, on whichever host this is. +# +# PowerShell 7 runs on Linux, macOS and the BSDs as well as Windows, so these +# scripts are usable from all of them -- but the old default was a hardcoded +# scoop path under $env:USERPROFILE, which exists on exactly one of those. PATH +# first, because a package-managed qemu is on it everywhere; the scoop location +# is only a fallback for the Windows install that does not add itself to PATH. +function Find-Qemu { + $exe = if ($IsWindows -or $env:OS -eq 'Windows_NT') { 'qemu-system-x86_64.exe' } + else { 'qemu-system-x86_64' } + $onPath = Get-Command $exe -ErrorAction SilentlyContinue + if ($onPath) { return $onPath.Source } + if ($env:USERPROFILE) { + $scoop = Join-Path (Join-Path $env:USERPROFILE 'scoop/apps/qemu/current') $exe + if (Test-Path $scoop) { return $scoop } + } + foreach ($p in @('/usr/bin', '/usr/local/bin', '/opt/homebrew/bin')) { + $c = Join-Path $p $exe + if (Test-Path $c) { return $c } + } + throw "qemu-system-x86_64 not found on PATH. Install QEMU, or pass -Qemu ." +} + $ErrorActionPreference = 'Stop' $root = Split-Path $PSScriptRoot -Parent $hda = Join-Path $root 'schos.img' -$hdb = if ($SchFS) { Join-Path $root 'build\schfs.img' } else { Join-Path $root 'build\fs.img' } -$log = Join-Path $root 'build\crashshot.log' +# The root filesystem. SchFS is the only root there is: the kernel mounts +# device 1 in forkret() and panics if it is not a SchFS volume, so this is +# not a choice and there is no -SchFS switch any more. It used to default to +# build/fs.img, which stopped being bootable the moment SchFS became root. +$hdb = Join-Path $root 'build/schfs.img' +$log = Join-Path $root 'build/crashshot.log' $ppm = Join-Path $root $Out -$mon = Join-Path $root 'build\qemu-mon.sock' +$mon = Join-Path $root 'build/qemu-mon.sock' Remove-Item -Force $log, $ppm -ErrorAction SilentlyContinue diff --git a/tools/dispshot.ps1 b/tools/dispshot.ps1 index bc882ab..cd70308 100644 --- a/tools/dispshot.ps1 +++ b/tools/dispshot.ps1 @@ -14,23 +14,48 @@ # shell. screendump writes a PPM; the PNG conversion is optional and skipped if # ImageMagick is not around. # -# tools\crashshot.ps1 crash the ramdisk-root build -# tools\crashshot.ps1 -SchFS with a SchFS volume attached +# tools/crashshot.ps1 crash the ramdisk-root build param( - [switch]$SchFS, [int]$BootSeconds = 20, - [string]$Out = "build\dispshot.ppm", - [string]$Qemu = "$env:USERPROFILE\scoop\apps\qemu\current\qemu-system-x86_64.exe" + [string]$Out = "build/dispshot.ppm", + [string]$Qemu = (Find-Qemu) ) +# QEMU, on whichever host this is. +# +# PowerShell 7 runs on Linux, macOS and the BSDs as well as Windows, so these +# scripts are usable from all of them -- but the old default was a hardcoded +# scoop path under $env:USERPROFILE, which exists on exactly one of those. PATH +# first, because a package-managed qemu is on it everywhere; the scoop location +# is only a fallback for the Windows install that does not add itself to PATH. +function Find-Qemu { + $exe = if ($IsWindows -or $env:OS -eq 'Windows_NT') { 'qemu-system-x86_64.exe' } + else { 'qemu-system-x86_64' } + $onPath = Get-Command $exe -ErrorAction SilentlyContinue + if ($onPath) { return $onPath.Source } + if ($env:USERPROFILE) { + $scoop = Join-Path (Join-Path $env:USERPROFILE 'scoop/apps/qemu/current') $exe + if (Test-Path $scoop) { return $scoop } + } + foreach ($p in @('/usr/bin', '/usr/local/bin', '/opt/homebrew/bin')) { + $c = Join-Path $p $exe + if (Test-Path $c) { return $c } + } + throw "qemu-system-x86_64 not found on PATH. Install QEMU, or pass -Qemu ." +} + $ErrorActionPreference = 'Stop' $root = Split-Path $PSScriptRoot -Parent $hda = Join-Path $root 'schos.img' -$hdb = if ($SchFS) { Join-Path $root 'build\schfs.img' } else { Join-Path $root 'build\fs.img' } -$log = Join-Path $root 'build\crashshot.log' +# The root filesystem. SchFS is the only root there is: the kernel mounts +# device 1 in forkret() and panics if it is not a SchFS volume, so this is +# not a choice and there is no -SchFS switch any more. It used to default to +# build/fs.img, which stopped being bootable the moment SchFS became root. +$hdb = Join-Path $root 'build/schfs.img' +$log = Join-Path $root 'build/crashshot.log' $ppm = Join-Path $root $Out -$mon = Join-Path $root 'build\qemu-mon.sock' +$mon = Join-Path $root 'build/qemu-mon.sock' Remove-Item -Force $log, $ppm -ErrorAction SilentlyContinue diff --git a/tools/fbshot.ps1 b/tools/fbshot.ps1 index 599cdae..13d23d5 100644 --- a/tools/fbshot.ps1 +++ b/tools/fbshot.ps1 @@ -6,22 +6,49 @@ # every byte accepted, and the only symptom is a picture that shears # progressively down the screen. That is a thing you have to look at. # -# tools\fbshot.ps1 gradient through mmap -# tools\fbshot.ps1 -Mode bars colour bars through write() +# tools/fbshot.ps1 gradient through mmap +# tools/fbshot.ps1 -Mode bars colour bars through write() param( [ValidateSet('fill', 'bars')] [string]$Mode = 'fill', [int]$BootSeconds = 20, - [string]$Qemu = "$env:USERPROFILE\scoop\apps\qemu\current\qemu-system-x86_64.exe" + [string]$Qemu = (Find-Qemu) ) +# QEMU, on whichever host this is. +# +# PowerShell 7 runs on Linux, macOS and the BSDs as well as Windows, so these +# scripts are usable from all of them -- but the old default was a hardcoded +# scoop path under $env:USERPROFILE, which exists on exactly one of those. PATH +# first, because a package-managed qemu is on it everywhere; the scoop location +# is only a fallback for the Windows install that does not add itself to PATH. +function Find-Qemu { + $exe = if ($IsWindows -or $env:OS -eq 'Windows_NT') { 'qemu-system-x86_64.exe' } + else { 'qemu-system-x86_64' } + $onPath = Get-Command $exe -ErrorAction SilentlyContinue + if ($onPath) { return $onPath.Source } + if ($env:USERPROFILE) { + $scoop = Join-Path (Join-Path $env:USERPROFILE 'scoop/apps/qemu/current') $exe + if (Test-Path $scoop) { return $scoop } + } + foreach ($p in @('/usr/bin', '/usr/local/bin', '/opt/homebrew/bin')) { + $c = Join-Path $p $exe + if (Test-Path $c) { return $c } + } + throw "qemu-system-x86_64 not found on PATH. Install QEMU, or pass -Qemu ." +} + $ErrorActionPreference = 'Stop' $root = Split-Path $PSScriptRoot -Parent $hda = Join-Path $root 'schos.img' -$hdb = Join-Path $root 'build\fs.img' -$log = Join-Path $root 'build\fbshot.log' -$ppm = Join-Path $root "build\fb-$Mode.ppm" +# The root filesystem. SchFS is the only root there is: the kernel mounts +# device 1 in forkret() and panics if it is not a SchFS volume, so this is +# not a choice and there is no -SchFS switch any more. It used to default to +# build/fs.img, which stopped being bootable the moment SchFS became root. +$hdb = Join-Path $root 'build/schfs.img' +$log = Join-Path $root 'build/fbshot.log' +$ppm = Join-Path $root "build/fb-$Mode.ppm" Remove-Item -Force $log, $ppm -ErrorAction SilentlyContinue diff --git a/tools/loc.ps1 b/tools/loc.ps1 index 6e0677a..e0de76a 100644 --- a/tools/loc.ps1 +++ b/tools/loc.ps1 @@ -6,9 +6,9 @@ # counts only first-party code, and separates the code from the comments so the # number means something. # -# tools\loc.ps1 the summary table -# tools\loc.ps1 -PerFile every file, largest first -# tools\loc.ps1 -All include third-party trees, for comparison +# tools/loc.ps1 the summary table +# tools/loc.ps1 -PerFile every file, largest first +# tools/loc.ps1 -All include third-party trees, for comparison # # Comment detection is deliberately simple: it handles // and /* */ for C and # # for scripts, and does not try to understand a // inside a string literal. diff --git a/tools/makeiso.ps1 b/tools/makeiso.ps1 index 84276dd..78d0a28 100644 --- a/tools/makeiso.ps1 +++ b/tools/makeiso.ps1 @@ -4,9 +4,9 @@ # El Torito image. The ramdisk root is the point: the ISO carries a complete # userland inside the kernel, so it boots with no hard disk attached at all. # -# tools\makeiso.ps1 build it -# tools\makeiso.ps1 -Run build it, then boot it under QEMU to check -# tools\makeiso.ps1 -Clean force a full rebuild first +# tools/makeiso.ps1 build it +# tools/makeiso.ps1 -Run build it, then boot it under QEMU to check +# tools/makeiso.ps1 -Clean force a full rebuild first # # Afterwards the ISO is schos.iso in the repository root; point a VM's optical # drive at it and boot from CD. @@ -14,9 +14,32 @@ param( [switch]$Run, [switch]$Clean, - [string]$Qemu = "$env:USERPROFILE\scoop\apps\qemu\current\qemu-system-x86_64.exe" + [string]$Qemu = (Find-Qemu) ) +# QEMU, on whichever host this is. +# +# PowerShell 7 runs on Linux, macOS and the BSDs as well as Windows, so these +# scripts are usable from all of them -- but the old default was a hardcoded +# scoop path under $env:USERPROFILE, which exists on exactly one of those. PATH +# first, because a package-managed qemu is on it everywhere; the scoop location +# is only a fallback for the Windows install that does not add itself to PATH. +function Find-Qemu { + $exe = if ($IsWindows -or $env:OS -eq 'Windows_NT') { 'qemu-system-x86_64.exe' } + else { 'qemu-system-x86_64' } + $onPath = Get-Command $exe -ErrorAction SilentlyContinue + if ($onPath) { return $onPath.Source } + if ($env:USERPROFILE) { + $scoop = Join-Path (Join-Path $env:USERPROFILE 'scoop/apps/qemu/current') $exe + if (Test-Path $scoop) { return $scoop } + } + foreach ($p in @('/usr/bin', '/usr/local/bin', '/opt/homebrew/bin')) { + $c = Join-Path $p $exe + if (Test-Path $c) { return $c } + } + throw "qemu-system-x86_64 not found on PATH. Install QEMU, or pass -Qemu ." +} + $ErrorActionPreference = 'Stop' $root = Split-Path $PSScriptRoot -Parent Push-Location $root @@ -117,7 +140,7 @@ Write-Host " RAM: 64 MB is plenty. The old 224 MB floor is gone." if ($Run) { Write-Host "" Write-Host "==> booting the ISO under QEMU, no disk attached" -ForegroundColor Cyan - $log = Join-Path $root 'build\isoboot.log' + $log = Join-Path $root 'build/isoboot.log' if (Test-Path $log) { Clear-Content $log } $psi = New-Object System.Diagnostics.ProcessStartInfo diff --git a/tools/mkschfs.c b/tools/mkschfs.c index 55166e9..b6d929f 100644 --- a/tools/mkschfs.c +++ b/tools/mkschfs.c @@ -55,7 +55,12 @@ #define S_ISLNK(m) 0 static long readlink(const char *p, char *b, unsigned long n) { (void)p; (void)b; (void)n; return -1; } -#else +#elif defined(__linux__) +/* glibc and musl both put major()/minor() here, and glibc warns and then + errors if you take them from instead. Linux only: macOS and + the BSDs have no at all and define both in , + which is already included above -- including it unconditionally is a build + failure on every non-Linux Unix. */ #include #endif diff --git a/tools/sha256check.sh b/tools/sha256check.sh new file mode 100644 index 0000000..a9a026e --- /dev/null +++ b/tools/sha256check.sh @@ -0,0 +1,62 @@ +#!/bin/sh +# Verify a file's SHA-256 against an expected hex digest, on any host. +# +# tools/sha256check.sh +# +# There is no one checksum command across the supported hosts. Linux and Git +# Bash have coreutils' sha256sum; macOS ships shasum(1) and no sha256sum; +# FreeBSD and NetBSD ship sha256(1) with a different output format again; +# OpenBSD has sha256 too but usually reached through cksum -a sha256. A recipe +# that hardcodes any one of them builds on some of the hosts this project +# supports and fails on the rest, and the failure arrives only when someone +# first runs `make ports` on a Mac. +# +# Compares digests rather than trusting a -c flag, because the -c input format +# differs between the implementations as well. + +set -eu + +if [ $# -ne 2 ]; then + echo "usage: $0 " >&2 + exit 2 +fi + +expected="$1" +file="$2" + +if [ ! -f "$file" ]; then + echo "sha256check: no such file: $file" >&2 + exit 1 +fi + +actual= +if command -v sha256sum >/dev/null 2>&1; then + actual=$(sha256sum "$file" | cut -d' ' -f1) +elif command -v shasum >/dev/null 2>&1; then + actual=$(shasum -a 256 "$file" | cut -d' ' -f1) +elif command -v sha256 >/dev/null 2>&1; then + # FreeBSD/NetBSD: -q prints the digest alone. + actual=$(sha256 -q "$file") +elif command -v cksum >/dev/null 2>&1 && cksum -a sha256 /dev/null >/dev/null 2>&1; then + # OpenBSD. + actual=$(cksum -a sha256 "$file" | sed 's/.*= *//') +elif command -v openssl >/dev/null 2>&1; then + actual=$(openssl dgst -sha256 "$file" | sed 's/.*= *//') +else + echo "sha256check: no usable checksum tool found" >&2 + echo " looked for: sha256sum, shasum, sha256, cksum, openssl" >&2 + exit 1 +fi + +# Case-insensitive: openssl and sha256 have been known to differ here. +expected_lc=$(printf '%s' "$expected" | tr 'A-Z' 'a-z') +actual_lc=$(printf '%s' "$actual" | tr 'A-Z' 'a-z') + +if [ "$expected_lc" != "$actual_lc" ]; then + echo "sha256check: checksum mismatch for $file" >&2 + echo " expected: $expected_lc" >&2 + echo " actual: $actual_lc" >&2 + exit 1 +fi + +echo "$file: OK" diff --git a/tools/splashshot.ps1 b/tools/splashshot.ps1 index 5cd3543..7546d01 100644 --- a/tools/splashshot.ps1 +++ b/tools/splashshot.ps1 @@ -10,19 +10,42 @@ # Same shape as tools/crashshot.ps1: monitor on TCP because QEMU on Windows has # no unix sockets, serial to a logfile so the transcript survives. # -# tools\splashshot.ps1 frames every 400ms for 8s -# tools\splashshot.ps1 -Every 200 -For 4 +# tools/splashshot.ps1 frames every 400ms for 8s +# tools/splashshot.ps1 -Every 200 -For 4 param( [int]$Every = 400, # milliseconds between frames [int]$For = 8, # seconds to keep capturing - [string]$OutDir = "build\shots", - [string]$Qemu = "$env:USERPROFILE\scoop\apps\qemu\current\qemu-system-x86_64.exe" + [string]$OutDir = "build/shots", + [string]$Qemu = (Find-Qemu) ) +# QEMU, on whichever host this is. +# +# PowerShell 7 runs on Linux, macOS and the BSDs as well as Windows, so these +# scripts are usable from all of them -- but the old default was a hardcoded +# scoop path under $env:USERPROFILE, which exists on exactly one of those. PATH +# first, because a package-managed qemu is on it everywhere; the scoop location +# is only a fallback for the Windows install that does not add itself to PATH. +function Find-Qemu { + $exe = if ($IsWindows -or $env:OS -eq 'Windows_NT') { 'qemu-system-x86_64.exe' } + else { 'qemu-system-x86_64' } + $onPath = Get-Command $exe -ErrorAction SilentlyContinue + if ($onPath) { return $onPath.Source } + if ($env:USERPROFILE) { + $scoop = Join-Path (Join-Path $env:USERPROFILE 'scoop/apps/qemu/current') $exe + if (Test-Path $scoop) { return $scoop } + } + foreach ($p in @('/usr/bin', '/usr/local/bin', '/opt/homebrew/bin')) { + $c = Join-Path $p $exe + if (Test-Path $c) { return $c } + } + throw "qemu-system-x86_64 not found on PATH. Install QEMU, or pass -Qemu ." +} + $ErrorActionPreference = 'Stop' $root = Split-Path $PSScriptRoot -Parent -$log = Join-Path $root 'build\splashshot.log' +$log = Join-Path $root 'build/splashshot.log' $dir = Join-Path $root $OutDir Remove-Item -Recurse -Force $dir -ErrorAction SilentlyContinue @@ -30,7 +53,11 @@ New-Item -ItemType Directory -Force $dir | Out-Null Remove-Item -Force $log -ErrorAction SilentlyContinue $hda = Join-Path $root 'schos.img' -$hdb = Join-Path $root 'build\fs.img' +# The root filesystem. SchFS is the only root there is: the kernel mounts +# device 1 in forkret() and panics if it is not a SchFS volume, so this is +# not a choice and there is no -SchFS switch any more. It used to default to +# build/fs.img, which stopped being bootable the moment SchFS became root. +$hdb = Join-Path $root 'build/schfs.img' $monPort = 55556 # not crashshot's port, so the two can run back to back @@ -42,7 +69,7 @@ $psi.Arguments = @( # developer already has open would otherwise make this fail with "Failed to # get write lock" -- and a capture script is exactly the thing you want to # run without first shutting down what you were looking at. Copying was the - # first fix and it meant duplicating build\fs.img, all 153 MB of it, on + # first fix and it meant duplicating build/fs.img, all 153 MB of it, on # every run. -snapshot keeps writes in a temporary overlay, so the originals # are untouched either way. "-drive file=`"$hda`",format=raw,index=0,media=disk,file.locking=off" diff --git a/tools/textshot.ps1 b/tools/textshot.ps1 index c6aceb0..3de8286 100644 --- a/tools/textshot.ps1 +++ b/tools/textshot.ps1 @@ -14,23 +14,48 @@ # shell. screendump writes a PPM; the PNG conversion is optional and skipped if # ImageMagick is not around. # -# tools\crashshot.ps1 crash the ramdisk-root build -# tools\crashshot.ps1 -SchFS with a SchFS volume attached +# tools/crashshot.ps1 crash the ramdisk-root build param( - [switch]$SchFS, [int]$BootSeconds = 20, - [string]$Out = "build\textshot.ppm", - [string]$Qemu = "$env:USERPROFILE\scoop\apps\qemu\current\qemu-system-x86_64.exe" + [string]$Out = "build/textshot.ppm", + [string]$Qemu = (Find-Qemu) ) +# QEMU, on whichever host this is. +# +# PowerShell 7 runs on Linux, macOS and the BSDs as well as Windows, so these +# scripts are usable from all of them -- but the old default was a hardcoded +# scoop path under $env:USERPROFILE, which exists on exactly one of those. PATH +# first, because a package-managed qemu is on it everywhere; the scoop location +# is only a fallback for the Windows install that does not add itself to PATH. +function Find-Qemu { + $exe = if ($IsWindows -or $env:OS -eq 'Windows_NT') { 'qemu-system-x86_64.exe' } + else { 'qemu-system-x86_64' } + $onPath = Get-Command $exe -ErrorAction SilentlyContinue + if ($onPath) { return $onPath.Source } + if ($env:USERPROFILE) { + $scoop = Join-Path (Join-Path $env:USERPROFILE 'scoop/apps/qemu/current') $exe + if (Test-Path $scoop) { return $scoop } + } + foreach ($p in @('/usr/bin', '/usr/local/bin', '/opt/homebrew/bin')) { + $c = Join-Path $p $exe + if (Test-Path $c) { return $c } + } + throw "qemu-system-x86_64 not found on PATH. Install QEMU, or pass -Qemu ." +} + $ErrorActionPreference = 'Stop' $root = Split-Path $PSScriptRoot -Parent $hda = Join-Path $root 'schos.img' -$hdb = if ($SchFS) { Join-Path $root 'build\schfs.img' } else { Join-Path $root 'build\fs.img' } -$log = Join-Path $root 'build\crashshot.log' +# The root filesystem. SchFS is the only root there is: the kernel mounts +# device 1 in forkret() and panics if it is not a SchFS volume, so this is +# not a choice and there is no -SchFS switch any more. It used to default to +# build/fs.img, which stopped being bootable the moment SchFS became root. +$hdb = Join-Path $root 'build/schfs.img' +$log = Join-Path $root 'build/crashshot.log' $ppm = Join-Path $root $Out -$mon = Join-Path $root 'build\qemu-mon.sock' +$mon = Join-Path $root 'build/qemu-mon.sock' Remove-Item -Force $log, $ppm -ErrorAction SilentlyContinue diff --git a/tools/ttyshot.ps1 b/tools/ttyshot.ps1 index f561b9a..a2a0483 100644 --- a/tools/ttyshot.ps1 +++ b/tools/ttyshot.ps1 @@ -7,19 +7,46 @@ # framebuffer. So the serial transcript can never tell you whether the escape # parser works -- it contains the raw bytes either way. Only a picture can. # -# tools\ttyshot.ps1 +# tools/ttyshot.ps1 param( [int]$BootSeconds = 22, - [string]$Out = "build\ttyshot.ppm", - [string]$Qemu = "$env:USERPROFILE\scoop\apps\qemu\current\qemu-system-x86_64.exe" + [string]$Out = "build/ttyshot.ppm", + [string]$Qemu = (Find-Qemu) ) +# QEMU, on whichever host this is. +# +# PowerShell 7 runs on Linux, macOS and the BSDs as well as Windows, so these +# scripts are usable from all of them -- but the old default was a hardcoded +# scoop path under $env:USERPROFILE, which exists on exactly one of those. PATH +# first, because a package-managed qemu is on it everywhere; the scoop location +# is only a fallback for the Windows install that does not add itself to PATH. +function Find-Qemu { + $exe = if ($IsWindows -or $env:OS -eq 'Windows_NT') { 'qemu-system-x86_64.exe' } + else { 'qemu-system-x86_64' } + $onPath = Get-Command $exe -ErrorAction SilentlyContinue + if ($onPath) { return $onPath.Source } + if ($env:USERPROFILE) { + $scoop = Join-Path (Join-Path $env:USERPROFILE 'scoop/apps/qemu/current') $exe + if (Test-Path $scoop) { return $scoop } + } + foreach ($p in @('/usr/bin', '/usr/local/bin', '/opt/homebrew/bin')) { + $c = Join-Path $p $exe + if (Test-Path $c) { return $c } + } + throw "qemu-system-x86_64 not found on PATH. Install QEMU, or pass -Qemu ." +} + $ErrorActionPreference = 'Stop' $root = Split-Path $PSScriptRoot -Parent $hda = Join-Path $root 'schos.img' -$hdb = Join-Path $root 'build\fs.img' -$log = Join-Path $root 'build\ttyshot.log' +# The root filesystem. SchFS is the only root there is: the kernel mounts +# device 1 in forkret() and panics if it is not a SchFS volume, so this is +# not a choice and there is no -SchFS switch any more. It used to default to +# build/fs.img, which stopped being bootable the moment SchFS became root. +$hdb = Join-Path $root 'build/schfs.img' +$log = Join-Path $root 'build/ttyshot.log' $ppm = Join-Path $root $Out $monPort = 55571