aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/paths/src/lib.rs7
-rw-r--r--crates/project_model/src/build_data.rs86
-rw-r--r--crates/project_model/src/workspace.rs10
-rw-r--r--crates/rust-analyzer/src/reload.rs41
-rw-r--r--crates/stdx/src/lib.rs8
5 files changed, 110 insertions, 42 deletions
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs
index 22011cb33..f09ad37e3 100644
--- a/crates/paths/src/lib.rs
+++ b/crates/paths/src/lib.rs
@@ -1,6 +1,7 @@
1//! Thin wrappers around `std::path`, distinguishing between absolute and 1//! Thin wrappers around `std::path`, distinguishing between absolute and
2//! relative paths. 2//! relative paths.
3use std::{ 3use std::{
4 borrow::Borrow,
4 convert::{TryFrom, TryInto}, 5 convert::{TryFrom, TryInto},
5 ops, 6 ops,
6 path::{Component, Path, PathBuf}, 7 path::{Component, Path, PathBuf},
@@ -35,6 +36,12 @@ impl AsRef<AbsPath> for AbsPathBuf {
35 } 36 }
36} 37}
37 38
39impl Borrow<AbsPath> for AbsPathBuf {
40 fn borrow(&self) -> &AbsPath {
41 self.as_path()
42 }
43}
44
38impl TryFrom<PathBuf> for AbsPathBuf { 45impl TryFrom<PathBuf> for AbsPathBuf {
39 type Error = PathBuf; 46 type Error = PathBuf;
40 fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> { 47 fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> {
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs
index c2c87b207..0d4d39fef 100644
--- a/crates/project_model/src/build_data.rs
+++ b/crates/project_model/src/build_data.rs
@@ -13,12 +13,12 @@ use cargo_metadata::{BuildScript, Message};
13use itertools::Itertools; 13use itertools::Itertools;
14use paths::{AbsPath, AbsPathBuf}; 14use paths::{AbsPath, AbsPathBuf};
15use rustc_hash::FxHashMap; 15use rustc_hash::FxHashMap;
16use stdx::JodChild; 16use stdx::{format_to, JodChild};
17 17
18use crate::{cfg_flag::CfgFlag, CargoConfig}; 18use crate::{cfg_flag::CfgFlag, CargoConfig};
19 19
20#[derive(Debug, Clone, Default, PartialEq, Eq)] 20#[derive(Debug, Clone, Default, PartialEq, Eq)]
21pub(crate) struct BuildData { 21pub(crate) struct PackageBuildData {
22 /// List of config flags defined by this package's build script 22 /// List of config flags defined by this package's build script
23 pub(crate) cfgs: Vec<CfgFlag>, 23 pub(crate) cfgs: Vec<CfgFlag>,
24 /// List of cargo-related environment variables with their value 24 /// List of cargo-related environment variables with their value
@@ -32,6 +32,17 @@ pub(crate) struct BuildData {
32 pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>, 32 pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
33} 33}
34 34
35#[derive(Debug, Default, PartialEq, Eq, Clone)]
36pub(crate) struct WorkspaceBuildData {
37 per_package: FxHashMap<String, PackageBuildData>,
38 error: Option<String>,
39}
40
41#[derive(Debug, Default, PartialEq, Eq, Clone)]
42pub struct BuildDataResult {
43 per_workspace: FxHashMap<AbsPathBuf, WorkspaceBuildData>,
44}
45
35#[derive(Clone, Debug)] 46#[derive(Clone, Debug)]
36pub(crate) struct BuildDataConfig { 47pub(crate) struct BuildDataConfig {
37 cargo_toml: AbsPathBuf, 48 cargo_toml: AbsPathBuf,
@@ -52,13 +63,6 @@ pub struct BuildDataCollector {
52 configs: FxHashMap<AbsPathBuf, BuildDataConfig>, 63 configs: FxHashMap<AbsPathBuf, BuildDataConfig>,
53} 64}
54 65
55#[derive(Debug, Default, PartialEq, Eq, Clone)]
56pub struct BuildDataResult {
57 data: FxHashMap<AbsPathBuf, BuildDataMap>,
58}
59
60pub(crate) type BuildDataMap = FxHashMap<String, BuildData>;
61
62impl BuildDataCollector { 66impl BuildDataCollector {
63 pub(crate) fn add_config(&mut self, workspace_root: &AbsPath, config: BuildDataConfig) { 67 pub(crate) fn add_config(&mut self, workspace_root: &AbsPath, config: BuildDataConfig) {
64 self.configs.insert(workspace_root.to_path_buf(), config); 68 self.configs.insert(workspace_root.to_path_buf(), config);
@@ -67,7 +71,7 @@ impl BuildDataCollector {
67 pub fn collect(&mut self, progress: &dyn Fn(String)) -> Result<BuildDataResult> { 71 pub fn collect(&mut self, progress: &dyn Fn(String)) -> Result<BuildDataResult> {
68 let mut res = BuildDataResult::default(); 72 let mut res = BuildDataResult::default();
69 for (path, config) in self.configs.iter() { 73 for (path, config) in self.configs.iter() {
70 res.data.insert( 74 res.per_workspace.insert(
71 path.clone(), 75 path.clone(),
72 collect_from_workspace( 76 collect_from_workspace(
73 &config.cargo_toml, 77 &config.cargo_toml,
@@ -81,9 +85,28 @@ impl BuildDataCollector {
81 } 85 }
82} 86}
83 87
88impl WorkspaceBuildData {
89 pub(crate) fn get(&self, package_id: &str) -> Option<&PackageBuildData> {
90 self.per_package.get(package_id)
91 }
92}
93
84impl BuildDataResult { 94impl BuildDataResult {
85 pub(crate) fn get(&self, root: &AbsPath) -> Option<&BuildDataMap> { 95 pub(crate) fn get(&self, workspace_root: &AbsPath) -> Option<&WorkspaceBuildData> {
86 self.data.get(&root.to_path_buf()) 96 self.per_workspace.get(workspace_root)
97 }
98 pub fn error(&self) -> Option<String> {
99 let mut buf = String::new();
100 for (_workspace_root, build_data) in &self.per_workspace {
101 if let Some(err) = &build_data.error {
102 format_to!(buf, "cargo check failed:\n{}", err);
103 }
104 }
105 if buf.is_empty() {
106 return None;
107 }
108
109 Some(buf)
87 } 110 }
88} 111}
89 112
@@ -102,7 +125,7 @@ fn collect_from_workspace(
102 cargo_features: &CargoConfig, 125 cargo_features: &CargoConfig,
103 packages: &Vec<cargo_metadata::Package>, 126 packages: &Vec<cargo_metadata::Package>,
104 progress: &dyn Fn(String), 127 progress: &dyn Fn(String),
105) -> Result<BuildDataMap> { 128) -> Result<WorkspaceBuildData> {
106 let mut cmd = Command::new(toolchain::cargo()); 129 let mut cmd = Command::new(toolchain::cargo());
107 cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"]) 130 cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"])
108 .arg(cargo_toml.as_ref()); 131 .arg(cargo_toml.as_ref());
@@ -130,13 +153,13 @@ fn collect_from_workspace(
130 } 153 }
131 } 154 }
132 155
133 cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null()); 156 cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
134 157
135 let mut child = cmd.spawn().map(JodChild)?; 158 let mut child = cmd.spawn().map(JodChild)?;
136 let child_stdout = child.stdout.take().unwrap(); 159 let child_stdout = child.stdout.take().unwrap();
137 let stdout = BufReader::new(child_stdout); 160 let stdout = BufReader::new(child_stdout);
138 161
139 let mut res = BuildDataMap::default(); 162 let mut res = WorkspaceBuildData::default();
140 for message in cargo_metadata::Message::parse_stream(stdout).flatten() { 163 for message in cargo_metadata::Message::parse_stream(stdout).flatten() {
141 match message { 164 match message {
142 Message::BuildScriptExecuted(BuildScript { 165 Message::BuildScriptExecuted(BuildScript {
@@ -154,16 +177,17 @@ fn collect_from_workspace(
154 } 177 }
155 acc 178 acc
156 }; 179 };
157 let res = res.entry(package_id.repr.clone()).or_default(); 180 let package_build_data =
181 res.per_package.entry(package_id.repr.clone()).or_default();
158 // cargo_metadata crate returns default (empty) path for 182 // cargo_metadata crate returns default (empty) path for
159 // older cargos, which is not absolute, so work around that. 183 // older cargos, which is not absolute, so work around that.
160 if !out_dir.as_str().is_empty() { 184 if !out_dir.as_str().is_empty() {
161 let out_dir = AbsPathBuf::assert(PathBuf::from(out_dir.into_os_string())); 185 let out_dir = AbsPathBuf::assert(PathBuf::from(out_dir.into_os_string()));
162 res.out_dir = Some(out_dir); 186 package_build_data.out_dir = Some(out_dir);
163 res.cfgs = cfgs; 187 package_build_data.cfgs = cfgs;
164 } 188 }
165 189
166 res.envs = env; 190 package_build_data.envs = env;
167 } 191 }
168 Message::CompilerArtifact(message) => { 192 Message::CompilerArtifact(message) => {
169 progress(format!("metadata {}", message.target.name)); 193 progress(format!("metadata {}", message.target.name));
@@ -173,8 +197,9 @@ fn collect_from_workspace(
173 // Skip rmeta file 197 // Skip rmeta file
174 if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) { 198 if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) {
175 let filename = AbsPathBuf::assert(PathBuf::from(&filename)); 199 let filename = AbsPathBuf::assert(PathBuf::from(&filename));
176 let res = res.entry(package_id.repr.clone()).or_default(); 200 let package_build_data =
177 res.proc_macro_dylib_path = Some(filename); 201 res.per_package.entry(package_id.repr.clone()).or_default();
202 package_build_data.proc_macro_dylib_path = Some(filename);
178 } 203 }
179 } 204 }
180 } 205 }
@@ -188,16 +213,25 @@ fn collect_from_workspace(
188 } 213 }
189 214
190 for package in packages { 215 for package in packages {
191 let build_data = res.entry(package.id.repr.clone()).or_default(); 216 let package_build_data = res.per_package.entry(package.id.repr.clone()).or_default();
192 inject_cargo_env(package, build_data); 217 inject_cargo_env(package, package_build_data);
193 if let Some(out_dir) = &build_data.out_dir { 218 if let Some(out_dir) = &package_build_data.out_dir {
194 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() 219 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
195 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { 220 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
196 build_data.envs.push(("OUT_DIR".to_string(), out_dir)); 221 package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
197 } 222 }
198 } 223 }
199 } 224 }
200 225
226 let output = child.into_inner().wait_with_output()?;
227 if !output.status.success() {
228 let mut stderr = String::from_utf8(output.stderr).unwrap_or_default();
229 if stderr.is_empty() {
230 stderr = "cargo check failed".to_string();
231 }
232 res.error = Some(stderr)
233 }
234
201 Ok(res) 235 Ok(res)
202} 236}
203 237
@@ -212,7 +246,7 @@ fn is_dylib(path: &Utf8Path) -> bool {
212/// Recreates the compile-time environment variables that Cargo sets. 246/// Recreates the compile-time environment variables that Cargo sets.
213/// 247///
214/// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates> 248/// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
215fn inject_cargo_env(package: &cargo_metadata::Package, build_data: &mut BuildData) { 249fn inject_cargo_env(package: &cargo_metadata::Package, build_data: &mut PackageBuildData) {
216 let env = &mut build_data.envs; 250 let env = &mut build_data.envs;
217 251
218 // FIXME: Missing variables: 252 // FIXME: Missing variables:
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 1b53fcc30..2fcd0f8fa 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -12,7 +12,7 @@ use proc_macro_api::ProcMacroClient;
12use rustc_hash::{FxHashMap, FxHashSet}; 12use rustc_hash::{FxHashMap, FxHashSet};
13 13
14use crate::{ 14use crate::{
15 build_data::{BuildData, BuildDataMap, BuildDataResult}, 15 build_data::{BuildDataResult, PackageBuildData, WorkspaceBuildData},
16 cargo_workspace, 16 cargo_workspace,
17 cfg_flag::CfgFlag, 17 cfg_flag::CfgFlag,
18 rustc_cfg, 18 rustc_cfg,
@@ -354,10 +354,10 @@ fn cargo_to_crate_graph(
354 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 354 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
355 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 355 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
356 cargo: &CargoWorkspace, 356 cargo: &CargoWorkspace,
357 build_data_map: Option<&BuildDataMap>, 357 build_data_map: Option<&WorkspaceBuildData>,
358 sysroot: &Sysroot, 358 sysroot: &Sysroot,
359 rustc: &Option<CargoWorkspace>, 359 rustc: &Option<CargoWorkspace>,
360 rustc_build_data_map: Option<&BuildDataMap>, 360 rustc_build_data_map: Option<&WorkspaceBuildData>,
361) -> CrateGraph { 361) -> CrateGraph {
362 let _p = profile::span("cargo_to_crate_graph"); 362 let _p = profile::span("cargo_to_crate_graph");
363 let mut crate_graph = CrateGraph::default(); 363 let mut crate_graph = CrateGraph::default();
@@ -464,7 +464,7 @@ fn handle_rustc_crates(
464 rustc_workspace: &CargoWorkspace, 464 rustc_workspace: &CargoWorkspace,
465 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 465 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
466 crate_graph: &mut CrateGraph, 466 crate_graph: &mut CrateGraph,
467 rustc_build_data_map: Option<&FxHashMap<String, BuildData>>, 467 rustc_build_data_map: Option<&WorkspaceBuildData>,
468 cfg_options: &CfgOptions, 468 cfg_options: &CfgOptions,
469 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 469 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
470 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>, 470 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
@@ -555,7 +555,7 @@ fn handle_rustc_crates(
555fn add_target_crate_root( 555fn add_target_crate_root(
556 crate_graph: &mut CrateGraph, 556 crate_graph: &mut CrateGraph,
557 pkg: &cargo_workspace::PackageData, 557 pkg: &cargo_workspace::PackageData,
558 build_data: Option<&BuildData>, 558 build_data: Option<&PackageBuildData>,
559 cfg_options: &CfgOptions, 559 cfg_options: &CfgOptions,
560 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 560 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
561 file_id: FileId, 561 file_id: FileId,
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 301c7003b..d0cc1b61a 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -109,6 +109,11 @@ impl GlobalState {
109 quiescent: self.is_quiescent(), 109 quiescent: self.is_quiescent(),
110 message: None, 110 message: None,
111 }; 111 };
112
113 if let Some(error) = self.build_data_error() {
114 status.health = lsp_ext::Health::Warning;
115 status.message = Some(error)
116 }
112 if !self.config.cargo_autoreload() 117 if !self.config.cargo_autoreload()
113 && self.is_quiescent() 118 && self.is_quiescent()
114 && self.fetch_workspaces_queue.op_requested() 119 && self.fetch_workspaces_queue.op_requested()
@@ -116,9 +121,10 @@ impl GlobalState {
116 status.health = lsp_ext::Health::Warning; 121 status.health = lsp_ext::Health::Warning;
117 status.message = Some("Workspace reload required".to_string()) 122 status.message = Some("Workspace reload required".to_string())
118 } 123 }
119 if let Some(error) = self.loading_error() { 124
125 if let Some(error) = self.fetch_workspace_error() {
120 status.health = lsp_ext::Health::Error; 126 status.health = lsp_ext::Health::Error;
121 status.message = Some(format!("Workspace reload failed: {}", error)) 127 status.message = Some(error)
122 } 128 }
123 129
124 if self.last_reported_status.as_ref() != Some(&status) { 130 if self.last_reported_status.as_ref() != Some(&status) {
@@ -217,7 +223,7 @@ impl GlobalState {
217 let _p = profile::span("GlobalState::switch_workspaces"); 223 let _p = profile::span("GlobalState::switch_workspaces");
218 log::info!("will switch workspaces"); 224 log::info!("will switch workspaces");
219 225
220 if let Some(error_message) = self.loading_error() { 226 if let Some(error_message) = self.fetch_workspace_error() {
221 log::error!("failed to switch workspaces: {}", error_message); 227 log::error!("failed to switch workspaces: {}", error_message);
222 self.show_message(lsp_types::MessageType::Error, error_message); 228 self.show_message(lsp_types::MessageType::Error, error_message);
223 if !self.workspaces.is_empty() { 229 if !self.workspaces.is_empty() {
@@ -225,6 +231,11 @@ impl GlobalState {
225 } 231 }
226 } 232 }
227 233
234 if let Some(error_message) = self.build_data_error() {
235 log::error!("failed to switch build data: {}", error_message);
236 self.show_message(lsp_types::MessageType::Error, error_message);
237 }
238
228 let workspaces = self 239 let workspaces = self
229 .fetch_workspaces_queue 240 .fetch_workspaces_queue
230 .last_op_result() 241 .last_op_result()
@@ -343,22 +354,30 @@ impl GlobalState {
343 log::info!("did switch workspaces"); 354 log::info!("did switch workspaces");
344 } 355 }
345 356
346 fn loading_error(&self) -> Option<String> { 357 fn fetch_workspace_error(&self) -> Option<String> {
347 let mut message = None; 358 let mut buf = String::new();
348 359
349 for ws in self.fetch_workspaces_queue.last_op_result() { 360 for ws in self.fetch_workspaces_queue.last_op_result() {
350 if let Err(err) = ws { 361 if let Err(err) = ws {
351 let message = message.get_or_insert_with(String::new); 362 stdx::format_to!(buf, "rust-analyzer failed to load workspace: {:#}\n", err);
352 stdx::format_to!(message, "rust-analyzer failed to load workspace: {:#}\n", err);
353 } 363 }
354 } 364 }
355 365
356 if let Some(Err(err)) = self.fetch_build_data_queue.last_op_result() { 366 if buf.is_empty() {
357 let message = message.get_or_insert_with(String::new); 367 return None;
358 stdx::format_to!(message, "rust-analyzer failed to fetch build data: {:#}\n", err);
359 } 368 }
360 369
361 message 370 Some(buf)
371 }
372
373 fn build_data_error(&self) -> Option<String> {
374 match self.fetch_build_data_queue.last_op_result() {
375 Some(Err(err)) => {
376 Some(format!("rust-analyzer failed to fetch build data: {:#}\n", err))
377 }
378 Some(Ok(data)) => data.error(),
379 None => None,
380 }
362 } 381 }
363 382
364 fn reload_flycheck(&mut self) { 383 fn reload_flycheck(&mut self) {
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index d26be4853..b0a18d58d 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -178,6 +178,7 @@ where
178 start..start + len 178 start..start + len
179} 179}
180 180
181#[repr(transparent)]
181pub struct JodChild(pub process::Child); 182pub struct JodChild(pub process::Child);
182 183
183impl ops::Deref for JodChild { 184impl ops::Deref for JodChild {
@@ -200,6 +201,13 @@ impl Drop for JodChild {
200 } 201 }
201} 202}
202 203
204impl JodChild {
205 pub fn into_inner(self) -> process::Child {
206 // SAFETY: repr transparent
207 unsafe { std::mem::transmute::<JodChild, process::Child>(self) }
208 }
209}
210
203#[cfg(test)] 211#[cfg(test)]
204mod tests { 212mod tests {
205 use super::*; 213 use super::*;