bsn-smoke — Bevy 0.20 generated-code compile check
bsn-smoke is Bevy Forge's proof that exported scenes are real Bevy 0.20 code, not
just plausible-looking text. It is the only crate in this repository that depends on
Bevy; it is excluded from the workspace (Cargo.toml → exclude = ["compat/bsn-smoke"])
so the editor, core and CLI crates stay Bevy-free and build in seconds.
compat/bsn-smoke/
├── Cargo.toml standalone package (empty [workspace]), pinned Bevy 0.20.0
├── Cargo.lock lockfile for the verification dependency graph
├── src/lib.rs hand-written scenes mirroring every codegen shape + support types
├── src/generated.rs BSN export of the 3D garden demo (written by the gate script)
├── src/generated_2d.rs BSN export of the 2D platformer demo (written by the gate script)
└── src/rust_export.rs imperative `Commands::spawn` export shape, also compiled
What is verified
Compile-checked against bevy 0.20.0 (default-features = false):
- Roots: single root
bsn!/-> impl Scene; multiple rootsbsn_list!/-> impl SceneListwith--separators. - Hierarchy: nested
Children [ ... -- ... ], including aVisibilitypatch on a grandchild and components before/afterChildren. - Names and transforms:
Name("..."), full three-fieldTransformwithQuat::from_euler(EulerRot::XYZ, x.to_radians(), ...). - Sprites: image path handles,
Color::srgba,flip_x/flip_y, and bracedcustom_size: { Some(Vec2::new(w, h)) }(the brace form is mandatory in BSN patches). - Text:
Text2d,TextFont { font_size: FontSize::Px(..) },TextColor. - Cameras: bare
Camera2d,Camera3d::default(). - Lights:
PointLight(color/intensity/range/shadows) andDirectionalLight(color/illuminance/shadows). - Custom components: struct-with-fields patches (
Health { current: 10.0, max: 10.0 },forge_demo::Orbit { radius: 2.4, speed: 0.45 }) and bare unit-component patches (Marker). - 3D meshes: all five primitive shapes (
Cuboid,Sphere,Plane3d,Cylinder,Capsule3d) with braced method-call handles (Mesh3d({ meshes.add(..) })) and inline materials (MeshMaterial3d::<StandardMaterial>({ materials.add(StandardMaterial { ..default() }) })), plus an asset mesh path (Mesh3d("models/...#Mesh0/Primitive0")) with an inline material. - Generated signatures: mesh scenes take
&mut Assets<Mesh>and&mut Assets<StandardMaterial>only when needed. - Imperative Rust export shape (
src/rust_export.rs):commands.spawn,with_children,Name::new,Transform::from_xyz(..).with_rotation(..).with_scale(..),Sprite { ..default() },Mesh3d/MeshMaterial3d,Text2d::new,TextFont,TextColor,Camera3d::default(), lights andVisibility.
The committed src/generated*.rs files are the CLI exports produced by the last
successful gate run (plus the shim described below), so the crate compiles on a fresh
checkout even before the script runs. scripts/check-generated-code.sh regenerates them
from the demo project and recompiles, verifying the generator + demo scenes end to end.
The exports are deterministic, so re-running the gate leaves the files unchanged.
Compatibility shim for custom type paths
Generated BSN references custom components by their project type path, e.g.
forge_demo::Orbit. In a real Bevy project the generated function is pasted where that
path is already in scope. Here the generated file is a module, so the gate script prepends
four lines (#![allow(unused_imports)], two comments, and use crate::*;) to expose the
crate-root support types; the generated code itself is appended unchanged. lib.rs
provides forge_demo::Orbit with the same fields as examples/demo-project/src/main.rs.
If a demo scene introduces another custom type, add its support type to lib.rs.
Bevy version and feature set
Pinned to Bevy 0.20.0 through Cargo.lock. Feature set (chosen by
.swarm/recon/R02.md to keep compile time and APIs aligned with the verified BSN forms):
bevy = { version = "0.20", default-features = false, features = [
"std", "bevy_scene", "bevy_sprite", "bevy_asset", "bevy_color", "bevy_text",
"bevy_pbr", "bevy_mesh", "bevy_camera", "bevy_light", "bevy_image",
] }
No windowing or audio backends are compiled in and no GPU device is initialized: this is a
compile check that needs no display, asset files or App.
Running
Directly (the load-bearing check):
cd compat/bsn-smoke
CARGO_NET_OFFLINE=true cargo check
End to end (generator → CLI → Bevy compile):
bash scripts/check-generated-code.sh
The script exports examples/demo-project/scenes/garden.forge.ron and
.../platformer.forge.ron with cargo run -q -p bevy_forge_cli -- export ... --format bsn,
writes the results into this crate, and runs the check. It fails loudly on any error; if
the CLI or demo project does not exist yet it fails with a clear message instead of
silently passing. FORGE_SKIP_GENERATED=1 skips with an explicit message (bootstrap only);
FORGE_SKIP_SECOND_SCENE=1 skips the 2D export; FORGE_GARDEN_SCENE /
FORGE_PLATFORMER_SCENE override the scene paths; CARGO_TARGET_DIR is passed through.
Expected cost
Measured on this Linux machine (Rust 1.99.1, 2026-10):
| Build | Wall time | target/ size |
|---|---|---|
First cargo check, cold target dir |
2 min 15 s | ~803 MB |
Subsequent cargo check, warm target dir |
< 1 s | — |
Reusing an existing Bevy 0.20 target/ (e.g. recon) |
~1 s | — |
compat/bsn-smoke/target/ is ignored by .gitignore; never commit it. If you already
have a warm Bevy 0.20 target directory, point CARGO_TARGET_DIR at it to avoid the cold
build (the dependency feature set must match exactly).
Runtime caveats (compile ≠ loaded assets)
- Asset paths (
Sprite { image: "textures/x.png" },Mesh3d("models/...gltf#...")) compile, but Bevy turns them intoHandleTemplate::Pathvalues that are resolved by theAssetServerat spawn time. If the project has no loader/asset for the path, the handle stays loading/failed and nothing renders — it is not a compile error (see R02 §9). - Bevy Forge never emits material asset paths (
MeshMaterial3d::<StandardMaterial>("...")), because those need a project-registered material loader; materials are always inlinematerials.add(StandardMaterial { ..default() })handles. - The check is compile-only: it does not spawn the scenes or validate asset existence.
Asset references are validated separately by
forge validate/ the editor.