Skip to main content

Module dev

Module dev 

Source
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

BuildBuildMode::CURRENT
cargo build, cargo test, cargo runDev
cargo build --releaseRelease
a profiling profile inheriting releaseRelease
any build with the dev feature onDev
any build with -C debug-assertions=onDev

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 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.

§Load-time versus per-frame

Not every log::warn! in this workspace goes through dev_only!. The rule is where the call sits, not what category of mistake it reports: a diagnostic reachable from a redraw loop is dev_only!-gated, because at 60fps an ungated one reformats its message and grows its seen dedup table every frame the condition holds. A diagnostic reachable only from a one-time setup path, such as decoding a tileset, has neither cost to save by gating it, and it may be reporting an asset or config mistake a consumer wants to see even in a shipped build. So it stays ungated. retroglyph-window’s tileset codepoint-collision warning is the example: it fires at most once per tileset load, not once per frame.

Enums§

BuildMode
Which diagnostics this build compiles in.

Constants§

DEV
Whether this build compiles in development diagnostics: BuildMode::CURRENT as a bool.