Table of Contents

CLI commands

The gbsharp command line is how a GB# project is created, checked, built, run, measured and published.

It installs as a global dotnet tool, dotnet tool install --global gbsharp, which is how these pages assume you have it. Working from a checkout instead, every command runs as dotnet run --project GBSharp.CLI -- <command> from the repository root, and gbsharp <command> on these pages is shorthand for exactly that. See installation.

Most commands take a project directory as their first argument and default it to the current directory, so gbsharp build inside a project and gbsharp build MyGame from outside it do the same thing.

gbsharp new

gbsharp new <name> [--template <template>] [--target <target>] [--out <dir>] [--force]

Creates a GB# project from a template, and one that builds: the scaffolded project compiles as written.

Argument / option Default What it does
<name> required The project name. Also the ROM name and the cartridge title.
--template, -t empty empty, sprite or background.
--target gb The machine to build for: gb for the original Game Boy or gbc for Game Boy Color.
--out, -o <name> The directory to write into.
--force off Write into a directory that is not empty.

Without --force, the command refuses a directory that already has files in it. The one thing new must never do is overwrite work.

The scaffolded project includes a .csproj so an editor can bind and analyse the code. gbsharp build never reads it (the compile set always comes from the directory and gbsharp.json), so a stale .csproj cannot produce a wrong ROM; a build reports the drift as a warning instead.

gbsharp new MyGame --template background
Created MyGame in G:\games\MyGame

  gbsharp build MyGame

gbsharp build

gbsharp build [<path>] [--emit-c] [--emit-ir] [--annotate-source] [--out <dir>] [--target <target>] [--gbdk-path <dir>] [--report-json [<file>]]

Compiles a GB# project into a ROM: parses the C#, validates it against the GB# subset, lowers it to IR, generates C, and hands that to GBDK-2020. The ROM, the linker's map and the .sym symbol file land in the output directory.

Argument / option Default What it does
<path> . The project directory.
--emit-c off Keep the generated C next to the ROM (in build/c/) so it can be inspected.
--emit-ir off Write the GB# intermediate representation alongside the ROM, as <name>.gbir.
--annotate-source off Comment every generated C statement with the C# line that produced it, and write sourcemap.json alongside the generated C. One code path produces both, so the comments and the JSON cannot disagree.
--out, -o <project>/build Output directory.
--target project setting Override the project target: gb or gbc. The override goes through the same validation as the project file's own setting.
--gbdk-path see below GBDK-2020 install root. Overrides GBDK_HOME and the vendored copy.
--report-json [<file>] off Write the build report as JSON. Given no value, it defaults to <out>/report.json.

Every build ends with the build report: target, ROM size, WRAM used, per-bank usage, cycle estimates and the call stack analysis. The report and the JSON are built once and rendered twice: the terminal used to compute its own figures alongside the JSON, and the two had already drifted. --report-json carries the same numbers unrounded, plus the GB# and GBDK versions that produced them, which is what a CI script wants to check.

A declared resource budget ([assembly: MaxWRAM(...)] and friends) is checked against what the linker actually placed, and exceeding one fails the build, but the ROM is kept. That is deliberate: a budget can only be checked once the ROM exists, and the report is how a developer finds the bytes to remove.

gbsharp build Samples/Background --emit-c
Parsing C#...        1 file
Validating GB#...
Lowering...
Generating C...
Compiling with GBDK...
Linking ROM...

GB# Build Report
────────────────────────────────

Target                    Game Boy Color
ROM                       32.0 KB
WRAM used                 63 B / 4.0 KB
Static objects (declared) 38 B

ROM Banks
  Bank 0                  2.5 KB / 16.0 KB  ███░░░░░░░░░░░░░░░░░

Generated C: Samples/Background/build/c  (2 files)

The cycle-estimate and call-stack sections of the report are explained in Profiling and cost.

gbsharp run

gbsharp run [<path>] [--emit-c] [--out <dir>] [--target <target>] [--gbdk-path <dir>] [--emulator <which>]

Builds a ROM and launches it. <path>, --emit-c, --out, --target and --gbdk-path mean what they mean for build.

--emulator names what to launch: player for the bundled GB# Player, sameboy, bgb, emulicious, mgba, or a path to an executable. The bundled Player runs a game; the catalog emulators debug one, and they load the .sym GB# writes, so naming one is a first-class choice rather than a workaround.

With no --emulator, the emulator is resolved in order: the project file's "emulator" setting, the GBSHARP_EMULATOR environment variable, a per-user emulator.txt in the GB# config directory, and then the bundled Player. The project file comes first because it is the most specific statement; the Player wins at the end on being reliably there, not on being better. Only if the Player has not been fetched are the catalog emulators searched for on PATH and in their usual install directories.

Name Emulator Picks up the .sym itself
player The bundled GB# Player No, it is what a player runs, not a debugger
sameboy SameBoy Yes
bgb BGB Yes
emulicious Emulicious Yes
mgba mGBA No

An emulator given as a path gets the plain treatment (the ROM path and nothing else), which is what every Game Boy emulator accepts. A machine-specific absolute path belongs in the per-user file or the environment variable rather than the project file: a project that only runs on the machine that wrote it is not shareable, and the failure lands on whoever cloned it.

A missing emulator has never failed a build and still does not. The ROM is the deliverable, and running it is a convenience:

