diff options
Diffstat (limited to 'crates/ra_project_model')
-rw-r--r-- | crates/ra_project_model/src/cargo_workspace.rs | 2 | ||||
-rw-r--r-- | crates/ra_project_model/src/json_project.rs | 55 | ||||
-rw-r--r-- | crates/ra_project_model/src/lib.rs | 111 |
3 files changed, 112 insertions, 56 deletions
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index a306ce95f..4b7444039 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
@@ -64,7 +64,7 @@ impl Default for CargoConfig { | |||
64 | fn default() -> Self { | 64 | fn default() -> Self { |
65 | CargoConfig { | 65 | CargoConfig { |
66 | no_default_features: false, | 66 | no_default_features: false, |
67 | all_features: true, | 67 | all_features: false, |
68 | features: Vec::new(), | 68 | features: Vec::new(), |
69 | load_out_dirs_from_check: false, | 69 | load_out_dirs_from_check: false, |
70 | target: None, | 70 | target: None, |
diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs index b030c8a6a..ee2de4c25 100644 --- a/crates/ra_project_model/src/json_project.rs +++ b/crates/ra_project_model/src/json_project.rs | |||
@@ -2,9 +2,16 @@ | |||
2 | 2 | ||
3 | use std::path::PathBuf; | 3 | use std::path::PathBuf; |
4 | 4 | ||
5 | use rustc_hash::{FxHashMap, FxHashSet}; | 5 | use rustc_hash::FxHashSet; |
6 | use serde::Deserialize; | 6 | use serde::Deserialize; |
7 | 7 | ||
8 | /// Roots and crates that compose this Rust project. | ||
9 | #[derive(Clone, Debug, Deserialize)] | ||
10 | pub struct JsonProject { | ||
11 | pub(crate) roots: Vec<Root>, | ||
12 | pub(crate) crates: Vec<Crate>, | ||
13 | } | ||
14 | |||
8 | /// A root points to the directory which contains Rust crates. rust-analyzer watches all files in | 15 | /// A root points to the directory which contains Rust crates. rust-analyzer watches all files in |
9 | /// all roots. Roots might be nested. | 16 | /// all roots. Roots might be nested. |
10 | #[derive(Clone, Debug, Deserialize)] | 17 | #[derive(Clone, Debug, Deserialize)] |
@@ -20,8 +27,10 @@ pub struct Crate { | |||
20 | pub(crate) root_module: PathBuf, | 27 | pub(crate) root_module: PathBuf, |
21 | pub(crate) edition: Edition, | 28 | pub(crate) edition: Edition, |
22 | pub(crate) deps: Vec<Dep>, | 29 | pub(crate) deps: Vec<Dep>, |
23 | pub(crate) atom_cfgs: FxHashSet<String>, | 30 | |
24 | pub(crate) key_value_cfgs: FxHashMap<String, String>, | 31 | #[serde(default)] |
32 | pub(crate) cfg: FxHashSet<String>, | ||
33 | |||
25 | pub(crate) out_dir: Option<PathBuf>, | 34 | pub(crate) out_dir: Option<PathBuf>, |
26 | pub(crate) proc_macro_dylib_path: Option<PathBuf>, | 35 | pub(crate) proc_macro_dylib_path: Option<PathBuf>, |
27 | } | 36 | } |
@@ -48,9 +57,39 @@ pub struct Dep { | |||
48 | pub(crate) name: String, | 57 | pub(crate) name: String, |
49 | } | 58 | } |
50 | 59 | ||
51 | /// Roots and crates that compose this Rust project. | 60 | #[cfg(test)] |
52 | #[derive(Clone, Debug, Deserialize)] | 61 | mod tests { |
53 | pub struct JsonProject { | 62 | use super::*; |
54 | pub(crate) roots: Vec<Root>, | 63 | use serde_json::json; |
55 | pub(crate) crates: Vec<Crate>, | 64 | |
65 | #[test] | ||
66 | fn test_crate_deserialization() { | ||
67 | let raw_json = json!( { | ||
68 | "crate_id": 2, | ||
69 | "root_module": "this/is/a/file/path.rs", | ||
70 | "deps": [ | ||
71 | { | ||
72 | "crate": 1, | ||
73 | "name": "some_dep_crate" | ||
74 | }, | ||
75 | ], | ||
76 | "edition": "2015", | ||
77 | "cfg": [ | ||
78 | "atom_1", | ||
79 | "atom_2", | ||
80 | "feature=feature_1", | ||
81 | "feature=feature_2", | ||
82 | "other=value", | ||
83 | ], | ||
84 | |||
85 | }); | ||
86 | |||
87 | let krate: Crate = serde_json::from_value(raw_json).unwrap(); | ||
88 | |||
89 | assert!(krate.cfg.contains(&"atom_1".to_string())); | ||
90 | assert!(krate.cfg.contains(&"atom_2".to_string())); | ||
91 | assert!(krate.cfg.contains(&"feature=feature_1".to_string())); | ||
92 | assert!(krate.cfg.contains(&"feature=feature_2".to_string())); | ||
93 | assert!(krate.cfg.contains(&"other=value".to_string())); | ||
94 | } | ||
56 | } | 95 | } |
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index a2e9f65ef..cb0e27dce 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -14,7 +14,7 @@ use std::{ | |||
14 | use anyhow::{bail, Context, Result}; | 14 | use anyhow::{bail, Context, Result}; |
15 | use ra_cfg::CfgOptions; | 15 | use ra_cfg::CfgOptions; |
16 | use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; | 16 | use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; |
17 | use rustc_hash::FxHashMap; | 17 | use rustc_hash::{FxHashMap, FxHashSet}; |
18 | use serde_json::from_reader; | 18 | use serde_json::from_reader; |
19 | 19 | ||
20 | pub use crate::{ | 20 | pub use crate::{ |
@@ -32,6 +32,12 @@ pub enum ProjectWorkspace { | |||
32 | Json { project: JsonProject }, | 32 | Json { project: JsonProject }, |
33 | } | 33 | } |
34 | 34 | ||
35 | impl From<JsonProject> for ProjectWorkspace { | ||
36 | fn from(project: JsonProject) -> ProjectWorkspace { | ||
37 | ProjectWorkspace::Json { project } | ||
38 | } | ||
39 | } | ||
40 | |||
35 | /// `PackageRoot` describes a package root folder. | 41 | /// `PackageRoot` describes a package root folder. |
36 | /// Which may be an external dependency, or a member of | 42 | /// Which may be an external dependency, or a member of |
37 | /// the current workspace. | 43 | /// the current workspace. |
@@ -41,41 +47,45 @@ pub struct PackageRoot { | |||
41 | path: PathBuf, | 47 | path: PathBuf, |
42 | /// Is a member of the current workspace | 48 | /// Is a member of the current workspace |
43 | is_member: bool, | 49 | is_member: bool, |
50 | out_dir: Option<PathBuf>, | ||
44 | } | 51 | } |
45 | impl PackageRoot { | 52 | impl PackageRoot { |
46 | pub fn new_member(path: PathBuf) -> PackageRoot { | 53 | pub fn new_member(path: PathBuf) -> PackageRoot { |
47 | Self { path, is_member: true } | 54 | Self { path, is_member: true, out_dir: None } |
48 | } | 55 | } |
49 | pub fn new_non_member(path: PathBuf) -> PackageRoot { | 56 | pub fn new_non_member(path: PathBuf) -> PackageRoot { |
50 | Self { path, is_member: false } | 57 | Self { path, is_member: false, out_dir: None } |
51 | } | 58 | } |
52 | pub fn path(&self) -> &Path { | 59 | pub fn path(&self) -> &Path { |
53 | &self.path | 60 | &self.path |
54 | } | 61 | } |
62 | pub fn out_dir(&self) -> Option<&Path> { | ||
63 | self.out_dir.as_deref() | ||
64 | } | ||
55 | pub fn is_member(&self) -> bool { | 65 | pub fn is_member(&self) -> bool { |
56 | self.is_member | 66 | self.is_member |
57 | } | 67 | } |
58 | } | 68 | } |
59 | 69 | ||
60 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 70 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] |
61 | pub enum ProjectRoot { | 71 | pub enum ProjectManifest { |
62 | ProjectJson(PathBuf), | 72 | ProjectJson(PathBuf), |
63 | CargoToml(PathBuf), | 73 | CargoToml(PathBuf), |
64 | } | 74 | } |
65 | 75 | ||
66 | impl ProjectRoot { | 76 | impl ProjectManifest { |
67 | pub fn from_manifest_file(path: PathBuf) -> Result<ProjectRoot> { | 77 | pub fn from_manifest_file(path: PathBuf) -> Result<ProjectManifest> { |
68 | if path.ends_with("rust-project.json") { | 78 | if path.ends_with("rust-project.json") { |
69 | return Ok(ProjectRoot::ProjectJson(path)); | 79 | return Ok(ProjectManifest::ProjectJson(path)); |
70 | } | 80 | } |
71 | if path.ends_with("Cargo.toml") { | 81 | if path.ends_with("Cargo.toml") { |
72 | return Ok(ProjectRoot::CargoToml(path)); | 82 | return Ok(ProjectManifest::CargoToml(path)); |
73 | } | 83 | } |
74 | bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) | 84 | bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) |
75 | } | 85 | } |
76 | 86 | ||
77 | pub fn discover_single(path: &Path) -> Result<ProjectRoot> { | 87 | pub fn discover_single(path: &Path) -> Result<ProjectManifest> { |
78 | let mut candidates = ProjectRoot::discover(path)?; | 88 | let mut candidates = ProjectManifest::discover(path)?; |
79 | let res = match candidates.pop() { | 89 | let res = match candidates.pop() { |
80 | None => bail!("no projects"), | 90 | None => bail!("no projects"), |
81 | Some(it) => it, | 91 | Some(it) => it, |
@@ -87,12 +97,12 @@ impl ProjectRoot { | |||
87 | Ok(res) | 97 | Ok(res) |
88 | } | 98 | } |
89 | 99 | ||
90 | pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> { | 100 | pub fn discover(path: &Path) -> io::Result<Vec<ProjectManifest>> { |
91 | if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { | 101 | if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { |
92 | return Ok(vec![ProjectRoot::ProjectJson(project_json)]); | 102 | return Ok(vec![ProjectManifest::ProjectJson(project_json)]); |
93 | } | 103 | } |
94 | return find_cargo_toml(path) | 104 | return find_cargo_toml(path) |
95 | .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); | 105 | .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect()); |
96 | 106 | ||
97 | fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { | 107 | fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { |
98 | match find_in_parent_dirs(path, "Cargo.toml") { | 108 | match find_in_parent_dirs(path, "Cargo.toml") { |
@@ -128,16 +138,28 @@ impl ProjectRoot { | |||
128 | .collect() | 138 | .collect() |
129 | } | 139 | } |
130 | } | 140 | } |
141 | |||
142 | pub fn discover_all(paths: &[impl AsRef<Path>]) -> Vec<ProjectManifest> { | ||
143 | let mut res = paths | ||
144 | .iter() | ||
145 | .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok()) | ||
146 | .flatten() | ||
147 | .collect::<FxHashSet<_>>() | ||
148 | .into_iter() | ||
149 | .collect::<Vec<_>>(); | ||
150 | res.sort(); | ||
151 | res | ||
152 | } | ||
131 | } | 153 | } |
132 | 154 | ||
133 | impl ProjectWorkspace { | 155 | impl ProjectWorkspace { |
134 | pub fn load( | 156 | pub fn load( |
135 | root: ProjectRoot, | 157 | manifest: ProjectManifest, |
136 | cargo_features: &CargoConfig, | 158 | cargo_features: &CargoConfig, |
137 | with_sysroot: bool, | 159 | with_sysroot: bool, |
138 | ) -> Result<ProjectWorkspace> { | 160 | ) -> Result<ProjectWorkspace> { |
139 | let res = match root { | 161 | let res = match manifest { |
140 | ProjectRoot::ProjectJson(project_json) => { | 162 | ProjectManifest::ProjectJson(project_json) => { |
141 | let file = File::open(&project_json).with_context(|| { | 163 | let file = File::open(&project_json).with_context(|| { |
142 | format!("Failed to open json file {}", project_json.display()) | 164 | format!("Failed to open json file {}", project_json.display()) |
143 | })?; | 165 | })?; |
@@ -148,7 +170,7 @@ impl ProjectWorkspace { | |||
148 | })?, | 170 | })?, |
149 | } | 171 | } |
150 | } | 172 | } |
151 | ProjectRoot::CargoToml(cargo_toml) => { | 173 | ProjectManifest::CargoToml(cargo_toml) => { |
152 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) | 174 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) |
153 | .with_context(|| { | 175 | .with_context(|| { |
154 | format!( | 176 | format!( |
@@ -186,6 +208,7 @@ impl ProjectWorkspace { | |||
186 | .map(|pkg| PackageRoot { | 208 | .map(|pkg| PackageRoot { |
187 | path: cargo[pkg].root().to_path_buf(), | 209 | path: cargo[pkg].root().to_path_buf(), |
188 | is_member: cargo[pkg].is_member, | 210 | is_member: cargo[pkg].is_member, |
211 | out_dir: cargo[pkg].out_dir.clone(), | ||
189 | }) | 212 | }) |
190 | .chain(sysroot.crates().map(|krate| { | 213 | .chain(sysroot.crates().map(|krate| { |
191 | PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf()) | 214 | PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf()) |
@@ -194,17 +217,6 @@ impl ProjectWorkspace { | |||
194 | } | 217 | } |
195 | } | 218 | } |
196 | 219 | ||
197 | pub fn out_dirs(&self) -> Vec<PathBuf> { | ||
198 | match self { | ||
199 | ProjectWorkspace::Json { project } => { | ||
200 | project.crates.iter().filter_map(|krate| krate.out_dir.as_ref()).cloned().collect() | ||
201 | } | ||
202 | ProjectWorkspace::Cargo { cargo, sysroot: _ } => { | ||
203 | cargo.packages().filter_map(|pkg| cargo[pkg].out_dir.as_ref()).cloned().collect() | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | |||
208 | pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> { | 220 | pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> { |
209 | match self { | 221 | match self { |
210 | ProjectWorkspace::Json { project } => project | 222 | ProjectWorkspace::Json { project } => project |
@@ -232,7 +244,7 @@ impl ProjectWorkspace { | |||
232 | 244 | ||
233 | pub fn to_crate_graph( | 245 | pub fn to_crate_graph( |
234 | &self, | 246 | &self, |
235 | default_cfg_options: &CfgOptions, | 247 | target: Option<&str>, |
236 | extern_source_roots: &FxHashMap<PathBuf, ExternSourceId>, | 248 | extern_source_roots: &FxHashMap<PathBuf, ExternSourceId>, |
237 | proc_macro_client: &ProcMacroClient, | 249 | proc_macro_client: &ProcMacroClient, |
238 | load: &mut dyn FnMut(&Path) -> Option<FileId>, | 250 | load: &mut dyn FnMut(&Path) -> Option<FileId>, |
@@ -251,12 +263,16 @@ impl ProjectWorkspace { | |||
251 | json_project::Edition::Edition2018 => Edition::Edition2018, | 263 | json_project::Edition::Edition2018 => Edition::Edition2018, |
252 | }; | 264 | }; |
253 | let cfg_options = { | 265 | let cfg_options = { |
254 | let mut opts = default_cfg_options.clone(); | 266 | let mut opts = CfgOptions::default(); |
255 | for name in &krate.atom_cfgs { | 267 | for cfg in &krate.cfg { |
256 | opts.insert_atom(name.into()); | 268 | match cfg.find('=') { |
257 | } | 269 | None => opts.insert_atom(cfg.into()), |
258 | for (key, value) in &krate.key_value_cfgs { | 270 | Some(pos) => { |
259 | opts.insert_key_value(key.into(), value.into()); | 271 | let key = &cfg[..pos]; |
272 | let value = cfg[pos + 1..].trim_matches('"'); | ||
273 | opts.insert_key_value(key.into(), value.into()); | ||
274 | } | ||
275 | } | ||
260 | } | 276 | } |
261 | opts | 277 | opts |
262 | }; | 278 | }; |
@@ -315,18 +331,13 @@ impl ProjectWorkspace { | |||
315 | } | 331 | } |
316 | } | 332 | } |
317 | ProjectWorkspace::Cargo { cargo, sysroot } => { | 333 | ProjectWorkspace::Cargo { cargo, sysroot } => { |
334 | let mut cfg_options = get_rustc_cfg_options(target); | ||
335 | |||
318 | let sysroot_crates: FxHashMap<_, _> = sysroot | 336 | let sysroot_crates: FxHashMap<_, _> = sysroot |
319 | .crates() | 337 | .crates() |
320 | .filter_map(|krate| { | 338 | .filter_map(|krate| { |
321 | let file_id = load(&sysroot[krate].root)?; | 339 | let file_id = load(&sysroot[krate].root)?; |
322 | 340 | ||
323 | // Crates from sysroot have `cfg(test)` disabled | ||
324 | let cfg_options = { | ||
325 | let mut opts = default_cfg_options.clone(); | ||
326 | opts.remove_atom("test"); | ||
327 | opts | ||
328 | }; | ||
329 | |||
330 | let env = Env::default(); | 341 | let env = Env::default(); |
331 | let extern_source = ExternSource::default(); | 342 | let extern_source = ExternSource::default(); |
332 | let proc_macro = vec![]; | 343 | let proc_macro = vec![]; |
@@ -337,7 +348,7 @@ impl ProjectWorkspace { | |||
337 | file_id, | 348 | file_id, |
338 | Edition::Edition2018, | 349 | Edition::Edition2018, |
339 | Some(crate_name), | 350 | Some(crate_name), |
340 | cfg_options, | 351 | cfg_options.clone(), |
341 | env, | 352 | env, |
342 | extern_source, | 353 | extern_source, |
343 | proc_macro, | 354 | proc_macro, |
@@ -368,6 +379,10 @@ impl ProjectWorkspace { | |||
368 | 379 | ||
369 | let mut pkg_to_lib_crate = FxHashMap::default(); | 380 | let mut pkg_to_lib_crate = FxHashMap::default(); |
370 | let mut pkg_crates = FxHashMap::default(); | 381 | let mut pkg_crates = FxHashMap::default(); |
382 | |||
383 | // Add test cfg for non-sysroot crates | ||
384 | cfg_options.insert_atom("test".into()); | ||
385 | |||
371 | // Next, create crates for each package, target pair | 386 | // Next, create crates for each package, target pair |
372 | for pkg in cargo.packages() { | 387 | for pkg in cargo.packages() { |
373 | let mut lib_tgt = None; | 388 | let mut lib_tgt = None; |
@@ -376,7 +391,7 @@ impl ProjectWorkspace { | |||
376 | if let Some(file_id) = load(root) { | 391 | if let Some(file_id) = load(root) { |
377 | let edition = cargo[pkg].edition; | 392 | let edition = cargo[pkg].edition; |
378 | let cfg_options = { | 393 | let cfg_options = { |
379 | let mut opts = default_cfg_options.clone(); | 394 | let mut opts = cfg_options.clone(); |
380 | for feature in cargo[pkg].features.iter() { | 395 | for feature in cargo[pkg].features.iter() { |
381 | opts.insert_key_value("feature".into(), feature.into()); | 396 | opts.insert_key_value("feature".into(), feature.into()); |
382 | } | 397 | } |
@@ -533,7 +548,7 @@ impl ProjectWorkspace { | |||
533 | } | 548 | } |
534 | } | 549 | } |
535 | 550 | ||
536 | pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { | 551 | fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions { |
537 | let mut cfg_options = CfgOptions::default(); | 552 | let mut cfg_options = CfgOptions::default(); |
538 | 553 | ||
539 | // Some nightly-only cfgs, which are required for stdlib | 554 | // Some nightly-only cfgs, which are required for stdlib |
@@ -551,7 +566,7 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { | |||
551 | let mut cmd = Command::new(ra_toolchain::rustc()); | 566 | let mut cmd = Command::new(ra_toolchain::rustc()); |
552 | cmd.args(&["--print", "cfg", "-O"]); | 567 | cmd.args(&["--print", "cfg", "-O"]); |
553 | if let Some(target) = target { | 568 | if let Some(target) = target { |
554 | cmd.args(&["--target", target.as_str()]); | 569 | cmd.args(&["--target", target]); |
555 | } | 570 | } |
556 | let output = output(cmd)?; | 571 | let output = output(cmd)?; |
557 | Ok(String::from_utf8(output.stdout)?) | 572 | Ok(String::from_utf8(output.stdout)?) |
@@ -573,6 +588,8 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { | |||
573 | Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), | 588 | Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), |
574 | } | 589 | } |
575 | 590 | ||
591 | cfg_options.insert_atom("debug_assertions".into()); | ||
592 | |||
576 | cfg_options | 593 | cfg_options |
577 | } | 594 | } |
578 | 595 | ||