Remove proc-macro server command from the rust-analyzer binary

This commit is contained in:
Lukas Wirth 2023-04-26 08:06:15 +02:00
parent 943d2a8a1c
commit c21860bd6a
15 changed files with 109 additions and 162 deletions

1
Cargo.lock generated
View file

@ -1464,7 +1464,6 @@ dependencies = [
"parking_lot 0.12.1", "parking_lot 0.12.1",
"parking_lot_core 0.9.6", "parking_lot_core 0.9.6",
"proc-macro-api", "proc-macro-api",
"proc-macro-srv-cli",
"profile", "profile",
"project-model", "project-model",
"rayon", "rayon",

View file

@ -13,7 +13,6 @@ mod version;
use paths::AbsPathBuf; use paths::AbsPathBuf;
use std::{ use std::{
ffi::OsStr,
fmt, io, fmt, io,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
@ -103,11 +102,8 @@ pub struct MacroPanic {
impl ProcMacroServer { impl ProcMacroServer {
/// Spawns an external process as the proc macro server and returns a client connected to it. /// Spawns an external process as the proc macro server and returns a client connected to it.
pub fn spawn( pub fn spawn(process_path: AbsPathBuf) -> io::Result<ProcMacroServer> {
process_path: AbsPathBuf, let process = ProcMacroProcessSrv::run(process_path)?;
args: impl IntoIterator<Item = impl AsRef<OsStr>> + Clone,
) -> io::Result<ProcMacroServer> {
let process = ProcMacroProcessSrv::run(process_path, args)?;
Ok(ProcMacroServer { process: Arc::new(Mutex::new(process)) }) Ok(ProcMacroServer { process: Arc::new(Mutex::new(process)) })
} }

View file

@ -1,7 +1,6 @@
//! Handle process life-time and message passing for proc-macro client //! Handle process life-time and message passing for proc-macro client
use std::{ use std::{
ffi::{OsStr, OsString},
io::{self, BufRead, BufReader, Write}, io::{self, BufRead, BufReader, Write},
process::{Child, ChildStdin, ChildStdout, Command, Stdio}, process::{Child, ChildStdin, ChildStdout, Command, Stdio},
}; };
@ -23,12 +22,9 @@ pub(crate) struct ProcMacroProcessSrv {
} }
impl ProcMacroProcessSrv { impl ProcMacroProcessSrv {
pub(crate) fn run( pub(crate) fn run(process_path: AbsPathBuf) -> io::Result<ProcMacroProcessSrv> {
process_path: AbsPathBuf,
args: impl IntoIterator<Item = impl AsRef<OsStr>> + Clone,
) -> io::Result<ProcMacroProcessSrv> {
let create_srv = |null_stderr| { let create_srv = |null_stderr| {
let mut process = Process::run(process_path.clone(), args.clone(), null_stderr)?; let mut process = Process::run(process_path.clone(), null_stderr)?;
let (stdin, stdout) = process.stdio().expect("couldn't access child stdio"); let (stdin, stdout) = process.stdio().expect("couldn't access child stdio");
io::Result::Ok(ProcMacroProcessSrv { _process: process, stdin, stdout, version: 0 }) io::Result::Ok(ProcMacroProcessSrv { _process: process, stdin, stdout, version: 0 })
@ -100,13 +96,8 @@ struct Process {
} }
impl Process { impl Process {
fn run( fn run(path: AbsPathBuf, null_stderr: bool) -> io::Result<Process> {
path: AbsPathBuf, let child = JodChild(mk_child(&path, null_stderr)?);
args: impl IntoIterator<Item = impl AsRef<OsStr>>,
null_stderr: bool,
) -> io::Result<Process> {
let args: Vec<OsString> = args.into_iter().map(|s| s.as_ref().into()).collect();
let child = JodChild(mk_child(&path, args, null_stderr)?);
Ok(Process { child }) Ok(Process { child })
} }
@ -119,13 +110,8 @@ impl Process {
} }
} }
fn mk_child( fn mk_child(path: &AbsPath, null_stderr: bool) -> io::Result<Child> {
path: &AbsPath,
args: impl IntoIterator<Item = impl AsRef<OsStr>>,
null_stderr: bool,
) -> io::Result<Child> {
Command::new(path.as_os_str()) Command::new(path.as_os_str())
.args(args)
.env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable") .env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable")
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())

View file

@ -1,54 +0,0 @@
//! Driver for proc macro server
use std::io;
use proc_macro_api::msg::{self, Message};
#[cfg(feature = "sysroot-abi")]
pub fn run() -> io::Result<()> {
let mut srv = proc_macro_srv::ProcMacroSrv::default();
let mut buf = String::new();
while let Some(req) = read_request(&mut buf)? {
let res = match req {
msg::Request::ListMacros { dylib_path } => {
msg::Response::ListMacros(srv.list_macros(&dylib_path))
}
msg::Request::ExpandMacro(task) => msg::Response::ExpandMacro(srv.expand(task)),
msg::Request::ApiVersionCheck {} => {
msg::Response::ApiVersionCheck(proc_macro_api::msg::CURRENT_API_VERSION)
}
};
write_response(res)?
}
Ok(())
}
#[cfg(not(feature = "sysroot-abi"))]
pub fn run() -> io::Result<()> {
let mut buf = String::new();
while let Some(req) = read_request(&mut buf)? {
let res = match req {
msg::Request::ListMacros { .. } => {
msg::Response::ListMacros(Err("server is built without sysroot support".to_owned()))
}
msg::Request::ExpandMacro(..) => msg::Response::ExpandMacro(Err(msg::PanicMessage(
"server is built without sysroot support".to_owned(),
))),
msg::Request::ApiVersionCheck {} => {
msg::Response::ApiVersionCheck(proc_macro_api::msg::CURRENT_API_VERSION)
}
};
write_response(res)?
}
Ok(())
}
fn read_request(buf: &mut String) -> io::Result<Option<msg::Request>> {
msg::Request::read(&mut io::stdin().lock(), buf)
}
fn write_response(msg: msg::Response) -> io::Result<()> {
msg.write(&mut io::stdout().lock())
}

View file

@ -1,5 +1,6 @@
//! A standalone binary for `proc-macro-srv`. //! A standalone binary for `proc-macro-srv`.
//! Driver for proc macro server //! Driver for proc macro server
use std::io;
fn main() -> std::io::Result<()> { fn main() -> std::io::Result<()> {
let v = std::env::var("RUST_ANALYZER_INTERNALS_DO_NOT_USE"); let v = std::env::var("RUST_ANALYZER_INTERNALS_DO_NOT_USE");
@ -14,5 +15,37 @@ fn main() -> std::io::Result<()> {
} }
} }
proc_macro_srv_cli::run() run()
}
#[cfg(not(feature = "sysroot-abi"))]
fn run() -> io::Result<()> {
panic!("proc-macro-srv-cli requires the `sysroot-abi` feature to be enabled");
}
#[cfg(feature = "sysroot-abi")]
fn run() -> io::Result<()> {
use proc_macro_api::msg::{self, Message};
let read_request = |buf: &mut String| msg::Request::read(&mut io::stdin().lock(), buf);
let write_response = |msg: msg::Response| msg.write(&mut io::stdout().lock());
let mut srv = proc_macro_srv::ProcMacroSrv::default();
let mut buf = String::new();
while let Some(req) = read_request(&mut buf)? {
let res = match req {
msg::Request::ListMacros { dylib_path } => {
msg::Response::ListMacros(srv.list_macros(&dylib_path))
}
msg::Request::ExpandMacro(task) => msg::Response::ExpandMacro(srv.expand(task)),
msg::Request::ApiVersionCheck {} => {
msg::Response::ApiVersionCheck(proc_macro_api::msg::CURRENT_API_VERSION)
}
};
write_response(res)?
}
Ok(())
} }

View file

@ -459,18 +459,35 @@ impl ProjectWorkspace {
} }
} }
pub fn find_sysroot_proc_macro_srv(&self) -> Option<AbsPathBuf> { pub fn find_sysroot_proc_macro_srv(&self) -> Result<AbsPathBuf> {
match self { match self {
ProjectWorkspace::Cargo { sysroot: Ok(sysroot), .. } ProjectWorkspace::Cargo { sysroot: Ok(sysroot), .. }
| ProjectWorkspace::Json { sysroot: Ok(sysroot), .. } => { | ProjectWorkspace::Json { sysroot: Ok(sysroot), .. }
| ProjectWorkspace::DetachedFiles { sysroot: Ok(sysroot), .. } => {
let standalone_server_name = let standalone_server_name =
format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX); format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX);
["libexec", "lib"] ["libexec", "lib"]
.into_iter() .into_iter()
.map(|segment| sysroot.root().join(segment).join(&standalone_server_name)) .map(|segment| sysroot.root().join(segment).join(&standalone_server_name))
.find(|server_path| std::fs::metadata(server_path).is_ok()) .find(|server_path| std::fs::metadata(server_path).is_ok())
.ok_or_else(|| {
anyhow::anyhow!(
"cannot find proc-macro server in sysroot `{}`",
sysroot.root().display()
)
})
} }
_ => None, ProjectWorkspace::DetachedFiles { .. } => {
Err(anyhow::anyhow!("cannot find proc-macro server, no sysroot was found"))
}
ProjectWorkspace::Cargo { cargo, .. } => Err(anyhow::anyhow!(
"cannot find proc-macro-srv, the workspace `{}` is missing a sysroot",
cargo.workspace_root().display()
)),
ProjectWorkspace::Json { project, .. } => Err(anyhow::anyhow!(
"cannot find proc-macro-srv, the workspace `{}` is missing a sysroot",
project.path().display()
)),
} }
} }

View file

@ -67,7 +67,6 @@ ide-db.workspace = true
ide-ssr.workspace = true ide-ssr.workspace = true
ide.workspace = true ide.workspace = true
proc-macro-api.workspace = true proc-macro-api.workspace = true
proc-macro-srv-cli.workspace = true
profile.workspace = true profile.workspace = true
project-model.workspace = true project-model.workspace = true
stdx.workspace = true stdx.workspace = true
@ -95,9 +94,7 @@ mbe.workspace = true
[features] [features]
jemalloc = ["jemallocator", "profile/jemalloc"] jemalloc = ["jemallocator", "profile/jemalloc"]
force-always-assert = ["always-assert/force"] force-always-assert = ["always-assert/force"]
sysroot-abi = ["proc-macro-srv-cli/sysroot-abi"]
in-rust-tree = [ in-rust-tree = [
"sysroot-abi",
"ide/in-rust-tree", "ide/in-rust-tree",
"syntax/in-rust-tree", "syntax/in-rust-tree",
] ]

View file

@ -76,9 +76,6 @@ fn try_main(flags: flags::RustAnalyzer) -> Result<()> {
} }
with_extra_thread("LspServer", run_server)?; with_extra_thread("LspServer", run_server)?;
} }
flags::RustAnalyzerCmd::ProcMacro(flags::ProcMacro) => {
with_extra_thread("MacroExpander", || proc_macro_srv_cli::run().map_err(Into::into))?;
}
flags::RustAnalyzerCmd::Parse(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Parse(cmd) => cmd.run()?,
flags::RustAnalyzerCmd::Symbols(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Symbols(cmd) => cmd.run()?,
flags::RustAnalyzerCmd::Highlight(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Highlight(cmd) => cmd.run()?,

View file

@ -106,8 +106,6 @@ xflags::xflags! {
optional --debug snippet: String optional --debug snippet: String
} }
cmd proc-macro {}
cmd lsif { cmd lsif {
required path: PathBuf required path: PathBuf
} }
@ -141,7 +139,6 @@ pub enum RustAnalyzerCmd {
Diagnostics(Diagnostics), Diagnostics(Diagnostics),
Ssr(Ssr), Ssr(Ssr),
Search(Search), Search(Search),
ProcMacro(ProcMacro),
Lsif(Lsif), Lsif(Lsif),
Scip(Scip), Scip(Scip),
} }
@ -203,9 +200,6 @@ pub struct Search {
pub debug: Option<String>, pub debug: Option<String>,
} }
#[derive(Debug)]
pub struct ProcMacro;
#[derive(Debug)] #[derive(Debug)]
pub struct Lsif { pub struct Lsif {
pub path: PathBuf, pub path: PathBuf,

View file

@ -1,8 +1,8 @@
//! Loads a Cargo project into a static instance of analysis, without support //! Loads a Cargo project into a static instance of analysis, without support
//! for incorporating changes. //! for incorporating changes.
use std::{convert::identity, path::Path, sync::Arc}; use std::{path::Path, sync::Arc};
use anyhow::Result; use anyhow::{anyhow, Result};
use crossbeam_channel::{unbounded, Receiver}; use crossbeam_channel::{unbounded, Receiver};
use ide::{AnalysisHost, Change}; use ide::{AnalysisHost, Change};
use ide_db::{ use ide_db::{
@ -26,7 +26,7 @@ pub struct LoadCargoConfig {
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum ProcMacroServerChoice { pub enum ProcMacroServerChoice {
Sysroot, Sysroot,
Explicit(AbsPathBuf, Vec<String>), Explicit(AbsPathBuf),
None, None,
} }
@ -71,14 +71,11 @@ pub fn load_workspace(
let proc_macro_server = match &load_config.with_proc_macro_server { let proc_macro_server = match &load_config.with_proc_macro_server {
ProcMacroServerChoice::Sysroot => ws ProcMacroServerChoice::Sysroot => ws
.find_sysroot_proc_macro_srv() .find_sysroot_proc_macro_srv()
.ok_or_else(|| "failed to find sysroot proc-macro server".to_owned()) .and_then(|it| ProcMacroServer::spawn(it).map_err(Into::into)),
.and_then(|it| { ProcMacroServerChoice::Explicit(path) => {
ProcMacroServer::spawn(it, identity::<&[&str]>(&[])).map_err(|e| e.to_string()) ProcMacroServer::spawn(path.clone()).map_err(Into::into)
}),
ProcMacroServerChoice::Explicit(path, args) => {
ProcMacroServer::spawn(path.clone(), args).map_err(|e| e.to_string())
} }
ProcMacroServerChoice::None => Err("proc macro server disabled".to_owned()), ProcMacroServerChoice::None => Err(anyhow!("proc macro server disabled")),
}; };
let (crate_graph, proc_macros) = ws.to_crate_graph( let (crate_graph, proc_macros) = ws.to_crate_graph(
@ -93,7 +90,7 @@ pub fn load_workspace(
let proc_macros = { let proc_macros = {
let proc_macro_server = match &proc_macro_server { let proc_macro_server = match &proc_macro_server {
Ok(it) => Ok(it), Ok(it) => Ok(it),
Err(e) => Err(e.as_str()), Err(e) => Err(e.to_string()),
}; };
proc_macros proc_macros
.into_iter() .into_iter()
@ -102,7 +99,11 @@ pub fn load_workspace(
crate_id, crate_id,
path.map_or_else( path.map_or_else(
|_| Err("proc macro crate is missing dylib".to_owned()), |_| Err("proc macro crate is missing dylib".to_owned()),
|(_, path)| load_proc_macro(proc_macro_server, &path, &[]), |(_, path)| {
proc_macro_server.as_ref().map_err(Clone::clone).and_then(
|proc_macro_server| load_proc_macro(proc_macro_server, &path, &[]),
)
},
), ),
) )
}) })

View file

@ -437,8 +437,7 @@ config_data! {
/// ///
/// This config takes a map of crate names with the exported proc-macro names to ignore as values. /// This config takes a map of crate names with the exported proc-macro names to ignore as values.
procMacro_ignored: FxHashMap<Box<str>, Box<[Box<str>]>> = "{}", procMacro_ignored: FxHashMap<Box<str>, Box<[Box<str>]>> = "{}",
/// Internal config, path to proc-macro server executable (typically, /// Internal config, path to proc-macro server executable.
/// this is rust-analyzer itself, but we override this in tests).
procMacro_server: Option<PathBuf> = "null", procMacro_server: Option<PathBuf> = "null",
/// Exclude imports from find-all-references. /// Exclude imports from find-all-references.
@ -1102,17 +1101,13 @@ impl Config {
self.data.lru_query_capacities.is_empty().not().then(|| &self.data.lru_query_capacities) self.data.lru_query_capacities.is_empty().not().then(|| &self.data.lru_query_capacities)
} }
pub fn proc_macro_srv(&self) -> Option<(AbsPathBuf, /* is path explicitly set */ bool)> { pub fn proc_macro_srv(&self) -> Option<AbsPathBuf> {
if !self.data.procMacro_enable { self.data
return None; .procMacro_server
} .clone()
Some(match &self.data.procMacro_server { .map(AbsPathBuf::try_from)?
Some(it) => ( .ok()
AbsPathBuf::try_from(it.clone()).unwrap_or_else(|path| self.root_path.join(path)), .map(|path| self.root_path.join(path))
true,
),
None => (AbsPathBuf::assert(std::env::current_exe().ok()?), false),
})
} }
pub fn dummy_replacements(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> { pub fn dummy_replacements(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {

View file

@ -63,7 +63,7 @@ pub(crate) struct GlobalState {
pub(crate) source_root_config: SourceRootConfig, pub(crate) source_root_config: SourceRootConfig,
pub(crate) proc_macro_changed: bool, pub(crate) proc_macro_changed: bool,
pub(crate) proc_macro_clients: Arc<[Result<ProcMacroServer, String>]>, pub(crate) proc_macro_clients: Arc<[anyhow::Result<ProcMacroServer>]>,
pub(crate) flycheck: Arc<[FlycheckHandle]>, pub(crate) flycheck: Arc<[FlycheckHandle]>,
pub(crate) flycheck_sender: Sender<flycheck::Message>, pub(crate) flycheck_sender: Sender<flycheck::Message>,

View file

@ -283,8 +283,8 @@ impl GlobalState {
let mut res = FxHashMap::default(); let mut res = FxHashMap::default();
let chain = proc_macro_clients let chain = proc_macro_clients
.iter() .iter()
.map(|res| res.as_ref().map_err(|e| &**e)) .map(|res| res.as_ref().map_err(|e| e.to_string()))
.chain(iter::repeat_with(|| Err("Proc macros servers are not running"))); .chain(iter::repeat_with(|| Err("Proc macros servers are not running".into())));
for (client, paths) in chain.zip(paths) { for (client, paths) in chain.zip(paths) {
res.extend(paths.into_iter().map(move |(crate_id, res)| { res.extend(paths.into_iter().map(move |(crate_id, res)| {
( (
@ -293,16 +293,18 @@ impl GlobalState {
|_| Err("proc macro crate is missing dylib".to_owned()), |_| Err("proc macro crate is missing dylib".to_owned()),
|(crate_name, path)| { |(crate_name, path)| {
progress(path.display().to_string()); progress(path.display().to_string());
load_proc_macro( client.as_ref().map_err(Clone::clone).and_then(|client| {
client, load_proc_macro(
&path, client,
crate_name &path,
.as_deref() crate_name
.and_then(|crate_name| { .as_deref()
dummy_replacements.get(crate_name).map(|v| &**v) .and_then(|crate_name| {
}) dummy_replacements.get(crate_name).map(|v| &**v)
.unwrap_or_default(), })
) .unwrap_or_default(),
)
})
}, },
), ),
) )
@ -410,39 +412,25 @@ impl GlobalState {
let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude); let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude);
if self.proc_macro_clients.is_empty() || !same_workspaces { if self.proc_macro_clients.is_empty() || !same_workspaces {
if let Some((path, path_manually_set)) = self.config.proc_macro_srv() { if self.config.expand_proc_macros() {
tracing::info!("Spawning proc-macro servers"); tracing::info!("Spawning proc-macro servers");
self.proc_macro_clients = self self.proc_macro_clients = self
.workspaces .workspaces
.iter() .iter()
.map(|ws| { .map(|ws| {
let path = if path_manually_set { let path = match self.config.proc_macro_srv() {
tracing::debug!( Some(path) => path,
"Pro-macro server path explicitly set: {}", None => ws.find_sysroot_proc_macro_srv()?,
path.display()
);
path.clone()
} else {
match ws.find_sysroot_proc_macro_srv() {
Some(server_path) => server_path,
None => path.clone(),
}
};
let args: &[_] = if path.file_stem() == Some("rust-analyzer".as_ref()) {
&["proc-macro"]
} else {
&[]
}; };
tracing::info!(?args, "Using proc-macro server at {}", path.display(),); tracing::info!("Using proc-macro server at {}", path.display(),);
ProcMacroServer::spawn(path.clone(), args).map_err(|err| { ProcMacroServer::spawn(path.clone()).map_err(|err| {
let error = format!( anyhow::anyhow!(
"Failed to run proc-macro server from path {}, error: {:?}", "Failed to run proc-macro server from path {}, error: {:?}",
path.display(), path.display(),
err err
); )
tracing::error!(error);
error
}) })
}) })
.collect() .collect()
@ -740,11 +728,10 @@ impl SourceRootConfig {
/// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace` /// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace`
/// with an identity dummy expander. /// with an identity dummy expander.
pub(crate) fn load_proc_macro( pub(crate) fn load_proc_macro(
server: Result<&ProcMacroServer, &str>, server: &ProcMacroServer,
path: &AbsPath, path: &AbsPath,
dummy_replace: &[Box<str>], dummy_replace: &[Box<str>],
) -> ProcMacroLoadResult { ) -> ProcMacroLoadResult {
let server = server.map_err(ToOwned::to_owned)?;
let res: Result<Vec<_>, String> = (|| { let res: Result<Vec<_>, String> = (|| {
let dylib = MacroDylib::new(path.to_path_buf()); let dylib = MacroDylib::new(path.to_path_buf());
let vec = server.load_dylib(dylib).map_err(|e| format!("{e}"))?; let vec = server.load_dylib(dylib).map_err(|e| format!("{e}"))?;

View file

@ -679,8 +679,7 @@ This config takes a map of crate names with the exported proc-macro names to ign
[[rust-analyzer.procMacro.server]]rust-analyzer.procMacro.server (default: `null`):: [[rust-analyzer.procMacro.server]]rust-analyzer.procMacro.server (default: `null`)::
+ +
-- --
Internal config, path to proc-macro server executable (typically, Internal config, path to proc-macro server executable.
this is rust-analyzer itself, but we override this in tests).
-- --
[[rust-analyzer.references.excludeImports]]rust-analyzer.references.excludeImports (default: `false`):: [[rust-analyzer.references.excludeImports]]rust-analyzer.references.excludeImports (default: `false`)::
+ +

View file

@ -1305,7 +1305,7 @@
"type": "object" "type": "object"
}, },
"rust-analyzer.procMacro.server": { "rust-analyzer.procMacro.server": {
"markdownDescription": "Internal config, path to proc-macro server executable (typically,\nthis is rust-analyzer itself, but we override this in tests).", "markdownDescription": "Internal config, path to proc-macro server executable.",
"default": null, "default": null,
"type": [ "type": [
"null", "null",