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

Commit cc07a61

Browse files
authored
Merge pull request #462 from Xanewok/stage-3
Use cached build plan mostly instead of Cargo to schedule a multi-target build
2 parents 826a12d + 5ff56f0 commit cc07a61

File tree

4 files changed

+351
-63
lines changed

4 files changed

+351
-63
lines changed

src/actions/notifications.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ impl<'a> NotificationAction<'a> for DidChange {
101101
let ctx = ctx.inited();
102102
let file_path = parse_file_path!(&params.text_document.uri, "on_change")?;
103103

104-
let changes: Vec<Change> = params.content_changes.iter().map(move |i| {
104+
let changes: Vec<Change> = params.content_changes.iter().map(|i| {
105105
if let Some(range) = i.range {
106106
let range = ls_util::range_to_rls(range);
107107
Change::ReplaceText {
@@ -117,6 +117,9 @@ impl<'a> NotificationAction<'a> for DidChange {
117117
}
118118
}).collect();
119119
ctx.vfs.on_changes(&changes).expect("error committing to VFS");
120+
if !changes.is_empty() {
121+
ctx.build_queue.mark_file_dirty(file_path, params.text_document.version)
122+
}
120123

121124
if !ctx.config.lock().unwrap().build_on_save {
122125
ctx.build_current_project(BuildPriority::Normal, out);

src/build/cargo.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,8 @@ impl RlsExecutor {
227227
}
228228
}
229229

230+
/// Returns wheter a given package is a primary one (every member of the
231+
/// workspace is considered as such).
230232
fn is_primary_crate(&self, id: &PackageId) -> bool {
231233
if self.workspace_mode {
232234
self.member_packages.lock().unwrap().contains(id)
@@ -253,13 +255,9 @@ impl Executor for RlsExecutor {
253255
}
254256

255257
fn force_rebuild(&self, unit: &Unit) -> bool {
256-
// TODO: Currently workspace_mode doesn't use rustc, so it doesn't
257-
// need args. When we start using rustc, we might consider doing
258-
// force_rebuild to retrieve args for given package if they're stale/missing
259-
if self.workspace_mode {
260-
return false;
261-
}
262-
258+
// In workspace_mode we need to force rebuild every package in the
259+
// workspace, even if it's not dirty at a time, to cache compiler
260+
// invocations in the build plan.
263261
// We only do a cargo build if we want to force rebuild the last
264262
// crate (e.g., because some args changed). Therefore we should
265263
// always force rebuild the primary crate.

src/build/mod.rs

+64-13
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ use std::time::Duration;
3030
mod environment;
3131
mod cargo;
3232
mod rustc;
33-
pub mod plan;
33+
mod plan;
3434

35-
use self::plan::Plan as BuildPlan;
35+
use self::plan::{Plan as BuildPlan, WorkStatus};
3636

3737
/// Manages builds.
3838
///
@@ -68,13 +68,18 @@ pub struct BuildQueue {
6868
queued: Arc<Mutex<(Build, Build)>>,
6969
}
7070

71+
/// Used when tracking modified files across different builds.
72+
type FileVersion = u64;
73+
7174
// Information needed to run and configure builds.
7275
struct Internals {
7376
// Arguments and environment with which we call rustc.
7477
// This can be further expanded for multi-crate target configuration.
7578
// This lock should only be held transiently.
7679
compilation_cx: Arc<Mutex<CompilationContext>>,
7780
env_lock: Arc<EnvironmentLock>,
81+
/// Set of files that were modified since last build.
82+
dirty_files: Arc<Mutex<HashMap<PathBuf, FileVersion>>>,
7883
vfs: Arc<Vfs>,
7984
// This lock should only be held transiently.
8085
config: Arc<Mutex<Config>>,
@@ -146,7 +151,8 @@ enum Build {
146151
struct PendingBuild {
147152
build_dir: PathBuf,
148153
priority: BuildPriority,
149-
// Closure to execture once the build is complete.
154+
built_files: HashMap<PathBuf, FileVersion>,
155+
// Closure to execute once the build is complete.
150156
and_then: Box<FnBox(BuildResult) + Send + 'static>,
151157
}
152158

@@ -228,6 +234,7 @@ impl BuildQueue {
228234

229235
let build = PendingBuild {
230236
build_dir: new_build_dir.to_owned(),
237+
built_files: self.internals.dirty_files.lock().unwrap().clone(),
231238
priority,
232239
and_then: Box::new(and_then),
233240
};
@@ -324,7 +331,8 @@ impl BuildQueue {
324331
}
325332

326333
// Run the build.
327-
let result = internals.run_build(&build.build_dir, build.priority);
334+
let result = internals.run_build(&build.build_dir, build.priority,
335+
&build.built_files);
328336
// Assert that the build was not squashed.
329337
if let BuildResult::Squashed = result {
330338
unreachable!();
@@ -340,6 +348,14 @@ impl BuildQueue {
340348
}
341349
}
342350
}
351+
352+
/// Marks a given versioned file as dirty since last build. The dirty flag
353+
/// will be cleared by a successful build that builds this or a more recent
354+
/// version of this file.
355+
pub fn mark_file_dirty(&self, file: PathBuf, version: FileVersion) {
356+
trace!("Marking file as dirty: {:?} ({})", file, version);
357+
self.internals.dirty_files.lock().unwrap().insert(file, version);
358+
}
343359
}
344360

345361
impl Internals {
@@ -348,6 +364,7 @@ impl Internals {
348364
compilation_cx: Arc::new(Mutex::new(CompilationContext::new())),
349365
vfs,
350366
config,
367+
dirty_files: Arc::new(Mutex::new(HashMap::new())),
351368
// Since environment is global mutable state and we can run multiple server
352369
// instances, be sure to use a global lock to ensure env var consistency
353370
env_lock: EnvironmentLock::get(),
@@ -356,7 +373,12 @@ impl Internals {
356373
}
357374

358375
// Entry point method for building.
359-
fn run_build(&self, new_build_dir: &Path, priority: BuildPriority) -> BuildResult {
376+
fn run_build(
377+
&self,
378+
new_build_dir: &Path,
379+
priority: BuildPriority,
380+
built_files: &HashMap<PathBuf, FileVersion>,
381+
) -> BuildResult {
360382
trace!("run_build, {:?} {:?}", new_build_dir, priority);
361383

362384
// Check if the build directory changed and update it.
@@ -375,7 +397,23 @@ impl Internals {
375397
}
376398
}
377399

378-
self.build()
400+
let result = self.build();
401+
// On a successful build, clear dirty files that were successfuly built
402+
// now. It's possible that a build was scheduled with given files, but
403+
// user later changed them. These should still be left as dirty (not built).
404+
match *&result {
405+
BuildResult::Success(_, _) | BuildResult::Failure(_, _) => {
406+
let mut dirty_files = self.dirty_files.lock().unwrap();
407+
dirty_files.retain(|file, dirty_version| {
408+
built_files.get(file)
409+
.map(|built_version| built_version < dirty_version)
410+
.unwrap_or(false)
411+
});
412+
trace!("Files still dirty after the build: {:?}", *dirty_files);
413+
},
414+
_ => {}
415+
};
416+
result
379417
}
380418

381419
// Build the project.
@@ -401,14 +439,27 @@ impl Internals {
401439
let needs_to_run_cargo = self.compilation_cx.lock().unwrap().args.is_empty();
402440
let workspace_mode = self.config.lock().unwrap().workspace_mode;
403441

404-
if workspace_mode || needs_to_run_cargo {
405-
let result = cargo::cargo(self);
406-
407-
match result {
408-
BuildResult::Err => return BuildResult::Err,
409-
_ if workspace_mode => return result,
410-
_ => {},
442+
if workspace_mode {
443+
// If the build plan has already been cached, use it, unless Cargo
444+
// has to be specifically rerun (e.g. when build scripts changed)
445+
let work = {
446+
let modified: Vec<_> = self.dirty_files.lock().unwrap()
447+
.keys().cloned().collect();
448+
let cx = self.compilation_cx.lock().unwrap();
449+
cx.build_plan.prepare_work(&modified)
450+
};
451+
return match work {
452+
// In workspace_mode, cargo performs the full build and returns
453+
// appropriate diagnostics/analysis data
454+
WorkStatus::NeedsCargo => cargo::cargo(self),
455+
WorkStatus::Execute(job_queue) => job_queue.execute(self),
411456
};
457+
// In single package mode Cargo needs to be run to cache args/envs for
458+
// future rustc calls
459+
} else if needs_to_run_cargo {
460+
if let BuildResult::Err = cargo::cargo(self) {
461+
return BuildResult::Err;
462+
}
412463
}
413464

414465
let compile_cx = self.compilation_cx.lock().unwrap();

0 commit comments

Comments
 (0)