diff options
Diffstat (limited to 'crates/ra_project_model')
-rw-r--r-- | crates/ra_project_model/src/cargo_workspace.rs | 18 | ||||
-rw-r--r-- | crates/ra_project_model/src/cfg_flag.rs | 51 | ||||
-rw-r--r-- | crates/ra_project_model/src/lib.rs | 77 | ||||
-rw-r--r-- | crates/ra_project_model/src/project_json.rs | 23 | ||||
-rw-r--r-- | crates/ra_project_model/src/sysroot.rs | 7 |
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}; | |||
14 | use ra_db::Edition; | 14 | use ra_db::Edition; |
15 | use rustc_hash::FxHashMap; | 15 | use rustc_hash::FxHashMap; |
16 | 16 | ||
17 | use 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 { | |||
276 | pub struct ExternResources { | 278 | pub 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 | ||
282 | pub fn load_extern_resources( | 284 | pub 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"' | ||
4 | use std::str::FromStr; | ||
5 | |||
6 | use ra_cfg::CfgOptions; | ||
7 | use stdx::split_delim; | ||
8 | |||
9 | #[derive(Clone, Eq, PartialEq, Debug)] | ||
10 | pub enum CfgFlag { | ||
11 | Atom(String), | ||
12 | KeyValue { key: String, value: String }, | ||
13 | } | ||
14 | |||
15 | impl 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 | |||
33 | impl<'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 | |||
42 | impl 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 @@ | |||
3 | mod cargo_workspace; | 3 | mod cargo_workspace; |
4 | mod project_json; | 4 | mod project_json; |
5 | mod sysroot; | 5 | mod sysroot; |
6 | mod cfg_flag; | ||
6 | 7 | ||
7 | use std::{ | 8 | use 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 | ||
13 | use anyhow::{bail, Context, Result}; | 14 | use anyhow::{bail, Context, Result}; |
@@ -15,13 +16,15 @@ use paths::{AbsPath, AbsPathBuf}; | |||
15 | use ra_cfg::CfgOptions; | 16 | use ra_cfg::CfgOptions; |
16 | use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId}; | 17 | use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId}; |
17 | use rustc_hash::{FxHashMap, FxHashSet}; | 18 | use rustc_hash::{FxHashMap, FxHashSet}; |
18 | use stdx::split_delim; | 19 | |
20 | use crate::cfg_flag::CfgFlag; | ||
19 | 21 | ||
20 | pub use crate::{ | 22 | pub 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 | |||
25 | pub use ra_proc_macro::ProcMacroClient; | 28 | pub 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 | ||
506 | fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions { | 504 | fn 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 | ||
550 | fn output(mut cmd: Command) -> Result<Output> { | 532 | fn 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 @@ | |||
3 | use std::path::PathBuf; | 3 | use std::path::PathBuf; |
4 | 4 | ||
5 | use paths::{AbsPath, AbsPathBuf}; | 5 | use paths::{AbsPath, AbsPathBuf}; |
6 | use ra_cfg::CfgOptions; | ||
7 | use ra_db::{CrateId, CrateName, Dependency, Edition}; | 6 | use ra_db::{CrateId, CrateName, Dependency, Edition}; |
8 | use rustc_hash::{FxHashMap, FxHashSet}; | 7 | use rustc_hash::FxHashMap; |
9 | use serde::{de, Deserialize}; | 8 | use serde::{de, Deserialize}; |
10 | use stdx::split_delim; | 9 | |
10 | use 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}; | |||
6 | use paths::{AbsPath, AbsPathBuf}; | 6 | use paths::{AbsPath, AbsPathBuf}; |
7 | use ra_arena::{Arena, Idx}; | 7 | use ra_arena::{Arena, Idx}; |
8 | 8 | ||
9 | use crate::output; | 9 | use crate::utf8_stdout; |
10 | 10 | ||
11 | #[derive(Default, Debug, Clone, Eq, PartialEq)] | 11 | #[derive(Default, Debug, Clone, Eq, PartialEq)] |
12 | pub struct Sysroot { | 12 | pub 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!( |