Skip to content

Commit f45e78e

Browse files
Add no_std support to bevy_app (#16874)
# Objective - Contributes to #15460 ## Solution - Added the following features: - `std` (default) - `bevy_tasks` (default) - `downcast ` (default) - `portable-atomic` - `critical-section` - `downcast` and `bevy_tasks` are now optional dependencies for `bevy_app`. ## Testing - CI - Personal UEFI and Raspberry Pi Pico demo applications compile and run against this branch ## Draft Release Notes Bevy's application framework now supports `no_std` platforms. Following up on `bevy_ecs` gaining `no_std` support, `bevy_app` extends the functionality available on these targets to include the powerful `App` and `Plugin` abstractions. With this, library authors now have the option of making their plugins `no_std` compatible, or even offering plugins specifically to improve Bevy on certain embedded platforms! To start making a `no_std` compatible plugin, simply disable default features when including `bevy_app`: ```toml [dependencies] bevy_app = { version = "0.16", default-features = false } ``` We encourage library authors to do this anyway, as it can also help with compile times and binary size on all platforms. Keep an eye out for future `no_std` updates as we continue to improve the parity between `std` and `no_std`. We look forward to seeing what kinds of applications are now possible with Bevy! ## Notes - `downcast-rs` is optional as it isn't compatible with `portable-atomic`. I will investigate making a PR upstream to add support for this functionality, as it should be very straightforward. - In line with the `bevy_ecs` no-std-ification, I've added documentation to all features, and grouped them as well. - ~~Creating this PR in draft while CI runs and so I can polish before review.~~ --------- Co-authored-by: Alice Cecile <[email protected]>
1 parent c425fc7 commit f45e78e

File tree

11 files changed

+139
-31
lines changed

11 files changed

+139
-31
lines changed

crates/bevy_app/Cargo.toml

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,77 @@ license = "MIT OR Apache-2.0"
99
keywords = ["bevy"]
1010

1111
[features]
12-
trace = []
13-
bevy_debug_stepping = []
14-
default = ["bevy_reflect"]
12+
default = ["std", "bevy_reflect", "bevy_tasks", "bevy_ecs/default", "downcast"]
13+
14+
# Functionality
15+
16+
## Adds runtime reflection support using `bevy_reflect`.
1517
bevy_reflect = ["dep:bevy_reflect", "bevy_ecs/bevy_reflect"]
18+
19+
## Extends reflection support to functions.
1620
reflect_functions = [
1721
"bevy_reflect",
1822
"bevy_reflect/functions",
1923
"bevy_ecs/reflect_functions",
2024
]
2125

26+
## Adds support for running async background tasks
27+
bevy_tasks = ["dep:bevy_tasks"]
28+
29+
## Adds `downcast-rs` integration for `Plugin`
30+
downcast = ["dep:downcast-rs"]
31+
32+
# Debugging Features
33+
34+
## Enables `tracing` integration, allowing spans and other metrics to be reported
35+
## through that framework.
36+
trace = ["dep:tracing"]
37+
38+
## Provides system stepping support, allowing them to be paused, stepped, and
39+
## other debug operations which can help with diagnosing certain behaviors.
40+
bevy_debug_stepping = []
41+
42+
# Platform Compatibility
43+
44+
## Allows access to the `std` crate. Enabling this feature will prevent compilation
45+
## on `no_std` targets, but provides access to certain additional features on
46+
## supported platforms.
47+
std = [
48+
"bevy_reflect?/std",
49+
"bevy_ecs/std",
50+
"dep:ctrlc",
51+
"downcast-rs?/std",
52+
"bevy_utils/std",
53+
"bevy_tasks?/std",
54+
]
55+
56+
## `critical-section` provides the building blocks for synchronization primitives
57+
## on all platforms, including `no_std`.
58+
critical-section = ["bevy_tasks?/critical-section", "bevy_ecs/critical-section"]
59+
60+
## `portable-atomic` provides additional platform support for atomic types and
61+
## operations, even on targets without native support.
62+
portable-atomic = ["bevy_tasks?/portable-atomic", "bevy_ecs/portable-atomic"]
63+
2264
[dependencies]
2365
# bevy
2466
bevy_derive = { path = "../bevy_derive", version = "0.15.0-dev" }
2567
bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev", default-features = false }
26-
bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", optional = true }
27-
bevy_utils = { path = "../bevy_utils", version = "0.15.0-dev" }
28-
bevy_tasks = { path = "../bevy_tasks", version = "0.15.0-dev" }
68+
bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", default-features = false, optional = true }
69+
bevy_utils = { path = "../bevy_utils", version = "0.15.0-dev", default-features = false, features = [
70+
"alloc",
71+
] }
72+
bevy_tasks = { path = "../bevy_tasks", version = "0.15.0-dev", default-features = false, optional = true }
2973

3074
# other
31-
downcast-rs = "1.2.0"
75+
downcast-rs = { version = "1.2.0", default-features = false, optional = true }
3276
thiserror = { version = "2", default-features = false }
3377
variadics_please = "1.1"
78+
tracing = { version = "0.1", default-features = false, optional = true }
79+
log = { version = "0.4", default-features = false }
3480

3581
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
36-
ctrlc = "3.4.4"
82+
ctrlc = { version = "3.4.4", optional = true }
3783

3884
[target.'cfg(target_arch = "wasm32")'.dependencies]
3985
wasm-bindgen = { version = "0.2" }

crates/bevy_app/src/app.rs

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ use crate::{
22
First, Main, MainSchedulePlugin, PlaceholderPlugin, Plugin, Plugins, PluginsState, SubApp,
33
SubApps,
44
};
5+
use alloc::{
6+
boxed::Box,
7+
string::{String, ToString},
8+
};
59
pub use bevy_derive::AppLabel;
610
use bevy_ecs::{
711
component::RequiredComponentsError,
@@ -11,15 +15,22 @@ use bevy_ecs::{
1115
schedule::{ScheduleBuildSettings, ScheduleLabel},
1216
system::{IntoObserverSystem, SystemId, SystemInput},
1317
};
14-
#[cfg(feature = "trace")]
15-
use bevy_utils::tracing::info_span;
16-
use bevy_utils::{tracing::debug, HashMap};
18+
use bevy_utils::HashMap;
1719
use core::{fmt::Debug, num::NonZero, panic::AssertUnwindSafe};
20+
use log::debug;
21+
use thiserror::Error;
22+
23+
#[cfg(feature = "trace")]
24+
use tracing::info_span;
25+
26+
#[cfg(feature = "std")]
1827
use std::{
1928
panic::{catch_unwind, resume_unwind},
2029
process::{ExitCode, Termination},
2130
};
22-
use thiserror::Error;
31+
32+
#[cfg(feature = "downcast")]
33+
use alloc::vec::Vec;
2334

2435
bevy_ecs::define_label!(
2536
/// A strongly-typed class of labels used to identify an [`App`].
@@ -458,12 +469,21 @@ impl App {
458469
.push(Box::new(PlaceholderPlugin));
459470

460471
self.main_mut().plugin_build_depth += 1;
461-
let result = catch_unwind(AssertUnwindSafe(|| plugin.build(self)));
472+
473+
let f = AssertUnwindSafe(|| plugin.build(self));
474+
475+
#[cfg(feature = "std")]
476+
let result = catch_unwind(f);
477+
478+
#[cfg(not(feature = "std"))]
479+
f();
480+
462481
self.main_mut()
463482
.plugin_names
464483
.insert(plugin.name().to_string());
465484
self.main_mut().plugin_build_depth -= 1;
466485

486+
#[cfg(feature = "std")]
467487
if let Err(payload) = result {
468488
resume_unwind(payload);
469489
}
@@ -499,6 +519,7 @@ impl App {
499519
/// # app.add_plugins(ImagePlugin::default());
500520
/// let default_sampler = app.get_added_plugins::<ImagePlugin>()[0].default_sampler;
501521
/// ```
522+
#[cfg(feature = "downcast")]
502523
pub fn get_added_plugins<T>(&self) -> Vec<&T>
503524
where
504525
T: Plugin,
@@ -1294,7 +1315,7 @@ type RunnerFn = Box<dyn FnOnce(App) -> AppExit>;
12941315

12951316
fn run_once(mut app: App) -> AppExit {
12961317
while app.plugins_state() == PluginsState::Adding {
1297-
#[cfg(not(target_arch = "wasm32"))]
1318+
#[cfg(all(not(target_arch = "wasm32"), feature = "bevy_tasks"))]
12981319
bevy_tasks::tick_global_task_pools_on_main_thread();
12991320
}
13001321
app.finish();
@@ -1364,6 +1385,7 @@ impl From<u8> for AppExit {
13641385
}
13651386
}
13661387

1388+
#[cfg(feature = "std")]
13671389
impl Termination for AppExit {
13681390
fn report(self) -> ExitCode {
13691391
match self {

crates/bevy_app/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
html_logo_url = "https://bevyengine.org/assets/icon.png",
1212
html_favicon_url = "https://bevyengine.org/assets/icon.png"
1313
)]
14+
#![cfg_attr(not(feature = "std"), no_std)]
1415

1516
//! This crate is about everything concerning the highest-level, application layer of a Bevy app.
1617
@@ -23,7 +24,7 @@ mod plugin;
2324
mod plugin_group;
2425
mod schedule_runner;
2526
mod sub_app;
26-
#[cfg(not(target_arch = "wasm32"))]
27+
#[cfg(all(not(target_arch = "wasm32"), feature = "std"))]
2728
mod terminal_ctrl_c_handler;
2829

2930
pub use app::*;
@@ -33,7 +34,7 @@ pub use plugin::*;
3334
pub use plugin_group::*;
3435
pub use schedule_runner::*;
3536
pub use sub_app::*;
36-
#[cfg(not(target_arch = "wasm32"))]
37+
#[cfg(all(not(target_arch = "wasm32"), feature = "std"))]
3738
pub use terminal_ctrl_c_handler::*;
3839

3940
/// The app prelude.

crates/bevy_app/src/main_schedule.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::{App, Plugin};
2+
use alloc::{vec, vec::Vec};
23
use bevy_ecs::{
34
schedule::{
45
ExecutorKind, InternedScheduleLabel, IntoSystemSetConfigs, Schedule, ScheduleLabel,

crates/bevy_app/src/plugin.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
1+
// TODO: Upstream `portable-atomic` support to `downcast_rs` and unconditionally
2+
// include it as a dependency.
3+
// See https://github.com/marcianx/downcast-rs/pull/22 for details
4+
#[cfg(feature = "downcast")]
15
use downcast_rs::{impl_downcast, Downcast};
26

37
use crate::App;
48
use core::any::Any;
59

10+
/// Dummy trait with the same name as `downcast_rs::Downcast`. This is to ensure
11+
/// the `Plugin: Downcast` bound can remain even when `downcast` isn't enabled.
12+
#[cfg(not(feature = "downcast"))]
13+
#[doc(hidden)]
14+
pub trait Downcast {}
15+
16+
#[cfg(not(feature = "downcast"))]
17+
impl<T: ?Sized> Downcast for T {}
18+
619
/// A collection of Bevy app logic and configuration.
720
///
821
/// Plugins configure an [`App`]. When an [`App`] registers a plugin,
@@ -92,6 +105,7 @@ pub trait Plugin: Downcast + Any + Send + Sync {
92105
}
93106
}
94107

108+
#[cfg(feature = "downcast")]
95109
impl_downcast!(Plugin);
96110

97111
impl<T: Fn(&mut App) + Send + Sync + 'static> Plugin for T {
@@ -129,6 +143,7 @@ pub trait Plugins<Marker>: sealed::Plugins<Marker> {}
129143
impl<Marker, T> Plugins<Marker> for T where T: sealed::Plugins<Marker> {}
130144

131145
mod sealed {
146+
use alloc::boxed::Box;
132147
use variadics_please::all_tuples;
133148

134149
use crate::{App, AppError, Plugin, PluginGroup};

crates/bevy_app/src/plugin_group.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
use crate::{App, AppError, Plugin};
2-
use bevy_utils::{
3-
tracing::{debug, warn},
4-
TypeIdMap,
2+
use alloc::{
3+
boxed::Box,
4+
string::{String, ToString},
5+
vec::Vec,
56
};
7+
use bevy_utils::TypeIdMap;
68
use core::any::TypeId;
9+
use log::{debug, warn};
710

811
/// A macro for generating a well-documented [`PluginGroup`] from a list of [`Plugin`] paths.
912
///

crates/bevy_app/src/schedule_runner.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ use crate::{
33
plugin::Plugin,
44
PluginsState,
55
};
6-
use bevy_utils::{Duration, Instant};
6+
use bevy_utils::Duration;
7+
8+
#[cfg(any(target_arch = "wasm32", feature = "std"))]
9+
use bevy_utils::Instant;
710

811
#[cfg(target_arch = "wasm32")]
912
use {
@@ -76,7 +79,7 @@ impl Plugin for ScheduleRunnerPlugin {
7679
let plugins_state = app.plugins_state();
7780
if plugins_state != PluginsState::Cleaned {
7881
while app.plugins_state() == PluginsState::Adding {
79-
#[cfg(not(target_arch = "wasm32"))]
82+
#[cfg(all(not(target_arch = "wasm32"), feature = "bevy_tasks"))]
8083
bevy_tasks::tick_global_task_pools_on_main_thread();
8184
}
8285
app.finish();
@@ -95,8 +98,9 @@ impl Plugin for ScheduleRunnerPlugin {
9598
}
9699
RunMode::Loop { wait } => {
97100
let tick = move |app: &mut App,
98-
wait: Option<Duration>|
101+
_wait: Option<Duration>|
99102
-> Result<Option<Duration>, AppExit> {
103+
#[cfg(any(target_arch = "wasm32", feature = "std"))]
100104
let start_time = Instant::now();
101105

102106
app.update();
@@ -105,9 +109,11 @@ impl Plugin for ScheduleRunnerPlugin {
105109
return Err(exit);
106110
};
107111

112+
#[cfg(any(target_arch = "wasm32", feature = "std"))]
108113
let end_time = Instant::now();
109114

110-
if let Some(wait) = wait {
115+
#[cfg(any(target_arch = "wasm32", feature = "std"))]
116+
if let Some(wait) = _wait {
111117
let exe_time = end_time - start_time;
112118
if exe_time < wait {
113119
return Ok(Some(wait - exe_time));
@@ -121,7 +127,10 @@ impl Plugin for ScheduleRunnerPlugin {
121127
{
122128
loop {
123129
match tick(&mut app, wait) {
124-
Ok(Some(delay)) => std::thread::sleep(delay),
130+
Ok(Some(_delay)) => {
131+
#[cfg(feature = "std")]
132+
std::thread::sleep(_delay);
133+
}
125134
Ok(None) => continue,
126135
Err(exit) => return exit,
127136
}

crates/bevy_app/src/sub_app.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
use crate::{App, AppLabel, InternedAppLabel, Plugin, Plugins, PluginsState};
2+
use alloc::{boxed::Box, string::String, vec::Vec};
23
use bevy_ecs::{
34
event::EventRegistry,
45
prelude::*,
56
schedule::{InternedScheduleLabel, ScheduleBuildSettings, ScheduleLabel},
67
system::{SystemId, SystemInput},
78
};
8-
9-
#[cfg(feature = "trace")]
10-
use bevy_utils::tracing::info_span;
119
use bevy_utils::{HashMap, HashSet};
1210
use core::fmt::Debug;
1311

12+
#[cfg(feature = "trace")]
13+
use tracing::info_span;
14+
1415
type ExtractFn = Box<dyn Fn(&mut World, &mut World) + Send>;
1516

1617
/// A secondary application with its own [`World`]. These can run independently of each other.
@@ -332,6 +333,7 @@ impl SubApp {
332333
}
333334

334335
/// See [`App::get_added_plugins`].
336+
#[cfg(feature = "downcast")]
335337
pub fn get_added_plugins<T>(&self) -> Vec<&T>
336338
where
337339
T: Plugin,

crates/bevy_app/src/terminal_ctrl_c_handler.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ impl Plugin for TerminalCtrlCHandlerPlugin {
6363
match result {
6464
Ok(()) => {}
6565
Err(ctrlc::Error::MultipleHandlers) => {
66-
bevy_utils::tracing::info!("Skipping installing `Ctrl+C` handler as one was already installed. Please call `TerminalCtrlCHandlerPlugin::gracefully_exit` in your own `Ctrl+C` handler if you want Bevy to gracefully exit on `Ctrl+C`.");
66+
log::info!("Skipping installing `Ctrl+C` handler as one was already installed. Please call `TerminalCtrlCHandlerPlugin::gracefully_exit` in your own `Ctrl+C` handler if you want Bevy to gracefully exit on `Ctrl+C`.");
6767
}
68-
Err(err) => bevy_utils::tracing::warn!("Failed to set `Ctrl+C` handler: {err}"),
68+
Err(err) => log::warn!("Failed to set `Ctrl+C` handler: {err}"),
6969
}
7070

7171
app.add_systems(Update, TerminalCtrlCHandlerPlugin::exit_on_flag);

crates/bevy_ecs/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ std = [
7979
## on all platforms, including `no_std`.
8080
critical-section = [
8181
"dep:critical-section",
82-
"bevy_tasks/critical-section",
82+
"bevy_tasks?/critical-section",
8383
"portable-atomic?/critical-section",
8484
]
8585

@@ -88,8 +88,9 @@ critical-section = [
8888
portable-atomic = [
8989
"dep:portable-atomic",
9090
"dep:portable-atomic-util",
91-
"bevy_tasks/portable-atomic",
91+
"bevy_tasks?/portable-atomic",
9292
"concurrent-queue/portable-atomic",
93+
"spin/portable_atomic",
9394
]
9495

9596
[dependencies]

tools/ci/src/commands/compile_check_no_std.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ impl Prepare for CompileCheckNoStdCommand {
102102
"Please fix compiler errors in output above for bevy_ecs no_std compatibility.",
103103
));
104104

105+
commands.push(PreparedCommand::new::<Self>(
106+
cmd!(
107+
sh,
108+
"cargo check -p bevy_app --no-default-features --features bevy_reflect --target {target}"
109+
),
110+
"Please fix compiler errors in output above for bevy_app no_std compatibility.",
111+
));
112+
105113
commands
106114
}
107115
}

0 commit comments

Comments
 (0)