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

Commit fcbd932

Browse files
committed
Support external builds
This is currently enabled via `build_command` config option. The specified command runs as a build procedure; it should output a list of save-analysis .json files to be reloaded by the RLS so that the usual features, e.g. type info on hover, work. Known bugs: * We should force `build_on_save` when `build_command` is specified (not a bug though) * Files dirtiness sometimes isn't properly reset when doing the external build, thus leading to constant rebuilds (might be fixed with build_on_save?)
1 parent 66c7898 commit fcbd932

File tree

5 files changed

+122
-4
lines changed

5 files changed

+122
-4
lines changed

Cargo.lock

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

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ categories = ["development-tools"]
1212

1313
build = "build.rs"
1414

15+
[patch.crates-io]
16+
rls-data = { git = "https://github.com/Xanewok/rls-data", branch = "serde-pathbuf" }
17+
1518
[dependencies]
1619
cargo = { git = "https://github.com/rust-lang/cargo", rev = "6a7672ef5344c1bb570610f2574250fbee932355" }
1720
cargo_metadata = "0.6"

src/build/external.rs

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! Performs a build using a provided black-box build command, which ought to
12+
//! return a list of save-analysis JSON files to be reloaded by the RLS.
13+
//! Please note that since the command is ran externally (at a file/OS level)
14+
//! this doesn't work with files that are not saved.
15+
16+
use std::io::Read;
17+
use std::fs::File;
18+
use std::path::{Path, PathBuf};
19+
use std::process::{Command, Stdio};
20+
21+
use super::BuildResult;
22+
23+
use rls_data::Analysis;
24+
use log::{log, trace};
25+
26+
/// Performs a build using an external command and interprets the results.
27+
/// The command should output on stdout a list of save-analysis .json files
28+
/// to be reloaded by the RLS.
29+
/// Note: This is *very* experimental and preliminary - this can viewed as
30+
/// an experimentation until a more complete solution emerges.
31+
pub(super) fn build_with_external_cmd<S: AsRef<str>>(path: S, build_dir: PathBuf) -> BuildResult {
32+
let path = path.as_ref();
33+
let (cmd, args) = {
34+
let mut words = path.split_whitespace();
35+
let cmd = match words.next() {
36+
Some(cmd) => cmd,
37+
None => {
38+
return BuildResult::Err("Specified build_command is empty".into(), None);
39+
}
40+
};
41+
(cmd, words)
42+
};
43+
let spawned = Command::new(&cmd)
44+
.args(args)
45+
.current_dir(&build_dir)
46+
.stdout(Stdio::piped())
47+
.stderr(Stdio::null())
48+
.spawn();
49+
50+
let child = match spawned {
51+
Ok(child) => child,
52+
Err(io) => {
53+
let err_msg = format!("Couldn't execute: {} ({:?})", path, io.kind());
54+
trace!("{}", err_msg);
55+
return BuildResult::Err(err_msg, Some(path.to_owned()));
56+
},
57+
};
58+
59+
// TODO: Timeout?
60+
let reader = std::io::BufReader::new(child.stdout.unwrap());
61+
use std::io::BufRead;
62+
63+
let files = reader.lines().filter_map(|res| res.ok())
64+
.map(PathBuf::from)
65+
// Relative paths are relative to build command, not RLS itself (cwd may be different)
66+
.map(|path| if !path.is_absolute() { build_dir.join(path) } else { path });
67+
68+
let analyses = match read_analysis_files(files) {
69+
Ok(analyses) => analyses,
70+
Err(cause) => {
71+
let err_msg = format!("Couldn't read analysis data: {}", cause);
72+
return BuildResult::Err(err_msg, Some(path.to_owned()));
73+
}
74+
};
75+
76+
BuildResult::Success(build_dir.clone(), vec![], analyses, false)
77+
}
78+
79+
/// Reads and deserializes given save-analysis JSON files into corresponding
80+
/// `rls_data::Analysis` for each file. If an error is encountered, a `String`
81+
/// with the error message is returned.
82+
fn read_analysis_files<I>(files: I) -> Result<Vec<Analysis>, String>
83+
where
84+
I: Iterator,
85+
I::Item: AsRef<Path>
86+
{
87+
let mut analyses = Vec::new();
88+
89+
for path in files {
90+
trace!("external::read_analysis_files: Attempt to read `{}`", path.as_ref().display());
91+
92+
let mut file = File::open(path).map_err(|e| e.to_string())?;
93+
let mut contents = String::new();
94+
file.read_to_string(&mut contents).map_err(|e| e.to_string())?;
95+
96+
let data: Analysis = serde_json::from_str(&contents).map_err(|e| e.to_string())?;
97+
analyses.push(data);
98+
}
99+
100+
Ok(analyses)
101+
}

src/build/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ pub mod environment;
3737
mod cargo;
3838
mod rustc;
3939
mod plan;
40+
mod external;
4041

4142
use self::plan::{Plan as BuildPlan, WorkStatus};
4243

@@ -530,6 +531,12 @@ impl Internals {
530531
// do this so we can load changed code from the VFS, rather than from
531532
// disk).
532533

534+
// Check if an override setting was provided and execute that, instead.
535+
if let Some(ref cmd) = self.config.lock().unwrap().build_command {
536+
let build_dir = self.compilation_cx.lock().unwrap().build_dir.clone();
537+
return external::build_with_external_cmd(cmd, build_dir.unwrap());
538+
}
539+
533540
// Don't hold this lock when we run Cargo.
534541
let needs_to_run_cargo = self.compilation_cx.lock().unwrap().args.is_empty();
535542

src/config.rs

+7
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,11 @@ pub struct Config {
157157
/// local variable declaration. When set to false, the content is only availabe when
158158
/// holding the `ctrl` key in some editors.
159159
pub show_hover_context: bool,
160+
/// EXPERIMENTAL (needs unstable features)
161+
/// If set, executes a given program responsible for rebuilding save-analysis
162+
/// to be loaded by the RLS. The program given should output a list of
163+
/// resulting .json files on stdout.
164+
pub build_command: Option<String>,
160165
}
161166

162167
impl Default for Config {
@@ -185,6 +190,7 @@ impl Default for Config {
185190
clippy_preference: ClippyPreference::OptIn,
186191
full_docs: Inferrable::Inferred(false),
187192
show_hover_context: true,
193+
build_command: None,
188194
};
189195
result.normalise();
190196
result
@@ -221,6 +227,7 @@ impl Config {
221227
self.build_bin = Inferrable::Inferred(None);
222228
self.build_lib = Inferrable::Inferred(false);
223229
self.cfg_test = false;
230+
self.build_command = None;
224231
}
225232
}
226233

0 commit comments

Comments
 (0)