aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_project_model/src/lib.rs
diff options
context:
space:
mode:
authorZac Pullar-Strecker <[email protected]>2020-07-31 03:12:44 +0100
committerZac Pullar-Strecker <[email protected]>2020-07-31 03:12:44 +0100
commitf05d7b41a719d848844b054a16477b29d0f063c6 (patch)
tree0a8a0946e8aef2ce64d4c13d0035ba41cce2daf3 /crates/ra_project_model/src/lib.rs
parent73ff610e41959e3e7c78a2b4b25b086883132956 (diff)
parent6b7cb8b5ab539fc4333ce34bc29bf77c976f232a (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.rs182
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 @@
3mod cargo_workspace; 3mod cargo_workspace;
4mod project_json; 4mod project_json;
5mod sysroot; 5mod sysroot;
6mod cfg_flag;
6 7
7use std::{ 8use 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
14use anyhow::{bail, Context, Result}; 14use anyhow::{bail, Context, Result};
@@ -17,14 +17,17 @@ use ra_cfg::CfgOptions;
17use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId}; 17use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId};
18use rustc_hash::{FxHashMap, FxHashSet}; 18use rustc_hash::{FxHashMap, FxHashSet};
19 19
20use crate::cfg_flag::CfgFlag;
21
20pub use crate::{ 22pub 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
25pub use ra_proc_macro::ProcMacroClient; 28pub use ra_proc_macro::ProcMacroClient;
26 29
27#[derive(Debug, Clone)] 30#[derive(Debug, Clone, Eq, PartialEq)]
28pub enum ProjectWorkspace { 31pub 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)]
39pub struct PackageRoot { 42pub 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>,
46impl 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 {
150impl ProjectWorkspace { 135impl 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
516fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions { 504fn 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
561fn output(mut cmd: Command) -> Result<Output> { 532fn 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}