# Building nuBASIC from scratch on Windows 11

A step-by-step, copy-paste guide to build nuBASIC on a clean Windows 11 machine
and end up with **self-contained executables that do not need any Visual C++
Redistributable DLLs**. This is the recommended path if a pre-built installer
fails to start (for example, a splash screen that freezes — see
[Troubleshooting](#troubleshooting)).

Everything below was verified end-to-end on Windows 11 with Visual Studio 2026.
It works the same on Visual Studio 2022 — just substitute the generator name
`Visual Studio 17 2022` wherever `Visual Studio 18 2026` appears below.

> ### Architecture: there is no "universal" Windows binary
> Unlike macOS, Windows executables are **not** universal/fat binaries. Each
> `.exe` targets exactly **one** CPU architecture:
> - **x64** — normal Intel/AMD PCs. This is what almost everyone needs.
> - **ARM64** — Snapdragon / Windows-on-ARM machines (e.g. Surface Pro X,
>   recent Copilot+ PCs).
>
> An ARM64 `.exe` will **not** run natively on an x64 PC, and by default CMake
> builds for the **machine you are building on**. So if you build on an ARM64
> laptop and hand the result to a friend with an Intel PC, it won't start.
> **Pick the target explicitly** with the `-A` flag (step 4) — an ARM64 host
> can cross-compile x64 and vice-versa, because the Visual Studio C++ toolset
> ships both cross-compilers. When in doubt, build **x64**.

---

## 1. Install the prerequisites

| Tool | Notes |
| ---- | ----- |
| **Visual Studio 2022 or 2026 — "Desktop development with C++" workload** | This one workload provides the MSVC compiler **and** bundles CMake + Ninja, so you do **not** need a separate CMake install. Community edition is free: <https://visualstudio.microsoft.com>. |
| **Git** | <https://git-scm.com>. |
| Node.js 18 LTS+ *(optional)* | Only needed to build the VS Code extension. `winget install OpenJS.NodeJS.LTS` |
| VS Code *(optional)* | Only if you want the VS Code extension. |

> You do **not** need WiX, NSIS, or vcpkg for a working interpreter + IDE.
> Those are only for building the MSI installer / enabling native `Declare`
> calls (see the optional sections).

## 2. Open the right shell

Use the **Developer PowerShell for VS 2026** (or 2022) from the Start menu — do
**not** use a plain PowerShell window. The developer shell puts `cmake`,
`ninja`, and the MSVC compiler on `PATH` automatically. Verify:

```powershell
cmake --version   # should print e.g. "cmake version 4.1.x-msvc1"
cl                # should print the Microsoft C/C++ compiler banner
```

## 3. Clone the repository

```powershell
cd $HOME\Documents
git clone https://github.com/eantcal/nubasic.git
cd nubasic
```

## 4. Configure — static runtime + explicit architecture

This is the important part. `NUBASIC_STATIC_RUNTIME=ON` static-links the MSVC
C/C++ runtime (`/MT`), so the resulting `.exe` files embed the runtime and start
on a clean machine **without** `vcruntime140.dll`, `msvcp140.dll`,
`concrt140.dll`, etc. `-A x64` pins the target architecture (see the box at the
top — do not rely on the default).

**Recommended (x64, self-contained, with the WinRaycast engine):**

```powershell
cmake -S . -B _generated\build\release-static-x64 `
  -G "Visual Studio 18 2026" -A x64 `
  -DCMAKE_BUILD_TYPE=Release -DNUBASIC_WITH_RAYCAST=ON -DNUBASIC_STATIC_RUNTIME=ON
```

For an **ARM64** machine, change `-A x64` to `-A ARM64` and the build directory
name to match (e.g. `release-static-arm64`).

There are also convenience CMake presets when you just want to target the host
architecture:

| Preset | Raycaster | MSVC runtime | Notes |
| ------ | --------- | ------------ | ----- |
| `raycast-release-static` | on | **static (/MT)** | self-contained + WinRaycast, host arch |
| `release-static` | (default) | **static (/MT)** | self-contained, host arch |
| `raycast-release` | on | dynamic (/MD) | needs VC++ redistributable installed |
| `release` | (default) | dynamic (/MD) | needs VC++ redistributable installed |

```powershell
# host-architecture shortcut equivalent to the recommended command above
cmake --preset raycast-release-static
```

> The presets build for whatever architecture you are on. To build **x64 on an
> ARM64 machine** (or vice-versa), use the explicit `-A` command above instead
> of a preset.

The first configure downloads Scintilla, Lexilla, googletest and nlohmann/json
via CMake FetchContent, so it takes a couple of minutes. A harmless
`libffi not found` warning is expected (see [native calls](#optional-native-declare-calls-libffi--vcpkg)).

In the configure output you should see the line
`MSVC runtime: static (/MT) — redistributable-free binaries`, which confirms the
static runtime is active.

## 5. Build

Use the same build directory you configured in step 4 (the examples below use
the recommended x64 directory `_generated\build\release-static-x64`).

```powershell
# Core interpreter, GDI graphics console, and VS Code debug backend
cmake --build _generated\build\release-static-x64 --target nuBasicCLI nuBasicGDI nuBasicDebugCLI --config Release

# Optional: the Scintilla-based desktop IDE
cmake --build _generated\build\release-static-x64 --target nuBasicIDE --config Release
```

The executables land here:

| File | Path (under your build dir) | What it is |
| ---- | --- | ---------- |
| `nubasic.exe` | `cli\win\Release\` | interpreter / REPL (console) |
| `nubasicgdi.exe` | `cli\win\Release\` | interpreter with a GDI graphics window |
| `nubasicdebug.exe` | `cli\win\Release\` | debug backend for the VS Code extension |
| `NuBasicIDE.exe` | `ide\win\Release\` | desktop IDE (needs `SciLexer.dll`, copied next to it automatically) |

## 6. Run "Hello, world"

```powershell
cd _generated\build\release-static-x64\cli\win\Release
".\nubasic.exe" --version
```

Create `hello.bas` **as UTF-8 without a BOM** (this matters — see the gotcha
below) and run it:

```powershell
# This writes the file WITHOUT a byte-order mark:
[System.IO.File]::WriteAllText("$PWD\hello.bas", "Print ""Hello, world!""`r`n", (New-Object System.Text.UTF8Encoding($false)))
".\nubasic.exe" -t hello.bas
```

Expected output: `Hello, world!`

> ### ⚠️ The UTF-8 BOM gotcha
> If you create a `.bas` file with `Set-Content -Encoding utf8`, Notepad's
> default "UTF-8 with BOM", or similar, Windows prepends an invisible byte-order
> mark. nuBASIC reads it as part of the first token and you get:
> `Runtime Error #4 ... print procedure not defined`.
> **Save `.bas` files as UTF-8 *without* BOM** (Notepad: *Save as → Encoding:
> UTF-8*, not "UTF-8 with BOM"; VS Code: bottom-right encoding selector).

## 7. Run the test suite (optional but recommended)

```powershell
cd $HOME\Documents\nubasic
.\tests\run_tests.ps1 -Interpreter .\_generated\build\release-static-x64\cli\win\Release\nubasic.exe
```

A healthy run reports the language suites passing. With this build the only
"failures" you should see are the four `test_native_*` suites — they require
libffi, which is off unless you install vcpkg (see below). Fourteen POSIX-only
suites are skipped on Windows by design.

---

## Optional: the VS Code extension

The extension gives you syntax highlighting plus an F5 debugger (breakpoints,
step in/over/out, watch, call stack) backed by `nubasicdebug.exe`.

```powershell
# Build the .vsix (requires Node.js on PATH)
cmake --build _generated\build\release-static-x64 --target VscodeExtensionBuild --config Release

# Install it into VS Code
code --install-extension vscode-nubasic\nubasic-latest.vsix --force
```

Then tell the extension where your interpreter is. Open VS Code **Settings
(JSON)** and add — pointing at the `nubasic.exe` you built (the extension finds
`nubasicdebug.exe` next to it automatically):

```json
{
  "nubasic.executablePath": "C:/Users/<you>/Documents/nubasic/_generated/build/release-static-x64/cli/win/Release/nubasic.exe"
}
```

Open a `.bas` file and press **F5** to debug or **Ctrl+F5** to run.

> **Version-match matters.** The extension talks to `nubasicdebug.exe` over a
> `--machine-interface` protocol and expects it to *stop on entry*. If you point
> `executablePath` at an old installed copy while using a newer extension (or
> vice-versa), debugging can appear to hang. Always point the extension at the
> `nubasicdebug.exe` from the **same build**. Building both from this repo, as
> above, keeps them in sync.

---

## Optional: native `Declare` calls (libffi + vcpkg)

nuBASIC can call into native DLLs (`Declare Function ... Lib "kernel32.dll"`).
That needs **libffi**, which on Windows comes from **vcpkg**. Skip this unless
you need native calls — everything else works without it.

```powershell
git clone https://github.com/microsoft/vcpkg C:\vcpkg
C:\vcpkg\bootstrap-vcpkg.bat
C:\vcpkg\vcpkg install libffi:x64-windows      # use arm64-windows on ARM64
$env:VCPKG_ROOT = "C:\vcpkg"
```

Re-run the configure (step 4) with `VCPKG_ROOT` set; CMake will detect libffi
and enable native invocation. Note that libffi ships as `ffi-8.dll`, which must
sit next to the executables — one more reason the static-runtime build is the
simplest to distribute when you don't need native calls.

---

## Troubleshooting

### Symptom: the app freezes on a splash screen, or complains about missing `vcruntime140.dll` / `msvcp140.dll`

**Cause:** the binaries were built against the *dynamic* MSVC runtime (`/MD`)
and the machine has no Visual C++ Redistributable installed. On a clean Windows
box the process can't load its runtime DLLs and dies before (or at) the splash.

**Fix — this whole guide.** Build with a `*-static` preset (step 4). The
`/MT` runtime is baked into each `.exe`, so there are no VC++ DLLs to be
missing. Confirm a finished build has no runtime-DLL imports:

```powershell
dumpbin /dependents .\_generated\build\release-static-x64\cli\win\Release\nubasic.exe
```

You should **not** see `vcruntime140.dll`, `vcruntime140_1.dll`, `msvcp140.dll`,
or `concrt140.dll` in the list — only core OS DLLs like `KERNEL32.dll`,
`USER32.dll`, `GDI32.dll`, `gdiplus.dll`.

*(Alternative without rebuilding: install the Microsoft Visual C++ 2015–2022
Redistributable for the machine's architecture — but static-linking removes the
dependency for good.)*

### Symptom: "This app can't run on your PC" / the exe does nothing

The binary is the **wrong architecture** for the machine (e.g. an ARM64 build on
an Intel PC). Check what a binary actually is:

```powershell
dumpbin /headers .\nubasic.exe | Select-String "machine"
#   8664 machine (x64)     -> Intel/AMD PC
#   AA64 machine (ARM64)   -> Windows-on-ARM PC
```

Rebuild for the correct architecture with the right `-A` flag (step 4).

### The desktop IDE (`NuBasicIDE.exe`) needs `SciLexer.dll`

`SciLexer.dll` is nuBASIC's own Scintilla component (not a system DLL). The
build copies it next to `NuBasicIDE.exe` automatically. If you move the IDE
`.exe`, take `SciLexer.dll` with it.

### `Runtime Error #4 ... procedure not defined` on the first line

Your `.bas` file has a UTF-8 BOM. Re-save it as UTF-8 *without* BOM (see the
gotcha in step 6).

### Deeper installer diagnostics

For MSI-install failures specifically, the repo ships a diagnostic collector and
checklist — see [`windows-installer-diagnostics.md`](windows-installer-diagnostics.md)
and [`windows-runtime-dependencies.md`](windows-runtime-dependencies.md).

---

## Giving a working copy to someone else (no build required)

Because the `*-static` **x64** build has no external runtime dependencies, the
simplest way to help someone whose installer won't start is to just hand them
the built binaries — no installer, no redistributable, no admin rights:

1. Build the **x64 static** configuration (steps 4–5).
2. Zip up the `Release` folder(s):
   - `…\release-static-x64\cli\win\Release\` → `nubasic.exe`, `nubasicgdi.exe`,
     `nubasicdebug.exe`
   - `…\release-static-x64\ide\win\Release\` → `NuBasicIDE.exe` **and**
     `SciLexer.dll` (keep these two together)
3. Send the zip. They unzip anywhere and double-click `NuBasicIDE.exe`, or run
   `nubasic.exe` from a terminal. Nothing to install.

Make sure it's the **x64** build unless you know their machine is ARM64 — see
the architecture note at the top.
