diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_cargo_watch/src/lib.rs | 78 | ||||
-rw-r--r-- | crates/ra_project_model/src/cargo_workspace.rs | 27 |
2 files changed, 56 insertions, 49 deletions
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_cargo_watch/src/lib.rs index deb763af9..35b0cb608 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); |
@@ -259,7 +270,7 @@ pub fn run_cargo( | |||
259 | let mut child = command | 270 | let mut child = command |
260 | .args(args) | 271 | .args(args) |
261 | .stdout(Stdio::piped()) | 272 | .stdout(Stdio::piped()) |
262 | .stderr(Stdio::piped()) | 273 | .stderr(Stdio::null()) |
263 | .stdin(Stdio::null()) | 274 | .stdin(Stdio::null()) |
264 | .spawn() | 275 | .spawn() |
265 | .expect("couldn't launch cargo"); | 276 | .expect("couldn't launch cargo"); |
@@ -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,27 @@ 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!("the command produced no valid metadata:\n cargo {}", args.join(" ")) | ||
322 | } | ||
323 | Err(err) => format!("io error: {:?}", err), | ||
324 | Ok(_) => return Ok(()), | ||
325 | }; | ||
326 | |||
327 | Err(CargoError(err_msg)) | ||
300 | } | 328 | } |
301 | 329 | ||
302 | impl WatchThread { | 330 | impl WatchThread { |
@@ -325,7 +353,7 @@ impl WatchThread { | |||
325 | // which will break out of the loop, and continue the shutdown | 353 | // which will break out of the loop, and continue the shutdown |
326 | let _ = message_send.send(CheckEvent::Begin); | 354 | let _ = message_send.send(CheckEvent::Begin); |
327 | 355 | ||
328 | let mut child = run_cargo(&args, Some(&workspace_root), &mut |message| { | 356 | let res = run_cargo(&args, Some(&workspace_root), &mut |message| { |
329 | // Skip certain kinds of messages to only spend time on what's useful | 357 | // Skip certain kinds of messages to only spend time on what's useful |
330 | match &message { | 358 | match &message { |
331 | Message::CompilerArtifact(artifact) if artifact.fresh => return true, | 359 | Message::CompilerArtifact(artifact) if artifact.fresh => return true, |
@@ -334,39 +362,19 @@ impl WatchThread { | |||
334 | _ => {} | 362 | _ => {} |
335 | } | 363 | } |
336 | 364 | ||
337 | match message_send.send(CheckEvent::Msg(message)) { | 365 | // if the send channel was closed, so we want to shutdown |
338 | Ok(()) => {} | 366 | 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 | }); | 367 | }); |
347 | 368 | ||
369 | if let Err(err) = res { | ||
370 | // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>` | ||
371 | // to display user-caused misconfiguration errors instead of just logging them here | ||
372 | log::error!("Cargo watcher failed {:?}", err); | ||
373 | } | ||
374 | |||
348 | // We can ignore any error here, as we are already in the progress | 375 | // We can ignore any error here, as we are already in the progress |
349 | // of shutting down. | 376 | // of shutting down. |
350 | let _ = message_send.send(CheckEvent::End); | 377 | 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 are resilient to errors, so we don't try to panic here | ||
356 | match child.wait_with_output() { | ||
357 | Ok(output) => match output.status.code() { | ||
358 | Some(0) | None => {} | ||
359 | Some(exit_code) => { | ||
360 | let output = | ||
361 | std::str::from_utf8(&output.stderr).unwrap_or("<bad utf8 output>"); | ||
362 | |||
363 | if !output.contains("could not compile") { | ||
364 | log::error!("Cargo failed with exit code {} {}", exit_code, output); | ||
365 | } | ||
366 | } | ||
367 | }, | ||
368 | Err(err) => log::error!("Cargo io error: {:?}", err), | ||
369 | } | ||
370 | })) | 378 | })) |
371 | } else { | 379 | } else { |
372 | None | 380 | 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 | } |