diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-03-23 11:55:26 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-03-23 11:55:26 +0000 |
commit | a27f5e3e058abd18925c606c00ee10d0dba45aba (patch) | |
tree | 7d431f92de44991eeb84424850489bf15bdaaa3b | |
parent | c7a2052e7302a5ff6c05aa2589ef3ff51a9e7c95 (diff) | |
parent | 8be28a2d4f1fa1593bab81e32e465dba35b99448 (diff) |
Merge #3632
3632: ra_cargo_watch: log errors r=matklad a=Veetaha
Until this moment we totally ignored all the errors from cargo process. Though this is still true, but we
now try to log ones that are critical (i.e. misconfiguration errors and ignore compile errors).
This fixes #3631, and gives us a better error message to more gracefully handle the #3265
![image](https://user-images.githubusercontent.com/36276403/76958683-d7e1f080-6920-11ea-83d8-04561c11ccc4.png)
Though I think that outputting this only to `Output` channel is not enough. We should somehow warn the user that he passed wrong arguments to `cargo-watch.args`. I didn't bother looking for how to do this now, but this PR at least gives us something.
*cc* @kiljacken @matklad
Co-authored-by: veetaha <[email protected]>
Co-authored-by: Veetaha <[email protected]>
-rw-r--r-- | crates/ra_cargo_watch/src/lib.rs | 67 | ||||
-rw-r--r-- | crates/ra_project_model/src/cargo_workspace.rs | 27 |
2 files changed, 59 insertions, 35 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 |
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index c2857dbfc..c7f9bd873 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
@@ -6,7 +6,7 @@ use std::{ | |||
6 | }; | 6 | }; |
7 | 7 | ||
8 | use anyhow::{Context, Result}; | 8 | use anyhow::{Context, Result}; |
9 | use cargo_metadata::{CargoOpt, Message, MetadataCommand, PackageId}; | 9 | use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; |
10 | use ra_arena::{Arena, Idx}; | 10 | use ra_arena::{Arena, Idx}; |
11 | use ra_cargo_watch::run_cargo; | 11 | use ra_cargo_watch::run_cargo; |
12 | use ra_db::Edition; | 12 | use ra_db::Edition; |
@@ -254,7 +254,7 @@ pub fn load_out_dirs( | |||
254 | "check".to_string(), | 254 | "check".to_string(), |
255 | "--message-format=json".to_string(), | 255 | "--message-format=json".to_string(), |
256 | "--manifest-path".to_string(), | 256 | "--manifest-path".to_string(), |
257 | format!("{}", cargo_toml.display()), | 257 | cargo_toml.display().to_string(), |
258 | ]; | 258 | ]; |
259 | 259 | ||
260 | if cargo_features.all_features { | 260 | if cargo_features.all_features { |
@@ -263,19 +263,15 @@ pub fn load_out_dirs( | |||
263 | // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` | 263 | // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` |
264 | // https://github.com/oli-obk/cargo_metadata/issues/79 | 264 | // https://github.com/oli-obk/cargo_metadata/issues/79 |
265 | args.push("--no-default-features".to_string()); | 265 | args.push("--no-default-features".to_string()); |
266 | } else if !cargo_features.features.is_empty() { | 266 | } else { |
267 | for feature in &cargo_features.features { | 267 | args.extend(cargo_features.features.iter().cloned()); |
268 | args.push(feature.clone()); | ||
269 | } | ||
270 | } | 268 | } |
271 | 269 | ||
272 | let mut res = FxHashMap::default(); | 270 | let mut acc = FxHashMap::default(); |
273 | let mut child = run_cargo(&args, cargo_toml.parent(), &mut |message| { | 271 | let res = run_cargo(&args, cargo_toml.parent(), &mut |message| { |
274 | match message { | 272 | match message { |
275 | Message::BuildScriptExecuted(message) => { | 273 | Message::BuildScriptExecuted(BuildScript { package_id, out_dir, .. }) => { |
276 | let package_id = message.package_id; | 274 | acc.insert(package_id, out_dir); |
277 | let out_dir = message.out_dir; | ||
278 | res.insert(package_id, out_dir); | ||
279 | } | 275 | } |
280 | 276 | ||
281 | Message::CompilerArtifact(_) => (), | 277 | Message::CompilerArtifact(_) => (), |
@@ -285,6 +281,9 @@ pub fn load_out_dirs( | |||
285 | true | 281 | true |
286 | }); | 282 | }); |
287 | 283 | ||
288 | let _ = child.wait(); | 284 | if let Err(err) = res { |
289 | res | 285 | log::error!("Failed to load outdirs: {:?}", err); |
286 | } | ||
287 | |||
288 | acc | ||
290 | } | 289 | } |