diff options
Diffstat (limited to 'crates/ra_cargo_watch/src/lib.rs')
-rw-r--r-- | crates/ra_cargo_watch/src/lib.rs | 67 |
1 files changed, 46 insertions, 21 deletions
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_cargo_watch/src/lib.rs index bffe5eb00..7c525c430 100644 --- a/crates/ra_cargo_watch/src/lib.rs +++ b/crates/ra_cargo_watch/src/lib.rs | |||
@@ -8,9 +8,10 @@ use lsp_types::{ | |||
8 | WorkDoneProgressEnd, WorkDoneProgressReport, | 8 | WorkDoneProgressEnd, WorkDoneProgressReport, |
9 | }; | 9 | }; |
10 | use std::{ | 10 | use std::{ |
11 | error, fmt, | ||
11 | io::{BufRead, BufReader}, | 12 | io::{BufRead, BufReader}, |
12 | path::{Path, PathBuf}, | 13 | path::{Path, PathBuf}, |
13 | process::{Child, Command, Stdio}, | 14 | process::{Command, Stdio}, |
14 | thread::JoinHandle, | 15 | thread::JoinHandle, |
15 | time::Instant, | 16 | time::Instant, |
16 | }; | 17 | }; |
@@ -70,10 +71,10 @@ impl std::ops::Drop for CheckWatcher { | |||
70 | fn drop(&mut self) { | 71 | fn drop(&mut self) { |
71 | if let Some(handle) = self.handle.take() { | 72 | if let Some(handle) = self.handle.take() { |
72 | // Take the sender out of the option | 73 | // Take the sender out of the option |
73 | let recv = self.cmd_send.take(); | 74 | let cmd_send = self.cmd_send.take(); |
74 | 75 | ||
75 | // Dropping the sender finishes the thread loop | 76 | // Dropping the sender finishes the thread loop |
76 | drop(recv); | 77 | drop(cmd_send); |
77 | 78 | ||
78 | // Join the thread, it should finish shortly. We don't really care | 79 | // Join the thread, it should finish shortly. We don't really care |
79 | // whether it panicked, so it is safe to ignore the result | 80 | // whether it panicked, so it is safe to ignore the result |
@@ -246,11 +247,21 @@ enum CheckEvent { | |||
246 | End, | 247 | End, |
247 | } | 248 | } |
248 | 249 | ||
250 | #[derive(Debug)] | ||
251 | pub struct CargoError(String); | ||
252 | |||
253 | impl fmt::Display for CargoError { | ||
254 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
255 | write!(f, "Cargo failed: {}", self.0) | ||
256 | } | ||
257 | } | ||
258 | impl error::Error for CargoError {} | ||
259 | |||
249 | pub fn run_cargo( | 260 | pub fn run_cargo( |
250 | args: &[String], | 261 | args: &[String], |
251 | current_dir: Option<&Path>, | 262 | current_dir: Option<&Path>, |
252 | on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool, | 263 | on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool, |
253 | ) -> Child { | 264 | ) -> Result<(), CargoError> { |
254 | let mut command = Command::new("cargo"); | 265 | let mut command = Command::new("cargo"); |
255 | if let Some(current_dir) = current_dir { | 266 | if let Some(current_dir) = current_dir { |
256 | command.current_dir(current_dir); | 267 | command.current_dir(current_dir); |
@@ -273,6 +284,8 @@ pub fn run_cargo( | |||
273 | // simply skip a line if it doesn't parse, which just ignores any | 284 | // simply skip a line if it doesn't parse, which just ignores any |
274 | // erroneus output. | 285 | // erroneus output. |
275 | let stdout = BufReader::new(child.stdout.take().unwrap()); | 286 | let stdout = BufReader::new(child.stdout.take().unwrap()); |
287 | let mut read_at_least_one_message = false; | ||
288 | |||
276 | for line in stdout.lines() { | 289 | for line in stdout.lines() { |
277 | let line = match line { | 290 | let line = match line { |
278 | Ok(line) => line, | 291 | Ok(line) => line, |
@@ -291,12 +304,31 @@ pub fn run_cargo( | |||
291 | } | 304 | } |
292 | }; | 305 | }; |
293 | 306 | ||
307 | read_at_least_one_message = true; | ||
308 | |||
294 | if !on_message(message) { | 309 | if !on_message(message) { |
295 | break; | 310 | break; |
296 | } | 311 | } |
297 | } | 312 | } |
298 | 313 | ||
299 | child | 314 | // It is okay to ignore the result, as it only errors if the process is already dead |
315 | let _ = child.kill(); | ||
316 | |||
317 | let err_msg = match child.wait() { | ||
318 | Ok(exit_code) if !exit_code.success() && !read_at_least_one_message => { | ||
319 | // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment: | ||
320 | // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298 | ||
321 | format!( | ||
322 | "the command produced no valid metadata (exit code: {:?}): cargo {}", | ||
323 | exit_code, | ||
324 | args.join(" ") | ||
325 | ) | ||
326 | } | ||
327 | Err(err) => format!("io error: {:?}", err), | ||
328 | Ok(_) => return Ok(()), | ||
329 | }; | ||
330 | |||
331 | Err(CargoError(err_msg)) | ||
300 | } | 332 | } |
301 | 333 | ||
302 | impl WatchThread { | 334 | impl WatchThread { |
@@ -325,7 +357,7 @@ impl WatchThread { | |||
325 | // which will break out of the loop, and continue the shutdown | 357 | // which will break out of the loop, and continue the shutdown |
326 | let _ = message_send.send(CheckEvent::Begin); | 358 | let _ = message_send.send(CheckEvent::Begin); |
327 | 359 | ||
328 | let mut child = run_cargo(&args, Some(&workspace_root), &mut |message| { | 360 | let res = run_cargo(&args, Some(&workspace_root), &mut |message| { |
329 | // Skip certain kinds of messages to only spend time on what's useful | 361 | // Skip certain kinds of messages to only spend time on what's useful |
330 | match &message { | 362 | match &message { |
331 | Message::CompilerArtifact(artifact) if artifact.fresh => return true, | 363 | Message::CompilerArtifact(artifact) if artifact.fresh => return true, |
@@ -334,26 +366,19 @@ impl WatchThread { | |||
334 | _ => {} | 366 | _ => {} |
335 | } | 367 | } |
336 | 368 | ||
337 | match message_send.send(CheckEvent::Msg(message)) { | 369 | // if the send channel was closed, we want to shutdown |
338 | Ok(()) => {} | 370 | message_send.send(CheckEvent::Msg(message)).is_ok() |
339 | Err(_err) => { | ||
340 | // The send channel was closed, so we want to shutdown | ||
341 | return false; | ||
342 | } | ||
343 | }; | ||
344 | |||
345 | true | ||
346 | }); | 371 | }); |
347 | 372 | ||
373 | if let Err(err) = res { | ||
374 | // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>` | ||
375 | // to display user-caused misconfiguration errors instead of just logging them here | ||
376 | log::error!("Cargo watcher failed {:?}", err); | ||
377 | } | ||
378 | |||
348 | // We can ignore any error here, as we are already in the progress | 379 | // We can ignore any error here, as we are already in the progress |
349 | // of shutting down. | 380 | // of shutting down. |
350 | let _ = message_send.send(CheckEvent::End); | 381 | let _ = message_send.send(CheckEvent::End); |
351 | |||
352 | // It is okay to ignore the result, as it only errors if the process is already dead | ||
353 | let _ = child.kill(); | ||
354 | |||
355 | // Again, we don't care about the exit status so just ignore the result | ||
356 | let _ = child.wait(); | ||
357 | })) | 382 | })) |
358 | } else { | 383 | } else { |
359 | None | 384 | None |