diff options
Diffstat (limited to 'crates/ra_project_model/src/lib.rs')
-rw-r--r-- | crates/ra_project_model/src/lib.rs | 122 |
1 files changed, 49 insertions, 73 deletions
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index fe3e81689..8b85b4831 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -1,25 +1,25 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | mod cargo_workspace; | 3 | mod cargo_workspace; |
4 | mod json_project; | 4 | mod project_json; |
5 | mod sysroot; | 5 | mod sysroot; |
6 | 6 | ||
7 | use std::{ | 7 | use std::{ |
8 | fs::{read_dir, File, ReadDir}, | 8 | fs::{self, read_dir, ReadDir}, |
9 | io::{self, BufReader}, | 9 | io, |
10 | path::{Path, PathBuf}, | 10 | path::Path, |
11 | process::{Command, Output}, | 11 | process::{Command, Output}, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | use anyhow::{bail, Context, Result}; | 14 | use anyhow::{bail, Context, Result}; |
15 | use paths::{AbsPath, AbsPathBuf}; | ||
15 | use ra_cfg::CfgOptions; | 16 | use ra_cfg::CfgOptions; |
16 | use ra_db::{CrateGraph, CrateName, Edition, Env, FileId}; | 17 | use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId}; |
17 | use rustc_hash::{FxHashMap, FxHashSet}; | 18 | use rustc_hash::{FxHashMap, FxHashSet}; |
18 | use serde_json::from_reader; | ||
19 | 19 | ||
20 | pub use crate::{ | 20 | pub use crate::{ |
21 | cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind}, | 21 | cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind}, |
22 | json_project::JsonProject, | 22 | project_json::{ProjectJson, ProjectJsonData}, |
23 | sysroot::Sysroot, | 23 | sysroot::Sysroot, |
24 | }; | 24 | }; |
25 | pub use ra_proc_macro::ProcMacroClient; | 25 | pub use ra_proc_macro::ProcMacroClient; |
@@ -29,7 +29,7 @@ pub enum ProjectWorkspace { | |||
29 | /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. | 29 | /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. |
30 | Cargo { cargo: CargoWorkspace, sysroot: Sysroot }, | 30 | Cargo { cargo: CargoWorkspace, sysroot: Sysroot }, |
31 | /// Project workspace was manually specified using a `rust-project.json` file. | 31 | /// Project workspace was manually specified using a `rust-project.json` file. |
32 | Json { project: JsonProject, project_location: PathBuf }, | 32 | Json { project: ProjectJson }, |
33 | } | 33 | } |
34 | 34 | ||
35 | /// `PackageRoot` describes a package root folder. | 35 | /// `PackageRoot` describes a package root folder. |
@@ -38,22 +38,22 @@ pub enum ProjectWorkspace { | |||
38 | #[derive(Debug, 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: AbsPathBuf, |
42 | /// Is a member of the current workspace | 42 | /// Is a member of the current workspace |
43 | is_member: bool, | 43 | is_member: bool, |
44 | out_dir: Option<PathBuf>, | 44 | out_dir: Option<AbsPathBuf>, |
45 | } | 45 | } |
46 | impl PackageRoot { | 46 | impl PackageRoot { |
47 | pub fn new_member(path: PathBuf) -> PackageRoot { | 47 | pub fn new_member(path: AbsPathBuf) -> PackageRoot { |
48 | Self { path, is_member: true, out_dir: None } | 48 | Self { path, is_member: true, out_dir: None } |
49 | } | 49 | } |
50 | pub fn new_non_member(path: PathBuf) -> PackageRoot { | 50 | pub fn new_non_member(path: AbsPathBuf) -> PackageRoot { |
51 | Self { path, is_member: false, out_dir: None } | 51 | Self { path, is_member: false, out_dir: None } |
52 | } | 52 | } |
53 | pub fn path(&self) -> &Path { | 53 | pub fn path(&self) -> &AbsPath { |
54 | &self.path | 54 | &self.path |
55 | } | 55 | } |
56 | pub fn out_dir(&self) -> Option<&Path> { | 56 | pub fn out_dir(&self) -> Option<&AbsPath> { |
57 | self.out_dir.as_deref() | 57 | self.out_dir.as_deref() |
58 | } | 58 | } |
59 | pub fn is_member(&self) -> bool { | 59 | pub fn is_member(&self) -> bool { |
@@ -63,12 +63,12 @@ impl PackageRoot { | |||
63 | 63 | ||
64 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] | 64 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] |
65 | pub enum ProjectManifest { | 65 | pub enum ProjectManifest { |
66 | ProjectJson(PathBuf), | 66 | ProjectJson(AbsPathBuf), |
67 | CargoToml(PathBuf), | 67 | CargoToml(AbsPathBuf), |
68 | } | 68 | } |
69 | 69 | ||
70 | impl ProjectManifest { | 70 | impl ProjectManifest { |
71 | pub fn from_manifest_file(path: PathBuf) -> Result<ProjectManifest> { | 71 | pub fn from_manifest_file(path: AbsPathBuf) -> Result<ProjectManifest> { |
72 | if path.ends_with("rust-project.json") { | 72 | if path.ends_with("rust-project.json") { |
73 | return Ok(ProjectManifest::ProjectJson(path)); | 73 | return Ok(ProjectManifest::ProjectJson(path)); |
74 | } | 74 | } |
@@ -78,7 +78,7 @@ impl ProjectManifest { | |||
78 | bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) | 78 | bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) |
79 | } | 79 | } |
80 | 80 | ||
81 | pub fn discover_single(path: &Path) -> Result<ProjectManifest> { | 81 | pub fn discover_single(path: &AbsPath) -> Result<ProjectManifest> { |
82 | let mut candidates = ProjectManifest::discover(path)?; | 82 | let mut candidates = ProjectManifest::discover(path)?; |
83 | let res = match candidates.pop() { | 83 | let res = match candidates.pop() { |
84 | None => bail!("no projects"), | 84 | None => bail!("no projects"), |
@@ -91,23 +91,23 @@ impl ProjectManifest { | |||
91 | Ok(res) | 91 | Ok(res) |
92 | } | 92 | } |
93 | 93 | ||
94 | pub fn discover(path: &Path) -> io::Result<Vec<ProjectManifest>> { | 94 | pub fn discover(path: &AbsPath) -> io::Result<Vec<ProjectManifest>> { |
95 | if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { | 95 | if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { |
96 | return Ok(vec![ProjectManifest::ProjectJson(project_json)]); | 96 | return Ok(vec![ProjectManifest::ProjectJson(project_json)]); |
97 | } | 97 | } |
98 | return find_cargo_toml(path) | 98 | return find_cargo_toml(path) |
99 | .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect()); | 99 | .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect()); |
100 | 100 | ||
101 | fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { | 101 | fn find_cargo_toml(path: &AbsPath) -> io::Result<Vec<AbsPathBuf>> { |
102 | match find_in_parent_dirs(path, "Cargo.toml") { | 102 | match find_in_parent_dirs(path, "Cargo.toml") { |
103 | Some(it) => Ok(vec![it]), | 103 | Some(it) => Ok(vec![it]), |
104 | None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)), | 104 | None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)), |
105 | } | 105 | } |
106 | } | 106 | } |
107 | 107 | ||
108 | fn find_in_parent_dirs(path: &Path, target_file_name: &str) -> Option<PathBuf> { | 108 | fn find_in_parent_dirs(path: &AbsPath, target_file_name: &str) -> Option<AbsPathBuf> { |
109 | if path.ends_with(target_file_name) { | 109 | if path.ends_with(target_file_name) { |
110 | return Some(path.to_owned()); | 110 | return Some(path.to_path_buf()); |
111 | } | 111 | } |
112 | 112 | ||
113 | let mut curr = Some(path); | 113 | let mut curr = Some(path); |
@@ -123,17 +123,18 @@ impl ProjectManifest { | |||
123 | None | 123 | None |
124 | } | 124 | } |
125 | 125 | ||
126 | fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<PathBuf> { | 126 | fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<AbsPathBuf> { |
127 | // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects | 127 | // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects |
128 | entities | 128 | entities |
129 | .filter_map(Result::ok) | 129 | .filter_map(Result::ok) |
130 | .map(|it| it.path().join("Cargo.toml")) | 130 | .map(|it| it.path().join("Cargo.toml")) |
131 | .filter(|it| it.exists()) | 131 | .filter(|it| it.exists()) |
132 | .map(AbsPathBuf::assert) | ||
132 | .collect() | 133 | .collect() |
133 | } | 134 | } |
134 | } | 135 | } |
135 | 136 | ||
136 | pub fn discover_all(paths: &[impl AsRef<Path>]) -> Vec<ProjectManifest> { | 137 | pub fn discover_all(paths: &[impl AsRef<AbsPath>]) -> Vec<ProjectManifest> { |
137 | let mut res = paths | 138 | let mut res = paths |
138 | .iter() | 139 | .iter() |
139 | .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok()) | 140 | .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok()) |
@@ -154,20 +155,15 @@ impl ProjectWorkspace { | |||
154 | ) -> Result<ProjectWorkspace> { | 155 | ) -> Result<ProjectWorkspace> { |
155 | let res = match manifest { | 156 | let res = match manifest { |
156 | ProjectManifest::ProjectJson(project_json) => { | 157 | ProjectManifest::ProjectJson(project_json) => { |
157 | let file = File::open(&project_json).with_context(|| { | 158 | let file = fs::read_to_string(&project_json).with_context(|| { |
158 | format!("Failed to open json file {}", project_json.display()) | 159 | format!("Failed to read json file {}", project_json.display()) |
159 | })?; | 160 | })?; |
160 | let reader = BufReader::new(file); | 161 | let data = serde_json::from_str(&file).with_context(|| { |
161 | let project_location = match project_json.parent() { | 162 | format!("Failed to deserialize json file {}", project_json.display()) |
162 | Some(parent) => PathBuf::from(parent), | 163 | })?; |
163 | None => PathBuf::new(), | 164 | let project_location = project_json.parent().unwrap().to_path_buf(); |
164 | }; | 165 | let project = ProjectJson::new(&project_location, data); |
165 | ProjectWorkspace::Json { | 166 | ProjectWorkspace::Json { project } |
166 | project: from_reader(reader).with_context(|| { | ||
167 | format!("Failed to deserialize json file {}", project_json.display()) | ||
168 | })?, | ||
169 | project_location: project_location, | ||
170 | } | ||
171 | } | 167 | } |
172 | ProjectManifest::CargoToml(cargo_toml) => { | 168 | ProjectManifest::CargoToml(cargo_toml) => { |
173 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) | 169 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) |
@@ -199,11 +195,9 @@ impl ProjectWorkspace { | |||
199 | /// the root is a member of the current workspace | 195 | /// the root is a member of the current workspace |
200 | pub fn to_roots(&self) -> Vec<PackageRoot> { | 196 | pub fn to_roots(&self) -> Vec<PackageRoot> { |
201 | match self { | 197 | match self { |
202 | ProjectWorkspace::Json { project, project_location } => project | 198 | ProjectWorkspace::Json { project } => { |
203 | .roots | 199 | project.roots.iter().map(|r| PackageRoot::new_member(r.path.clone())).collect() |
204 | .iter() | 200 | } |
205 | .map(|r| PackageRoot::new_member(project_location.join(&r.path))) | ||
206 | .collect(), | ||
207 | ProjectWorkspace::Cargo { cargo, sysroot } => cargo | 201 | ProjectWorkspace::Cargo { cargo, sysroot } => cargo |
208 | .packages() | 202 | .packages() |
209 | .map(|pkg| PackageRoot { | 203 | .map(|pkg| PackageRoot { |
@@ -218,9 +212,9 @@ impl ProjectWorkspace { | |||
218 | } | 212 | } |
219 | } | 213 | } |
220 | 214 | ||
221 | pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> { | 215 | pub fn proc_macro_dylib_paths(&self) -> Vec<AbsPathBuf> { |
222 | match self { | 216 | match self { |
223 | ProjectWorkspace::Json { project, .. } => project | 217 | ProjectWorkspace::Json { project } => project |
224 | .crates | 218 | .crates |
225 | .iter() | 219 | .iter() |
226 | .filter_map(|krate| krate.proc_macro_dylib_path.as_ref()) | 220 | .filter_map(|krate| krate.proc_macro_dylib_path.as_ref()) |
@@ -247,36 +241,18 @@ impl ProjectWorkspace { | |||
247 | &self, | 241 | &self, |
248 | target: Option<&str>, | 242 | target: Option<&str>, |
249 | proc_macro_client: &ProcMacroClient, | 243 | proc_macro_client: &ProcMacroClient, |
250 | load: &mut dyn FnMut(&Path) -> Option<FileId>, | 244 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, |
251 | ) -> CrateGraph { | 245 | ) -> CrateGraph { |
252 | let mut crate_graph = CrateGraph::default(); | 246 | let mut crate_graph = CrateGraph::default(); |
253 | match self { | 247 | match self { |
254 | ProjectWorkspace::Json { project, project_location } => { | 248 | ProjectWorkspace::Json { project } => { |
255 | let crates: FxHashMap<_, _> = project | 249 | let crates: FxHashMap<_, _> = project |
256 | .crates | 250 | .crates |
257 | .iter() | 251 | .iter() |
258 | .enumerate() | 252 | .enumerate() |
259 | .filter_map(|(seq_index, krate)| { | 253 | .filter_map(|(seq_index, krate)| { |
260 | let file_path = project_location.join(&krate.root_module); | 254 | let file_path = &krate.root_module; |
261 | let file_id = load(&file_path)?; | 255 | let file_id = load(&file_path)?; |
262 | let edition = match krate.edition { | ||
263 | json_project::Edition::Edition2015 => Edition::Edition2015, | ||
264 | json_project::Edition::Edition2018 => Edition::Edition2018, | ||
265 | }; | ||
266 | let cfg_options = { | ||
267 | let mut opts = CfgOptions::default(); | ||
268 | for cfg in &krate.cfg { | ||
269 | match cfg.find('=') { | ||
270 | None => opts.insert_atom(cfg.into()), | ||
271 | Some(pos) => { | ||
272 | let key = &cfg[..pos]; | ||
273 | let value = cfg[pos + 1..].trim_matches('"'); | ||
274 | opts.insert_key_value(key.into(), value.into()); | ||
275 | } | ||
276 | } | ||
277 | } | ||
278 | opts | ||
279 | }; | ||
280 | 256 | ||
281 | let mut env = Env::default(); | 257 | let mut env = Env::default(); |
282 | if let Some(out_dir) = &krate.out_dir { | 258 | if let Some(out_dir) = &krate.out_dir { |
@@ -291,13 +267,13 @@ impl ProjectWorkspace { | |||
291 | .map(|it| proc_macro_client.by_dylib_path(&it)); | 267 | .map(|it| proc_macro_client.by_dylib_path(&it)); |
292 | // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env | 268 | // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env |
293 | Some(( | 269 | Some(( |
294 | json_project::CrateId(seq_index), | 270 | CrateId(seq_index as u32), |
295 | crate_graph.add_crate_root( | 271 | crate_graph.add_crate_root( |
296 | file_id, | 272 | file_id, |
297 | edition, | 273 | krate.edition, |
298 | // FIXME json definitions can store the crate name | 274 | // FIXME json definitions can store the crate name |
299 | None, | 275 | None, |
300 | cfg_options, | 276 | krate.cfg.clone(), |
301 | env, | 277 | env, |
302 | proc_macro.unwrap_or_default(), | 278 | proc_macro.unwrap_or_default(), |
303 | ), | 279 | ), |
@@ -307,8 +283,8 @@ impl ProjectWorkspace { | |||
307 | 283 | ||
308 | for (id, krate) in project.crates.iter().enumerate() { | 284 | for (id, krate) in project.crates.iter().enumerate() { |
309 | for dep in &krate.deps { | 285 | for dep in &krate.deps { |
310 | let from_crate_id = json_project::CrateId(id); | 286 | let from_crate_id = CrateId(id as u32); |
311 | let to_crate_id = dep.krate; | 287 | let to_crate_id = dep.crate_id; |
312 | if let (Some(&from), Some(&to)) = | 288 | if let (Some(&from), Some(&to)) = |
313 | (crates.get(&from_crate_id), crates.get(&to_crate_id)) | 289 | (crates.get(&from_crate_id), crates.get(&to_crate_id)) |
314 | { | 290 | { |
@@ -524,15 +500,15 @@ impl ProjectWorkspace { | |||
524 | crate_graph | 500 | crate_graph |
525 | } | 501 | } |
526 | 502 | ||
527 | pub fn workspace_root_for(&self, path: &Path) -> Option<&Path> { | 503 | pub fn workspace_root_for(&self, path: &Path) -> Option<&AbsPath> { |
528 | match self { | 504 | match self { |
529 | ProjectWorkspace::Cargo { cargo, .. } => { | 505 | ProjectWorkspace::Cargo { cargo, .. } => { |
530 | Some(cargo.workspace_root()).filter(|root| path.starts_with(root)) | 506 | Some(cargo.workspace_root()).filter(|root| path.starts_with(root)) |
531 | } | 507 | } |
532 | ProjectWorkspace::Json { project: JsonProject { roots, .. }, .. } => roots | 508 | ProjectWorkspace::Json { project: ProjectJson { roots, .. }, .. } => roots |
533 | .iter() | 509 | .iter() |
534 | .find(|root| path.starts_with(&root.path)) | 510 | .find(|root| path.starts_with(&root.path)) |
535 | .map(|root| root.path.as_ref()), | 511 | .map(|root| root.path.as_path()), |
536 | } | 512 | } |
537 | } | 513 | } |
538 | } | 514 | } |