diff options
Diffstat (limited to 'crates/project_model')
-rw-r--r-- | crates/project_model/Cargo.toml | 10 | ||||
-rw-r--r-- | crates/project_model/src/build_data.rs | 254 | ||||
-rw-r--r-- | crates/project_model/src/cargo_workspace.rs | 249 | ||||
-rw-r--r-- | crates/project_model/src/lib.rs | 6 | ||||
-rw-r--r-- | crates/project_model/src/sysroot.rs | 40 | ||||
-rw-r--r-- | crates/project_model/src/workspace.rs | 79 |
6 files changed, 399 insertions, 239 deletions
diff --git a/crates/project_model/Cargo.toml b/crates/project_model/Cargo.toml index 293cb5bfe..fe3258332 100644 --- a/crates/project_model/Cargo.toml +++ b/crates/project_model/Cargo.toml | |||
@@ -12,7 +12,7 @@ doctest = false | |||
12 | [dependencies] | 12 | [dependencies] |
13 | log = "0.4.8" | 13 | log = "0.4.8" |
14 | rustc-hash = "1.1.0" | 14 | rustc-hash = "1.1.0" |
15 | cargo_metadata = "0.12.2" | 15 | cargo_metadata = "0.13" |
16 | serde = { version = "1.0.106", features = ["derive"] } | 16 | serde = { version = "1.0.106", features = ["derive"] } |
17 | serde_json = "1.0.48" | 17 | serde_json = "1.0.48" |
18 | anyhow = "1.0.26" | 18 | anyhow = "1.0.26" |
@@ -22,7 +22,7 @@ la-arena = { version = "0.2.0", path = "../../lib/arena" } | |||
22 | cfg = { path = "../cfg", version = "0.0.0" } | 22 | cfg = { path = "../cfg", version = "0.0.0" } |
23 | base_db = { path = "../base_db", version = "0.0.0" } | 23 | base_db = { path = "../base_db", version = "0.0.0" } |
24 | toolchain = { path = "../toolchain", version = "0.0.0" } | 24 | toolchain = { path = "../toolchain", version = "0.0.0" } |
25 | proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" } | 25 | proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" } |
26 | paths = { path = "../paths", version = "0.0.0" } | 26 | paths = { path = "../paths", version = "0.0.0" } |
27 | stdx = { path = "../stdx", version = "0.0.0" } | 27 | stdx = { path = "../stdx", version = "0.0.0" } |
28 | profile = { path = "../profile", version = "0.0.0" } | 28 | profile = { path = "../profile", version = "0.0.0" } |
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs new file mode 100644 index 000000000..728a258ea --- /dev/null +++ b/crates/project_model/src/build_data.rs | |||
@@ -0,0 +1,254 @@ | |||
1 | //! Handles build script specific information | ||
2 | |||
3 | use std::{ | ||
4 | io::BufReader, | ||
5 | path::PathBuf, | ||
6 | process::{Command, Stdio}, | ||
7 | sync::Arc, | ||
8 | }; | ||
9 | |||
10 | use anyhow::Result; | ||
11 | use cargo_metadata::camino::Utf8Path; | ||
12 | use cargo_metadata::{BuildScript, Message}; | ||
13 | use itertools::Itertools; | ||
14 | use paths::{AbsPath, AbsPathBuf}; | ||
15 | use rustc_hash::FxHashMap; | ||
16 | use stdx::JodChild; | ||
17 | |||
18 | use crate::{cfg_flag::CfgFlag, CargoConfig}; | ||
19 | |||
20 | #[derive(Debug, Clone, Default, PartialEq, Eq)] | ||
21 | pub(crate) struct BuildData { | ||
22 | /// List of config flags defined by this package's build script | ||
23 | pub(crate) cfgs: Vec<CfgFlag>, | ||
24 | /// List of cargo-related environment variables with their value | ||
25 | /// | ||
26 | /// If the package has a build script which defines environment variables, | ||
27 | /// they can also be found here. | ||
28 | pub(crate) envs: Vec<(String, String)>, | ||
29 | /// Directory where a build script might place its output | ||
30 | pub(crate) out_dir: Option<AbsPathBuf>, | ||
31 | /// Path to the proc-macro library file if this package exposes proc-macros | ||
32 | pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>, | ||
33 | } | ||
34 | |||
35 | #[derive(Clone, Debug)] | ||
36 | pub(crate) struct BuildDataConfig { | ||
37 | cargo_toml: AbsPathBuf, | ||
38 | cargo_features: CargoConfig, | ||
39 | packages: Arc<Vec<cargo_metadata::Package>>, | ||
40 | } | ||
41 | |||
42 | impl PartialEq for BuildDataConfig { | ||
43 | fn eq(&self, other: &Self) -> bool { | ||
44 | Arc::ptr_eq(&self.packages, &other.packages) | ||
45 | } | ||
46 | } | ||
47 | |||
48 | impl Eq for BuildDataConfig {} | ||
49 | |||
50 | #[derive(Debug, Default)] | ||
51 | pub struct BuildDataCollector { | ||
52 | configs: FxHashMap<AbsPathBuf, BuildDataConfig>, | ||
53 | } | ||
54 | |||
55 | #[derive(Debug, Default, PartialEq, Eq)] | ||
56 | pub struct BuildDataResult { | ||
57 | data: FxHashMap<AbsPathBuf, BuildDataMap>, | ||
58 | } | ||
59 | |||
60 | pub(crate) type BuildDataMap = FxHashMap<String, BuildData>; | ||
61 | |||
62 | impl BuildDataCollector { | ||
63 | pub(crate) fn add_config(&mut self, workspace_root: &AbsPath, config: BuildDataConfig) { | ||
64 | self.configs.insert(workspace_root.to_path_buf(), config); | ||
65 | } | ||
66 | |||
67 | pub fn collect(&mut self, progress: &dyn Fn(String)) -> Result<BuildDataResult> { | ||
68 | let mut res = BuildDataResult::default(); | ||
69 | for (path, config) in self.configs.iter() { | ||
70 | res.data.insert( | ||
71 | path.clone(), | ||
72 | collect_from_workspace( | ||
73 | &config.cargo_toml, | ||
74 | &config.cargo_features, | ||
75 | &config.packages, | ||
76 | progress, | ||
77 | )?, | ||
78 | ); | ||
79 | } | ||
80 | Ok(res) | ||
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 | } | ||
89 | |||
90 | impl BuildDataConfig { | ||
91 | pub(crate) fn new( | ||
92 | cargo_toml: AbsPathBuf, | ||
93 | cargo_features: CargoConfig, | ||
94 | packages: Arc<Vec<cargo_metadata::Package>>, | ||
95 | ) -> Self { | ||
96 | Self { cargo_toml, cargo_features, packages } | ||
97 | } | ||
98 | } | ||
99 | |||
100 | fn collect_from_workspace( | ||
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]); | ||
117 | } | ||
118 | |||
119 | if cargo_features.all_features { | ||
120 | cmd.arg("--all-features"); | ||
121 | } else { | ||
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()); | ||
134 | |||
135 | let mut child = cmd.spawn().map(JodChild)?; | ||
136 | let child_stdout = child.stdout.take().unwrap(); | ||
137 | let stdout = BufReader::new(child_stdout); | ||
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.as_str().is_empty() { | ||
166 | let out_dir = AbsPathBuf::assert(PathBuf::from(out_dir.into_os_string())); | ||
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(PathBuf::from(&filename)); | ||
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()); | ||
189 | } | ||
190 | Message::BuildFinished(_) => {} | ||
191 | Message::TextLine(_) => {} | ||
192 | _ => {} | ||
193 | } | ||
194 | } | ||
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) | ||
209 | } | ||
210 | |||
211 | // FIXME: File a better way to know if it is a dylib | ||
212 | fn is_dylib(path: &Utf8Path) -> bool { | ||
213 | match path.extension().map(|e| e.to_string().to_lowercase()) { | ||
214 | None => false, | ||
215 | Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"), | ||
216 | } | ||
217 | } | ||
218 | |||
219 | /// Recreates the compile-time environment variables that Cargo sets. | ||
220 | /// | ||
221 | /// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates> | ||
222 | fn inject_cargo_env(package: &cargo_metadata::Package, build_data: &mut BuildData) { | ||
223 | let env = &mut build_data.envs; | ||
224 | |||
225 | // FIXME: Missing variables: | ||
226 | // CARGO_PKG_HOMEPAGE, CARGO_CRATE_NAME, CARGO_BIN_NAME, CARGO_BIN_EXE_<name> | ||
227 | |||
228 | let mut manifest_dir = package.manifest_path.clone(); | ||
229 | manifest_dir.pop(); | ||
230 | env.push(("CARGO_MANIFEST_DIR".into(), manifest_dir.into_string())); | ||
231 | |||
232 | // Not always right, but works for common cases. | ||
233 | env.push(("CARGO".into(), "cargo".into())); | ||
234 | |||
235 | env.push(("CARGO_PKG_VERSION".into(), package.version.to_string())); | ||
236 | env.push(("CARGO_PKG_VERSION_MAJOR".into(), package.version.major.to_string())); | ||
237 | env.push(("CARGO_PKG_VERSION_MINOR".into(), package.version.minor.to_string())); | ||
238 | env.push(("CARGO_PKG_VERSION_PATCH".into(), package.version.patch.to_string())); | ||
239 | |||
240 | let pre = package.version.pre.iter().map(|id| id.to_string()).format("."); | ||
241 | env.push(("CARGO_PKG_VERSION_PRE".into(), pre.to_string())); | ||
242 | |||
243 | let authors = package.authors.join(";"); | ||
244 | env.push(("CARGO_PKG_AUTHORS".into(), authors)); | ||
245 | |||
246 | env.push(("CARGO_PKG_NAME".into(), package.name.clone())); | ||
247 | env.push(("CARGO_PKG_DESCRIPTION".into(), package.description.clone().unwrap_or_default())); | ||
248 | //env.push(("CARGO_PKG_HOMEPAGE".into(), package.homepage.clone().unwrap_or_default())); | ||
249 | env.push(("CARGO_PKG_REPOSITORY".into(), package.repository.clone().unwrap_or_default())); | ||
250 | env.push(("CARGO_PKG_LICENSE".into(), package.license.clone().unwrap_or_default())); | ||
251 | |||
252 | let license_file = package.license_file.as_ref().map(|buf| buf.to_string()).unwrap_or_default(); | ||
253 | env.push(("CARGO_PKG_LICENSE_FILE".into(), license_file)); | ||
254 | } | ||
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs index c0ed37fc1..f7241b711 100644 --- a/crates/project_model/src/cargo_workspace.rs +++ b/crates/project_model/src/cargo_workspace.rs | |||
@@ -1,24 +1,16 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use std::{ | 3 | use std::path::PathBuf; |
4 | convert::TryInto, | 4 | use std::{convert::TryInto, ops, process::Command, sync::Arc}; |
5 | ffi::OsStr, | ||
6 | io::BufReader, | ||
7 | ops, | ||
8 | path::{Path, PathBuf}, | ||
9 | process::{Command, Stdio}, | ||
10 | }; | ||
11 | 5 | ||
12 | use anyhow::{Context, Result}; | 6 | use anyhow::{Context, Result}; |
13 | use base_db::Edition; | 7 | use base_db::Edition; |
14 | use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; | 8 | use cargo_metadata::{CargoOpt, MetadataCommand}; |
15 | use itertools::Itertools; | ||
16 | use la_arena::{Arena, Idx}; | 9 | use la_arena::{Arena, Idx}; |
17 | use paths::{AbsPath, AbsPathBuf}; | 10 | use paths::{AbsPath, AbsPathBuf}; |
18 | use rustc_hash::FxHashMap; | 11 | use rustc_hash::FxHashMap; |
19 | use stdx::JodChild; | ||
20 | 12 | ||
21 | use crate::cfg_flag::CfgFlag; | 13 | use crate::build_data::BuildDataConfig; |
22 | use crate::utf8_stdout; | 14 | use crate::utf8_stdout; |
23 | 15 | ||
24 | /// `CargoWorkspace` represents the logical structure of, well, a Cargo | 16 | /// `CargoWorkspace` represents the logical structure of, well, a Cargo |
@@ -36,6 +28,7 @@ pub struct CargoWorkspace { | |||
36 | packages: Arena<PackageData>, | 28 | packages: Arena<PackageData>, |
37 | targets: Arena<TargetData>, | 29 | targets: Arena<TargetData>, |
38 | workspace_root: AbsPathBuf, | 30 | workspace_root: AbsPathBuf, |
31 | build_data_config: BuildDataConfig, | ||
39 | } | 32 | } |
40 | 33 | ||
41 | impl ops::Index<Package> for CargoWorkspace { | 34 | impl ops::Index<Package> for CargoWorkspace { |
@@ -52,6 +45,15 @@ impl ops::Index<Target> for CargoWorkspace { | |||
52 | } | 45 | } |
53 | } | 46 | } |
54 | 47 | ||
48 | /// Describes how to set the rustc source directory. | ||
49 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
50 | pub enum RustcSource { | ||
51 | /// Explicit path for the rustc source directory. | ||
52 | Path(AbsPathBuf), | ||
53 | /// Try to automatically detect where the rustc source directory is. | ||
54 | Discover, | ||
55 | } | ||
56 | |||
55 | #[derive(Default, Clone, Debug, PartialEq, Eq)] | 57 | #[derive(Default, Clone, Debug, PartialEq, Eq)] |
56 | pub struct CargoConfig { | 58 | pub struct CargoConfig { |
57 | /// Do not activate the `default` feature. | 59 | /// Do not activate the `default` feature. |
@@ -64,9 +66,6 @@ pub struct CargoConfig { | |||
64 | /// This will be ignored if `cargo_all_features` is true. | 66 | /// This will be ignored if `cargo_all_features` is true. |
65 | pub features: Vec<String>, | 67 | pub features: Vec<String>, |
66 | 68 | ||
67 | /// Runs cargo check on launch to figure out the correct values of OUT_DIR | ||
68 | pub load_out_dirs_from_check: bool, | ||
69 | |||
70 | /// rustc target | 69 | /// rustc target |
71 | pub target: Option<String>, | 70 | pub target: Option<String>, |
72 | 71 | ||
@@ -75,7 +74,7 @@ pub struct CargoConfig { | |||
75 | pub no_sysroot: bool, | 74 | pub no_sysroot: bool, |
76 | 75 | ||
77 | /// rustc private crate source | 76 | /// rustc private crate source |
78 | pub rustc_source: Option<AbsPathBuf>, | 77 | pub rustc_source: Option<RustcSource>, |
79 | } | 78 | } |
80 | 79 | ||
81 | pub type Package = Idx<PackageData>; | 80 | pub type Package = Idx<PackageData>; |
@@ -99,19 +98,12 @@ pub struct PackageData { | |||
99 | pub dependencies: Vec<PackageDependency>, | 98 | pub dependencies: Vec<PackageDependency>, |
100 | /// Rust edition for this package | 99 | /// Rust edition for this package |
101 | pub edition: Edition, | 100 | pub edition: Edition, |
102 | /// List of features to activate | 101 | /// Features provided by the crate, mapped to the features required by that feature. |
103 | pub features: Vec<String>, | 102 | pub features: FxHashMap<String, Vec<String>>, |
104 | /// List of config flags defined by this package's build script | 103 | /// List of features enabled on this package |
105 | pub cfgs: Vec<CfgFlag>, | 104 | pub active_features: Vec<String>, |
106 | /// List of cargo-related environment variables with their value | 105 | // String representation of package id |
107 | /// | 106 | pub id: String, |
108 | /// If the package has a build script which defines environment variables, | ||
109 | /// they can also be found here. | ||
110 | pub envs: Vec<(String, String)>, | ||
111 | /// Directory where a build script might place its output | ||
112 | pub out_dir: Option<AbsPathBuf>, | ||
113 | /// Path to the proc-macro library file if this package exposes proc-macros | ||
114 | pub proc_macro_dylib_path: Option<AbsPathBuf>, | ||
115 | } | 107 | } |
116 | 108 | ||
117 | #[derive(Debug, Clone, Eq, PartialEq)] | 109 | #[derive(Debug, Clone, Eq, PartialEq)] |
@@ -244,18 +236,6 @@ impl CargoWorkspace { | |||
244 | ) | 236 | ) |
245 | })?; | 237 | })?; |
246 | 238 | ||
247 | let mut out_dir_by_id = FxHashMap::default(); | ||
248 | let mut cfgs = FxHashMap::default(); | ||
249 | let mut envs = FxHashMap::default(); | ||
250 | let mut proc_macro_dylib_paths = FxHashMap::default(); | ||
251 | if config.load_out_dirs_from_check { | ||
252 | let resources = load_extern_resources(cargo_toml, config, progress)?; | ||
253 | out_dir_by_id = resources.out_dirs; | ||
254 | cfgs = resources.cfgs; | ||
255 | envs = resources.env; | ||
256 | proc_macro_dylib_paths = resources.proc_dylib_paths; | ||
257 | } | ||
258 | |||
259 | let mut pkg_by_id = FxHashMap::default(); | 239 | let mut pkg_by_id = FxHashMap::default(); |
260 | let mut packages = Arena::default(); | 240 | let mut packages = Arena::default(); |
261 | let mut targets = Arena::default(); | 241 | let mut targets = Arena::default(); |
@@ -263,38 +243,34 @@ impl CargoWorkspace { | |||
263 | let ws_members = &meta.workspace_members; | 243 | let ws_members = &meta.workspace_members; |
264 | 244 | ||
265 | meta.packages.sort_by(|a, b| a.id.cmp(&b.id)); | 245 | meta.packages.sort_by(|a, b| a.id.cmp(&b.id)); |
266 | for meta_pkg in meta.packages { | 246 | for meta_pkg in &meta.packages { |
267 | let id = meta_pkg.id.clone(); | ||
268 | inject_cargo_env(&meta_pkg, envs.entry(id).or_default()); | ||
269 | |||
270 | let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } = | 247 | let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } = |
271 | meta_pkg; | 248 | meta_pkg; |
272 | let is_member = ws_members.contains(&id); | 249 | let is_member = ws_members.contains(&id); |
273 | let edition = edition | 250 | let edition = edition |
274 | .parse::<Edition>() | 251 | .parse::<Edition>() |
275 | .with_context(|| format!("Failed to parse edition {}", edition))?; | 252 | .with_context(|| format!("Failed to parse edition {}", edition))?; |
253 | |||
276 | let pkg = packages.alloc(PackageData { | 254 | let pkg = packages.alloc(PackageData { |
277 | name, | 255 | id: id.repr.clone(), |
256 | name: name.clone(), | ||
278 | version: version.to_string(), | 257 | version: version.to_string(), |
279 | manifest: AbsPathBuf::assert(manifest_path), | 258 | manifest: AbsPathBuf::assert(PathBuf::from(&manifest_path)), |
280 | targets: Vec::new(), | 259 | targets: Vec::new(), |
281 | is_member, | 260 | is_member, |
282 | edition, | 261 | edition, |
283 | dependencies: Vec::new(), | 262 | dependencies: Vec::new(), |
284 | features: Vec::new(), | 263 | features: meta_pkg.features.clone().into_iter().collect(), |
285 | cfgs: cfgs.get(&id).cloned().unwrap_or_default(), | 264 | active_features: Vec::new(), |
286 | envs: envs.get(&id).cloned().unwrap_or_default(), | ||
287 | out_dir: out_dir_by_id.get(&id).cloned(), | ||
288 | proc_macro_dylib_path: proc_macro_dylib_paths.get(&id).cloned(), | ||
289 | }); | 265 | }); |
290 | let pkg_data = &mut packages[pkg]; | 266 | let pkg_data = &mut packages[pkg]; |
291 | pkg_by_id.insert(id, pkg); | 267 | pkg_by_id.insert(id, pkg); |
292 | for meta_tgt in meta_pkg.targets { | 268 | for meta_tgt in &meta_pkg.targets { |
293 | let is_proc_macro = meta_tgt.kind.as_slice() == ["proc-macro"]; | 269 | let is_proc_macro = meta_tgt.kind.as_slice() == ["proc-macro"]; |
294 | let tgt = targets.alloc(TargetData { | 270 | let tgt = targets.alloc(TargetData { |
295 | package: pkg, | 271 | package: pkg, |
296 | name: meta_tgt.name, | 272 | name: meta_tgt.name.clone(), |
297 | root: AbsPathBuf::assert(meta_tgt.src_path.clone()), | 273 | root: AbsPathBuf::assert(PathBuf::from(&meta_tgt.src_path)), |
298 | kind: TargetKind::new(meta_tgt.kind.as_slice()), | 274 | kind: TargetKind::new(meta_tgt.kind.as_slice()), |
299 | is_proc_macro, | 275 | is_proc_macro, |
300 | }); | 276 | }); |
@@ -328,11 +304,18 @@ impl CargoWorkspace { | |||
328 | let dep = PackageDependency { name: dep_node.name, pkg }; | 304 | let dep = PackageDependency { name: dep_node.name, pkg }; |
329 | packages[source].dependencies.push(dep); | 305 | packages[source].dependencies.push(dep); |
330 | } | 306 | } |
331 | packages[source].features.extend(node.features); | 307 | packages[source].active_features.extend(node.features); |
332 | } | 308 | } |
333 | 309 | ||
334 | let workspace_root = AbsPathBuf::assert(meta.workspace_root); | 310 | let workspace_root = |
335 | Ok(CargoWorkspace { packages, targets, workspace_root: workspace_root }) | 311 | AbsPathBuf::assert(PathBuf::from(meta.workspace_root.into_os_string())); |
312 | let build_data_config = BuildDataConfig::new( | ||
313 | cargo_toml.to_path_buf(), | ||
314 | config.clone(), | ||
315 | Arc::new(meta.packages.clone()), | ||
316 | ); | ||
317 | |||
318 | Ok(CargoWorkspace { packages, targets, workspace_root, build_data_config }) | ||
336 | } | 319 | } |
337 | 320 | ||
338 | pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a { | 321 | pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a { |
@@ -358,153 +341,11 @@ impl CargoWorkspace { | |||
358 | } | 341 | } |
359 | } | 342 | } |
360 | 343 | ||
361 | fn is_unique(&self, name: &str) -> bool { | 344 | pub(crate) fn build_data_config(&self) -> &BuildDataConfig { |
362 | self.packages.iter().filter(|(_, v)| v.name == name).count() == 1 | 345 | &self.build_data_config |
363 | } | ||
364 | } | ||
365 | |||
366 | #[derive(Debug, Clone, Default)] | ||
367 | pub(crate) struct ExternResources { | ||
368 | out_dirs: FxHashMap<PackageId, AbsPathBuf>, | ||
369 | proc_dylib_paths: FxHashMap<PackageId, AbsPathBuf>, | ||
370 | cfgs: FxHashMap<PackageId, Vec<CfgFlag>>, | ||
371 | env: FxHashMap<PackageId, Vec<(String, String)>>, | ||
372 | } | ||
373 | |||
374 | pub(crate) fn load_extern_resources( | ||
375 | cargo_toml: &Path, | ||
376 | cargo_features: &CargoConfig, | ||
377 | progress: &dyn Fn(String), | ||
378 | ) -> Result<ExternResources> { | ||
379 | let mut cmd = Command::new(toolchain::cargo()); | ||
380 | cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"]).arg(cargo_toml); | ||
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 | |||
387 | if let Some(target) = &cargo_features.target { | ||
388 | cmd.args(&["--target", target]); | ||
389 | } | ||
390 | |||
391 | if cargo_features.all_features { | ||
392 | cmd.arg("--all-features"); | ||
393 | } else { | ||
394 | if cargo_features.no_default_features { | ||
395 | // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` | ||
396 | // https://github.com/oli-obk/cargo_metadata/issues/79 | ||
397 | cmd.arg("--no-default-features"); | ||
398 | } | ||
399 | if !cargo_features.features.is_empty() { | ||
400 | cmd.arg("--features"); | ||
401 | cmd.arg(cargo_features.features.join(" ")); | ||
402 | } | ||
403 | } | ||
404 | |||
405 | cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null()); | ||
406 | |||
407 | let mut child = cmd.spawn().map(JodChild)?; | ||
408 | let child_stdout = child.stdout.take().unwrap(); | ||
409 | let stdout = BufReader::new(child_stdout); | ||
410 | |||
411 | let mut res = ExternResources::default(); | ||
412 | for message in cargo_metadata::Message::parse_stream(stdout) { | ||
413 | if let Ok(message) = message { | ||
414 | match message { | ||
415 | Message::BuildScriptExecuted(BuildScript { | ||
416 | package_id, | ||
417 | out_dir, | ||
418 | cfgs, | ||
419 | env, | ||
420 | .. | ||
421 | }) => { | ||
422 | let cfgs = { | ||
423 | let mut acc = Vec::new(); | ||
424 | for cfg in cfgs { | ||
425 | match cfg.parse::<CfgFlag>() { | ||
426 | Ok(it) => acc.push(it), | ||
427 | Err(err) => { | ||
428 | anyhow::bail!("invalid cfg from cargo-metadata: {}", err) | ||
429 | } | ||
430 | }; | ||
431 | } | ||
432 | acc | ||
433 | }; | ||
434 | // cargo_metadata crate returns default (empty) path for | ||
435 | // older cargos, which is not absolute, so work around that. | ||
436 | if out_dir != PathBuf::default() { | ||
437 | let out_dir = AbsPathBuf::assert(out_dir); | ||
438 | res.out_dirs.insert(package_id.clone(), out_dir); | ||
439 | res.cfgs.insert(package_id.clone(), cfgs); | ||
440 | } | ||
441 | |||
442 | res.env.insert(package_id, env); | ||
443 | } | ||
444 | Message::CompilerArtifact(message) => { | ||
445 | progress(format!("metadata {}", message.target.name)); | ||
446 | |||
447 | if message.target.kind.contains(&"proc-macro".to_string()) { | ||
448 | let package_id = message.package_id; | ||
449 | // Skip rmeta file | ||
450 | if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) | ||
451 | { | ||
452 | let filename = AbsPathBuf::assert(filename.clone()); | ||
453 | res.proc_dylib_paths.insert(package_id, filename); | ||
454 | } | ||
455 | } | ||
456 | } | ||
457 | Message::CompilerMessage(message) => { | ||
458 | progress(message.target.name.clone()); | ||
459 | } | ||
460 | Message::Unknown => (), | ||
461 | Message::BuildFinished(_) => {} | ||
462 | Message::TextLine(_) => {} | ||
463 | } | ||
464 | } | ||
465 | } | 346 | } |
466 | Ok(res) | ||
467 | } | ||
468 | 347 | ||
469 | // FIXME: File a better way to know if it is a dylib | 348 | fn is_unique(&self, name: &str) -> bool { |
470 | fn is_dylib(path: &Path) -> bool { | 349 | self.packages.iter().filter(|(_, v)| v.name == name).count() == 1 |
471 | match path.extension().and_then(OsStr::to_str).map(|it| it.to_string().to_lowercase()) { | ||
472 | None => false, | ||
473 | Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"), | ||
474 | } | ||
475 | } | ||
476 | |||
477 | /// Recreates the compile-time environment variables that Cargo sets. | ||
478 | /// | ||
479 | /// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates> | ||
480 | fn inject_cargo_env(package: &cargo_metadata::Package, env: &mut Vec<(String, String)>) { | ||
481 | // FIXME: Missing variables: | ||
482 | // CARGO, CARGO_PKG_HOMEPAGE, CARGO_CRATE_NAME, CARGO_BIN_NAME, CARGO_BIN_EXE_<name> | ||
483 | |||
484 | let mut manifest_dir = package.manifest_path.clone(); | ||
485 | manifest_dir.pop(); | ||
486 | if let Some(cargo_manifest_dir) = manifest_dir.to_str() { | ||
487 | env.push(("CARGO_MANIFEST_DIR".into(), cargo_manifest_dir.into())); | ||
488 | } | 350 | } |
489 | |||
490 | env.push(("CARGO_PKG_VERSION".into(), package.version.to_string())); | ||
491 | env.push(("CARGO_PKG_VERSION_MAJOR".into(), package.version.major.to_string())); | ||
492 | env.push(("CARGO_PKG_VERSION_MINOR".into(), package.version.minor.to_string())); | ||
493 | env.push(("CARGO_PKG_VERSION_PATCH".into(), package.version.patch.to_string())); | ||
494 | |||
495 | let pre = package.version.pre.iter().map(|id| id.to_string()).format("."); | ||
496 | env.push(("CARGO_PKG_VERSION_PRE".into(), pre.to_string())); | ||
497 | |||
498 | let authors = package.authors.join(";"); | ||
499 | env.push(("CARGO_PKG_AUTHORS".into(), authors)); | ||
500 | |||
501 | env.push(("CARGO_PKG_NAME".into(), package.name.clone())); | ||
502 | env.push(("CARGO_PKG_DESCRIPTION".into(), package.description.clone().unwrap_or_default())); | ||
503 | //env.push(("CARGO_PKG_HOMEPAGE".into(), package.homepage.clone().unwrap_or_default())); | ||
504 | env.push(("CARGO_PKG_REPOSITORY".into(), package.repository.clone().unwrap_or_default())); | ||
505 | env.push(("CARGO_PKG_LICENSE".into(), package.license.clone().unwrap_or_default())); | ||
506 | |||
507 | let license_file = | ||
508 | package.license_file.as_ref().map(|buf| buf.display().to_string()).unwrap_or_default(); | ||
509 | env.push(("CARGO_PKG_LICENSE_FILE".into(), license_file)); | ||
510 | } | 351 | } |
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs index 970a7e140..a5b35ed95 100644 --- a/crates/project_model/src/lib.rs +++ b/crates/project_model/src/lib.rs | |||
@@ -6,6 +6,7 @@ mod project_json; | |||
6 | mod sysroot; | 6 | mod sysroot; |
7 | mod workspace; | 7 | mod workspace; |
8 | mod rustc_cfg; | 8 | mod rustc_cfg; |
9 | mod build_data; | ||
9 | 10 | ||
10 | use std::{ | 11 | use std::{ |
11 | fs::{read_dir, ReadDir}, | 12 | fs::{read_dir, ReadDir}, |
@@ -18,9 +19,10 @@ use paths::{AbsPath, AbsPathBuf}; | |||
18 | use rustc_hash::FxHashSet; | 19 | use rustc_hash::FxHashSet; |
19 | 20 | ||
20 | pub use crate::{ | 21 | pub use crate::{ |
22 | build_data::{BuildDataCollector, BuildDataResult}, | ||
21 | cargo_workspace::{ | 23 | cargo_workspace::{ |
22 | CargoConfig, CargoWorkspace, Package, PackageData, PackageDependency, Target, TargetData, | 24 | CargoConfig, CargoWorkspace, Package, PackageData, PackageDependency, RustcSource, Target, |
23 | TargetKind, | 25 | TargetData, TargetKind, |
24 | }, | 26 | }, |
25 | project_json::{ProjectJson, ProjectJsonData}, | 27 | project_json::{ProjectJson, ProjectJsonData}, |
26 | sysroot::Sysroot, | 28 | sysroot::Sysroot, |
diff --git a/crates/project_model/src/sysroot.rs b/crates/project_model/src/sysroot.rs index ff44dae4a..3b0ff506d 100644 --- a/crates/project_model/src/sysroot.rs +++ b/crates/project_model/src/sysroot.rs | |||
@@ -51,11 +51,18 @@ impl Sysroot { | |||
51 | pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> { | 51 | pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> { |
52 | log::debug!("Discovering sysroot for {}", cargo_toml.display()); | 52 | log::debug!("Discovering sysroot for {}", cargo_toml.display()); |
53 | let current_dir = cargo_toml.parent().unwrap(); | 53 | let current_dir = cargo_toml.parent().unwrap(); |
54 | let sysroot_src_dir = discover_sysroot_src_dir(current_dir)?; | 54 | let sysroot_dir = discover_sysroot_dir(current_dir)?; |
55 | let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, current_dir)?; | ||
55 | let res = Sysroot::load(&sysroot_src_dir)?; | 56 | let res = Sysroot::load(&sysroot_src_dir)?; |
56 | Ok(res) | 57 | Ok(res) |
57 | } | 58 | } |
58 | 59 | ||
60 | pub fn discover_rustc(cargo_toml: &AbsPath) -> Option<AbsPathBuf> { | ||
61 | log::debug!("Discovering rustc source for {}", cargo_toml.display()); | ||
62 | let current_dir = cargo_toml.parent().unwrap(); | ||
63 | discover_sysroot_dir(current_dir).ok().and_then(|sysroot_dir| get_rustc_src(&sysroot_dir)) | ||
64 | } | ||
65 | |||
59 | pub fn load(sysroot_src_dir: &AbsPath) -> Result<Sysroot> { | 66 | pub fn load(sysroot_src_dir: &AbsPath) -> Result<Sysroot> { |
60 | let mut sysroot = Sysroot { crates: Arena::default() }; | 67 | let mut sysroot = Sysroot { crates: Arena::default() }; |
61 | 68 | ||
@@ -110,7 +117,18 @@ impl Sysroot { | |||
110 | } | 117 | } |
111 | } | 118 | } |
112 | 119 | ||
113 | fn discover_sysroot_src_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> { | 120 | fn discover_sysroot_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> { |
121 | let mut rustc = Command::new(toolchain::rustc()); | ||
122 | rustc.current_dir(current_dir).args(&["--print", "sysroot"]); | ||
123 | log::debug!("Discovering sysroot by {:?}", rustc); | ||
124 | let stdout = utf8_stdout(rustc)?; | ||
125 | Ok(AbsPathBuf::assert(PathBuf::from(stdout))) | ||
126 | } | ||
127 | |||
128 | fn discover_sysroot_src_dir( | ||
129 | sysroot_path: &AbsPathBuf, | ||
130 | current_dir: &AbsPath, | ||
131 | ) -> Result<AbsPathBuf> { | ||
114 | if let Ok(path) = env::var("RUST_SRC_PATH") { | 132 | if let Ok(path) = env::var("RUST_SRC_PATH") { |
115 | let path = AbsPathBuf::try_from(path.as_str()) | 133 | let path = AbsPathBuf::try_from(path.as_str()) |
116 | .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?; | 134 | .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?; |
@@ -122,14 +140,6 @@ fn discover_sysroot_src_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> { | |||
122 | log::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core); | 140 | log::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core); |
123 | } | 141 | } |
124 | 142 | ||
125 | let sysroot_path = { | ||
126 | let mut rustc = Command::new(toolchain::rustc()); | ||
127 | rustc.current_dir(current_dir).args(&["--print", "sysroot"]); | ||
128 | log::debug!("Discovering sysroot by {:?}", rustc); | ||
129 | let stdout = utf8_stdout(rustc)?; | ||
130 | AbsPathBuf::assert(PathBuf::from(stdout)) | ||
131 | }; | ||
132 | |||
133 | get_rust_src(&sysroot_path) | 143 | get_rust_src(&sysroot_path) |
134 | .or_else(|| { | 144 | .or_else(|| { |
135 | let mut rustup = Command::new(toolchain::rustup()); | 145 | let mut rustup = Command::new(toolchain::rustup()); |
@@ -149,6 +159,16 @@ try installing the Rust source the same way you installed rustc", | |||
149 | }) | 159 | }) |
150 | } | 160 | } |
151 | 161 | ||
162 | fn get_rustc_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> { | ||
163 | let rustc_src = sysroot_path.join("lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml"); | ||
164 | log::debug!("Checking for rustc source code: {}", rustc_src.display()); | ||
165 | if rustc_src.exists() { | ||
166 | Some(rustc_src) | ||
167 | } else { | ||
168 | None | ||
169 | } | ||
170 | } | ||
171 | |||
152 | fn get_rust_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> { | 172 | fn get_rust_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> { |
153 | // Try the new path first since the old one still exists. | 173 | // Try the new path first since the old one still exists. |
154 | let rust_src = sysroot_path.join("lib/rustlib/src/rust"); | 174 | let rust_src = sysroot_path.join("lib/rustlib/src/rust"); |
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs index 8e0481ae9..0220efdb4 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. |
@@ -51,6 +56,7 @@ pub enum ProjectWorkspace { | |||
51 | 56 | ||
52 | impl fmt::Debug for ProjectWorkspace { | 57 | impl fmt::Debug for ProjectWorkspace { |
53 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 58 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
59 | // Make sure this isn't too verbose. | ||
54 | match self { | 60 | match self { |
55 | ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f | 61 | ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f |
56 | .debug_struct("Cargo") | 62 | .debug_struct("Cargo") |
@@ -60,7 +66,7 @@ impl fmt::Debug for ProjectWorkspace { | |||
60 | "n_rustc_compiler_crates", | 66 | "n_rustc_compiler_crates", |
61 | &rustc.as_ref().map_or(0, |rc| rc.packages().len()), | 67 | &rustc.as_ref().map_or(0, |rc| rc.packages().len()), |
62 | ) | 68 | ) |
63 | .field("rustc_cfg", rustc_cfg) | 69 | .field("n_rustc_cfg", &rustc_cfg.len()) |
64 | .finish(), | 70 | .finish(), |
65 | ProjectWorkspace::Json { project, sysroot, rustc_cfg } => { | 71 | ProjectWorkspace::Json { project, sysroot, rustc_cfg } => { |
66 | let mut debug_struct = f.debug_struct("Json"); | 72 | let mut debug_struct = f.debug_struct("Json"); |
@@ -68,7 +74,7 @@ impl fmt::Debug for ProjectWorkspace { | |||
68 | if let Some(sysroot) = sysroot { | 74 | if let Some(sysroot) = sysroot { |
69 | debug_struct.field("n_sysroot_crates", &sysroot.crates().len()); | 75 | debug_struct.field("n_sysroot_crates", &sysroot.crates().len()); |
70 | } | 76 | } |
71 | debug_struct.field("rustc_cfg", rustc_cfg); | 77 | debug_struct.field("n_rustc_cfg", &rustc_cfg.len()); |
72 | debug_struct.finish() | 78 | debug_struct.finish() |
73 | } | 79 | } |
74 | } | 80 | } |
@@ -108,6 +114,7 @@ impl ProjectWorkspace { | |||
108 | cargo_version | 114 | cargo_version |
109 | ) | 115 | ) |
110 | })?; | 116 | })?; |
117 | |||
111 | let sysroot = if config.no_sysroot { | 118 | let sysroot = if config.no_sysroot { |
112 | Sysroot::default() | 119 | Sysroot::default() |
113 | } else { | 120 | } else { |
@@ -119,7 +126,17 @@ impl ProjectWorkspace { | |||
119 | })? | 126 | })? |
120 | }; | 127 | }; |
121 | 128 | ||
122 | let rustc = if let Some(rustc_dir) = &config.rustc_source { | 129 | let rustc_dir = if let Some(rustc_source) = &config.rustc_source { |
130 | use cargo_workspace::RustcSource; | ||
131 | match rustc_source { | ||
132 | RustcSource::Path(path) => Some(path.clone()), | ||
133 | RustcSource::Discover => Sysroot::discover_rustc(&cargo_toml), | ||
134 | } | ||
135 | } else { | ||
136 | None | ||
137 | }; | ||
138 | |||
139 | let rustc = if let Some(rustc_dir) = rustc_dir { | ||
123 | Some( | 140 | Some( |
124 | CargoWorkspace::from_cargo_metadata(&rustc_dir, config, progress) | 141 | CargoWorkspace::from_cargo_metadata(&rustc_dir, config, progress) |
125 | .with_context(|| { | 142 | .with_context(|| { |
@@ -152,7 +169,7 @@ impl ProjectWorkspace { | |||
152 | /// Returns the roots for the current `ProjectWorkspace` | 169 | /// Returns the roots for the current `ProjectWorkspace` |
153 | /// The return type contains the path and whether or not | 170 | /// The return type contains the path and whether or not |
154 | /// the root is a member of the current workspace | 171 | /// the root is a member of the current workspace |
155 | pub fn to_roots(&self) -> Vec<PackageRoot> { | 172 | pub fn to_roots(&self, build_data: Option<&BuildDataResult>) -> Vec<PackageRoot> { |
156 | match self { | 173 | match self { |
157 | ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project | 174 | ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project |
158 | .crates() | 175 | .crates() |
@@ -178,7 +195,12 @@ impl ProjectWorkspace { | |||
178 | let pkg_root = cargo[pkg].root().to_path_buf(); | 195 | let pkg_root = cargo[pkg].root().to_path_buf(); |
179 | 196 | ||
180 | let mut include = vec![pkg_root.clone()]; | 197 | let mut include = vec![pkg_root.clone()]; |
181 | include.extend(cargo[pkg].out_dir.clone()); | 198 | include.extend( |
199 | build_data | ||
200 | .and_then(|it| it.get(cargo.workspace_root())) | ||
201 | .and_then(|map| map.get(&cargo[pkg].id)) | ||
202 | .and_then(|it| it.out_dir.clone()), | ||
203 | ); | ||
182 | 204 | ||
183 | let mut exclude = vec![pkg_root.join(".git")]; | 205 | let mut exclude = vec![pkg_root.join(".git")]; |
184 | if is_member { | 206 | if is_member { |
@@ -218,6 +240,7 @@ impl ProjectWorkspace { | |||
218 | 240 | ||
219 | pub fn to_crate_graph( | 241 | pub fn to_crate_graph( |
220 | &self, | 242 | &self, |
243 | build_data: Option<&BuildDataResult>, | ||
221 | proc_macro_client: Option<&ProcMacroClient>, | 244 | proc_macro_client: Option<&ProcMacroClient>, |
222 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, | 245 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, |
223 | ) -> CrateGraph { | 246 | ) -> CrateGraph { |
@@ -240,8 +263,10 @@ impl ProjectWorkspace { | |||
240 | &proc_macro_loader, | 263 | &proc_macro_loader, |
241 | load, | 264 | load, |
242 | cargo, | 265 | cargo, |
266 | build_data.and_then(|it| it.get(cargo.workspace_root())), | ||
243 | sysroot, | 267 | sysroot, |
244 | rustc, | 268 | rustc, |
269 | rustc.as_ref().zip(build_data).and_then(|(it, map)| map.get(it.workspace_root())), | ||
245 | ), | 270 | ), |
246 | }; | 271 | }; |
247 | if crate_graph.patch_cfg_if() { | 272 | if crate_graph.patch_cfg_if() { |
@@ -251,6 +276,18 @@ impl ProjectWorkspace { | |||
251 | } | 276 | } |
252 | crate_graph | 277 | crate_graph |
253 | } | 278 | } |
279 | |||
280 | pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) { | ||
281 | match self { | ||
282 | ProjectWorkspace::Cargo { cargo, rustc, .. } => { | ||
283 | collector.add_config(&cargo.workspace_root(), cargo.build_data_config().clone()); | ||
284 | if let Some(rustc) = rustc { | ||
285 | collector.add_config(rustc.workspace_root(), rustc.build_data_config().clone()); | ||
286 | } | ||
287 | } | ||
288 | _ => {} | ||
289 | } | ||
290 | } | ||
254 | } | 291 | } |
255 | 292 | ||
256 | fn project_json_to_crate_graph( | 293 | fn project_json_to_crate_graph( |
@@ -323,8 +360,10 @@ fn cargo_to_crate_graph( | |||
323 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, | 360 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, |
324 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, | 361 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, |
325 | cargo: &CargoWorkspace, | 362 | cargo: &CargoWorkspace, |
363 | build_data_map: Option<&BuildDataMap>, | ||
326 | sysroot: &Sysroot, | 364 | sysroot: &Sysroot, |
327 | rustc: &Option<CargoWorkspace>, | 365 | rustc: &Option<CargoWorkspace>, |
366 | rustc_build_data_map: Option<&BuildDataMap>, | ||
328 | ) -> CrateGraph { | 367 | ) -> CrateGraph { |
329 | let _p = profile::span("cargo_to_crate_graph"); | 368 | let _p = profile::span("cargo_to_crate_graph"); |
330 | let mut crate_graph = CrateGraph::default(); | 369 | let mut crate_graph = CrateGraph::default(); |
@@ -350,6 +389,7 @@ fn cargo_to_crate_graph( | |||
350 | let crate_id = add_target_crate_root( | 389 | let crate_id = add_target_crate_root( |
351 | &mut crate_graph, | 390 | &mut crate_graph, |
352 | &cargo[pkg], | 391 | &cargo[pkg], |
392 | build_data_map.and_then(|it| it.get(&cargo[pkg].id)), | ||
353 | &cfg_options, | 393 | &cfg_options, |
354 | proc_macro_loader, | 394 | proc_macro_loader, |
355 | file_id, | 395 | file_id, |
@@ -426,6 +466,7 @@ fn cargo_to_crate_graph( | |||
426 | let crate_id = add_target_crate_root( | 466 | let crate_id = add_target_crate_root( |
427 | &mut crate_graph, | 467 | &mut crate_graph, |
428 | &rustc_workspace[pkg], | 468 | &rustc_workspace[pkg], |
469 | rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)), | ||
429 | &cfg_options, | 470 | &cfg_options, |
430 | proc_macro_loader, | 471 | proc_macro_loader, |
431 | file_id, | 472 | file_id, |
@@ -474,6 +515,7 @@ fn cargo_to_crate_graph( | |||
474 | fn add_target_crate_root( | 515 | fn add_target_crate_root( |
475 | crate_graph: &mut CrateGraph, | 516 | crate_graph: &mut CrateGraph, |
476 | pkg: &cargo_workspace::PackageData, | 517 | pkg: &cargo_workspace::PackageData, |
518 | build_data: Option<&BuildData>, | ||
477 | cfg_options: &CfgOptions, | 519 | cfg_options: &CfgOptions, |
478 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, | 520 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, |
479 | file_id: FileId, | 521 | file_id: FileId, |
@@ -481,26 +523,27 @@ fn add_target_crate_root( | |||
481 | let edition = pkg.edition; | 523 | let edition = pkg.edition; |
482 | let cfg_options = { | 524 | let cfg_options = { |
483 | let mut opts = cfg_options.clone(); | 525 | let mut opts = cfg_options.clone(); |
484 | for feature in pkg.features.iter() { | 526 | for feature in pkg.active_features.iter() { |
485 | opts.insert_key_value("feature".into(), feature.into()); | 527 | opts.insert_key_value("feature".into(), feature.into()); |
486 | } | 528 | } |
487 | opts.extend(pkg.cfgs.iter().cloned()); | 529 | if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) { |
530 | opts.extend(cfgs.iter().cloned()); | ||
531 | } | ||
488 | opts | 532 | opts |
489 | }; | 533 | }; |
490 | 534 | ||
491 | let mut env = Env::default(); | 535 | let mut env = Env::default(); |
492 | for (k, v) in &pkg.envs { | 536 | if let Some(envs) = build_data.map(|it| &it.envs) { |
493 | env.set(k, v.clone()); | 537 | for (k, v) in envs { |
494 | } | 538 | env.set(k, v.clone()); |
495 | if let Some(out_dir) = &pkg.out_dir { | ||
496 | // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() | ||
497 | if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { | ||
498 | env.set("OUT_DIR", out_dir); | ||
499 | } | 539 | } |
500 | } | 540 | } |
501 | 541 | ||
502 | let proc_macro = | 542 | let proc_macro = build_data |
503 | pkg.proc_macro_dylib_path.as_ref().map(|it| proc_macro_loader(&it)).unwrap_or_default(); | 543 | .as_ref() |
544 | .and_then(|it| it.proc_macro_dylib_path.as_ref()) | ||
545 | .map(|it| proc_macro_loader(&it)) | ||
546 | .unwrap_or_default(); | ||
504 | 547 | ||
505 | let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone()); | 548 | let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone()); |
506 | let crate_id = crate_graph.add_crate_root( | 549 | let crate_id = crate_graph.add_crate_root( |