aboutsummaryrefslogtreecommitdiff
path: root/crates/project_model
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-01-28 17:43:51 +0000
committerGitHub <[email protected]>2021-01-28 17:43:51 +0000
commit703e6bfdb6d7db87318a8f7c3b5c8c96283ee379 (patch)
tree479c972997987d535c84d8cb03ab0c05d935a280 /crates/project_model
parent00b9b2d6458d20e15e602e659cafe6c09765c5ae (diff)
parentd069ef60b6d46925bebbb7bb0b59953ef38153a2 (diff)
Merge #7412
7412: Async loading for outdir and proc-macro r=maklad a=edwin0cheng cc #7328 ![Peek 2021-01-24 02-04](https://user-images.githubusercontent.com/11014119/105610083-8f208100-5de8-11eb-8e96-c2d4e349b352.gif) [Edit] ~~Finding a way to know when the workspace and build data are loaded...~~ [Edit 2] Not perfect solution, but seem to work now. Co-authored-by: Edwin Cheng <[email protected]>
Diffstat (limited to 'crates/project_model')
-rw-r--r--crates/project_model/src/build_data.rs288
-rw-r--r--crates/project_model/src/cargo_workspace.rs47
-rw-r--r--crates/project_model/src/lib.rs1
-rw-r--r--crates/project_model/src/workspace.rs53
4 files changed, 235 insertions, 154 deletions
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs
index 3ff347e2c..a5c564e0a 100644
--- a/crates/project_model/src/build_data.rs
+++ b/crates/project_model/src/build_data.rs
@@ -5,10 +5,11 @@ use std::{
5 io::BufReader, 5 io::BufReader,
6 path::{Path, PathBuf}, 6 path::{Path, PathBuf},
7 process::{Command, Stdio}, 7 process::{Command, Stdio},
8 sync::Arc,
8}; 9};
9 10
10use anyhow::Result; 11use anyhow::Result;
11use cargo_metadata::{BuildScript, Message, Package, PackageId}; 12use cargo_metadata::{BuildScript, Message};
12use itertools::Itertools; 13use itertools::Itertools;
13use paths::{AbsPath, AbsPathBuf}; 14use paths::{AbsPath, AbsPathBuf};
14use rustc_hash::FxHashMap; 15use rustc_hash::FxHashMap;
@@ -16,150 +17,195 @@ use stdx::JodChild;
16 17
17use crate::{cfg_flag::CfgFlag, CargoConfig}; 18use crate::{cfg_flag::CfgFlag, CargoConfig};
18 19
19#[derive(Debug, Clone, Default)]
20pub(crate) struct BuildDataMap {
21 data: FxHashMap<PackageId, BuildData>,
22}
23#[derive(Debug, Clone, Default, PartialEq, Eq)] 20#[derive(Debug, Clone, Default, PartialEq, Eq)]
24pub struct BuildData { 21pub(crate) struct BuildData {
25 /// List of config flags defined by this package's build script 22 /// List of config flags defined by this package's build script
26 pub cfgs: Vec<CfgFlag>, 23 pub(crate) cfgs: Vec<CfgFlag>,
27 /// List of cargo-related environment variables with their value 24 /// List of cargo-related environment variables with their value
28 /// 25 ///
29 /// If the package has a build script which defines environment variables, 26 /// If the package has a build script which defines environment variables,
30 /// they can also be found here. 27 /// they can also be found here.
31 pub envs: Vec<(String, String)>, 28 pub(crate) envs: Vec<(String, String)>,
32 /// Directory where a build script might place its output 29 /// Directory where a build script might place its output
33 pub out_dir: Option<AbsPathBuf>, 30 pub(crate) out_dir: Option<AbsPathBuf>,
34 /// Path to the proc-macro library file if this package exposes proc-macros 31 /// Path to the proc-macro library file if this package exposes proc-macros
35 pub proc_macro_dylib_path: Option<AbsPathBuf>, 32 pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
36} 33}
37 34
38impl BuildDataMap { 35#[derive(Clone, Debug)]
39 pub(crate) fn new( 36pub(crate) struct BuildDataConfig {
40 cargo_toml: &AbsPath, 37 cargo_toml: AbsPathBuf,
41 cargo_features: &CargoConfig, 38 cargo_features: CargoConfig,
42 packages: &Vec<Package>, 39 packages: Arc<Vec<cargo_metadata::Package>>,
43 progress: &dyn Fn(String), 40}
44 ) -> Result<BuildDataMap> {
45 let mut cmd = Command::new(toolchain::cargo());
46 cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"])
47 .arg(cargo_toml.as_ref());
48
49 // --all-targets includes tests, benches and examples in addition to the
50 // default lib and bins. This is an independent concept from the --targets
51 // flag below.
52 cmd.arg("--all-targets");
53
54 if let Some(target) = &cargo_features.target {
55 cmd.args(&["--target", target]);
56 }
57 41
58 if cargo_features.all_features { 42impl PartialEq for BuildDataConfig {
59 cmd.arg("--all-features"); 43 fn eq(&self, other: &Self) -> bool {
60 } else { 44 Arc::ptr_eq(&self.packages, &other.packages)
61 if cargo_features.no_default_features { 45 }
62 // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` 46}
63 // https://github.com/oli-obk/cargo_metadata/issues/79
64 cmd.arg("--no-default-features");
65 }
66 if !cargo_features.features.is_empty() {
67 cmd.arg("--features");
68 cmd.arg(cargo_features.features.join(" "));
69 }
70 }
71 47
72 cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null()); 48impl Eq for BuildDataConfig {}
73
74 let mut child = cmd.spawn().map(JodChild)?;
75 let child_stdout = child.stdout.take().unwrap();
76 let stdout = BufReader::new(child_stdout);
77
78 let mut res = BuildDataMap::default();
79 for message in cargo_metadata::Message::parse_stream(stdout) {
80 if let Ok(message) = message {
81 match message {
82 Message::BuildScriptExecuted(BuildScript {
83 package_id,
84 out_dir,
85 cfgs,
86 env,
87 ..
88 }) => {
89 let cfgs = {
90 let mut acc = Vec::new();
91 for cfg in cfgs {
92 match cfg.parse::<CfgFlag>() {
93 Ok(it) => acc.push(it),
94 Err(err) => {
95 anyhow::bail!("invalid cfg from cargo-metadata: {}", err)
96 }
97 };
98 }
99 acc
100 };
101 let res = res.data.entry(package_id.clone()).or_default();
102 // cargo_metadata crate returns default (empty) path for
103 // older cargos, which is not absolute, so work around that.
104 if out_dir != PathBuf::default() {
105 let out_dir = AbsPathBuf::assert(out_dir);
106 res.out_dir = Some(out_dir);
107 res.cfgs = cfgs;
108 }
109 49
110 res.envs = env; 50#[derive(Debug, Default)]
111 } 51pub struct BuildDataCollector {
112 Message::CompilerArtifact(message) => { 52 configs: FxHashMap<AbsPathBuf, BuildDataConfig>,
113 progress(format!("metadata {}", message.target.name)); 53}
114 54
115 if message.target.kind.contains(&"proc-macro".to_string()) { 55#[derive(Debug, Default, PartialEq, Eq)]
116 let package_id = message.package_id; 56pub struct BuildDataResult {
117 // Skip rmeta file 57 data: FxHashMap<AbsPathBuf, BuildDataMap>,
118 if let Some(filename) = 58}
119 message.filenames.iter().find(|name| is_dylib(name)) 59
120 { 60pub(crate) type BuildDataMap = FxHashMap<String, BuildData>;
121 let filename = AbsPathBuf::assert(filename.clone()); 61
122 let res = res.data.entry(package_id.clone()).or_default(); 62impl BuildDataCollector {
123 res.proc_macro_dylib_path = Some(filename); 63 pub(crate) fn add_config(&mut self, workspace_root: &AbsPath, config: BuildDataConfig) {
124 } 64 self.configs.insert(workspace_root.to_path_buf().clone(), config);
125 } 65 }
126 } 66
127 Message::CompilerMessage(message) => { 67 pub fn collect(&mut self, progress: &dyn Fn(String)) -> Result<BuildDataResult> {
128 progress(message.target.name.clone()); 68 let mut res = BuildDataResult::default();
129 } 69 for (path, config) in self.configs.iter() {
130 Message::Unknown => (), 70 res.data.insert(
131 Message::BuildFinished(_) => {} 71 path.clone(),
132 Message::TextLine(_) => {} 72 collect_from_workspace(
133 } 73 &config.cargo_toml,
134 } 74 &config.cargo_features,
75 &config.packages,
76 progress,
77 )?,
78 );
135 } 79 }
136 res.inject_cargo_env(packages);
137 Ok(res) 80 Ok(res)
138 } 81 }
82}
83
84impl BuildDataResult {
85 pub(crate) fn get(&self, root: &AbsPath) -> Option<&BuildDataMap> {
86 self.data.get(&root.to_path_buf())
87 }
88}
139 89
140 pub(crate) fn with_cargo_env(packages: &Vec<Package>) -> Self { 90impl BuildDataConfig {
141 let mut res = Self::default(); 91 pub(crate) fn new(
142 res.inject_cargo_env(packages); 92 cargo_toml: AbsPathBuf,
143 res 93 cargo_features: CargoConfig,
94 packages: Arc<Vec<cargo_metadata::Package>>,
95 ) -> Self {
96 Self { cargo_toml, cargo_features, packages }
144 } 97 }
98}
145 99
146 pub(crate) fn get(&self, id: &PackageId) -> Option<&BuildData> { 100fn collect_from_workspace(
147 self.data.get(id) 101 cargo_toml: &AbsPath,
102 cargo_features: &CargoConfig,
103 packages: &Vec<cargo_metadata::Package>,
104 progress: &dyn Fn(String),
105) -> Result<BuildDataMap> {
106 let mut cmd = Command::new(toolchain::cargo());
107 cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"])
108 .arg(cargo_toml.as_ref());
109
110 // --all-targets includes tests, benches and examples in addition to the
111 // default lib and bins. This is an independent concept from the --targets
112 // flag below.
113 cmd.arg("--all-targets");
114
115 if let Some(target) = &cargo_features.target {
116 cmd.args(&["--target", target]);
148 } 117 }
149 118
150 fn inject_cargo_env(&mut self, packages: &Vec<Package>) { 119 if cargo_features.all_features {
151 for meta_pkg in packages { 120 cmd.arg("--all-features");
152 let resource = self.data.entry(meta_pkg.id.clone()).or_default(); 121 } else {
153 inject_cargo_env(meta_pkg, &mut resource.envs); 122 if cargo_features.no_default_features {
123 // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
124 // https://github.com/oli-obk/cargo_metadata/issues/79
125 cmd.arg("--no-default-features");
126 }
127 if !cargo_features.features.is_empty() {
128 cmd.arg("--features");
129 cmd.arg(cargo_features.features.join(" "));
130 }
131 }
132
133 cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null());
154 134
155 if let Some(out_dir) = &resource.out_dir { 135 let mut child = cmd.spawn().map(JodChild)?;
156 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() 136 let child_stdout = child.stdout.take().unwrap();
157 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { 137 let stdout = BufReader::new(child_stdout);
158 resource.envs.push(("OUT_DIR".to_string(), out_dir)); 138
139 let mut res = BuildDataMap::default();
140 for message in cargo_metadata::Message::parse_stream(stdout) {
141 if let Ok(message) = message {
142 match message {
143 Message::BuildScriptExecuted(BuildScript {
144 package_id,
145 out_dir,
146 cfgs,
147 env,
148 ..
149 }) => {
150 let cfgs = {
151 let mut acc = Vec::new();
152 for cfg in cfgs {
153 match cfg.parse::<CfgFlag>() {
154 Ok(it) => acc.push(it),
155 Err(err) => {
156 anyhow::bail!("invalid cfg from cargo-metadata: {}", err)
157 }
158 };
159 }
160 acc
161 };
162 let res = res.entry(package_id.repr.clone()).or_default();
163 // cargo_metadata crate returns default (empty) path for
164 // older cargos, which is not absolute, so work around that.
165 if out_dir != PathBuf::default() {
166 let out_dir = AbsPathBuf::assert(out_dir);
167 res.out_dir = Some(out_dir);
168 res.cfgs = cfgs;
169 }
170
171 res.envs = env;
172 }
173 Message::CompilerArtifact(message) => {
174 progress(format!("metadata {}", message.target.name));
175
176 if message.target.kind.contains(&"proc-macro".to_string()) {
177 let package_id = message.package_id;
178 // Skip rmeta file
179 if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name))
180 {
181 let filename = AbsPathBuf::assert(filename.clone());
182 let res = res.entry(package_id.repr.clone()).or_default();
183 res.proc_macro_dylib_path = Some(filename);
184 }
185 }
186 }
187 Message::CompilerMessage(message) => {
188 progress(message.target.name.clone());
159 } 189 }
190 Message::Unknown => (),
191 Message::BuildFinished(_) => {}
192 Message::TextLine(_) => {}
160 } 193 }
161 } 194 }
162 } 195 }
196
197 for package in packages {
198 let build_data = res.entry(package.id.repr.clone()).or_default();
199 inject_cargo_env(package, build_data);
200 if let Some(out_dir) = &build_data.out_dir {
201 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
202 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
203 build_data.envs.push(("OUT_DIR".to_string(), out_dir));
204 }
205 }
206 }
207
208 Ok(res)
163} 209}
164 210
165// FIXME: File a better way to know if it is a dylib 211// FIXME: File a better way to know if it is a dylib
@@ -173,7 +219,9 @@ fn is_dylib(path: &Path) -> bool {
173/// Recreates the compile-time environment variables that Cargo sets. 219/// Recreates the compile-time environment variables that Cargo sets.
174/// 220///
175/// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates> 221/// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
176fn inject_cargo_env(package: &cargo_metadata::Package, env: &mut Vec<(String, String)>) { 222fn inject_cargo_env(package: &cargo_metadata::Package, build_data: &mut BuildData) {
223 let env = &mut build_data.envs;
224
177 // FIXME: Missing variables: 225 // FIXME: Missing variables:
178 // CARGO_PKG_HOMEPAGE, CARGO_CRATE_NAME, CARGO_BIN_NAME, CARGO_BIN_EXE_<name> 226 // CARGO_PKG_HOMEPAGE, CARGO_CRATE_NAME, CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
179 227
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index c8a5333c4..f47898b9b 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -1,6 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::{convert::TryInto, ops, process::Command}; 3use std::{convert::TryInto, ops, process::Command, sync::Arc};
4 4
5use anyhow::{Context, Result}; 5use anyhow::{Context, Result};
6use base_db::Edition; 6use base_db::Edition;
@@ -9,7 +9,7 @@ use la_arena::{Arena, Idx};
9use paths::{AbsPath, AbsPathBuf}; 9use paths::{AbsPath, AbsPathBuf};
10use rustc_hash::FxHashMap; 10use rustc_hash::FxHashMap;
11 11
12use crate::build_data::{BuildData, BuildDataMap}; 12use crate::build_data::BuildDataConfig;
13use crate::utf8_stdout; 13use crate::utf8_stdout;
14 14
15/// `CargoWorkspace` represents the logical structure of, well, a Cargo 15/// `CargoWorkspace` represents the logical structure of, well, a Cargo
@@ -27,6 +27,7 @@ pub struct CargoWorkspace {
27 packages: Arena<PackageData>, 27 packages: Arena<PackageData>,
28 targets: Arena<TargetData>, 28 targets: Arena<TargetData>,
29 workspace_root: AbsPathBuf, 29 workspace_root: AbsPathBuf,
30 build_data_config: BuildDataConfig,
30} 31}
31 32
32impl ops::Index<Package> for CargoWorkspace { 33impl ops::Index<Package> for CargoWorkspace {
@@ -55,9 +56,6 @@ pub struct CargoConfig {
55 /// This will be ignored if `cargo_all_features` is true. 56 /// This will be ignored if `cargo_all_features` is true.
56 pub features: Vec<String>, 57 pub features: Vec<String>,
57 58
58 /// Runs cargo check on launch to figure out the correct values of OUT_DIR
59 pub load_out_dirs_from_check: bool,
60
61 /// rustc target 59 /// rustc target
62 pub target: Option<String>, 60 pub target: Option<String>,
63 61
@@ -94,8 +92,8 @@ pub struct PackageData {
94 pub features: FxHashMap<String, Vec<String>>, 92 pub features: FxHashMap<String, Vec<String>>,
95 /// List of features enabled on this package 93 /// List of features enabled on this package
96 pub active_features: Vec<String>, 94 pub active_features: Vec<String>,
97 /// Build script related data for this package 95 // String representation of package id
98 pub build_data: BuildData, 96 pub id: String,
99} 97}
100 98
101#[derive(Debug, Clone, Eq, PartialEq)] 99#[derive(Debug, Clone, Eq, PartialEq)]
@@ -228,12 +226,6 @@ impl CargoWorkspace {
228 ) 226 )
229 })?; 227 })?;
230 228
231 let resources = if config.load_out_dirs_from_check {
232 BuildDataMap::new(cargo_toml, config, &meta.packages, progress)?
233 } else {
234 BuildDataMap::with_cargo_env(&meta.packages)
235 };
236
237 let mut pkg_by_id = FxHashMap::default(); 229 let mut pkg_by_id = FxHashMap::default();
238 let mut packages = Arena::default(); 230 let mut packages = Arena::default();
239 let mut targets = Arena::default(); 231 let mut targets = Arena::default();
@@ -241,10 +233,7 @@ impl CargoWorkspace {
241 let ws_members = &meta.workspace_members; 233 let ws_members = &meta.workspace_members;
242 234
243 meta.packages.sort_by(|a, b| a.id.cmp(&b.id)); 235 meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
244 for meta_pkg in meta.packages { 236 for meta_pkg in &meta.packages {
245 let id = meta_pkg.id.clone();
246 let build_data = resources.get(&id).cloned().unwrap_or_default();
247
248 let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } = 237 let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } =
249 meta_pkg; 238 meta_pkg;
250 let is_member = ws_members.contains(&id); 239 let is_member = ws_members.contains(&id);
@@ -252,24 +241,24 @@ impl CargoWorkspace {
252 .parse::<Edition>() 241 .parse::<Edition>()
253 .with_context(|| format!("Failed to parse edition {}", edition))?; 242 .with_context(|| format!("Failed to parse edition {}", edition))?;
254 let pkg = packages.alloc(PackageData { 243 let pkg = packages.alloc(PackageData {
255 name, 244 id: id.repr.clone(),
245 name: name.clone(),
256 version: version.to_string(), 246 version: version.to_string(),
257 manifest: AbsPathBuf::assert(manifest_path), 247 manifest: AbsPathBuf::assert(manifest_path.clone()),
258 targets: Vec::new(), 248 targets: Vec::new(),
259 is_member, 249 is_member,
260 edition, 250 edition,
261 dependencies: Vec::new(), 251 dependencies: Vec::new(),
262 features: meta_pkg.features.into_iter().collect(), 252 features: meta_pkg.features.clone().into_iter().collect(),
263 active_features: Vec::new(), 253 active_features: Vec::new(),
264 build_data,
265 }); 254 });
266 let pkg_data = &mut packages[pkg]; 255 let pkg_data = &mut packages[pkg];
267 pkg_by_id.insert(id, pkg); 256 pkg_by_id.insert(id, pkg);
268 for meta_tgt in meta_pkg.targets { 257 for meta_tgt in &meta_pkg.targets {
269 let is_proc_macro = meta_tgt.kind.as_slice() == ["proc-macro"]; 258 let is_proc_macro = meta_tgt.kind.as_slice() == ["proc-macro"];
270 let tgt = targets.alloc(TargetData { 259 let tgt = targets.alloc(TargetData {
271 package: pkg, 260 package: pkg,
272 name: meta_tgt.name, 261 name: meta_tgt.name.clone(),
273 root: AbsPathBuf::assert(meta_tgt.src_path.clone()), 262 root: AbsPathBuf::assert(meta_tgt.src_path.clone()),
274 kind: TargetKind::new(meta_tgt.kind.as_slice()), 263 kind: TargetKind::new(meta_tgt.kind.as_slice()),
275 is_proc_macro, 264 is_proc_macro,
@@ -308,7 +297,13 @@ impl CargoWorkspace {
308 } 297 }
309 298
310 let workspace_root = AbsPathBuf::assert(meta.workspace_root); 299 let workspace_root = AbsPathBuf::assert(meta.workspace_root);
311 Ok(CargoWorkspace { packages, targets, workspace_root: workspace_root }) 300 let build_data_config = BuildDataConfig::new(
301 cargo_toml.to_path_buf(),
302 config.clone(),
303 Arc::new(meta.packages.clone()),
304 );
305
306 Ok(CargoWorkspace { packages, targets, workspace_root, build_data_config })
312 } 307 }
313 308
314 pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a { 309 pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a {
@@ -334,6 +329,10 @@ impl CargoWorkspace {
334 } 329 }
335 } 330 }
336 331
332 pub(crate) fn build_data_config(&self) -> &BuildDataConfig {
333 &self.build_data_config
334 }
335
337 fn is_unique(&self, name: &str) -> bool { 336 fn is_unique(&self, name: &str) -> bool {
338 self.packages.iter().filter(|(_, v)| v.name == name).count() == 1 337 self.packages.iter().filter(|(_, v)| v.name == name).count() == 1
339 } 338 }
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs
index 525c336e6..d712095a6 100644
--- a/crates/project_model/src/lib.rs
+++ b/crates/project_model/src/lib.rs
@@ -19,6 +19,7 @@ use paths::{AbsPath, AbsPathBuf};
19use rustc_hash::FxHashSet; 19use rustc_hash::FxHashSet;
20 20
21pub use crate::{ 21pub use crate::{
22 build_data::{BuildDataCollector, BuildDataResult},
22 cargo_workspace::{ 23 cargo_workspace::{
23 CargoConfig, CargoWorkspace, Package, PackageData, PackageDependency, Target, TargetData, 24 CargoConfig, CargoWorkspace, Package, PackageData, PackageDependency, Target, TargetData,
24 TargetKind, 25 TargetKind,
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 559f4e7bf..c30861976 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -16,8 +16,13 @@ use proc_macro_api::ProcMacroClient;
16use rustc_hash::{FxHashMap, FxHashSet}; 16use rustc_hash::{FxHashMap, FxHashSet};
17 17
18use crate::{ 18use crate::{
19 cargo_workspace, cfg_flag::CfgFlag, rustc_cfg, sysroot::SysrootCrate, utf8_stdout, CargoConfig, 19 build_data::{BuildData, BuildDataMap, BuildDataResult},
20 CargoWorkspace, ProjectJson, ProjectManifest, Sysroot, TargetKind, 20 cargo_workspace,
21 cfg_flag::CfgFlag,
22 rustc_cfg,
23 sysroot::SysrootCrate,
24 utf8_stdout, BuildDataCollector, CargoConfig, CargoWorkspace, ProjectJson, ProjectManifest,
25 Sysroot, TargetKind,
21}; 26};
22 27
23/// `PackageRoot` describes a package root folder. 28/// `PackageRoot` describes a package root folder.
@@ -153,7 +158,7 @@ impl ProjectWorkspace {
153 /// Returns the roots for the current `ProjectWorkspace` 158 /// Returns the roots for the current `ProjectWorkspace`
154 /// The return type contains the path and whether or not 159 /// The return type contains the path and whether or not
155 /// the root is a member of the current workspace 160 /// the root is a member of the current workspace
156 pub fn to_roots(&self) -> Vec<PackageRoot> { 161 pub fn to_roots(&self, build_data: Option<&BuildDataResult>) -> Vec<PackageRoot> {
157 match self { 162 match self {
158 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project 163 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
159 .crates() 164 .crates()
@@ -179,7 +184,12 @@ impl ProjectWorkspace {
179 let pkg_root = cargo[pkg].root().to_path_buf(); 184 let pkg_root = cargo[pkg].root().to_path_buf();
180 185
181 let mut include = vec![pkg_root.clone()]; 186 let mut include = vec![pkg_root.clone()];
182 include.extend(cargo[pkg].build_data.out_dir.clone()); 187 include.extend(
188 build_data
189 .and_then(|it| it.get(cargo.workspace_root()))
190 .and_then(|map| map.get(&cargo[pkg].id))
191 .and_then(|it| it.out_dir.clone()),
192 );
183 193
184 let mut exclude = vec![pkg_root.join(".git")]; 194 let mut exclude = vec![pkg_root.join(".git")];
185 if is_member { 195 if is_member {
@@ -219,6 +229,7 @@ impl ProjectWorkspace {
219 229
220 pub fn to_crate_graph( 230 pub fn to_crate_graph(
221 &self, 231 &self,
232 build_data: Option<&BuildDataResult>,
222 proc_macro_client: Option<&ProcMacroClient>, 233 proc_macro_client: Option<&ProcMacroClient>,
223 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 234 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
224 ) -> CrateGraph { 235 ) -> CrateGraph {
@@ -241,8 +252,10 @@ impl ProjectWorkspace {
241 &proc_macro_loader, 252 &proc_macro_loader,
242 load, 253 load,
243 cargo, 254 cargo,
255 build_data.and_then(|it| it.get(cargo.workspace_root())),
244 sysroot, 256 sysroot,
245 rustc, 257 rustc,
258 rustc.as_ref().zip(build_data).and_then(|(it, map)| map.get(it.workspace_root())),
246 ), 259 ),
247 }; 260 };
248 if crate_graph.patch_cfg_if() { 261 if crate_graph.patch_cfg_if() {
@@ -252,6 +265,18 @@ impl ProjectWorkspace {
252 } 265 }
253 crate_graph 266 crate_graph
254 } 267 }
268
269 pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) {
270 match self {
271 ProjectWorkspace::Cargo { cargo, rustc, .. } => {
272 collector.add_config(&cargo.workspace_root(), cargo.build_data_config().clone());
273 if let Some(rustc) = rustc {
274 collector.add_config(rustc.workspace_root(), rustc.build_data_config().clone());
275 }
276 }
277 _ => {}
278 }
279 }
255} 280}
256 281
257fn project_json_to_crate_graph( 282fn project_json_to_crate_graph(
@@ -324,8 +349,10 @@ fn cargo_to_crate_graph(
324 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 349 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
325 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 350 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
326 cargo: &CargoWorkspace, 351 cargo: &CargoWorkspace,
352 build_data_map: Option<&BuildDataMap>,
327 sysroot: &Sysroot, 353 sysroot: &Sysroot,
328 rustc: &Option<CargoWorkspace>, 354 rustc: &Option<CargoWorkspace>,
355 rustc_build_data_map: Option<&BuildDataMap>,
329) -> CrateGraph { 356) -> CrateGraph {
330 let _p = profile::span("cargo_to_crate_graph"); 357 let _p = profile::span("cargo_to_crate_graph");
331 let mut crate_graph = CrateGraph::default(); 358 let mut crate_graph = CrateGraph::default();
@@ -351,6 +378,7 @@ fn cargo_to_crate_graph(
351 let crate_id = add_target_crate_root( 378 let crate_id = add_target_crate_root(
352 &mut crate_graph, 379 &mut crate_graph,
353 &cargo[pkg], 380 &cargo[pkg],
381 build_data_map.and_then(|it| it.get(&cargo[pkg].id)),
354 &cfg_options, 382 &cfg_options,
355 proc_macro_loader, 383 proc_macro_loader,
356 file_id, 384 file_id,
@@ -427,6 +455,7 @@ fn cargo_to_crate_graph(
427 let crate_id = add_target_crate_root( 455 let crate_id = add_target_crate_root(
428 &mut crate_graph, 456 &mut crate_graph,
429 &rustc_workspace[pkg], 457 &rustc_workspace[pkg],
458 rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)),
430 &cfg_options, 459 &cfg_options,
431 proc_macro_loader, 460 proc_macro_loader,
432 file_id, 461 file_id,
@@ -475,6 +504,7 @@ fn cargo_to_crate_graph(
475fn add_target_crate_root( 504fn add_target_crate_root(
476 crate_graph: &mut CrateGraph, 505 crate_graph: &mut CrateGraph,
477 pkg: &cargo_workspace::PackageData, 506 pkg: &cargo_workspace::PackageData,
507 build_data: Option<&BuildData>,
478 cfg_options: &CfgOptions, 508 cfg_options: &CfgOptions,
479 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 509 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
480 file_id: FileId, 510 file_id: FileId,
@@ -485,19 +515,22 @@ fn add_target_crate_root(
485 for feature in pkg.active_features.iter() { 515 for feature in pkg.active_features.iter() {
486 opts.insert_key_value("feature".into(), feature.into()); 516 opts.insert_key_value("feature".into(), feature.into());
487 } 517 }
488 opts.extend(pkg.build_data.cfgs.iter().cloned()); 518 if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
519 opts.extend(cfgs.iter().cloned());
520 }
489 opts 521 opts
490 }; 522 };
491 523
492 let mut env = Env::default(); 524 let mut env = Env::default();
493 for (k, v) in &pkg.build_data.envs { 525 if let Some(envs) = build_data.map(|it| &it.envs) {
494 env.set(k, v.clone()); 526 for (k, v) in envs {
527 env.set(k, v.clone());
528 }
495 } 529 }
496 530
497 let proc_macro = pkg 531 let proc_macro = build_data
498 .build_data
499 .proc_macro_dylib_path
500 .as_ref() 532 .as_ref()
533 .and_then(|it| it.proc_macro_dylib_path.as_ref())
501 .map(|it| proc_macro_loader(&it)) 534 .map(|it| proc_macro_loader(&it))
502 .unwrap_or_default(); 535 .unwrap_or_default();
503 536