blob: 1978a68dd959c2ac556334e5ae632dd78e2068a2 [file]
//! Handles dynamic library loading for proc macro
mod proc_macros;
use rustc_codegen_ssa::back::metadata::DefaultMetadataLoader;
use rustc_interface::util::rustc_version_str;
use rustc_proc_macro::bridge;
use rustc_session::config::host_tuple;
use rustc_target::spec::{Target, TargetTuple};
use std::path::Path;
use std::{fs, io, time::SystemTime};
use temp_dir::TempDir;
use paths::{Utf8Path, Utf8PathBuf};
use crate::{
PanicMessage, ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, TrackedEnv,
dylib::proc_macros::ProcMacros, token_stream::TokenStream,
};
pub(crate) struct Expander {
inner: ProcMacroLibrary,
modified_time: SystemTime,
}
impl Expander {
pub(crate) fn new(temp_dir: &TempDir, lib: &Utf8Path) -> io::Result<Expander> {
// Some libraries for dynamic loading require canonicalized path even when it is
// already absolute
let lib = lib.canonicalize_utf8()?;
let modified_time = fs::metadata(&lib).and_then(|it| it.modified())?;
let path = ensure_file_with_lock_free_access(temp_dir, &lib)?;
let library = ProcMacroLibrary::open(path.as_ref())?;
Ok(Expander { inner: library, modified_time })
}
pub(crate) fn expand<'a, S: ProcMacroSrvSpan + 'a>(
&self,
macro_name: &str,
macro_body: TokenStream<S>,
attribute: Option<TokenStream<S>>,
def_site: S,
call_site: S,
mixed_site: S,
tracked_env: &'a mut TrackedEnv,
callback: Option<ProcMacroClientHandle<'a>>,
) -> Result<TokenStream<S>, PanicMessage>
where
<S::Server<'a> as bridge::server::Server>::TokenStream: Default,
{
self.inner.proc_macros.expand(
macro_name,
macro_body,
attribute,
def_site,
call_site,
mixed_site,
tracked_env,
callback,
)
}
pub(crate) fn list_macros(&self) -> impl Iterator<Item = (&str, ProcMacroKind)> {
self.inner.proc_macros.list_macros()
}
pub(crate) fn modified_time(&self) -> SystemTime {
self.modified_time
}
}
struct ProcMacroLibrary {
proc_macros: ProcMacros,
}
impl ProcMacroLibrary {
fn open(path: &Utf8Path) -> io::Result<Self> {
let proc_macros = rustc_span::create_default_session_globals_then(|| {
let (target, _) =
Target::search(&TargetTuple::from_tuple(host_tuple()), Path::new(""), false)
.unwrap();
rustc_metadata::locator::get_proc_macros(
&target,
path.as_ref(),
&DefaultMetadataLoader,
rustc_version_str().unwrap_or("unknown"),
)
})?;
Ok(ProcMacroLibrary { proc_macros: ProcMacros::new(proc_macros) })
}
}
/// Copy the dylib to temp directory to prevent locking in Windows
#[cfg(windows)]
fn ensure_file_with_lock_free_access(
temp_dir: &TempDir,
path: &Utf8Path,
) -> io::Result<Utf8PathBuf> {
use std::collections::hash_map::RandomState;
use std::hash::{BuildHasher, Hasher};
if std::env::var("RA_DONT_COPY_PROC_MACRO_DLL").is_ok() {
return Ok(path.to_path_buf());
}
let mut to = Utf8Path::from_path(temp_dir.path()).unwrap().to_owned();
let file_name = path.file_stem().ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, format!("File path is invalid: {path}"))
})?;
to.push({
// Generate a unique number by abusing `HashMap`'s hasher.
// Maybe this will also "inspire" a libs team member to finally put `rand` in libstd.
let unique_name = RandomState::new().build_hasher().finish();
format!("{file_name}-{unique_name}.dll")
});
fs::copy(path, &to)?;
Ok(to)
}
#[cfg(unix)]
fn ensure_file_with_lock_free_access(
_temp_dir: &TempDir,
path: &Utf8Path,
) -> io::Result<Utf8PathBuf> {
Ok(path.to_owned())
}