diff options
Diffstat (limited to 'crates/ra_project_model/src')
-rw-r--r-- | crates/ra_project_model/src/cargo_workspace.rs | 79 | ||||
-rw-r--r-- | crates/ra_project_model/src/json_project.rs | 56 | ||||
-rw-r--r-- | crates/ra_project_model/src/lib.rs | 220 | ||||
-rw-r--r-- | crates/ra_project_model/src/project_json.rs | 129 | ||||
-rw-r--r-- | crates/ra_project_model/src/sysroot.rs | 28 |
5 files changed, 285 insertions, 227 deletions
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index a306ce95f..4182ca156 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
@@ -9,6 +9,7 @@ use std::{ | |||
9 | 9 | ||
10 | use anyhow::{Context, Result}; | 10 | use anyhow::{Context, Result}; |
11 | use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; | 11 | use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; |
12 | use paths::{AbsPath, AbsPathBuf}; | ||
12 | use ra_arena::{Arena, Idx}; | 13 | use ra_arena::{Arena, Idx}; |
13 | use ra_db::Edition; | 14 | use ra_db::Edition; |
14 | use rustc_hash::FxHashMap; | 15 | use rustc_hash::FxHashMap; |
@@ -20,11 +21,14 @@ use rustc_hash::FxHashMap; | |||
20 | /// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates, | 21 | /// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates, |
21 | /// while this knows about `Packages` & `Targets`: purely cargo-related | 22 | /// while this knows about `Packages` & `Targets`: purely cargo-related |
22 | /// concepts. | 23 | /// concepts. |
23 | #[derive(Debug, Clone)] | 24 | /// |
25 | /// We use absolute paths here, `cargo metadata` guarantees to always produce | ||
26 | /// abs paths. | ||
27 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
24 | pub struct CargoWorkspace { | 28 | pub struct CargoWorkspace { |
25 | packages: Arena<PackageData>, | 29 | packages: Arena<PackageData>, |
26 | targets: Arena<TargetData>, | 30 | targets: Arena<TargetData>, |
27 | workspace_root: PathBuf, | 31 | workspace_root: AbsPathBuf, |
28 | } | 32 | } |
29 | 33 | ||
30 | impl ops::Index<Package> for CargoWorkspace { | 34 | impl ops::Index<Package> for CargoWorkspace { |
@@ -41,7 +45,7 @@ impl ops::Index<Target> for CargoWorkspace { | |||
41 | } | 45 | } |
42 | } | 46 | } |
43 | 47 | ||
44 | #[derive(Clone, Debug, PartialEq, Eq)] | 48 | #[derive(Default, Clone, Debug, PartialEq, Eq)] |
45 | pub struct CargoConfig { | 49 | pub struct CargoConfig { |
46 | /// Do not activate the `default` feature. | 50 | /// Do not activate the `default` feature. |
47 | pub no_default_features: bool, | 51 | pub no_default_features: bool, |
@@ -60,48 +64,36 @@ pub struct CargoConfig { | |||
60 | pub target: Option<String>, | 64 | pub target: Option<String>, |
61 | } | 65 | } |
62 | 66 | ||
63 | impl Default for CargoConfig { | ||
64 | fn default() -> Self { | ||
65 | CargoConfig { | ||
66 | no_default_features: false, | ||
67 | all_features: true, | ||
68 | features: Vec::new(), | ||
69 | load_out_dirs_from_check: false, | ||
70 | target: None, | ||
71 | } | ||
72 | } | ||
73 | } | ||
74 | |||
75 | pub type Package = Idx<PackageData>; | 67 | pub type Package = Idx<PackageData>; |
76 | 68 | ||
77 | pub type Target = Idx<TargetData>; | 69 | pub type Target = Idx<TargetData>; |
78 | 70 | ||
79 | #[derive(Debug, Clone)] | 71 | #[derive(Debug, Clone, Eq, PartialEq)] |
80 | pub struct PackageData { | 72 | pub struct PackageData { |
81 | pub version: String, | 73 | pub version: String, |
82 | pub name: String, | 74 | pub name: String, |
83 | pub manifest: PathBuf, | 75 | pub manifest: AbsPathBuf, |
84 | pub targets: Vec<Target>, | 76 | pub targets: Vec<Target>, |
85 | pub is_member: bool, | 77 | pub is_member: bool, |
86 | pub dependencies: Vec<PackageDependency>, | 78 | pub dependencies: Vec<PackageDependency>, |
87 | pub edition: Edition, | 79 | pub edition: Edition, |
88 | pub features: Vec<String>, | 80 | pub features: Vec<String>, |
89 | pub cfgs: Vec<String>, | 81 | pub cfgs: Vec<String>, |
90 | pub out_dir: Option<PathBuf>, | 82 | pub out_dir: Option<AbsPathBuf>, |
91 | pub proc_macro_dylib_path: Option<PathBuf>, | 83 | pub proc_macro_dylib_path: Option<AbsPathBuf>, |
92 | } | 84 | } |
93 | 85 | ||
94 | #[derive(Debug, Clone)] | 86 | #[derive(Debug, Clone, Eq, PartialEq)] |
95 | pub struct PackageDependency { | 87 | pub struct PackageDependency { |
96 | pub pkg: Package, | 88 | pub pkg: Package, |
97 | pub name: String, | 89 | pub name: String, |
98 | } | 90 | } |
99 | 91 | ||
100 | #[derive(Debug, Clone)] | 92 | #[derive(Debug, Clone, Eq, PartialEq)] |
101 | pub struct TargetData { | 93 | pub struct TargetData { |
102 | pub package: Package, | 94 | pub package: Package, |
103 | pub name: String, | 95 | pub name: String, |
104 | pub root: PathBuf, | 96 | pub root: AbsPathBuf, |
105 | pub kind: TargetKind, | 97 | pub kind: TargetKind, |
106 | pub is_proc_macro: bool, | 98 | pub is_proc_macro: bool, |
107 | } | 99 | } |
@@ -135,19 +127,19 @@ impl TargetKind { | |||
135 | } | 127 | } |
136 | 128 | ||
137 | impl PackageData { | 129 | impl PackageData { |
138 | pub fn root(&self) -> &Path { | 130 | pub fn root(&self) -> &AbsPath { |
139 | self.manifest.parent().unwrap() | 131 | self.manifest.parent().unwrap() |
140 | } | 132 | } |
141 | } | 133 | } |
142 | 134 | ||
143 | impl CargoWorkspace { | 135 | impl CargoWorkspace { |
144 | pub fn from_cargo_metadata( | 136 | pub fn from_cargo_metadata( |
145 | cargo_toml: &Path, | 137 | cargo_toml: &AbsPath, |
146 | cargo_features: &CargoConfig, | 138 | cargo_features: &CargoConfig, |
147 | ) -> Result<CargoWorkspace> { | 139 | ) -> Result<CargoWorkspace> { |
148 | let mut meta = MetadataCommand::new(); | 140 | let mut meta = MetadataCommand::new(); |
149 | meta.cargo_path(ra_toolchain::cargo()); | 141 | meta.cargo_path(ra_toolchain::cargo()); |
150 | meta.manifest_path(cargo_toml); | 142 | meta.manifest_path(cargo_toml.to_path_buf()); |
151 | if cargo_features.all_features { | 143 | if cargo_features.all_features { |
152 | meta.features(CargoOpt::AllFeatures); | 144 | meta.features(CargoOpt::AllFeatures); |
153 | } else if cargo_features.no_default_features { | 145 | } else if cargo_features.no_default_features { |
@@ -158,12 +150,12 @@ impl CargoWorkspace { | |||
158 | meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone())); | 150 | meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone())); |
159 | } | 151 | } |
160 | if let Some(parent) = cargo_toml.parent() { | 152 | if let Some(parent) = cargo_toml.parent() { |
161 | meta.current_dir(parent); | 153 | meta.current_dir(parent.to_path_buf()); |
162 | } | 154 | } |
163 | if let Some(target) = cargo_features.target.as_ref() { | 155 | if let Some(target) = cargo_features.target.as_ref() { |
164 | meta.other_options(vec![String::from("--filter-platform"), target.clone()]); | 156 | meta.other_options(vec![String::from("--filter-platform"), target.clone()]); |
165 | } | 157 | } |
166 | let meta = meta.exec().with_context(|| { | 158 | let mut meta = meta.exec().with_context(|| { |
167 | format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) | 159 | format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) |
168 | })?; | 160 | })?; |
169 | 161 | ||
@@ -183,6 +175,7 @@ impl CargoWorkspace { | |||
183 | 175 | ||
184 | let ws_members = &meta.workspace_members; | 176 | let ws_members = &meta.workspace_members; |
185 | 177 | ||
178 | meta.packages.sort_by(|a, b| a.id.cmp(&b.id)); | ||
186 | for meta_pkg in meta.packages { | 179 | for meta_pkg in meta.packages { |
187 | let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } = | 180 | let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } = |
188 | meta_pkg; | 181 | meta_pkg; |
@@ -193,7 +186,7 @@ impl CargoWorkspace { | |||
193 | let pkg = packages.alloc(PackageData { | 186 | let pkg = packages.alloc(PackageData { |
194 | name, | 187 | name, |
195 | version: version.to_string(), | 188 | version: version.to_string(), |
196 | manifest: manifest_path, | 189 | manifest: AbsPathBuf::assert(manifest_path), |
197 | targets: Vec::new(), | 190 | targets: Vec::new(), |
198 | is_member, | 191 | is_member, |
199 | edition, | 192 | edition, |
@@ -210,7 +203,7 @@ impl CargoWorkspace { | |||
210 | let tgt = targets.alloc(TargetData { | 203 | let tgt = targets.alloc(TargetData { |
211 | package: pkg, | 204 | package: pkg, |
212 | name: meta_tgt.name, | 205 | name: meta_tgt.name, |
213 | root: meta_tgt.src_path.clone(), | 206 | root: AbsPathBuf::assert(meta_tgt.src_path.clone()), |
214 | kind: TargetKind::new(meta_tgt.kind.as_slice()), | 207 | kind: TargetKind::new(meta_tgt.kind.as_slice()), |
215 | is_proc_macro, | 208 | is_proc_macro, |
216 | }); | 209 | }); |
@@ -218,7 +211,7 @@ impl CargoWorkspace { | |||
218 | } | 211 | } |
219 | } | 212 | } |
220 | let resolve = meta.resolve.expect("metadata executed with deps"); | 213 | let resolve = meta.resolve.expect("metadata executed with deps"); |
221 | for node in resolve.nodes { | 214 | for mut node in resolve.nodes { |
222 | let source = match pkg_by_id.get(&node.id) { | 215 | let source = match pkg_by_id.get(&node.id) { |
223 | Some(&src) => src, | 216 | Some(&src) => src, |
224 | // FIXME: replace this and a similar branch below with `.unwrap`, once | 217 | // FIXME: replace this and a similar branch below with `.unwrap`, once |
@@ -229,6 +222,7 @@ impl CargoWorkspace { | |||
229 | continue; | 222 | continue; |
230 | } | 223 | } |
231 | }; | 224 | }; |
225 | node.deps.sort_by(|a, b| a.pkg.cmp(&b.pkg)); | ||
232 | for dep_node in node.deps { | 226 | for dep_node in node.deps { |
233 | let pkg = match pkg_by_id.get(&dep_node.pkg) { | 227 | let pkg = match pkg_by_id.get(&dep_node.pkg) { |
234 | Some(&pkg) => pkg, | 228 | Some(&pkg) => pkg, |
@@ -246,21 +240,22 @@ impl CargoWorkspace { | |||
246 | packages[source].features.extend(node.features); | 240 | packages[source].features.extend(node.features); |
247 | } | 241 | } |
248 | 242 | ||
249 | Ok(CargoWorkspace { packages, targets, workspace_root: meta.workspace_root }) | 243 | let workspace_root = AbsPathBuf::assert(meta.workspace_root); |
244 | Ok(CargoWorkspace { packages, targets, workspace_root: workspace_root }) | ||
250 | } | 245 | } |
251 | 246 | ||
252 | pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a { | 247 | pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a { |
253 | self.packages.iter().map(|(id, _pkg)| id) | 248 | self.packages.iter().map(|(id, _pkg)| id) |
254 | } | 249 | } |
255 | 250 | ||
256 | pub fn target_by_root(&self, root: &Path) -> Option<Target> { | 251 | pub fn target_by_root(&self, root: &AbsPath) -> Option<Target> { |
257 | self.packages() | 252 | self.packages() |
258 | .filter_map(|pkg| self[pkg].targets.iter().find(|&&it| self[it].root == root)) | 253 | .filter_map(|pkg| self[pkg].targets.iter().find(|&&it| &self[it].root == root)) |
259 | .next() | 254 | .next() |
260 | .copied() | 255 | .copied() |
261 | } | 256 | } |
262 | 257 | ||
263 | pub fn workspace_root(&self) -> &Path { | 258 | pub fn workspace_root(&self) -> &AbsPath { |
264 | &self.workspace_root | 259 | &self.workspace_root |
265 | } | 260 | } |
266 | 261 | ||
@@ -279,8 +274,8 @@ impl CargoWorkspace { | |||
279 | 274 | ||
280 | #[derive(Debug, Clone, Default)] | 275 | #[derive(Debug, Clone, Default)] |
281 | pub struct ExternResources { | 276 | pub struct ExternResources { |
282 | out_dirs: FxHashMap<PackageId, PathBuf>, | 277 | out_dirs: FxHashMap<PackageId, AbsPathBuf>, |
283 | proc_dylib_paths: FxHashMap<PackageId, PathBuf>, | 278 | proc_dylib_paths: FxHashMap<PackageId, AbsPathBuf>, |
284 | cfgs: FxHashMap<PackageId, Vec<String>>, | 279 | cfgs: FxHashMap<PackageId, Vec<String>>, |
285 | } | 280 | } |
286 | 281 | ||
@@ -308,8 +303,13 @@ pub fn load_extern_resources( | |||
308 | if let Ok(message) = message { | 303 | if let Ok(message) = message { |
309 | match message { | 304 | match message { |
310 | Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => { | 305 | Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => { |
311 | res.out_dirs.insert(package_id.clone(), out_dir); | 306 | // cargo_metadata crate returns default (empty) path for |
312 | res.cfgs.insert(package_id, cfgs); | 307 | // older cargos, which is not absolute, so work around that. |
308 | if out_dir != PathBuf::default() { | ||
309 | let out_dir = AbsPathBuf::assert(out_dir); | ||
310 | res.out_dirs.insert(package_id.clone(), out_dir); | ||
311 | res.cfgs.insert(package_id, cfgs); | ||
312 | } | ||
313 | } | 313 | } |
314 | Message::CompilerArtifact(message) => { | 314 | Message::CompilerArtifact(message) => { |
315 | if message.target.kind.contains(&"proc-macro".to_string()) { | 315 | if message.target.kind.contains(&"proc-macro".to_string()) { |
@@ -317,7 +317,8 @@ pub fn load_extern_resources( | |||
317 | // Skip rmeta file | 317 | // Skip rmeta file |
318 | if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) | 318 | if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) |
319 | { | 319 | { |
320 | res.proc_dylib_paths.insert(package_id, filename.clone()); | 320 | let filename = AbsPathBuf::assert(filename.clone()); |
321 | res.proc_dylib_paths.insert(package_id, filename); | ||
321 | } | 322 | } |
322 | } | 323 | } |
323 | } | 324 | } |
diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs deleted file mode 100644 index b030c8a6a..000000000 --- a/crates/ra_project_model/src/json_project.rs +++ /dev/null | |||
@@ -1,56 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use std::path::PathBuf; | ||
4 | |||
5 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
6 | use serde::Deserialize; | ||
7 | |||
8 | /// A root points to the directory which contains Rust crates. rust-analyzer watches all files in | ||
9 | /// all roots. Roots might be nested. | ||
10 | #[derive(Clone, Debug, Deserialize)] | ||
11 | #[serde(transparent)] | ||
12 | pub struct Root { | ||
13 | pub(crate) path: PathBuf, | ||
14 | } | ||
15 | |||
16 | /// A crate points to the root module of a crate and lists the dependencies of the crate. This is | ||
17 | /// useful in creating the crate graph. | ||
18 | #[derive(Clone, Debug, Deserialize)] | ||
19 | pub struct Crate { | ||
20 | pub(crate) root_module: PathBuf, | ||
21 | pub(crate) edition: Edition, | ||
22 | pub(crate) deps: Vec<Dep>, | ||
23 | pub(crate) atom_cfgs: FxHashSet<String>, | ||
24 | pub(crate) key_value_cfgs: FxHashMap<String, String>, | ||
25 | pub(crate) out_dir: Option<PathBuf>, | ||
26 | pub(crate) proc_macro_dylib_path: Option<PathBuf>, | ||
27 | } | ||
28 | |||
29 | #[derive(Clone, Copy, Debug, Deserialize)] | ||
30 | #[serde(rename = "edition")] | ||
31 | pub enum Edition { | ||
32 | #[serde(rename = "2015")] | ||
33 | Edition2015, | ||
34 | #[serde(rename = "2018")] | ||
35 | Edition2018, | ||
36 | } | ||
37 | |||
38 | /// Identifies a crate by position in the crates array. | ||
39 | #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd)] | ||
40 | #[serde(transparent)] | ||
41 | pub struct CrateId(pub usize); | ||
42 | |||
43 | /// A dependency of a crate, identified by its id in the crates array and name. | ||
44 | #[derive(Clone, Debug, Deserialize)] | ||
45 | pub struct Dep { | ||
46 | #[serde(rename = "crate")] | ||
47 | pub(crate) krate: CrateId, | ||
48 | pub(crate) name: String, | ||
49 | } | ||
50 | |||
51 | /// Roots and crates that compose this Rust project. | ||
52 | #[derive(Clone, Debug, Deserialize)] | ||
53 | pub struct JsonProject { | ||
54 | pub(crate) roots: Vec<Root>, | ||
55 | pub(crate) crates: Vec<Crate>, | ||
56 | } | ||
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index a2e9f65ef..b9c5424bf 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -1,35 +1,35 @@ | |||
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, ExternSource, ExternSourceId, FileId}; | 17 | use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId}; |
17 | use rustc_hash::FxHashMap; | 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; |
26 | 26 | ||
27 | #[derive(Debug, Clone)] | 27 | #[derive(Debug, Clone, Eq, PartialEq)] |
28 | pub enum ProjectWorkspace { | 28 | 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 }, | 32 | Json { project: ProjectJson }, |
33 | } | 33 | } |
34 | 34 | ||
35 | /// `PackageRoot` describes a package root folder. | 35 | /// `PackageRoot` describes a package root folder. |
@@ -38,44 +38,48 @@ 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<AbsPathBuf>, | ||
44 | } | 45 | } |
45 | impl PackageRoot { | 46 | impl PackageRoot { |
46 | pub fn new_member(path: PathBuf) -> PackageRoot { | 47 | pub fn new_member(path: AbsPathBuf) -> PackageRoot { |
47 | Self { path, is_member: true } | 48 | Self { path, is_member: true, out_dir: None } |
48 | } | 49 | } |
49 | pub fn new_non_member(path: PathBuf) -> PackageRoot { | 50 | pub fn new_non_member(path: AbsPathBuf) -> PackageRoot { |
50 | Self { path, is_member: false } | 51 | Self { path, is_member: false, out_dir: None } |
51 | } | 52 | } |
52 | pub fn path(&self) -> &Path { | 53 | pub fn path(&self) -> &AbsPath { |
53 | &self.path | 54 | &self.path |
54 | } | 55 | } |
56 | pub fn out_dir(&self) -> Option<&AbsPath> { | ||
57 | self.out_dir.as_deref() | ||
58 | } | ||
55 | pub fn is_member(&self) -> bool { | 59 | pub fn is_member(&self) -> bool { |
56 | self.is_member | 60 | self.is_member |
57 | } | 61 | } |
58 | } | 62 | } |
59 | 63 | ||
60 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 64 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] |
61 | pub enum ProjectRoot { | 65 | pub enum ProjectManifest { |
62 | ProjectJson(PathBuf), | 66 | ProjectJson(AbsPathBuf), |
63 | CargoToml(PathBuf), | 67 | CargoToml(AbsPathBuf), |
64 | } | 68 | } |
65 | 69 | ||
66 | impl ProjectRoot { | 70 | impl ProjectManifest { |
67 | pub fn from_manifest_file(path: PathBuf) -> Result<ProjectRoot> { | 71 | pub fn from_manifest_file(path: AbsPathBuf) -> Result<ProjectManifest> { |
68 | if path.ends_with("rust-project.json") { | 72 | if path.ends_with("rust-project.json") { |
69 | return Ok(ProjectRoot::ProjectJson(path)); | 73 | return Ok(ProjectManifest::ProjectJson(path)); |
70 | } | 74 | } |
71 | if path.ends_with("Cargo.toml") { | 75 | if path.ends_with("Cargo.toml") { |
72 | return Ok(ProjectRoot::CargoToml(path)); | 76 | return Ok(ProjectManifest::CargoToml(path)); |
73 | } | 77 | } |
74 | 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()) |
75 | } | 79 | } |
76 | 80 | ||
77 | pub fn discover_single(path: &Path) -> Result<ProjectRoot> { | 81 | pub fn discover_single(path: &AbsPath) -> Result<ProjectManifest> { |
78 | let mut candidates = ProjectRoot::discover(path)?; | 82 | let mut candidates = ProjectManifest::discover(path)?; |
79 | let res = match candidates.pop() { | 83 | let res = match candidates.pop() { |
80 | None => bail!("no projects"), | 84 | None => bail!("no projects"), |
81 | Some(it) => it, | 85 | Some(it) => it, |
@@ -87,23 +91,23 @@ impl ProjectRoot { | |||
87 | Ok(res) | 91 | Ok(res) |
88 | } | 92 | } |
89 | 93 | ||
90 | pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> { | 94 | pub fn discover(path: &AbsPath) -> io::Result<Vec<ProjectManifest>> { |
91 | 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") { |
92 | return Ok(vec![ProjectRoot::ProjectJson(project_json)]); | 96 | return Ok(vec![ProjectManifest::ProjectJson(project_json)]); |
93 | } | 97 | } |
94 | return find_cargo_toml(path) | 98 | return find_cargo_toml(path) |
95 | .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); | 99 | .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect()); |
96 | 100 | ||
97 | fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { | 101 | fn find_cargo_toml(path: &AbsPath) -> io::Result<Vec<AbsPathBuf>> { |
98 | match find_in_parent_dirs(path, "Cargo.toml") { | 102 | match find_in_parent_dirs(path, "Cargo.toml") { |
99 | Some(it) => Ok(vec![it]), | 103 | Some(it) => Ok(vec![it]), |
100 | None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)), | 104 | None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)), |
101 | } | 105 | } |
102 | } | 106 | } |
103 | 107 | ||
104 | 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> { |
105 | if path.ends_with(target_file_name) { | 109 | if path.ends_with(target_file_name) { |
106 | return Some(path.to_owned()); | 110 | return Some(path.to_path_buf()); |
107 | } | 111 | } |
108 | 112 | ||
109 | let mut curr = Some(path); | 113 | let mut curr = Some(path); |
@@ -119,37 +123,50 @@ impl ProjectRoot { | |||
119 | None | 123 | None |
120 | } | 124 | } |
121 | 125 | ||
122 | fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<PathBuf> { | 126 | fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<AbsPathBuf> { |
123 | // 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 |
124 | entities | 128 | entities |
125 | .filter_map(Result::ok) | 129 | .filter_map(Result::ok) |
126 | .map(|it| it.path().join("Cargo.toml")) | 130 | .map(|it| it.path().join("Cargo.toml")) |
127 | .filter(|it| it.exists()) | 131 | .filter(|it| it.exists()) |
132 | .map(AbsPathBuf::assert) | ||
128 | .collect() | 133 | .collect() |
129 | } | 134 | } |
130 | } | 135 | } |
136 | |||
137 | pub fn discover_all(paths: &[impl AsRef<AbsPath>]) -> Vec<ProjectManifest> { | ||
138 | let mut res = paths | ||
139 | .iter() | ||
140 | .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok()) | ||
141 | .flatten() | ||
142 | .collect::<FxHashSet<_>>() | ||
143 | .into_iter() | ||
144 | .collect::<Vec<_>>(); | ||
145 | res.sort(); | ||
146 | res | ||
147 | } | ||
131 | } | 148 | } |
132 | 149 | ||
133 | impl ProjectWorkspace { | 150 | impl ProjectWorkspace { |
134 | pub fn load( | 151 | pub fn load( |
135 | root: ProjectRoot, | 152 | manifest: ProjectManifest, |
136 | cargo_features: &CargoConfig, | 153 | cargo_config: &CargoConfig, |
137 | with_sysroot: bool, | 154 | with_sysroot: bool, |
138 | ) -> Result<ProjectWorkspace> { | 155 | ) -> Result<ProjectWorkspace> { |
139 | let res = match root { | 156 | let res = match manifest { |
140 | ProjectRoot::ProjectJson(project_json) => { | 157 | ProjectManifest::ProjectJson(project_json) => { |
141 | let file = File::open(&project_json).with_context(|| { | 158 | let file = fs::read_to_string(&project_json).with_context(|| { |
142 | format!("Failed to open json file {}", project_json.display()) | 159 | format!("Failed to read json file {}", project_json.display()) |
143 | })?; | 160 | })?; |
144 | let reader = BufReader::new(file); | 161 | let data = serde_json::from_str(&file).with_context(|| { |
145 | ProjectWorkspace::Json { | 162 | format!("Failed to deserialize json file {}", project_json.display()) |
146 | project: from_reader(reader).with_context(|| { | 163 | })?; |
147 | format!("Failed to deserialize json file {}", project_json.display()) | 164 | let project_location = project_json.parent().unwrap().to_path_buf(); |
148 | })?, | 165 | let project = ProjectJson::new(&project_location, data); |
149 | } | 166 | ProjectWorkspace::Json { project } |
150 | } | 167 | } |
151 | ProjectRoot::CargoToml(cargo_toml) => { | 168 | ProjectManifest::CargoToml(cargo_toml) => { |
152 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) | 169 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_config) |
153 | .with_context(|| { | 170 | .with_context(|| { |
154 | format!( | 171 | format!( |
155 | "Failed to read Cargo metadata from Cargo.toml file {}", | 172 | "Failed to read Cargo metadata from Cargo.toml file {}", |
@@ -186,6 +203,7 @@ impl ProjectWorkspace { | |||
186 | .map(|pkg| PackageRoot { | 203 | .map(|pkg| PackageRoot { |
187 | path: cargo[pkg].root().to_path_buf(), | 204 | path: cargo[pkg].root().to_path_buf(), |
188 | is_member: cargo[pkg].is_member, | 205 | is_member: cargo[pkg].is_member, |
206 | out_dir: cargo[pkg].out_dir.clone(), | ||
189 | }) | 207 | }) |
190 | .chain(sysroot.crates().map(|krate| { | 208 | .chain(sysroot.crates().map(|krate| { |
191 | PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf()) | 209 | PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf()) |
@@ -194,18 +212,7 @@ impl ProjectWorkspace { | |||
194 | } | 212 | } |
195 | } | 213 | } |
196 | 214 | ||
197 | pub fn out_dirs(&self) -> Vec<PathBuf> { | 215 | pub fn proc_macro_dylib_paths(&self) -> Vec<AbsPathBuf> { |
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> { | ||
209 | match self { | 216 | match self { |
210 | ProjectWorkspace::Json { project } => project | 217 | ProjectWorkspace::Json { project } => project |
211 | .crates | 218 | .crates |
@@ -223,7 +230,7 @@ impl ProjectWorkspace { | |||
223 | 230 | ||
224 | pub fn n_packages(&self) -> usize { | 231 | pub fn n_packages(&self) -> usize { |
225 | match self { | 232 | match self { |
226 | ProjectWorkspace::Json { project } => project.crates.len(), | 233 | ProjectWorkspace::Json { project, .. } => project.crates.len(), |
227 | ProjectWorkspace::Cargo { cargo, sysroot } => { | 234 | ProjectWorkspace::Cargo { cargo, sysroot } => { |
228 | cargo.packages().len() + sysroot.crates().len() | 235 | cargo.packages().len() + sysroot.crates().len() |
229 | } | 236 | } |
@@ -232,61 +239,51 @@ impl ProjectWorkspace { | |||
232 | 239 | ||
233 | pub fn to_crate_graph( | 240 | pub fn to_crate_graph( |
234 | &self, | 241 | &self, |
235 | default_cfg_options: &CfgOptions, | 242 | target: Option<&str>, |
236 | extern_source_roots: &FxHashMap<PathBuf, ExternSourceId>, | ||
237 | proc_macro_client: &ProcMacroClient, | 243 | proc_macro_client: &ProcMacroClient, |
238 | load: &mut dyn FnMut(&Path) -> Option<FileId>, | 244 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, |
239 | ) -> CrateGraph { | 245 | ) -> CrateGraph { |
240 | let mut crate_graph = CrateGraph::default(); | 246 | let mut crate_graph = CrateGraph::default(); |
241 | match self { | 247 | match self { |
242 | ProjectWorkspace::Json { project } => { | 248 | ProjectWorkspace::Json { project } => { |
249 | let mut target_cfg_map = FxHashMap::<Option<&str>, CfgOptions>::default(); | ||
243 | let crates: FxHashMap<_, _> = project | 250 | let crates: FxHashMap<_, _> = project |
244 | .crates | 251 | .crates |
245 | .iter() | 252 | .iter() |
246 | .enumerate() | 253 | .enumerate() |
247 | .filter_map(|(seq_index, krate)| { | 254 | .filter_map(|(seq_index, krate)| { |
248 | let file_id = load(&krate.root_module)?; | 255 | let file_path = &krate.root_module; |
249 | let edition = match krate.edition { | 256 | let file_id = load(&file_path)?; |
250 | json_project::Edition::Edition2015 => Edition::Edition2015, | ||
251 | json_project::Edition::Edition2018 => Edition::Edition2018, | ||
252 | }; | ||
253 | let cfg_options = { | ||
254 | let mut opts = default_cfg_options.clone(); | ||
255 | for name in &krate.atom_cfgs { | ||
256 | opts.insert_atom(name.into()); | ||
257 | } | ||
258 | for (key, value) in &krate.key_value_cfgs { | ||
259 | opts.insert_key_value(key.into(), value.into()); | ||
260 | } | ||
261 | opts | ||
262 | }; | ||
263 | 257 | ||
264 | let mut env = Env::default(); | 258 | let mut env = Env::default(); |
265 | let mut extern_source = ExternSource::default(); | ||
266 | if let Some(out_dir) = &krate.out_dir { | 259 | if let Some(out_dir) = &krate.out_dir { |
267 | // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() | 260 | // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() |
268 | if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { | 261 | if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { |
269 | env.set("OUT_DIR", out_dir); | 262 | env.set("OUT_DIR", out_dir); |
270 | } | 263 | } |
271 | if let Some(&extern_source_id) = extern_source_roots.get(out_dir) { | ||
272 | extern_source.set_extern_path(&out_dir, extern_source_id); | ||
273 | } | ||
274 | } | 264 | } |
275 | let proc_macro = krate | 265 | let proc_macro = krate |
276 | .proc_macro_dylib_path | 266 | .proc_macro_dylib_path |
277 | .clone() | 267 | .clone() |
278 | .map(|it| proc_macro_client.by_dylib_path(&it)); | 268 | .map(|it| proc_macro_client.by_dylib_path(&it)); |
269 | |||
270 | let target = krate.target.as_deref().or(target); | ||
271 | let target_cfgs = target_cfg_map | ||
272 | .entry(target.clone()) | ||
273 | .or_insert_with(|| get_rustc_cfg_options(target.as_deref())); | ||
274 | let mut cfg_options = krate.cfg.clone(); | ||
275 | cfg_options.append(target_cfgs); | ||
276 | |||
279 | // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env | 277 | // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env |
280 | Some(( | 278 | Some(( |
281 | json_project::CrateId(seq_index), | 279 | CrateId(seq_index as u32), |
282 | crate_graph.add_crate_root( | 280 | crate_graph.add_crate_root( |
283 | file_id, | 281 | file_id, |
284 | edition, | 282 | krate.edition, |
285 | // FIXME json definitions can store the crate name | 283 | // FIXME json definitions can store the crate name |
286 | None, | 284 | None, |
287 | cfg_options, | 285 | cfg_options, |
288 | env, | 286 | env, |
289 | extern_source, | ||
290 | proc_macro.unwrap_or_default(), | 287 | proc_macro.unwrap_or_default(), |
291 | ), | 288 | ), |
292 | )) | 289 | )) |
@@ -295,15 +292,12 @@ impl ProjectWorkspace { | |||
295 | 292 | ||
296 | for (id, krate) in project.crates.iter().enumerate() { | 293 | for (id, krate) in project.crates.iter().enumerate() { |
297 | for dep in &krate.deps { | 294 | for dep in &krate.deps { |
298 | let from_crate_id = json_project::CrateId(id); | 295 | let from_crate_id = CrateId(id as u32); |
299 | let to_crate_id = dep.krate; | 296 | let to_crate_id = dep.crate_id; |
300 | if let (Some(&from), Some(&to)) = | 297 | if let (Some(&from), Some(&to)) = |
301 | (crates.get(&from_crate_id), crates.get(&to_crate_id)) | 298 | (crates.get(&from_crate_id), crates.get(&to_crate_id)) |
302 | { | 299 | { |
303 | if crate_graph | 300 | if crate_graph.add_dep(from, dep.name.clone(), to).is_err() { |
304 | .add_dep(from, CrateName::new(&dep.name).unwrap(), to) | ||
305 | .is_err() | ||
306 | { | ||
307 | log::error!( | 301 | log::error!( |
308 | "cyclic dependency {:?} -> {:?}", | 302 | "cyclic dependency {:?} -> {:?}", |
309 | from_crate_id, | 303 | from_crate_id, |
@@ -315,31 +309,22 @@ impl ProjectWorkspace { | |||
315 | } | 309 | } |
316 | } | 310 | } |
317 | ProjectWorkspace::Cargo { cargo, sysroot } => { | 311 | ProjectWorkspace::Cargo { cargo, sysroot } => { |
312 | let mut cfg_options = get_rustc_cfg_options(target); | ||
313 | |||
318 | let sysroot_crates: FxHashMap<_, _> = sysroot | 314 | let sysroot_crates: FxHashMap<_, _> = sysroot |
319 | .crates() | 315 | .crates() |
320 | .filter_map(|krate| { | 316 | .filter_map(|krate| { |
321 | let file_id = load(&sysroot[krate].root)?; | 317 | let file_id = load(&sysroot[krate].root)?; |
322 | 318 | ||
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(); | 319 | let env = Env::default(); |
331 | let extern_source = ExternSource::default(); | ||
332 | let proc_macro = vec![]; | 320 | let proc_macro = vec![]; |
333 | let crate_name = CrateName::new(&sysroot[krate].name) | 321 | let name = sysroot[krate].name.clone(); |
334 | .expect("Sysroot crate names should not contain dashes"); | ||
335 | |||
336 | let crate_id = crate_graph.add_crate_root( | 322 | let crate_id = crate_graph.add_crate_root( |
337 | file_id, | 323 | file_id, |
338 | Edition::Edition2018, | 324 | Edition::Edition2018, |
339 | Some(crate_name), | 325 | Some(name), |
340 | cfg_options, | 326 | cfg_options.clone(), |
341 | env, | 327 | env, |
342 | extern_source, | ||
343 | proc_macro, | 328 | proc_macro, |
344 | ); | 329 | ); |
345 | Some((krate, crate_id)) | 330 | Some((krate, crate_id)) |
@@ -368,6 +353,10 @@ impl ProjectWorkspace { | |||
368 | 353 | ||
369 | let mut pkg_to_lib_crate = FxHashMap::default(); | 354 | let mut pkg_to_lib_crate = FxHashMap::default(); |
370 | let mut pkg_crates = FxHashMap::default(); | 355 | let mut pkg_crates = FxHashMap::default(); |
356 | |||
357 | // Add test cfg for non-sysroot crates | ||
358 | cfg_options.insert_atom("test".into()); | ||
359 | |||
371 | // Next, create crates for each package, target pair | 360 | // Next, create crates for each package, target pair |
372 | for pkg in cargo.packages() { | 361 | for pkg in cargo.packages() { |
373 | let mut lib_tgt = None; | 362 | let mut lib_tgt = None; |
@@ -376,7 +365,7 @@ impl ProjectWorkspace { | |||
376 | if let Some(file_id) = load(root) { | 365 | if let Some(file_id) = load(root) { |
377 | let edition = cargo[pkg].edition; | 366 | let edition = cargo[pkg].edition; |
378 | let cfg_options = { | 367 | let cfg_options = { |
379 | let mut opts = default_cfg_options.clone(); | 368 | let mut opts = cfg_options.clone(); |
380 | for feature in cargo[pkg].features.iter() { | 369 | for feature in cargo[pkg].features.iter() { |
381 | opts.insert_key_value("feature".into(), feature.into()); | 370 | opts.insert_key_value("feature".into(), feature.into()); |
382 | } | 371 | } |
@@ -392,15 +381,11 @@ impl ProjectWorkspace { | |||
392 | opts | 381 | opts |
393 | }; | 382 | }; |
394 | let mut env = Env::default(); | 383 | let mut env = Env::default(); |
395 | let mut extern_source = ExternSource::default(); | ||
396 | if let Some(out_dir) = &cargo[pkg].out_dir { | 384 | if let Some(out_dir) = &cargo[pkg].out_dir { |
397 | // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() | 385 | // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() |
398 | if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { | 386 | if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { |
399 | env.set("OUT_DIR", out_dir); | 387 | env.set("OUT_DIR", out_dir); |
400 | } | 388 | } |
401 | if let Some(&extern_source_id) = extern_source_roots.get(out_dir) { | ||
402 | extern_source.set_extern_path(&out_dir, extern_source_id); | ||
403 | } | ||
404 | } | 389 | } |
405 | let proc_macro = cargo[pkg] | 390 | let proc_macro = cargo[pkg] |
406 | .proc_macro_dylib_path | 391 | .proc_macro_dylib_path |
@@ -411,10 +396,9 @@ impl ProjectWorkspace { | |||
411 | let crate_id = crate_graph.add_crate_root( | 396 | let crate_id = crate_graph.add_crate_root( |
412 | file_id, | 397 | file_id, |
413 | edition, | 398 | edition, |
414 | Some(CrateName::normalize_dashes(&cargo[pkg].name)), | 399 | Some(cargo[pkg].name.clone()), |
415 | cfg_options, | 400 | cfg_options, |
416 | env, | 401 | env, |
417 | extern_source, | ||
418 | proc_macro.clone(), | 402 | proc_macro.clone(), |
419 | ); | 403 | ); |
420 | if cargo[tgt].kind == TargetKind::Lib { | 404 | if cargo[tgt].kind == TargetKind::Lib { |
@@ -520,20 +504,20 @@ impl ProjectWorkspace { | |||
520 | crate_graph | 504 | crate_graph |
521 | } | 505 | } |
522 | 506 | ||
523 | pub fn workspace_root_for(&self, path: &Path) -> Option<&Path> { | 507 | pub fn workspace_root_for(&self, path: &Path) -> Option<&AbsPath> { |
524 | match self { | 508 | match self { |
525 | ProjectWorkspace::Cargo { cargo, .. } => { | 509 | ProjectWorkspace::Cargo { cargo, .. } => { |
526 | Some(cargo.workspace_root()).filter(|root| path.starts_with(root)) | 510 | Some(cargo.workspace_root()).filter(|root| path.starts_with(root)) |
527 | } | 511 | } |
528 | ProjectWorkspace::Json { project: JsonProject { roots, .. } } => roots | 512 | ProjectWorkspace::Json { project: ProjectJson { roots, .. }, .. } => roots |
529 | .iter() | 513 | .iter() |
530 | .find(|root| path.starts_with(&root.path)) | 514 | .find(|root| path.starts_with(&root.path)) |
531 | .map(|root| root.path.as_ref()), | 515 | .map(|root| root.path.as_path()), |
532 | } | 516 | } |
533 | } | 517 | } |
534 | } | 518 | } |
535 | 519 | ||
536 | pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { | 520 | fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions { |
537 | let mut cfg_options = CfgOptions::default(); | 521 | let mut cfg_options = CfgOptions::default(); |
538 | 522 | ||
539 | // Some nightly-only cfgs, which are required for stdlib | 523 | // Some nightly-only cfgs, which are required for stdlib |
@@ -551,7 +535,7 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { | |||
551 | let mut cmd = Command::new(ra_toolchain::rustc()); | 535 | let mut cmd = Command::new(ra_toolchain::rustc()); |
552 | cmd.args(&["--print", "cfg", "-O"]); | 536 | cmd.args(&["--print", "cfg", "-O"]); |
553 | if let Some(target) = target { | 537 | if let Some(target) = target { |
554 | cmd.args(&["--target", target.as_str()]); | 538 | cmd.args(&["--target", target]); |
555 | } | 539 | } |
556 | let output = output(cmd)?; | 540 | let output = output(cmd)?; |
557 | Ok(String::from_utf8(output.stdout)?) | 541 | Ok(String::from_utf8(output.stdout)?) |
@@ -573,6 +557,8 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { | |||
573 | Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), | 557 | Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), |
574 | } | 558 | } |
575 | 559 | ||
560 | cfg_options.insert_atom("debug_assertions".into()); | ||
561 | |||
576 | cfg_options | 562 | cfg_options |
577 | } | 563 | } |
578 | 564 | ||
diff --git a/crates/ra_project_model/src/project_json.rs b/crates/ra_project_model/src/project_json.rs new file mode 100644 index 000000000..b96227949 --- /dev/null +++ b/crates/ra_project_model/src/project_json.rs | |||
@@ -0,0 +1,129 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use std::path::PathBuf; | ||
4 | |||
5 | use paths::{AbsPath, AbsPathBuf}; | ||
6 | use ra_cfg::CfgOptions; | ||
7 | use ra_db::{CrateId, CrateName, Dependency, Edition}; | ||
8 | use rustc_hash::FxHashSet; | ||
9 | use serde::{de, Deserialize}; | ||
10 | use stdx::split_delim; | ||
11 | |||
12 | /// Roots and crates that compose this Rust project. | ||
13 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
14 | pub struct ProjectJson { | ||
15 | pub(crate) roots: Vec<Root>, | ||
16 | pub(crate) crates: Vec<Crate>, | ||
17 | } | ||
18 | |||
19 | /// A root points to the directory which contains Rust crates. rust-analyzer watches all files in | ||
20 | /// all roots. Roots might be nested. | ||
21 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
22 | pub struct Root { | ||
23 | pub(crate) path: AbsPathBuf, | ||
24 | } | ||
25 | |||
26 | /// A crate points to the root module of a crate and lists the dependencies of the crate. This is | ||
27 | /// useful in creating the crate graph. | ||
28 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
29 | pub struct Crate { | ||
30 | pub(crate) root_module: AbsPathBuf, | ||
31 | pub(crate) edition: Edition, | ||
32 | pub(crate) deps: Vec<Dependency>, | ||
33 | pub(crate) cfg: CfgOptions, | ||
34 | pub(crate) target: Option<String>, | ||
35 | pub(crate) out_dir: Option<AbsPathBuf>, | ||
36 | pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>, | ||
37 | } | ||
38 | |||
39 | impl ProjectJson { | ||
40 | pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { | ||
41 | ProjectJson { | ||
42 | roots: data.roots.into_iter().map(|path| Root { path: base.join(path) }).collect(), | ||
43 | crates: data | ||
44 | .crates | ||
45 | .into_iter() | ||
46 | .map(|crate_data| Crate { | ||
47 | root_module: base.join(crate_data.root_module), | ||
48 | edition: crate_data.edition.into(), | ||
49 | deps: crate_data | ||
50 | .deps | ||
51 | .into_iter() | ||
52 | .map(|dep_data| Dependency { | ||
53 | crate_id: CrateId(dep_data.krate as u32), | ||
54 | name: dep_data.name, | ||
55 | }) | ||
56 | .collect::<Vec<_>>(), | ||
57 | cfg: { | ||
58 | let mut cfg = CfgOptions::default(); | ||
59 | for entry in &crate_data.cfg { | ||
60 | match split_delim(entry, '=') { | ||
61 | Some((key, value)) => { | ||
62 | cfg.insert_key_value(key.into(), value.into()); | ||
63 | } | ||
64 | None => cfg.insert_atom(entry.into()), | ||
65 | } | ||
66 | } | ||
67 | cfg | ||
68 | }, | ||
69 | target: crate_data.target, | ||
70 | out_dir: crate_data.out_dir.map(|it| base.join(it)), | ||
71 | proc_macro_dylib_path: crate_data.proc_macro_dylib_path.map(|it| base.join(it)), | ||
72 | }) | ||
73 | .collect::<Vec<_>>(), | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | |||
78 | #[derive(Deserialize)] | ||
79 | pub struct ProjectJsonData { | ||
80 | roots: Vec<PathBuf>, | ||
81 | crates: Vec<CrateData>, | ||
82 | } | ||
83 | |||
84 | #[derive(Deserialize)] | ||
85 | struct CrateData { | ||
86 | root_module: PathBuf, | ||
87 | edition: EditionData, | ||
88 | deps: Vec<DepData>, | ||
89 | #[serde(default)] | ||
90 | cfg: FxHashSet<String>, | ||
91 | target: Option<String>, | ||
92 | out_dir: Option<PathBuf>, | ||
93 | proc_macro_dylib_path: Option<PathBuf>, | ||
94 | } | ||
95 | |||
96 | #[derive(Deserialize)] | ||
97 | #[serde(rename = "edition")] | ||
98 | enum EditionData { | ||
99 | #[serde(rename = "2015")] | ||
100 | Edition2015, | ||
101 | #[serde(rename = "2018")] | ||
102 | Edition2018, | ||
103 | } | ||
104 | |||
105 | impl From<EditionData> for Edition { | ||
106 | fn from(data: EditionData) -> Self { | ||
107 | match data { | ||
108 | EditionData::Edition2015 => Edition::Edition2015, | ||
109 | EditionData::Edition2018 => Edition::Edition2018, | ||
110 | } | ||
111 | } | ||
112 | } | ||
113 | |||
114 | #[derive(Deserialize)] | ||
115 | struct DepData { | ||
116 | /// Identifies a crate by position in the crates array. | ||
117 | #[serde(rename = "crate")] | ||
118 | krate: usize, | ||
119 | #[serde(deserialize_with = "deserialize_crate_name")] | ||
120 | name: CrateName, | ||
121 | } | ||
122 | |||
123 | fn deserialize_crate_name<'de, D>(de: D) -> Result<CrateName, D::Error> | ||
124 | where | ||
125 | D: de::Deserializer<'de>, | ||
126 | { | ||
127 | let name = String::deserialize(de)?; | ||
128 | CrateName::new(&name).map_err(|err| de::Error::custom(format!("invalid crate name: {:?}", err))) | ||
129 | } | ||
diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs index a8a196e64..68d134da4 100644 --- a/crates/ra_project_model/src/sysroot.rs +++ b/crates/ra_project_model/src/sysroot.rs | |||
@@ -1,27 +1,24 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use std::{ | 3 | use std::{convert::TryFrom, env, ops, path::Path, process::Command}; |
4 | env, ops, | ||
5 | path::{Path, PathBuf}, | ||
6 | process::Command, | ||
7 | }; | ||
8 | 4 | ||
9 | use anyhow::{bail, Result}; | 5 | use anyhow::{bail, format_err, Result}; |
6 | use paths::{AbsPath, AbsPathBuf}; | ||
10 | use ra_arena::{Arena, Idx}; | 7 | use ra_arena::{Arena, Idx}; |
11 | 8 | ||
12 | use crate::output; | 9 | use crate::output; |
13 | 10 | ||
14 | #[derive(Default, Debug, Clone)] | 11 | #[derive(Default, Debug, Clone, Eq, PartialEq)] |
15 | pub struct Sysroot { | 12 | pub struct Sysroot { |
16 | crates: Arena<SysrootCrateData>, | 13 | crates: Arena<SysrootCrateData>, |
17 | } | 14 | } |
18 | 15 | ||
19 | pub type SysrootCrate = Idx<SysrootCrateData>; | 16 | pub type SysrootCrate = Idx<SysrootCrateData>; |
20 | 17 | ||
21 | #[derive(Debug, Clone)] | 18 | #[derive(Debug, Clone, Eq, PartialEq)] |
22 | pub struct SysrootCrateData { | 19 | pub struct SysrootCrateData { |
23 | pub name: String, | 20 | pub name: String, |
24 | pub root: PathBuf, | 21 | pub root: AbsPathBuf, |
25 | pub deps: Vec<SysrootCrate>, | 22 | pub deps: Vec<SysrootCrate>, |
26 | } | 23 | } |
27 | 24 | ||
@@ -53,7 +50,7 @@ impl Sysroot { | |||
53 | self.crates.iter().map(|(id, _data)| id) | 50 | self.crates.iter().map(|(id, _data)| id) |
54 | } | 51 | } |
55 | 52 | ||
56 | pub fn discover(cargo_toml: &Path) -> Result<Sysroot> { | 53 | pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> { |
57 | let src = get_or_install_rust_src(cargo_toml)?; | 54 | let src = get_or_install_rust_src(cargo_toml)?; |
58 | let mut sysroot = Sysroot { crates: Arena::default() }; | 55 | let mut sysroot = Sysroot { crates: Arena::default() }; |
59 | for name in SYSROOT_CRATES.trim().lines() { | 56 | for name in SYSROOT_CRATES.trim().lines() { |
@@ -86,16 +83,18 @@ impl Sysroot { | |||
86 | } | 83 | } |
87 | } | 84 | } |
88 | 85 | ||
89 | fn get_or_install_rust_src(cargo_toml: &Path) -> Result<PathBuf> { | 86 | fn get_or_install_rust_src(cargo_toml: &AbsPath) -> Result<AbsPathBuf> { |
90 | if let Ok(path) = env::var("RUST_SRC_PATH") { | 87 | if let Ok(path) = env::var("RUST_SRC_PATH") { |
91 | return Ok(path.into()); | 88 | let path = AbsPathBuf::try_from(path.as_str()) |
89 | .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?; | ||
90 | return Ok(path); | ||
92 | } | 91 | } |
93 | let current_dir = cargo_toml.parent().unwrap(); | 92 | let current_dir = cargo_toml.parent().unwrap(); |
94 | let mut rustc = Command::new(ra_toolchain::rustc()); | 93 | let mut rustc = Command::new(ra_toolchain::rustc()); |
95 | rustc.current_dir(current_dir).args(&["--print", "sysroot"]); | 94 | rustc.current_dir(current_dir).args(&["--print", "sysroot"]); |
96 | let rustc_output = output(rustc)?; | 95 | let rustc_output = output(rustc)?; |
97 | let stdout = String::from_utf8(rustc_output.stdout)?; | 96 | let stdout = String::from_utf8(rustc_output.stdout)?; |
98 | let sysroot_path = Path::new(stdout.trim()); | 97 | let sysroot_path = AbsPath::assert(Path::new(stdout.trim())); |
99 | let src_path = sysroot_path.join("lib/rustlib/src/rust/src"); | 98 | let src_path = sysroot_path.join("lib/rustlib/src/rust/src"); |
100 | 99 | ||
101 | if !src_path.exists() { | 100 | if !src_path.exists() { |
@@ -116,7 +115,7 @@ fn get_or_install_rust_src(cargo_toml: &Path) -> Result<PathBuf> { | |||
116 | } | 115 | } |
117 | 116 | ||
118 | impl SysrootCrateData { | 117 | impl SysrootCrateData { |
119 | pub fn root_dir(&self) -> &Path { | 118 | pub fn root_dir(&self) -> &AbsPath { |
120 | self.root.parent().unwrap() | 119 | self.root.parent().unwrap() |
121 | } | 120 | } |
122 | } | 121 | } |
@@ -127,7 +126,6 @@ core | |||
127 | alloc | 126 | alloc |
128 | collections | 127 | collections |
129 | libc | 128 | libc |
130 | panic_unwind | ||
131 | proc_macro | 129 | proc_macro |
132 | rustc_unicode | 130 | rustc_unicode |
133 | std_unicode | 131 | std_unicode |