diff options
Diffstat (limited to 'crates/ra_project_model/src/lib.rs')
-rw-r--r-- | crates/ra_project_model/src/lib.rs | 108 |
1 files changed, 53 insertions, 55 deletions
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index e7da683d6..9b30bef8d 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -8,7 +8,7 @@ use std::{ | |||
8 | fs::{read_dir, File, ReadDir}, | 8 | fs::{read_dir, File, ReadDir}, |
9 | io::{self, BufReader}, | 9 | io::{self, BufReader}, |
10 | path::{Path, PathBuf}, | 10 | path::{Path, PathBuf}, |
11 | process::Command, | 11 | process::{Command, Output}, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | use anyhow::{bail, Context, Result}; | 14 | use anyhow::{bail, Context, Result}; |
@@ -35,7 +35,7 @@ pub enum ProjectWorkspace { | |||
35 | /// `PackageRoot` describes a package root folder. | 35 | /// `PackageRoot` describes a package root folder. |
36 | /// Which may be an external dependency, or a member of | 36 | /// Which may be an external dependency, or a member of |
37 | /// the current workspace. | 37 | /// the current workspace. |
38 | #[derive(Clone)] | 38 | #[derive(Debug, Clone)] |
39 | pub struct PackageRoot { | 39 | pub struct PackageRoot { |
40 | /// Path to the root folder | 40 | /// Path to the root folder |
41 | path: PathBuf, | 41 | path: PathBuf, |
@@ -88,46 +88,28 @@ impl ProjectRoot { | |||
88 | } | 88 | } |
89 | 89 | ||
90 | pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> { | 90 | pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> { |
91 | if let Some(project_json) = find_rust_project_json(path) { | 91 | if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { |
92 | return Ok(vec![ProjectRoot::ProjectJson(project_json)]); | 92 | return Ok(vec![ProjectRoot::ProjectJson(project_json)]); |
93 | } | 93 | } |
94 | return find_cargo_toml(path) | 94 | return find_cargo_toml(path) |
95 | .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); | 95 | .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); |
96 | 96 | ||
97 | fn find_rust_project_json(path: &Path) -> Option<PathBuf> { | ||
98 | if path.ends_with("rust-project.json") { | ||
99 | return Some(path.to_path_buf()); | ||
100 | } | ||
101 | |||
102 | let mut curr = Some(path); | ||
103 | while let Some(path) = curr { | ||
104 | let candidate = path.join("rust-project.json"); | ||
105 | if candidate.exists() { | ||
106 | return Some(candidate); | ||
107 | } | ||
108 | curr = path.parent(); | ||
109 | } | ||
110 | |||
111 | None | ||
112 | } | ||
113 | |||
114 | fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { | 97 | fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { |
115 | if path.ends_with("Cargo.toml") { | 98 | match find_in_parent_dirs(path, "Cargo.toml") { |
116 | return Ok(vec![path.to_path_buf()]); | 99 | Some(it) => Ok(vec![it]), |
100 | None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)), | ||
117 | } | 101 | } |
102 | } | ||
118 | 103 | ||
119 | if let Some(p) = find_cargo_toml_in_parent_dir(path) { | 104 | fn find_in_parent_dirs(path: &Path, target_file_name: &str) -> Option<PathBuf> { |
120 | return Ok(vec![p]); | 105 | if path.ends_with(target_file_name) { |
106 | return Some(path.to_owned()); | ||
121 | } | 107 | } |
122 | 108 | ||
123 | let entities = read_dir(path)?; | ||
124 | Ok(find_cargo_toml_in_child_dir(entities)) | ||
125 | } | ||
126 | |||
127 | fn find_cargo_toml_in_parent_dir(path: &Path) -> Option<PathBuf> { | ||
128 | let mut curr = Some(path); | 109 | let mut curr = Some(path); |
110 | |||
129 | while let Some(path) = curr { | 111 | while let Some(path) = curr { |
130 | let candidate = path.join("Cargo.toml"); | 112 | let candidate = path.join(target_file_name); |
131 | if candidate.exists() { | 113 | if candidate.exists() { |
132 | return Some(candidate); | 114 | return Some(candidate); |
133 | } | 115 | } |
@@ -139,14 +121,11 @@ impl ProjectRoot { | |||
139 | 121 | ||
140 | fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<PathBuf> { | 122 | fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<PathBuf> { |
141 | // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects | 123 | // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects |
142 | let mut valid_canditates = vec![]; | 124 | entities |
143 | for entity in entities.filter_map(Result::ok) { | 125 | .filter_map(Result::ok) |
144 | let candidate = entity.path().join("Cargo.toml"); | 126 | .map(|it| it.path().join("Cargo.toml")) |
145 | if candidate.exists() { | 127 | .filter(|it| it.exists()) |
146 | valid_canditates.push(candidate) | 128 | .collect() |
147 | } | ||
148 | } | ||
149 | valid_canditates | ||
150 | } | 129 | } |
151 | } | 130 | } |
152 | } | 131 | } |
@@ -408,7 +387,18 @@ impl ProjectWorkspace { | |||
408 | let edition = cargo[pkg].edition; | 387 | let edition = cargo[pkg].edition; |
409 | let cfg_options = { | 388 | let cfg_options = { |
410 | let mut opts = default_cfg_options.clone(); | 389 | let mut opts = default_cfg_options.clone(); |
411 | opts.insert_features(cargo[pkg].features.iter().map(Into::into)); | 390 | for feature in cargo[pkg].features.iter() { |
391 | opts.insert_key_value("feature".into(), feature.into()); | ||
392 | } | ||
393 | for cfg in cargo[pkg].cfgs.iter() { | ||
394 | match cfg.find('=') { | ||
395 | Some(split) => opts.insert_key_value( | ||
396 | cfg[..split].into(), | ||
397 | cfg[split + 1..].trim_matches('"').into(), | ||
398 | ), | ||
399 | None => opts.insert_atom(cfg.into()), | ||
400 | }; | ||
401 | } | ||
412 | opts | 402 | opts |
413 | }; | 403 | }; |
414 | let mut env = Env::default(); | 404 | let mut env = Env::default(); |
@@ -553,7 +543,7 @@ impl ProjectWorkspace { | |||
553 | } | 543 | } |
554 | } | 544 | } |
555 | 545 | ||
556 | pub fn get_rustc_cfg_options() -> CfgOptions { | 546 | pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { |
557 | let mut cfg_options = CfgOptions::default(); | 547 | let mut cfg_options = CfgOptions::default(); |
558 | 548 | ||
559 | // Some nightly-only cfgs, which are required for stdlib | 549 | // Some nightly-only cfgs, which are required for stdlib |
@@ -566,23 +556,18 @@ pub fn get_rustc_cfg_options() -> CfgOptions { | |||
566 | } | 556 | } |
567 | } | 557 | } |
568 | 558 | ||
569 | match (|| -> Result<String> { | 559 | let rustc_cfgs = || -> Result<String> { |
570 | // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here. | 560 | // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here. |
571 | let output = Command::new("rustc") | 561 | let mut cmd = Command::new(ra_toolchain::rustc()); |
572 | .args(&["--print", "cfg", "-O"]) | 562 | cmd.args(&["--print", "cfg", "-O"]); |
573 | .output() | 563 | if let Some(target) = target { |
574 | .context("Failed to get output from rustc --print cfg -O")?; | 564 | cmd.args(&["--target", target.as_str()]); |
575 | if !output.status.success() { | ||
576 | bail!( | ||
577 | "rustc --print cfg -O exited with exit code ({})", | ||
578 | output | ||
579 | .status | ||
580 | .code() | ||
581 | .map_or(String::from("no exit code"), |code| format!("{}", code)) | ||
582 | ); | ||
583 | } | 565 | } |
566 | let output = output(cmd)?; | ||
584 | Ok(String::from_utf8(output.stdout)?) | 567 | Ok(String::from_utf8(output.stdout)?) |
585 | })() { | 568 | }(); |
569 | |||
570 | match rustc_cfgs { | ||
586 | Ok(rustc_cfgs) => { | 571 | Ok(rustc_cfgs) => { |
587 | for line in rustc_cfgs.lines() { | 572 | for line in rustc_cfgs.lines() { |
588 | match line.find('=') { | 573 | match line.find('=') { |
@@ -595,8 +580,21 @@ pub fn get_rustc_cfg_options() -> CfgOptions { | |||
595 | } | 580 | } |
596 | } | 581 | } |
597 | } | 582 | } |
598 | Err(e) => log::error!("failed to get rustc cfgs: {}", e), | 583 | Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), |
599 | } | 584 | } |
600 | 585 | ||
601 | cfg_options | 586 | cfg_options |
602 | } | 587 | } |
588 | |||
589 | fn output(mut cmd: Command) -> Result<Output> { | ||
590 | let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?; | ||
591 | if !output.status.success() { | ||
592 | match String::from_utf8(output.stderr) { | ||
593 | Ok(stderr) if !stderr.is_empty() => { | ||
594 | bail!("{:?} failed, {}\nstderr:\n{}", cmd, output.status, stderr) | ||
595 | } | ||
596 | _ => bail!("{:?} failed, {}", cmd, output.status), | ||
597 | } | ||
598 | } | ||
599 | Ok(output) | ||
600 | } | ||