Skip to content

Building

mscrnt edited this page Sep 22, 2026 · 2 revisions

Building

Two builds exist, and they are different things:

  • the native Windows build — build\win32\sightline.exe, produced by tools\windows\build.ps1. This is what you play, and what a release package contains.
  • the matching build — the original decomp compiled to a byte-identical N64 ROM with the IDO compiler, driven by make on Linux. This is the correctness oracle and is kept buildable permanently.

You do not need to build anything to play: see Releases. This page is for building from source.

The Windows workflow

Three commands and one gate, all from an ordinary Windows PowerShell prompt at the repository root:

.\tools\windows\setup.ps1     # check the toolchain (add -Install to fix it)
.\tools\windows\build.ps1     # build build\win32\sightline.exe
.\tools\windows\play.ps1      # launch the game
.\tools\windows\test.ps1      # headless Facility acceptance

setup.ps1 — what it checks and installs

setup.ps1 reports; setup.ps1 -Install installs what is missing with MSYS2's pacman. MSYS2 is used purely as a toolchain provider — nothing asks you to open an MSYS2 shell, and nothing is downloaded into the repository. If you do not have MSYS2:

winget install --id MSYS2.MSYS2
.\tools\windows\setup.ps1 -Install

Every requirement is validated by querying the executable, and the last check compiles and links a real SDL2 program and reads its PE header back, so "Toolchain satisfied" means the toolchain actually produced a 32-bit, large-address-aware image with SDL2 linked. The checks:

Check Package installed by -Install
i686-w64-mingw32-gcc (targets i686) mingw-w64-i686-gcc
binutils (as, ld, objdump, nm) mingw-w64-i686-gcc / mingw-w64-i686-binutils
pkgconf (asked for the SDL2 flags) mingw-w64-i686-pkgconf
SDL2 development files and a 32-bit SDL2.dll mingw-w64-i686-SDL2
sed (applies tools/native/aiprint.sed) sed
Python — .venv\Scripts\python.exe, or python.exe on PATH not installed; create the venv yourself with python -m venv .venv
the matching build's map, build\u\ge007.u.map see the note below — build.ps1 no longer needs it
a ROM at SL_ROM or baserom.u.z64 reported, but not a build requirement

Parameters: -Install, -Yes (do not prompt before installing), and -Msys2Root <path> (or the SL_MSYS2_ROOT environment variable) when MSYS2 is not in one of the usual places. An explicitly named root is authoritative; the script will not quietly fall back to another installation.

The linker-map input (no longer a prerequisite)

build.ps1 places the ROM-flavoured symbols and the segment windows from the matching build's linker map. Since v0.2.0 what it reads is a tracked extraction of that map, tools\native\ge007.u.linkmap.txt — symbol names and link addresses only (no ROM bytes, no game data), with its provenance (the source map's SHA-256, the commit make matching ran at, the ROM it reproduced) in its header. A fresh clone on a Windows-only machine therefore builds without ever running the matching build. SL_LINKMAP can name a raw map or another extraction for comparison; tools\native\extract_linkmap.py regenerates and verifies the extraction after a make matching.

Note that setup.ps1 in the current tree still reports the raw build\u\ge007.u.map as a check and counts it missing when absent. That line describes the old input; build.ps1 does not read it and does not require it.

build.ps1 — output

.\tools\windows\build.ps1           # incremental
.\tools\windows\build.ps1 -Clean    # delete build\win32 first

Objects and the executable land in build\win32\ (git-ignored). The script stages the non-system runtime DLLs next to the exe — read out of the PE import table rather than guessed — so a launch needs nothing on PATH. It also converts the committed model source packages under data\asset-overrides\source\ (the four boot models and the two controller models) into build\win32\data\asset-overrides\ (see Custom Boot Models).

The build needs no ROM and nothing ROM-derived: the ROM is read at run time — the audio microcode's tables included, which are derived from the player's ROM at start-up rather than generated or compiled in. The build flags (-mno-ms-bitfields, -fno-common, -Wl,--large-address-aware, -mconsole, -DSDL_MAIN_HANDLED) are each there for a measured reason and are documented in tools/windows/README.md.

Environment overrides: SL_MSYS2_ROOT, SL_PYTHON, SL_LINKMAP, SL_TRACE_DEFS (extra -D flags; changing it invalidates all objects), SL_SDL_CFLAGS and SL_SDL_LIBS. -Demo builds the private demo core (package-demo.ps1); a release is never a demo build.

test.ps1 — headless Facility acceptance

.\tools\windows\test.ps1
.\tools\windows\test.ps1 -Runs 2

Boots the level with no window, pumps a fixed number of frames, and passes only when the binary itself prints sightline native: survived <N> pumped frames and exits 0. Anything else is a failure, including a zero exit with no line. Parameters: -Level (default facility), -Frames (default 300), -Runs (default 1), -TimeoutSeconds (default 180 — a hang is a failure with evidence, not a test that never returns). It needs the ROM, and builds first if the executable is missing.

Beside it are self-tests for the native platform layer, each compiling one subsystem with its fixture and exiting 0 on success: settingstest.ps1 (the settings store), displaytest.ps1 (the aspect / viewport fit and the window-mode seam), inputtest.ps1 (the input path) and ucodetabtest.ps1 (the run-time derivation of the audio microcode tables from a ROM, and that a ROM with one changed byte is refused).

package-release.ps1 — a release package

.\tools\windows\package-release.ps1 -Version X.Y.Z -SourceDir <clean checkout> `
    -OutDir <dir> [-Rom <your ROM>] [-CanonicalTag vX.Y.Z] [-PublicTag vX.Y.Z] ...

Builds nothing and touches no remote. It takes the normal (never -Demo) build.ps1 output already in <SourceDir>\build\win32, refuses a dirty checkout, and stages exactly what a player needs — the executable, the DLLs its import table names (walked transitively with objdump), every built .slmodel, Sightline.cmd and README.txt from tools\windows\release\, the LICENSES\ tree plus the controller models' ATTRIBUTION.md files, and a generated VERSION.txt (version, commits and tags, build date, toolchain identity, build flags, the hash of every non-Git build input, the SHA-256 of every file). It then runs validate-package.ps1 on the staged directory, zips it, validates the ZIP again and writes the .sha256 file last. With -Rom the validator scans every shipped file for ROM-derived excerpts (the ROM header, the audio microcode data segment, the two tables) and a hit is a rejection that leaves no ZIP behind. See Releases for what the result looks like.

The matching build (Linux, make)

make verify-rom          # check the ROM before anything else
make venv                # Python tooling in .venv
make matching            # build tools, extract assets, build the ROM
make trace-verify        # replay every recorded trace, diff state hashes

make matching ends with MATCH! and writes build/u/ge007.u.z64, which is bit-for-bit identical to the base ROM. It also produces build/u/ge007.u.map, the raw linker map from which tools/native/ge007.u.linkmap.txt is extracted.

The environment, package list, the vendored IDO recompiler, the two-phase build (asset extraction writes source files that the main dependency graph then globs, so it runs to completion first) and the known sharp edges are in docs/toolchain.md; the upstream decomp's setup, including the Docker route, is in docs/SetupGuide.md. Keep the repository on a real Linux filesystem under WSL2 (not /mnt/c): the case-insensitive DrvFs mangles generated symbol files.

make trace-verify and the rest of the harness are described in Trace Harness.

Clone this wiki locally