aboutsummaryrefslogtreecommitdiff
path: root/crates/project_model/src/workspace.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/project_model/src/workspace.rs')
-rw-r--r--crates/project_model/src/workspace.rs143
1 files changed, 75 insertions, 68 deletions
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 68a235ce3..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 }
@@ -64,7 +76,11 @@ impl fmt::Debug for ProjectWorkspace {
64} 76}
65 77
66impl ProjectWorkspace { 78impl ProjectWorkspace {
67 pub fn load(manifest: ProjectManifest, config: &CargoConfig) -> Result<ProjectWorkspace> { 79 pub fn load(
80 manifest: ProjectManifest,
81 config: &CargoConfig,
82 progress: &dyn Fn(String),
83 ) -> Result<ProjectWorkspace> {
68 let res = match manifest { 84 let res = match manifest {
69 ProjectManifest::ProjectJson(project_json) => { 85 ProjectManifest::ProjectJson(project_json) => {
70 let file = fs::read_to_string(&project_json).with_context(|| { 86 let file = fs::read_to_string(&project_json).with_context(|| {
@@ -75,7 +91,7 @@ impl ProjectWorkspace {
75 })?; 91 })?;
76 let project_location = project_json.parent().unwrap().to_path_buf(); 92 let project_location = project_json.parent().unwrap().to_path_buf();
77 let project_json = ProjectJson::new(&project_location, data); 93 let project_json = ProjectJson::new(&project_location, data);
78 ProjectWorkspace::load_inline(project_json)? 94 ProjectWorkspace::load_inline(project_json, config.target.as_deref())?
79 } 95 }
80 ProjectManifest::CargoToml(cargo_toml) => { 96 ProjectManifest::CargoToml(cargo_toml) => {
81 let cargo_version = utf8_stdout({ 97 let cargo_version = utf8_stdout({
@@ -84,15 +100,14 @@ impl ProjectWorkspace {
84 cmd 100 cmd
85 })?; 101 })?;
86 102
87 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, config).with_context( 103 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, config, progress)
88 || { 104 .with_context(|| {
89 format!( 105 format!(
90 "Failed to read Cargo metadata from Cargo.toml file {}, {}", 106 "Failed to read Cargo metadata from Cargo.toml file {}, {}",
91 cargo_toml.display(), 107 cargo_toml.display(),
92 cargo_version 108 cargo_version
93 ) 109 )
94 }, 110 })?;
95 )?;
96 let sysroot = if config.no_sysroot { 111 let sysroot = if config.no_sysroot {
97 Sysroot::default() 112 Sysroot::default()
98 } else { 113 } else {
@@ -105,27 +120,33 @@ impl ProjectWorkspace {
105 }; 120 };
106 121
107 let rustc = if let Some(rustc_dir) = &config.rustc_source { 122 let rustc = if let Some(rustc_dir) = &config.rustc_source {
108 Some(CargoWorkspace::from_cargo_metadata(&rustc_dir, config).with_context( 123 Some(
109 || format!("Failed to read Cargo metadata for Rust sources"), 124 CargoWorkspace::from_cargo_metadata(&rustc_dir, config, progress)
110 )?) 125 .with_context(|| {
126 format!("Failed to read Cargo metadata for Rust sources")
127 })?,
128 )
111 } else { 129 } else {
112 None 130 None
113 }; 131 };
114 132 let rustc_cfg = rustc_cfg::get(config.target.as_deref());
115 ProjectWorkspace::Cargo { cargo, sysroot, rustc } 133 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg }
116 } 134 }
117 }; 135 };
118 136
119 Ok(res) 137 Ok(res)
120 } 138 }
121 139
122 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> {
123 let sysroot = match &project_json.sysroot_src { 144 let sysroot = match &project_json.sysroot_src {
124 Some(path) => Some(Sysroot::load(path)?), 145 Some(path) => Some(Sysroot::load(path)?),
125 None => None, 146 None => None,
126 }; 147 };
127 148 let rustc_cfg = rustc_cfg::get(target);
128 Ok(ProjectWorkspace::Json { project: project_json, sysroot }) 149 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
129 } 150 }
130 151
131 /// Returns the roots for the current `ProjectWorkspace` 152 /// Returns the roots for the current `ProjectWorkspace`
@@ -133,7 +154,7 @@ impl ProjectWorkspace {
133 /// the root is a member of the current workspace 154 /// the root is a member of the current workspace
134 pub fn to_roots(&self) -> Vec<PackageRoot> { 155 pub fn to_roots(&self) -> Vec<PackageRoot> {
135 match self { 156 match self {
136 ProjectWorkspace::Json { project, sysroot } => project 157 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
137 .crates() 158 .crates()
138 .map(|(_, krate)| PackageRoot { 159 .map(|(_, krate)| PackageRoot {
139 is_member: krate.is_workspace_member, 160 is_member: krate.is_workspace_member,
@@ -150,7 +171,7 @@ impl ProjectWorkspace {
150 }) 171 })
151 })) 172 }))
152 .collect::<Vec<_>>(), 173 .collect::<Vec<_>>(),
153 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => cargo 174 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg: _ } => cargo
154 .packages() 175 .packages()
155 .map(|pkg| { 176 .map(|pkg| {
156 let is_member = cargo[pkg].is_member; 177 let is_member = cargo[pkg].is_member;
@@ -188,7 +209,7 @@ impl ProjectWorkspace {
188 pub fn n_packages(&self) -> usize { 209 pub fn n_packages(&self) -> usize {
189 match self { 210 match self {
190 ProjectWorkspace::Json { project, .. } => project.n_crates(), 211 ProjectWorkspace::Json { project, .. } => project.n_crates(),
191 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => { 212 ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
192 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());
193 cargo.packages().len() + sysroot.crates().len() + rustc_package_len 214 cargo.packages().len() + sysroot.crates().len() + rustc_package_len
194 } 215 }
@@ -197,22 +218,31 @@ impl ProjectWorkspace {
197 218
198 pub fn to_crate_graph( 219 pub fn to_crate_graph(
199 &self, 220 &self,
200 target: Option<&str>,
201 proc_macro_client: Option<&ProcMacroClient>, 221 proc_macro_client: Option<&ProcMacroClient>,
202 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 222 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
203 ) -> CrateGraph { 223 ) -> CrateGraph {
224 let _p = profile::span("ProjectWorkspace::to_crate_graph");
204 let proc_macro_loader = |path: &Path| match proc_macro_client { 225 let proc_macro_loader = |path: &Path| match proc_macro_client {
205 Some(client) => client.by_dylib_path(path), 226 Some(client) => client.by_dylib_path(path),
206 None => Vec::new(), 227 None => Vec::new(),
207 }; 228 };
208 229
209 let mut crate_graph = match self { 230 let mut crate_graph = match self {
210 ProjectWorkspace::Json { project, sysroot } => { 231 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
211 project_json_to_crate_graph(target, &proc_macro_loader, load, project, sysroot) 232 rustc_cfg.clone(),
212 } 233 &proc_macro_loader,
213 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => { 234 load,
214 cargo_to_crate_graph(target, &proc_macro_loader, load, cargo, sysroot, rustc) 235 project,
215 } 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 ),
216 }; 246 };
217 if crate_graph.patch_cfg_if() { 247 if crate_graph.patch_cfg_if() {
218 log::debug!("Patched std to depend on cfg-if") 248 log::debug!("Patched std to depend on cfg-if")
@@ -224,7 +254,7 @@ impl ProjectWorkspace {
224} 254}
225 255
226fn project_json_to_crate_graph( 256fn project_json_to_crate_graph(
227 target: Option<&str>, 257 rustc_cfg: Vec<CfgFlag>,
228 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 258 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
229 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 259 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
230 project: &ProjectJson, 260 project: &ProjectJson,
@@ -233,9 +263,9 @@ fn project_json_to_crate_graph(
233 let mut crate_graph = CrateGraph::default(); 263 let mut crate_graph = CrateGraph::default();
234 let sysroot_deps = sysroot 264 let sysroot_deps = sysroot
235 .as_ref() 265 .as_ref()
236 .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));
237 267
238 let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default(); 268 let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
239 let crates: FxHashMap<CrateId, CrateId> = project 269 let crates: FxHashMap<CrateId, CrateId> = project
240 .crates() 270 .crates()
241 .filter_map(|(crate_id, krate)| { 271 .filter_map(|(crate_id, krate)| {
@@ -247,9 +277,12 @@ fn project_json_to_crate_graph(
247 let env = krate.env.clone().into_iter().collect(); 277 let env = krate.env.clone().into_iter().collect();
248 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));
249 279
250 let target = krate.target.as_deref().or(target); 280 let target_cfgs = match krate.target.as_deref() {
251 let target_cfgs = 281 Some(target) => {
252 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 };
253 286
254 let mut cfg_options = CfgOptions::default(); 287 let mut cfg_options = CfgOptions::default();
255 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned()); 288 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
@@ -286,19 +319,20 @@ fn project_json_to_crate_graph(
286} 319}
287 320
288fn cargo_to_crate_graph( 321fn cargo_to_crate_graph(
289 target: Option<&str>, 322 rustc_cfg: Vec<CfgFlag>,
290 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 323 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
291 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 324 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
292 cargo: &CargoWorkspace, 325 cargo: &CargoWorkspace,
293 sysroot: &Sysroot, 326 sysroot: &Sysroot,
294 rustc: &Option<CargoWorkspace>, 327 rustc: &Option<CargoWorkspace>,
295) -> CrateGraph { 328) -> CrateGraph {
329 let _p = profile::span("cargo_to_crate_graph");
296 let mut crate_graph = CrateGraph::default(); 330 let mut crate_graph = CrateGraph::default();
297 let (public_deps, libproc_macro) = 331 let (public_deps, libproc_macro) =
298 sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load); 332 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
299 333
300 let mut cfg_options = CfgOptions::default(); 334 let mut cfg_options = CfgOptions::default();
301 cfg_options.extend(get_rustc_cfg_options(target)); 335 cfg_options.extend(rustc_cfg);
302 336
303 let mut pkg_to_lib_crate = FxHashMap::default(); 337 let mut pkg_to_lib_crate = FxHashMap::default();
304 338
@@ -484,11 +518,12 @@ fn add_target_crate_root(
484fn sysroot_to_crate_graph( 518fn sysroot_to_crate_graph(
485 crate_graph: &mut CrateGraph, 519 crate_graph: &mut CrateGraph,
486 sysroot: &Sysroot, 520 sysroot: &Sysroot,
487 target: Option<&str>, 521 rustc_cfg: Vec<CfgFlag>,
488 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 522 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
489) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) { 523) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) {
524 let _p = profile::span("sysroot_to_crate_graph");
490 let mut cfg_options = CfgOptions::default(); 525 let mut cfg_options = CfgOptions::default();
491 cfg_options.extend(get_rustc_cfg_options(target)); 526 cfg_options.extend(rustc_cfg);
492 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot 527 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
493 .crates() 528 .crates()
494 .filter_map(|krate| { 529 .filter_map(|krate| {
@@ -527,34 +562,6 @@ fn sysroot_to_crate_graph(
527 (public_deps, libproc_macro) 562 (public_deps, libproc_macro)
528} 563}
529 564
530fn get_rustc_cfg_options(target: Option<&str>) -> Vec<CfgFlag> {
531 let mut res = Vec::new();
532
533 // Some nightly-only cfgs, which are required for stdlib
534 res.push(CfgFlag::Atom("target_thread_local".into()));
535 for &ty in ["8", "16", "32", "64", "cas", "ptr"].iter() {
536 for &key in ["target_has_atomic", "target_has_atomic_load_store"].iter() {
537 res.push(CfgFlag::KeyValue { key: key.to_string(), value: ty.into() });
538 }
539 }
540
541 let rustc_cfgs = {
542 let mut cmd = Command::new(toolchain::rustc());
543 cmd.args(&["--print", "cfg", "-O"]);
544 if let Some(target) = target {
545 cmd.args(&["--target", target]);
546 }
547 utf8_stdout(cmd)
548 };
549
550 match rustc_cfgs {
551 Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())),
552 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e),
553 }
554
555 res
556}
557
558fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) { 565fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
559 if let Err(err) = graph.add_dep(from, name, to) { 566 if let Err(err) = graph.add_dep(from, name, to) {
560 log::error!("{}", err) 567 log::error!("{}", err)