Allow bevy_utils in no_std Contexts (#15279)

# Objective

- Adjust `bevy_utils` to make it `no_std` compatible
- Partially replaces #6581
- Contributes to #8161
- Contributes to #6370

## Solution

Added `alloc` and `std` features to `bevy_utils` (`std` is enabled by
default), allowing the crate's use in `no_std` contexts.

## Testing

- CI passed locally.
- Used `bevy_utils` in a `no_std` crate as an experiment and compiled
successfully.

## Migration Guide

If you were importing `bevy_utils` and setting `default_features` to
`false`, but relying on elements which are now gated behind the `std` or
`alloc` features, include the relevant feature in your `Cargo.toml`.

## Notes

- Bevy already includes a single `no_std` crate, `bevy_ptr`, so there is
precedent for this change.
- As `bevy_utils` is widely used across the rest of Bevy, further work
to make Bevy `no_std` compatible would be blocked on this crate, if such
work was to be undertaken.
- Most of the changes in this PR are just the removal of an unnecessary
call to `to_string()` within unit tests.
This commit is contained in:
Zachary Harrold 2024-09-19 02:00:03 +10:00 committed by GitHub
parent b1273d48cb
commit bd489068c6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 50 additions and 35 deletions

View file

@ -9,11 +9,16 @@ license = "MIT OR Apache-2.0"
keywords = ["bevy"]
[features]
default = ["std"]
std = ["alloc", "tracing/std", "ahash/std"]
alloc = []
detailed_trace = []
[dependencies]
ahash = "0.8.7"
tracing = { version = "0.1", default-features = false, features = ["std"] }
ahash = { version = "0.8.7", default-features = false, features = [
"runtime-rng",
] }
tracing = { version = "0.1", default-features = false }
web-time = { version = "1.1" }
hashbrown = { version = "0.14.2", features = ["serde"] }
bevy_utils_proc_macros = { version = "0.15.0-dev", path = "macros" }

View file

@ -1,5 +1,5 @@
//! Utilities for working with [`Future`]s.
use std::{
use core::{
future::Future,
pin::Pin,
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
@ -44,7 +44,7 @@ fn noop(_data: *const ()) {}
const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop);
fn noop_raw_waker() -> RawWaker {
RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE)
RawWaker::new(core::ptr::null(), &NOOP_WAKER_VTABLE)
}
fn noop_waker() -> Waker {

View file

@ -4,12 +4,16 @@
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
)]
#![cfg_attr(not(feature = "std"), no_std)]
//! General utilities for first-party [Bevy] engine crates.
//!
//! [Bevy]: https://bevyengine.org/
//!
#[cfg(feature = "alloc")]
extern crate alloc;
/// The utilities prelude.
///
/// This includes the most common types in this crate, re-exported for your convenience.
@ -18,7 +22,9 @@ pub mod prelude {
}
pub mod futures;
#[cfg(feature = "alloc")]
mod short_names;
#[cfg(feature = "alloc")]
pub use short_names::get_short_name;
pub mod synccell;
pub mod syncunsafecell;
@ -37,8 +43,10 @@ pub use parallel_queue::*;
pub use tracing;
pub use web_time::{Duration, Instant, SystemTime, SystemTimeError, TryFromFloatSecsError};
use hashbrown::hash_map::RawEntryMut;
use std::{
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
use core::{
any::TypeId,
fmt::Debug,
hash::{BuildHasher, BuildHasherDefault, Hash, Hasher},
@ -46,6 +54,7 @@ use std::{
mem::ManuallyDrop,
ops::Deref,
};
use hashbrown::hash_map::RawEntryMut;
#[cfg(not(target_arch = "wasm32"))]
mod conditional_send {
@ -66,11 +75,12 @@ pub use conditional_send::*;
/// Use [`ConditionalSendFuture`] for a future with an optional Send trait bound, as on certain platforms (eg. Wasm),
/// futures aren't Send.
pub trait ConditionalSendFuture: std::future::Future + ConditionalSend {}
impl<T: std::future::Future + ConditionalSend> ConditionalSendFuture for T {}
pub trait ConditionalSendFuture: core::future::Future + ConditionalSend {}
impl<T: core::future::Future + ConditionalSend> ConditionalSendFuture for T {}
/// An owned and dynamically typed Future used when you can't statically type your result or need to add some indirection.
pub type BoxedFuture<'a, T> = std::pin::Pin<Box<dyn ConditionalSendFuture<Output = T> + 'a>>;
#[cfg(feature = "alloc")]
pub type BoxedFuture<'a, T> = core::pin::Pin<Box<dyn ConditionalSendFuture<Output = T> + 'a>>;
/// A shortcut alias for [`hashbrown::hash_map::Entry`].
pub type Entry<'a, K, V, S = BuildHasherDefault<AHasher>> = hashbrown::hash_map::Entry<'a, K, V, S>;
@ -192,7 +202,7 @@ impl<V: PartialEq, H> PartialEq for Hashed<V, H> {
}
impl<V: Debug, H> Debug for Hashed<V, H> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Hashed")
.field("hash", &self.hash)
.field("value", &self.value)
@ -417,7 +427,7 @@ mod tests {
fn fast_typeid_hash() {
struct Hasher;
impl std::hash::Hasher for Hasher {
impl core::hash::Hasher for Hasher {
fn finish(&self) -> u64 {
0
}
@ -430,8 +440,11 @@ mod tests {
Hash::hash(&TypeId::of::<()>(), &mut Hasher);
}
#[cfg(feature = "alloc")]
#[test]
fn stable_hash_within_same_program_execution() {
use alloc::vec::Vec;
let mut map_1 = HashMap::new();
let mut map_2 = HashMap::new();
for i in 1..10 {

View file

@ -1,4 +1,7 @@
use std::{cell::RefCell, ops::DerefMut};
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::vec::Vec;
use core::{cell::RefCell, ops::DerefMut};
use thread_local::ThreadLocal;
/// A cohesive set of thread-local values of a given type.
@ -56,6 +59,7 @@ where
}
}
#[cfg(feature = "alloc")]
impl<T: Send> Parallel<Vec<T>> {
/// Collect all enqueued items from all threads and appends them to the end of a
/// single Vec.

View file

@ -1,3 +1,5 @@
use alloc::string::String;
/// Shortens a type name to remove all module paths.
///
/// The short name of a type is its full name as returned by
@ -88,43 +90,37 @@ mod name_formatting_tests {
fn path_separated() {
assert_eq!(
get_short_name("bevy_prelude::make_fun_game"),
"make_fun_game".to_string()
"make_fun_game"
);
}
#[test]
fn tuple_type() {
assert_eq!(
get_short_name("(String, String)"),
"(String, String)".to_string()
);
assert_eq!(get_short_name("(String, String)"), "(String, String)");
}
#[test]
fn array_type() {
assert_eq!(get_short_name("[i32; 3]"), "[i32; 3]".to_string());
assert_eq!(get_short_name("[i32; 3]"), "[i32; 3]");
}
#[test]
fn trivial_generics() {
assert_eq!(get_short_name("a<B>"), "a<B>".to_string());
assert_eq!(get_short_name("a<B>"), "a<B>");
}
#[test]
fn multiple_type_parameters() {
assert_eq!(get_short_name("a<B, C>"), "a<B, C>".to_string());
assert_eq!(get_short_name("a<B, C>"), "a<B, C>");
}
#[test]
fn enums() {
assert_eq!(get_short_name("Option::None"), "Option::None".to_string());
assert_eq!(
get_short_name("Option::Some(2)"),
"Option::Some(2)".to_string()
);
assert_eq!(get_short_name("Option::None"), "Option::None");
assert_eq!(get_short_name("Option::Some(2)"), "Option::Some(2)");
assert_eq!(
get_short_name("bevy_render::RenderSet::Prepare"),
"RenderSet::Prepare".to_string()
"RenderSet::Prepare"
);
}
@ -132,7 +128,7 @@ mod name_formatting_tests {
fn generics() {
assert_eq!(
get_short_name("bevy_render::camera::camera::extract_cameras<bevy_render::camera::bundle::Camera3d>"),
"extract_cameras<Camera3d>".to_string()
"extract_cameras<Camera3d>"
);
}
@ -140,7 +136,7 @@ mod name_formatting_tests {
fn nested_generics() {
assert_eq!(
get_short_name("bevy::mad_science::do_mad_science<mad_science::Test<mad_science::Tube>, bavy::TypeSystemAbuse>"),
"do_mad_science<Test<Tube>, TypeSystemAbuse>".to_string()
"do_mad_science<Test<Tube>, TypeSystemAbuse>"
);
}
@ -148,15 +144,12 @@ mod name_formatting_tests {
fn sub_path_after_closing_bracket() {
assert_eq!(
get_short_name("bevy_asset::assets::Assets<bevy_scene::dynamic_scene::DynamicScene>::asset_event_system"),
"Assets<DynamicScene>::asset_event_system".to_string()
"Assets<DynamicScene>::asset_event_system"
);
assert_eq!(
get_short_name("(String, String)::default"),
"(String, String)::default".to_string()
);
assert_eq!(
get_short_name("[i32; 16]::default"),
"[i32; 16]::default".to_string()
"(String, String)::default"
);
assert_eq!(get_short_name("[i32; 16]::default"), "[i32; 16]::default");
}
}

View file

@ -2,7 +2,7 @@
//!
//! [`std::sync::Exclusive`]: https://doc.rust-lang.org/nightly/std/sync/struct.Exclusive.html
use std::ptr;
use core::ptr;
/// See [`Exclusive`](https://github.com/rust-lang/rust/issues/98407) for stdlib's upcoming implementation,
/// which should replace this one entirely.