aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_project_model/src/cargo_workspace.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_project_model/src/cargo_workspace.rs')
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs98
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
3use std::{ffi::OsStr, ops, path::Path, process::Command}; 3use std::{
4 ffi::OsStr,
5 ops,
6 path::{Path, PathBuf},
7 process::Command,
8};
4 9
5use anyhow::{Context, Result}; 10use anyhow::{Context, Result};
6use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; 11use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
@@ -9,6 +14,8 @@ use ra_arena::{Arena, Idx};
9use ra_db::Edition; 14use ra_db::Edition;
10use rustc_hash::FxHashMap; 15use rustc_hash::FxHashMap;
11 16
17use 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)]
23pub struct CargoWorkspace { 30pub 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)]
44pub struct CargoConfig { 51pub 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
62impl 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
74pub type Package = Idx<PackageData>; 69pub type Package = Idx<PackageData>;
75 70
76pub type Target = Idx<TargetData>; 71pub type Target = Idx<TargetData>;
77 72
78#[derive(Debug, Clone)] 73#[derive(Debug, Clone, Eq, PartialEq)]
79pub struct PackageData { 74pub 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)]
94pub struct PackageDependency { 89pub 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)]
100pub struct TargetData { 95pub 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
142impl CargoWorkspace { 137impl 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 {
281pub struct ExternResources { 281pub 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
287pub fn load_extern_resources( 287pub 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()) {