diff options
Diffstat (limited to 'crates/ra_project_model/src/cargo_workspace.rs')
-rw-r--r-- | crates/ra_project_model/src/cargo_workspace.rs | 98 |
1 files changed, 59 insertions, 39 deletions
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()) { |