aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_cargo_watch/src/lib.rs78
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs27
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};
10use std::{ 10use 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)]
251pub struct CargoError(String);
252
253impl fmt::Display for CargoError {
254 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
255 write!(f, "Cargo failed: {}", self.0)
256 }
257}
258impl error::Error for CargoError {}
259
249pub fn run_cargo( 260pub 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
302impl WatchThread { 330impl 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
8use anyhow::{Context, Result}; 8use anyhow::{Context, Result};
9use cargo_metadata::{CargoOpt, Message, MetadataCommand, PackageId}; 9use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
10use ra_arena::{Arena, Idx}; 10use ra_arena::{Arena, Idx};
11use ra_cargo_watch::run_cargo; 11use ra_cargo_watch::run_cargo;
12use ra_db::Edition; 12use 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}