gbsharp run Samples/Metasprite
Launched SameBoy
  Symbols alongside the ROM will be picked up for source-level debugging.

gbsharp profile

gbsharp profile [<path>] [--out <dir>] [--target <target>] [--gbdk-path <dir>] [--frames <n>]

Builds a ROM, runs it headlessly on the instrumented flavour of the bundled emulator, and reports where the frame budget went, in your own methods. This is the measured counterpart of the cycle estimates the build report already prints: the estimate says what a method should cost from a walk over the IR, this says what it did cost, and the two disagreeing is information rather than a bug in either.

Argument / option Default What it does
<path> . The project directory.
--frames 600 Frames to run. 60 is one second of emulated time.
--out, -o, --target, --gbdk-path as for build

Cycles are attributed to C# methods through the same symbol chain every build writes: the .sym plus <rom>.functions.json. The same run also reports coverage: the profile says what the expensive code was, coverage says what code this run never reached and so proved nothing about.

Profiling needs the instrumented emulator runtime, fetched by gbsharp doctor --fix (or tools/get-emulator.ps1 in a checkout). Nothing else in the CLI does, so this is the only command that says so when it is missing. See Profiling and cost for reading the output.

gbsharp publish

gbsharp publish [<rid>] [<path>] [--out <dir>] [--target <target>] [--gbdk-path <dir>] [--single-file]

Builds a ROM and wraps it in a standalone game that runs without an emulator. See Publishing a game for the full walkthrough.

Argument / option Default What it does
<rid> the host platform The platform to publish for: win-x64, linux-x64, linux-arm64, osx-x64, osx-arm64, or web.
<path> . The project directory.
--out, -o <project>/publish/<rid> Output directory.
--single-file off Web only: inline the runtime and the ROM into one .html that opens without a server.
--target, --gbdk-path as for build

For the native platforms, the published executable is the prebuilt GB# Player for the target with the ROM and the window settings (from gbsharp.json's "player" section) appended to it; the Player reads them out of its own file at startup. Nothing is relinked, which is why a game can be published for a platform this machine could not compile for, and why publishing takes about as long as copying a file. Signing, when it happens, has to happen after publishing: the appended bytes are part of what a signature covers. On Windows the game ships with SDL2.dll beside it.

For web, the output is a folder to upload to any static host. Browsers block module scripts and wasm fetches under file://, so that layout must be served over http, which is exactly the constraint --single-file exists to sidestep: one .html with the emulator and the ROM inlined, opening from a file manager with nothing else installed.

gbsharp publish win-x64 Samples/Metasprite
Published: Samples/Metasprite/publish/win-x64/Metasprite.exe
  124.1 KB, opens straight into the game

gbsharp clean

gbsharp clean [<path>] [--out <dir>]

Deletes a project's build output: <project>/build, or the directory named by --out. A directory that does not exist is reported rather than treated as an error.

gbsharp analyze

gbsharp analyze [<path>] [--target <target>]

Checks a project without building a ROM. This is build minus the backend: parsing, validation, lowering and asset conversion are all managed code, so it runs with no GBDK installed. That is the whole point: it makes a fast CI lint job possible, one that does not have to install a C toolchain to find out a project uses List<T>.

Diagnostics are reported exactly as a build would report them; a clean run prints how many files it found nothing wrong with. The diagnostics themselves are catalogued under Diagnostics.

gbsharp analyze MyGame
No problems found in 3 files.

gbsharp assets

gbsharp assets [<path>] [--target <target>]

Converts a project's assets and reports what they cost (tiles before and after deduplication, palettes, and bytes of ROM) without building a ROM. Like analyze, it needs no toolchain, which gives an artist working on a PNG a loop that does not involve a C compiler.

gbsharp assets MyGame
Assets
  Forest          forest.png      20x18   360 -> 9 tiles, 3 palettes          888 B

gbsharp doctor

gbsharp doctor [--gbdk-path <dir>] [--fix]

Reports the state of the GB# toolchain: the GB# and .NET versions, the framework assembly, the GBDK root, version and compiler driver, and which emulator gbsharp run would launch. When GBDK cannot be found, it lists where it looked and exits non-zero, which makes it usable as a setup check in a script. See Installation for fetching the pinned toolchain.

--fix fetches whatever is missing (GBDK-2020, the emulator runtime) into a per-user cache, checksum-verified against the pinned lock files. It is how an installed gbsharp tool acquires its toolchain on a machine with no GB# checkout; inside a checkout, the copies vendored under tools/ keep winning.

gbsharp doctor
GB# doctor
────────────────────────────────

GB# version             1.0.0.0
.NET runtime            10.0.0
Framework assembly      .../GBSharp.CLI/bin/Debug/net10.0/GBSharp.Framework.dll
GBDK root               .../tools/gbdk
GBDK version            4.5.0
Compiler driver         .../tools/gbdk/bin/lcc
Emulator                GB# Player (.../tools/emulator/bin/gbsharp-player)

Ready to build.

Locating GBDK

Every command that compiles resolves GBDK-2020 the same way: --gbdk-path if given, then the GBDK_HOME environment variable, then the vendored copy that tools/get-gbdk.ps1 fetches, then the per-user cache that gbsharp doctor --fix fills. doctor reports which one won.