Skip to content

Commit 7ba94a8

Browse files
committed
Auto merge of #12858 - fasterthanlime:proc-macro-srv-bin, r=Veykril
Add `rust-analyzer-proc-macro-srv` binary, use it if found in sysroot This adds a `bin` crate which simply runs `proc_macro_srv::cli::run()` (it does no CLI argument parsing, nothing). The intent is to build that crate in Rust CI as part of the `dist::Rustc` component, then ship it in the sysroot: it would probably land in something like `~/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu/libexec/proc-macro-srv-cli`. This makes rust-lang/rustup#3022 less pressing. (Instead of teaching RA about rustup components, we simply teach it to look in the sysroot via `rustc --print sysroot`. If it can't find `proc-macro-srv-cli`, it falls back to its own `proc-macro` subcommand). This is closely related to #12803 (but doesn't close it yet). Things to address now: * [ ] What should the binary be named? What should the crate be named? We can pick different names with `[bin]` in the `Cargo.toml` Things to address later: * Disable the "multi ABI compatibility scheme" when building that binary in Rust CI (that'll probably happen in `rust-lang/rust`) * Teaching RA to look in the sysroot Things to address much, much later: * Is JSON a good fit here * Do we want to add versioning to future-proof it? * Other bikesheds When built with `--features sysroot` on `nightly-2022-07-23-x86_64-unknown-linux-gnu`, the binary is 7.4MB. After stripping debuginfo, it's 2.6MB. When compressed to `.tar.xz`, it's 619KB. In a Zulip discussion, `@jyn514` and `@Mark-Simulacrum` seemed to think that those sizes weren't a stopper for including the binary in the rustc component, even before we shrink it down further.
2 parents 0b1ed70 + 2c2520f commit 7ba94a8

File tree

9 files changed

+126
-13
lines changed

9 files changed

+126
-13
lines changed

Cargo.lock

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/proc-macro-api/src/process.rs

