Skip to content

Add rust-analyzer-proc-macro-srv binary, use it if found in sysroot #12858

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/proc-macro-api/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ fn mk_child(
) -> io::Result<Child> {
Command::new(path.as_os_str())
.args(args)
.env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::inherit())
Expand Down
17 changes: 17 additions & 0 deletions crates/proc-macro-srv-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "proc-macro-srv-cli"
version = "0.0.0"
description = "TBD"
license = "MIT OR Apache-2.0"
edition = "2021"
rust-version = "1.57"

[dependencies]
proc-macro-srv = { version = "0.0.0", path = "../proc-macro-srv" }

[features]
sysroot-abi = ["proc-macro-srv/sysroot-abi"]

[[bin]]
name = "rust-analyzer-proc-macro-srv"
path = "src/main.rs"
19 changes: 19 additions & 0 deletions crates/proc-macro-srv-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//! A standalone binary for `proc-macro-srv`.

use proc_macro_srv::cli;

fn main() -> std::io::Result<()> {
let v = std::env::var("RUST_ANALYZER_INTERNALS_DO_NOT_USE");
match v.as_deref() {
Ok("this is unstable") => {
// very well, if you must
}
_ => {
eprintln!("If you're rust-analyzer, you can use this tool by exporting RUST_ANALYZER_INTERNALS_DO_NOT_USE='this is unstable'.");
eprintln!("If not, you probably shouldn't use this tool. But do what you want: I'm an error message, not a cop.");
std::process::exit(122);
}
}

cli::run()
}
5 changes: 5 additions & 0 deletions crates/project-model/src/project_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ use crate::cfg_flag::CfgFlag;
/// Roots and crates that compose this Rust project.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ProjectJson {
/// e.g. `path/to/sysroot`
pub(crate) sysroot: Option<AbsPathBuf>,
/// e.g. `path/to/sysroot/lib/rustlib/src/rust`
pub(crate) sysroot_src: Option<AbsPathBuf>,
project_root: AbsPathBuf,
crates: Vec<Crate>,
Expand Down Expand Up @@ -52,6 +55,7 @@ impl ProjectJson {
/// configuration.
pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson {
ProjectJson {
sysroot: data.sysroot.map(|it| base.join(it)),
sysroot_src: data.sysroot_src.map(|it| base.join(it)),
project_root: base.to_path_buf(),
crates: data
Expand Down Expand Up @@ -122,6 +126,7 @@ impl ProjectJson {

#[derive(Deserialize, Debug, Clone)]
pub struct ProjectJsonData {
sysroot: Option<PathBuf>,
sysroot_src: Option<PathBuf>,
crates: Vec<CrateData>,
}
Expand Down
21 changes: 16 additions & 5 deletions crates/project-model/src/sysroot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::{utf8_stdout, ManifestPath};
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Sysroot {
root: AbsPathBuf,
src_root: AbsPathBuf,
crates: Arena<SysrootCrateData>,
}

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

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

/// Returns the sysroot "source" directory, where stdlib sources are located, like:
/// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library`
pub fn src_root(&self) -> &AbsPath {
&self.src_root
}

pub fn public_deps(&self) -> impl Iterator<Item = (&'static str, SysrootCrate, bool)> + '_ {
// core is added as a dependency before std in order to
// mimic rustcs dependency order
Expand All @@ -61,7 +71,7 @@ impl Sysroot {
tracing::debug!("Discovering sysroot for {}", dir.display());
let sysroot_dir = discover_sysroot_dir(dir)?;
let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, dir)?;
let res = Sysroot::load(sysroot_src_dir)?;
let res = Sysroot::load(sysroot_dir, sysroot_src_dir)?;
Ok(res)
}

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

pub fn load(sysroot_src_dir: AbsPathBuf) -> Result<Sysroot> {
let mut sysroot = Sysroot { root: sysroot_src_dir, crates: Arena::default() };
pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf) -> Result<Sysroot> {
let mut sysroot =
Sysroot { root: sysroot_dir, src_root: sysroot_src_dir, crates: Arena::default() };

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

Expand Down Expand Up @@ -119,7 +130,7 @@ impl Sysroot {
};
anyhow::bail!(
"could not find libcore in sysroot path `{}`{}",
sysroot.root.as_path().display(),
sysroot.src_root.as_path().display(),
var_note,
);
}
Expand Down
7 changes: 5 additions & 2 deletions crates/project-model/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,11 @@ fn get_test_path(file: &str) -> PathBuf {

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

fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
Expand Down
24 changes: 20 additions & 4 deletions crates/project-model/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,26 @@ impl ProjectWorkspace {
project_json: ProjectJson,
target: Option<&str>,
) -> Result<ProjectWorkspace> {
let sysroot = match &project_json.sysroot_src {
Some(path) => Some(Sysroot::load(path.clone())?),
None => None,
let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) {
(Some(sysroot), Some(sysroot_src)) => Some(Sysroot::load(sysroot, sysroot_src)?),
(Some(sysroot), None) => {
// assume sysroot is structured like rustup's and guess `sysroot_src`
let sysroot_src =
sysroot.join("lib").join("rustlib").join("src").join("rust").join("library");

Some(Sysroot::load(sysroot, sysroot_src)?)
}
(None, Some(sysroot_src)) => {
// assume sysroot is structured like rustup's and guess `sysroot`
let mut sysroot = sysroot_src.clone();
for _ in 0..5 {
sysroot.pop();
}
Some(Sysroot::load(sysroot, sysroot_src)?)
}
(None, None) => None,
};

let rustc_cfg = rustc_cfg::get(None, target);
Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
}
Expand Down Expand Up @@ -345,7 +361,7 @@ impl ProjectWorkspace {
})
.chain(sysroot.iter().map(|sysroot| PackageRoot {
is_local: false,
include: vec![sysroot.root().to_path_buf()],
include: vec![sysroot.src_root().to_path_buf()],
exclude: Vec::new(),
}))
.chain(rustc.iter().flat_map(|rustc| {
Expand Down
38 changes: 36 additions & 2 deletions crates/rust-analyzer/src/reload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,42 @@ impl GlobalState {

if self.proc_macro_clients.is_empty() {
if let Some((path, args)) = self.config.proc_macro_srv() {
self.proc_macro_clients = (0..self.workspaces.len())
.map(|_| {
self.proc_macro_clients = self
.workspaces
.iter()
.map(|ws| {
let mut args = args.clone();
let mut path = path.clone();

if let ProjectWorkspace::Cargo { sysroot, .. } = ws {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So at this point in time, we don't have any support for ProjectWorkspace::Json, even if sysroot is set?

Copy link
Member

@Veykril Veykril Aug 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did proc-macros ever work for project.json workspaces actually? From the looks of it we never even build build scripts for them since we can't really do that

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, proc macros should work with project.json workspaces. We have a field that allows specifying the path of the compiled proc macro.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you point me to where the proc-macro-srv is spawned for ProjectWorkspace::Json workspaces?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some(it) => load_proc_macro(
I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@woody77 that match is definitely missing an arm I forgot to add when I added the additional field to rust-project.json's schema. I also didn't have any projects to test it with. I encourage you to open the small PR to fix it!

tracing::info!("Found a cargo workspace...");
if let Some(sysroot) = sysroot.as_ref() {
tracing::info!("Found a cargo workspace with a sysroot...");
let server_path = sysroot
.root()
.join("libexec")
.join("rust-analyzer-proc-macro-srv");
if std::fs::metadata(&server_path).is_ok() {
tracing::info!(
"And the server exists at {}",
server_path.display()
);
path = server_path;
args = vec![];
} else {
tracing::info!(
"And the server does not exist at {}",
server_path.display()
);
}
}
}

tracing::info!(
"Using proc-macro server at {} with args {:?}",
path.display(),
args
);
ProcMacroServer::spawn(path.clone(), args.clone()).map_err(|err| {
let error = format!(
"Failed to run proc_macro_srv from path {}, error: {:?}",
Expand Down