aboutsummaryrefslogtreecommitdiff
path: root/crates/project_model
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2021-01-18 11:52:12 +0000
committerAleksey Kladov <[email protected]>2021-01-18 11:52:12 +0000
commitadd87f54240a20e72136a403c166113af5173572 (patch)
tree14ed8b5ca07a40dea9b0599506d78b3d6f4c4a6c /crates/project_model
parentcc963d2b1190578ad9f1d1f4589e8ea2c98c1a14 (diff)
Avoid blocking the main loop when editing Cargo.toml
I've noticed a bunch of "main loop too long" warnings in console when typing in Cargo.toml. Profiling showed that the culprit is `rustc --print cfg` call. I moved it to the background project loading phase, where it belongs. This highlighted a problem: we generally use single `cfg`, while it really should be per crate.
Diffstat (limited to 'crates/project_model')
-rw-r--r--crates/project_model/src/lib.rs1
-rw-r--r--crates/project_model/src/rustc_cfg.rs34
-rw-r--r--crates/project_model/src/workspace.rs119
3 files changed, 93 insertions, 61 deletions
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs
index aabb7a47d..970a7e140 100644
--- a/crates/project_model/src/lib.rs
+++ b/crates/project_model/src/lib.rs
@@ -5,6 +5,7 @@ mod cfg_flag;
5mod project_json; 5mod project_json;
6mod sysroot; 6mod sysroot;
7mod workspace; 7mod workspace;
8mod rustc_cfg;
8 9
9use std::{ 10use std::{
10 fs::{read_dir, ReadDir}, 11 fs::{read_dir, ReadDir},
diff --git a/crates/project_model/src/rustc_cfg.rs b/crates/project_model/src/rustc_cfg.rs
new file mode 100644
index 000000000..4a7bd8ae3
--- /dev/null
+++ b/crates/project_model/src/rustc_cfg.rs
@@ -0,0 +1,34 @@
1//! Runs `rustc --print cfg` to get built-in cfg flags.
2
3use std::process::Command;
4
5use crate::{cfg_flag::CfgFlag, utf8_stdout};
6
7pub(crate) fn get(target: Option<&str>) -> Vec<CfgFlag> {
8 let _p = profile::span("rustc_cfg::get");
9 let mut res = Vec::new();
10
11 // Some nightly-only cfgs, which are required for stdlib
12 res.push(CfgFlag::Atom("target_thread_local".into()));
13 for &ty in ["8", "16", "32", "64", "cas", "ptr"].iter() {
14 for &key in ["target_has_atomic", "target_has_atomic_load_store"].iter() {
15 res.push(CfgFlag::KeyValue { key: key.to_string(), value: ty.into() });
16 }
17 }
18
19 let rustc_cfgs = {
20 let mut cmd = Command::new(toolchain::rustc());
21 cmd.args(&["--print", "cfg", "-O"]);
22 if let Some(target) = target {
23 cmd.args(&["--target", target]);
24 }
25 utf8_stdout(cmd)
26 };
27
28 match rustc_cfgs {
29 Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())),
30 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e),
31 }
32
33 res
34}
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index bd990594b..8e0481ae9 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -16,7 +16,7 @@ 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, sysroot::SysrootCrate, utf8_stdout, CargoConfig, 19 cargo_workspace, cfg_flag::CfgFlag, rustc_cfg, sysroot::SysrootCrate, utf8_stdout, CargoConfig,
20 CargoWorkspace, ProjectJson, ProjectManifest, Sysroot, TargetKind, 20 CargoWorkspace, ProjectJson, ProjectManifest, Sysroot, TargetKind,
21}; 21};
22 22
@@ -34,15 +34,25 @@ pub struct PackageRoot {
34#[derive(Clone, Eq, PartialEq)] 34#[derive(Clone, Eq, PartialEq)]
35pub enum ProjectWorkspace { 35pub enum ProjectWorkspace {
36 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. 36 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
37 Cargo { cargo: CargoWorkspace, sysroot: Sysroot, rustc: Option<CargoWorkspace> }, 37 Cargo {
38 cargo: CargoWorkspace,
39 sysroot: Sysroot,
40 rustc: Option<CargoWorkspace>,
41 /// Holds cfg flags for the current target. We get those by running
42 /// `rustc --print cfg`.
43 ///
44 /// FIXME: make this a per-crate map, as, eg, build.rs might have a
45 /// different target.
46 rustc_cfg: Vec<CfgFlag>,
47 },
38 /// Project workspace was manually specified using a `rust-project.json` file. 48 /// Project workspace was manually specified using a `rust-project.json` file.
39 Json { project: ProjectJson, sysroot: Option<Sysroot> }, 49 Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
40} 50}
41 51
42impl fmt::Debug for ProjectWorkspace { 52impl fmt::Debug for ProjectWorkspace {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 match self { 54 match self {
45 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => f 55 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f
46 .debug_struct("Cargo") 56 .debug_struct("Cargo")
47 .field("n_packages", &cargo.packages().len()) 57 .field("n_packages", &cargo.packages().len())
48 .field("n_sysroot_crates", &sysroot.crates().len()) 58 .field("n_sysroot_crates", &sysroot.crates().len())
@@ -50,13 +60,15 @@ impl fmt::Debug for ProjectWorkspace {
50 "n_rustc_compiler_crates", 60 "n_rustc_compiler_crates",
51 &rustc.as_ref().map_or(0, |rc| rc.packages().len()), 61 &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
52 ) 62 )
63 .field("rustc_cfg", rustc_cfg)
53 .finish(), 64 .finish(),
54 ProjectWorkspace::Json { project, sysroot } => { 65 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
55 let mut debug_struct = f.debug_struct("Json"); 66 let mut debug_struct = f.debug_struct("Json");
56 debug_struct.field("n_crates", &project.n_crates()); 67 debug_struct.field("n_crates", &project.n_crates());
57 if let Some(sysroot) = sysroot { 68 if let Some(sysroot) = sysroot {
58 debug_struct.field("n_sysroot_crates", &sysroot.crates().len()); 69 debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
59 } 70 }
71 debug_struct.field("rustc_cfg", rustc_cfg);
60 debug_struct.finish() 72 debug_struct.finish()
61 } 73 }
62 } 74 }
@@ -79,7 +91,7 @@ impl ProjectWorkspace {
79 })?; 91 })?;
80 let project_location = project_json.parent().unwrap().to_path_buf(); 92 let project_location = project_json.parent().unwrap().to_path_buf();
81 let project_json = ProjectJson::new(&project_location, data); 93 let project_json = ProjectJson::new(&project_location, data);
82 ProjectWorkspace::load_inline(project_json)? 94 ProjectWorkspace::load_inline(project_json, config.target.as_deref())?
83 } 95 }
84 ProjectManifest::CargoToml(cargo_toml) => { 96 ProjectManifest::CargoToml(cargo_toml) => {
85 let cargo_version = utf8_stdout({ 97 let cargo_version = utf8_stdout({
@@ -117,21 +129,24 @@ impl ProjectWorkspace {
117 } else { 129 } else {
118 None 130 None
119 }; 131 };
120 132 let rustc_cfg = rustc_cfg::get(config.target.as_deref());
121 ProjectWorkspace::Cargo { cargo, sysroot, rustc } 133 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg }
122 } 134 }
123 }; 135 };
124 136
125 Ok(res) 137 Ok(res)
126 } 138 }
127 139
128 pub fn load_inline(project_json: ProjectJson) -> Result<ProjectWorkspace> { 140 pub fn load_inline(
141 project_json: ProjectJson,
142 target: Option<&str>,
143 ) -> Result<ProjectWorkspace> {
129 let sysroot = match &project_json.sysroot_src { 144 let sysroot = match &project_json.sysroot_src {
130 Some(path) => Some(Sysroot::load(path)?), 145 Some(path) => Some(Sysroot::load(path)?),
131 None => None, 146 None => None,
132 }; 147 };
133 148 let rustc_cfg = rustc_cfg::get(target);
134 Ok(ProjectWorkspace::Json { project: project_json, sysroot }) 149 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
135 } 150 }
136 151
137 /// Returns the roots for the current `ProjectWorkspace` 152 /// Returns the roots for the current `ProjectWorkspace`
@@ -139,7 +154,7 @@ impl ProjectWorkspace {
139 /// the root is a member of the current workspace 154 /// the root is a member of the current workspace
140 pub fn to_roots(&self) -> Vec<PackageRoot> { 155 pub fn to_roots(&self) -> Vec<PackageRoot> {
141 match self { 156 match self {
142 ProjectWorkspace::Json { project, sysroot } => project 157 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
143 .crates() 158 .crates()
144 .map(|(_, krate)| PackageRoot { 159 .map(|(_, krate)| PackageRoot {
145 is_member: krate.is_workspace_member, 160 is_member: krate.is_workspace_member,
@@ -156,7 +171,7 @@ impl ProjectWorkspace {
156 }) 171 })
157 })) 172 }))
158 .collect::<Vec<_>>(), 173 .collect::<Vec<_>>(),
159 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => cargo 174 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg: _ } => cargo
160 .packages() 175 .packages()
161 .map(|pkg| { 176 .map(|pkg| {
162 let is_member = cargo[pkg].is_member; 177 let is_member = cargo[pkg].is_member;
@@ -194,7 +209,7 @@ impl ProjectWorkspace {
194 pub fn n_packages(&self) -> usize { 209 pub fn n_packages(&self) -> usize {
195 match self { 210 match self {
196 ProjectWorkspace::Json { project, .. } => project.n_crates(), 211 ProjectWorkspace::Json { project, .. } => project.n_crates(),
197 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => { 212 ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
198 let rustc_package_len = rustc.as_ref().map_or(0, |rc| rc.packages().len()); 213 let rustc_package_len = rustc.as_ref().map_or(0, |rc| rc.packages().len());
199 cargo.packages().len() + sysroot.crates().len() + rustc_package_len 214 cargo.packages().len() + sysroot.crates().len() + rustc_package_len
200 } 215 }
@@ -203,7 +218,6 @@ impl ProjectWorkspace {
203 218
204 pub fn to_crate_graph( 219 pub fn to_crate_graph(
205 &self, 220 &self,
206 target: Option<&str>,
207 proc_macro_client: Option<&ProcMacroClient>, 221 proc_macro_client: Option<&ProcMacroClient>,
208 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 222 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
209 ) -> CrateGraph { 223 ) -> CrateGraph {
@@ -214,12 +228,21 @@ impl ProjectWorkspace {
214 }; 228 };
215 229
216 let mut crate_graph = match self { 230 let mut crate_graph = match self {
217 ProjectWorkspace::Json { project, sysroot } => { 231 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
218 project_json_to_crate_graph(target, &proc_macro_loader, load, project, sysroot) 232 rustc_cfg.clone(),
219 } 233 &proc_macro_loader,
220 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => { 234 load,
221 cargo_to_crate_graph(target, &proc_macro_loader, load, cargo, sysroot, rustc) 235 project,
222 } 236 sysroot,
237 ),
238 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => cargo_to_crate_graph(
239 rustc_cfg.clone(),
240 &proc_macro_loader,
241 load,
242 cargo,
243 sysroot,
244 rustc,
245 ),
223 }; 246 };
224 if crate_graph.patch_cfg_if() { 247 if crate_graph.patch_cfg_if() {
225 log::debug!("Patched std to depend on cfg-if") 248 log::debug!("Patched std to depend on cfg-if")
@@ -231,7 +254,7 @@ impl ProjectWorkspace {
231} 254}
232 255
233fn project_json_to_crate_graph( 256fn project_json_to_crate_graph(
234 target: Option<&str>, 257 rustc_cfg: Vec<CfgFlag>,
235 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 258 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
236 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 259 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
237 project: &ProjectJson, 260 project: &ProjectJson,
@@ -240,9 +263,9 @@ fn project_json_to_crate_graph(
240 let mut crate_graph = CrateGraph::default(); 263 let mut crate_graph = CrateGraph::default();
241 let sysroot_deps = sysroot 264 let sysroot_deps = sysroot
242 .as_ref() 265 .as_ref()
243 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load)); 266 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
244 267
245 let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default(); 268 let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
246 let crates: FxHashMap<CrateId, CrateId> = project 269 let crates: FxHashMap<CrateId, CrateId> = project
247 .crates() 270 .crates()
248 .filter_map(|(crate_id, krate)| { 271 .filter_map(|(crate_id, krate)| {
@@ -254,9 +277,12 @@ fn project_json_to_crate_graph(
254 let env = krate.env.clone().into_iter().collect(); 277 let env = krate.env.clone().into_iter().collect();
255 let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| proc_macro_loader(&it)); 278 let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| proc_macro_loader(&it));
256 279
257 let target = krate.target.as_deref().or(target); 280 let target_cfgs = match krate.target.as_deref() {
258 let target_cfgs = 281 Some(target) => {
259 cfg_cache.entry(target).or_insert_with(|| get_rustc_cfg_options(target)); 282 cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(Some(target)))
283 }
284 None => &rustc_cfg,
285 };
260 286
261 let mut cfg_options = CfgOptions::default(); 287 let mut cfg_options = CfgOptions::default();
262 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned()); 288 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
@@ -293,7 +319,7 @@ fn project_json_to_crate_graph(
293} 319}
294 320
295fn cargo_to_crate_graph( 321fn cargo_to_crate_graph(
296 target: Option<&str>, 322 rustc_cfg: Vec<CfgFlag>,
297 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 323 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
298 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 324 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
299 cargo: &CargoWorkspace, 325 cargo: &CargoWorkspace,
@@ -303,10 +329,10 @@ fn cargo_to_crate_graph(
303 let _p = profile::span("cargo_to_crate_graph"); 329 let _p = profile::span("cargo_to_crate_graph");
304 let mut crate_graph = CrateGraph::default(); 330 let mut crate_graph = CrateGraph::default();
305 let (public_deps, libproc_macro) = 331 let (public_deps, libproc_macro) =
306 sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load); 332 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
307 333
308 let mut cfg_options = CfgOptions::default(); 334 let mut cfg_options = CfgOptions::default();
309 cfg_options.extend(get_rustc_cfg_options(target)); 335 cfg_options.extend(rustc_cfg);
310 336
311 let mut pkg_to_lib_crate = FxHashMap::default(); 337 let mut pkg_to_lib_crate = FxHashMap::default();
312 338
@@ -492,12 +518,12 @@ fn add_target_crate_root(
492fn sysroot_to_crate_graph( 518fn sysroot_to_crate_graph(
493 crate_graph: &mut CrateGraph, 519 crate_graph: &mut CrateGraph,
494 sysroot: &Sysroot, 520 sysroot: &Sysroot,
495 target: Option<&str>, 521 rustc_cfg: Vec<CfgFlag>,
496 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 522 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
497) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) { 523) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) {
498 let _p = profile::span("sysroot_to_crate_graph"); 524 let _p = profile::span("sysroot_to_crate_graph");
499 let mut cfg_options = CfgOptions::default(); 525 let mut cfg_options = CfgOptions::default();
500 cfg_options.extend(get_rustc_cfg_options(target)); 526 cfg_options.extend(rustc_cfg);
501 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot 527 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
502 .crates() 528 .crates()
503 .filter_map(|krate| { 529 .filter_map(|krate| {
@@ -536,35 +562,6 @@ fn sysroot_to_crate_graph(
536 (public_deps, libproc_macro) 562 (public_deps, libproc_macro)
537} 563}
538 564
539fn get_rustc_cfg_options(target: Option<&str>) -> Vec<CfgFlag> {
540 let _p = profile::span("get_rustc_cfg_options");
541 let mut res = Vec::new();
542
543 // Some nightly-only cfgs, which are required for stdlib
544 res.push(CfgFlag::Atom("target_thread_local".into()));
545 for &ty in ["8", "16", "32", "64", "cas", "ptr"].iter() {
546 for &key in ["target_has_atomic", "target_has_atomic_load_store"].iter() {
547 res.push(CfgFlag::KeyValue { key: key.to_string(), value: ty.into() });
548 }
549 }
550
551 let rustc_cfgs = {
552 let mut cmd = Command::new(toolchain::rustc());
553 cmd.args(&["--print", "cfg", "-O"]);
554 if let Some(target) = target {
555 cmd.args(&["--target", target]);
556 }
557 utf8_stdout(cmd)
558 };
559
560 match rustc_cfgs {
561 Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())),
562 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e),
563 }
564
565 res
566}
567
568fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) { 565fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
569 if let Err(err) = graph.add_dep(from, name, to) { 566 if let Err(err) = graph.add_dep(from, name, to) {
570 log::error!("{}", err) 567 log::error!("{}", err)