diff options
author | Zac Pullar-Strecker <[email protected]> | 2020-07-31 03:12:44 +0100 |
---|---|---|
committer | Zac Pullar-Strecker <[email protected]> | 2020-07-31 03:12:44 +0100 |
commit | f05d7b41a719d848844b054a16477b29d0f063c6 (patch) | |
tree | 0a8a0946e8aef2ce64d4c13d0035ba41cce2daf3 /crates/ra_project_model/src/lib.rs | |
parent | 73ff610e41959e3e7c78a2b4b25b086883132956 (diff) | |
parent | 6b7cb8b5ab539fc4333ce34bc29bf77c976f232a (diff) |
Merge remote-tracking branch 'upstream/master' into 503-hover-doc-links
Hasn't fixed tests yet.
Diffstat (limited to 'crates/ra_project_model/src/lib.rs')
-rw-r--r-- | crates/ra_project_model/src/lib.rs | 182 |
1 files changed, 77 insertions, 105 deletions
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 8b85b4831..300e75135 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -3,12 +3,12 @@ | |||
3 | mod cargo_workspace; | 3 | mod cargo_workspace; |
4 | mod project_json; | 4 | mod project_json; |
5 | mod sysroot; | 5 | mod sysroot; |
6 | mod cfg_flag; | ||
6 | 7 | ||
7 | use std::{ | 8 | use std::{ |
8 | fs::{self, read_dir, ReadDir}, | 9 | fs::{self, read_dir, ReadDir}, |
9 | io, | 10 | io, |
10 | path::Path, | 11 | process::Command, |
11 | process::{Command, Output}, | ||
12 | }; | 12 | }; |
13 | 13 | ||
14 | use anyhow::{bail, Context, Result}; | 14 | use anyhow::{bail, Context, Result}; |
@@ -17,14 +17,17 @@ use ra_cfg::CfgOptions; | |||
17 | use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId}; | 17 | use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId}; |
18 | use rustc_hash::{FxHashMap, FxHashSet}; | 18 | use rustc_hash::{FxHashMap, FxHashSet}; |
19 | 19 | ||
20 | use crate::cfg_flag::CfgFlag; | ||
21 | |||
20 | pub use crate::{ | 22 | pub use crate::{ |
21 | cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind}, | 23 | cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind}, |
22 | project_json::{ProjectJson, ProjectJsonData}, | 24 | project_json::{ProjectJson, ProjectJsonData}, |
23 | sysroot::Sysroot, | 25 | sysroot::Sysroot, |
24 | }; | 26 | }; |
27 | |||
25 | pub use ra_proc_macro::ProcMacroClient; | 28 | pub use ra_proc_macro::ProcMacroClient; |
26 | 29 | ||
27 | #[derive(Debug, Clone)] | 30 | #[derive(Debug, Clone, Eq, PartialEq)] |
28 | pub enum ProjectWorkspace { | 31 | pub enum ProjectWorkspace { |
29 | /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. | 32 | /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. |
30 | Cargo { cargo: CargoWorkspace, sysroot: Sysroot }, | 33 | Cargo { cargo: CargoWorkspace, sysroot: Sysroot }, |
@@ -35,30 +38,12 @@ pub enum ProjectWorkspace { | |||
35 | /// `PackageRoot` describes a package root folder. | 38 | /// `PackageRoot` describes a package root folder. |
36 | /// Which may be an external dependency, or a member of | 39 | /// Which may be an external dependency, or a member of |
37 | /// the current workspace. | 40 | /// the current workspace. |
38 | #[derive(Debug, Clone)] | 41 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] |
39 | pub struct PackageRoot { | 42 | pub struct PackageRoot { |
40 | /// Path to the root folder | ||
41 | path: AbsPathBuf, | ||
42 | /// Is a member of the current workspace | 43 | /// Is a member of the current workspace |
43 | is_member: bool, | 44 | pub is_member: bool, |
44 | out_dir: Option<AbsPathBuf>, | 45 | pub include: Vec<AbsPathBuf>, |
45 | } | 46 | pub exclude: Vec<AbsPathBuf>, |
46 | impl PackageRoot { | ||
47 | pub fn new_member(path: AbsPathBuf) -> PackageRoot { | ||
48 | Self { path, is_member: true, out_dir: None } | ||
49 | } | ||
50 | pub fn new_non_member(path: AbsPathBuf) -> PackageRoot { | ||
51 | Self { path, is_member: false, out_dir: None } | ||
52 | } | ||
53 | pub fn path(&self) -> &AbsPath { | ||
54 | &self.path | ||
55 | } | ||
56 | pub fn out_dir(&self) -> Option<&AbsPath> { | ||
57 | self.out_dir.as_deref() | ||
58 | } | ||
59 | pub fn is_member(&self) -> bool { | ||
60 | self.is_member | ||
61 | } | ||
62 | } | 47 | } |
63 | 48 | ||
64 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] | 49 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] |
@@ -150,7 +135,7 @@ impl ProjectManifest { | |||
150 | impl ProjectWorkspace { | 135 | impl ProjectWorkspace { |
151 | pub fn load( | 136 | pub fn load( |
152 | manifest: ProjectManifest, | 137 | manifest: ProjectManifest, |
153 | cargo_features: &CargoConfig, | 138 | cargo_config: &CargoConfig, |
154 | with_sysroot: bool, | 139 | with_sysroot: bool, |
155 | ) -> Result<ProjectWorkspace> { | 140 | ) -> Result<ProjectWorkspace> { |
156 | let res = match manifest { | 141 | let res = match manifest { |
@@ -166,7 +151,7 @@ impl ProjectWorkspace { | |||
166 | ProjectWorkspace::Json { project } | 151 | ProjectWorkspace::Json { project } |
167 | } | 152 | } |
168 | ProjectManifest::CargoToml(cargo_toml) => { | 153 | ProjectManifest::CargoToml(cargo_toml) => { |
169 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) | 154 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_config) |
170 | .with_context(|| { | 155 | .with_context(|| { |
171 | format!( | 156 | format!( |
172 | "Failed to read Cargo metadata from Cargo.toml file {}", | 157 | "Failed to read Cargo metadata from Cargo.toml file {}", |
@@ -195,18 +180,40 @@ impl ProjectWorkspace { | |||
195 | /// the root is a member of the current workspace | 180 | /// the root is a member of the current workspace |
196 | pub fn to_roots(&self) -> Vec<PackageRoot> { | 181 | pub fn to_roots(&self) -> Vec<PackageRoot> { |
197 | match self { | 182 | match self { |
198 | ProjectWorkspace::Json { project } => { | 183 | ProjectWorkspace::Json { project } => project |
199 | project.roots.iter().map(|r| PackageRoot::new_member(r.path.clone())).collect() | 184 | .crates |
200 | } | 185 | .iter() |
186 | .map(|krate| PackageRoot { | ||
187 | is_member: krate.is_workspace_member, | ||
188 | include: krate.include.clone(), | ||
189 | exclude: krate.exclude.clone(), | ||
190 | }) | ||
191 | .collect::<FxHashSet<_>>() | ||
192 | .into_iter() | ||
193 | .collect::<Vec<_>>(), | ||
201 | ProjectWorkspace::Cargo { cargo, sysroot } => cargo | 194 | ProjectWorkspace::Cargo { cargo, sysroot } => cargo |
202 | .packages() | 195 | .packages() |
203 | .map(|pkg| PackageRoot { | 196 | .map(|pkg| { |
204 | path: cargo[pkg].root().to_path_buf(), | 197 | let is_member = cargo[pkg].is_member; |
205 | is_member: cargo[pkg].is_member, | 198 | let pkg_root = cargo[pkg].root().to_path_buf(); |
206 | out_dir: cargo[pkg].out_dir.clone(), | 199 | |
200 | let mut include = vec![pkg_root.clone()]; | ||
201 | include.extend(cargo[pkg].out_dir.clone()); | ||
202 | |||
203 | let mut exclude = vec![pkg_root.join(".git")]; | ||
204 | if is_member { | ||
205 | exclude.push(pkg_root.join("target")); | ||
206 | } else { | ||
207 | exclude.push(pkg_root.join("tests")); | ||
208 | exclude.push(pkg_root.join("examples")); | ||
209 | exclude.push(pkg_root.join("benches")); | ||
210 | } | ||
211 | PackageRoot { is_member, include, exclude } | ||
207 | }) | 212 | }) |
208 | .chain(sysroot.crates().map(|krate| { | 213 | .chain(sysroot.crates().map(|krate| PackageRoot { |
209 | PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf()) | 214 | is_member: false, |
215 | include: vec![sysroot[krate].root_dir().to_path_buf()], | ||
216 | exclude: Vec::new(), | ||
210 | })) | 217 | })) |
211 | .collect(), | 218 | .collect(), |
212 | } | 219 | } |
@@ -246,6 +253,7 @@ impl ProjectWorkspace { | |||
246 | let mut crate_graph = CrateGraph::default(); | 253 | let mut crate_graph = CrateGraph::default(); |
247 | match self { | 254 | match self { |
248 | ProjectWorkspace::Json { project } => { | 255 | ProjectWorkspace::Json { project } => { |
256 | let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default(); | ||
249 | let crates: FxHashMap<_, _> = project | 257 | let crates: FxHashMap<_, _> = project |
250 | .crates | 258 | .crates |
251 | .iter() | 259 | .iter() |
@@ -254,17 +262,20 @@ impl ProjectWorkspace { | |||
254 | let file_path = &krate.root_module; | 262 | let file_path = &krate.root_module; |
255 | let file_id = load(&file_path)?; | 263 | let file_id = load(&file_path)?; |
256 | 264 | ||
257 | let mut env = Env::default(); | 265 | let env = krate.env.clone().into_iter().collect(); |
258 | if let Some(out_dir) = &krate.out_dir { | ||
259 | // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() | ||
260 | if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { | ||
261 | env.set("OUT_DIR", out_dir); | ||
262 | } | ||
263 | } | ||
264 | let proc_macro = krate | 266 | let proc_macro = krate |
265 | .proc_macro_dylib_path | 267 | .proc_macro_dylib_path |
266 | .clone() | 268 | .clone() |
267 | .map(|it| proc_macro_client.by_dylib_path(&it)); | 269 | .map(|it| proc_macro_client.by_dylib_path(&it)); |
270 | |||
271 | let target = krate.target.as_deref().or(target); | ||
272 | let target_cfgs = cfg_cache | ||
273 | .entry(target) | ||
274 | .or_insert_with(|| get_rustc_cfg_options(target)); | ||
275 | |||
276 | let mut cfg_options = CfgOptions::default(); | ||
277 | cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned()); | ||
278 | |||
268 | // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env | 279 | // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env |
269 | Some(( | 280 | Some(( |
270 | CrateId(seq_index as u32), | 281 | CrateId(seq_index as u32), |
@@ -273,7 +284,7 @@ impl ProjectWorkspace { | |||
273 | krate.edition, | 284 | krate.edition, |
274 | // FIXME json definitions can store the crate name | 285 | // FIXME json definitions can store the crate name |
275 | None, | 286 | None, |
276 | krate.cfg.clone(), | 287 | cfg_options, |
277 | env, | 288 | env, |
278 | proc_macro.unwrap_or_default(), | 289 | proc_macro.unwrap_or_default(), |
279 | ), | 290 | ), |
@@ -288,10 +299,7 @@ impl ProjectWorkspace { | |||
288 | if let (Some(&from), Some(&to)) = | 299 | if let (Some(&from), Some(&to)) = |
289 | (crates.get(&from_crate_id), crates.get(&to_crate_id)) | 300 | (crates.get(&from_crate_id), crates.get(&to_crate_id)) |
290 | { | 301 | { |
291 | if crate_graph | 302 | if crate_graph.add_dep(from, dep.name.clone(), to).is_err() { |
292 | .add_dep(from, CrateName::new(&dep.name).unwrap(), to) | ||
293 | .is_err() | ||
294 | { | ||
295 | log::error!( | 303 | log::error!( |
296 | "cyclic dependency {:?} -> {:?}", | 304 | "cyclic dependency {:?} -> {:?}", |
297 | from_crate_id, | 305 | from_crate_id, |
@@ -303,7 +311,8 @@ impl ProjectWorkspace { | |||
303 | } | 311 | } |
304 | } | 312 | } |
305 | ProjectWorkspace::Cargo { cargo, sysroot } => { | 313 | ProjectWorkspace::Cargo { cargo, sysroot } => { |
306 | let mut cfg_options = get_rustc_cfg_options(target); | 314 | let mut cfg_options = CfgOptions::default(); |
315 | cfg_options.extend(get_rustc_cfg_options(target)); | ||
307 | 316 | ||
308 | let sysroot_crates: FxHashMap<_, _> = sysroot | 317 | let sysroot_crates: FxHashMap<_, _> = sysroot |
309 | .crates() | 318 | .crates() |
@@ -312,13 +321,11 @@ impl ProjectWorkspace { | |||
312 | 321 | ||
313 | let env = Env::default(); | 322 | let env = Env::default(); |
314 | let proc_macro = vec![]; | 323 | let proc_macro = vec![]; |
315 | let crate_name = CrateName::new(&sysroot[krate].name) | 324 | let name = sysroot[krate].name.clone(); |
316 | .expect("Sysroot crate names should not contain dashes"); | ||
317 | |||
318 | let crate_id = crate_graph.add_crate_root( | 325 | let crate_id = crate_graph.add_crate_root( |
319 | file_id, | 326 | file_id, |
320 | Edition::Edition2018, | 327 | Edition::Edition2018, |
321 | Some(crate_name), | 328 | Some(name), |
322 | cfg_options.clone(), | 329 | cfg_options.clone(), |
323 | env, | 330 | env, |
324 | proc_macro, | 331 | proc_macro, |
@@ -352,6 +359,7 @@ impl ProjectWorkspace { | |||
352 | 359 | ||
353 | // Add test cfg for non-sysroot crates | 360 | // Add test cfg for non-sysroot crates |
354 | cfg_options.insert_atom("test".into()); | 361 | cfg_options.insert_atom("test".into()); |
362 | cfg_options.insert_atom("debug_assertions".into()); | ||
355 | 363 | ||
356 | // Next, create crates for each package, target pair | 364 | // Next, create crates for each package, target pair |
357 | for pkg in cargo.packages() { | 365 | for pkg in cargo.packages() { |
@@ -365,15 +373,7 @@ impl ProjectWorkspace { | |||
365 | for feature in cargo[pkg].features.iter() { | 373 | for feature in cargo[pkg].features.iter() { |
366 | opts.insert_key_value("feature".into(), feature.into()); | 374 | opts.insert_key_value("feature".into(), feature.into()); |
367 | } | 375 | } |
368 | for cfg in cargo[pkg].cfgs.iter() { | 376 | opts.extend(cargo[pkg].cfgs.iter().cloned()); |
369 | match cfg.find('=') { | ||
370 | Some(split) => opts.insert_key_value( | ||
371 | cfg[..split].into(), | ||
372 | cfg[split + 1..].trim_matches('"').into(), | ||
373 | ), | ||
374 | None => opts.insert_atom(cfg.into()), | ||
375 | }; | ||
376 | } | ||
377 | opts | 377 | opts |
378 | }; | 378 | }; |
379 | let mut env = Env::default(); | 379 | let mut env = Env::default(); |
@@ -392,7 +392,7 @@ impl ProjectWorkspace { | |||
392 | let crate_id = crate_graph.add_crate_root( | 392 | let crate_id = crate_graph.add_crate_root( |
393 | file_id, | 393 | file_id, |
394 | edition, | 394 | edition, |
395 | Some(CrateName::normalize_dashes(&cargo[pkg].name)), | 395 | Some(cargo[pkg].name.clone()), |
396 | cfg_options, | 396 | cfg_options, |
397 | env, | 397 | env, |
398 | proc_macro.clone(), | 398 | proc_macro.clone(), |
@@ -499,66 +499,37 @@ impl ProjectWorkspace { | |||
499 | } | 499 | } |
500 | crate_graph | 500 | crate_graph |
501 | } | 501 | } |
502 | |||
503 | pub fn workspace_root_for(&self, path: &Path) -> Option<&AbsPath> { | ||
504 | match self { | ||
505 | ProjectWorkspace::Cargo { cargo, .. } => { | ||
506 | Some(cargo.workspace_root()).filter(|root| path.starts_with(root)) | ||
507 | } | ||
508 | ProjectWorkspace::Json { project: ProjectJson { roots, .. }, .. } => roots | ||
509 | .iter() | ||
510 | .find(|root| path.starts_with(&root.path)) | ||
511 | .map(|root| root.path.as_path()), | ||
512 | } | ||
513 | } | ||
514 | } | 502 | } |
515 | 503 | ||
516 | fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions { | 504 | fn get_rustc_cfg_options(target: Option<&str>) -> Vec<CfgFlag> { |
517 | let mut cfg_options = CfgOptions::default(); | 505 | let mut res = Vec::new(); |
518 | 506 | ||
519 | // Some nightly-only cfgs, which are required for stdlib | 507 | // Some nightly-only cfgs, which are required for stdlib |
520 | { | 508 | res.push(CfgFlag::Atom("target_thread_local".into())); |
521 | cfg_options.insert_atom("target_thread_local".into()); | 509 | for &ty in ["8", "16", "32", "64", "cas", "ptr"].iter() { |
522 | for &target_has_atomic in ["8", "16", "32", "64", "cas", "ptr"].iter() { | 510 | for &key in ["target_has_atomic", "target_has_atomic_load_store"].iter() { |
523 | cfg_options.insert_key_value("target_has_atomic".into(), target_has_atomic.into()); | 511 | res.push(CfgFlag::KeyValue { key: key.to_string(), value: ty.into() }); |
524 | cfg_options | ||
525 | .insert_key_value("target_has_atomic_load_store".into(), target_has_atomic.into()); | ||
526 | } | 512 | } |
527 | } | 513 | } |
528 | 514 | ||
529 | let rustc_cfgs = || -> Result<String> { | 515 | let rustc_cfgs = { |
530 | // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here. | ||
531 | let mut cmd = Command::new(ra_toolchain::rustc()); | 516 | let mut cmd = Command::new(ra_toolchain::rustc()); |
532 | cmd.args(&["--print", "cfg", "-O"]); | 517 | cmd.args(&["--print", "cfg", "-O"]); |
533 | if let Some(target) = target { | 518 | if let Some(target) = target { |
534 | cmd.args(&["--target", target]); | 519 | cmd.args(&["--target", target]); |
535 | } | 520 | } |
536 | let output = output(cmd)?; | 521 | utf8_stdout(cmd) |
537 | Ok(String::from_utf8(output.stdout)?) | 522 | }; |
538 | }(); | ||
539 | 523 | ||
540 | match rustc_cfgs { | 524 | match rustc_cfgs { |
541 | Ok(rustc_cfgs) => { | 525 | Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())), |
542 | for line in rustc_cfgs.lines() { | ||
543 | match line.find('=') { | ||
544 | None => cfg_options.insert_atom(line.into()), | ||
545 | Some(pos) => { | ||
546 | let key = &line[..pos]; | ||
547 | let value = line[pos + 1..].trim_matches('"'); | ||
548 | cfg_options.insert_key_value(key.into(), value.into()); | ||
549 | } | ||
550 | } | ||
551 | } | ||
552 | } | ||
553 | Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), | 526 | Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), |
554 | } | 527 | } |
555 | 528 | ||
556 | cfg_options.insert_atom("debug_assertions".into()); | 529 | res |
557 | |||
558 | cfg_options | ||
559 | } | 530 | } |
560 | 531 | ||
561 | fn output(mut cmd: Command) -> Result<Output> { | 532 | fn utf8_stdout(mut cmd: Command) -> Result<String> { |
562 | let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?; | 533 | let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?; |
563 | if !output.status.success() { | 534 | if !output.status.success() { |
564 | match String::from_utf8(output.stderr) { | 535 | match String::from_utf8(output.stderr) { |
@@ -568,5 +539,6 @@ fn output(mut cmd: Command) -> Result<Output> { | |||
568 | _ => bail!("{:?} failed, {}", cmd, output.status), | 539 | _ => bail!("{:?} failed, {}", cmd, output.status), |
569 | } | 540 | } |
570 | } | 541 | } |
571 | Ok(output) | 542 | let stdout = String::from_utf8(output.stdout)?; |
543 | Ok(stdout) | ||
572 | } | 544 | } |