aboutsummaryrefslogtreecommitdiff
path: root/crates/project_model/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/project_model/src')
-rw-r--r--crates/project_model/src/cargo_workspace.rs71
-rw-r--r--crates/project_model/src/lib.rs7
-rw-r--r--crates/project_model/src/project_json.rs13
-rw-r--r--crates/project_model/src/workspace.rs22
4 files changed, 91 insertions, 22 deletions
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index bb3b6f2ef..2ee4e88b2 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -1,10 +1,12 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::{ 3use std::{
4 convert::TryInto,
4 ffi::OsStr, 5 ffi::OsStr,
6 io::BufReader,
5 ops, 7 ops,
6 path::{Path, PathBuf}, 8 path::{Path, PathBuf},
7 process::Command, 9 process::{Command, Stdio},
8}; 10};
9 11
10use anyhow::{Context, Result}; 12use anyhow::{Context, Result};
@@ -14,6 +16,7 @@ use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}
14use itertools::Itertools; 16use itertools::Itertools;
15use paths::{AbsPath, AbsPathBuf}; 17use paths::{AbsPath, AbsPathBuf};
16use rustc_hash::FxHashMap; 18use rustc_hash::FxHashMap;
19use stdx::JodChild;
17 20
18use crate::cfg_flag::CfgFlag; 21use crate::cfg_flag::CfgFlag;
19use crate::utf8_stdout; 22use crate::utf8_stdout;
@@ -79,19 +82,35 @@ pub type Package = Idx<PackageData>;
79 82
80pub type Target = Idx<TargetData>; 83pub type Target = Idx<TargetData>;
81 84
85/// Information associated with a cargo crate
82#[derive(Debug, Clone, Eq, PartialEq)] 86#[derive(Debug, Clone, Eq, PartialEq)]
83pub struct PackageData { 87pub struct PackageData {
88 /// Version given in the `Cargo.toml`
84 pub version: String, 89 pub version: String,
90 /// Name as given in the `Cargo.toml`
85 pub name: String, 91 pub name: String,
92 /// Path containing the `Cargo.toml`
86 pub manifest: AbsPathBuf, 93 pub manifest: AbsPathBuf,
94 /// Targets provided by the crate (lib, bin, example, test, ...)
87 pub targets: Vec<Target>, 95 pub targets: Vec<Target>,
96 /// Is this package a member of the current workspace
88 pub is_member: bool, 97 pub is_member: bool,
98 /// List of packages this package depends on
89 pub dependencies: Vec<PackageDependency>, 99 pub dependencies: Vec<PackageDependency>,
100 /// Rust edition for this package
90 pub edition: Edition, 101 pub edition: Edition,
102 /// List of features to activate
91 pub features: Vec<String>, 103 pub features: Vec<String>,
104 /// List of config flags defined by this package's build script
92 pub cfgs: Vec<CfgFlag>, 105 pub cfgs: Vec<CfgFlag>,
106 /// List of cargo-related environment variables with their value
107 ///
108 /// If the package has a build script which defines environment variables,
109 /// they can also be found here.
93 pub envs: Vec<(String, String)>, 110 pub envs: Vec<(String, String)>,
111 /// Directory where a build script might place its output
94 pub out_dir: Option<AbsPathBuf>, 112 pub out_dir: Option<AbsPathBuf>,
113 /// Path to the proc-macro library file if this package exposes proc-macros
95 pub proc_macro_dylib_path: Option<AbsPathBuf>, 114 pub proc_macro_dylib_path: Option<AbsPathBuf>,
96} 115}
97 116
@@ -101,12 +120,18 @@ pub struct PackageDependency {
101 pub name: String, 120 pub name: String,
102} 121}
103 122
123/// Information associated with a package's target
104#[derive(Debug, Clone, Eq, PartialEq)] 124#[derive(Debug, Clone, Eq, PartialEq)]
105pub struct TargetData { 125pub struct TargetData {
126 /// Package that provided this target
106 pub package: Package, 127 pub package: Package,
128 /// Name as given in the `Cargo.toml` or generated from the file name
107 pub name: String, 129 pub name: String,
130 /// Path to the main source file of the target
108 pub root: AbsPathBuf, 131 pub root: AbsPathBuf,
132 /// Kind of target
109 pub kind: TargetKind, 133 pub kind: TargetKind,
134 /// Is this target a proc-macro
110 pub is_proc_macro: bool, 135 pub is_proc_macro: bool,
111} 136}
112 137
@@ -148,6 +173,7 @@ impl CargoWorkspace {
148 pub fn from_cargo_metadata( 173 pub fn from_cargo_metadata(
149 cargo_toml: &AbsPath, 174 cargo_toml: &AbsPath,
150 config: &CargoConfig, 175 config: &CargoConfig,
176 progress: &dyn Fn(String),
151 ) -> Result<CargoWorkspace> { 177 ) -> Result<CargoWorkspace> {
152 let mut meta = MetadataCommand::new(); 178 let mut meta = MetadataCommand::new();
153 meta.cargo_path(toolchain::cargo()); 179 meta.cargo_path(toolchain::cargo());
@@ -196,8 +222,26 @@ impl CargoWorkspace {
196 if let Some(target) = target { 222 if let Some(target) = target {
197 meta.other_options(vec![String::from("--filter-platform"), target]); 223 meta.other_options(vec![String::from("--filter-platform"), target]);
198 } 224 }
225
226 // FIXME: Currently MetadataCommand is not based on parse_stream,
227 // So we just report it as a whole
228 progress("metadata".to_string());
199 let mut meta = meta.exec().with_context(|| { 229 let mut meta = meta.exec().with_context(|| {
200 format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) 230 let cwd: Option<AbsPathBuf> =
231 std::env::current_dir().ok().and_then(|p| p.try_into().ok());
232
233 let workdir = cargo_toml
234 .parent()
235 .map(|p| p.to_path_buf())
236 .or(cwd)
237 .map(|dir| dir.to_string_lossy().to_string())
238 .unwrap_or_else(|| "<failed to get path>".into());
239
240 format!(
241 "Failed to run `cargo metadata --manifest-path {}` in `{}`",
242 cargo_toml.display(),
243 workdir
244 )
201 })?; 245 })?;
202 246
203 let mut out_dir_by_id = FxHashMap::default(); 247 let mut out_dir_by_id = FxHashMap::default();
@@ -205,7 +249,7 @@ impl CargoWorkspace {
205 let mut envs = FxHashMap::default(); 249 let mut envs = FxHashMap::default();
206 let mut proc_macro_dylib_paths = FxHashMap::default(); 250 let mut proc_macro_dylib_paths = FxHashMap::default();
207 if config.load_out_dirs_from_check { 251 if config.load_out_dirs_from_check {
208 let resources = load_extern_resources(cargo_toml, config)?; 252 let resources = load_extern_resources(cargo_toml, config, progress)?;
209 out_dir_by_id = resources.out_dirs; 253 out_dir_by_id = resources.out_dirs;
210 cfgs = resources.cfgs; 254 cfgs = resources.cfgs;
211 envs = resources.env; 255 envs = resources.env;
@@ -330,10 +374,16 @@ pub(crate) struct ExternResources {
330pub(crate) fn load_extern_resources( 374pub(crate) fn load_extern_resources(
331 cargo_toml: &Path, 375 cargo_toml: &Path,
332 cargo_features: &CargoConfig, 376 cargo_features: &CargoConfig,
377 progress: &dyn Fn(String),
333) -> Result<ExternResources> { 378) -> Result<ExternResources> {
334 let mut cmd = Command::new(toolchain::cargo()); 379 let mut cmd = Command::new(toolchain::cargo());
335 cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml); 380 cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml);
336 381
382 // --all-targets includes tests, benches and examples in addition to the
383 // default lib and bins. This is an independent concept from the --targets
384 // flag below.
385 cmd.arg("--all-targets");
386
337 if let Some(target) = &cargo_features.target { 387 if let Some(target) = &cargo_features.target {
338 cmd.args(&["--target", target]); 388 cmd.args(&["--target", target]);
339 } 389 }
@@ -352,11 +402,14 @@ pub(crate) fn load_extern_resources(
352 } 402 }
353 } 403 }
354 404
355 let output = cmd.output()?; 405 cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null());
356 406
357 let mut res = ExternResources::default(); 407 let mut child = cmd.spawn().map(JodChild)?;
408 let child_stdout = child.stdout.take().unwrap();
409 let stdout = BufReader::new(child_stdout);
358 410
359 for message in cargo_metadata::Message::parse_stream(output.stdout.as_slice()) { 411 let mut res = ExternResources::default();
412 for message in cargo_metadata::Message::parse_stream(stdout) {
360 if let Ok(message) = message { 413 if let Ok(message) = message {
361 match message { 414 match message {
362 Message::BuildScriptExecuted(BuildScript { 415 Message::BuildScriptExecuted(BuildScript {
@@ -389,6 +442,8 @@ pub(crate) fn load_extern_resources(
389 res.env.insert(package_id, env); 442 res.env.insert(package_id, env);
390 } 443 }
391 Message::CompilerArtifact(message) => { 444 Message::CompilerArtifact(message) => {
445 progress(format!("metadata {}", message.target.name));
446
392 if message.target.kind.contains(&"proc-macro".to_string()) { 447 if message.target.kind.contains(&"proc-macro".to_string()) {
393 let package_id = message.package_id; 448 let package_id = message.package_id;
394 // Skip rmeta file 449 // Skip rmeta file
@@ -399,7 +454,9 @@ pub(crate) fn load_extern_resources(
399 } 454 }
400 } 455 }
401 } 456 }
402 Message::CompilerMessage(_) => (), 457 Message::CompilerMessage(message) => {
458 progress(message.target.name.clone());
459 }
403 Message::Unknown => (), 460 Message::Unknown => (),
404 Message::BuildFinished(_) => {} 461 Message::BuildFinished(_) => {}
405 Message::TextLine(_) => {} 462 Message::TextLine(_) => {}
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs
index 24aa9b8fa..aabb7a47d 100644
--- a/crates/project_model/src/lib.rs
+++ b/crates/project_model/src/lib.rs
@@ -1,9 +1,9 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3mod cargo_workspace; 3mod cargo_workspace;
4mod cfg_flag;
4mod project_json; 5mod project_json;
5mod sysroot; 6mod sysroot;
6mod cfg_flag;
7mod workspace; 7mod workspace;
8 8
9use std::{ 9use std::{
@@ -17,7 +17,10 @@ use paths::{AbsPath, AbsPathBuf};
17use rustc_hash::FxHashSet; 17use rustc_hash::FxHashSet;
18 18
19pub use crate::{ 19pub use crate::{
20 cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind}, 20 cargo_workspace::{
21 CargoConfig, CargoWorkspace, Package, PackageData, PackageDependency, Target, TargetData,
22 TargetKind,
23 },
21 project_json::{ProjectJson, ProjectJsonData}, 24 project_json::{ProjectJson, ProjectJsonData},
22 sysroot::Sysroot, 25 sysroot::Sysroot,
23 workspace::{PackageRoot, ProjectWorkspace}, 26 workspace::{PackageRoot, ProjectWorkspace},
diff --git a/crates/project_model/src/project_json.rs b/crates/project_model/src/project_json.rs
index aab279223..41a2ac03e 100644
--- a/crates/project_model/src/project_json.rs
+++ b/crates/project_model/src/project_json.rs
@@ -110,13 +110,13 @@ impl ProjectJson {
110 } 110 }
111} 111}
112 112
113#[derive(Deserialize)] 113#[derive(Deserialize, Debug, Clone)]
114pub struct ProjectJsonData { 114pub struct ProjectJsonData {
115 sysroot_src: Option<PathBuf>, 115 sysroot_src: Option<PathBuf>,
116 crates: Vec<CrateData>, 116 crates: Vec<CrateData>,
117} 117}
118 118
119#[derive(Deserialize)] 119#[derive(Deserialize, Debug, Clone)]
120struct CrateData { 120struct CrateData {
121 display_name: Option<String>, 121 display_name: Option<String>,
122 root_module: PathBuf, 122 root_module: PathBuf,
@@ -132,13 +132,15 @@ struct CrateData {
132 source: Option<CrateSource>, 132 source: Option<CrateSource>,
133} 133}
134 134
135#[derive(Deserialize)] 135#[derive(Deserialize, Debug, Clone)]
136#[serde(rename = "edition")] 136#[serde(rename = "edition")]
137enum EditionData { 137enum EditionData {
138 #[serde(rename = "2015")] 138 #[serde(rename = "2015")]
139 Edition2015, 139 Edition2015,
140 #[serde(rename = "2018")] 140 #[serde(rename = "2018")]
141 Edition2018, 141 Edition2018,
142 #[serde(rename = "2021")]
143 Edition2021,
142} 144}
143 145
144impl From<EditionData> for Edition { 146impl From<EditionData> for Edition {
@@ -146,11 +148,12 @@ impl From<EditionData> for Edition {
146 match data { 148 match data {
147 EditionData::Edition2015 => Edition::Edition2015, 149 EditionData::Edition2015 => Edition::Edition2015,
148 EditionData::Edition2018 => Edition::Edition2018, 150 EditionData::Edition2018 => Edition::Edition2018,
151 EditionData::Edition2021 => Edition::Edition2021,
149 } 152 }
150 } 153 }
151} 154}
152 155
153#[derive(Deserialize)] 156#[derive(Deserialize, Debug, Clone)]
154struct DepData { 157struct DepData {
155 /// Identifies a crate by position in the crates array. 158 /// Identifies a crate by position in the crates array.
156 #[serde(rename = "crate")] 159 #[serde(rename = "crate")]
@@ -159,7 +162,7 @@ struct DepData {
159 name: CrateName, 162 name: CrateName,
160} 163}
161 164
162#[derive(Deserialize)] 165#[derive(Deserialize, Debug, Clone)]
163struct CrateSource { 166struct CrateSource {
164 include_dirs: Vec<PathBuf>, 167 include_dirs: Vec<PathBuf>,
165 exclude_dirs: Vec<PathBuf>, 168 exclude_dirs: Vec<PathBuf>,
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 68a235ce3..06a0be284 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -64,7 +64,11 @@ impl fmt::Debug for ProjectWorkspace {
64} 64}
65 65
66impl ProjectWorkspace { 66impl ProjectWorkspace {
67 pub fn load(manifest: ProjectManifest, config: &CargoConfig) -> Result<ProjectWorkspace> { 67 pub fn load(
68 manifest: ProjectManifest,
69 config: &CargoConfig,
70 progress: &dyn Fn(String),
71 ) -> Result<ProjectWorkspace> {
68 let res = match manifest { 72 let res = match manifest {
69 ProjectManifest::ProjectJson(project_json) => { 73 ProjectManifest::ProjectJson(project_json) => {
70 let file = fs::read_to_string(&project_json).with_context(|| { 74 let file = fs::read_to_string(&project_json).with_context(|| {
@@ -84,15 +88,14 @@ impl ProjectWorkspace {
84 cmd 88 cmd
85 })?; 89 })?;
86 90
87 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, config).with_context( 91 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, config, progress)
88 || { 92 .with_context(|| {
89 format!( 93 format!(
90 "Failed to read Cargo metadata from Cargo.toml file {}, {}", 94 "Failed to read Cargo metadata from Cargo.toml file {}, {}",
91 cargo_toml.display(), 95 cargo_toml.display(),
92 cargo_version 96 cargo_version
93 ) 97 )
94 }, 98 })?;
95 )?;
96 let sysroot = if config.no_sysroot { 99 let sysroot = if config.no_sysroot {
97 Sysroot::default() 100 Sysroot::default()
98 } else { 101 } else {
@@ -105,9 +108,12 @@ impl ProjectWorkspace {
105 }; 108 };
106 109
107 let rustc = if let Some(rustc_dir) = &config.rustc_source { 110 let rustc = if let Some(rustc_dir) = &config.rustc_source {
108 Some(CargoWorkspace::from_cargo_metadata(&rustc_dir, config).with_context( 111 Some(
109 || format!("Failed to read Cargo metadata for Rust sources"), 112 CargoWorkspace::from_cargo_metadata(&rustc_dir, config, progress)
110 )?) 113 .with_context(|| {
114 format!("Failed to read Cargo metadata for Rust sources")
115 })?,
116 )
111 } else { 117 } else {
112 None 118 None
113 }; 119 };