diff options
author | Zac Pullar-Strecker <[email protected]> | 2020-07-31 03:12:44 +0100 |
---|---|---|
committer | Zac Pullar-Strecker <[email protected]> | 2020-07-31 03:12:44 +0100 |
commit | f05d7b41a719d848844b054a16477b29d0f063c6 (patch) | |
tree | 0a8a0946e8aef2ce64d4c13d0035ba41cce2daf3 /crates/ra_project_model | |
parent | 73ff610e41959e3e7c78a2b4b25b086883132956 (diff) | |
parent | 6b7cb8b5ab539fc4333ce34bc29bf77c976f232a (diff) |
Merge remote-tracking branch 'upstream/master' into 503-hover-doc-links
Hasn't fixed tests yet.
Diffstat (limited to 'crates/ra_project_model')
-rw-r--r-- | crates/ra_project_model/Cargo.toml | 3 | ||||
-rw-r--r-- | crates/ra_project_model/src/cargo_workspace.rs | 98 | ||||
-rw-r--r-- | crates/ra_project_model/src/cfg_flag.rs | 51 | ||||
-rw-r--r-- | crates/ra_project_model/src/lib.rs | 182 | ||||
-rw-r--r-- | crates/ra_project_model/src/project_json.rs | 118 | ||||
-rw-r--r-- | crates/ra_project_model/src/sysroot.rs | 102 |
6 files changed, 317 insertions, 237 deletions
diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml index b1b44dcf7..99adea8e4 100644 --- a/crates/ra_project_model/Cargo.toml +++ b/crates/ra_project_model/Cargo.toml | |||
@@ -3,6 +3,7 @@ edition = "2018" | |||
3 | name = "ra_project_model" | 3 | name = "ra_project_model" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
@@ -11,7 +12,7 @@ doctest = false | |||
11 | log = "0.4.8" | 12 | log = "0.4.8" |
12 | rustc-hash = "1.1.0" | 13 | rustc-hash = "1.1.0" |
13 | 14 | ||
14 | cargo_metadata = "0.10.0" | 15 | cargo_metadata = "0.11.1" |
15 | 16 | ||
16 | ra_arena = { path = "../ra_arena" } | 17 | ra_arena = { path = "../ra_arena" } |
17 | ra_cfg = { path = "../ra_cfg" } | 18 | ra_cfg = { path = "../ra_cfg" } |
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 3b124020d..10513542e 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
@@ -1,6 +1,11 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use std::{ffi::OsStr, ops, path::Path, process::Command}; | 3 | use std::{ |
4 | ffi::OsStr, | ||
5 | ops, | ||
6 | path::{Path, PathBuf}, | ||
7 | process::Command, | ||
8 | }; | ||
4 | 9 | ||
5 | use anyhow::{Context, Result}; | 10 | use anyhow::{Context, Result}; |
6 | use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; | 11 | use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; |
@@ -9,6 +14,8 @@ use ra_arena::{Arena, Idx}; | |||
9 | use ra_db::Edition; | 14 | use ra_db::Edition; |
10 | use rustc_hash::FxHashMap; | 15 | use rustc_hash::FxHashMap; |
11 | 16 | ||
17 | use crate::cfg_flag::CfgFlag; | ||
18 | |||
12 | /// `CargoWorkspace` represents the logical structure of, well, a Cargo | 19 | /// `CargoWorkspace` represents the logical structure of, well, a Cargo |
13 | /// workspace. It pretty closely mirrors `cargo metadata` output. | 20 | /// workspace. It pretty closely mirrors `cargo metadata` output. |
14 | /// | 21 | /// |
@@ -19,7 +26,7 @@ use rustc_hash::FxHashMap; | |||
19 | /// | 26 | /// |
20 | /// We use absolute paths here, `cargo metadata` guarantees to always produce | 27 | /// We use absolute paths here, `cargo metadata` guarantees to always produce |
21 | /// abs paths. | 28 | /// abs paths. |
22 | #[derive(Debug, Clone)] | 29 | #[derive(Debug, Clone, Eq, PartialEq)] |
23 | pub struct CargoWorkspace { | 30 | pub struct CargoWorkspace { |
24 | packages: Arena<PackageData>, | 31 | packages: Arena<PackageData>, |
25 | targets: Arena<TargetData>, | 32 | targets: Arena<TargetData>, |
@@ -40,7 +47,7 @@ impl ops::Index<Target> for CargoWorkspace { | |||
40 | } | 47 | } |
41 | } | 48 | } |
42 | 49 | ||
43 | #[derive(Clone, Debug, PartialEq, Eq)] | 50 | #[derive(Default, Clone, Debug, PartialEq, Eq)] |
44 | pub struct CargoConfig { | 51 | pub struct CargoConfig { |
45 | /// Do not activate the `default` feature. | 52 | /// Do not activate the `default` feature. |
46 | pub no_default_features: bool, | 53 | pub no_default_features: bool, |
@@ -59,23 +66,11 @@ pub struct CargoConfig { | |||
59 | pub target: Option<String>, | 66 | pub target: Option<String>, |
60 | } | 67 | } |
61 | 68 | ||
62 | impl Default for CargoConfig { | ||
63 | fn default() -> Self { | ||
64 | CargoConfig { | ||
65 | no_default_features: false, | ||
66 | all_features: false, | ||
67 | features: Vec::new(), | ||
68 | load_out_dirs_from_check: false, | ||
69 | target: None, | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | |||
74 | pub type Package = Idx<PackageData>; | 69 | pub type Package = Idx<PackageData>; |
75 | 70 | ||
76 | pub type Target = Idx<TargetData>; | 71 | pub type Target = Idx<TargetData>; |
77 | 72 | ||
78 | #[derive(Debug, Clone)] | 73 | #[derive(Debug, Clone, Eq, PartialEq)] |
79 | pub struct PackageData { | 74 | pub struct PackageData { |
80 | pub version: String, | 75 | pub version: String, |
81 | pub name: String, | 76 | pub name: String, |
@@ -85,18 +80,18 @@ pub struct PackageData { | |||
85 | pub dependencies: Vec<PackageDependency>, | 80 | pub dependencies: Vec<PackageDependency>, |
86 | pub edition: Edition, | 81 | pub edition: Edition, |
87 | pub features: Vec<String>, | 82 | pub features: Vec<String>, |
88 | pub cfgs: Vec<String>, | 83 | pub cfgs: Vec<CfgFlag>, |
89 | pub out_dir: Option<AbsPathBuf>, | 84 | pub out_dir: Option<AbsPathBuf>, |
90 | pub proc_macro_dylib_path: Option<AbsPathBuf>, | 85 | pub proc_macro_dylib_path: Option<AbsPathBuf>, |
91 | } | 86 | } |
92 | 87 | ||
93 | #[derive(Debug, Clone)] | 88 | #[derive(Debug, Clone, Eq, PartialEq)] |
94 | pub struct PackageDependency { | 89 | pub struct PackageDependency { |
95 | pub pkg: Package, | 90 | pub pkg: Package, |
96 | pub name: String, | 91 | pub name: String, |
97 | } | 92 | } |
98 | 93 | ||
99 | #[derive(Debug, Clone)] | 94 | #[derive(Debug, Clone, Eq, PartialEq)] |
100 | pub struct TargetData { | 95 | pub struct TargetData { |
101 | pub package: Package, | 96 | pub package: Package, |
102 | pub name: String, | 97 | pub name: String, |
@@ -141,28 +136,31 @@ impl PackageData { | |||
141 | 136 | ||
142 | impl CargoWorkspace { | 137 | impl CargoWorkspace { |
143 | pub fn from_cargo_metadata( | 138 | pub fn from_cargo_metadata( |
144 | cargo_toml: &Path, | 139 | cargo_toml: &AbsPath, |
145 | cargo_features: &CargoConfig, | 140 | cargo_features: &CargoConfig, |
146 | ) -> Result<CargoWorkspace> { | 141 | ) -> Result<CargoWorkspace> { |
147 | let mut meta = MetadataCommand::new(); | 142 | let mut meta = MetadataCommand::new(); |
148 | meta.cargo_path(ra_toolchain::cargo()); | 143 | meta.cargo_path(ra_toolchain::cargo()); |
149 | meta.manifest_path(cargo_toml); | 144 | meta.manifest_path(cargo_toml.to_path_buf()); |
150 | if cargo_features.all_features { | 145 | if cargo_features.all_features { |
151 | meta.features(CargoOpt::AllFeatures); | 146 | meta.features(CargoOpt::AllFeatures); |
152 | } else if cargo_features.no_default_features { | 147 | } else { |
153 | // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` | 148 | if cargo_features.no_default_features { |
154 | // https://github.com/oli-obk/cargo_metadata/issues/79 | 149 | // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` |
155 | meta.features(CargoOpt::NoDefaultFeatures); | 150 | // https://github.com/oli-obk/cargo_metadata/issues/79 |
156 | } else if !cargo_features.features.is_empty() { | 151 | meta.features(CargoOpt::NoDefaultFeatures); |
157 | meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone())); | 152 | } |
153 | if !cargo_features.features.is_empty() { | ||
154 | meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone())); | ||
155 | } | ||
158 | } | 156 | } |
159 | if let Some(parent) = cargo_toml.parent() { | 157 | if let Some(parent) = cargo_toml.parent() { |
160 | meta.current_dir(parent); | 158 | meta.current_dir(parent.to_path_buf()); |
161 | } | 159 | } |
162 | if let Some(target) = cargo_features.target.as_ref() { | 160 | if let Some(target) = cargo_features.target.as_ref() { |
163 | meta.other_options(vec![String::from("--filter-platform"), target.clone()]); | 161 | meta.other_options(vec![String::from("--filter-platform"), target.clone()]); |
164 | } | 162 | } |
165 | let meta = meta.exec().with_context(|| { | 163 | let mut meta = meta.exec().with_context(|| { |
166 | format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) | 164 | format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) |
167 | })?; | 165 | })?; |
168 | 166 | ||
@@ -182,6 +180,7 @@ impl CargoWorkspace { | |||
182 | 180 | ||
183 | let ws_members = &meta.workspace_members; | 181 | let ws_members = &meta.workspace_members; |
184 | 182 | ||
183 | meta.packages.sort_by(|a, b| a.id.cmp(&b.id)); | ||
185 | for meta_pkg in meta.packages { | 184 | for meta_pkg in meta.packages { |
186 | let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } = | 185 | let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } = |
187 | meta_pkg; | 186 | meta_pkg; |
@@ -217,7 +216,7 @@ impl CargoWorkspace { | |||
217 | } | 216 | } |
218 | } | 217 | } |
219 | let resolve = meta.resolve.expect("metadata executed with deps"); | 218 | let resolve = meta.resolve.expect("metadata executed with deps"); |
220 | for node in resolve.nodes { | 219 | for mut node in resolve.nodes { |
221 | let source = match pkg_by_id.get(&node.id) { | 220 | let source = match pkg_by_id.get(&node.id) { |
222 | Some(&src) => src, | 221 | Some(&src) => src, |
223 | // FIXME: replace this and a similar branch below with `.unwrap`, once | 222 | // FIXME: replace this and a similar branch below with `.unwrap`, once |
@@ -228,6 +227,7 @@ impl CargoWorkspace { | |||
228 | continue; | 227 | continue; |
229 | } | 228 | } |
230 | }; | 229 | }; |
230 | node.deps.sort_by(|a, b| a.pkg.cmp(&b.pkg)); | ||
231 | for dep_node in node.deps { | 231 | for dep_node in node.deps { |
232 | let pkg = match pkg_by_id.get(&dep_node.pkg) { | 232 | let pkg = match pkg_by_id.get(&dep_node.pkg) { |
233 | Some(&pkg) => pkg, | 233 | Some(&pkg) => pkg, |
@@ -281,7 +281,7 @@ impl CargoWorkspace { | |||
281 | pub struct ExternResources { | 281 | pub struct ExternResources { |
282 | out_dirs: FxHashMap<PackageId, AbsPathBuf>, | 282 | out_dirs: FxHashMap<PackageId, AbsPathBuf>, |
283 | proc_dylib_paths: FxHashMap<PackageId, AbsPathBuf>, | 283 | proc_dylib_paths: FxHashMap<PackageId, AbsPathBuf>, |
284 | cfgs: FxHashMap<PackageId, Vec<String>>, | 284 | cfgs: FxHashMap<PackageId, Vec<CfgFlag>>, |
285 | } | 285 | } |
286 | 286 | ||
287 | pub fn load_extern_resources( | 287 | pub fn load_extern_resources( |
@@ -292,12 +292,16 @@ pub fn load_extern_resources( | |||
292 | cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml); | 292 | cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml); |
293 | if cargo_features.all_features { | 293 | if cargo_features.all_features { |
294 | cmd.arg("--all-features"); | 294 | cmd.arg("--all-features"); |
295 | } else if cargo_features.no_default_features { | ||
296 | // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` | ||
297 | // https://github.com/oli-obk/cargo_metadata/issues/79 | ||
298 | cmd.arg("--no-default-features"); | ||
299 | } else { | 295 | } else { |
300 | cmd.args(&cargo_features.features); | 296 | if cargo_features.no_default_features { |
297 | // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` | ||
298 | // https://github.com/oli-obk/cargo_metadata/issues/79 | ||
299 | cmd.arg("--no-default-features"); | ||
300 | } | ||
301 | if !cargo_features.features.is_empty() { | ||
302 | cmd.arg("--features"); | ||
303 | cmd.arg(cargo_features.features.join(" ")); | ||
304 | } | ||
301 | } | 305 | } |
302 | 306 | ||
303 | let output = cmd.output()?; | 307 | let output = cmd.output()?; |
@@ -308,9 +312,25 @@ pub fn load_extern_resources( | |||
308 | if let Ok(message) = message { | 312 | if let Ok(message) = message { |
309 | match message { | 313 | match message { |
310 | Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => { | 314 | Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => { |
311 | let out_dir = AbsPathBuf::assert(out_dir); | 315 | let cfgs = { |
312 | res.out_dirs.insert(package_id.clone(), out_dir); | 316 | let mut acc = Vec::new(); |
313 | res.cfgs.insert(package_id, cfgs); | 317 | for cfg in cfgs { |
318 | match cfg.parse::<CfgFlag>() { | ||
319 | Ok(it) => acc.push(it), | ||
320 | Err(err) => { | ||
321 | anyhow::bail!("invalid cfg from cargo-metadata: {}", err) | ||
322 | } | ||
323 | }; | ||
324 | } | ||
325 | acc | ||
326 | }; | ||
327 | // cargo_metadata crate returns default (empty) path for | ||
328 | // older cargos, which is not absolute, so work around that. | ||
329 | if out_dir != PathBuf::default() { | ||
330 | let out_dir = AbsPathBuf::assert(out_dir); | ||
331 | res.out_dirs.insert(package_id.clone(), out_dir); | ||
332 | res.cfgs.insert(package_id, cfgs); | ||
333 | } | ||
314 | } | 334 | } |
315 | Message::CompilerArtifact(message) => { | 335 | Message::CompilerArtifact(message) => { |
316 | if message.target.kind.contains(&"proc-macro".to_string()) { | 336 | if message.target.kind.contains(&"proc-macro".to_string()) { |
diff --git a/crates/ra_project_model/src/cfg_flag.rs b/crates/ra_project_model/src/cfg_flag.rs new file mode 100644 index 000000000..bd50056c6 --- /dev/null +++ b/crates/ra_project_model/src/cfg_flag.rs | |||
@@ -0,0 +1,51 @@ | |||
1 | //! Parsing of CfgFlags as command line arguments, as in | ||
2 | //! | ||
3 | //! rustc main.rs --cfg foo --cfg 'feature="bar"' | ||
4 | use std::str::FromStr; | ||
5 | |||
6 | use ra_cfg::CfgOptions; | ||
7 | use stdx::split_once; | ||
8 | |||
9 | #[derive(Clone, Eq, PartialEq, Debug)] | ||
10 | pub enum CfgFlag { | ||
11 | Atom(String), | ||
12 | KeyValue { key: String, value: String }, | ||
13 | } | ||
14 | |||
15 | impl FromStr for CfgFlag { | ||
16 | type Err = String; | ||
17 | fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
18 | let res = match split_once(s, '=') { | ||
19 | Some((key, value)) => { | ||
20 | if !(value.starts_with('"') && value.ends_with('"')) { | ||
21 | return Err(format!("Invalid cfg ({:?}), value should be in quotes", s)); | ||
22 | } | ||
23 | let key = key.to_string(); | ||
24 | let value = value[1..value.len() - 1].to_string(); | ||
25 | CfgFlag::KeyValue { key, value } | ||
26 | } | ||
27 | None => CfgFlag::Atom(s.into()), | ||
28 | }; | ||
29 | Ok(res) | ||
30 | } | ||
31 | } | ||
32 | |||
33 | impl<'de> serde::Deserialize<'de> for CfgFlag { | ||
34 | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
35 | where | ||
36 | D: serde::Deserializer<'de>, | ||
37 | { | ||
38 | String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom) | ||
39 | } | ||
40 | } | ||
41 | |||
42 | impl Extend<CfgFlag> for CfgOptions { | ||
43 | fn extend<T: IntoIterator<Item = CfgFlag>>(&mut self, iter: T) { | ||
44 | for cfg_flag in iter { | ||
45 | match cfg_flag { | ||
46 | CfgFlag::Atom(it) => self.insert_atom(it.into()), | ||
47 | CfgFlag::KeyValue { key, value } => self.insert_key_value(key.into(), value.into()), | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | } | ||
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 8b85b4831..300e75135 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -3,12 +3,12 @@ | |||
3 | mod cargo_workspace; | 3 | mod cargo_workspace; |
4 | mod project_json; | 4 | mod project_json; |
5 | mod sysroot; | 5 | mod sysroot; |
6 | mod cfg_flag; | ||
6 | 7 | ||
7 | use std::{ | 8 | use std::{ |
8 | fs::{self, read_dir, ReadDir}, | 9 | fs::{self, read_dir, ReadDir}, |
9 | io, | 10 | io, |
10 | path::Path, | 11 | process::Command, |
11 | process::{Command, Output}, | ||
12 | }; | 12 | }; |
13 | 13 | ||
14 | use anyhow::{bail, Context, Result}; | 14 | use anyhow::{bail, Context, Result}; |
@@ -17,14 +17,17 @@ use ra_cfg::CfgOptions; | |||
17 | use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId}; | 17 | use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId}; |
18 | use rustc_hash::{FxHashMap, FxHashSet}; | 18 | use rustc_hash::{FxHashMap, FxHashSet}; |
19 | 19 | ||
20 | use crate::cfg_flag::CfgFlag; | ||
21 | |||
20 | pub use crate::{ | 22 | pub use crate::{ |
21 | cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind}, | 23 | cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind}, |
22 | project_json::{ProjectJson, ProjectJsonData}, | 24 | project_json::{ProjectJson, ProjectJsonData}, |
23 | sysroot::Sysroot, | 25 | sysroot::Sysroot, |
24 | }; | 26 | }; |
27 | |||
25 | pub use ra_proc_macro::ProcMacroClient; | 28 | pub use ra_proc_macro::ProcMacroClient; |
26 | 29 | ||
27 | #[derive(Debug, Clone)] | 30 | #[derive(Debug, Clone, Eq, PartialEq)] |
28 | pub enum ProjectWorkspace { | 31 | pub enum ProjectWorkspace { |
29 | /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. | 32 | /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. |
30 | Cargo { cargo: CargoWorkspace, sysroot: Sysroot }, | 33 | Cargo { cargo: CargoWorkspace, sysroot: Sysroot }, |
@@ -35,30 +38,12 @@ pub enum ProjectWorkspace { | |||
35 | /// `PackageRoot` describes a package root folder. | 38 | /// `PackageRoot` describes a package root folder. |
36 | /// Which may be an external dependency, or a member of | 39 | /// Which may be an external dependency, or a member of |
37 | /// the current workspace. | 40 | /// the current workspace. |
38 | #[derive(Debug, Clone)] | 41 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] |
39 | pub struct PackageRoot { | 42 | pub struct PackageRoot { |
40 | /// Path to the root folder | ||
41 | path: AbsPathBuf, | ||
42 | /// Is a member of the current workspace | 43 | /// Is a member of the current workspace |
43 | is_member: bool, | 44 | pub is_member: bool, |
44 | out_dir: Option<AbsPathBuf>, | 45 | pub include: Vec<AbsPathBuf>, |
45 | } | 46 | pub exclude: Vec<AbsPathBuf>, |
46 | impl PackageRoot { | ||
47 | pub fn new_member(path: AbsPathBuf) -> PackageRoot { | ||
48 | Self { path, is_member: true, out_dir: None } | ||
49 | } | ||
50 | pub fn new_non_member(path: AbsPathBuf) -> PackageRoot { | ||
51 | Self { path, is_member: false, out_dir: None } | ||
52 | } | ||
53 | pub fn path(&self) -> &AbsPath { | ||
54 | &self.path | ||
55 | } | ||
56 | pub fn out_dir(&self) -> Option<&AbsPath> { | ||
57 | self.out_dir.as_deref() | ||
58 | } | ||
59 | pub fn is_member(&self) -> bool { | ||
60 | self.is_member | ||
61 | } | ||
62 | } | 47 | } |
63 | 48 | ||
64 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] | 49 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] |
@@ -150,7 +135,7 @@ impl ProjectManifest { | |||
150 | impl ProjectWorkspace { | 135 | impl ProjectWorkspace { |
151 | pub fn load( | 136 | pub fn load( |
152 | manifest: ProjectManifest, | 137 | manifest: ProjectManifest, |
153 | cargo_features: &CargoConfig, | 138 | cargo_config: &CargoConfig, |
154 | with_sysroot: bool, | 139 | with_sysroot: bool, |
155 | ) -> Result<ProjectWorkspace> { | 140 | ) -> Result<ProjectWorkspace> { |
156 | let res = match manifest { | 141 | let res = match manifest { |
@@ -166,7 +151,7 @@ impl ProjectWorkspace { | |||
166 | ProjectWorkspace::Json { project } | 151 | ProjectWorkspace::Json { project } |
167 | } | 152 | } |
168 | ProjectManifest::CargoToml(cargo_toml) => { | 153 | ProjectManifest::CargoToml(cargo_toml) => { |
169 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) | 154 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_config) |
170 | .with_context(|| { | 155 | .with_context(|| { |
171 | format!( | 156 | format!( |
172 | "Failed to read Cargo metadata from Cargo.toml file {}", | 157 | "Failed to read Cargo metadata from Cargo.toml file {}", |
@@ -195,18 +180,40 @@ impl ProjectWorkspace { | |||
195 | /// the root is a member of the current workspace | 180 | /// the root is a member of the current workspace |
196 | pub fn to_roots(&self) -> Vec<PackageRoot> { | 181 | pub fn to_roots(&self) -> Vec<PackageRoot> { |
197 | match self { | 182 | match self { |
198 | ProjectWorkspace::Json { project } => { | 183 | ProjectWorkspace::Json { project } => project |
199 | project.roots.iter().map(|r| PackageRoot::new_member(r.path.clone())).collect() | 184 | .crates |
200 | } | 185 | .iter() |
186 | .map(|krate| PackageRoot { | ||
187 | is_member: krate.is_workspace_member, | ||
188 | include: krate.include.clone(), | ||
189 | exclude: krate.exclude.clone(), | ||
190 | }) | ||
191 | .collect::<FxHashSet<_>>() | ||
192 | .into_iter() | ||
193 | .collect::<Vec<_>>(), | ||
201 | ProjectWorkspace::Cargo { cargo, sysroot } => cargo | 194 | ProjectWorkspace::Cargo { cargo, sysroot } => cargo |
202 | .packages() | 195 | .packages() |
203 | .map(|pkg| PackageRoot { | 196 | .map(|pkg| { |
204 | path: cargo[pkg].root().to_path_buf(), | 197 | let is_member = cargo[pkg].is_member; |
205 | is_member: cargo[pkg].is_member, | 198 | let pkg_root = cargo[pkg].root().to_path_buf(); |
206 | out_dir: cargo[pkg].out_dir.clone(), | 199 | |
200 | let mut include = vec![pkg_root.clone()]; | ||
201 | include.extend(cargo[pkg].out_dir.clone()); | ||
202 | |||
203 | let mut exclude = vec![pkg_root.join(".git")]; | ||
204 | if is_member { | ||
205 | exclude.push(pkg_root.join("target")); | ||
206 | } else { | ||
207 | exclude.push(pkg_root.join("tests")); | ||
208 | exclude.push(pkg_root.join("examples")); | ||
209 | exclude.push(pkg_root.join("benches")); | ||
210 | } | ||
211 | PackageRoot { is_member, include, exclude } | ||
207 | }) | 212 | }) |
208 | .chain(sysroot.crates().map(|krate| { | 213 | .chain(sysroot.crates().map(|krate| PackageRoot { |
209 | PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf()) | 214 | is_member: false, |
215 | include: vec![sysroot[krate].root_dir().to_path_buf()], | ||
216 | exclude: Vec::new(), | ||
210 | })) | 217 | })) |
211 | .collect(), | 218 | .collect(), |
212 | } | 219 | } |
@@ -246,6 +253,7 @@ impl ProjectWorkspace { | |||
246 | let mut crate_graph = CrateGraph::default(); | 253 | let mut crate_graph = CrateGraph::default(); |
247 | match self { | 254 | match self { |
248 | ProjectWorkspace::Json { project } => { | 255 | ProjectWorkspace::Json { project } => { |
256 | let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default(); | ||
249 | let crates: FxHashMap<_, _> = project | 257 | let crates: FxHashMap<_, _> = project |
250 | .crates | 258 | .crates |
251 | .iter() | 259 | .iter() |
@@ -254,17 +262,20 @@ impl ProjectWorkspace { | |||
254 | let file_path = &krate.root_module; | 262 | let file_path = &krate.root_module; |
255 | let file_id = load(&file_path)?; | 263 | let file_id = load(&file_path)?; |
256 | 264 | ||
257 | let mut env = Env::default(); | 265 | let env = krate.env.clone().into_iter().collect(); |
258 | if let Some(out_dir) = &krate.out_dir { | ||
259 | // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() | ||
260 | if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { | ||
261 | env.set("OUT_DIR", out_dir); | ||
262 | } | ||
263 | } | ||
264 | let proc_macro = krate | 266 | let proc_macro = krate |
265 | .proc_macro_dylib_path | 267 | .proc_macro_dylib_path |
266 | .clone() | 268 | .clone() |
267 | .map(|it| proc_macro_client.by_dylib_path(&it)); | 269 | .map(|it| proc_macro_client.by_dylib_path(&it)); |
270 | |||
271 | let target = krate.target.as_deref().or(target); | ||
272 | let target_cfgs = cfg_cache | ||
273 | .entry(target) | ||
274 | .or_insert_with(|| get_rustc_cfg_options(target)); | ||
275 | |||
276 | let mut cfg_options = CfgOptions::default(); | ||
277 | cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned()); | ||
278 | |||
268 | // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env | 279 | // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env |
269 | Some(( | 280 | Some(( |
270 | CrateId(seq_index as u32), | 281 | CrateId(seq_index as u32), |
@@ -273,7 +284,7 @@ impl ProjectWorkspace { | |||
273 | krate.edition, | 284 | krate.edition, |
274 | // FIXME json definitions can store the crate name | 285 | // FIXME json definitions can store the crate name |
275 | None, | 286 | None, |
276 | krate.cfg.clone(), | 287 | cfg_options, |
277 | env, | 288 | env, |
278 | proc_macro.unwrap_or_default(), | 289 | proc_macro.unwrap_or_default(), |
279 | ), | 290 | ), |
@@ -288,10 +299,7 @@ impl ProjectWorkspace { | |||
288 | if let (Some(&from), Some(&to)) = | 299 | if let (Some(&from), Some(&to)) = |
289 | (crates.get(&from_crate_id), crates.get(&to_crate_id)) | 300 | (crates.get(&from_crate_id), crates.get(&to_crate_id)) |
290 | { | 301 | { |
291 | if crate_graph | 302 | if crate_graph.add_dep(from, dep.name.clone(), to).is_err() { |
292 | .add_dep(from, CrateName::new(&dep.name).unwrap(), to) | ||
293 | .is_err() | ||
294 | { | ||
295 | log::error!( | 303 | log::error!( |
296 | "cyclic dependency {:?} -> {:?}", | 304 | "cyclic dependency {:?} -> {:?}", |
297 | from_crate_id, | 305 | from_crate_id, |
@@ -303,7 +311,8 @@ impl ProjectWorkspace { | |||
303 | } | 311 | } |
304 | } | 312 | } |
305 | ProjectWorkspace::Cargo { cargo, sysroot } => { | 313 | ProjectWorkspace::Cargo { cargo, sysroot } => { |
306 | let mut cfg_options = get_rustc_cfg_options(target); | 314 | let mut cfg_options = CfgOptions::default(); |
315 | cfg_options.extend(get_rustc_cfg_options(target)); | ||
307 | 316 | ||
308 | let sysroot_crates: FxHashMap<_, _> = sysroot | 317 | let sysroot_crates: FxHashMap<_, _> = sysroot |
309 | .crates() | 318 | .crates() |
@@ -312,13 +321,11 @@ impl ProjectWorkspace { | |||
312 | 321 | ||
313 | let env = Env::default(); | 322 | let env = Env::default(); |
314 | let proc_macro = vec![]; | 323 | let proc_macro = vec![]; |
315 | let crate_name = CrateName::new(&sysroot[krate].name) | 324 | let name = sysroot[krate].name.clone(); |
316 | .expect("Sysroot crate names should not contain dashes"); | ||
317 | |||
318 | let crate_id = crate_graph.add_crate_root( | 325 | let crate_id = crate_graph.add_crate_root( |
319 | file_id, | 326 | file_id, |
320 | Edition::Edition2018, | 327 | Edition::Edition2018, |
321 | Some(crate_name), | 328 | Some(name), |
322 | cfg_options.clone(), | 329 | cfg_options.clone(), |
323 | env, | 330 | env, |
324 | proc_macro, | 331 | proc_macro, |
@@ -352,6 +359,7 @@ impl ProjectWorkspace { | |||
352 | 359 | ||
353 | // Add test cfg for non-sysroot crates | 360 | // Add test cfg for non-sysroot crates |
354 | cfg_options.insert_atom("test".into()); | 361 | cfg_options.insert_atom("test".into()); |
362 | cfg_options.insert_atom("debug_assertions".into()); | ||
355 | 363 | ||
356 | // Next, create crates for each package, target pair | 364 | // Next, create crates for each package, target pair |
357 | for pkg in cargo.packages() { | 365 | for pkg in cargo.packages() { |
@@ -365,15 +373,7 @@ impl ProjectWorkspace { | |||
365 | for feature in cargo[pkg].features.iter() { | 373 | for feature in cargo[pkg].features.iter() { |
366 | opts.insert_key_value("feature".into(), feature.into()); | 374 | opts.insert_key_value("feature".into(), feature.into()); |
367 | } | 375 | } |
368 | for cfg in cargo[pkg].cfgs.iter() { | 376 | opts.extend(cargo[pkg].cfgs.iter().cloned()); |
369 | match cfg.find('=') { | ||
370 | Some(split) => opts.insert_key_value( | ||
371 | cfg[..split].into(), | ||
372 | cfg[split + 1..].trim_matches('"').into(), | ||
373 | ), | ||
374 | None => opts.insert_atom(cfg.into()), | ||
375 | }; | ||
376 | } | ||
377 | opts | 377 | opts |
378 | }; | 378 | }; |
379 | let mut env = Env::default(); | 379 | let mut env = Env::default(); |
@@ -392,7 +392,7 @@ impl ProjectWorkspace { | |||
392 | let crate_id = crate_graph.add_crate_root( | 392 | let crate_id = crate_graph.add_crate_root( |
393 | file_id, | 393 | file_id, |
394 | edition, | 394 | edition, |
395 | Some(CrateName::normalize_dashes(&cargo[pkg].name)), | 395 | Some(cargo[pkg].name.clone()), |
396 | cfg_options, | 396 | cfg_options, |
397 | env, | 397 | env, |
398 | proc_macro.clone(), | 398 | proc_macro.clone(), |
@@ -499,66 +499,37 @@ impl ProjectWorkspace { | |||
499 | } | 499 | } |
500 | crate_graph | 500 | crate_graph |
501 | } | 501 | } |
502 | |||
503 | pub fn workspace_root_for(&self, path: &Path) -> Option<&AbsPath> { | ||
504 | match self { | ||
505 | ProjectWorkspace::Cargo { cargo, .. } => { | ||
506 | Some(cargo.workspace_root()).filter(|root| path.starts_with(root)) | ||
507 | } | ||
508 | ProjectWorkspace::Json { project: ProjectJson { roots, .. }, .. } => roots | ||
509 | .iter() | ||
510 | .find(|root| path.starts_with(&root.path)) | ||
511 | .map(|root| root.path.as_path()), | ||
512 | } | ||
513 | } | ||
514 | } | 502 | } |
515 | 503 | ||
516 | fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions { | 504 | fn get_rustc_cfg_options(target: Option<&str>) -> Vec<CfgFlag> { |
517 | let mut cfg_options = CfgOptions::default(); | 505 | let mut res = Vec::new(); |
518 | 506 | ||
519 | // Some nightly-only cfgs, which are required for stdlib | 507 | // Some nightly-only cfgs, which are required for stdlib |
520 | { | 508 | res.push(CfgFlag::Atom("target_thread_local".into())); |
521 | cfg_options.insert_atom("target_thread_local".into()); | 509 | for &ty in ["8", "16", "32", "64", "cas", "ptr"].iter() { |
522 | for &target_has_atomic in ["8", "16", "32", "64", "cas", "ptr"].iter() { | 510 | for &key in ["target_has_atomic", "target_has_atomic_load_store"].iter() { |
523 | cfg_options.insert_key_value("target_has_atomic".into(), target_has_atomic.into()); | 511 | res.push(CfgFlag::KeyValue { key: key.to_string(), value: ty.into() }); |
524 | cfg_options | ||
525 | .insert_key_value("target_has_atomic_load_store".into(), target_has_atomic.into()); | ||
526 | } | 512 | } |
527 | } | 513 | } |
528 | 514 | ||
529 | let rustc_cfgs = || -> Result<String> { | 515 | let rustc_cfgs = { |
530 | // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here. | ||
531 | let mut cmd = Command::new(ra_toolchain::rustc()); | 516 | let mut cmd = Command::new(ra_toolchain::rustc()); |
532 | cmd.args(&["--print", "cfg", "-O"]); | 517 | cmd.args(&["--print", "cfg", "-O"]); |
533 | if let Some(target) = target { | 518 | if let Some(target) = target { |
534 | cmd.args(&["--target", target]); | 519 | cmd.args(&["--target", target]); |
535 | } | 520 | } |
536 | let output = output(cmd)?; | 521 | utf8_stdout(cmd) |
537 | Ok(String::from_utf8(output.stdout)?) | 522 | }; |
538 | }(); | ||
539 | 523 | ||
540 | match rustc_cfgs { | 524 | match rustc_cfgs { |
541 | Ok(rustc_cfgs) => { | 525 | Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())), |
542 | for line in rustc_cfgs.lines() { | ||
543 | match line.find('=') { | ||
544 | None => cfg_options.insert_atom(line.into()), | ||
545 | Some(pos) => { | ||
546 | let key = &line[..pos]; | ||
547 | let value = line[pos + 1..].trim_matches('"'); | ||
548 | cfg_options.insert_key_value(key.into(), value.into()); | ||
549 | } | ||
550 | } | ||
551 | } | ||
552 | } | ||
553 | Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), | 526 | Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), |
554 | } | 527 | } |
555 | 528 | ||
556 | cfg_options.insert_atom("debug_assertions".into()); | 529 | res |
557 | |||
558 | cfg_options | ||
559 | } | 530 | } |
560 | 531 | ||
561 | fn output(mut cmd: Command) -> Result<Output> { | 532 | fn utf8_stdout(mut cmd: Command) -> Result<String> { |
562 | let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?; | 533 | let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?; |
563 | if !output.status.success() { | 534 | if !output.status.success() { |
564 | match String::from_utf8(output.stderr) { | 535 | match String::from_utf8(output.stderr) { |
@@ -568,5 +539,6 @@ fn output(mut cmd: Command) -> Result<Output> { | |||
568 | _ => bail!("{:?} failed, {}", cmd, output.status), | 539 | _ => bail!("{:?} failed, {}", cmd, output.status), |
569 | } | 540 | } |
570 | } | 541 | } |
571 | Ok(output) | 542 | let stdout = String::from_utf8(output.stdout)?; |
543 | Ok(stdout) | ||
572 | } | 544 | } |
diff --git a/crates/ra_project_model/src/project_json.rs b/crates/ra_project_model/src/project_json.rs index 4b5dcd634..e3f3163f6 100644 --- a/crates/ra_project_model/src/project_json.rs +++ b/crates/ra_project_model/src/project_json.rs | |||
@@ -3,70 +3,78 @@ | |||
3 | use std::path::PathBuf; | 3 | use std::path::PathBuf; |
4 | 4 | ||
5 | use paths::{AbsPath, AbsPathBuf}; | 5 | use paths::{AbsPath, AbsPathBuf}; |
6 | use ra_cfg::CfgOptions; | 6 | use ra_db::{CrateId, CrateName, Dependency, Edition}; |
7 | use ra_db::{CrateId, Dependency, Edition}; | 7 | use rustc_hash::FxHashMap; |
8 | use rustc_hash::FxHashSet; | 8 | use serde::{de, Deserialize}; |
9 | use serde::Deserialize; | 9 | |
10 | use stdx::split_delim; | 10 | use crate::cfg_flag::CfgFlag; |
11 | 11 | ||
12 | /// Roots and crates that compose this Rust project. | 12 | /// Roots and crates that compose this Rust project. |
13 | #[derive(Clone, Debug)] | 13 | #[derive(Clone, Debug, Eq, PartialEq)] |
14 | pub struct ProjectJson { | 14 | pub struct ProjectJson { |
15 | pub(crate) roots: Vec<Root>, | ||
16 | pub(crate) crates: Vec<Crate>, | 15 | pub(crate) crates: Vec<Crate>, |
17 | } | 16 | } |
18 | 17 | ||
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)] | ||
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 | 18 | /// 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. | 19 | /// useful in creating the crate graph. |
28 | #[derive(Clone, Debug)] | 20 | #[derive(Clone, Debug, Eq, PartialEq)] |
29 | pub struct Crate { | 21 | pub struct Crate { |
30 | pub(crate) root_module: AbsPathBuf, | 22 | pub(crate) root_module: AbsPathBuf, |
31 | pub(crate) edition: Edition, | 23 | pub(crate) edition: Edition, |
32 | pub(crate) deps: Vec<Dependency>, | 24 | pub(crate) deps: Vec<Dependency>, |
33 | pub(crate) cfg: CfgOptions, | 25 | pub(crate) cfg: Vec<CfgFlag>, |
34 | pub(crate) out_dir: Option<AbsPathBuf>, | 26 | pub(crate) target: Option<String>, |
27 | pub(crate) env: FxHashMap<String, String>, | ||
35 | pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>, | 28 | pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>, |
29 | pub(crate) is_workspace_member: bool, | ||
30 | pub(crate) include: Vec<AbsPathBuf>, | ||
31 | pub(crate) exclude: Vec<AbsPathBuf>, | ||
36 | } | 32 | } |
37 | 33 | ||
38 | impl ProjectJson { | 34 | impl ProjectJson { |
39 | pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { | 35 | pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { |
40 | ProjectJson { | 36 | ProjectJson { |
41 | roots: data.roots.into_iter().map(|path| Root { path: base.join(path) }).collect(), | ||
42 | crates: data | 37 | crates: data |
43 | .crates | 38 | .crates |
44 | .into_iter() | 39 | .into_iter() |
45 | .map(|crate_data| Crate { | 40 | .map(|crate_data| { |
46 | root_module: base.join(crate_data.root_module), | 41 | let is_workspace_member = crate_data.is_workspace_member.unwrap_or_else(|| { |
47 | edition: crate_data.edition.into(), | 42 | crate_data.root_module.is_relative() |
48 | deps: crate_data | 43 | && !crate_data.root_module.starts_with("..") |
49 | .deps | 44 | || crate_data.root_module.starts_with(base) |
50 | .into_iter() | 45 | }); |
51 | .map(|dep_data| Dependency { | 46 | let root_module = base.join(crate_data.root_module); |
52 | crate_id: CrateId(dep_data.krate as u32), | 47 | let (include, exclude) = match crate_data.source { |
53 | name: dep_data.name.into(), | 48 | Some(src) => { |
54 | }) | 49 | let absolutize = |dirs: Vec<PathBuf>| { |
55 | .collect::<Vec<_>>(), | 50 | dirs.into_iter().map(|it| base.join(it)).collect::<Vec<_>>() |
56 | cfg: { | 51 | }; |
57 | let mut cfg = CfgOptions::default(); | 52 | (absolutize(src.include_dirs), absolutize(src.exclude_dirs)) |
58 | for entry in &crate_data.cfg { | ||
59 | match split_delim(entry, '=') { | ||
60 | Some((key, value)) => { | ||
61 | cfg.insert_key_value(key.into(), value.into()); | ||
62 | } | ||
63 | None => cfg.insert_atom(entry.into()), | ||
64 | } | ||
65 | } | 53 | } |
66 | cfg | 54 | None => (vec![root_module.parent().unwrap().to_path_buf()], Vec::new()), |
67 | }, | 55 | }; |
68 | out_dir: crate_data.out_dir.map(|it| base.join(it)), | 56 | |
69 | proc_macro_dylib_path: crate_data.proc_macro_dylib_path.map(|it| base.join(it)), | 57 | Crate { |
58 | root_module, | ||
59 | edition: crate_data.edition.into(), | ||
60 | deps: crate_data | ||
61 | .deps | ||
62 | .into_iter() | ||
63 | .map(|dep_data| Dependency { | ||
64 | crate_id: CrateId(dep_data.krate as u32), | ||
65 | name: dep_data.name, | ||
66 | }) | ||
67 | .collect::<Vec<_>>(), | ||
68 | cfg: crate_data.cfg, | ||
69 | target: crate_data.target, | ||
70 | env: crate_data.env, | ||
71 | proc_macro_dylib_path: crate_data | ||
72 | .proc_macro_dylib_path | ||
73 | .map(|it| base.join(it)), | ||
74 | is_workspace_member, | ||
75 | include, | ||
76 | exclude, | ||
77 | } | ||
70 | }) | 78 | }) |
71 | .collect::<Vec<_>>(), | 79 | .collect::<Vec<_>>(), |
72 | } | 80 | } |
@@ -75,7 +83,6 @@ impl ProjectJson { | |||
75 | 83 | ||
76 | #[derive(Deserialize)] | 84 | #[derive(Deserialize)] |
77 | pub struct ProjectJsonData { | 85 | pub struct ProjectJsonData { |
78 | roots: Vec<PathBuf>, | ||
79 | crates: Vec<CrateData>, | 86 | crates: Vec<CrateData>, |
80 | } | 87 | } |
81 | 88 | ||
@@ -85,9 +92,13 @@ struct CrateData { | |||
85 | edition: EditionData, | 92 | edition: EditionData, |
86 | deps: Vec<DepData>, | 93 | deps: Vec<DepData>, |
87 | #[serde(default)] | 94 | #[serde(default)] |
88 | cfg: FxHashSet<String>, | 95 | cfg: Vec<CfgFlag>, |
89 | out_dir: Option<PathBuf>, | 96 | target: Option<String>, |
97 | #[serde(default)] | ||
98 | env: FxHashMap<String, String>, | ||
90 | proc_macro_dylib_path: Option<PathBuf>, | 99 | proc_macro_dylib_path: Option<PathBuf>, |
100 | is_workspace_member: Option<bool>, | ||
101 | source: Option<CrateSource>, | ||
91 | } | 102 | } |
92 | 103 | ||
93 | #[derive(Deserialize)] | 104 | #[derive(Deserialize)] |
@@ -113,5 +124,20 @@ struct DepData { | |||
113 | /// Identifies a crate by position in the crates array. | 124 | /// Identifies a crate by position in the crates array. |
114 | #[serde(rename = "crate")] | 125 | #[serde(rename = "crate")] |
115 | krate: usize, | 126 | krate: usize, |
116 | name: String, | 127 | #[serde(deserialize_with = "deserialize_crate_name")] |
128 | name: CrateName, | ||
129 | } | ||
130 | |||
131 | #[derive(Deserialize)] | ||
132 | struct CrateSource { | ||
133 | include_dirs: Vec<PathBuf>, | ||
134 | exclude_dirs: Vec<PathBuf>, | ||
135 | } | ||
136 | |||
137 | fn deserialize_crate_name<'de, D>(de: D) -> Result<CrateName, D::Error> | ||
138 | where | ||
139 | D: de::Deserializer<'de>, | ||
140 | { | ||
141 | let name = String::deserialize(de)?; | ||
142 | CrateName::new(&name).map_err(|err| de::Error::custom(format!("invalid crate name: {:?}", err))) | ||
117 | } | 143 | } |
diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs index 943ff92df..a10ade375 100644 --- a/crates/ra_project_model/src/sysroot.rs +++ b/crates/ra_project_model/src/sysroot.rs | |||
@@ -3,19 +3,19 @@ | |||
3 | use std::{convert::TryFrom, env, ops, path::Path, process::Command}; | 3 | use std::{convert::TryFrom, env, ops, path::Path, process::Command}; |
4 | 4 | ||
5 | use anyhow::{bail, format_err, Result}; | 5 | use anyhow::{bail, format_err, Result}; |
6 | use paths::{AbsPath, AbsPathBuf}; | ||
6 | use ra_arena::{Arena, Idx}; | 7 | use ra_arena::{Arena, Idx}; |
7 | 8 | ||
8 | use crate::output; | 9 | use crate::utf8_stdout; |
9 | use paths::{AbsPath, AbsPathBuf}; | ||
10 | 10 | ||
11 | #[derive(Default, Debug, Clone)] | 11 | #[derive(Default, Debug, Clone, Eq, PartialEq)] |
12 | pub struct Sysroot { | 12 | pub struct Sysroot { |
13 | crates: Arena<SysrootCrateData>, | 13 | crates: Arena<SysrootCrateData>, |
14 | } | 14 | } |
15 | 15 | ||
16 | pub type SysrootCrate = Idx<SysrootCrateData>; | 16 | pub type SysrootCrate = Idx<SysrootCrateData>; |
17 | 17 | ||
18 | #[derive(Debug, Clone)] | 18 | #[derive(Debug, Clone, Eq, PartialEq)] |
19 | pub struct SysrootCrateData { | 19 | pub struct SysrootCrateData { |
20 | pub name: String, | 20 | pub name: String, |
21 | pub root: AbsPathBuf, | 21 | pub root: AbsPathBuf, |
@@ -54,6 +54,8 @@ impl Sysroot { | |||
54 | let src = get_or_install_rust_src(cargo_toml)?; | 54 | let src = get_or_install_rust_src(cargo_toml)?; |
55 | let mut sysroot = Sysroot { crates: Arena::default() }; | 55 | let mut sysroot = Sysroot { crates: Arena::default() }; |
56 | for name in SYSROOT_CRATES.trim().lines() { | 56 | for name in SYSROOT_CRATES.trim().lines() { |
57 | // FIXME: remove this path when 1.47 comes out | ||
58 | // https://github.com/rust-lang/rust/pull/73265 | ||
57 | let root = src.join(format!("lib{}", name)).join("lib.rs"); | 59 | let root = src.join(format!("lib{}", name)).join("lib.rs"); |
58 | if root.exists() { | 60 | if root.exists() { |
59 | sysroot.crates.alloc(SysrootCrateData { | 61 | sysroot.crates.alloc(SysrootCrateData { |
@@ -61,6 +63,15 @@ impl Sysroot { | |||
61 | root, | 63 | root, |
62 | deps: Vec::new(), | 64 | deps: Vec::new(), |
63 | }); | 65 | }); |
66 | } else { | ||
67 | let root = src.join(name).join("src/lib.rs"); | ||
68 | if root.exists() { | ||
69 | sysroot.crates.alloc(SysrootCrateData { | ||
70 | name: name.into(), | ||
71 | root, | ||
72 | deps: Vec::new(), | ||
73 | }); | ||
74 | } | ||
64 | } | 75 | } |
65 | } | 76 | } |
66 | if let Some(std) = sysroot.std() { | 77 | if let Some(std) = sysroot.std() { |
@@ -92,26 +103,40 @@ fn get_or_install_rust_src(cargo_toml: &AbsPath) -> Result<AbsPathBuf> { | |||
92 | let current_dir = cargo_toml.parent().unwrap(); | 103 | let current_dir = cargo_toml.parent().unwrap(); |
93 | let mut rustc = Command::new(ra_toolchain::rustc()); | 104 | let mut rustc = Command::new(ra_toolchain::rustc()); |
94 | rustc.current_dir(current_dir).args(&["--print", "sysroot"]); | 105 | rustc.current_dir(current_dir).args(&["--print", "sysroot"]); |
95 | let rustc_output = output(rustc)?; | 106 | let stdout = utf8_stdout(rustc)?; |
96 | let stdout = String::from_utf8(rustc_output.stdout)?; | ||
97 | let sysroot_path = AbsPath::assert(Path::new(stdout.trim())); | 107 | let sysroot_path = AbsPath::assert(Path::new(stdout.trim())); |
98 | let src_path = sysroot_path.join("lib/rustlib/src/rust/src"); | 108 | let mut src = get_rust_src(sysroot_path); |
99 | 109 | if src.is_none() { | |
100 | if !src_path.exists() { | ||
101 | let mut rustup = Command::new(ra_toolchain::rustup()); | 110 | let mut rustup = Command::new(ra_toolchain::rustup()); |
102 | rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]); | 111 | rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]); |
103 | let _output = output(rustup)?; | 112 | utf8_stdout(rustup)?; |
113 | src = get_rust_src(sysroot_path); | ||
104 | } | 114 | } |
105 | if !src_path.exists() { | 115 | match src { |
106 | bail!( | 116 | Some(r) => Ok(r), |
117 | None => bail!( | ||
107 | "can't load standard library from sysroot\n\ | 118 | "can't load standard library from sysroot\n\ |
108 | {}\n\ | 119 | {}\n\ |
109 | (discovered via `rustc --print sysroot`)\n\ | 120 | (discovered via `rustc --print sysroot`)\n\ |
110 | try running `rustup component add rust-src` or set `RUST_SRC_PATH`", | 121 | try running `rustup component add rust-src` or set `RUST_SRC_PATH`", |
111 | src_path.display(), | 122 | sysroot_path.display(), |
112 | ) | 123 | ), |
124 | } | ||
125 | } | ||
126 | |||
127 | fn get_rust_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> { | ||
128 | // try the new path first since the old one still exists | ||
129 | let mut src_path = sysroot_path.join("lib/rustlib/src/rust/library"); | ||
130 | if !src_path.exists() { | ||
131 | // FIXME: remove this path when 1.47 comes out | ||
132 | // https://github.com/rust-lang/rust/pull/73265 | ||
133 | src_path = sysroot_path.join("lib/rustlib/src/rust/src"); | ||
134 | } | ||
135 | if src_path.exists() { | ||
136 | Some(src_path) | ||
137 | } else { | ||
138 | None | ||
113 | } | 139 | } |
114 | Ok(src_path) | ||
115 | } | 140 | } |
116 | 141 | ||
117 | impl SysrootCrateData { | 142 | impl SysrootCrateData { |
@@ -121,43 +146,28 @@ impl SysrootCrateData { | |||
121 | } | 146 | } |
122 | 147 | ||
123 | const SYSROOT_CRATES: &str = " | 148 | const SYSROOT_CRATES: &str = " |
124 | std | ||
125 | core | ||
126 | alloc | 149 | alloc |
127 | collections | 150 | core |
128 | libc | 151 | panic_abort |
129 | panic_unwind | 152 | panic_unwind |
130 | proc_macro | 153 | proc_macro |
131 | rustc_unicode | 154 | profiler_builtins |
132 | std_unicode | 155 | rtstartup |
133 | test | 156 | std |
134 | alloc_jemalloc | 157 | stdarch |
135 | alloc_system | ||
136 | compiler_builtins | ||
137 | getopts | ||
138 | panic_unwind | ||
139 | panic_abort | ||
140 | rand | ||
141 | term | 158 | term |
142 | unwind | 159 | test |
143 | build_helper | 160 | unwind"; |
144 | rustc_asan | ||
145 | rustc_lsan | ||
146 | rustc_msan | ||
147 | rustc_tsan | ||
148 | syntax"; | ||
149 | 161 | ||
150 | const STD_DEPS: &str = " | 162 | const STD_DEPS: &str = " |
151 | alloc | 163 | alloc |
152 | alloc_jemalloc | ||
153 | alloc_system | ||
154 | core | 164 | core |
155 | panic_abort | 165 | panic_abort |
156 | rand | 166 | panic_unwind |
157 | compiler_builtins | 167 | profiler_builtins |
158 | unwind | 168 | rtstartup |
159 | rustc_asan | 169 | proc_macro |
160 | rustc_lsan | 170 | stdarch |
161 | rustc_msan | 171 | term |
162 | rustc_tsan | 172 | test |
163 | build_helper"; | 173 | unwind"; |