Properly clean proc-macro-srv proc-macro temp dir

This commit is contained in:
Lukas Wirth 2025-07-31 09:26:05 +02:00
parent 4d5bb86ad7
commit a7a365e8f8
8 changed files with 125 additions and 18 deletions

View file

@ -15,10 +15,13 @@ proc-macro-srv.workspace = true
proc-macro-api.workspace = true
tt.workspace = true
clap = {version = "4.5.42", default-features = false, features = ["std"]}
postcard = { version = "1.1.3", optional = true }
[features]
default = ["postcard"]
sysroot-abi = ["proc-macro-srv/sysroot-abi"]
in-rust-tree = ["proc-macro-srv/in-rust-tree", "sysroot-abi"]
postcard = ["dep:postcard"]
[[bin]]

View file

@ -39,6 +39,7 @@ fn main() -> std::io::Result<()> {
#[derive(Copy, Clone)]
enum ProtocolFormat {
Json,
#[cfg(feature = "postcard")]
Postcard,
}
@ -50,12 +51,14 @@ impl ValueEnum for ProtocolFormat {
fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
match self {
ProtocolFormat::Json => Some(clap::builder::PossibleValue::new("json")),
#[cfg(feature = "postcard")]
ProtocolFormat::Postcard => Some(clap::builder::PossibleValue::new("postcard")),
}
}
fn from_str(input: &str, _ignore_case: bool) -> Result<Self, String> {
match input {
"json" => Ok(ProtocolFormat::Json),
#[cfg(feature = "postcard")]
"postcard" => Ok(ProtocolFormat::Postcard),
_ => Err(format!("unknown protocol format: {input}")),
}

View file

@ -37,6 +37,7 @@ impl SpanTransformer for SpanTrans {
pub(crate) fn run(format: ProtocolFormat) -> io::Result<()> {
match format {
ProtocolFormat::Json => run_json(),
#[cfg(feature = "postcard")]
ProtocolFormat::Postcard => unimplemented!(),
}
}
@ -96,7 +97,7 @@ fn run_json() -> io::Result<()> {
srv.expand(
lib,
env,
&env,
current_dir,
macro_name,
macro_body,
@ -127,7 +128,7 @@ fn run_json() -> io::Result<()> {
});
srv.expand(
lib,
env,
&env,
current_dir,
macro_name,
macro_body,

View file

@ -16,6 +16,7 @@ doctest = false
object.workspace = true
libloading.workspace = true
memmap2.workspace = true
temp-dir.workspace = true
tt.workspace = true
syntax-bridge.workspace = true
@ -26,6 +27,7 @@ intern.workspace = true
ra-ap-rustc_lexer.workspace = true
[target.'cfg(unix)'.dependencies]
libc.workspace = true

View file

@ -4,6 +4,7 @@ mod version;
use proc_macro::bridge;
use std::{fmt, fs, io, time::SystemTime};
use temp_dir::TempDir;
use libloading::Library;
use object::Object;
@ -141,13 +142,16 @@ pub(crate) struct Expander {
}
impl Expander {
pub(crate) fn new(lib: &Utf8Path) -> Result<Expander, LoadProcMacroDylibError> {
pub(crate) fn new(
temp_dir: &TempDir,
lib: &Utf8Path,
) -> Result<Expander, LoadProcMacroDylibError> {
// 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(&lib)?;
let path = ensure_file_with_lock_free_access(temp_dir, &lib)?;
let library = ProcMacroLibrary::open(path.as_ref())?;
Ok(Expander { inner: library, _remove_on_drop: RemoveFileOnDrop(path), modified_time })
@ -221,7 +225,10 @@ impl Drop for RemoveFileOnDrop {
/// Copy the dylib to temp directory to prevent locking in Windows
#[cfg(windows)]
fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result<Utf8PathBuf> {
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};
@ -229,9 +236,7 @@ fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result<Utf8PathBuf>
return Ok(path.to_path_buf());
}
let mut to = Utf8PathBuf::from_path_buf(std::env::temp_dir()).unwrap();
to.push("rust-analyzer-proc-macros");
_ = fs::create_dir(&to);
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}"))
@ -248,6 +253,9 @@ fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result<Utf8PathBuf>
}
#[cfg(unix)]
fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result<Utf8PathBuf> {
fn ensure_file_with_lock_free_access(
_temp_dir: &TempDir,
path: &Utf8Path,
) -> io::Result<Utf8PathBuf> {
Ok(path.to_owned())
}

View file

@ -42,6 +42,7 @@ use std::{
use paths::{Utf8Path, Utf8PathBuf};
use span::Span;
use temp_dir::TempDir;
use crate::server_impl::TokenStream;
@ -59,11 +60,16 @@ pub const RUSTC_VERSION_STRING: &str = env!("RUSTC_VERSION");
pub struct ProcMacroSrv<'env> {
expanders: Mutex<HashMap<Utf8PathBuf, Arc<dylib::Expander>>>,
env: &'env EnvSnapshot,
temp_dir: TempDir,
}
impl<'env> ProcMacroSrv<'env> {
pub fn new(env: &'env EnvSnapshot) -> Self {
Self { expanders: Default::default(), env }
Self {
expanders: Default::default(),
env,
temp_dir: TempDir::with_prefix("proc-macro-srv").unwrap(),
}
}
}
@ -73,7 +79,7 @@ impl ProcMacroSrv<'_> {
pub fn expand<S: ProcMacroSrvSpan>(
&self,
lib: impl AsRef<Utf8Path>,
env: Vec<(String, String)>,
env: &[(String, String)],
current_dir: Option<impl AsRef<Path>>,
macro_name: String,
macro_body: tt::TopSubtree<S>,
@ -131,7 +137,7 @@ impl ProcMacroSrv<'_> {
fn expander(&self, path: &Utf8Path) -> Result<Arc<dylib::Expander>, String> {
let expander = || {
let expander = dylib::Expander::new(path)
let expander = dylib::Expander::new(&self.temp_dir, path)
.map_err(|err| format!("Cannot create expander for {path}: {err}",));
expander.map(Arc::new)
};
@ -203,7 +209,7 @@ impl Default for EnvSnapshot {
static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
struct EnvChange<'snap> {
changed_vars: Vec<String>,
changed_vars: Vec<&'snap str>,
prev_working_dir: Option<PathBuf>,
snap: &'snap EnvSnapshot,
_guard: std::sync::MutexGuard<'snap, ()>,
@ -212,7 +218,7 @@ struct EnvChange<'snap> {
impl<'snap> EnvChange<'snap> {
fn apply(
snap: &'snap EnvSnapshot,
new_vars: Vec<(String, String)>,
new_vars: &'snap [(String, String)],
current_dir: Option<&Path>,
) -> EnvChange<'snap> {
let guard = ENV_LOCK.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
@ -232,11 +238,11 @@ impl<'snap> EnvChange<'snap> {
EnvChange {
snap,
changed_vars: new_vars
.into_iter()
.iter()
.map(|(k, v)| {
// SAFETY: We have acquired the environment lock
unsafe { env::set_var(&k, v) };
k
unsafe { env::set_var(k, v) };
&**k
})
.collect(),
prev_working_dir,

View file

@ -55,7 +55,7 @@ fn assert_expand_impl(
expect_spanned: Expect,
) {
let path = proc_macro_test_dylib_path();
let expander = dylib::Expander::new(&path).unwrap();
let expander = dylib::Expander::new(&temp_dir::TempDir::new().unwrap(), &path).unwrap();
let def_site = SpanId(0);
let call_site = SpanId(1);