aboutsummaryrefslogtreecommitdiff
path: root/crates/project_model
diff options
context:
space:
mode:
Diffstat (limited to 'crates/project_model')
-rw-r--r--crates/project_model/src/workspace.rs471
1 files changed, 241 insertions, 230 deletions
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index dbf1dc5bf..a71f96164 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -197,269 +197,280 @@ impl ProjectWorkspace {
197 proc_macro_client: &ProcMacroClient, 197 proc_macro_client: &ProcMacroClient,
198 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 198 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
199 ) -> CrateGraph { 199 ) -> CrateGraph {
200 let mut crate_graph = CrateGraph::default(); 200 let mut crate_graph = match self {
201 match self {
202 ProjectWorkspace::Json { project, sysroot } => { 201 ProjectWorkspace::Json { project, sysroot } => {
203 let sysroot_dps = sysroot 202 project_json_to_crate_graph(target, proc_macro_client, load, project, sysroot)
204 .as_ref()
205 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load));
206
207 let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default();
208 let crates: FxHashMap<_, _> = project
209 .crates()
210 .filter_map(|(crate_id, krate)| {
211 let file_path = &krate.root_module;
212 let file_id = match load(&file_path) {
213 Some(id) => id,
214 None => {
215 log::error!("failed to load crate root {}", file_path.display());
216 return None;
217 }
218 };
219
220 let env = krate.env.clone().into_iter().collect();
221 let proc_macro = krate
222 .proc_macro_dylib_path
223 .clone()
224 .map(|it| proc_macro_client.by_dylib_path(&it));
225
226 let target = krate.target.as_deref().or(target);
227 let target_cfgs = cfg_cache
228 .entry(target)
229 .or_insert_with(|| get_rustc_cfg_options(target));
230
231 let mut cfg_options = CfgOptions::default();
232 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
233
234 Some((
235 crate_id,
236 crate_graph.add_crate_root(
237 file_id,
238 krate.edition,
239 krate.display_name.clone(),
240 cfg_options,
241 env,
242 proc_macro.unwrap_or_default(),
243 ),
244 ))
245 })
246 .collect();
247
248 for (from, krate) in project.crates() {
249 if let Some(&from) = crates.get(&from) {
250 if let Some((public_deps, _proc_macro)) = &sysroot_dps {
251 for (name, to) in public_deps.iter() {
252 add_dep(&mut crate_graph, from, name.clone(), *to)
253 }
254 }
255
256 for dep in &krate.deps {
257 let to_crate_id = dep.crate_id;
258 if let Some(&to) = crates.get(&to_crate_id) {
259 add_dep(&mut crate_graph, from, dep.name.clone(), to)
260 }
261 }
262 }
263 }
264 } 203 }
265 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => { 204 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => {
266 let (public_deps, libproc_macro) = 205 cargo_to_crate_graph(target, proc_macro_client, load, cargo, sysroot, rustc)
267 sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load); 206 }
207 };
208 if crate_graph.patch_cfg_if() {
209 log::debug!("Patched std to depend on cfg-if")
210 } else {
211 log::debug!("Did not patch std to depend on cfg-if")
212 }
213 crate_graph
214 }
215}
268 216
269 let mut cfg_options = CfgOptions::default(); 217fn project_json_to_crate_graph(
270 cfg_options.extend(get_rustc_cfg_options(target)); 218 target: Option<&str>,
219 proc_macro_client: &ProcMacroClient,
220 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
221 project: &ProjectJson,
222 sysroot: &Option<Sysroot>,
223) -> CrateGraph {
224 let mut crate_graph = CrateGraph::default();
225 let sysroot_deps = sysroot
226 .as_ref()
227 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load));
228
229 let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default();
230 let crates: FxHashMap<CrateId, CrateId> = project
231 .crates()
232 .filter_map(|(crate_id, krate)| {
233 let file_path = &krate.root_module;
234 let file_id = load(&file_path)?;
235 Some((crate_id, krate, file_id))
236 })
237 .map(|(crate_id, krate, file_id)| {
238 let env = krate.env.clone().into_iter().collect();
239 let proc_macro =
240 krate.proc_macro_dylib_path.clone().map(|it| proc_macro_client.by_dylib_path(&it));
241
242 let target = krate.target.as_deref().or(target);
243 let target_cfgs =
244 cfg_cache.entry(target).or_insert_with(|| get_rustc_cfg_options(target));
245
246 let mut cfg_options = CfgOptions::default();
247 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
248 (
249 crate_id,
250 crate_graph.add_crate_root(
251 file_id,
252 krate.edition,
253 krate.display_name.clone(),
254 cfg_options,
255 env,
256 proc_macro.unwrap_or_default(),
257 ),
258 )
259 })
260 .collect();
271 261
272 let mut pkg_to_lib_crate = FxHashMap::default(); 262 for (from, krate) in project.crates() {
263 if let Some(&from) = crates.get(&from) {
264 if let Some((public_deps, _proc_macro)) = &sysroot_deps {
265 for (name, to) in public_deps.iter() {
266 add_dep(&mut crate_graph, from, name.clone(), *to)
267 }
268 }
269
270 for dep in &krate.deps {
271 if let Some(&to) = crates.get(&dep.crate_id) {
272 add_dep(&mut crate_graph, from, dep.name.clone(), to)
273 }
274 }
275 }
276 }
277 crate_graph
278}
273 279
274 // Add test cfg for non-sysroot crates 280fn cargo_to_crate_graph(
275 cfg_options.insert_atom("test".into()); 281 target: Option<&str>,
276 cfg_options.insert_atom("debug_assertions".into()); 282 proc_macro_client: &ProcMacroClient,
283 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
284 cargo: &CargoWorkspace,
285 sysroot: &Sysroot,
286 rustc: &Option<CargoWorkspace>,
287) -> CrateGraph {
288 let mut crate_graph = CrateGraph::default();
289 let (public_deps, libproc_macro) =
290 sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load);
277 291
278 let mut pkg_crates = FxHashMap::default(); 292 let mut cfg_options = CfgOptions::default();
293 cfg_options.extend(get_rustc_cfg_options(target));
279 294
280 // Next, create crates for each package, target pair 295 let mut pkg_to_lib_crate = FxHashMap::default();
281 for pkg in cargo.packages() { 296
282 let mut lib_tgt = None; 297 // Add test cfg for non-sysroot crates
283 for &tgt in cargo[pkg].targets.iter() { 298 cfg_options.insert_atom("test".into());
284 if let Some(crate_id) = add_target_crate_root( 299 cfg_options.insert_atom("debug_assertions".into());
300
301 let mut pkg_crates = FxHashMap::default();
302
303 // Next, create crates for each package, target pair
304 for pkg in cargo.packages() {
305 let mut lib_tgt = None;
306 for &tgt in cargo[pkg].targets.iter() {
307 if let Some(file_id) = load(&cargo[tgt].root) {
308 let crate_id = add_target_crate_root(
309 &mut crate_graph,
310 &cargo[pkg],
311 &cfg_options,
312 proc_macro_client,
313 file_id,
314 );
315 if cargo[tgt].kind == TargetKind::Lib {
316 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
317 pkg_to_lib_crate.insert(pkg, crate_id);
318 }
319 if cargo[tgt].is_proc_macro {
320 if let Some(proc_macro) = libproc_macro {
321 add_dep(
285 &mut crate_graph, 322 &mut crate_graph,
286 &cargo[pkg], 323 crate_id,
287 &cargo[tgt], 324 CrateName::new("proc_macro").unwrap(),
288 &cfg_options, 325 proc_macro,
289 proc_macro_client, 326 );
290 load,
291 ) {
292 if cargo[tgt].kind == TargetKind::Lib {
293 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
294 pkg_to_lib_crate.insert(pkg, crate_id);
295 }
296 if cargo[tgt].is_proc_macro {
297 if let Some(proc_macro) = libproc_macro {
298 add_dep(
299 &mut crate_graph,
300 crate_id,
301 CrateName::new("proc_macro").unwrap(),
302 proc_macro,
303 );
304 }
305 }
306
307 pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
308 }
309 } 327 }
328 }
310 329
311 // Set deps to the core, std and to the lib target of the current package 330 pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
312 for &from in pkg_crates.get(&pkg).into_iter().flatten() { 331 }
313 if let Some((to, name)) = lib_tgt.clone() { 332 }
314 // For root projects with dashes in their name, 333
315 // cargo metadata does not do any normalization, 334 // Set deps to the core, std and to the lib target of the current package
316 // so we do it ourselves currently 335 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
317 let name = CrateName::normalize_dashes(&name); 336 if let Some((to, name)) = lib_tgt.clone() {
318 if to != from { 337 if to != from {
319 add_dep(&mut crate_graph, from, name, to); 338 // For root projects with dashes in their name,
320 } 339 // cargo metadata does not do any normalization,
321 } 340 // so we do it ourselves currently
322 for (name, krate) in public_deps.iter() { 341 let name = CrateName::normalize_dashes(&name);
323 add_dep(&mut crate_graph, from, name.clone(), *krate); 342 add_dep(&mut crate_graph, from, name, to);
324 }
325 }
326 } 343 }
344 }
345 for (name, krate) in public_deps.iter() {
346 add_dep(&mut crate_graph, from, name.clone(), *krate);
347 }
348 }
349 }
327 350
328 // Now add a dep edge from all targets of upstream to the lib 351 // Now add a dep edge from all targets of upstream to the lib
329 // target of downstream. 352 // target of downstream.
330 for pkg in cargo.packages() { 353 for pkg in cargo.packages() {
331 for dep in cargo[pkg].dependencies.iter() { 354 for dep in cargo[pkg].dependencies.iter() {
332 let name = CrateName::new(&dep.name).unwrap(); 355 let name = CrateName::new(&dep.name).unwrap();
333 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { 356 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
334 for &from in pkg_crates.get(&pkg).into_iter().flatten() { 357 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
335 add_dep(&mut crate_graph, from, name.clone(), to) 358 add_dep(&mut crate_graph, from, name.clone(), to)
336 } 359 }
337 } 360 }
338 } 361 }
362 }
363
364 let mut rustc_pkg_crates = FxHashMap::default();
365
366 // If the user provided a path to rustc sources, we add all the rustc_private crates
367 // and create dependencies on them for the crates in the current workspace
368 if let Some(rustc_workspace) = rustc {
369 for pkg in rustc_workspace.packages() {
370 for &tgt in rustc_workspace[pkg].targets.iter() {
371 if rustc_workspace[tgt].kind != TargetKind::Lib {
372 continue;
373 }
374 // Exclude alloc / core / std
375 if rustc_workspace[tgt]
376 .root
377 .components()
378 .any(|c| c == Component::Normal("library".as_ref()))
379 {
380 continue;
339 } 381 }
340 382
341 let mut rustc_pkg_crates = FxHashMap::default(); 383 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
342 384 let crate_id = add_target_crate_root(
343 // If the user provided a path to rustc sources, we add all the rustc_private crates 385 &mut crate_graph,
344 // and create dependencies on them for the crates in the current workspace 386 &rustc_workspace[pkg],
345 if let Some(rustc_workspace) = rustc { 387 &cfg_options,
346 for pkg in rustc_workspace.packages() { 388 proc_macro_client,
347 for &tgt in rustc_workspace[pkg].targets.iter() { 389 file_id,
348 if rustc_workspace[tgt].kind != TargetKind::Lib { 390 );
349 continue; 391 pkg_to_lib_crate.insert(pkg, crate_id);
350 } 392 // Add dependencies on the core / std / alloc for rustc
351 // Exclude alloc / core / std 393 for (name, krate) in public_deps.iter() {
352 if rustc_workspace[tgt] 394 add_dep(&mut crate_graph, crate_id, name.clone(), *krate);
353 .root
354 .components()
355 .any(|c| c == Component::Normal("library".as_ref()))
356 {
357 continue;
358 }
359
360 if let Some(crate_id) = add_target_crate_root(
361 &mut crate_graph,
362 &rustc_workspace[pkg],
363 &rustc_workspace[tgt],
364 &cfg_options,
365 proc_macro_client,
366 load,
367 ) {
368 pkg_to_lib_crate.insert(pkg, crate_id);
369 // Add dependencies on the core / std / alloc for rustc
370 for (name, krate) in public_deps.iter() {
371 add_dep(&mut crate_graph, crate_id, name.clone(), *krate);
372 }
373 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
374 }
375 }
376 } 395 }
377 // Now add a dep edge from all targets of upstream to the lib 396 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
378 // target of downstream. 397 }
379 for pkg in rustc_workspace.packages() { 398 }
380 for dep in rustc_workspace[pkg].dependencies.iter() { 399 }
381 let name = CrateName::new(&dep.name).unwrap(); 400 // Now add a dep edge from all targets of upstream to the lib
382 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { 401 // target of downstream.
383 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() { 402 for pkg in rustc_workspace.packages() {
384 add_dep(&mut crate_graph, from, name.clone(), to); 403 for dep in rustc_workspace[pkg].dependencies.iter() {
385 } 404 let name = CrateName::new(&dep.name).unwrap();
386 } 405 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
387 } 406 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
407 add_dep(&mut crate_graph, from, name.clone(), to);
388 } 408 }
409 }
410 }
411 }
389 412
390 // Add dependencies for all the crates of the current workspace to rustc_private libraries 413 // Add dependencies for all the crates of the current workspace to rustc_private libraries
391 for dep in rustc_workspace.packages() { 414 for dep in rustc_workspace.packages() {
392 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name); 415 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
393 416
394 if let Some(&to) = pkg_to_lib_crate.get(&dep) { 417 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
395 for pkg in cargo.packages() { 418 for pkg in cargo.packages() {
396 if !cargo[pkg].is_member { 419 if !cargo[pkg].is_member {
397 continue; 420 continue;
398 } 421 }
399 for &from in pkg_crates.get(&pkg).into_iter().flatten() { 422 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
400 add_dep(&mut crate_graph, from, name.clone(), to); 423 add_dep(&mut crate_graph, from, name.clone(), to);
401 }
402 }
403 }
404 } 424 }
405 } 425 }
406 } 426 }
407 } 427 }
408 if crate_graph.patch_cfg_if() {
409 log::debug!("Patched std to depend on cfg-if")
410 } else {
411 log::debug!("Did not patch std to depend on cfg-if")
412 }
413 crate_graph
414 } 428 }
429 crate_graph
415} 430}
416 431
417fn add_target_crate_root( 432fn add_target_crate_root(
418 crate_graph: &mut CrateGraph, 433 crate_graph: &mut CrateGraph,
419 pkg: &cargo_workspace::PackageData, 434 pkg: &cargo_workspace::PackageData,
420 tgt: &cargo_workspace::TargetData,
421 cfg_options: &CfgOptions, 435 cfg_options: &CfgOptions,
422 proc_macro_client: &ProcMacroClient, 436 proc_macro_client: &ProcMacroClient,
423 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 437 file_id: FileId,
424) -> Option<CrateId> { 438) -> CrateId {
425 let root = tgt.root.as_path(); 439 let edition = pkg.edition;
426 if let Some(file_id) = load(root) { 440 let cfg_options = {
427 let edition = pkg.edition; 441 let mut opts = cfg_options.clone();
428 let cfg_options = { 442 for feature in pkg.features.iter() {
429 let mut opts = cfg_options.clone(); 443 opts.insert_key_value("feature".into(), feature.into());
430 for feature in pkg.features.iter() { 444 }
431 opts.insert_key_value("feature".into(), feature.into()); 445 opts.extend(pkg.cfgs.iter().cloned());
432 } 446 opts
433 opts.extend(pkg.cfgs.iter().cloned()); 447 };
434 opts 448 let mut env = Env::default();
435 }; 449 if let Some(out_dir) = &pkg.out_dir {
436 let mut env = Env::default(); 450 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
437 if let Some(out_dir) = &pkg.out_dir { 451 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
438 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() 452 env.set("OUT_DIR", out_dir);
439 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
440 env.set("OUT_DIR", out_dir);
441 }
442 } 453 }
443 let proc_macro = pkg
444 .proc_macro_dylib_path
445 .as_ref()
446 .map(|it| proc_macro_client.by_dylib_path(&it))
447 .unwrap_or_default();
448
449 let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone());
450 let crate_id = crate_graph.add_crate_root(
451 file_id,
452 edition,
453 Some(display_name),
454 cfg_options,
455 env,
456 proc_macro.clone(),
457 );
458
459 return Some(crate_id);
460 } 454 }
461 None 455 let proc_macro = pkg
456 .proc_macro_dylib_path
457 .as_ref()
458 .map(|it| proc_macro_client.by_dylib_path(&it))
459 .unwrap_or_default();
460
461 let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone());
462 let crate_id = crate_graph.add_crate_root(
463 file_id,
464 edition,
465 Some(display_name),
466 cfg_options,
467 env,
468 proc_macro.clone(),
469 );
470
471 crate_id
462} 472}
473
463fn sysroot_to_crate_graph( 474fn sysroot_to_crate_graph(
464 crate_graph: &mut CrateGraph, 475 crate_graph: &mut CrateGraph,
465 sysroot: &Sysroot, 476 sysroot: &Sysroot,