Skip to content
This repository was archived by the owner on Dec 29, 2022. It is now read-only.

Commit 3bcfa0f

Browse files
committed
Implement out-of-process compilation
1 parent b06cbba commit 3bcfa0f

19 files changed

+1042
-71
lines changed

Cargo.lock

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

Cargo.toml

+7-2
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,17 @@ rls-data = "0.19"
2727
rls-rustc = { version = "0.6.0", path = "rls-rustc" }
2828
rls-span = "0.5"
2929
rls-vfs = "0.8"
30+
rls-ipc = { version = "0.1.0", path = "rls-ipc", optional = true }
3031

3132
cargo = { git = "https://github.com/rust-lang/cargo", rev = "1f74bdf4494f4d51dbe3a6af5474e39c8d194ad6" }
3233
cargo_metadata = "0.8"
3334
clippy_lints = { git = "https://github.com/rust-lang/rust-clippy", rev = "72da1015d6d918fe1b29170acbf486d30e0c2695", optional = true }
3435
env_logger = "0.6"
3536
failure = "0.1.1"
37+
futures = { version = "0.1", optional = true }
3638
home = "0.5"
3739
itertools = "0.8"
38-
jsonrpc-core = "12"
40+
jsonrpc-core = "13"
3941
lsp-types = { version = "0.60", features = ["proposed"] }
4042
lazy_static = "1"
4143
log = "0.4"
@@ -50,6 +52,7 @@ serde = "1.0"
5052
serde_json = "1.0"
5153
serde_derive = "1.0"
5254
serde_ignored = "0.1"
55+
tokio = { version = "0.1", optional = true }
5356
url = "2"
5457
walkdir = "2"
5558
regex = "1"
@@ -76,4 +79,6 @@ tokio-timer = "0.2"
7679
rustc_tools_util = "0.2"
7780

7881
[features]
79-
clippy = ["clippy_lints"]
82+
clippy = ["clippy_lints", "rls-rustc/clippy"]
83+
ipc = ["tokio", "futures", "rls-rustc/ipc", "rls-ipc/server"]
84+
default = []

