aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_project_model
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_project_model')
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs18
-rw-r--r--crates/ra_project_model/src/cfg_flag.rs51
-rw-r--r--crates/ra_project_model/src/lib.rs77
-rw-r--r--crates/ra_project_model/src/project_json.rs23
-rw-r--r--crates/ra_project_model/src/sysroot.rs7
5 files changed, 106 insertions, 70 deletions
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index 4182ca156..fb88e0f06 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -14,6 +14,8 @@ use ra_arena::{Arena, Idx};
14use ra_db::Edition; 14use ra_db::Edition;
15use rustc_hash::FxHashMap; 15use rustc_hash::FxHashMap;
16 16
17use crate::cfg_flag::CfgFlag;
18
17/// `CargoWorkspace` represents the logical structure of, well, a Cargo 19/// `CargoWorkspace` represents the logical structure of, well, a Cargo
18/// workspace. It pretty closely mirrors `cargo metadata` output. 20/// workspace. It pretty closely mirrors `cargo metadata` output.
19/// 21///
@@ -78,7 +80,7 @@ pub struct PackageData {
78 pub dependencies: Vec<PackageDependency>, 80 pub dependencies: Vec<PackageDependency>,
79 pub edition: Edition, 81 pub edition: Edition,
80 pub features: Vec<String>, 82 pub features: Vec<String>,
81 pub cfgs: Vec<String>, 83 pub cfgs: Vec<CfgFlag>,
82 pub out_dir: Option<AbsPathBuf>, 84 pub out_dir: Option<AbsPathBuf>,
83 pub proc_macro_dylib_path: Option<AbsPathBuf>, 85 pub proc_macro_dylib_path: Option<AbsPathBuf>,
84} 86}
@@ -276,7 +278,7 @@ impl CargoWorkspace {
276pub struct ExternResources { 278pub struct ExternResources {
277 out_dirs: FxHashMap<PackageId, AbsPathBuf>, 279 out_dirs: FxHashMap<PackageId, AbsPathBuf>,
278 proc_dylib_paths: FxHashMap<PackageId, AbsPathBuf>, 280 proc_dylib_paths: FxHashMap<PackageId, AbsPathBuf>,
279 cfgs: FxHashMap<PackageId, Vec<String>>, 281 cfgs: FxHashMap<PackageId, Vec<CfgFlag>>,
280} 282}
281 283
282pub fn load_extern_resources( 284pub fn load_extern_resources(
@@ -303,6 +305,18 @@ pub fn load_extern_resources(
303 if let Ok(message) = message { 305 if let Ok(message) = message {
304 match message { 306 match message {
305 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => { 307 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => {
308 let cfgs = {
309 let mut acc = Vec::new();
310 for cfg in cfgs {
311 match cfg.parse::<CfgFlag>() {
312 Ok(it) => acc.push(it),
313 Err(err) => {
314 anyhow::bail!("invalid cfg from cargo-metadata: {}", err)
315 }
316 };
317 }
318 acc
319 };
306 // cargo_metadata crate returns default (empty) path for 320 // cargo_metadata crate returns default (empty) path for
307 // older cargos, which is not absolute, so work around that. 321 // older cargos, which is not absolute, so work around that.
308 if out_dir != PathBuf::default() { 322 if out_dir != PathBuf::default() {
diff --git a/crates/ra_project_model/src/cfg_flag.rs b/crates/ra_project_model/src/cfg_flag.rs
new file mode 100644
index 000000000..1bc5d4832
--- /dev/null
+++ b/crates/ra_project_model/src/cfg_flag.rs
@@ -0,0 +1,51 @@
1//! Parsing of CfgFlags as command line arguments, as in
2//!
3//! rustc main.rs --cfg foo --cfg 'feature="bar"'
4use std::str::FromStr;
5
6use ra_cfg::CfgOptions;
7use stdx::split_delim;
8
9#[derive(Clone, Eq, PartialEq, Debug)]
10pub enum CfgFlag {
11 Atom(String),
12 KeyValue { key: String, value: String },
13}
14
15impl FromStr for CfgFlag {
16 type Err = String;
17 fn from_str(s: &str) -> Result<Self, Self::Err> {
18 let res = match split_delim(s, '=') {
19 Some((key, value)) => {
20 if !(value.starts_with('"') && value.ends_with('"')) {
21 return Err(format!("Invalid cfg ({:?}), value should be in quotes", s));
22 }
23 let key = key.to_string();
24 let value = value[1..value.len() - 1].to_string();
25 CfgFlag::KeyValue { key, value }
26 }
27 None => CfgFlag::Atom(s.into()),
28 };
29 Ok(res)
30 }
31}
32
33impl<'de> serde::Deserialize<'de> for CfgFlag {
34 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
35 where
36 D: serde::Deserializer<'de>,
37 {
38 String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom)
39 }
40}
41
42impl Extend<CfgFlag> for CfgOptions {
43 fn extend<T: IntoIterator<Item = CfgFlag>>(&mut self, iter: T) {
44 for cfg_flag in iter {
45 match cfg_flag {
46 CfgFlag::Atom(it) => self.insert_atom(it.into()),
47 CfgFlag::KeyValue { key, value } => self.insert_key_value(key.into(), value.into()),
48 }
49 }
50 }
51}
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index 8053712ff..300e75135 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -3,11 +3,12 @@
3mod cargo_workspace; 3mod cargo_workspace;
4mod project_json; 4mod project_json;
5mod sysroot; 5mod sysroot;
6mod cfg_flag;
6 7
7use std::{ 8use std::{
8 fs::{self, read_dir, ReadDir}, 9 fs::{self, read_dir, ReadDir},
9 io, 10 io,
10 process::{Command, Output}, 11 process::Command,
11}; 12};
12 13
13use anyhow::{bail, Context, Result}; 14use anyhow::{bail, Context, Result};
@@ -15,13 +16,15 @@ use paths::{AbsPath, AbsPathBuf};
15use ra_cfg::CfgOptions; 16use ra_cfg::CfgOptions;
16use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId}; 17use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId};
17use rustc_hash::{FxHashMap, FxHashSet}; 18use rustc_hash::{FxHashMap, FxHashSet};
18use stdx::split_delim; 19
20use crate::cfg_flag::CfgFlag;
19 21
20pub use crate::{ 22pub use crate::{
21 cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind}, 23 cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind},
22 project_json::{ProjectJson, ProjectJsonData}, 24 project_json::{ProjectJson, ProjectJsonData},
23 sysroot::Sysroot, 25 sysroot::Sysroot,
24}; 26};
27
25pub use ra_proc_macro::ProcMacroClient; 28pub use ra_proc_macro::ProcMacroClient;
26 29
27#[derive(Debug, Clone, Eq, PartialEq)] 30#[derive(Debug, Clone, Eq, PartialEq)]
@@ -250,7 +253,7 @@ impl ProjectWorkspace {
250 let mut crate_graph = CrateGraph::default(); 253 let mut crate_graph = CrateGraph::default();
251 match self { 254 match self {
252 ProjectWorkspace::Json { project } => { 255 ProjectWorkspace::Json { project } => {
253 let mut target_cfg_map = FxHashMap::<Option<&str>, CfgOptions>::default(); 256 let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default();
254 let crates: FxHashMap<_, _> = project 257 let crates: FxHashMap<_, _> = project
255 .crates 258 .crates
256 .iter() 259 .iter()
@@ -266,11 +269,12 @@ impl ProjectWorkspace {
266 .map(|it| proc_macro_client.by_dylib_path(&it)); 269 .map(|it| proc_macro_client.by_dylib_path(&it));
267 270
268 let target = krate.target.as_deref().or(target); 271 let target = krate.target.as_deref().or(target);
269 let target_cfgs = target_cfg_map 272 let target_cfgs = cfg_cache
270 .entry(target.clone()) 273 .entry(target)
271 .or_insert_with(|| get_rustc_cfg_options(target.as_deref())); 274 .or_insert_with(|| get_rustc_cfg_options(target));
272 let mut cfg_options = krate.cfg.clone(); 275
273 cfg_options.append(target_cfgs); 276 let mut cfg_options = CfgOptions::default();
277 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
274 278
275 // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env 279 // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env
276 Some(( 280 Some((
@@ -307,7 +311,8 @@ impl ProjectWorkspace {
307 } 311 }
308 } 312 }
309 ProjectWorkspace::Cargo { cargo, sysroot } => { 313 ProjectWorkspace::Cargo { cargo, sysroot } => {
310 let mut cfg_options = get_rustc_cfg_options(target); 314 let mut cfg_options = CfgOptions::default();
315 cfg_options.extend(get_rustc_cfg_options(target));
311 316
312 let sysroot_crates: FxHashMap<_, _> = sysroot 317 let sysroot_crates: FxHashMap<_, _> = sysroot
313 .crates() 318 .crates()
@@ -354,6 +359,7 @@ impl ProjectWorkspace {
354 359
355 // Add test cfg for non-sysroot crates 360 // Add test cfg for non-sysroot crates
356 cfg_options.insert_atom("test".into()); 361 cfg_options.insert_atom("test".into());
362 cfg_options.insert_atom("debug_assertions".into());
357 363
358 // Next, create crates for each package, target pair 364 // Next, create crates for each package, target pair
359 for pkg in cargo.packages() { 365 for pkg in cargo.packages() {
@@ -367,15 +373,7 @@ impl ProjectWorkspace {
367 for feature in cargo[pkg].features.iter() { 373 for feature in cargo[pkg].features.iter() {
368 opts.insert_key_value("feature".into(), feature.into()); 374 opts.insert_key_value("feature".into(), feature.into());
369 } 375 }
370 for cfg in cargo[pkg].cfgs.iter() { 376 opts.extend(cargo[pkg].cfgs.iter().cloned());
371 match cfg.find('=') {
372 Some(split) => opts.insert_key_value(
373 cfg[..split].into(),
374 cfg[split + 1..].trim_matches('"').into(),
375 ),
376 None => opts.insert_atom(cfg.into()),
377 };
378 }
379 opts 377 opts
380 }; 378 };
381 let mut env = Env::default(); 379 let mut env = Env::default();
@@ -503,51 +501,35 @@ impl ProjectWorkspace {
503 } 501 }
504} 502}
505 503
506fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions { 504fn get_rustc_cfg_options(target: Option<&str>) -> Vec<CfgFlag> {
507 let mut cfg_options = CfgOptions::default(); 505 let mut res = Vec::new();
508 506
509 // Some nightly-only cfgs, which are required for stdlib 507 // Some nightly-only cfgs, which are required for stdlib
510 { 508 res.push(CfgFlag::Atom("target_thread_local".into()));
511 cfg_options.insert_atom("target_thread_local".into()); 509 for &ty in ["8", "16", "32", "64", "cas", "ptr"].iter() {
512 for &target_has_atomic in ["8", "16", "32", "64", "cas", "ptr"].iter() { 510 for &key in ["target_has_atomic", "target_has_atomic_load_store"].iter() {
513 cfg_options.insert_key_value("target_has_atomic".into(), target_has_atomic.into()); 511 res.push(CfgFlag::KeyValue { key: key.to_string(), value: ty.into() });
514 cfg_options
515 .insert_key_value("target_has_atomic_load_store".into(), target_has_atomic.into());
516 } 512 }
517 } 513 }
518 514
519 let rustc_cfgs = || -> Result<String> { 515 let rustc_cfgs = {
520 // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here.
521 let mut cmd = Command::new(ra_toolchain::rustc()); 516 let mut cmd = Command::new(ra_toolchain::rustc());
522 cmd.args(&["--print", "cfg", "-O"]); 517 cmd.args(&["--print", "cfg", "-O"]);
523 if let Some(target) = target { 518 if let Some(target) = target {
524 cmd.args(&["--target", target]); 519 cmd.args(&["--target", target]);
525 } 520 }
526 let output = output(cmd)?; 521 utf8_stdout(cmd)
527 Ok(String::from_utf8(output.stdout)?) 522 };
528 }();
529 523
530 match rustc_cfgs { 524 match rustc_cfgs {
531 Ok(rustc_cfgs) => { 525 Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())),
532 for line in rustc_cfgs.lines() {
533 match split_delim(line, '=') {
534 None => cfg_options.insert_atom(line.into()),
535 Some((key, value)) => {
536 let value = value.trim_matches('"');
537 cfg_options.insert_key_value(key.into(), value.into());
538 }
539 }
540 }
541 }
542 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), 526 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e),
543 } 527 }
544 528
545 cfg_options.insert_atom("debug_assertions".into()); 529 res
546
547 cfg_options
548} 530}
549 531
550fn output(mut cmd: Command) -> Result<Output> { 532fn utf8_stdout(mut cmd: Command) -> Result<String> {
551 let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?; 533 let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?;
552 if !output.status.success() { 534 if !output.status.success() {
553 match String::from_utf8(output.stderr) { 535 match String::from_utf8(output.stderr) {
@@ -557,5 +539,6 @@ fn output(mut cmd: Command) -> Result<Output> {
557 _ => bail!("{:?} failed, {}", cmd, output.status), 539 _ => bail!("{:?} failed, {}", cmd, output.status),
558 } 540 }
559 } 541 }
560 Ok(output) 542 let stdout = String::from_utf8(output.stdout)?;
543 Ok(stdout)
561} 544}
diff --git a/crates/ra_project_model/src/project_json.rs b/crates/ra_project_model/src/project_json.rs
index e9a333191..e3f3163f6 100644
--- a/crates/ra_project_model/src/project_json.rs
+++ b/crates/ra_project_model/src/project_json.rs
@@ -3,11 +3,11 @@
3use std::path::PathBuf; 3use std::path::PathBuf;
4 4
5use paths::{AbsPath, AbsPathBuf}; 5use paths::{AbsPath, AbsPathBuf};
6use ra_cfg::CfgOptions;
7use ra_db::{CrateId, CrateName, Dependency, Edition}; 6use ra_db::{CrateId, CrateName, Dependency, Edition};
8use rustc_hash::{FxHashMap, FxHashSet}; 7use rustc_hash::FxHashMap;
9use serde::{de, Deserialize}; 8use serde::{de, Deserialize};
10use stdx::split_delim; 9
10use crate::cfg_flag::CfgFlag;
11 11
12/// Roots and crates that compose this Rust project. 12/// Roots and crates that compose this Rust project.
13#[derive(Clone, Debug, Eq, PartialEq)] 13#[derive(Clone, Debug, Eq, PartialEq)]
@@ -22,7 +22,7 @@ pub struct Crate {
22 pub(crate) root_module: AbsPathBuf, 22 pub(crate) root_module: AbsPathBuf,
23 pub(crate) edition: Edition, 23 pub(crate) edition: Edition,
24 pub(crate) deps: Vec<Dependency>, 24 pub(crate) deps: Vec<Dependency>,
25 pub(crate) cfg: CfgOptions, 25 pub(crate) cfg: Vec<CfgFlag>,
26 pub(crate) target: Option<String>, 26 pub(crate) target: Option<String>,
27 pub(crate) env: FxHashMap<String, String>, 27 pub(crate) env: FxHashMap<String, String>,
28 pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>, 28 pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
@@ -65,18 +65,7 @@ impl ProjectJson {
65 name: dep_data.name, 65 name: dep_data.name,
66 }) 66 })
67 .collect::<Vec<_>>(), 67 .collect::<Vec<_>>(),
68 cfg: { 68 cfg: crate_data.cfg,
69 let mut cfg = CfgOptions::default();
70 for entry in &crate_data.cfg {
71 match split_delim(entry, '=') {
72 Some((key, value)) => {
73 cfg.insert_key_value(key.into(), value.into());
74 }
75 None => cfg.insert_atom(entry.into()),
76 }
77 }
78 cfg
79 },
80 target: crate_data.target, 69 target: crate_data.target,
81 env: crate_data.env, 70 env: crate_data.env,
82 proc_macro_dylib_path: crate_data 71 proc_macro_dylib_path: crate_data
@@ -103,7 +92,7 @@ struct CrateData {
103 edition: EditionData, 92 edition: EditionData,
104 deps: Vec<DepData>, 93 deps: Vec<DepData>,
105 #[serde(default)] 94 #[serde(default)]
106 cfg: FxHashSet<String>, 95 cfg: Vec<CfgFlag>,
107 target: Option<String>, 96 target: Option<String>,
108 #[serde(default)] 97 #[serde(default)]
109 env: FxHashMap<String, String>, 98 env: FxHashMap<String, String>,
diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs
index 68d134da4..8a92acea5 100644
--- a/crates/ra_project_model/src/sysroot.rs
+++ b/crates/ra_project_model/src/sysroot.rs
@@ -6,7 +6,7 @@ use anyhow::{bail, format_err, Result};
6use paths::{AbsPath, AbsPathBuf}; 6use paths::{AbsPath, AbsPathBuf};
7use ra_arena::{Arena, Idx}; 7use ra_arena::{Arena, Idx};
8 8
9use crate::output; 9use crate::utf8_stdout;
10 10
11#[derive(Default, Debug, Clone, Eq, PartialEq)] 11#[derive(Default, Debug, Clone, Eq, PartialEq)]
12pub struct Sysroot { 12pub struct Sysroot {
@@ -92,15 +92,14 @@ fn get_or_install_rust_src(cargo_toml: &AbsPath) -> Result<AbsPathBuf> {
92 let current_dir = cargo_toml.parent().unwrap(); 92 let current_dir = cargo_toml.parent().unwrap();
93 let mut rustc = Command::new(ra_toolchain::rustc()); 93 let mut rustc = Command::new(ra_toolchain::rustc());
94 rustc.current_dir(current_dir).args(&["--print", "sysroot"]); 94 rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
95 let rustc_output = output(rustc)?; 95 let stdout = utf8_stdout(rustc)?;
96 let stdout = String::from_utf8(rustc_output.stdout)?;
97 let sysroot_path = AbsPath::assert(Path::new(stdout.trim())); 96 let sysroot_path = AbsPath::assert(Path::new(stdout.trim()));
98 let src_path = sysroot_path.join("lib/rustlib/src/rust/src"); 97 let src_path = sysroot_path.join("lib/rustlib/src/rust/src");
99 98
100 if !src_path.exists() { 99 if !src_path.exists() {
101 let mut rustup = Command::new(ra_toolchain::rustup()); 100 let mut rustup = Command::new(ra_toolchain::rustup());
102 rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]); 101 rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]);
103 let _output = output(rustup)?; 102 utf8_stdout(rustup)?;
104 } 103 }
105 if !src_path.exists() { 104 if !src_path.exists() {
106 bail!( 105 bail!(