diff options
Diffstat (limited to 'crates/ra_project_model/src/lib.rs')
-rw-r--r-- | crates/ra_project_model/src/lib.rs | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs new file mode 100644 index 000000000..3b1e07149 --- /dev/null +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -0,0 +1,130 @@ | |||
1 | mod cargo_workspace; | ||
2 | mod sysroot; | ||
3 | |||
4 | use std::path::{Path, PathBuf}; | ||
5 | |||
6 | use failure::bail; | ||
7 | use rustc_hash::FxHashMap; | ||
8 | |||
9 | use ra_db::{CrateGraph, FileId}; | ||
10 | |||
11 | pub use crate::{ | ||
12 | cargo_workspace::{CargoWorkspace, Package, Target, TargetKind}, | ||
13 | sysroot::Sysroot, | ||
14 | }; | ||
15 | |||
16 | // TODO use proper error enum | ||
17 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; | ||
18 | |||
19 | #[derive(Debug, Clone)] | ||
20 | pub struct ProjectWorkspace { | ||
21 | pub cargo: CargoWorkspace, | ||
22 | pub sysroot: Sysroot, | ||
23 | } | ||
24 | |||
25 | impl ProjectWorkspace { | ||
26 | pub fn discover(path: &Path) -> Result<ProjectWorkspace> { | ||
27 | let cargo_toml = find_cargo_toml(path)?; | ||
28 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml)?; | ||
29 | let sysroot = Sysroot::discover(&cargo_toml)?; | ||
30 | let res = ProjectWorkspace { cargo, sysroot }; | ||
31 | Ok(res) | ||
32 | } | ||
33 | |||
34 | pub fn to_crate_graph(&self, load: &mut dyn FnMut(&Path) -> Option<FileId>) -> CrateGraph { | ||
35 | let mut crate_graph = CrateGraph::default(); | ||
36 | let mut sysroot_crates = FxHashMap::default(); | ||
37 | for krate in self.sysroot.crates() { | ||
38 | if let Some(file_id) = load(krate.root(&self.sysroot)) { | ||
39 | sysroot_crates.insert(krate, crate_graph.add_crate_root(file_id)); | ||
40 | } | ||
41 | } | ||
42 | for from in self.sysroot.crates() { | ||
43 | for to in from.deps(&self.sysroot) { | ||
44 | let name = to.name(&self.sysroot); | ||
45 | if let (Some(&from), Some(&to)) = | ||
46 | (sysroot_crates.get(&from), sysroot_crates.get(&to)) | ||
47 | { | ||
48 | if let Err(_) = crate_graph.add_dep(from, name.into(), to) { | ||
49 | log::error!("cyclic dependency between sysroot crates") | ||
50 | } | ||
51 | } | ||
52 | } | ||
53 | } | ||
54 | |||
55 | let libstd = self.sysroot.std().and_then(|it| sysroot_crates.get(&it).map(|&it| it)); | ||
56 | |||
57 | let mut pkg_to_lib_crate = FxHashMap::default(); | ||
58 | let mut pkg_crates = FxHashMap::default(); | ||
59 | // Next, create crates for each package, target pair | ||
60 | for pkg in self.cargo.packages() { | ||
61 | let mut lib_tgt = None; | ||
62 | for tgt in pkg.targets(&self.cargo) { | ||
63 | let root = tgt.root(&self.cargo); | ||
64 | if let Some(file_id) = load(root) { | ||
65 | let crate_id = crate_graph.add_crate_root(file_id); | ||
66 | if tgt.kind(&self.cargo) == TargetKind::Lib { | ||
67 | lib_tgt = Some(crate_id); | ||
68 | pkg_to_lib_crate.insert(pkg, crate_id); | ||
69 | } | ||
70 | pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | // Set deps to the std and to the lib target of the current package | ||
75 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | ||
76 | if let Some(to) = lib_tgt { | ||
77 | if to != from { | ||
78 | if let Err(_) = crate_graph.add_dep(from, pkg.name(&self.cargo).into(), to) | ||
79 | { | ||
80 | log::error!( | ||
81 | "cyclic dependency between targets of {}", | ||
82 | pkg.name(&self.cargo) | ||
83 | ) | ||
84 | } | ||
85 | } | ||
86 | } | ||
87 | if let Some(std) = libstd { | ||
88 | if let Err(_) = crate_graph.add_dep(from, "std".into(), std) { | ||
89 | log::error!("cyclic dependency on std for {}", pkg.name(&self.cargo)) | ||
90 | } | ||
91 | } | ||
92 | } | ||
93 | } | ||
94 | |||
95 | // Now add a dep ednge from all targets of upstream to the lib | ||
96 | // target of downstream. | ||
97 | for pkg in self.cargo.packages() { | ||
98 | for dep in pkg.dependencies(&self.cargo) { | ||
99 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { | ||
100 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | ||
101 | if let Err(_) = crate_graph.add_dep(from, dep.name.clone().into(), to) { | ||
102 | log::error!( | ||
103 | "cyclic dependency {} -> {}", | ||
104 | pkg.name(&self.cargo), | ||
105 | dep.pkg.name(&self.cargo) | ||
106 | ) | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | |||
113 | crate_graph | ||
114 | } | ||
115 | } | ||
116 | |||
117 | fn find_cargo_toml(path: &Path) -> Result<PathBuf> { | ||
118 | if path.ends_with("Cargo.toml") { | ||
119 | return Ok(path.to_path_buf()); | ||
120 | } | ||
121 | let mut curr = Some(path); | ||
122 | while let Some(path) = curr { | ||
123 | let candidate = path.join("Cargo.toml"); | ||
124 | if candidate.exists() { | ||
125 | return Ok(candidate); | ||
126 | } | ||
127 | curr = path.parent(); | ||
128 | } | ||
129 | bail!("can't find Cargo.toml at {}", path.display()) | ||
130 | } | ||