+1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ fn mk_child(
8686
) -> io::Result<Child> {
8787
Command::new(path.as_os_str())
8888
.args(args)
89+
.env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable")
8990
.stdin(Stdio::piped())
9091
.stdout(Stdio::piped())
9192
.stderr(Stdio::inherit())

crates/proc-macro-srv-cli/Cargo.toml

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "proc-macro-srv-cli"
3+
version = "0.0.0"
4+
description = "TBD"
5+
license = "MIT OR Apache-2.0"
6+
edition = "2021"
7+
rust-version = "1.57"
8+
9+
[dependencies]
10+
proc-macro-srv = { version = "0.0.0", path = "../proc-macro-srv" }
11+
12+
[features]
13+
sysroot-abi = ["proc-macro-srv/sysroot-abi"]
14+
15+
[[bin]]
16+
name = "rust-analyzer-proc-macro-srv"
17+
path = "src/main.rs"

crates/proc-macro-srv-cli/src/main.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//! A standalone binary for `proc-macro-srv`.
2+
3+
use proc_macro_srv::cli;
4+
5+
fn main() -> std::io::Result<()> {
6+
let v = std::env::var("RUST_ANALYZER_INTERNALS_DO_NOT_USE");
7+
match v.as_deref() {
8+
Ok("this is unstable") => {
9+
// very well, if you must
10+
}
11+
_ => {
12+
eprintln!("If you're rust-analyzer, you can use this tool by exporting RUST_ANALYZER_INTERNALS_DO_NOT_USE='this is unstable'.");
13+
eprintln!("If not, you probably shouldn't use this tool. But do what you want: I'm an error message, not a cop.");
14+
std::process::exit(122);
15+
}
16+
}
17+
18+
cli::run()
19+
}

crates/project-model/src/project_json.rs

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ use crate::cfg_flag::CfgFlag;
1717
/// Roots and crates that compose this Rust project.
1818
#[derive(Clone, Debug, Eq, PartialEq)]
1919
pub struct ProjectJson {
20+
/// e.g. `path/to/sysroot`
21+
pub(crate) sysroot: Option<AbsPathBuf>,
22+
/// e.g. `path/to/sysroot/lib/rustlib/src/rust`
2023
pub(crate) sysroot_src: Option<AbsPathBuf>,
2124
project_root: AbsPathBuf,
2225
crates: Vec<Crate>,
@@ -52,6 +55,7 @@ impl ProjectJson {
5255
/// configuration.
5356
pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson {
5457
ProjectJson {
58+
sysroot: data.sysroot.map(|it| base.join(it)),
5559
sysroot_src: data.sysroot_src.map(|it| base.join(it)),
5660
project_root: base.to_path_buf(),
5761
crates: data
@@ -122,6 +126,7 @@ impl ProjectJson {
122126

123127
#[derive(Deserialize, Debug, Clone)]
124128
pub struct ProjectJsonData {
129+
sysroot: Option<PathBuf>,
125130
sysroot_src: Option<PathBuf>,
126131
crates: Vec<CrateData>,
127132
}

crates/project-model/src/sysroot.rs

+16-5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use crate::{utf8_stdout, ManifestPath};
1515
#[derive(Debug, Clone, Eq, PartialEq)]
1616
pub struct Sysroot {
1717
root: AbsPathBuf,
18+
src_root: AbsPathBuf,
1819
crates: Arena<SysrootCrateData>,
1920
}
2021

@@ -35,10 +36,19 @@ impl ops::Index<SysrootCrate> for Sysroot {
3536
}
3637

3738
impl Sysroot {
39+
/// Returns sysroot "root" directory, where `bin/`, `etc/`, `lib/`, `libexec/`
40+
/// subfolder live, like:
41+
/// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu`
3842
pub fn root(&self) -> &AbsPath {
3943
&self.root
4044
}
4145

46+
/// Returns the sysroot "source" directory, where stdlib sources are located, like:
47+
/// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library`
48+
pub fn src_root(&self) -> &AbsPath {
49+
&self.src_root
50+
}
51+
4252
pub fn public_deps(&self) -> impl Iterator<Item = (&'static str, SysrootCrate, bool)> + '_ {
4353
// core is added as a dependency before std in order to
4454
// mimic rustcs dependency order
@@ -61,7 +71,7 @@ impl Sysroot {
6171
tracing::debug!("Discovering sysroot for {}", dir.display());
6272
let sysroot_dir = discover_sysroot_dir(dir)?;
6373
let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, dir)?;
64-
let res = Sysroot::load(sysroot_src_dir)?;
74+
let res = Sysroot::load(sysroot_dir, sysroot_src_dir)?;
6575
Ok(res)
6676
}
6777

@@ -71,14 +81,15 @@ impl Sysroot {
7181
discover_sysroot_dir(current_dir).ok().and_then(|sysroot_dir| get_rustc_src(&sysroot_dir))
7282
}
7383

74-
pub fn load(sysroot_src_dir: AbsPathBuf) -> Result<Sysroot> {
75-
let mut sysroot = Sysroot { root: sysroot_src_dir, crates: Arena::default() };
84+
pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf) -> Result<Sysroot> {
85+
let mut sysroot =
86+
Sysroot { root: sysroot_dir, src_root: sysroot_src_dir, crates: Arena::default() };
7687

7788
for path in SYSROOT_CRATES.trim().lines() {
7889
let name = path.split('/').last().unwrap();
7990
let root = [format!("{}/src/lib.rs", path), format!("lib{}/lib.rs", path)]
8091
.into_iter()
81-
.map(|it| sysroot.root.join(it))
92+
.map(|it| sysroot.src_root.join(it))
8293
.filter_map(|it| ManifestPath::try_from(it).ok())
8394
.find(|it| fs::metadata(it).is_ok());
8495

@@ -119,7 +130,7 @@ impl Sysroot {
119130
};
120131
anyhow::bail!(
121132
"could not find libcore in sysroot path `{}`{}",
122-
sysroot.root.as_path().display(),
133+
sysroot.src_root.as_path().display(),
123134
var_note,
124135
);
125136
}

crates/project-model/src/tests.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,11 @@ fn get_test_path(file: &str) -> PathBuf {
7575

7676
fn get_fake_sysroot() -> Sysroot {
7777
let sysroot_path = get_test_path("fake-sysroot");
78-
let sysroot_src_dir = AbsPathBuf::assert(sysroot_path);
79-
Sysroot::load(sysroot_src_dir).unwrap()
78+
// there's no `libexec/` directory with a `proc-macro-srv` binary in that
79+
// fake sysroot, so we give them both the same path:
80+
let sysroot_dir = AbsPathBuf::assert(sysroot_path);
81+
let sysroot_src_dir = sysroot_dir.clone();
82+
Sysroot::load(sysroot_dir, sysroot_src_dir).unwrap()
8083
}
8184

8285
fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {

crates/project-model/src/workspace.rs

+20-4
Original file line numberDiff line numberDiff line change
@@ -230,10 +230,26 @@ impl ProjectWorkspace {
230230
project_json: ProjectJson,
231231
target: Option<&str>,
232232
) -> Result<ProjectWorkspace> {
233-
let sysroot = match &project_json.sysroot_src {
234-
Some(path) => Some(Sysroot::load(path.clone())?),
235-
None => None,
233+
let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) {
234+
(Some(sysroot), Some(sysroot_src)) => Some(Sysroot::load(sysroot, sysroot_src)?),
235+
(Some(sysroot), None) => {
236+
// assume sysroot is structured like rustup's and guess `sysroot_src`
237+
let sysroot_src =
238+
sysroot.join("lib").join("rustlib").join("src").join("rust").join("library");
239+
240+
Some(Sysroot::load(sysroot, sysroot_src)?)
241+
}
242+
(None, Some(sysroot_src)) => {
243+
// assume sysroot is structured like rustup's and guess `sysroot`
244+
let mut sysroot = sysroot_src.clone();
245+
for _ in 0..5 {
246+
sysroot.pop();
247+
}
248+
Some(Sysroot::load(sysroot, sysroot_src)?)
249+
}
250+
(None, None) => None,
236251
};
252+
237253
let rustc_cfg = rustc_cfg::get(None, target);
238254
Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
239255
}
@@ -345,7 +361,7 @@ impl ProjectWorkspace {
345361
})
346362
.chain(sysroot.iter().map(|sysroot| PackageRoot {
347363
is_local: false,
348-
include: vec![sysroot.root().to_path_buf()],
364+
include: vec![sysroot.src_root().to_path_buf()],
349365
exclude: Vec::new(),
350366
}))
351367
.chain(rustc.iter().flat_map(|rustc| {

crates/rust-analyzer/src/reload.rs

+36-2
Original file line numberDiff line numberDiff line change
@@ -305,8 +305,42 @@ impl GlobalState {
305305

306306
if self.proc_macro_clients.is_empty() {
307307
if let Some((path, args)) = self.config.proc_macro_srv() {
308-
self.proc_macro_clients = (0..self.workspaces.len())
309-
.map(|_| {
308+
self.proc_macro_clients = self
309+
.workspaces
310+
.iter()
311+
.map(|ws| {
312+
let mut args = args.clone();
313+
let mut path = path.clone();
314+
315+
if let ProjectWorkspace::Cargo { sysroot, .. } = ws {
316+
tracing::info!("Found a cargo workspace...");
317+
if let Some(sysroot) = sysroot.as_ref() {
318+
tracing::info!("Found a cargo workspace with a sysroot...");
319+
let server_path = sysroot
320+
.root()
321+
.join("libexec")
322+
.join("rust-analyzer-proc-macro-srv");
323+
if std::fs::metadata(&server_path).is_ok() {
324+
tracing::info!(
325+
"And the server exists at {}",
326+
server_path.display()
327+
);
328+
path = server_path;
329+
args = vec![];
330+
} else {
331+
tracing::info!(
332+
"And the server does not exist at {}",
333+
server_path.display()
334+
);
335+
}
336+
}
337+
}
338+
339+
tracing::info!(
340+
"Using proc-macro server at {} with args {:?}",
341+
path.display(),
342+
args
343+
);
310344
ProcMacroServer::spawn(path.clone(), args.clone()).map_err(|err| {
311345
let error = format!(
312346
"Failed to run proc_macro_srv from path {}, error: {:?}",

0 commit comments

Comments
 (0)