diff options
Diffstat (limited to 'crates/project_model')
-rw-r--r-- | crates/project_model/src/build_data.rs | 288 | ||||
-rw-r--r-- | crates/project_model/src/cargo_workspace.rs | 47 | ||||
-rw-r--r-- | crates/project_model/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/project_model/src/workspace.rs | 53 |
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 | ||
10 | use anyhow::Result; | 11 | use anyhow::Result; |
11 | use cargo_metadata::{BuildScript, Message, Package, PackageId}; | 12 | use cargo_metadata::{BuildScript, Message}; |
12 | use itertools::Itertools; | 13 | use itertools::Itertools; |
13 | use paths::{AbsPath, AbsPathBuf}; | 14 | use paths::{AbsPath, AbsPathBuf}; |
14 | use rustc_hash::FxHashMap; | 15 | use rustc_hash::FxHashMap; |
@@ -16,150 +17,195 @@ use stdx::JodChild; | |||
16 | 17 | ||
17 | use crate::{cfg_flag::CfgFlag, CargoConfig}; | 18 | use crate::{cfg_flag::CfgFlag, CargoConfig}; |
18 | 19 | ||
19 | #[derive(Debug, Clone, Default)] | ||
20 | pub(crate) struct BuildDataMap { | ||
21 | data: FxHashMap<PackageId, BuildData>, | ||
22 | } | ||
23 | #[derive(Debug, Clone, Default, PartialEq, Eq)] | 20 | #[derive(Debug, Clone, Default, PartialEq, Eq)] |
24 | pub struct BuildData { | 21 | pub(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 | ||
38 | impl BuildDataMap { | 35 | #[derive(Clone, Debug)] |
39 | pub(crate) fn new( | 36 | pub(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 { | 42 | impl 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()); | 48 | impl 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 | } | 51 | pub 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; | 56 | pub 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 | { | 60 | pub(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(); | 62 | impl 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 | |||
84 | impl 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 { | 90 | impl 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> { | 100 | fn 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> |
176 | fn inject_cargo_env(package: &cargo_metadata::Package, env: &mut Vec<(String, String)>) { | 222 | fn 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 | ||
3 | use std::{convert::TryInto, ops, process::Command}; | 3 | use std::{convert::TryInto, ops, process::Command, sync::Arc}; |
4 | 4 | ||
5 | use anyhow::{Context, Result}; | 5 | use anyhow::{Context, Result}; |
6 | use base_db::Edition; | 6 | use base_db::Edition; |
@@ -9,7 +9,7 @@ use la_arena::{Arena, Idx}; | |||
9 | use paths::{AbsPath, AbsPathBuf}; | 9 | use paths::{AbsPath, AbsPathBuf}; |
10 | use rustc_hash::FxHashMap; | 10 | use rustc_hash::FxHashMap; |
11 | 11 | ||
12 | use crate::build_data::{BuildData, BuildDataMap}; | 12 | use crate::build_data::BuildDataConfig; |
13 | use crate::utf8_stdout; | 13 | use 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 | ||
32 | impl ops::Index<Package> for CargoWorkspace { | 33 | impl 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}; | |||
19 | use rustc_hash::FxHashSet; | 19 | use rustc_hash::FxHashSet; |
20 | 20 | ||
21 | pub use crate::{ | 21 | pub 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; | |||
16 | use rustc_hash::{FxHashMap, FxHashSet}; | 16 | use rustc_hash::{FxHashMap, FxHashSet}; |
17 | 17 | ||
18 | use crate::{ | 18 | use 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 | ||
257 | fn project_json_to_crate_graph( | 282 | fn 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( | |||
475 | fn add_target_crate_root( | 504 | fn 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 | ||