4 use std::path::{Path, PathBuf};
7 use rustc_hash::FxHashMap;
9 use ra_db::{CrateGraph, FileId};
13 cargo_workspace::{CargoWorkspace, Package, Target, TargetKind},
17 // TODO use own error enum?
18 pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
20 #[derive(Debug, Clone)]
21 pub struct ProjectWorkspace {
22 pub cargo: CargoWorkspace,
26 impl ProjectWorkspace {
27 pub fn discover(path: &Path) -> Result<ProjectWorkspace> {
28 let cargo_toml = find_cargo_toml(path)?;
29 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml)?;
30 let sysroot = Sysroot::discover(&cargo_toml)?;
31 let res = ProjectWorkspace { cargo, sysroot };
35 pub fn to_crate_graph(&self, vfs: &mut Vfs) -> CrateGraph {
36 let mut crate_graph = CrateGraph::default();
37 let mut sysroot_crates = FxHashMap::default();
38 for krate in self.sysroot.crates() {
39 if let Some(file_id) = vfs.load(krate.root(&self.sysroot)) {
40 let file_id = FileId(file_id.0.into());
41 sysroot_crates.insert(krate, crate_graph.add_crate_root(file_id));
44 for from in self.sysroot.crates() {
45 for to in from.deps(&self.sysroot) {
46 let name = to.name(&self.sysroot);
47 if let (Some(&from), Some(&to)) =
48 (sysroot_crates.get(&from), sysroot_crates.get(&to))
50 if let Err(_) = crate_graph.add_dep(from, name.into(), to) {
51 log::error!("cyclic dependency between sysroot crates")
57 let libstd = self.sysroot.std().and_then(|it| sysroot_crates.get(&it).map(|&it| it));
59 let mut pkg_to_lib_crate = FxHashMap::default();
60 let mut pkg_crates = FxHashMap::default();
61 // Next, create crates for each package, target pair
62 for pkg in self.cargo.packages() {
63 let mut lib_tgt = None;
64 for tgt in pkg.targets(&self.cargo) {
65 let root = tgt.root(&self.cargo);
66 if let Some(file_id) = vfs.load(root) {
67 let file_id = FileId(file_id.0.into());
68 let crate_id = crate_graph.add_crate_root(file_id);
69 if tgt.kind(&self.cargo) == TargetKind::Lib {
70 lib_tgt = Some(crate_id);
71 pkg_to_lib_crate.insert(pkg, crate_id);
73 pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
77 // Set deps to the std and to the lib target of the current package
78 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
79 if let Some(to) = lib_tgt {
81 if let Err(_) = crate_graph.add_dep(from, pkg.name(&self.cargo).into(), to)
84 "cyclic dependency between targets of {}",
90 if let Some(std) = libstd {
91 if let Err(_) = crate_graph.add_dep(from, "std".into(), std) {
92 log::error!("cyclic dependency on std for {}", pkg.name(&self.cargo))
98 // Now add a dep ednge from all targets of upstream to the lib
99 // target of downstream.
100 for pkg in self.cargo.packages() {
101 for dep in pkg.dependencies(&self.cargo) {
102 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
103 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
104 if let Err(_) = crate_graph.add_dep(from, dep.name.clone().into(), to) {
106 "cyclic dependency {} -> {}",
107 pkg.name(&self.cargo),
108 dep.pkg.name(&self.cargo)
120 fn find_cargo_toml(path: &Path) -> Result<PathBuf> {
121 if path.ends_with("Cargo.toml") {
122 return Ok(path.to_path_buf());
124 let mut curr = Some(path);
125 while let Some(path) = curr {
126 let candidate = path.join("Cargo.toml");
127 if candidate.exists() {
128 return Ok(candidate);
130 curr = path.parent();
132 bail!("can't find Cargo.toml at {}", path.display())