Expand description
Which diagnostics a build compiles in. Build-mode vocabulary: which diagnostics a build compiles in.
retroglyph emits diagnostics that exist purely to shorten the debugging loop: a warning that a sprite is bigger than the cells reserved for it, a warning that a tint was set on a cell that resolved to a font glyph rather than a sprite. Each one costs something to produce (a formatted message, and usually a side table so a 60fps redraw loop reports each offender once instead of every frame), and none of it is worth anything in a shipped game, where nobody is reading the log.
BuildMode::CURRENT names which kind of build this is, and dev_only! gates a block on
it. In a release build the const is false, the branch folds away, and everything inside it
(message strings, the bookkeeping that dedupes them) is dropped as dead code.
use retroglyph_core::dev_only;
dev_only!({
if cache_misses > 0 {
// Costs nothing in a release build: neither the check nor the message survives.
report("glyphs missed the sprite cache", cache_misses);
}
});§Two modes, not three
Engines that own their whole toolchain usually expose three build modes. Flutter’s
debug/profile/release is the clearest version: debug is unoptimized with every
assertion live, profile is optimized but keeps enough instrumentation to attribute a frame
budget, and release is what ships.
Cargo has no profile mode in that sense. A profiling build is a release build that keeps
debug symbols ([profile.profiling] inherits = "release", plus debug = true), and it is
supposed to be one: measuring a build whose diagnostics differ from the shipped build
measures the wrong program. So there are two modes here, and a profiling build resolves to
Release.
That is also why the gate is written as “is this a dev build” rather than “is this not a
release build”. Flutter’s own guidance on its kReleaseMode constant is to prefer kDebugMode
or assert precisely because gating on not release is what makes a profile build behave
unlike the release build it is meant to predict.
§How a mode is chosen
| Build | BuildMode::CURRENT |
|---|---|
cargo build, cargo test, cargo run | Dev |
cargo build --release | Release |
a profiling profile inheriting release | Release |
any build with the dev feature on | Dev |
any build with -C debug-assertions=on | Dev |
The default signal is debug_assertions, which Cargo turns on for the dev profile and off
for release. It follows whichever profile the consumer built with, so a game gets
diagnostics from cargo run and none from cargo run --release without configuring anything.
The dev feature forces Dev on regardless, for an optimized build that
still reports. This is the equivalent of Unity’s “Development Build” checkbox or Bevy’s dev
feature: release codegen, because an unoptimized build of a renderer is too slow to reproduce
anything frame-dependent, but with the instrumentation left in.
§Turning diagnostics off in a dev build
There is deliberately no feature for this. Cargo features are additive, so a no-dev feature
would be silently defeated by any other crate in the graph that wanted diagnostics.
Every diagnostic in this workspace goes through the log crate, so the two working controls
are the consumer’s own log filter at runtime, and log’s max_level_* /
release_max_level_* features, which drop the calls at compile time. Those cut deeper than
this module does: they apply to every log user in the graph, not just retroglyph.
Enums§
- Build
Mode - Which diagnostics this build compiles in.
Constants§
- DEV
- Whether this build compiles in development diagnostics:
BuildMode::CURRENTas abool.