aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_project_model/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_project_model/src/lib.rs')
-rw-r--r--crates/ra_project_model/src/lib.rs111
1 files changed, 64 insertions, 47 deletions
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index a2e9f65ef..cb0e27dce 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -14,7 +14,7 @@ use std::{
14use anyhow::{bail, Context, Result}; 14use anyhow::{bail, Context, Result};
15use ra_cfg::CfgOptions; 15use ra_cfg::CfgOptions;
16use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; 16use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId};
17use rustc_hash::FxHashMap; 17use rustc_hash::{FxHashMap, FxHashSet};
18use serde_json::from_reader; 18use serde_json::from_reader;
19 19
20pub use crate::{ 20pub use crate::{
@@ -32,6 +32,12 @@ pub enum ProjectWorkspace {
32 Json { project: JsonProject }, 32 Json { project: JsonProject },
33} 33}
34 34
35impl From<JsonProject> for ProjectWorkspace {
36 fn from(project: JsonProject) -> ProjectWorkspace {
37 ProjectWorkspace::Json { project }
38 }
39}
40
35/// `PackageRoot` describes a package root folder. 41/// `PackageRoot` describes a package root folder.
36/// Which may be an external dependency, or a member of 42/// Which may be an external dependency, or a member of
37/// the current workspace. 43/// the current workspace.
@@ -41,41 +47,45 @@ pub struct PackageRoot {
41 path: PathBuf, 47 path: PathBuf,
42 /// Is a member of the current workspace 48 /// Is a member of the current workspace
43 is_member: bool, 49 is_member: bool,
50 out_dir: Option<PathBuf>,
44} 51}
45impl PackageRoot { 52impl PackageRoot {
46 pub fn new_member(path: PathBuf) -> PackageRoot { 53 pub fn new_member(path: PathBuf) -> PackageRoot {
47 Self { path, is_member: true } 54 Self { path, is_member: true, out_dir: None }
48 } 55 }
49 pub fn new_non_member(path: PathBuf) -> PackageRoot { 56 pub fn new_non_member(path: PathBuf) -> PackageRoot {
50 Self { path, is_member: false } 57 Self { path, is_member: false, out_dir: None }
51 } 58 }
52 pub fn path(&self) -> &Path { 59 pub fn path(&self) -> &Path {
53 &self.path 60 &self.path
54 } 61 }
62 pub fn out_dir(&self) -> Option<&Path> {
63 self.out_dir.as_deref()
64 }
55 pub fn is_member(&self) -> bool { 65 pub fn is_member(&self) -> bool {
56 self.is_member 66 self.is_member
57 } 67 }
58} 68}
59 69
60#[derive(Debug, Clone, PartialEq, Eq, Hash)] 70#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
61pub enum ProjectRoot { 71pub enum ProjectManifest {
62 ProjectJson(PathBuf), 72 ProjectJson(PathBuf),
63 CargoToml(PathBuf), 73 CargoToml(PathBuf),
64} 74}
65 75
66impl ProjectRoot { 76impl ProjectManifest {
67 pub fn from_manifest_file(path: PathBuf) -> Result<ProjectRoot> { 77 pub fn from_manifest_file(path: PathBuf) -> Result<ProjectManifest> {
68 if path.ends_with("rust-project.json") { 78 if path.ends_with("rust-project.json") {
69 return Ok(ProjectRoot::ProjectJson(path)); 79 return Ok(ProjectManifest::ProjectJson(path));
70 } 80 }
71 if path.ends_with("Cargo.toml") { 81 if path.ends_with("Cargo.toml") {
72 return Ok(ProjectRoot::CargoToml(path)); 82 return Ok(ProjectManifest::CargoToml(path));
73 } 83 }
74 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) 84 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
75 } 85 }
76 86
77 pub fn discover_single(path: &Path) -> Result<ProjectRoot> { 87 pub fn discover_single(path: &Path) -> Result<ProjectManifest> {
78 let mut candidates = ProjectRoot::discover(path)?; 88 let mut candidates = ProjectManifest::discover(path)?;
79 let res = match candidates.pop() { 89 let res = match candidates.pop() {
80 None => bail!("no projects"), 90 None => bail!("no projects"),
81 Some(it) => it, 91 Some(it) => it,
@@ -87,12 +97,12 @@ impl ProjectRoot {
87 Ok(res) 97 Ok(res)
88 } 98 }
89 99
90 pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> { 100 pub fn discover(path: &Path) -> io::Result<Vec<ProjectManifest>> {
91 if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { 101 if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") {
92 return Ok(vec![ProjectRoot::ProjectJson(project_json)]); 102 return Ok(vec![ProjectManifest::ProjectJson(project_json)]);
93 } 103 }
94 return find_cargo_toml(path) 104 return find_cargo_toml(path)
95 .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); 105 .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect());
96 106
97 fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { 107 fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> {
98 match find_in_parent_dirs(path, "Cargo.toml") { 108 match find_in_parent_dirs(path, "Cargo.toml") {
@@ -128,16 +138,28 @@ impl ProjectRoot {
128 .collect() 138 .collect()
129 } 139 }
130 } 140 }
141
142 pub fn discover_all(paths: &[impl AsRef<Path>]) -> Vec<ProjectManifest> {
143 let mut res = paths
144 .iter()
145 .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok())
146 .flatten()
147 .collect::<FxHashSet<_>>()
148 .into_iter()
149 .collect::<Vec<_>>();
150 res.sort();
151 res
152 }
131} 153}
132 154
133impl ProjectWorkspace { 155impl ProjectWorkspace {
134 pub fn load( 156 pub fn load(
135 root: ProjectRoot, 157 manifest: ProjectManifest,
136 cargo_features: &CargoConfig, 158 cargo_features: &CargoConfig,
137 with_sysroot: bool, 159 with_sysroot: bool,
138 ) -> Result<ProjectWorkspace> { 160 ) -> Result<ProjectWorkspace> {
139 let res = match root { 161 let res = match manifest {
140 ProjectRoot::ProjectJson(project_json) => { 162 ProjectManifest::ProjectJson(project_json) => {
141 let file = File::open(&project_json).with_context(|| { 163 let file = File::open(&project_json).with_context(|| {
142 format!("Failed to open json file {}", project_json.display()) 164 format!("Failed to open json file {}", project_json.display())
143 })?; 165 })?;
@@ -148,7 +170,7 @@ impl ProjectWorkspace {
148 })?, 170 })?,
149 } 171 }
150 } 172 }
151 ProjectRoot::CargoToml(cargo_toml) => { 173 ProjectManifest::CargoToml(cargo_toml) => {
152 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) 174 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features)
153 .with_context(|| { 175 .with_context(|| {
154 format!( 176 format!(
@@ -186,6 +208,7 @@ impl ProjectWorkspace {
186 .map(|pkg| PackageRoot { 208 .map(|pkg| PackageRoot {
187 path: cargo[pkg].root().to_path_buf(), 209 path: cargo[pkg].root().to_path_buf(),
188 is_member: cargo[pkg].is_member, 210 is_member: cargo[pkg].is_member,
211 out_dir: cargo[pkg].out_dir.clone(),
189 }) 212 })
190 .chain(sysroot.crates().map(|krate| { 213 .chain(sysroot.crates().map(|krate| {
191 PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf()) 214 PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf())
@@ -194,17 +217,6 @@ impl ProjectWorkspace {
194 } 217 }
195 } 218 }
196 219
197 pub fn out_dirs(&self) -> Vec<PathBuf> {
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> { 220 pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> {
209 match self { 221 match self {
210 ProjectWorkspace::Json { project } => project 222 ProjectWorkspace::Json { project } => project
@@ -232,7 +244,7 @@ impl ProjectWorkspace {
232 244
233 pub fn to_crate_graph( 245 pub fn to_crate_graph(
234 &self, 246 &self,
235 default_cfg_options: &CfgOptions, 247 target: Option<&str>,
236 extern_source_roots: &FxHashMap<PathBuf, ExternSourceId>, 248 extern_source_roots: &FxHashMap<PathBuf, ExternSourceId>,
237 proc_macro_client: &ProcMacroClient, 249 proc_macro_client: &ProcMacroClient,
238 load: &mut dyn FnMut(&Path) -> Option<FileId>, 250 load: &mut dyn FnMut(&Path) -> Option<FileId>,
@@ -251,12 +263,16 @@ impl ProjectWorkspace {
251 json_project::Edition::Edition2018 => Edition::Edition2018, 263 json_project::Edition::Edition2018 => Edition::Edition2018,
252 }; 264 };
253 let cfg_options = { 265 let cfg_options = {
254 let mut opts = default_cfg_options.clone(); 266 let mut opts = CfgOptions::default();
255 for name in &krate.atom_cfgs { 267 for cfg in &krate.cfg {
256 opts.insert_atom(name.into()); 268 match cfg.find('=') {
257 } 269 None => opts.insert_atom(cfg.into()),
258 for (key, value) in &krate.key_value_cfgs { 270 Some(pos) => {
259 opts.insert_key_value(key.into(), value.into()); 271 let key = &cfg[..pos];
272 let value = cfg[pos + 1..].trim_matches('"');
273 opts.insert_key_value(key.into(), value.into());
274 }
275 }
260 } 276 }
261 opts 277 opts
262 }; 278 };
@@ -315,18 +331,13 @@ impl ProjectWorkspace {
315 } 331 }
316 } 332 }
317 ProjectWorkspace::Cargo { cargo, sysroot } => { 333 ProjectWorkspace::Cargo { cargo, sysroot } => {
334 let mut cfg_options = get_rustc_cfg_options(target);
335
318 let sysroot_crates: FxHashMap<_, _> = sysroot 336 let sysroot_crates: FxHashMap<_, _> = sysroot
319 .crates() 337 .crates()
320 .filter_map(|krate| { 338 .filter_map(|krate| {
321 let file_id = load(&sysroot[krate].root)?; 339 let file_id = load(&sysroot[krate].root)?;
322 340
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(); 341 let env = Env::default();
331 let extern_source = ExternSource::default(); 342 let extern_source = ExternSource::default();
332 let proc_macro = vec![]; 343 let proc_macro = vec![];
@@ -337,7 +348,7 @@ impl ProjectWorkspace {
337 file_id, 348 file_id,
338 Edition::Edition2018, 349 Edition::Edition2018,
339 Some(crate_name), 350 Some(crate_name),
340 cfg_options, 351 cfg_options.clone(),
341 env, 352 env,
342 extern_source, 353 extern_source,
343 proc_macro, 354 proc_macro,
@@ -368,6 +379,10 @@ impl ProjectWorkspace {
368 379
369 let mut pkg_to_lib_crate = FxHashMap::default(); 380 let mut pkg_to_lib_crate = FxHashMap::default();
370 let mut pkg_crates = FxHashMap::default(); 381 let mut pkg_crates = FxHashMap::default();
382
383 // Add test cfg for non-sysroot crates
384 cfg_options.insert_atom("test".into());
385
371 // Next, create crates for each package, target pair 386 // Next, create crates for each package, target pair
372 for pkg in cargo.packages() { 387 for pkg in cargo.packages() {
373 let mut lib_tgt = None; 388 let mut lib_tgt = None;
@@ -376,7 +391,7 @@ impl ProjectWorkspace {
376 if let Some(file_id) = load(root) { 391 if let Some(file_id) = load(root) {
377 let edition = cargo[pkg].edition; 392 let edition = cargo[pkg].edition;
378 let cfg_options = { 393 let cfg_options = {
379 let mut opts = default_cfg_options.clone(); 394 let mut opts = cfg_options.clone();
380 for feature in cargo[pkg].features.iter() { 395 for feature in cargo[pkg].features.iter() {
381 opts.insert_key_value("feature".into(), feature.into()); 396 opts.insert_key_value("feature".into(), feature.into());
382 } 397 }
@@ -533,7 +548,7 @@ impl ProjectWorkspace {
533 } 548 }
534} 549}
535 550
536pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { 551fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions {
537 let mut cfg_options = CfgOptions::default(); 552 let mut cfg_options = CfgOptions::default();
538 553
539 // Some nightly-only cfgs, which are required for stdlib 554 // Some nightly-only cfgs, which are required for stdlib
@@ -551,7 +566,7 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions {
551 let mut cmd = Command::new(ra_toolchain::rustc()); 566 let mut cmd = Command::new(ra_toolchain::rustc());
552 cmd.args(&["--print", "cfg", "-O"]); 567 cmd.args(&["--print", "cfg", "-O"]);
553 if let Some(target) = target { 568 if let Some(target) = target {
554 cmd.args(&["--target", target.as_str()]); 569 cmd.args(&["--target", target]);
555 } 570 }
556 let output = output(cmd)?; 571 let output = output(cmd)?;
557 Ok(String::from_utf8(output.stdout)?) 572 Ok(String::from_utf8(output.stdout)?)
@@ -573,6 +588,8 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions {
573 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), 588 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e),
574 } 589 }
575 590
591 cfg_options.insert_atom("debug_assertions".into());
592
576 cfg_options 593 cfg_options
577} 594}
578 595