use new proc_macro::Span apis to produce stable names for registration functions

This commit is contained in:
eugineerd 2025-07-16 12:43:23 +00:00
parent 23f9ae8d9e
commit ca6827e7b9
2 changed files with 45 additions and 21 deletions

View File

@ -169,40 +169,67 @@ pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option<proc_macro2::Toke
}; };
if cfg!(feature = "auto_register_static") { if cfg!(feature = "auto_register_static") {
use std::{env, fs, io::Write, path::PathBuf, sync::LazyLock, sync::Mutex}; use core::hash::{Hash, Hasher};
use proc_macro::Span;
use std::{
env, fs,
hash::DefaultHasher,
io::Write,
path::PathBuf,
sync::{LazyLock, Mutex},
};
// Skip unless env var is set, otherwise this might slow down rust-analyzer
if env::var("BEVY_REFLECT_AUTO_REGISTER_STATIC").is_err() {
return None;
}
// Names of registrations functions will be stored in this file. // Names of registrations functions will be stored in this file.
// To allow writing to this file from multiple threads during compilation it is protected by mutex. // To allow writing to this file from multiple threads during compilation it is protected by mutex.
// This static is valid for the duration of compilation of one crate and we have one file per crate, // This static is valid for the duration of compilation of one crate and we have one file per crate,
// so it is enough to protect compilation threads from overwriting each other. // so it is enough to protect compilation threads from overwriting each other.
// This file is reset on every recompilation. // This file is reset on every crate recompilation.
//
// It might make sense to replace the mutex with File::lock when file_lock feature becomes stable.
static REGISTRATION_FNS_EXPORT: LazyLock<Mutex<fs::File>> = LazyLock::new(|| { static REGISTRATION_FNS_EXPORT: LazyLock<Mutex<fs::File>> = LazyLock::new(|| {
let path = PathBuf::from("target").join("type_registrations"); let path = PathBuf::from("target").join("bevy_reflect_type_registrations");
fs::DirBuilder::new() fs::DirBuilder::new()
.recursive(true) .recursive(true)
.create(&path) .create(&path)
.unwrap_or_else(|_| panic!("Failed to create {:?}", path)); .unwrap_or_else(|_| panic!("Failed to create {path:?}"));
let file_path = path.join( let file_path = path.join(env::var("CARGO_CRATE_NAME").unwrap());
env::var("CARGO_CRATE_NAME")
.expect("Expected cargo to set CARGO_CRATE_NAME env var"),
);
let file = fs::OpenOptions::new() let file = fs::OpenOptions::new()
.create(true) .create(true)
.write(true) .write(true)
.truncate(true) .truncate(true)
.open(&file_path) .open(&file_path)
.unwrap_or_else(|_| panic!("Failed to create {:?}", file_path)); .unwrap_or_else(|_| panic!("Failed to create {file_path:?}"));
Mutex::new(file) Mutex::new(file)
}); });
let export_name = format!("_bevy_reflect_register_{}", uuid::Uuid::new_v4().as_u128()); let crate_name =
if env::var("BEVY_REFLECT_AUTO_REGISTER_STATIC").is_ok_and(|v| v != "0") { env::var("CARGO_CRATE_NAME").expect("Expected cargo to set CARGO_CRATE_NAME env var");
let span = Span::call_site();
let mut hasher = DefaultHasher::new();
span.file().hash(&mut hasher);
let file_path_hash = hasher.finish();
let export_name = format!(
"_bevy_reflect_register_{}_{}_{}_{}",
crate_name,
file_path_hash,
span.line(),
span.column(),
);
{
let mut file = REGISTRATION_FNS_EXPORT.lock().unwrap(); let mut file = REGISTRATION_FNS_EXPORT.lock().unwrap();
writeln!(file, "{}", export_name) writeln!(file, "{export_name}")
.unwrap_or_else(|_| panic!("Failed to write registration function")); .unwrap_or_else(|_| panic!("Failed to write registration function {export_name}"));
// We must sync_data to ensure all content is written before releasing the mutex. // We must sync_data to ensure all content is written before releasing the lock.
file.sync_data().unwrap(); file.sync_data().unwrap();
}; };
Some(quote! { Some(quote! {
/// # Safety /// # Safety
/// This function must only be used by the `load_type_registrations` macro. /// This function must only be used by the `load_type_registrations` macro.
@ -211,7 +238,7 @@ pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option<proc_macro2::Toke
<#type_path as #bevy_reflect_path::__macro_exports::RegisterForReflection>::__register(registry); <#type_path as #bevy_reflect_path::__macro_exports::RegisterForReflection>::__register(registry);
} }
}) })
} else if cfg!(feature = "auto_register_inventory") { } else {
Some(quote! { Some(quote! {
#bevy_reflect_path::__macro_exports::auto_register::inventory::submit!{ #bevy_reflect_path::__macro_exports::auto_register::inventory::submit!{
#bevy_reflect_path::__macro_exports::auto_register::AutomaticReflectRegistrations( #bevy_reflect_path::__macro_exports::auto_register::AutomaticReflectRegistrations(
@ -219,7 +246,5 @@ pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option<proc_macro2::Toke
) )
} }
}) })
} else {
None
} }
} }

View File

@ -869,7 +869,8 @@ pub fn load_type_registrations(_input: TokenStream) -> TokenStream {
return TokenStream::new(); return TokenStream::new();
} }
let Ok(dir) = fs::read_dir(PathBuf::from("target").join("type_registrations")) else { let Ok(dir) = fs::read_dir(PathBuf::from("target").join("bevy_reflect_type_registrations"))
else {
return TokenStream::new(); return TokenStream::new();
}; };
let mut str_buf = String::new(); let mut str_buf = String::new();
@ -893,9 +894,7 @@ pub fn load_type_registrations(_input: TokenStream) -> TokenStream {
unsafe extern "Rust" { unsafe extern "Rust" {
#( safe fn #registration_fns(registry_ptr: &mut #bevy_reflect_path::TypeRegistry); )* #( safe fn #registration_fns(registry_ptr: &mut #bevy_reflect_path::TypeRegistry); )*
}; };
unsafe { #( #bevy_reflect_path::__macro_exports::auto_register::push_registration_fn(#registration_fns); )*
#( #bevy_reflect_path::__macro_exports::auto_register::push_registration_fn(#registration_fns); )*
};
} }
_register_types(); _register_types();
} }