A crate built on top of benilla now adds its own Bevy plugins through `benilla_app::run_with(build, extend)`: `extend` gets the client fully built, after the game plugins, the asset loaders and the `ExtractSchedule` executor flip, and before the probe fleet, which sees what it adds. `run` is `run_with` with nothing added, so the stock client is unchanged. - A client started through `run_with` carries `extended` in its build id (`BuildId::summary`), so the startup banner, the crash report and the debug panel's footer all say so, and a report from an extended client reads apart from stock. The entry point sets the flag; a launcher leaves it to `..Default::default()`. - `crates/benilla-app/examples/extended_launcher.rs` is a launcher with a plugin of its own, compiled by the gates. Its header is the shape a crate of its own copies: the manifest (benilla-app without default features and `dev` forwarded, the workspace's bevy, and the root's `[patch]` and `[profile.dev]`, which cargo reads from the root manifest only), the build script's stamp, and a `Cargo.lock` started from benilla's. - `tests/launcher_shape.rs` holds that documented manifest to the root `Cargo.toml` and the `benilla` launcher's, so a patch or profile added later cannot leave the documented shape building a client that is not benilla's. - `docs/CONTRIBUTING.md`: a feature 1.12.1 does not have lives in its own crate on top of benilla, started through `run_with`. Fixes #483 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
95 lines
3.3 KiB
Rust
95 lines
3.3 KiB
Rust
//! The launcher `examples/extended_launcher.rs` documents for a crate on top of benilla stays in
|
|
//! step with the workspace: cargo reads `[patch]` and `[profile]` from the root manifest only, so
|
|
//! the header repeats the root's, a missing patch builds a client that is silently not benilla's,
|
|
//! and a bevy other than the workspace's is a second bevy. The rest mirrors the `benilla` launcher.
|
|
|
|
use std::path::Path;
|
|
|
|
/// A manifest by its path from this crate's root.
|
|
fn manifest(rel: &str) -> toml::Table {
|
|
let path = Path::new(env!("CARGO_MANIFEST_DIR")).join(rel);
|
|
std::fs::read_to_string(&path)
|
|
.unwrap_or_else(|e| panic!("{}: {e}", path.display()))
|
|
.parse()
|
|
.unwrap_or_else(|e| panic!("{}: {e}", path.display()))
|
|
}
|
|
|
|
/// The example header's ```toml block, parsed.
|
|
fn documented() -> toml::Table {
|
|
let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("examples/extended_launcher.rs");
|
|
let text = std::fs::read_to_string(&path).unwrap();
|
|
let block: Vec<&str> = text
|
|
.lines()
|
|
.map(|l| {
|
|
l.strip_prefix("//!")
|
|
.map(|l| l.strip_prefix(' ').unwrap_or(l))
|
|
})
|
|
.skip_while(|l| *l != Some("```toml"))
|
|
.skip(1)
|
|
.take_while(|l| *l != Some("```"))
|
|
.map(|l| l.expect("the ```toml block ends inside the header"))
|
|
.collect();
|
|
assert!(!block.is_empty(), "no ```toml block in {}", path.display());
|
|
block
|
|
.join("\n")
|
|
.parse()
|
|
.expect("the documented manifest parses")
|
|
}
|
|
|
|
/// `t[k1][k2]…`, or a panic naming the path.
|
|
fn at<'a>(t: &'a toml::Table, keys: &[&str]) -> &'a toml::Value {
|
|
let (first, rest) = keys.split_first().unwrap();
|
|
let mut v = t.get(*first).unwrap_or_else(|| panic!("no `{first}`"));
|
|
for k in rest {
|
|
v = v
|
|
.get(*k)
|
|
.unwrap_or_else(|| panic!("no `{}`", keys.join(".")));
|
|
}
|
|
v
|
|
}
|
|
|
|
#[test]
|
|
fn the_documented_launcher_repeats_the_root_patches_profile_and_bevy() {
|
|
let doc = documented();
|
|
let root = manifest("../../Cargo.toml");
|
|
let patched = |t: &toml::Table| -> Vec<String> {
|
|
let table = at(t, &["patch", "crates-io"]).as_table().unwrap();
|
|
table.keys().cloned().collect()
|
|
};
|
|
assert_eq!(patched(&doc), patched(&root), "[patch.crates-io]");
|
|
for keys in [
|
|
&["profile", "dev", "opt-level"][..],
|
|
&["profile", "dev", "package", "*", "opt-level"],
|
|
] {
|
|
assert_eq!(at(&doc, keys), at(&root, keys), "{}", keys.join("."));
|
|
}
|
|
assert_eq!(
|
|
at(&doc, &["dependencies", "bevy", "version"]),
|
|
at(&root, &["workspace", "dependencies", "bevy", "version"]),
|
|
"bevy's version"
|
|
);
|
|
assert_eq!(
|
|
at(&doc, &["dependencies", "bevy", "default-features"]).as_bool(),
|
|
Some(false),
|
|
"bevy adds no features"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn the_documented_launcher_takes_benilla_app_as_the_benilla_launcher_does() {
|
|
let doc = documented();
|
|
let shim = manifest("../benilla/Cargo.toml");
|
|
for keys in [
|
|
&["features", "dev"][..],
|
|
&["dependencies", "benilla-app", "default-features"],
|
|
] {
|
|
assert_eq!(at(&doc, keys), at(&shim, keys), "{}", keys.join("."));
|
|
}
|
|
assert!(
|
|
at(&doc, &["build-dependencies"])
|
|
.get("benilla-buildstamp")
|
|
.is_some(),
|
|
"the build script's stamp"
|
|
);
|
|
}
|