Building from a Checkout
Clone the Renzora engine workspace and build the editor, the game runtime, or every export target.
Build for your own machine with cargo renzora. It's an ordinary Cargo build — no Docker, no images, no container. Use Docker when you need to cross-compile: producing export templates for platforms you don't own is the job those toolchain images exist to do.
Why the split. The host binary, the editor bundle and every installed plugin must share one compiled copy of Bevy and the
renzoracontract crate. Building everything from source in a single environment gives you that by construction, which is exactly whatcargo renzoradoes — andrust-toolchain.tomlpins the same rustc the images use. Docker guarantees the same thing across different machines, which is what makes it the right tool for release artefacts, and the wrong one for a local build.Plugins sidestep the question: they ship as source and are compiled on the machine that installs them, against an SDK cut from the engine sitting right there. There is no build environment to match because the plugin is always built in the one it will run in.
What you're building
Renzora is a single Bevy 0.20 Cargo workspace with exactly one binary: renzora_app, which produces renzora (renzora.exe on Windows) from src/main.rs. That one binary is the engine: editor, game runtime, and dedicated server in one. The editor is not a compile-time feature: it ships as a removable cdylib, renzora_editor (renzora_editor.dll / librenzora_editor.so / .dylib), that the binary dlopens from beside itself at startup.
- Bundle present beside the exe → the binary launches as the editor.
- Delete that one file (or pass
--no-editor) → the same binary is the shipped game.
Two things share the name
renzora. The crate in this workspace (crates/renzora) is the SDK library (crate-type = ["dylib", "rlib"]) — installing it produces nothing runnable. TherenzoraCLI (cargo install renzora, a separate published crate) scaffolds projects and drives the Docker image below; most people start with it (renzora new→renzora run). This page covers building a checkout you already have with that same CLI.
Prerequisites
- Git — to clone the engine.
- Rust — via rustup.
rust-toolchain.tomlpins the version, so rustup installs and selects the right one on first build. - Your platform's usual native build dependencies: a C/C++ toolchain, and on Linux the X11/Wayland/ALSA/udev dev headers. The full list mirrors
docker/base/Dockerfile.
Docker is needed only for cross-compiling and for running the test suite on Windows.
The pinned Rust version lives in two lockstep files:
rust-toolchain.toml(native) anddocker/base/Dockerfile(FROM rust:1.97.1-bookworm, container). They must move together. Bevy 0.20 needs at least Rust 1.97.1.
Clone and run
git clone https://github.com/renzora/engine.git
cd engine
cargo renzora # build the workspace, stage dist/, and launch the editor
That's the whole flow. cargo renzora is an xtask that compiles the workspace, arranges dist/<platform>/ exactly the way the container's build-all.sh does — bevy_dylib and renzora beside the exe, the editor bundle beside them, plugins in plugins/ — and then launches it. cargo renzora dist stages without launching.
The first build takes several minutes (Bevy is large); subsequent builds are incremental.
Everyday commands
cargo renzora | build, stage, run the editor |
cargo renzora dist | build and stage without launching |
cargo check | fast compile check while editing — doesn't link |
cargo clippy | reproduces the CI lint gate exactly |
renzora test | the test suite (see the caveat below) |
cargo testdoes not link natively on Windows. The test harness pushes therenzoradylib past the PE format's 65,535 exported-symbol ceiling and the linker refuses. It's a limit of the executable format, not something to configure around. Runrenzora test, which builds in the Linux container where no such table exists.cargo checkandcargo clippyare unaffected and run natively everywhere.
| Command | What it does |
|---|---|
cargo renzora | Build the workspace, stage dist/<platform>/, and launch the editor |
cargo renzora dist | Same build + stage, but don't launch — just produce the folder |
cargo renzora -- --no-editor | Build + stage + launch in shipped-game mode (args after the binary are forwarded) |
Why not just cargo run? A bare cargo run compiles everything but leaves the shared images (bevy_dylib, renzora_dylib, renzora_ember_dylib) and the editor image flat in target/dist/, while the binary looks for them beside itself. cargo renzora adds the one missing step — staging the artifacts into the runnable dist/ layout, exactly like the container's build-all.sh — and cuts the plugin SDK at the same time. The staging lives in the xtask/ crate and runs on plain cargo (the renzora cargo alias points at it).
What native does not do: cross-compile. cargo renzora only ever produces artifacts for the machine it runs on. For Windows/macOS/Linux/wasm/mobile builds from one host, use Docker (renzora build, below).
The renzora CLI — the build interface
The CLI is the canonical way to build and run a checkout. Each command runs cargo inside the container against the custom dist profile (inherits = "release", opt-level = 2, strip = "symbols") so that plugin ABI hashes stay consistent across every build and machine.
| Command | What it builds / runs |
|---|---|
renzora run | Build the workspace and run the editor |
renzora run runtime | Run the shipped-game shape (same binary, --no-editor) |
renzora run -- --server | Run a headless dedicated server |
renzora build [platforms...] | Cross-build the binary + editor bundle + shared bevy_dylib (no args = all platforms) |
renzora add <name> [--editor] | Scaffold an in-workspace plugin crate |
renzora remove <name> | Delete a plugin crate and unregister it |
renzora test / renzora check | Reproduce the CI test + clippy jobs |
renzora shell | Open a shell inside the build container |
Under the hood the CLI maps to
cargoinvocations on thedistprofile inside the container — e.g. the editor isrun --profile dist --workspace --bin renzora, the lean runtime isbuild --profile dist --bin renzora(deliberately not--workspace, so editor-only crates never enter the build graph). You never run these natively; the CLI runs them in the image for you.
Runtime modes
The same binary picks its mode at launch by flag — there are no separate "editor" / "server" builds:
| Flag | Mode |
|---|---|
| (none) | Editor if the bundle dll is present, otherwise the shipped game |
--no-editor (or RENZORA_NO_EDITOR) | Force the shipped-game runtime |
--server | Headless dedicated server (no window, no GPU) |
--host | Windowed listen server (client + server in one process; wins over --server) |
A --server/--host launch is never an editor session even if the bundle dll is present. Server flags --port, --addr/--address, --tick-rate, and --max-clients overlay the project's [network] settings; --project <path> and --rpak <path> are also recognized. The dedicated server is the same renzora binary, not a separate executable.
How the shared-library build works
The host binary, the dlopened editor image, and every installed plugin must share one compiled copy of Bevy and of the renzora contract crate so their TypeIds match across the dlopen boundary. The root manifest's default dynamic_linking feature arranges this:
bevyships as a singlebevy_dylib-<hash>shared library.renzoraandrenzora_embership throughrenzora_dylibandrenzora_ember_dylib, so their process-global state (the translation table, the Console buffers, the theme palette) is singular.- In-workspace plugins are plain rlibs statically linked into the binary; installed plugins are
dylibs compiled from source against the staged SDK and loaded fromplugins/.
Because the binary links these by name, the .dll/.so/.dylib files must travel beside the binary (Linux/macOS use an rpath of $ORIGIN / @loader_path; on Windows they sit in the same folder).
Windows uses
rust-lldas the linker (MSVClink.exehits the 65535-object limit onbevy_dylib).crt-staticis enabled, becausevcruntime140.dll/msvcp140.dllare not part of Windows: they come with the Visual C++ Redistributable, so a dynamically linked build runs on any machine that has ever installed Visual Studio or a game, and fails beforemainon a freshly installed one. Linking the CRT in removes the prerequisite.
build.rs
The root build.rs emits two environment values used by the dynamic-plugin ABI guard:
RENZORA_ENGINE_VERSION— the package version.RENZORA_BUILD_HASH— an FNV-1a hash of"<version>-<rustc version>-bevy0.19". The loader rejects any plugin whose hash differs, so a plugin built against a different compiler or engine version is refused rather than crashing. (Building everyone in the same image is what keeps this hash equal across machines.)
It also embeds the Windows icon/version resource (via winres on a Windows host, or a hand-written .rc + llvm-rc when cross-compiling Linux→Windows-MSVC) and re-emits the static zstd link directive.
Cross-compiling for other platforms
This is what Docker is for. Shipping a game means producing builds for machines you don't have — a macOS bundle from a Windows box, an Android APK from Linux. Each target needs its own compiler, linker and SDK, and installing six of those by hand is exactly the problem a container solves. Nothing on this page before this point needed Docker; everything after it does.
cargo install renzora # the CLI that drives the images
renzora init # pull the toolchain images
Every cross-platform target builds inside the engine's Docker toolchain, split into a shared base plus one image per platform (all under ghcr.io/renzora/*). The base (docker/base/Dockerfile, FROM rust:1.97.1-bookworm) is the single source of truth for the Rust version and carries the linux-gnu targets + mold/clang/lld linkers + LLVM-19; each platform image builds FROM it and adds its cross toolchain: windows (xwin/MSVC), macos & ios (osxcross + SDKs), android (NDK r27c), wasm (wasm-bindgen + binaryen), linux (dual-arch cross-gcc + appimagetool + UPX). The host only needs Docker, and the CLI pulls just the images a command needs; the GPU editor/game still runs natively from dist/.
# Build specific platforms into ./dist
renzora build windows linux
# Build everything the container can produce
renzora build
renzora build [platform ...] (no args = all) accepts these platform tokens:
| Token | Output directory |
|---|---|
linux (= linux-x64 + linux-arm64) | dist/linux-x64/, dist/linux-arm64/ |
linux-x64 / linux-arm64 | the individual Linux dir |
windows | dist/windows-x64/ |
macos (= macos-x64 + macos-arm64) | dist/macos-x64/, dist/macos-arm64/ |
macos-x64 / macos-arm64 | the individual macOS dir |
wasm | dist/web-wasm32/ |
android (= android-arm64 + android-x86) | dist/android-arm64/, dist/android-x86/ |
ios | dist/ios-arm64/ |
Desktop targets place the binary and its shared libraries directly in the platform dir; wasm and mobile targets nest their output under a runtime/ subdirectory.
Notes: macOS lanes build only when osxcross is present; the Android and iOS lanes are best-effort (a failure there does not fail the whole build), as is the non-host Linux arch. The web build is game-runtime only — there is no WebAssembly editor (the binary has no
editorcompile feature, and the editor bundle is a desktop dlopen target). On Linux the editor build is additionally wrapped into an AppDir and.AppImagewhenappimagetoolis available.
windows-arm64is missing from that list on purpose — the container cannot produce it. Building for Windows on ARM needs the real MSVC toolchain, which is not redistributable, so that slice is built natively (on the machine, or by thewindows-arm64job in.github/workflows/build-engine.yml). See Cross-compilation.
Plugin scaffolding
The CLI creates and removes plugin crates with the right Cargo.toml wiring:
renzora add cool_fx # statically-linked engine plugin (Runtime scope)
renzora add cool_fx --editor # editor-only plugin (Editor scope, optional dep)
renzora remove cool_fx # delete the crate and unregister it
Both scaffold an in-workspace plugin: an rlib baked into the host binary, wired in by the generator that reads its add! line. To build an installable plugin instead, see Native Plugins — those need no engine checkout and live outside this repository.
Compressing binaries with UPX
renzora upx # compress every platform under dist/
renzora upx dist/windows-x64 # just one platform
This runs upx --brute (slowest, smallest) over the host binary, the SDK dylibs (renzora, renzora_editor), bevy_dylib, and everything in plugins/. The wasm and ios outputs (.wasm / .a) are not UPX-compressible and are skipped.
This command packs the engine's own dist/ artefacts. To pack a game the same way, tick Compress binary with UPX in the export dialog's Compression tab — it runs the same packer over the exported executable and its libraries, at --best --lzma rather than --brute so an export doesn't take an hour. See Exporting → overview.
What is NOT in this repo
A few things that live outside this workspace, or that older docs got wrong:
| Often referenced | Reality |
|---|---|
The renzora CLI source | The CLI (cargo install renzora) is real, but its source is a separate published crate, not this workspace. Its commands drive the docker/ toolchain: build/add/remove/upx wrap the docker/*.sh scripts here; new/init/run/test/check/shell/clean/destroy are CLI-level (container lifecycle + cargo wrappers that run inside the image). |
A native cargo run / cargo build build | A bare cargo run compiles but does not stage, so the shared images and the editor image are not beside the binary and the plugin SDK is never cut. For a complete native build use cargo renzora (see Building natively); for cross-platform use renzora build. |
rust-toolchain.toml | Exists — it pins the Rust version for native builds. The container's version lives in docker/base/Dockerfile; the two are kept in lockstep. |
Makefile.toml / cargo-make (makers ...) | No Makefile.toml / cargo-make — the old makers staging was replaced by the xtask crate behind cargo renzora (no extra install). Cross-platform/release still go through the renzora CLI + docker/ scripts. |
| A separate dedicated-server binary | Gone — the server is the same renzora binary launched with --server. |
An editor compile-time feature / separate editor binary | Removed — the editor is the removable renzora_editor cdylib bundle. |
| tvOS / Apple TV target | Aspirational only — no tvOS toolchain in the image and no build-all.sh lane. |
What's next?
- Project Structure — how the workspace and its ~187 crates are laid out
- Architecture — the one-binary, editor-as-removable-cdylib model in depth
- Building Plugins — extend the engine with
renzora::add! - Building Export Templates — produce shippable game builds