aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_project_model/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_project_model/src')
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs79
-rw-r--r--crates/ra_project_model/src/json_project.rs56
-rw-r--r--crates/ra_project_model/src/lib.rs220
-rw-r--r--crates/ra_project_model/src/project_json.rs129
-rw-r--r--crates/ra_project_model/src/sysroot.rs28
5 files changed, 285 insertions, 227 deletions
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index a306ce95f..4182ca156 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -9,6 +9,7 @@ use std::{
9 9
10use anyhow::{Context, Result}; 10use anyhow::{Context, Result};
11use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; 11use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
12use paths::{AbsPath, AbsPathBuf};
12use ra_arena::{Arena, Idx}; 13use ra_arena::{Arena, Idx};
13use ra_db::Edition; 14use ra_db::Edition;
14use rustc_hash::FxHashMap; 15use rustc_hash::FxHashMap;
@@ -20,11 +21,14 @@ use rustc_hash::FxHashMap;
20/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates, 21/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
21/// while this knows about `Packages` & `Targets`: purely cargo-related 22/// while this knows about `Packages` & `Targets`: purely cargo-related
22/// concepts. 23/// concepts.
23#[derive(Debug, Clone)] 24///
25/// We use absolute paths here, `cargo metadata` guarantees to always produce
26/// abs paths.
27#[derive(Debug, Clone, Eq, PartialEq)]
24pub struct CargoWorkspace { 28pub struct CargoWorkspace {
25 packages: Arena<PackageData>, 29 packages: Arena<PackageData>,
26 targets: Arena<TargetData>, 30 targets: Arena<TargetData>,
27 workspace_root: PathBuf, 31 workspace_root: AbsPathBuf,
28} 32}
29 33
30impl ops::Index<Package> for CargoWorkspace { 34impl ops::Index<Package> for CargoWorkspace {
@@ -41,7 +45,7 @@ impl ops::Index<Target> for CargoWorkspace {
41 } 45 }
42} 46}
43 47
44#[derive(Clone, Debug, PartialEq, Eq)] 48#[derive(Default, Clone, Debug, PartialEq, Eq)]
45pub struct CargoConfig { 49pub struct CargoConfig {
46 /// Do not activate the `default` feature. 50 /// Do not activate the `default` feature.
47 pub no_default_features: bool, 51 pub no_default_features: bool,
@@ -60,48 +64,36 @@ pub struct CargoConfig {
60 pub target: Option<String>, 64 pub target: Option<String>,
61} 65}
62 66
63impl Default for CargoConfig {
64 fn default() -> Self {
65 CargoConfig {
66 no_default_features: false,
67 all_features: true,
68 features: Vec::new(),
69 load_out_dirs_from_check: false,
70 target: None,
71 }
72 }
73}
74
75pub type Package = Idx<PackageData>; 67pub type Package = Idx<PackageData>;
76 68
77pub type Target = Idx<TargetData>; 69pub type Target = Idx<TargetData>;
78 70
79#[derive(Debug, Clone)] 71#[derive(Debug, Clone, Eq, PartialEq)]
80pub struct PackageData { 72pub struct PackageData {
81 pub version: String, 73 pub version: String,
82 pub name: String, 74 pub name: String,
83 pub manifest: PathBuf, 75 pub manifest: AbsPathBuf,
84 pub targets: Vec<Target>, 76 pub targets: Vec<Target>,
85 pub is_member: bool, 77 pub is_member: bool,
86 pub dependencies: Vec<PackageDependency>, 78 pub dependencies: Vec<PackageDependency>,
87 pub edition: Edition, 79 pub edition: Edition,
88 pub features: Vec<String>, 80 pub features: Vec<String>,
89 pub cfgs: Vec<String>, 81 pub cfgs: Vec<String>,
90 pub out_dir: Option<PathBuf>, 82 pub out_dir: Option<AbsPathBuf>,
91 pub proc_macro_dylib_path: Option<PathBuf>, 83 pub proc_macro_dylib_path: Option<AbsPathBuf>,
92} 84}
93 85
94#[derive(Debug, Clone)] 86#[derive(Debug, Clone, Eq, PartialEq)]
95pub struct PackageDependency { 87pub struct PackageDependency {
96 pub pkg: Package, 88 pub pkg: Package,
97 pub name: String, 89 pub name: String,
98} 90}
99 91
100#[derive(Debug, Clone)] 92#[derive(Debug, Clone, Eq, PartialEq)]
101pub struct TargetData { 93pub struct TargetData {
102 pub package: Package, 94 pub package: Package,
103 pub name: String, 95 pub name: String,
104 pub root: PathBuf, 96 pub root: AbsPathBuf,
105 pub kind: TargetKind, 97 pub kind: TargetKind,
106 pub is_proc_macro: bool, 98 pub is_proc_macro: bool,
107} 99}
@@ -135,19 +127,19 @@ impl TargetKind {
135} 127}
136 128
137impl PackageData { 129impl PackageData {
138 pub fn root(&self) -> &Path { 130 pub fn root(&self) -> &AbsPath {
139 self.manifest.parent().unwrap() 131 self.manifest.parent().unwrap()
140 } 132 }
141} 133}
142 134
143impl CargoWorkspace { 135impl CargoWorkspace {
144 pub fn from_cargo_metadata( 136 pub fn from_cargo_metadata(
145 cargo_toml: &Path, 137 cargo_toml: &AbsPath,
146 cargo_features: &CargoConfig, 138 cargo_features: &CargoConfig,
147 ) -> Result<CargoWorkspace> { 139 ) -> Result<CargoWorkspace> {
148 let mut meta = MetadataCommand::new(); 140 let mut meta = MetadataCommand::new();
149 meta.cargo_path(ra_toolchain::cargo()); 141 meta.cargo_path(ra_toolchain::cargo());
150 meta.manifest_path(cargo_toml); 142 meta.manifest_path(cargo_toml.to_path_buf());
151 if cargo_features.all_features { 143 if cargo_features.all_features {
152 meta.features(CargoOpt::AllFeatures); 144 meta.features(CargoOpt::AllFeatures);
153 } else if cargo_features.no_default_features { 145 } else if cargo_features.no_default_features {
@@ -158,12 +150,12 @@ impl CargoWorkspace {
158 meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone())); 150 meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone()));
159 } 151 }
160 if let Some(parent) = cargo_toml.parent() { 152 if let Some(parent) = cargo_toml.parent() {
161 meta.current_dir(parent); 153 meta.current_dir(parent.to_path_buf());
162 } 154 }
163 if let Some(target) = cargo_features.target.as_ref() { 155 if let Some(target) = cargo_features.target.as_ref() {
164 meta.other_options(vec![String::from("--filter-platform"), target.clone()]); 156 meta.other_options(vec![String::from("--filter-platform"), target.clone()]);
165 } 157 }
166 let meta = meta.exec().with_context(|| { 158 let mut meta = meta.exec().with_context(|| {
167 format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) 159 format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display())
168 })?; 160 })?;
169 161
@@ -183,6 +175,7 @@ impl CargoWorkspace {
183 175
184 let ws_members = &meta.workspace_members; 176 let ws_members = &meta.workspace_members;
185 177
178 meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
186 for meta_pkg in meta.packages { 179 for meta_pkg in meta.packages {
187 let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } = 180 let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } =
188 meta_pkg; 181 meta_pkg;
@@ -193,7 +186,7 @@ impl CargoWorkspace {
193 let pkg = packages.alloc(PackageData { 186 let pkg = packages.alloc(PackageData {
194 name, 187 name,
195 version: version.to_string(), 188 version: version.to_string(),
196 manifest: manifest_path, 189 manifest: AbsPathBuf::assert(manifest_path),
197 targets: Vec::new(), 190 targets: Vec::new(),
198 is_member, 191 is_member,
199 edition, 192 edition,
@@ -210,7 +203,7 @@ impl CargoWorkspace {
210 let tgt = targets.alloc(TargetData { 203 let tgt = targets.alloc(TargetData {
211 package: pkg, 204 package: pkg,
212 name: meta_tgt.name, 205 name: meta_tgt.name,
213 root: meta_tgt.src_path.clone(), 206 root: AbsPathBuf::assert(meta_tgt.src_path.clone()),
214 kind: TargetKind::new(meta_tgt.kind.as_slice()), 207 kind: TargetKind::new(meta_tgt.kind.as_slice()),
215 is_proc_macro, 208 is_proc_macro,
216 }); 209 });
@@ -218,7 +211,7 @@ impl CargoWorkspace {
218 } 211 }
219 } 212 }
220 let resolve = meta.resolve.expect("metadata executed with deps"); 213 let resolve = meta.resolve.expect("metadata executed with deps");
221 for node in resolve.nodes { 214 for mut node in resolve.nodes {
222 let source = match pkg_by_id.get(&node.id) { 215 let source = match pkg_by_id.get(&node.id) {
223 Some(&src) => src, 216 Some(&src) => src,
224 // FIXME: replace this and a similar branch below with `.unwrap`, once 217 // FIXME: replace this and a similar branch below with `.unwrap`, once
@@ -229,6 +222,7 @@ impl CargoWorkspace {
229 continue; 222 continue;
230 } 223 }
231 }; 224 };
225 node.deps.sort_by(|a, b| a.pkg.cmp(&b.pkg));
232 for dep_node in node.deps { 226 for dep_node in node.deps {
233 let pkg = match pkg_by_id.get(&dep_node.pkg) { 227 let pkg = match pkg_by_id.get(&dep_node.pkg) {
234 Some(&pkg) => pkg, 228 Some(&pkg) => pkg,
@@ -246,21 +240,22 @@ impl CargoWorkspace {
246 packages[source].features.extend(node.features); 240 packages[source].features.extend(node.features);
247 } 241 }
248 242
249 Ok(CargoWorkspace { packages, targets, workspace_root: meta.workspace_root }) 243 let workspace_root = AbsPathBuf::assert(meta.workspace_root);
244 Ok(CargoWorkspace { packages, targets, workspace_root: workspace_root })
250 } 245 }
251 246
252 pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a { 247 pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a {
253 self.packages.iter().map(|(id, _pkg)| id) 248 self.packages.iter().map(|(id, _pkg)| id)
254 } 249 }
255 250
256 pub fn target_by_root(&self, root: &Path) -> Option<Target> { 251 pub fn target_by_root(&self, root: &AbsPath) -> Option<Target> {
257 self.packages() 252 self.packages()
258 .filter_map(|pkg| self[pkg].targets.iter().find(|&&it| self[it].root == root)) 253 .filter_map(|pkg| self[pkg].targets.iter().find(|&&it| &self[it].root == root))
259 .next() 254 .next()
260 .copied() 255 .copied()
261 } 256 }
262 257
263 pub fn workspace_root(&self) -> &Path { 258 pub fn workspace_root(&self) -> &AbsPath {
264 &self.workspace_root 259 &self.workspace_root
265 } 260 }
266 261
@@ -279,8 +274,8 @@ impl CargoWorkspace {
279 274
280#[derive(Debug, Clone, Default)] 275#[derive(Debug, Clone, Default)]
281pub struct ExternResources { 276pub struct ExternResources {
282 out_dirs: FxHashMap<PackageId, PathBuf>, 277 out_dirs: FxHashMap<PackageId, AbsPathBuf>,
283 proc_dylib_paths: FxHashMap<PackageId, PathBuf>, 278 proc_dylib_paths: FxHashMap<PackageId, AbsPathBuf>,
284 cfgs: FxHashMap<PackageId, Vec<String>>, 279 cfgs: FxHashMap<PackageId, Vec<String>>,
285} 280}
286 281
@@ -308,8 +303,13 @@ pub fn load_extern_resources(
308 if let Ok(message) = message { 303 if let Ok(message) = message {
309 match message { 304 match message {
310 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => { 305 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => {
311 res.out_dirs.insert(package_id.clone(), out_dir); 306 // cargo_metadata crate returns default (empty) path for
312 res.cfgs.insert(package_id, cfgs); 307 // older cargos, which is not absolute, so work around that.
308 if out_dir != PathBuf::default() {
309 let out_dir = AbsPathBuf::assert(out_dir);
310 res.out_dirs.insert(package_id.clone(), out_dir);
311 res.cfgs.insert(package_id, cfgs);
312 }
313 } 313 }
314 Message::CompilerArtifact(message) => { 314 Message::CompilerArtifact(message) => {
315 if message.target.kind.contains(&"proc-macro".to_string()) { 315 if message.target.kind.contains(&"proc-macro".to_string()) {
@@ -317,7 +317,8 @@ pub fn load_extern_resources(
317 // Skip rmeta file 317 // Skip rmeta file
318 if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) 318 if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name))
319 { 319 {
320 res.proc_dylib_paths.insert(package_id, filename.clone()); 320 let filename = AbsPathBuf::assert(filename.clone());
321 res.proc_dylib_paths.insert(package_id, filename);
321 } 322 }
322 } 323 }
323 } 324 }
diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs
deleted file mode 100644
index b030c8a6a..000000000
--- a/crates/ra_project_model/src/json_project.rs
+++ /dev/null
@@ -1,56 +0,0 @@
1//! FIXME: write short doc here
2
3use std::path::PathBuf;
4
5use rustc_hash::{FxHashMap, FxHashSet};
6use serde::Deserialize;
7
8/// A root points to the directory which contains Rust crates. rust-analyzer watches all files in
9/// all roots. Roots might be nested.
10#[derive(Clone, Debug, Deserialize)]
11#[serde(transparent)]
12pub struct Root {
13 pub(crate) path: PathBuf,
14}
15
16/// A crate points to the root module of a crate and lists the dependencies of the crate. This is
17/// useful in creating the crate graph.
18#[derive(Clone, Debug, Deserialize)]
19pub struct Crate {
20 pub(crate) root_module: PathBuf,
21 pub(crate) edition: Edition,
22 pub(crate) deps: Vec<Dep>,
23 pub(crate) atom_cfgs: FxHashSet<String>,
24 pub(crate) key_value_cfgs: FxHashMap<String, String>,
25 pub(crate) out_dir: Option<PathBuf>,
26 pub(crate) proc_macro_dylib_path: Option<PathBuf>,
27}
28
29#[derive(Clone, Copy, Debug, Deserialize)]
30#[serde(rename = "edition")]
31pub enum Edition {
32 #[serde(rename = "2015")]
33 Edition2015,
34 #[serde(rename = "2018")]
35 Edition2018,
36}
37
38/// Identifies a crate by position in the crates array.
39#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd)]
40#[serde(transparent)]
41pub struct CrateId(pub usize);
42
43/// A dependency of a crate, identified by its id in the crates array and name.
44#[derive(Clone, Debug, Deserialize)]
45pub struct Dep {
46 #[serde(rename = "crate")]
47 pub(crate) krate: CrateId,
48 pub(crate) name: String,
49}
50
51/// Roots and crates that compose this Rust project.
52#[derive(Clone, Debug, Deserialize)]
53pub struct JsonProject {
54 pub(crate) roots: Vec<Root>,
55 pub(crate) crates: Vec<Crate>,
56}
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index a2e9f65ef..b9c5424bf 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -1,35 +1,35 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3mod cargo_workspace; 3mod cargo_workspace;
4mod json_project; 4mod project_json;
5mod sysroot; 5mod sysroot;
6 6
7use std::{ 7use std::{
8 fs::{read_dir, File, ReadDir}, 8 fs::{self, read_dir, ReadDir},
9 io::{self, BufReader}, 9 io,
10 path::{Path, PathBuf}, 10 path::Path,
11 process::{Command, Output}, 11 process::{Command, Output},
12}; 12};
13 13
14use anyhow::{bail, Context, Result}; 14use anyhow::{bail, Context, Result};
15use paths::{AbsPath, AbsPathBuf};
15use ra_cfg::CfgOptions; 16use ra_cfg::CfgOptions;
16use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; 17use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId};
17use rustc_hash::FxHashMap; 18use rustc_hash::{FxHashMap, FxHashSet};
18use serde_json::from_reader;
19 19
20pub use crate::{ 20pub use crate::{
21 cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind}, 21 cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind},
22 json_project::JsonProject, 22 project_json::{ProjectJson, ProjectJsonData},
23 sysroot::Sysroot, 23 sysroot::Sysroot,
24}; 24};
25pub use ra_proc_macro::ProcMacroClient; 25pub use ra_proc_macro::ProcMacroClient;
26 26
27#[derive(Debug, Clone)] 27#[derive(Debug, Clone, Eq, PartialEq)]
28pub enum ProjectWorkspace { 28pub enum ProjectWorkspace {
29 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. 29 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
30 Cargo { cargo: CargoWorkspace, sysroot: Sysroot }, 30 Cargo { cargo: CargoWorkspace, sysroot: Sysroot },
31 /// Project workspace was manually specified using a `rust-project.json` file. 31 /// Project workspace was manually specified using a `rust-project.json` file.
32 Json { project: JsonProject }, 32 Json { project: ProjectJson },
33} 33}
34 34
35/// `PackageRoot` describes a package root folder. 35/// `PackageRoot` describes a package root folder.
@@ -38,44 +38,48 @@ pub enum ProjectWorkspace {
38#[derive(Debug, Clone)] 38#[derive(Debug, Clone)]
39pub struct PackageRoot { 39pub struct PackageRoot {
40 /// Path to the root folder 40 /// Path to the root folder
41 path: PathBuf, 41 path: AbsPathBuf,
42 /// Is a member of the current workspace 42 /// Is a member of the current workspace
43 is_member: bool, 43 is_member: bool,
44 out_dir: Option<AbsPathBuf>,
44} 45}
45impl PackageRoot { 46impl PackageRoot {
46 pub fn new_member(path: PathBuf) -> PackageRoot { 47 pub fn new_member(path: AbsPathBuf) -> PackageRoot {
47 Self { path, is_member: true } 48 Self { path, is_member: true, out_dir: None }
48 } 49 }
49 pub fn new_non_member(path: PathBuf) -> PackageRoot { 50 pub fn new_non_member(path: AbsPathBuf) -> PackageRoot {
50 Self { path, is_member: false } 51 Self { path, is_member: false, out_dir: None }
51 } 52 }
52 pub fn path(&self) -> &Path { 53 pub fn path(&self) -> &AbsPath {
53 &self.path 54 &self.path
54 } 55 }
56 pub fn out_dir(&self) -> Option<&AbsPath> {
57 self.out_dir.as_deref()
58 }
55 pub fn is_member(&self) -> bool { 59 pub fn is_member(&self) -> bool {
56 self.is_member 60 self.is_member
57 } 61 }
58} 62}
59 63
60#[derive(Debug, Clone, PartialEq, Eq, Hash)] 64#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
61pub enum ProjectRoot { 65pub enum ProjectManifest {
62 ProjectJson(PathBuf), 66 ProjectJson(AbsPathBuf),
63 CargoToml(PathBuf), 67 CargoToml(AbsPathBuf),
64} 68}
65 69
66impl ProjectRoot { 70impl ProjectManifest {
67 pub fn from_manifest_file(path: PathBuf) -> Result<ProjectRoot> { 71 pub fn from_manifest_file(path: AbsPathBuf) -> Result<ProjectManifest> {
68 if path.ends_with("rust-project.json") { 72 if path.ends_with("rust-project.json") {
69 return Ok(ProjectRoot::ProjectJson(path)); 73 return Ok(ProjectManifest::ProjectJson(path));
70 } 74 }
71 if path.ends_with("Cargo.toml") { 75 if path.ends_with("Cargo.toml") {
72 return Ok(ProjectRoot::CargoToml(path)); 76 return Ok(ProjectManifest::CargoToml(path));
73 } 77 }
74 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) 78 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
75 } 79 }
76 80
77 pub fn discover_single(path: &Path) -> Result<ProjectRoot> { 81 pub fn discover_single(path: &AbsPath) -> Result<ProjectManifest> {
78 let mut candidates = ProjectRoot::discover(path)?; 82 let mut candidates = ProjectManifest::discover(path)?;
79 let res = match candidates.pop() { 83 let res = match candidates.pop() {
80 None => bail!("no projects"), 84 None => bail!("no projects"),
81 Some(it) => it, 85 Some(it) => it,
@@ -87,23 +91,23 @@ impl ProjectRoot {
87 Ok(res) 91 Ok(res)
88 } 92 }
89 93
90 pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> { 94 pub fn discover(path: &AbsPath) -> io::Result<Vec<ProjectManifest>> {
91 if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { 95 if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") {
92 return Ok(vec![ProjectRoot::ProjectJson(project_json)]); 96 return Ok(vec![ProjectManifest::ProjectJson(project_json)]);
93 } 97 }
94 return find_cargo_toml(path) 98 return find_cargo_toml(path)
95 .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); 99 .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect());
96 100
97 fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { 101 fn find_cargo_toml(path: &AbsPath) -> io::Result<Vec<AbsPathBuf>> {
98 match find_in_parent_dirs(path, "Cargo.toml") { 102 match find_in_parent_dirs(path, "Cargo.toml") {
99 Some(it) => Ok(vec![it]), 103 Some(it) => Ok(vec![it]),
100 None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)), 104 None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)),
101 } 105 }
102 } 106 }
103 107
104 fn find_in_parent_dirs(path: &Path, target_file_name: &str) -> Option<PathBuf> { 108 fn find_in_parent_dirs(path: &AbsPath, target_file_name: &str) -> Option<AbsPathBuf> {
105 if path.ends_with(target_file_name) { 109 if path.ends_with(target_file_name) {
106 return Some(path.to_owned()); 110 return Some(path.to_path_buf());
107 } 111 }
108 112
109 let mut curr = Some(path); 113 let mut curr = Some(path);
@@ -119,37 +123,50 @@ impl ProjectRoot {
119 None 123 None
120 } 124 }
121 125
122 fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<PathBuf> { 126 fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<AbsPathBuf> {
123 // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects 127 // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects
124 entities 128 entities
125 .filter_map(Result::ok) 129 .filter_map(Result::ok)
126 .map(|it| it.path().join("Cargo.toml")) 130 .map(|it| it.path().join("Cargo.toml"))
127 .filter(|it| it.exists()) 131 .filter(|it| it.exists())
132 .map(AbsPathBuf::assert)
128 .collect() 133 .collect()
129 } 134 }
130 } 135 }
136
137 pub fn discover_all(paths: &[impl AsRef<AbsPath>]) -> Vec<ProjectManifest> {
138 let mut res = paths
139 .iter()
140 .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok())
141 .flatten()
142 .collect::<FxHashSet<_>>()
143 .into_iter()
144 .collect::<Vec<_>>();
145 res.sort();
146 res
147 }
131} 148}
132 149
133impl ProjectWorkspace { 150impl ProjectWorkspace {
134 pub fn load( 151 pub fn load(
135 root: ProjectRoot, 152 manifest: ProjectManifest,
136 cargo_features: &CargoConfig, 153 cargo_config: &CargoConfig,
137 with_sysroot: bool, 154 with_sysroot: bool,
138 ) -> Result<ProjectWorkspace> { 155 ) -> Result<ProjectWorkspace> {
139 let res = match root { 156 let res = match manifest {
140 ProjectRoot::ProjectJson(project_json) => { 157 ProjectManifest::ProjectJson(project_json) => {
141 let file = File::open(&project_json).with_context(|| { 158 let file = fs::read_to_string(&project_json).with_context(|| {
142 format!("Failed to open json file {}", project_json.display()) 159 format!("Failed to read json file {}", project_json.display())
143 })?; 160 })?;
144 let reader = BufReader::new(file); 161 let data = serde_json::from_str(&file).with_context(|| {
145 ProjectWorkspace::Json { 162 format!("Failed to deserialize json file {}", project_json.display())
146 project: from_reader(reader).with_context(|| { 163 })?;
147 format!("Failed to deserialize json file {}", project_json.display()) 164 let project_location = project_json.parent().unwrap().to_path_buf();
148 })?, 165 let project = ProjectJson::new(&project_location, data);
149 } 166 ProjectWorkspace::Json { project }
150 } 167 }
151 ProjectRoot::CargoToml(cargo_toml) => { 168 ProjectManifest::CargoToml(cargo_toml) => {
152 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) 169 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_config)
153 .with_context(|| { 170 .with_context(|| {
154 format!( 171 format!(
155 "Failed to read Cargo metadata from Cargo.toml file {}", 172 "Failed to read Cargo metadata from Cargo.toml file {}",
@@ -186,6 +203,7 @@ impl ProjectWorkspace {
186 .map(|pkg| PackageRoot { 203 .map(|pkg| PackageRoot {
187 path: cargo[pkg].root().to_path_buf(), 204 path: cargo[pkg].root().to_path_buf(),
188 is_member: cargo[pkg].is_member, 205 is_member: cargo[pkg].is_member,
206 out_dir: cargo[pkg].out_dir.clone(),
189 }) 207 })
190 .chain(sysroot.crates().map(|krate| { 208 .chain(sysroot.crates().map(|krate| {
191 PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf()) 209 PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf())
@@ -194,18 +212,7 @@ impl ProjectWorkspace {
194 } 212 }
195 } 213 }
196 214
197 pub fn out_dirs(&self) -> Vec<PathBuf> { 215 pub fn proc_macro_dylib_paths(&self) -> Vec<AbsPathBuf> {
198 match self {
199 ProjectWorkspace::Json { project } => {
200 project.crates.iter().filter_map(|krate| krate.out_dir.as_ref()).cloned().collect()
201 }
202 ProjectWorkspace::Cargo { cargo, sysroot: _ } => {
203 cargo.packages().filter_map(|pkg| cargo[pkg].out_dir.as_ref()).cloned().collect()
204 }
205 }
206 }
207
208 pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> {
209 match self { 216 match self {
210 ProjectWorkspace::Json { project } => project 217 ProjectWorkspace::Json { project } => project
211 .crates 218 .crates
@@ -223,7 +230,7 @@ impl ProjectWorkspace {
223 230
224 pub fn n_packages(&self) -> usize { 231 pub fn n_packages(&self) -> usize {
225 match self { 232 match self {
226 ProjectWorkspace::Json { project } => project.crates.len(), 233 ProjectWorkspace::Json { project, .. } => project.crates.len(),
227 ProjectWorkspace::Cargo { cargo, sysroot } => { 234 ProjectWorkspace::Cargo { cargo, sysroot } => {
228 cargo.packages().len() + sysroot.crates().len() 235 cargo.packages().len() + sysroot.crates().len()
229 } 236 }
@@ -232,61 +239,51 @@ impl ProjectWorkspace {
232 239
233 pub fn to_crate_graph( 240 pub fn to_crate_graph(
234 &self, 241 &self,
235 default_cfg_options: &CfgOptions, 242 target: Option<&str>,
236 extern_source_roots: &FxHashMap<PathBuf, ExternSourceId>,
237 proc_macro_client: &ProcMacroClient, 243 proc_macro_client: &ProcMacroClient,
238 load: &mut dyn FnMut(&Path) -> Option<FileId>, 244 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
239 ) -> CrateGraph { 245 ) -> CrateGraph {
240 let mut crate_graph = CrateGraph::default(); 246 let mut crate_graph = CrateGraph::default();
241 match self { 247 match self {
242 ProjectWorkspace::Json { project } => { 248 ProjectWorkspace::Json { project } => {
249 let mut target_cfg_map = FxHashMap::<Option<&str>, CfgOptions>::default();
243 let crates: FxHashMap<_, _> = project 250 let crates: FxHashMap<_, _> = project
244 .crates 251 .crates
245 .iter() 252 .iter()
246 .enumerate() 253 .enumerate()
247 .filter_map(|(seq_index, krate)| { 254 .filter_map(|(seq_index, krate)| {
248 let file_id = load(&krate.root_module)?; 255 let file_path = &krate.root_module;
249 let edition = match krate.edition { 256 let file_id = load(&file_path)?;
250 json_project::Edition::Edition2015 => Edition::Edition2015,
251 json_project::Edition::Edition2018 => Edition::Edition2018,
252 };
253 let cfg_options = {
254 let mut opts = default_cfg_options.clone();
255 for name in &krate.atom_cfgs {
256 opts.insert_atom(name.into());
257 }
258 for (key, value) in &krate.key_value_cfgs {
259 opts.insert_key_value(key.into(), value.into());
260 }
261 opts
262 };
263 257
264 let mut env = Env::default(); 258 let mut env = Env::default();
265 let mut extern_source = ExternSource::default();
266 if let Some(out_dir) = &krate.out_dir { 259 if let Some(out_dir) = &krate.out_dir {
267 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() 260 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
268 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { 261 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
269 env.set("OUT_DIR", out_dir); 262 env.set("OUT_DIR", out_dir);
270 } 263 }
271 if let Some(&extern_source_id) = extern_source_roots.get(out_dir) {
272 extern_source.set_extern_path(&out_dir, extern_source_id);
273 }
274 } 264 }
275 let proc_macro = krate 265 let proc_macro = krate
276 .proc_macro_dylib_path 266 .proc_macro_dylib_path
277 .clone() 267 .clone()
278 .map(|it| proc_macro_client.by_dylib_path(&it)); 268 .map(|it| proc_macro_client.by_dylib_path(&it));
269
270 let target = krate.target.as_deref().or(target);
271 let target_cfgs = target_cfg_map
272 .entry(target.clone())
273 .or_insert_with(|| get_rustc_cfg_options(target.as_deref()));
274 let mut cfg_options = krate.cfg.clone();
275 cfg_options.append(target_cfgs);
276
279 // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env 277 // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env
280 Some(( 278 Some((
281 json_project::CrateId(seq_index), 279 CrateId(seq_index as u32),
282 crate_graph.add_crate_root( 280 crate_graph.add_crate_root(
283 file_id, 281 file_id,
284 edition, 282 krate.edition,
285 // FIXME json definitions can store the crate name 283 // FIXME json definitions can store the crate name
286 None, 284 None,
287 cfg_options, 285 cfg_options,
288 env, 286 env,
289 extern_source,
290 proc_macro.unwrap_or_default(), 287 proc_macro.unwrap_or_default(),
291 ), 288 ),
292 )) 289 ))
@@ -295,15 +292,12 @@ impl ProjectWorkspace {
295 292
296 for (id, krate) in project.crates.iter().enumerate() { 293 for (id, krate) in project.crates.iter().enumerate() {
297 for dep in &krate.deps { 294 for dep in &krate.deps {
298 let from_crate_id = json_project::CrateId(id); 295 let from_crate_id = CrateId(id as u32);
299 let to_crate_id = dep.krate; 296 let to_crate_id = dep.crate_id;
300 if let (Some(&from), Some(&to)) = 297 if let (Some(&from), Some(&to)) =
301 (crates.get(&from_crate_id), crates.get(&to_crate_id)) 298 (crates.get(&from_crate_id), crates.get(&to_crate_id))
302 { 299 {
303 if crate_graph 300 if crate_graph.add_dep(from, dep.name.clone(), to).is_err() {
304 .add_dep(from, CrateName::new(&dep.name).unwrap(), to)
305 .is_err()
306 {
307 log::error!( 301 log::error!(
308 "cyclic dependency {:?} -> {:?}", 302 "cyclic dependency {:?} -> {:?}",
309 from_crate_id, 303 from_crate_id,
@@ -315,31 +309,22 @@ impl ProjectWorkspace {
315 } 309 }
316 } 310 }
317 ProjectWorkspace::Cargo { cargo, sysroot } => { 311 ProjectWorkspace::Cargo { cargo, sysroot } => {
312 let mut cfg_options = get_rustc_cfg_options(target);
313
318 let sysroot_crates: FxHashMap<_, _> = sysroot 314 let sysroot_crates: FxHashMap<_, _> = sysroot
319 .crates() 315 .crates()
320 .filter_map(|krate| { 316 .filter_map(|krate| {
321 let file_id = load(&sysroot[krate].root)?; 317 let file_id = load(&sysroot[krate].root)?;
322 318
323 // Crates from sysroot have `cfg(test)` disabled
324 let cfg_options = {
325 let mut opts = default_cfg_options.clone();
326 opts.remove_atom("test");
327 opts
328 };
329
330 let env = Env::default(); 319 let env = Env::default();
331 let extern_source = ExternSource::default();
332 let proc_macro = vec![]; 320 let proc_macro = vec![];
333 let crate_name = CrateName::new(&sysroot[krate].name) 321 let name = sysroot[krate].name.clone();
334 .expect("Sysroot crate names should not contain dashes");
335
336 let crate_id = crate_graph.add_crate_root( 322 let crate_id = crate_graph.add_crate_root(
337 file_id, 323 file_id,
338 Edition::Edition2018, 324 Edition::Edition2018,
339 Some(crate_name), 325 Some(name),
340 cfg_options, 326 cfg_options.clone(),
341 env, 327 env,
342 extern_source,
343 proc_macro, 328 proc_macro,
344 ); 329 );
345 Some((krate, crate_id)) 330 Some((krate, crate_id))
@@ -368,6 +353,10 @@ impl ProjectWorkspace {
368 353
369 let mut pkg_to_lib_crate = FxHashMap::default(); 354 let mut pkg_to_lib_crate = FxHashMap::default();
370 let mut pkg_crates = FxHashMap::default(); 355 let mut pkg_crates = FxHashMap::default();
356
357 // Add test cfg for non-sysroot crates
358 cfg_options.insert_atom("test".into());
359
371 // Next, create crates for each package, target pair 360 // Next, create crates for each package, target pair
372 for pkg in cargo.packages() { 361 for pkg in cargo.packages() {
373 let mut lib_tgt = None; 362 let mut lib_tgt = None;
@@ -376,7 +365,7 @@ impl ProjectWorkspace {
376 if let Some(file_id) = load(root) { 365 if let Some(file_id) = load(root) {
377 let edition = cargo[pkg].edition; 366 let edition = cargo[pkg].edition;
378 let cfg_options = { 367 let cfg_options = {
379 let mut opts = default_cfg_options.clone(); 368 let mut opts = cfg_options.clone();
380 for feature in cargo[pkg].features.iter() { 369 for feature in cargo[pkg].features.iter() {
381 opts.insert_key_value("feature".into(), feature.into()); 370 opts.insert_key_value("feature".into(), feature.into());
382 } 371 }
@@ -392,15 +381,11 @@ impl ProjectWorkspace {
392 opts 381 opts
393 }; 382 };
394 let mut env = Env::default(); 383 let mut env = Env::default();
395 let mut extern_source = ExternSource::default();
396 if let Some(out_dir) = &cargo[pkg].out_dir { 384 if let Some(out_dir) = &cargo[pkg].out_dir {
397 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() 385 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
398 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { 386 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
399 env.set("OUT_DIR", out_dir); 387 env.set("OUT_DIR", out_dir);
400 } 388 }
401 if let Some(&extern_source_id) = extern_source_roots.get(out_dir) {
402 extern_source.set_extern_path(&out_dir, extern_source_id);
403 }
404 } 389 }
405 let proc_macro = cargo[pkg] 390 let proc_macro = cargo[pkg]
406 .proc_macro_dylib_path 391 .proc_macro_dylib_path
@@ -411,10 +396,9 @@ impl ProjectWorkspace {
411 let crate_id = crate_graph.add_crate_root( 396 let crate_id = crate_graph.add_crate_root(
412 file_id, 397 file_id,
413 edition, 398 edition,
414 Some(CrateName::normalize_dashes(&cargo[pkg].name)), 399 Some(cargo[pkg].name.clone()),
415 cfg_options, 400 cfg_options,
416 env, 401 env,
417 extern_source,
418 proc_macro.clone(), 402 proc_macro.clone(),
419 ); 403 );
420 if cargo[tgt].kind == TargetKind::Lib { 404 if cargo[tgt].kind == TargetKind::Lib {
@@ -520,20 +504,20 @@ impl ProjectWorkspace {
520 crate_graph 504 crate_graph
521 } 505 }
522 506
523 pub fn workspace_root_for(&self, path: &Path) -> Option<&Path> { 507 pub fn workspace_root_for(&self, path: &Path) -> Option<&AbsPath> {
524 match self { 508 match self {
525 ProjectWorkspace::Cargo { cargo, .. } => { 509 ProjectWorkspace::Cargo { cargo, .. } => {
526 Some(cargo.workspace_root()).filter(|root| path.starts_with(root)) 510 Some(cargo.workspace_root()).filter(|root| path.starts_with(root))
527 } 511 }
528 ProjectWorkspace::Json { project: JsonProject { roots, .. } } => roots 512 ProjectWorkspace::Json { project: ProjectJson { roots, .. }, .. } => roots
529 .iter() 513 .iter()
530 .find(|root| path.starts_with(&root.path)) 514 .find(|root| path.starts_with(&root.path))
531 .map(|root| root.path.as_ref()), 515 .map(|root| root.path.as_path()),
532 } 516 }
533 } 517 }
534} 518}
535 519
536pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { 520fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions {
537 let mut cfg_options = CfgOptions::default(); 521 let mut cfg_options = CfgOptions::default();
538 522
539 // Some nightly-only cfgs, which are required for stdlib 523 // Some nightly-only cfgs, which are required for stdlib
@@ -551,7 +535,7 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions {
551 let mut cmd = Command::new(ra_toolchain::rustc()); 535 let mut cmd = Command::new(ra_toolchain::rustc());
552 cmd.args(&["--print", "cfg", "-O"]); 536 cmd.args(&["--print", "cfg", "-O"]);
553 if let Some(target) = target { 537 if let Some(target) = target {
554 cmd.args(&["--target", target.as_str()]); 538 cmd.args(&["--target", target]);
555 } 539 }
556 let output = output(cmd)?; 540 let output = output(cmd)?;
557 Ok(String::from_utf8(output.stdout)?) 541 Ok(String::from_utf8(output.stdout)?)
@@ -573,6 +557,8 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions {
573 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), 557 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e),
574 } 558 }
575 559
560 cfg_options.insert_atom("debug_assertions".into());
561
576 cfg_options 562 cfg_options
577} 563}
578 564
diff --git a/crates/ra_project_model/src/project_json.rs b/crates/ra_project_model/src/project_json.rs
new file mode 100644
index 000000000..b96227949
--- /dev/null
+++ b/crates/ra_project_model/src/project_json.rs
@@ -0,0 +1,129 @@
1//! FIXME: write short doc here
2
3use std::path::PathBuf;
4
5use paths::{AbsPath, AbsPathBuf};
6use ra_cfg::CfgOptions;
7use ra_db::{CrateId, CrateName, Dependency, Edition};
8use rustc_hash::FxHashSet;
9use serde::{de, Deserialize};
10use stdx::split_delim;
11
12/// Roots and crates that compose this Rust project.
13#[derive(Clone, Debug, Eq, PartialEq)]
14pub struct ProjectJson {
15 pub(crate) roots: Vec<Root>,
16 pub(crate) crates: Vec<Crate>,
17}
18
19/// A root points to the directory which contains Rust crates. rust-analyzer watches all files in
20/// all roots. Roots might be nested.
21#[derive(Clone, Debug, Eq, PartialEq)]
22pub struct Root {
23 pub(crate) path: AbsPathBuf,
24}
25
26/// A crate points to the root module of a crate and lists the dependencies of the crate. This is
27/// useful in creating the crate graph.
28#[derive(Clone, Debug, Eq, PartialEq)]
29pub struct Crate {
30 pub(crate) root_module: AbsPathBuf,
31 pub(crate) edition: Edition,
32 pub(crate) deps: Vec<Dependency>,
33 pub(crate) cfg: CfgOptions,
34 pub(crate) target: Option<String>,
35 pub(crate) out_dir: Option<AbsPathBuf>,
36 pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
37}
38
39impl ProjectJson {
40 pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson {
41 ProjectJson {
42 roots: data.roots.into_iter().map(|path| Root { path: base.join(path) }).collect(),
43 crates: data
44 .crates
45 .into_iter()
46 .map(|crate_data| Crate {
47 root_module: base.join(crate_data.root_module),
48 edition: crate_data.edition.into(),
49 deps: crate_data
50 .deps
51 .into_iter()
52 .map(|dep_data| Dependency {
53 crate_id: CrateId(dep_data.krate as u32),
54 name: dep_data.name,
55 })
56 .collect::<Vec<_>>(),
57 cfg: {
58 let mut cfg = CfgOptions::default();
59 for entry in &crate_data.cfg {
60 match split_delim(entry, '=') {
61 Some((key, value)) => {
62 cfg.insert_key_value(key.into(), value.into());
63 }
64 None => cfg.insert_atom(entry.into()),
65 }
66 }
67 cfg
68 },
69 target: crate_data.target,
70 out_dir: crate_data.out_dir.map(|it| base.join(it)),
71 proc_macro_dylib_path: crate_data.proc_macro_dylib_path.map(|it| base.join(it)),
72 })
73 .collect::<Vec<_>>(),
74 }
75 }
76}
77
78#[derive(Deserialize)]
79pub struct ProjectJsonData {
80 roots: Vec<PathBuf>,
81 crates: Vec<CrateData>,
82}
83
84#[derive(Deserialize)]
85struct CrateData {
86 root_module: PathBuf,
87 edition: EditionData,
88 deps: Vec<DepData>,
89 #[serde(default)]
90 cfg: FxHashSet<String>,
91 target: Option<String>,
92 out_dir: Option<PathBuf>,
93 proc_macro_dylib_path: Option<PathBuf>,
94}
95
96#[derive(Deserialize)]
97#[serde(rename = "edition")]
98enum EditionData {
99 #[serde(rename = "2015")]
100 Edition2015,
101 #[serde(rename = "2018")]
102 Edition2018,
103}
104
105impl From<EditionData> for Edition {
106 fn from(data: EditionData) -> Self {
107 match data {
108 EditionData::Edition2015 => Edition::Edition2015,
109 EditionData::Edition2018 => Edition::Edition2018,
110 }
111 }
112}
113
114#[derive(Deserialize)]
115struct DepData {
116 /// Identifies a crate by position in the crates array.
117 #[serde(rename = "crate")]
118 krate: usize,
119 #[serde(deserialize_with = "deserialize_crate_name")]
120 name: CrateName,
121}
122
123fn deserialize_crate_name<'de, D>(de: D) -> Result<CrateName, D::Error>
124where
125 D: de::Deserializer<'de>,
126{
127 let name = String::deserialize(de)?;
128 CrateName::new(&name).map_err(|err| de::Error::custom(format!("invalid crate name: {:?}", err)))
129}
diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs
index a8a196e64..68d134da4 100644
--- a/crates/ra_project_model/src/sysroot.rs
+++ b/crates/ra_project_model/src/sysroot.rs
@@ -1,27 +1,24 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::{ 3use std::{convert::TryFrom, env, ops, path::Path, process::Command};
4 env, ops,
5 path::{Path, PathBuf},
6 process::Command,
7};
8 4
9use anyhow::{bail, Result}; 5use anyhow::{bail, format_err, Result};
6use paths::{AbsPath, AbsPathBuf};
10use ra_arena::{Arena, Idx}; 7use ra_arena::{Arena, Idx};
11 8
12use crate::output; 9use crate::output;
13 10
14#[derive(Default, Debug, Clone)] 11#[derive(Default, Debug, Clone, Eq, PartialEq)]
15pub struct Sysroot { 12pub struct Sysroot {
16 crates: Arena<SysrootCrateData>, 13 crates: Arena<SysrootCrateData>,
17} 14}
18 15
19pub type SysrootCrate = Idx<SysrootCrateData>; 16pub type SysrootCrate = Idx<SysrootCrateData>;
20 17
21#[derive(Debug, Clone)] 18#[derive(Debug, Clone, Eq, PartialEq)]
22pub struct SysrootCrateData { 19pub struct SysrootCrateData {
23 pub name: String, 20 pub name: String,
24 pub root: PathBuf, 21 pub root: AbsPathBuf,
25 pub deps: Vec<SysrootCrate>, 22 pub deps: Vec<SysrootCrate>,
26} 23}
27 24
@@ -53,7 +50,7 @@ impl Sysroot {
53 self.crates.iter().map(|(id, _data)| id) 50 self.crates.iter().map(|(id, _data)| id)
54 } 51 }
55 52
56 pub fn discover(cargo_toml: &Path) -> Result<Sysroot> { 53 pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> {
57 let src = get_or_install_rust_src(cargo_toml)?; 54 let src = get_or_install_rust_src(cargo_toml)?;
58 let mut sysroot = Sysroot { crates: Arena::default() }; 55 let mut sysroot = Sysroot { crates: Arena::default() };
59 for name in SYSROOT_CRATES.trim().lines() { 56 for name in SYSROOT_CRATES.trim().lines() {
@@ -86,16 +83,18 @@ impl Sysroot {
86 } 83 }
87} 84}
88 85
89fn get_or_install_rust_src(cargo_toml: &Path) -> Result<PathBuf> { 86fn get_or_install_rust_src(cargo_toml: &AbsPath) -> Result<AbsPathBuf> {
90 if let Ok(path) = env::var("RUST_SRC_PATH") { 87 if let Ok(path) = env::var("RUST_SRC_PATH") {
91 return Ok(path.into()); 88 let path = AbsPathBuf::try_from(path.as_str())
89 .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?;
90 return Ok(path);
92 } 91 }
93 let current_dir = cargo_toml.parent().unwrap(); 92 let current_dir = cargo_toml.parent().unwrap();
94 let mut rustc = Command::new(ra_toolchain::rustc()); 93 let mut rustc = Command::new(ra_toolchain::rustc());
95 rustc.current_dir(current_dir).args(&["--print", "sysroot"]); 94 rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
96 let rustc_output = output(rustc)?; 95 let rustc_output = output(rustc)?;
97 let stdout = String::from_utf8(rustc_output.stdout)?; 96 let stdout = String::from_utf8(rustc_output.stdout)?;
98 let sysroot_path = Path::new(stdout.trim()); 97 let sysroot_path = AbsPath::assert(Path::new(stdout.trim()));
99 let src_path = sysroot_path.join("lib/rustlib/src/rust/src"); 98 let src_path = sysroot_path.join("lib/rustlib/src/rust/src");
100 99
101 if !src_path.exists() { 100 if !src_path.exists() {
@@ -116,7 +115,7 @@ fn get_or_install_rust_src(cargo_toml: &Path) -> Result<PathBuf> {
116} 115}
117 116
118impl SysrootCrateData { 117impl SysrootCrateData {
119 pub fn root_dir(&self) -> &Path { 118 pub fn root_dir(&self) -> &AbsPath {
120 self.root.parent().unwrap() 119 self.root.parent().unwrap()
121 } 120 }
122} 121}
@@ -127,7 +126,6 @@ core
127alloc 126alloc
128collections 127collections
129libc 128libc
130panic_unwind
131proc_macro 129proc_macro
132rustc_unicode 130rustc_unicode
133std_unicode 131std_unicode