rls-ipc/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/target/
2+
**/*.rs.bk
3+
Cargo.lock

rls-ipc/Cargo.toml

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[package]
2+
name = "rls-ipc"
3+
version = "0.1.0"
4+
authors = ["Igor Matuszewski <Xanewok@gmail.com>"]
5+
edition = "2018"
6+
description = "Inter-process communication (IPC) layer between RLS and rustc"
7+
license = "Apache-2.0/MIT"
8+
repository = "https://github.com/rust-lang/rls"
9+
categories = ["development-tools"]
10+
11+
[dependencies]
12+
jsonrpc-core = "13"
13+
jsonrpc-core-client = "13"
14+
jsonrpc-derive = "13"
15+
jsonrpc-ipc-server = { version = "13", optional = true }
16+
rls-data = "0.19"
17+
serde = { version = "1.0", features = ["derive"] }
18+
19+
[features]
20+
client = ["jsonrpc-core-client/ipc"]
21+
server = ["jsonrpc-ipc-server"]

rls-ipc/src/client.rs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//! Allows to connect to an IPC server.
2+
3+
use crate::rpc::callbacks::gen_client::Client as CallbacksClient;
4+
use crate::rpc::file_loader::gen_client::Client as FileLoaderClient;
5+
6+
pub use jsonrpc_core_client::transports::ipc::connect;
7+
pub use jsonrpc_core_client::{RpcChannel, RpcError};
8+
9+
/// Joint IPC client.
10+
#[derive(Clone)]
11+
pub struct Client {
12+
/// File loader interface
13+
pub file_loader: FileLoaderClient,
14+
/// Callbacks interface
15+
pub callbacks: CallbacksClient,
16+
}
17+
18+
impl From<RpcChannel> for Client {
19+
fn from(channel: RpcChannel) -> Self {
20+
Client {
21+
file_loader: FileLoaderClient::from(channel.clone()),
22+
callbacks: CallbacksClient::from(channel),
23+
}
24+
}
25+
}

rls-ipc/src/lib.rs

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//! Inter-process communication (IPC) layer between RLS and rustc.
2+
3+
#![deny(missing_docs)]
4+
5+
#[cfg(feature = "client")]
6+
pub mod client;
7+
pub mod rpc;
8+
#[cfg(feature = "server")]
9+
pub mod server;

rls-ipc/src/rpc.rs

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//! Available remote procedure call (RPC) interfaces.
2+
3+
use std::collections::{HashMap, HashSet};
4+
use std::path::PathBuf;
5+
6+
use jsonrpc_derive::rpc;
7+
use serde::{Deserialize, Serialize};
8+
9+
pub use jsonrpc_core::{Error, Result};
10+
11+
// Separated because #[rpc] macro generated a `gen_client` mod and so two
12+
// interfaces cannot be derived in the same scope due to a generated name clash
13+
/// RPC interface for an overriden file loader to be used inside `rustc`.
14+
pub mod file_loader {
15+
use super::*;
16+
// Expanded via #[rpc]
17+
pub use gen_client::Client;
18+
pub use rpc_impl_Rpc::gen_server::Rpc as Server;
19+
20+
#[rpc]
21+
/// RPC interface for an overriden file loader to be used inside `rustc`.
22+
pub trait Rpc {
23+
/// Query the existence of a file.
24+
#[rpc(name = "file_exists")]
25+
fn file_exists(&self, path: PathBuf) -> Result<bool>;
26+
27+
/// Returns an absolute path to a file, if possible.
28+
#[rpc(name = "abs_path")]
29+
fn abs_path(&self, path: PathBuf) -> Result<Option<PathBuf>>;
30+
31+
/// Read the contents of an UTF-8 file into memory.
32+
#[rpc(name = "read_file")]
33+
fn read_file(&self, path: PathBuf) -> Result<String>;
34+
}
35+
}
36+
37+
// Separated because #[rpc] macro generated a `gen_client` mod and so two
38+
// interfaces cannot be derived in the same scope due to a generated name clash
39+
/// RPC interface to feed back data from `rustc` instances.
40+
pub mod callbacks {
41+
use super::*;
42+
// Expanded via #[rpc]
43+
pub use gen_client::Client;
44+
pub use rpc_impl_Rpc::gen_server::Rpc as Server;
45+
46+
#[rpc]
47+
/// RPC interface to feed back data from `rustc` instances.
48+
pub trait Rpc {
49+
/// Hands back computed analysis data for the compiled crate
50+
#[rpc(name = "complete_analysis")]
51+
fn complete_analysis(&self, analysis: rls_data::Analysis) -> Result<()>;
52+
53+
/// Hands back computed input files for the compiled crate
54+
#[rpc(name = "input_files")]
55+
fn input_files(&self, input_files: HashMap<PathBuf, HashSet<Crate>>) -> Result<()>;
56+
}
57+
}
58+
59+
/// Build system-agnostic, basic compilation unit
60+
#[derive(PartialEq, Eq, Hash, Debug, Clone, Deserialize, Serialize)]
61+
pub struct Crate {
62+
/// Crate name
63+
pub name: String,
64+
/// Optional path to a crate root
65+
pub src_path: Option<PathBuf>,
66+
/// Edition in which a given crate is compiled
67+
pub edition: Edition,
68+
/// From rustc; mainly used to group other properties used to disambiguate a
69+
/// given compilation unit.
70+
pub disambiguator: (u64, u64),
71+
}
72+
73+
/// Rust edition
74+
#[derive(PartialEq, Eq, Hash, Debug, PartialOrd, Ord, Copy, Clone, Deserialize, Serialize)]
75+
pub enum Edition {
76+
/// Rust 2015
77+
Edition2015,
78+
/// Rust 2018
79+
Edition2018,
80+
}

rls-ipc/src/server.rs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
//! Includes facility functions to start an IPC server.
2+
3+
pub use jsonrpc_ipc_server::{CloseHandle, ServerBuilder};

rls-rustc/Cargo.toml

+15
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,18 @@ repository = "https://github.com/rust-lang/rls"
99
categories = ["development-tools"]
1010

1111
[dependencies]
12+
env_logger = "0.6"
13+
log = "0.4"
14+
failure = "0.1"
15+
rand = "0.6"
16+
clippy_lints = { git = "https://github.com/rust-lang/rust-clippy", rev = "72da1015d6d918fe1b29170acbf486d30e0c2695", optional = true }
17+
tokio = { version = "0.1", optional = true }
18+
futures = { version = "0.1", optional = true }
19+
serde = { version = "1", features = ["derive"], optional = true }
20+
rls-data = { version = "0.19", optional = true }
21+
rls-ipc = { path = "../rls-ipc", optional = true }
22+
23+
[features]
24+
clippy = ["clippy_lints"]
25+
ipc = ["tokio", "futures", "serde", "rls-data", "rls-ipc/client"]
26+
default = []

rls-rustc/src/bin/rustc.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
fn main() {
2-
rls_rustc::run();
1+
fn main() -> Result<(), ()> {
2+
rls_rustc::run()
33
}

rls-rustc/src/clippy.rs

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//! Copied from rls/src/config.rs
2+
3+
use std::str::FromStr;
4+
5+
#[derive(Debug, Clone, Copy, PartialEq)]
6+
pub enum ClippyPreference {
7+
/// Disable clippy.
8+
Off,
9+
/// Enable clippy, but `allow` clippy lints (i.e., require `warn` override).
10+
OptIn,
11+
/// Enable clippy.
12+
On,
13+
}
14+
15+
pub fn preference() -> Option<ClippyPreference> {
16+
std::env::var("RLS_CLIPPY_PREFERENCE").ok().and_then(|pref| FromStr::from_str(&pref).ok())
17+
}
18+
19+
/// Permissive deserialization for `ClippyPreference`
20+
/// "opt-in", "Optin" -> `ClippyPreference::OptIn`
21+
impl FromStr for ClippyPreference {
22+
type Err = ();
23+
fn from_str(s: &str) -> Result<Self, Self::Err> {
24+
match s.to_lowercase().as_str() {
25+
"off" => Ok(ClippyPreference::Off),
26+
"optin" | "opt-in" => Ok(ClippyPreference::OptIn),
27+
"on" => Ok(ClippyPreference::On),
28+
_ => Err(()),
29+
}
30+
}
31+
}
32+
33+
pub fn adjust_args(args: Vec<String>, preference: ClippyPreference) -> Vec<String> {
34+
if preference != ClippyPreference::Off {
35+
// Allow feature gating in the same way as `cargo clippy`
36+
let mut clippy_args = vec!["--cfg".to_owned(), r#"feature="cargo-clippy""#.to_owned()];
37+
38+
if preference == ClippyPreference::OptIn {
39+
// `OptIn`: Require explicit `#![warn(clippy::all)]` annotation in each workspace crate
40+
clippy_args.push("-A".to_owned());
41+
clippy_args.push("clippy::all".to_owned());
42+
}
43+
44+
args.iter().map(ToOwned::to_owned).chain(clippy_args).collect()
45+
} else {
46+
args.to_owned()
47+
}
48+
}
49+
50+
#[cfg(feature = "clippy")]
51+
pub fn after_parse_callback(compiler: &rustc_interface::interface::Compiler) {
52+
use rustc_plugin::registry::Registry;
53+
54+
let sess = compiler.session();
55+
let mut registry = Registry::new(
56+
sess,
57+
compiler
58+
.parse()
59+
.expect(
60+
"at this compilation stage \
61+
the crate must be parsed",
62+
)
63+
.peek()
64+
.span,
65+
);
66+
registry.args_hidden = Some(Vec::new());
67+
68+
let conf = clippy_lints::read_conf(&registry);
69+
clippy_lints::register_plugins(&mut registry, &conf);
70+
71+
let Registry {
72+
early_lint_passes, late_lint_passes, lint_groups, llvm_passes, attributes, ..
73+
} = registry;
74+
let mut ls = sess.lint_store.borrow_mut();
75+
for pass in early_lint_passes {
76+
ls.register_early_pass(Some(sess), true, false, pass);
77+
}
78+
for pass in late_lint_passes {
79+
ls.register_late_pass(Some(sess), true, false, false, pass);
80+
}
81+
82+
for (name, (to, deprecated_name)) in lint_groups {
83+
ls.register_group(Some(sess), true, name, deprecated_name, to);
84+
}
85+
clippy_lints::register_pre_expansion_lints(sess, &mut ls, &conf);
86+
clippy_lints::register_renamed(&mut ls);
87+
88+
sess.plugin_llvm_passes.borrow_mut().extend(llvm_passes);
89+
sess.plugin_attributes.borrow_mut().extend(attributes);
90+
}

rls-rustc/src/ipc.rs

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use std::collections::{HashMap, HashSet};
2+
use std::io;
3+
use std::path::{Path, PathBuf};
4+
5+
use failure::Fail;
6+
use futures::Future;
7+
8+
use rls_ipc::client::{Client as JointClient, RpcChannel, RpcError};
9+
use rls_ipc::rpc::callbacks::Client as CallbacksClient;
10+
use rls_ipc::rpc::file_loader::Client as FileLoaderClient;
11+
12+
pub use rls_ipc::client::connect;
13+
14+
#[derive(Clone)]
15+
pub struct Client(JointClient);
16+
17+
impl From<RpcChannel> for Client {
18+
fn from(channel: RpcChannel) -> Self {
19+
Client(channel.into())
20+
}
21+
}
22+
23+
#[derive(Clone)]
24+
pub struct IpcFileLoader(FileLoaderClient);
25+
26+
impl IpcFileLoader {
27+
pub fn into_boxed(self) -> Option<Box<dyn syntax::source_map::FileLoader + Send + Sync>> {
28+
Some(Box::new(self))
29+
}
30+
}
31+
32+
impl syntax::source_map::FileLoader for IpcFileLoader {
33+
fn file_exists(&self, path: &Path) -> bool {
34+
self.0.file_exists(path.to_owned()).wait().unwrap()
35+
}
36+
37+
fn abs_path(&self, path: &Path) -> Option<PathBuf> {
38+
self.0.abs_path(path.to_owned()).wait().ok()?
39+
}
40+
41+
fn read_file(&self, path: &Path) -> io::Result<String> {
42+
self.0
43+
.read_file(path.to_owned())
44+
.wait()
45+
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.compat()))
46+
}
47+
}
48+
49+
#[derive(Clone)]
50+
pub struct IpcCallbacks(CallbacksClient);
51+
52+
impl IpcCallbacks {
53+
pub fn complete_analysis(
54+
&self,
55+
analysis: rls_data::Analysis,
56+
) -> impl Future<Item = (), Error = RpcError> {
57+
self.0.complete_analysis(analysis)
58+
}
59+
60+
pub fn input_files(
61+
&self,
62+
input_files: HashMap<PathBuf, HashSet<rls_ipc::rpc::Crate>>,
63+
) -> impl Future<Item = (), Error = RpcError> {
64+
self.0.input_files(input_files)
65+
}
66+
}
67+
68+
impl Client {
69+
pub fn split(self) -> (IpcFileLoader, IpcCallbacks) {
70+
let JointClient { file_loader, callbacks } = self.0;
71+
(IpcFileLoader(file_loader), IpcCallbacks(callbacks))
72+
}
73+
}

0 commit comments

Comments
 (0)